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 0000000000..d0226bba58 --- /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 cb60611ba5..cd85eda50d 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 9493a36b04..aad77aeb56 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 7c9643b754..904668606d 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 c92b1ec415..0ea1aa4e25 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 0000000000..0c99ecbdc0 --- /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 9cf93bdabc..f5400a3782 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) }