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(&params)
   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  }