* using token

---------

Co-authored-by: Mattermost Build <build@mattermost.com>
This commit is contained in:
Ben Cooke 2024-02-09 11:17:28 -05:00 committed by GitHub
parent 7e317c7568
commit fe47e75b92
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 105 additions and 2 deletions

View File

@ -4328,6 +4328,23 @@ func TestSwitchAccount(t *testing.T) {
_, resp, err = th.Client.SwitchAccountType(context.Background(), sr) _, resp, err = th.Client.SwitchAccountType(context.Background(), sr)
require.Error(t, err) require.Error(t, err)
CheckUnauthorizedStatus(t, resp) CheckUnauthorizedStatus(t, resp)
sr = &model.SwitchRequest{
CurrentService: model.UserAuthServiceEmail,
NewService: model.UserAuthServiceSaml,
Email: th.BasicUser.Email,
Password: th.BasicUser.Password,
}
link, _, err = th.Client.SwitchAccountType(context.Background(), sr)
require.NoError(t, err)
values, parseErr := url.ParseQuery(link)
require.NoError(t, parseErr)
appToken, tokenErr := th.App.Srv().Store().Token().GetByToken(values.Get("email_token"))
require.NoError(t, tokenErr)
require.Equal(t, th.BasicUser.Email, appToken.Extra)
} }
func assertToken(t *testing.T, th *TestHelper, token *model.UserAccessToken, expectedUserId string) { func assertToken(t *testing.T, th *TestHelper, token *model.UserAccessToken, expectedUserId string) {

View File

@ -527,6 +527,7 @@ type AppIface interface {
CreatePostMissingChannel(c request.CTX, post *model.Post, triggerWebhooks bool, setOnline bool) (*model.Post, *model.AppError) CreatePostMissingChannel(c request.CTX, post *model.Post, triggerWebhooks bool, setOnline bool) (*model.Post, *model.AppError)
CreateRetentionPolicy(policy *model.RetentionPolicyWithTeamAndChannelIDs) (*model.RetentionPolicyWithTeamAndChannelCounts, *model.AppError) CreateRetentionPolicy(policy *model.RetentionPolicyWithTeamAndChannelIDs) (*model.RetentionPolicyWithTeamAndChannelCounts, *model.AppError)
CreateRole(role *model.Role) (*model.Role, *model.AppError) CreateRole(role *model.Role) (*model.Role, *model.AppError)
CreateSamlRelayToken(extra string) (*model.Token, *model.AppError)
CreateScheme(scheme *model.Scheme) (*model.Scheme, *model.AppError) CreateScheme(scheme *model.Scheme) (*model.Scheme, *model.AppError)
CreateSession(c request.CTX, session *model.Session) (*model.Session, *model.AppError) CreateSession(c request.CTX, session *model.Session) (*model.Session, *model.AppError)
CreateSidebarCategory(c request.CTX, userID, teamID string, newCategory *model.SidebarCategoryWithChannels) (*model.SidebarCategoryWithChannels, *model.AppError) CreateSidebarCategory(c request.CTX, userID, teamID string, newCategory *model.SidebarCategoryWithChannels) (*model.SidebarCategoryWithChannels, *model.AppError)
@ -785,6 +786,7 @@ type AppIface interface {
GetRoleByName(ctx context.Context, name string) (*model.Role, *model.AppError) GetRoleByName(ctx context.Context, name string) (*model.Role, *model.AppError)
GetRolesByNames(names []string) ([]*model.Role, *model.AppError) GetRolesByNames(names []string) ([]*model.Role, *model.AppError)
GetSamlCertificateStatus() *model.SamlCertificateStatus GetSamlCertificateStatus() *model.SamlCertificateStatus
GetSamlEmailToken(token string) (*model.Token, *model.AppError)
GetSamlMetadata(c request.CTX) (string, *model.AppError) GetSamlMetadata(c request.CTX) (string, *model.AppError)
GetSamlMetadataFromIdp(idpMetadataURL string) (*model.SamlMetadataResponse, *model.AppError) GetSamlMetadataFromIdp(idpMetadataURL string) (*model.SamlMetadataResponse, *model.AppError)
GetSanitizeOptions(asAdmin bool) map[string]bool GetSanitizeOptions(asAdmin bool) map[string]bool

View File

@ -958,7 +958,12 @@ func (a *App) SwitchEmailToOAuth(c request.CTX, w http.ResponseWriter, r *http.R
stateProps["email"] = email stateProps["email"] = email
if service == model.UserAuthServiceSaml { if service == model.UserAuthServiceSaml {
return a.GetSiteURL() + "/login/sso/saml?action=" + model.OAuthActionEmailToSSO + "&email=" + utils.URLEncode(email), nil samlToken, samlErr := a.CreateSamlRelayToken(email)
if samlErr != nil {
return "", samlErr
}
return a.GetSiteURL() + "/login/sso/saml?action=" + model.OAuthActionEmailToSSO + "&email_token=" + utils.URLEncode(samlToken.Token), nil
} }
authURL, err := a.GetAuthorizationCode(c, w, r, service, stateProps, "") authURL, err := a.GetAuthorizationCode(c, w, r, service, stateProps, "")

View File

@ -2504,6 +2504,28 @@ func (a *OpenTracingAppLayer) CreateRole(role *model.Role) (*model.Role, *model.
return resultVar0, resultVar1 return resultVar0, resultVar1
} }
func (a *OpenTracingAppLayer) CreateSamlRelayToken(extra string) (*model.Token, *model.AppError) {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.CreateSamlRelayToken")
a.ctx = newCtx
a.app.Srv().Store().SetContext(newCtx)
defer func() {
a.app.Srv().Store().SetContext(origCtx)
a.ctx = origCtx
}()
defer span.Finish()
resultVar0, resultVar1 := a.app.CreateSamlRelayToken(extra)
if resultVar1 != nil {
span.LogFields(spanlog.Error(resultVar1))
ext.Error.Set(span, true)
}
return resultVar0, resultVar1
}
func (a *OpenTracingAppLayer) CreateScheme(scheme *model.Scheme) (*model.Scheme, *model.AppError) { func (a *OpenTracingAppLayer) CreateScheme(scheme *model.Scheme) (*model.Scheme, *model.AppError) {
origCtx := a.ctx origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.CreateScheme") span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.CreateScheme")
@ -9038,6 +9060,28 @@ func (a *OpenTracingAppLayer) GetSamlCertificateStatus() *model.SamlCertificateS
return resultVar0 return resultVar0
} }
func (a *OpenTracingAppLayer) GetSamlEmailToken(token string) (*model.Token, *model.AppError) {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetSamlEmailToken")
a.ctx = newCtx
a.app.Srv().Store().SetContext(newCtx)
defer func() {
a.app.Srv().Store().SetContext(origCtx)
a.ctx = origCtx
}()
defer span.Finish()
resultVar0, resultVar1 := a.app.GetSamlEmailToken(token)
if resultVar1 != nil {
span.LogFields(spanlog.Error(resultVar1))
ext.Error.Set(span, true)
}
return resultVar0, resultVar1
}
func (a *OpenTracingAppLayer) GetSamlMetadata(c request.CTX) (string, *model.AppError) { func (a *OpenTracingAppLayer) GetSamlMetadata(c request.CTX) (string, *model.AppError) {
origCtx := a.ctx origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetSamlMetadata") span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetSamlMetadata")

View File

@ -7,6 +7,7 @@ import (
"crypto/x509" "crypto/x509"
"encoding/pem" "encoding/pem"
"encoding/xml" "encoding/xml"
"errors"
"fmt" "fmt"
"io" "io"
"mime/multipart" "mime/multipart"
@ -296,3 +297,32 @@ func (a *App) ResetSamlAuthDataToEmail(includeDeleted bool, dryRun bool, userIDs
} }
return return
} }
func (a *App) CreateSamlRelayToken(extra string) (*model.Token, *model.AppError) {
token := model.NewToken(model.TokenTypeSaml, extra)
if err := a.Srv().Store().Token().Save(token); err != nil {
var appErr *model.AppError
switch {
case errors.As(err, &appErr):
return nil, appErr
default:
return nil, model.NewAppError("CreateSamlRelayToken", "app.recover.save.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
}
return token, nil
}
func (a *App) GetSamlEmailToken(token string) (*model.Token, *model.AppError) {
mToken, err := a.Srv().Store().Token().GetByToken(token)
if err != nil {
return nil, model.NewAppError("GetSamlEmailToken", "api.saml.invalid_email_token.app_error", nil, "", http.StatusBadRequest).Wrap(err)
}
if mToken.Type != model.TokenTypeSaml {
return nil, model.NewAppError("GetSamlEmailToken", "api.saml.invalid_email_token.app_error", nil, "", http.StatusBadRequest)
}
return mToken, nil
}

View File

@ -47,7 +47,7 @@ func loginWithSaml(c *Context, w http.ResponseWriter, r *http.Request) {
relayProps["team_id"] = teamId relayProps["team_id"] = teamId
relayProps["action"] = action relayProps["action"] = action
if action == model.OAuthActionEmailToSSO { if action == model.OAuthActionEmailToSSO {
relayProps["email"] = r.URL.Query().Get("email") relayProps["email_token"] = r.URL.Query().Get("email_token")
} }
} }

View File

@ -2718,6 +2718,10 @@
"id": "api.roles.patch_roles.not_allowed_permission.error", "id": "api.roles.patch_roles.not_allowed_permission.error",
"translation": "One or more of the following permissions that you are trying to add or remove is not allowed" "translation": "One or more of the following permissions that you are trying to add or remove is not allowed"
}, },
{
"id": "api.saml.invalid_email_token.app_error",
"translation": "Invalid email_token"
},
{ {
"id": "api.scheme.create_scheme.license.error", "id": "api.scheme.create_scheme.license.error",
"translation": "Your license does not support creating permissions schemes." "translation": "Your license does not support creating permissions schemes."

View File

@ -11,6 +11,7 @@ const (
TokenSize = 64 TokenSize = 64
MaxTokenExipryTime = 1000 * 60 * 60 * 48 // 48 hour MaxTokenExipryTime = 1000 * 60 * 60 * 48 // 48 hour
TokenTypeOAuth = "oauth" TokenTypeOAuth = "oauth"
TokenTypeSaml = "saml"
) )
type Token struct { type Token struct {