From 3fcecd521a5c6ccfdb52fb4c3fb1f8c6ea528a4e Mon Sep 17 00:00:00 2001 From: Chris Duarte Date: Wed, 18 Jul 2018 14:56:38 -0700 Subject: [PATCH] Support for Embeded chat (#9129) * Add ucLive support crazy testing lovely logs more cookie work arounds Added Access-Control-Expose-Headers to user login Add complete_saml_body template and revert loginWithSaml endpoint Set Access-Control-Allow-Credentials to true in user login Login via email instead of username Clean up code Add comment to give some context Move faml logic into saml function Communicate via chrome sendMessage api Remove unused code Add config to support multiple extensions Clean up embedded complete_saml template Fix indentation for templates Added license header to extension.go Add EnableExperimentalExtensions flag Extension validated for email auth Clean up api auth code Remove complete_saml_body.html * Add extension support in saml * Clean up code * Clean up extension validation --- app/extension.go | 82 +++++++++++++++++++++ config/default.json | 4 + i18n/en.json | 16 ++++ model/config.go | 17 +++++ model/oauth.go | 1 + templates/complete_saml_extension_body.html | 30 ++++++++ web/saml.go | 17 +++++ 7 files changed, 167 insertions(+) create mode 100644 app/extension.go create mode 100644 templates/complete_saml_extension_body.html diff --git a/app/extension.go b/app/extension.go new file mode 100644 index 00000000000..d0226bba586 --- /dev/null +++ b/app/extension.go @@ -0,0 +1,82 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package app + +import ( + "github.com/mattermost/mattermost-server/model" + "html/template" + "net/http" +) + +func (a *App) isExtensionSupportEnabled() bool { + return *a.Config().ExtensionSettings.EnableExperimentalExtensions +} + +func (a *App) isExtensionValid(extensionId string) bool { + extensionIsValid := false + extensionIDs := a.Config().ExtensionSettings.AllowedExtensionsIDs + + for _, id := range extensionIDs { + if extensionId == id { + extensionIsValid = true + } + } + + return extensionIsValid +} + +func (a *App) ValidateExtension(extensionId string) *model.AppError { + enabled := a.isExtensionSupportEnabled() + if !enabled { + return model.NewAppError("completeSaml", "api.user.saml.extension_unsupported", nil, "", http.StatusInternalServerError) + } + + valid := a.isExtensionValid(extensionId) + if !valid { + params := map[string]interface{}{"ExtensionId": extensionId} + return model.NewAppError("completeSaml", "api.user.saml.invalid_extension", params, "", http.StatusInternalServerError) + } + + return nil +} + +func (a *App) SendMessageToExtension(w http.ResponseWriter, extensionId string, token string) *model.AppError { + var err error + var t *template.Template + if len(extensionId) == 0 { + return model.NewAppError("completeSaml", "api.user.saml.extension_id.app_error", nil, "", http.StatusInternalServerError) + } + + t = template.New("complete_saml_extension_body") + t, err = t.ParseFiles("templates/complete_saml_extension_body.html") + + if err != nil { + return model.NewAppError("completeSaml", "api.user.saml.app_error", nil, "err="+err.Error(), http.StatusInternalServerError) + } + + w.Header().Set("Content-Type", "text/html") + w.WriteHeader(http.StatusOK) + + var errMessage string + if len(token) == 0 { + loginError := model.NewAppError("completeSaml", "api.user.saml.app_error", nil, "", http.StatusInternalServerError) + errMessage = loginError.Message + } + + data := struct { + ExtensionId string + Token string + Error string + }{ + extensionId, + token, + errMessage, + } + + if err := t.Execute(w, data); err != nil { + return model.NewAppError("completeSaml", "api.user.saml.app_error", nil, "err="+err.Error(), http.StatusInternalServerError) + } + + return nil +} diff --git a/config/default.json b/config/default.json index cb60611ba54..cd85eda50d7 100644 --- a/config/default.json +++ b/config/default.json @@ -194,6 +194,10 @@ "LoginButtonBorderColor": "", "LoginButtonTextColor": "" }, + "ExtensionSettings": { + "EnableExperimentalExtensions": false, + "AllowedExtensionsIDs": [] + }, "RateLimitSettings": { "Enable": false, "PerSec": 10, diff --git a/i18n/en.json b/i18n/en.json index 9493a36b04f..aad77aeb565 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -2166,6 +2166,22 @@ "id": "api.user.send_deactivate_email_and_forget.failed.error", "translation": "Failed to send the deactivate account email successfully" }, + { + "id": "api.user.saml.app_error", + "translation": "Unable to process SAML login request." + }, + { + "id": "api.user.saml.extension_unsupported", + "translation": "Extensions are not supported." + }, + { + "id": "api.user.saml.extension_id.app_error", + "translation": "Invalid extension id" + }, + { + "id": "api.user.saml.invalid_extension", + "translation": "Extension with extension_id={{.ExtensionId}} is not supported." + }, { "id": "api.user.send_email_change_email_and_forget.error", "translation": "Failed to send email change notification email successfully" diff --git a/model/config.go b/model/config.go index 7c9643b754c..904668606d1 100644 --- a/model/config.go +++ b/model/config.go @@ -907,6 +907,21 @@ func (s *EmailSettings) SetDefaults() { } } +type ExtensionSettings struct { + EnableExperimentalExtensions *bool + AllowedExtensionsIDs []string +} + +func (s *ExtensionSettings) SetDefaults() { + if s.EnableExperimentalExtensions == nil { + s.EnableExperimentalExtensions = NewBool(false) + } + + if s.AllowedExtensionsIDs == nil { + s.AllowedExtensionsIDs = []string{} + } +} + type RateLimitSettings struct { Enable *bool PerSec *int @@ -1870,6 +1885,7 @@ type Config struct { PasswordSettings PasswordSettings FileSettings FileSettings EmailSettings EmailSettings + ExtensionSettings ExtensionSettings RateLimitSettings RateLimitSettings PrivacySettings PrivacySettings SupportSettings SupportSettings @@ -1967,6 +1983,7 @@ func (o *Config) SetDefaults() { o.MessageExportSettings.SetDefaults() o.TimezoneSettings.SetDefaults() o.DisplaySettings.SetDefaults() + o.ExtensionSettings.SetDefaults() } func (o *Config) IsValid() *AppError { diff --git a/model/oauth.go b/model/oauth.go index c92b1ec415a..0ea1aa4e25e 100644 --- a/model/oauth.go +++ b/model/oauth.go @@ -17,6 +17,7 @@ const ( OAUTH_ACTION_EMAIL_TO_SSO = "email_to_sso" OAUTH_ACTION_SSO_TO_EMAIL = "sso_to_email" OAUTH_ACTION_MOBILE = "mobile" + OAUTH_ACTION_CLIENT = "client" ) type OAuthApp struct { diff --git a/templates/complete_saml_extension_body.html b/templates/complete_saml_extension_body.html new file mode 100644 index 00000000000..0c99ecbdc01 --- /dev/null +++ b/templates/complete_saml_extension_body.html @@ -0,0 +1,30 @@ +{{define "complete_saml_extension_body"}} + + + + + + + Login Successful + + + +{{end}} diff --git a/web/saml.go b/web/saml.go index 9cf93bdabcf..f5400a37825 100644 --- a/web/saml.go +++ b/web/saml.go @@ -32,6 +32,7 @@ func loginWithSaml(c *Context, w http.ResponseWriter, r *http.Request) { } action := r.URL.Query().Get("action") redirectTo := r.URL.Query().Get("redirect_to") + extensionId := r.URL.Query().Get("extension_id") relayProps := map[string]string{} relayState := "" @@ -47,6 +48,15 @@ func loginWithSaml(c *Context, w http.ResponseWriter, r *http.Request) { relayProps["redirect_to"] = redirectTo } + if len(extensionId) != 0 { + relayProps["extension_id"] = extensionId + err := c.App.ValidateExtension(extensionId) + if err != nil { + c.Err = err + return + } + } + if len(relayProps) > 0 { relayState = b64.StdEncoding.EncodeToString([]byte(model.MapToJson(relayProps))) } @@ -141,6 +151,13 @@ func completeSaml(c *Context, w http.ResponseWriter, r *http.Request) { if action == model.OAUTH_ACTION_MOBILE { ReturnStatusOK(w) + } else if action == model.OAUTH_ACTION_CLIENT { + err = c.App.SendMessageToExtension(w, relayProps["extension_id"], c.Session.Token) + + if err != nil { + c.Err = err + return + } } else { http.Redirect(w, r, c.GetSiteURLHeader(), http.StatusFound) }