github.com/mattermost/mattermost-server/v5@v5.39.3/api4/saml.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See LICENSE.txt for license information. 3 4 package api4 5 6 import ( 7 "encoding/json" 8 "io/ioutil" 9 "mime" 10 "mime/multipart" 11 "net/http" 12 13 "github.com/mattermost/mattermost-server/v5/audit" 14 "github.com/mattermost/mattermost-server/v5/model" 15 ) 16 17 func (api *API) InitSaml() { 18 api.BaseRoutes.SAML.Handle("/metadata", api.ApiHandler(getSamlMetadata)).Methods("GET") 19 20 api.BaseRoutes.SAML.Handle("/certificate/public", api.ApiSessionRequired(addSamlPublicCertificate)).Methods("POST") 21 api.BaseRoutes.SAML.Handle("/certificate/private", api.ApiSessionRequired(addSamlPrivateCertificate)).Methods("POST") 22 api.BaseRoutes.SAML.Handle("/certificate/idp", api.ApiSessionRequired(addSamlIdpCertificate)).Methods("POST") 23 24 api.BaseRoutes.SAML.Handle("/certificate/public", api.ApiSessionRequired(removeSamlPublicCertificate)).Methods("DELETE") 25 api.BaseRoutes.SAML.Handle("/certificate/private", api.ApiSessionRequired(removeSamlPrivateCertificate)).Methods("DELETE") 26 api.BaseRoutes.SAML.Handle("/certificate/idp", api.ApiSessionRequired(removeSamlIdpCertificate)).Methods("DELETE") 27 28 api.BaseRoutes.SAML.Handle("/certificate/status", api.ApiSessionRequired(getSamlCertificateStatus)).Methods("GET") 29 30 api.BaseRoutes.SAML.Handle("/metadatafromidp", api.ApiHandler(getSamlMetadataFromIdp)).Methods("POST") 31 32 api.BaseRoutes.SAML.Handle("/reset_auth_data", api.ApiSessionRequired(resetAuthDataToEmail)).Methods("POST") 33 } 34 35 func (api *API) InitSamlLocal() { 36 api.BaseRoutes.SAML.Handle("/reset_auth_data", api.ApiLocal(resetAuthDataToEmail)).Methods("POST") 37 } 38 39 func getSamlMetadata(c *Context, w http.ResponseWriter, r *http.Request) { 40 metadata, err := c.App.GetSamlMetadata() 41 if err != nil { 42 c.Err = err 43 return 44 } 45 46 w.Header().Set("Content-Type", "application/xml") 47 w.Header().Set("Content-Disposition", "attachment; filename=\"metadata.xml\"") 48 w.Write([]byte(metadata)) 49 } 50 51 func parseSamlCertificateRequest(r *http.Request, maxFileSize int64) (*multipart.FileHeader, *model.AppError) { 52 err := r.ParseMultipartForm(maxFileSize) 53 if err != nil { 54 return nil, model.NewAppError("addSamlCertificate", "api.admin.add_certificate.no_file.app_error", nil, err.Error(), http.StatusBadRequest) 55 } 56 57 m := r.MultipartForm 58 59 fileArray, ok := m.File["certificate"] 60 if !ok { 61 return nil, model.NewAppError("addSamlCertificate", "api.admin.add_certificate.no_file.app_error", nil, "", http.StatusBadRequest) 62 } 63 64 if len(fileArray) <= 0 { 65 return nil, model.NewAppError("addSamlCertificate", "api.admin.add_certificate.array.app_error", nil, "", http.StatusBadRequest) 66 } 67 68 return fileArray[0], nil 69 } 70 71 func addSamlPublicCertificate(c *Context, w http.ResponseWriter, r *http.Request) { 72 if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PERMISSION_ADD_SAML_PUBLIC_CERT) { 73 c.SetPermissionError(model.PERMISSION_ADD_SAML_PUBLIC_CERT) 74 return 75 } 76 77 fileData, err := parseSamlCertificateRequest(r, *c.App.Config().FileSettings.MaxFileSize) 78 if err != nil { 79 c.Err = err 80 return 81 } 82 83 auditRec := c.MakeAuditRecord("addSamlPublicCertificate", audit.Fail) 84 defer c.LogAuditRec(auditRec) 85 auditRec.AddMeta("filename", fileData.Filename) 86 87 if err := c.App.AddSamlPublicCertificate(fileData); err != nil { 88 c.Err = err 89 return 90 } 91 auditRec.Success() 92 ReturnStatusOK(w) 93 } 94 95 func addSamlPrivateCertificate(c *Context, w http.ResponseWriter, r *http.Request) { 96 if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PERMISSION_ADD_SAML_PRIVATE_CERT) { 97 c.SetPermissionError(model.PERMISSION_ADD_SAML_PRIVATE_CERT) 98 return 99 } 100 101 fileData, err := parseSamlCertificateRequest(r, *c.App.Config().FileSettings.MaxFileSize) 102 if err != nil { 103 c.Err = err 104 return 105 } 106 107 auditRec := c.MakeAuditRecord("addSamlPrivateCertificate", audit.Fail) 108 defer c.LogAuditRec(auditRec) 109 auditRec.AddMeta("filename", fileData.Filename) 110 111 if err := c.App.AddSamlPrivateCertificate(fileData); err != nil { 112 c.Err = err 113 return 114 } 115 auditRec.Success() 116 ReturnStatusOK(w) 117 } 118 119 func addSamlIdpCertificate(c *Context, w http.ResponseWriter, r *http.Request) { 120 if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PERMISSION_ADD_SAML_IDP_CERT) { 121 c.SetPermissionError(model.PERMISSION_ADD_SAML_IDP_CERT) 122 return 123 } 124 125 v := r.Header.Get("Content-Type") 126 if v == "" { 127 c.Err = model.NewAppError("addSamlIdpCertificate", "api.admin.saml.set_certificate_from_metadata.missing_content_type.app_error", nil, "", http.StatusBadRequest) 128 return 129 } 130 d, _, err := mime.ParseMediaType(v) 131 if err != nil { 132 c.Err = model.NewAppError("addSamlIdpCertificate", "api.admin.saml.set_certificate_from_metadata.invalid_content_type.app_error", nil, err.Error(), http.StatusBadRequest) 133 return 134 } 135 136 auditRec := c.MakeAuditRecord("addSamlIdpCertificate", audit.Fail) 137 defer c.LogAuditRec(auditRec) 138 auditRec.AddMeta("type", d) 139 140 if d == "application/x-pem-file" { 141 body, err := ioutil.ReadAll(r.Body) 142 if err != nil { 143 c.Err = model.NewAppError("addSamlIdpCertificate", "api.admin.saml.set_certificate_from_metadata.invalid_body.app_error", nil, err.Error(), http.StatusBadRequest) 144 return 145 } 146 147 if err := c.App.SetSamlIdpCertificateFromMetadata(body); err != nil { 148 c.Err = err 149 return 150 } 151 } else if d == "multipart/form-data" { 152 fileData, err := parseSamlCertificateRequest(r, *c.App.Config().FileSettings.MaxFileSize) 153 if err != nil { 154 c.Err = err 155 return 156 } 157 auditRec.AddMeta("filename", fileData.Filename) 158 159 if err := c.App.AddSamlIdpCertificate(fileData); err != nil { 160 c.Err = err 161 return 162 } 163 } else { 164 c.Err = model.NewAppError("addSamlIdpCertificate", "api.admin.saml.set_certificate_from_metadata.invalid_content_type.app_error", nil, "", http.StatusBadRequest) 165 return 166 } 167 168 auditRec.Success() 169 ReturnStatusOK(w) 170 } 171 172 func removeSamlPublicCertificate(c *Context, w http.ResponseWriter, r *http.Request) { 173 if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PERMISSION_REMOVE_SAML_PUBLIC_CERT) { 174 c.SetPermissionError(model.PERMISSION_REMOVE_SAML_PUBLIC_CERT) 175 return 176 } 177 178 auditRec := c.MakeAuditRecord("removeSamlPublicCertificate", audit.Fail) 179 defer c.LogAuditRec(auditRec) 180 181 if err := c.App.RemoveSamlPublicCertificate(); err != nil { 182 c.Err = err 183 return 184 } 185 186 auditRec.Success() 187 ReturnStatusOK(w) 188 } 189 190 func removeSamlPrivateCertificate(c *Context, w http.ResponseWriter, r *http.Request) { 191 if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PERMISSION_REMOVE_SAML_PRIVATE_CERT) { 192 c.SetPermissionError(model.PERMISSION_REMOVE_SAML_PRIVATE_CERT) 193 return 194 } 195 196 auditRec := c.MakeAuditRecord("removeSamlPrivateCertificate", audit.Fail) 197 defer c.LogAuditRec(auditRec) 198 199 if err := c.App.RemoveSamlPrivateCertificate(); err != nil { 200 c.Err = err 201 return 202 } 203 204 auditRec.Success() 205 ReturnStatusOK(w) 206 } 207 208 func removeSamlIdpCertificate(c *Context, w http.ResponseWriter, r *http.Request) { 209 if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PERMISSION_REMOVE_SAML_IDP_CERT) { 210 c.SetPermissionError(model.PERMISSION_REMOVE_SAML_IDP_CERT) 211 return 212 } 213 214 auditRec := c.MakeAuditRecord("removeSamlIdpCertificate", audit.Fail) 215 defer c.LogAuditRec(auditRec) 216 217 if err := c.App.RemoveSamlIdpCertificate(); err != nil { 218 c.Err = err 219 return 220 } 221 222 auditRec.Success() 223 ReturnStatusOK(w) 224 } 225 226 func getSamlCertificateStatus(c *Context, w http.ResponseWriter, r *http.Request) { 227 if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PERMISSION_GET_SAML_CERT_STATUS) { 228 c.SetPermissionError(model.PERMISSION_GET_SAML_CERT_STATUS) 229 return 230 } 231 232 status := c.App.GetSamlCertificateStatus() 233 w.Write([]byte(status.ToJson())) 234 } 235 236 func getSamlMetadataFromIdp(c *Context, w http.ResponseWriter, r *http.Request) { 237 if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PERMISSION_GET_SAML_METADATA_FROM_IDP) { 238 c.SetPermissionError(model.PERMISSION_GET_SAML_METADATA_FROM_IDP) 239 return 240 } 241 242 props := model.MapFromJson(r.Body) 243 url := props["saml_metadata_url"] 244 if url == "" { 245 c.SetInvalidParam("saml_metadata_url") 246 return 247 } 248 249 metadata, err := c.App.GetSamlMetadataFromIdp(url) 250 if err != nil { 251 c.Err = model.NewAppError("getSamlMetadataFromIdp", "api.admin.saml.failure_get_metadata_from_idp.app_error", nil, err.Error(), http.StatusBadRequest) 252 return 253 } 254 255 w.Write([]byte(metadata.ToJson())) 256 } 257 258 func resetAuthDataToEmail(c *Context, w http.ResponseWriter, r *http.Request) { 259 if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PERMISSION_MANAGE_SYSTEM) { 260 c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM) 261 return 262 } 263 type ResetAuthDataParams struct { 264 IncludeDeleted bool `json:"include_deleted"` 265 DryRun bool `json:"dry_run"` 266 SpecifiedUserIDs []string `json:"user_ids"` 267 } 268 var params *ResetAuthDataParams 269 jsonErr := json.NewDecoder(r.Body).Decode(¶ms) 270 if jsonErr != nil { 271 c.Err = model.NewAppError("resetAuthDataToEmail", "model.utils.decode_json.app_error", nil, jsonErr.Error(), http.StatusBadRequest) 272 return 273 } 274 numAffected, appErr := c.App.ResetSamlAuthDataToEmail(params.IncludeDeleted, params.DryRun, params.SpecifiedUserIDs) 275 if appErr != nil { 276 c.Err = appErr 277 return 278 } 279 b, _ := json.Marshal(map[string]interface{}{"num_affected": numAffected}) 280 w.Write(b) 281 }