* implement openid connect

* update error strings

* handle OpenIdSetting.Secret as FAKE SETTING

* add openid to telemetry

* update config defaults, add telemetry

* fix bug with Office365

* Retrieve Office365 AuthData from IdToken

* fix linter

* add feature flag, reset defaults for config

* fix build error

* fix unit tests

* add authentication permission to Feature
Flags

* turn off feature flag

* set default button color

* set default button color only on openid

* fix for merging FeatureFlags in config

* remove feature flag

* revert config changes

* remove debug statements

Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
Этот коммит содержится в:
Scott Bishel
2020-12-08 19:58:37 -07:00
коммит произвёл GitHub
родитель 2a71fc5ee2
Коммит f548ecbee1
22 изменённых файлов: 258 добавлений и 138 удалений

Просмотреть файл

@@ -1691,11 +1691,12 @@ func login(c *Context, w http.ResponseWriter, r *http.Request) {
enableUsername := *config.EmailSettings.EnableSignInWithUsername
enableEmail := *config.EmailSettings.EnableSignInWithEmail
samlEnabled := *config.SamlSettings.Enable
gitlabEnabled := *config.GetSSOService("gitlab").Enable
googleEnabled := *config.GetSSOService("google").Enable
gitlabEnabled := *config.GitLabSettings.Enable
openidEnabled := *config.OpenIdSettings.Enable
googleEnabled := *config.GoogleSettings.Enable
office365Enabled := *config.Office365Settings.Enable
if samlEnabled || gitlabEnabled || googleEnabled || office365Enabled {
if samlEnabled || gitlabEnabled || googleEnabled || office365Enabled || openidEnabled {
c.Err = model.NewAppError("login", "api.user.login.invalid_credentials_sso", nil, "", http.StatusUnauthorized)
return
}

Просмотреть файл

@@ -376,7 +376,7 @@ type AppIface interface {
AttachDeviceId(sessionId string, deviceId string, expiresAt int64) *model.AppError
AttachSessionCookies(w http.ResponseWriter, r *http.Request)
AuthenticateUserForLogin(id, loginId, password, mfaToken, cwsToken string, ldapOnly bool) (user *model.User, err *model.AppError)
AuthorizeOAuthUser(w http.ResponseWriter, r *http.Request, service, code, state, redirectUri string) (io.ReadCloser, string, map[string]string, *model.AppError)
AuthorizeOAuthUser(w http.ResponseWriter, r *http.Request, service, code, state, redirectUri string) (io.ReadCloser, string, map[string]string, *model.User, *model.AppError)
AutocompleteChannels(teamId string, term string) (*model.ChannelList, *model.AppError)
AutocompleteChannelsForSearch(teamId string, userId string, term string) (*model.ChannelList, *model.AppError)
AutocompleteUsersInChannel(teamId string, channelId string, term string, options *model.UserSearchOptions) (*model.UserAutocompleteInChannel, *model.AppError)
@@ -412,8 +412,8 @@ type AppIface interface {
Cluster() einterfaces.ClusterInterface
CompareAndDeletePluginKey(pluginId string, key string, oldValue []byte) (bool, *model.AppError)
CompareAndSetPluginKey(pluginId string, key string, oldValue, newValue []byte) (bool, *model.AppError)
CompleteOAuth(service string, body io.ReadCloser, teamId string, props map[string]string) (*model.User, *model.AppError)
CompleteSwitchWithOAuth(service string, userData io.Reader, email string) (*model.User, *model.AppError)
CompleteOAuth(service string, body io.ReadCloser, teamId string, props map[string]string, tokenUser *model.User) (*model.User, *model.AppError)
CompleteSwitchWithOAuth(service string, userData io.Reader, email string, tokenUser *model.User) (*model.User, *model.AppError)
Compliance() einterfaces.ComplianceInterface
Config() *model.Config
Context() context.Context
@@ -429,7 +429,7 @@ type AppIface interface {
CreateJob(job *model.Job) (*model.Job, *model.AppError)
CreateOAuthApp(app *model.OAuthApp) (*model.OAuthApp, *model.AppError)
CreateOAuthStateToken(extra string) (*model.Token, *model.AppError)
CreateOAuthUser(service string, userData io.Reader, teamId string) (*model.User, *model.AppError)
CreateOAuthUser(service string, userData io.Reader, teamId string, tokenUser *model.User) (*model.User, *model.AppError)
CreateOutgoingWebhook(hook *model.OutgoingWebhook) (*model.OutgoingWebhook, *model.AppError)
CreatePasswordRecoveryToken(userId, email string) (*model.Token, *model.AppError)
CreatePost(post *model.Post, channel *model.Channel, triggerWebhooks, setOnline bool) (savedPost *model.Post, err *model.AppError)
@@ -778,7 +778,7 @@ type AppIface interface {
ListPluginKeys(pluginId string, page, perPage int) ([]string, *model.AppError)
ListTeamCommands(teamId string) ([]*model.Command, *model.AppError)
Log() *mlog.Logger
LoginByOAuth(service string, userData io.Reader, teamId string) (*model.User, *model.AppError)
LoginByOAuth(service string, userData io.Reader, teamId string, tokenUser *model.User) (*model.User, *model.AppError)
MakePermissionError(permissions []*model.Permission) *model.AppError
MarkChannelsAsViewed(channelIds []string, userId string, currentSessionId string) (map[string]int64, *model.AppError)
MaxPostSize() int
@@ -982,7 +982,7 @@ type AppIface interface {
UpdateLastActivityAtIfNeeded(session model.Session)
UpdateMfa(activate bool, userId, token string) *model.AppError
UpdateMobileAppBadge(userId string)
UpdateOAuthUserAttrs(userData io.Reader, user *model.User, provider einterfaces.OauthProvider, service string) *model.AppError
UpdateOAuthUserAttrs(userData io.Reader, user *model.User, provider einterfaces.OauthProvider, service string, tokenUser *model.User) *model.AppError
UpdateOauthApp(oldApp, updatedApp *model.OAuthApp) (*model.OAuthApp, *model.AppError)
UpdateOutgoingWebhook(oldHook, updatedHook *model.OutgoingWebhook) (*model.OutgoingWebhook, *model.AppError)
UpdatePassword(user *model.User, newPassword string) *model.AppError

Просмотреть файл

@@ -26,6 +26,7 @@ import (
const (
OAUTH_COOKIE_MAX_AGE_SECONDS = 30 * 60 // 30 minutes
COOKIE_OAUTH = "MMOAUTH"
OPENID_SCOPE = "openid"
)
func (a *App) CreateOAuthApp(app *model.OAuthApp) (*model.OAuthApp, *model.AppError) {
@@ -544,52 +545,69 @@ func (a *App) RevokeAccessToken(token string) *model.AppError {
return nil
}
func (a *App) CompleteOAuth(service string, body io.ReadCloser, teamId string, props map[string]string) (*model.User, *model.AppError) {
func (a *App) CompleteOAuth(service string, body io.ReadCloser, teamId string, props map[string]string, tokenUser *model.User) (*model.User, *model.AppError) {
defer body.Close()
action := props["action"]
switch action {
case model.OAUTH_ACTION_SIGNUP:
return a.CreateOAuthUser(service, body, teamId)
return a.CreateOAuthUser(service, body, teamId, tokenUser)
case model.OAUTH_ACTION_LOGIN:
return a.LoginByOAuth(service, body, teamId)
return a.LoginByOAuth(service, body, teamId, tokenUser)
case model.OAUTH_ACTION_EMAIL_TO_SSO:
return a.CompleteSwitchWithOAuth(service, body, props["email"])
return a.CompleteSwitchWithOAuth(service, body, props["email"], tokenUser)
case model.OAUTH_ACTION_SSO_TO_EMAIL:
return a.LoginByOAuth(service, body, teamId)
return a.LoginByOAuth(service, body, teamId, tokenUser)
default:
return a.LoginByOAuth(service, body, teamId)
return a.LoginByOAuth(service, body, teamId, tokenUser)
}
}
func (a *App) LoginByOAuth(service string, userData io.Reader, teamId string) (*model.User, *model.AppError) {
provider := einterfaces.GetOauthProvider(service)
func (a *App) getSSOProvider(service string) (einterfaces.OauthProvider, *model.AppError) {
sso := a.Config().GetSSOService(service)
if sso == nil || !*sso.Enable {
return nil, model.NewAppError("getSSOProvider", "api.user.authorize_oauth_user.unsupported.app_error", nil, "service="+service, http.StatusNotImplemented)
}
providerType := service
if strings.Contains(*sso.Scope, OPENID_SCOPE) {
providerType = model.SERVICE_OPENID
}
provider := einterfaces.GetOauthProvider(providerType)
if provider == nil {
return nil, model.NewAppError("LoginByOAuth", "api.user.login_by_oauth.not_available.app_error",
return nil, model.NewAppError("getSSOProvider", "api.user.login_by_oauth.not_available.app_error",
map[string]interface{}{"Service": strings.Title(service)}, "", http.StatusNotImplemented)
}
return provider, nil
}
func (a *App) LoginByOAuth(service string, userData io.Reader, teamId string, tokenUser *model.User) (*model.User, *model.AppError) {
provider, e := a.getSSOProvider(service)
if e != nil {
return nil, e
}
buf := bytes.Buffer{}
if _, err := buf.ReadFrom(userData); err != nil {
return nil, model.NewAppError("LoginByOAuth", "api.user.login_by_oauth.parse.app_error",
return nil, model.NewAppError("LoginByOAuth2", "api.user.login_by_oauth.parse.app_error",
map[string]interface{}{"Service": service}, "", http.StatusBadRequest)
}
authUser, err1 := provider.GetUserFromJson(bytes.NewReader(buf.Bytes()))
authUser, err1 := provider.GetUserFromJson(bytes.NewReader(buf.Bytes()), tokenUser)
if err1 != nil {
return nil, model.NewAppError("LoginByOAuth", "api.user.login_by_oauth.parse.app_error",
map[string]interface{}{"Service": service}, err1.Error(), http.StatusBadRequest)
}
if *authUser.AuthData == "" {
return nil, model.NewAppError("LoginByOAuth", "api.user.login_by_oauth.parse.app_error",
return nil, model.NewAppError("LoginByOAuth3", "api.user.login_by_oauth.parse.app_error",
map[string]interface{}{"Service": service}, "", http.StatusBadRequest)
}
user, err := a.GetUserByAuth(model.NewString(*authUser.AuthData), service)
if err != nil {
if err.Id == MISSING_AUTH_ACCOUNT_ERROR {
user, err = a.CreateOAuthUser(service, bytes.NewReader(buf.Bytes()), teamId)
user, err = a.CreateOAuthUser(service, bytes.NewReader(buf.Bytes()), teamId, tokenUser)
} else {
return nil, err
}
@@ -601,7 +619,7 @@ func (a *App) LoginByOAuth(service string, userData io.Reader, teamId string) (*
return nil, model.NewAppError("loginByOAuth", "api.user.login_by_oauth.bot_login_forbidden.app_error", nil, "", http.StatusForbidden)
}
if err = a.UpdateOAuthUserAttrs(bytes.NewReader(buf.Bytes()), user, provider, service); err != nil {
if err = a.UpdateOAuthUserAttrs(bytes.NewReader(buf.Bytes()), user, provider, service, tokenUser); err != nil {
return nil, err
}
if len(teamId) > 0 {
@@ -616,18 +634,17 @@ func (a *App) LoginByOAuth(service string, userData io.Reader, teamId string) (*
return user, nil
}
func (a *App) CompleteSwitchWithOAuth(service string, userData io.Reader, email string) (*model.User, *model.AppError) {
provider := einterfaces.GetOauthProvider(service)
if provider == nil {
return nil, model.NewAppError("CompleteSwitchWithOAuth", "api.user.complete_switch_with_oauth.unavailable.app_error",
map[string]interface{}{"Service": strings.Title(service)}, "", http.StatusNotImplemented)
func (a *App) CompleteSwitchWithOAuth(service string, userData io.Reader, email string, tokenUser *model.User) (*model.User, *model.AppError) {
provider, e := a.getSSOProvider(service)
if e != nil {
return nil, e
}
if email == "" {
return nil, model.NewAppError("CompleteSwitchWithOAuth", "api.user.complete_switch_with_oauth.blank_email.app_error", nil, "", http.StatusBadRequest)
}
ssoUser, err1 := provider.GetUserFromJson(userData)
ssoUser, err1 := provider.GetUserFromJson(userData, tokenUser)
if err1 != nil {
return nil, model.NewAppError("CompleteSwitchWithOAuth", "api.user.complete_switch_with_oauth.parse.app_error",
map[string]interface{}{"Service": service}, err1.Error(), http.StatusBadRequest)
@@ -696,9 +713,14 @@ func (a *App) GetOAuthStateToken(token string) (*model.Token, *model.AppError) {
}
func (a *App) GetAuthorizationCode(w http.ResponseWriter, r *http.Request, service string, props map[string]string, loginHint string) (string, *model.AppError) {
sso := a.Config().GetSSOService(service)
if sso == nil || !*sso.Enable {
return "", model.NewAppError("GetAuthorizationCode", "api.user.get_authorization_code.unsupported.app_error", nil, "service="+service, http.StatusNotImplemented)
provider, e := a.getSSOProvider(service)
if e != nil {
return "", e
}
sso, e2 := provider.GetSSOSettings(a.Config(), service)
if e2 != nil {
return "", model.NewAppError("GetAuthorizationCode.GetSSOSettings", "api.user.get_authorization_code.endpoint.app_error", nil, e.Error(), http.StatusNotImplemented)
}
secure := false
@@ -755,40 +777,44 @@ func (a *App) GetAuthorizationCode(w http.ResponseWriter, r *http.Request, servi
return authUrl, nil
}
func (a *App) AuthorizeOAuthUser(w http.ResponseWriter, r *http.Request, service, code, state, redirectUri string) (io.ReadCloser, string, map[string]string, *model.AppError) {
sso := a.Config().GetSSOService(service)
if sso == nil || !*sso.Enable {
return nil, "", nil, model.NewAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.unsupported.app_error", nil, "service="+service, http.StatusNotImplemented)
func (a *App) AuthorizeOAuthUser(w http.ResponseWriter, r *http.Request, service, code, state, redirectUri string) (io.ReadCloser, string, map[string]string, *model.User, *model.AppError) {
provider, e := a.getSSOProvider(service)
if e != nil {
return nil, "", nil, nil, e
}
sso, e2 := provider.GetSSOSettings(a.Config(), service)
if e2 != nil {
return nil, "", nil, nil, model.NewAppError("GetAuthorizationCode.GetSSOSettings", "api.user.get_authorization_code.endpoint.app_error", nil, e.Error(), http.StatusNotImplemented)
}
b, strErr := b64.StdEncoding.DecodeString(state)
if strErr != nil {
return nil, "", nil, model.NewAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.invalid_state.app_error", nil, strErr.Error(), http.StatusBadRequest)
return nil, "", nil, nil, model.NewAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.invalid_state.app_error", nil, strErr.Error(), http.StatusBadRequest)
}
stateStr := string(b)
stateProps := model.MapFromJson(strings.NewReader(stateStr))
expectedToken, appErr := a.GetOAuthStateToken(stateProps["token"])
if appErr != nil {
return nil, "", stateProps, appErr
return nil, "", stateProps, nil, appErr
}
stateEmail := stateProps["email"]
stateAction := stateProps["action"]
if stateAction == model.OAUTH_ACTION_EMAIL_TO_SSO && stateEmail == "" {
return nil, "", stateProps, model.NewAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.invalid_state.app_error", nil, "", http.StatusBadRequest)
return nil, "", stateProps, nil, model.NewAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.invalid_state.app_error", nil, "", http.StatusBadRequest)
}
cookie, cookieErr := r.Cookie(COOKIE_OAUTH)
if cookieErr != nil {
return nil, "", stateProps, model.NewAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.invalid_state.app_error", nil, "", http.StatusBadRequest)
return nil, "", stateProps, nil, model.NewAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.invalid_state.app_error", nil, "", http.StatusBadRequest)
}
expectedTokenExtra := generateOAuthStateTokenExtra(stateEmail, stateAction, cookie.Value)
if expectedTokenExtra != expectedToken.Extra {
return nil, "", stateProps, model.NewAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.invalid_state.app_error", nil, "", http.StatusBadRequest)
return nil, "", stateProps, nil, model.NewAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.invalid_state.app_error", nil, "", http.StatusBadRequest)
}
appErr = a.DeleteToken(expectedToken)
@@ -819,7 +845,7 @@ func (a *App) AuthorizeOAuthUser(w http.ResponseWriter, r *http.Request, service
req, requestErr := http.NewRequest("POST", *sso.TokenEndpoint, strings.NewReader(p.Encode()))
if requestErr != nil {
return nil, "", stateProps, model.NewAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.token_failed.app_error", nil, requestErr.Error(), http.StatusInternalServerError)
return nil, "", stateProps, nil, model.NewAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.token_failed.app_error", nil, requestErr.Error(), http.StatusInternalServerError)
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
@@ -827,31 +853,39 @@ func (a *App) AuthorizeOAuthUser(w http.ResponseWriter, r *http.Request, service
resp, err := a.HTTPService().MakeClient(true).Do(req)
if err != nil {
return nil, "", stateProps, model.NewAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.token_failed.app_error", nil, err.Error(), http.StatusInternalServerError)
return nil, "", stateProps, nil, model.NewAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.token_failed.app_error", nil, err.Error(), http.StatusInternalServerError)
}
defer resp.Body.Close()
var buf bytes.Buffer
tee := io.TeeReader(resp.Body, &buf)
ar := model.AccessResponseFromJson(tee)
if ar == nil || resp.StatusCode != http.StatusOK {
return nil, "", stateProps, model.NewAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.bad_response.app_error", nil, fmt.Sprintf("response_body=%s, status_code=%d", buf.String(), resp.StatusCode), http.StatusInternalServerError)
return nil, "", stateProps, nil, model.NewAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.bad_response.app_error", nil, fmt.Sprintf("response_body=%s, status_code=%d", buf.String(), resp.StatusCode), http.StatusInternalServerError)
}
if strings.ToLower(ar.TokenType) != model.ACCESS_TOKEN_TYPE {
return nil, "", stateProps, model.NewAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.bad_token.app_error", nil, "token_type="+ar.TokenType+", response_body="+buf.String(), http.StatusInternalServerError)
return nil, "", stateProps, nil, model.NewAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.bad_token.app_error", nil, "token_type="+ar.TokenType+", response_body="+buf.String(), http.StatusInternalServerError)
}
if len(ar.AccessToken) == 0 {
return nil, "", stateProps, model.NewAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.missing.app_error", nil, "response_body="+buf.String(), http.StatusInternalServerError)
return nil, "", stateProps, nil, model.NewAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.missing.app_error", nil, "response_body="+buf.String(), http.StatusInternalServerError)
}
p = url.Values{}
p.Set("access_token", ar.AccessToken)
var userFromToken *model.User
if ar.IdToken != "" {
userFromToken, err = provider.GetUserFromIdToken(ar.IdToken)
if err != nil {
return nil, "", stateProps, nil, model.NewAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.token_failed.app_error", nil, e.Error(), http.StatusInternalServerError)
}
}
req, requestErr = http.NewRequest("GET", *sso.UserApiEndpoint, strings.NewReader(""))
if requestErr != nil {
return nil, "", stateProps, model.NewAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.service.app_error", map[string]interface{}{"Service": service}, requestErr.Error(), http.StatusInternalServerError)
return nil, "", stateProps, nil, model.NewAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.service.app_error", map[string]interface{}{"Service": service}, requestErr.Error(), http.StatusInternalServerError)
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
@@ -860,7 +894,7 @@ func (a *App) AuthorizeOAuthUser(w http.ResponseWriter, r *http.Request, service
resp, err = a.HTTPService().MakeClient(true).Do(req)
if err != nil {
return nil, "", stateProps, model.NewAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.service.app_error", map[string]interface{}{"Service": service}, err.Error(), http.StatusInternalServerError)
return nil, "", stateProps, nil, model.NewAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.service.app_error", map[string]interface{}{"Service": service}, err.Error(), http.StatusInternalServerError)
} else if resp.StatusCode != http.StatusOK {
defer resp.Body.Close()
@@ -868,18 +902,18 @@ func (a *App) AuthorizeOAuthUser(w http.ResponseWriter, r *http.Request, service
bodyBytes, _ := ioutil.ReadAll(resp.Body)
bodyString := string(bodyBytes)
mlog.Error("Error getting OAuth user", mlog.String("body_string", bodyString))
mlog.Error("Error getting OAuth user", mlog.Int("response", resp.StatusCode), mlog.String("body_string", bodyString))
if service == model.SERVICE_GITLAB && resp.StatusCode == http.StatusForbidden && strings.Contains(bodyString, "Terms of Service") {
// Return a nicer error when the user hasn't accepted GitLab's terms of service
return nil, "", stateProps, model.NewAppError("AuthorizeOAuthUser", "oauth.gitlab.tos.error", nil, "", http.StatusBadRequest)
return nil, "", stateProps, nil, model.NewAppError("AuthorizeOAuthUser", "oauth.gitlab.tos.error", nil, "", http.StatusBadRequest)
}
return nil, "", stateProps, model.NewAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.response.app_error", nil, "response_body="+bodyString, http.StatusInternalServerError)
return nil, "", stateProps, nil, model.NewAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.response.app_error", nil, "response_body="+bodyString, http.StatusInternalServerError)
}
// Note that resp.Body is not closed here, so it must be closed by the caller
return resp.Body, teamId, stateProps, nil
return resp.Body, teamId, stateProps, userFromToken, nil
}
func (a *App) SwitchEmailToOAuth(w http.ResponseWriter, r *http.Request, email, password, code, service string) (string, *model.AppError) {

Просмотреть файл

@@ -191,7 +191,7 @@ func TestAuthorizeOAuthUser(t *testing.T) {
th := setup(t, false, true, true, "")
defer th.TearDown()
_, _, _, err := th.App.AuthorizeOAuthUser(nil, nil, model.SERVICE_GITLAB, "", "", "")
_, _, _, _, err := th.App.AuthorizeOAuthUser(nil, nil, model.SERVICE_GITLAB, "", "", "")
require.NotNil(t, err)
assert.Equal(t, "api.user.authorize_oauth_user.unsupported.app_error", err.Id)
})
@@ -202,7 +202,7 @@ func TestAuthorizeOAuthUser(t *testing.T) {
state := "!"
_, _, _, err := th.App.AuthorizeOAuthUser(nil, nil, model.SERVICE_GITLAB, "", state, "")
_, _, _, _, err := th.App.AuthorizeOAuthUser(nil, nil, model.SERVICE_GITLAB, "", state, "")
require.NotNil(t, err)
assert.Equal(t, "api.user.authorize_oauth_user.invalid_state.app_error", err.Id)
})
@@ -215,7 +215,7 @@ func TestAuthorizeOAuthUser(t *testing.T) {
"token": model.NewId(),
})))
_, _, _, err := th.App.AuthorizeOAuthUser(nil, nil, model.SERVICE_GITLAB, "", state, "")
_, _, _, _, err := th.App.AuthorizeOAuthUser(nil, nil, model.SERVICE_GITLAB, "", state, "")
require.NotNil(t, err)
assert.Equal(t, "api.oauth.invalid_state_token.app_error", err.Id)
assert.NotEqual(t, "", err.DetailedError)
@@ -230,7 +230,7 @@ func TestAuthorizeOAuthUser(t *testing.T) {
state := makeState(token)
_, _, _, err := th.App.AuthorizeOAuthUser(nil, nil, model.SERVICE_GITLAB, "", state, "")
_, _, _, _, err := th.App.AuthorizeOAuthUser(nil, nil, model.SERVICE_GITLAB, "", state, "")
require.NotNil(t, err)
assert.Equal(t, "api.oauth.invalid_state_token.app_error", err.Id)
assert.Equal(t, "", err.DetailedError)
@@ -253,7 +253,7 @@ func TestAuthorizeOAuthUser(t *testing.T) {
"token": token.Token,
})))
_, _, _, err = th.App.AuthorizeOAuthUser(nil, nil, model.SERVICE_GITLAB, "", state, "")
_, _, _, _, err = th.App.AuthorizeOAuthUser(nil, nil, model.SERVICE_GITLAB, "", state, "")
require.NotNil(t, err)
assert.Equal(t, "api.user.authorize_oauth_user.invalid_state.app_error", err.Id)
})
@@ -266,7 +266,7 @@ func TestAuthorizeOAuthUser(t *testing.T) {
request := makeRequest(t, "")
state := makeState(makeToken(th, cookie))
_, _, _, err := th.App.AuthorizeOAuthUser(nil, request, model.SERVICE_GITLAB, "", state, "")
_, _, _, _, err := th.App.AuthorizeOAuthUser(nil, request, model.SERVICE_GITLAB, "", state, "")
require.NotNil(t, err)
assert.Equal(t, "api.user.authorize_oauth_user.invalid_state.app_error", err.Id)
})
@@ -283,7 +283,7 @@ func TestAuthorizeOAuthUser(t *testing.T) {
request := makeRequest(t, cookie)
state := makeState(token)
_, _, _, err = th.App.AuthorizeOAuthUser(nil, request, model.SERVICE_GITLAB, "", state, "")
_, _, _, _, err = th.App.AuthorizeOAuthUser(nil, request, model.SERVICE_GITLAB, "", state, "")
require.NotNil(t, err)
assert.Equal(t, "api.user.authorize_oauth_user.invalid_state.app_error", err.Id)
})
@@ -296,7 +296,7 @@ func TestAuthorizeOAuthUser(t *testing.T) {
request := makeRequest(t, cookie)
state := makeState(makeToken(th, cookie))
_, _, _, err := th.App.AuthorizeOAuthUser(&httptest.ResponseRecorder{}, request, model.SERVICE_GITLAB, "", state, "")
_, _, _, _, err := th.App.AuthorizeOAuthUser(&httptest.ResponseRecorder{}, request, model.SERVICE_GITLAB, "", state, "")
require.NotNil(t, err)
assert.Equal(t, "api.user.authorize_oauth_user.token_failed.app_error", err.Id)
})
@@ -314,7 +314,7 @@ func TestAuthorizeOAuthUser(t *testing.T) {
request := makeRequest(t, cookie)
state := makeState(makeToken(th, cookie))
_, _, _, err := th.App.AuthorizeOAuthUser(&httptest.ResponseRecorder{}, request, model.SERVICE_GITLAB, "", state, "")
_, _, _, _, err := th.App.AuthorizeOAuthUser(&httptest.ResponseRecorder{}, request, model.SERVICE_GITLAB, "", state, "")
require.NotNil(t, err)
assert.Equal(t, "api.user.authorize_oauth_user.bad_response.app_error", err.Id)
assert.Contains(t, err.DetailedError, "status_code=418")
@@ -333,7 +333,7 @@ func TestAuthorizeOAuthUser(t *testing.T) {
request := makeRequest(t, cookie)
state := makeState(makeToken(th, cookie))
_, _, _, err := th.App.AuthorizeOAuthUser(&httptest.ResponseRecorder{}, request, model.SERVICE_GITLAB, "", state, "")
_, _, _, _, err := th.App.AuthorizeOAuthUser(&httptest.ResponseRecorder{}, request, model.SERVICE_GITLAB, "", state, "")
require.NotNil(t, err)
assert.Equal(t, "api.user.authorize_oauth_user.bad_response.app_error", err.Id)
assert.Contains(t, err.DetailedError, "response_body=invalid")
@@ -355,7 +355,7 @@ func TestAuthorizeOAuthUser(t *testing.T) {
request := makeRequest(t, cookie)
state := makeState(makeToken(th, cookie))
_, _, _, err := th.App.AuthorizeOAuthUser(&httptest.ResponseRecorder{}, request, model.SERVICE_GITLAB, "", state, "")
_, _, _, _, err := th.App.AuthorizeOAuthUser(&httptest.ResponseRecorder{}, request, model.SERVICE_GITLAB, "", state, "")
require.NotNil(t, err)
assert.Equal(t, "api.user.authorize_oauth_user.bad_token.app_error", err.Id)
})
@@ -376,7 +376,7 @@ func TestAuthorizeOAuthUser(t *testing.T) {
request := makeRequest(t, cookie)
state := makeState(makeToken(th, cookie))
_, _, _, err := th.App.AuthorizeOAuthUser(&httptest.ResponseRecorder{}, request, model.SERVICE_GITLAB, "", state, "")
_, _, _, _, err := th.App.AuthorizeOAuthUser(&httptest.ResponseRecorder{}, request, model.SERVICE_GITLAB, "", state, "")
require.NotNil(t, err)
assert.Equal(t, "api.user.authorize_oauth_user.missing.app_error", err.Id)
})
@@ -397,7 +397,7 @@ func TestAuthorizeOAuthUser(t *testing.T) {
request := makeRequest(t, cookie)
state := makeState(makeToken(th, cookie))
_, _, _, err := th.App.AuthorizeOAuthUser(&httptest.ResponseRecorder{}, request, model.SERVICE_GITLAB, "", state, "")
_, _, _, _, err := th.App.AuthorizeOAuthUser(&httptest.ResponseRecorder{}, request, model.SERVICE_GITLAB, "", state, "")
require.NotNil(t, err)
assert.Equal(t, "api.user.authorize_oauth_user.service.app_error", err.Id)
})
@@ -425,7 +425,7 @@ func TestAuthorizeOAuthUser(t *testing.T) {
request := makeRequest(t, cookie)
state := makeState(makeToken(th, cookie))
_, _, _, err := th.App.AuthorizeOAuthUser(&httptest.ResponseRecorder{}, request, model.SERVICE_GITLAB, "", state, "")
_, _, _, _, err := th.App.AuthorizeOAuthUser(&httptest.ResponseRecorder{}, request, model.SERVICE_GITLAB, "", state, "")
require.NotNil(t, err)
assert.Equal(t, "api.user.authorize_oauth_user.response.app_error", err.Id)
})
@@ -454,7 +454,7 @@ func TestAuthorizeOAuthUser(t *testing.T) {
request := makeRequest(t, cookie)
state := makeState(makeToken(th, cookie))
_, _, _, err := th.App.AuthorizeOAuthUser(&httptest.ResponseRecorder{}, request, model.SERVICE_GITLAB, "", state, "")
_, _, _, _, err := th.App.AuthorizeOAuthUser(&httptest.ResponseRecorder{}, request, model.SERVICE_GITLAB, "", state, "")
require.NotNil(t, err)
assert.Equal(t, "oauth.gitlab.tos.error", err.Id)
})
@@ -504,7 +504,7 @@ func TestAuthorizeOAuthUser(t *testing.T) {
state := base64.StdEncoding.EncodeToString([]byte(model.MapToJson(stateProps)))
recorder := httptest.ResponseRecorder{}
body, receivedTeamId, receivedStateProps, err := th.App.AuthorizeOAuthUser(&recorder, request, model.SERVICE_GITLAB, "", state, "")
body, receivedTeamId, receivedStateProps, _, err := th.App.AuthorizeOAuthUser(&recorder, request, model.SERVICE_GITLAB, "", state, "")
require.NotNil(t, body)
bodyBytes, bodyErr := ioutil.ReadAll(body)
@@ -533,7 +533,8 @@ func TestGetAuthorizationCode(t *testing.T) {
_, err := th.App.GetAuthorizationCode(nil, nil, model.SERVICE_GITLAB, map[string]string{}, "")
require.NotNil(t, err)
assert.Equal(t, "api.user.get_authorization_code.unsupported.app_error", err.Id)
assert.Equal(t, "api.user.authorize_oauth_user.unsupported.app_error", err.Id)
})
t.Run("enabled and properly configured", func(t *testing.T) {

Просмотреть файл

@@ -687,7 +687,7 @@ func (a *OpenTracingAppLayer) AuthenticateUserForLogin(id string, loginId string
return resultVar0, resultVar1
}
func (a *OpenTracingAppLayer) AuthorizeOAuthUser(w http.ResponseWriter, r *http.Request, service string, code string, state string, redirectUri string) (io.ReadCloser, string, map[string]string, *model.AppError) {
func (a *OpenTracingAppLayer) AuthorizeOAuthUser(w http.ResponseWriter, r *http.Request, service string, code string, state string, redirectUri string) (io.ReadCloser, string, map[string]string, *model.User, *model.AppError) {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.AuthorizeOAuthUser")
@@ -699,14 +699,14 @@ func (a *OpenTracingAppLayer) AuthorizeOAuthUser(w http.ResponseWriter, r *http.
}()
defer span.Finish()
resultVar0, resultVar1, resultVar2, resultVar3 := a.app.AuthorizeOAuthUser(w, r, service, code, state, redirectUri)
resultVar0, resultVar1, resultVar2, resultVar3, resultVar4 := a.app.AuthorizeOAuthUser(w, r, service, code, state, redirectUri)
if resultVar3 != nil {
span.LogFields(spanlog.Error(resultVar3))
if resultVar4 != nil {
span.LogFields(spanlog.Error(resultVar4))
ext.Error.Set(span, true)
}
return resultVar0, resultVar1, resultVar2, resultVar3
return resultVar0, resultVar1, resultVar2, resultVar3, resultVar4
}
func (a *OpenTracingAppLayer) AutocompleteChannels(teamId string, term string) (*model.ChannelList, *model.AppError) {
@@ -1427,7 +1427,7 @@ func (a *OpenTracingAppLayer) CompareAndSetPluginKey(pluginId string, key string
return resultVar0, resultVar1
}
func (a *OpenTracingAppLayer) CompleteOAuth(service string, body io.ReadCloser, teamId string, props map[string]string) (*model.User, *model.AppError) {
func (a *OpenTracingAppLayer) CompleteOAuth(service string, body io.ReadCloser, teamId string, props map[string]string, tokenUser *model.User) (*model.User, *model.AppError) {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.CompleteOAuth")
@@ -1439,7 +1439,7 @@ func (a *OpenTracingAppLayer) CompleteOAuth(service string, body io.ReadCloser,
}()
defer span.Finish()
resultVar0, resultVar1 := a.app.CompleteOAuth(service, body, teamId, props)
resultVar0, resultVar1 := a.app.CompleteOAuth(service, body, teamId, props, tokenUser)
if resultVar1 != nil {
span.LogFields(spanlog.Error(resultVar1))
@@ -1449,7 +1449,7 @@ func (a *OpenTracingAppLayer) CompleteOAuth(service string, body io.ReadCloser,
return resultVar0, resultVar1
}
func (a *OpenTracingAppLayer) CompleteSwitchWithOAuth(service string, userData io.Reader, email string) (*model.User, *model.AppError) {
func (a *OpenTracingAppLayer) CompleteSwitchWithOAuth(service string, userData io.Reader, email string, tokenUser *model.User) (*model.User, *model.AppError) {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.CompleteSwitchWithOAuth")
@@ -1461,7 +1461,7 @@ func (a *OpenTracingAppLayer) CompleteSwitchWithOAuth(service string, userData i
}()
defer span.Finish()
resultVar0, resultVar1 := a.app.CompleteSwitchWithOAuth(service, userData, email)
resultVar0, resultVar1 := a.app.CompleteSwitchWithOAuth(service, userData, email, tokenUser)
if resultVar1 != nil {
span.LogFields(spanlog.Error(resultVar1))
@@ -1932,7 +1932,7 @@ func (a *OpenTracingAppLayer) CreateOAuthStateToken(extra string) (*model.Token,
return resultVar0, resultVar1
}
func (a *OpenTracingAppLayer) CreateOAuthUser(service string, userData io.Reader, teamId string) (*model.User, *model.AppError) {
func (a *OpenTracingAppLayer) CreateOAuthUser(service string, userData io.Reader, teamId string, tokenUser *model.User) (*model.User, *model.AppError) {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.CreateOAuthUser")
@@ -1944,7 +1944,7 @@ func (a *OpenTracingAppLayer) CreateOAuthUser(service string, userData io.Reader
}()
defer span.Finish()
resultVar0, resultVar1 := a.app.CreateOAuthUser(service, userData, teamId)
resultVar0, resultVar1 := a.app.CreateOAuthUser(service, userData, teamId, tokenUser)
if resultVar1 != nil {
span.LogFields(spanlog.Error(resultVar1))
@@ -10392,7 +10392,7 @@ func (a *OpenTracingAppLayer) LogAuditRecWithLevel(rec *audit.Record, level mlog
a.app.LogAuditRecWithLevel(rec, level, err)
}
func (a *OpenTracingAppLayer) LoginByOAuth(service string, userData io.Reader, teamId string) (*model.User, *model.AppError) {
func (a *OpenTracingAppLayer) LoginByOAuth(service string, userData io.Reader, teamId string, tokenUser *model.User) (*model.User, *model.AppError) {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.LoginByOAuth")
@@ -10404,7 +10404,7 @@ func (a *OpenTracingAppLayer) LoginByOAuth(service string, userData io.Reader, t
}()
defer span.Finish()
resultVar0, resultVar1 := a.app.LoginByOAuth(service, userData, teamId)
resultVar0, resultVar1 := a.app.LoginByOAuth(service, userData, teamId, tokenUser)
if resultVar1 != nil {
span.LogFields(spanlog.Error(resultVar1))
@@ -14864,7 +14864,7 @@ func (a *OpenTracingAppLayer) UpdateMobileAppBadge(userId string) {
a.app.UpdateMobileAppBadge(userId)
}
func (a *OpenTracingAppLayer) UpdateOAuthUserAttrs(userData io.Reader, user *model.User, provider einterfaces.OauthProvider, service string) *model.AppError {
func (a *OpenTracingAppLayer) UpdateOAuthUserAttrs(userData io.Reader, user *model.User, provider einterfaces.OauthProvider, service string, tokenUser *model.User) *model.AppError {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.UpdateOAuthUserAttrs")
@@ -14876,7 +14876,7 @@ func (a *OpenTracingAppLayer) UpdateOAuthUserAttrs(userData io.Reader, user *mod
}()
defer span.Finish()
resultVar0 := a.app.UpdateOAuthUserAttrs(userData, user, provider, service)
resultVar0 := a.app.UpdateOAuthUserAttrs(userData, user, provider, service, tokenUser)
if resultVar0 != nil {
span.LogFields(spanlog.Error(resultVar0))

Просмотреть файл

@@ -335,19 +335,22 @@ func (a *App) createUser(user *model.User) (*model.User, *model.AppError) {
return ruser, nil
}
func (a *App) CreateOAuthUser(service string, userData io.Reader, teamId string) (*model.User, *model.AppError) {
func (a *App) CreateOAuthUser(service string, userData io.Reader, teamId string, tokenUser *model.User) (*model.User, *model.AppError) {
if !*a.Config().TeamSettings.EnableUserCreation {
return nil, model.NewAppError("CreateOAuthUser", "api.user.create_user.disabled.app_error", nil, "", http.StatusNotImplemented)
}
provider := einterfaces.GetOauthProvider(service)
if provider == nil {
return nil, model.NewAppError("CreateOAuthUser", "api.user.create_oauth_user.not_available.app_error", map[string]interface{}{"Service": strings.Title(service)}, "", http.StatusNotImplemented)
provider, e := a.getSSOProvider(service)
if e != nil {
return nil, e
}
user, err1 := provider.GetUserFromJson(userData)
user, err1 := provider.GetUserFromJson(userData, tokenUser)
if err1 != nil {
return nil, model.NewAppError("CreateOAuthUser", "api.user.create_oauth_user.create.app_error", map[string]interface{}{"Service": service}, err1.Error(), http.StatusInternalServerError)
}
if user.AuthService == "" {
user.AuthService = service
}
suchan := make(chan store.StoreResult, 1)
euchan := make(chan store.StoreResult, 1)
@@ -1990,8 +1993,8 @@ func (a *App) AutocompleteUsersInTeam(teamId string, term string, options *model
return autocomplete, nil
}
func (a *App) UpdateOAuthUserAttrs(userData io.Reader, user *model.User, provider einterfaces.OauthProvider, service string) *model.AppError {
oauthUser, err1 := provider.GetUserFromJson(userData)
func (a *App) UpdateOAuthUserAttrs(userData io.Reader, user *model.User, provider einterfaces.OauthProvider, service string, tokenUser *model.User) *model.AppError {
oauthUser, err1 := provider.GetUserFromJson(userData, tokenUser)
if err1 != nil {
return model.NewAppError("UpdateOAuthUserAttrs", "api.user.update_oauth_user_attrs.get_user.app_error", map[string]interface{}{"Service": service}, err1.Error(), http.StatusBadRequest)
}

Просмотреть файл

@@ -76,10 +76,14 @@ func TestCreateOAuthUser(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.GitLabSettings.Enable = true
})
glUser := oauthgitlab.GitLabUser{Id: 42, Username: "o" + model.NewId(), Email: model.NewId() + "@simulator.amazonses.com", Name: "Joram Wilander"}
json := glUser.ToJson()
user, err := th.App.CreateOAuthUser(model.USER_AUTH_SERVICE_GITLAB, strings.NewReader(json), th.BasicTeam.Id)
user, err := th.App.CreateOAuthUser(model.USER_AUTH_SERVICE_GITLAB, strings.NewReader(json), th.BasicTeam.Id, nil)
require.Nil(t, err)
require.Equal(t, glUser.Username, user.Username, "usernames didn't match")
@@ -88,7 +92,7 @@ func TestCreateOAuthUser(t *testing.T) {
*th.App.Config().TeamSettings.EnableUserCreation = false
_, err = th.App.CreateOAuthUser(model.USER_AUTH_SERVICE_GITLAB, strings.NewReader(json), th.BasicTeam.Id)
_, err = th.App.CreateOAuthUser(model.USER_AUTH_SERVICE_GITLAB, strings.NewReader(json), th.BasicTeam.Id, nil)
require.NotNil(t, err, "should have failed - user creation disabled")
}
@@ -269,6 +273,9 @@ func TestUpdateOAuthUserAttrs(t *testing.T) {
id := model.NewId()
id2 := model.NewId()
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.GitLabSettings.Enable = true
})
gitlabProvider := einterfaces.GetOauthProvider("gitlab")
username := "user" + id
@@ -289,7 +296,7 @@ func TestUpdateOAuthUserAttrs(t *testing.T) {
data := bytes.NewReader(gitlabUser)
user = getUserFromDB(th.App, user.Id, t)
th.App.UpdateOAuthUserAttrs(data, user, gitlabProvider, "gitlab")
th.App.UpdateOAuthUserAttrs(data, user, gitlabProvider, "gitlab", nil)
user = getUserFromDB(th.App, user.Id, t)
require.Equal(t, gitlabUserObj.Username, user.Username, "user's username is not updated")
@@ -302,7 +309,7 @@ func TestUpdateOAuthUserAttrs(t *testing.T) {
data := bytes.NewReader(gitlabUser)
user = getUserFromDB(th.App, user.Id, t)
th.App.UpdateOAuthUserAttrs(data, user, gitlabProvider, "gitlab")
th.App.UpdateOAuthUserAttrs(data, user, gitlabProvider, "gitlab", nil)
user = getUserFromDB(th.App, user.Id, t)
require.NotEqual(t, gitlabUserObj.Username, user.Username, "user's username is updated though there already exists another user with the same username")
@@ -316,7 +323,7 @@ func TestUpdateOAuthUserAttrs(t *testing.T) {
data := bytes.NewReader(gitlabUser)
user = getUserFromDB(th.App, user.Id, t)
th.App.UpdateOAuthUserAttrs(data, user, gitlabProvider, "gitlab")
th.App.UpdateOAuthUserAttrs(data, user, gitlabProvider, "gitlab", nil)
user = getUserFromDB(th.App, user.Id, t)
require.Equal(t, gitlabUserObj.Email, user.Email, "user's email is not updated")
@@ -331,7 +338,7 @@ func TestUpdateOAuthUserAttrs(t *testing.T) {
data := bytes.NewReader(gitlabUser)
user = getUserFromDB(th.App, user.Id, t)
th.App.UpdateOAuthUserAttrs(data, user, gitlabProvider, "gitlab")
th.App.UpdateOAuthUserAttrs(data, user, gitlabProvider, "gitlab", nil)
user = getUserFromDB(th.App, user.Id, t)
require.NotEqual(t, gitlabUserObj.Email, user.Email, "user's email is updated though there already exists another user with the same email")
@@ -344,7 +351,7 @@ func TestUpdateOAuthUserAttrs(t *testing.T) {
data := bytes.NewReader(gitlabUser)
user = getUserFromDB(th.App, user.Id, t)
th.App.UpdateOAuthUserAttrs(data, user, gitlabProvider, "gitlab")
th.App.UpdateOAuthUserAttrs(data, user, gitlabProvider, "gitlab", nil)
user = getUserFromDB(th.App, user.Id, t)
require.Equal(t, "Updated", user.FirstName, "user's first name is not updated")
@@ -356,7 +363,7 @@ func TestUpdateOAuthUserAttrs(t *testing.T) {
data := bytes.NewReader(gitlabUser)
user = getUserFromDB(th.App, user.Id, t)
th.App.UpdateOAuthUserAttrs(data, user, gitlabProvider, "gitlab")
th.App.UpdateOAuthUserAttrs(data, user, gitlabProvider, "gitlab", nil)
user = getUserFromDB(th.App, user.Id, t)
require.Equal(t, "Lastname", user.LastName, "user's last name is not updated")
@@ -532,7 +539,7 @@ func createGitlabUser(t *testing.T, a *App, id int64, username string, email str
var user *model.User
var err *model.AppError
user, err = a.CreateOAuthUser("gitlab", bytes.NewReader(gitlabUser), "")
user, err = a.CreateOAuthUser("gitlab", bytes.NewReader(gitlabUser), "", nil)
require.Nil(t, err, "unable to create the user", err)
return user, gitlabUserObj

Просмотреть файл

@@ -288,6 +288,9 @@ func GenerateLimitedClientConfig(c *model.Config, telemetryID string, license *m
props["SamlLoginButtonTextColor"] = ""
props["EnableSignUpWithGoogle"] = "false"
props["EnableSignUpWithOffice365"] = "false"
props["EnableSignUpWithOpenId"] = "false"
props["OpenIdButtonText"] = ""
props["OpenIdButtonColor"] = ""
props["CWSUrl"] = ""
props["EnableCustomBrand"] = strconv.FormatBool(*c.TeamSettings.EnableCustomBrand)
props["CustomBrandText"] = *c.TeamSettings.CustomBrandText
@@ -322,6 +325,12 @@ func GenerateLimitedClientConfig(c *model.Config, telemetryID string, license *m
props["EnableSignUpWithOffice365"] = strconv.FormatBool(*c.Office365Settings.Enable)
}
if *license.Features.OpenId {
props["EnableSignUpWithOpenId"] = strconv.FormatBool(*c.OpenIdSettings.Enable)
props["OpenIdButtonColor"] = *c.OpenIdSettings.ButtonColor
props["OpenIdButtonText"] = *c.OpenIdSettings.ButtonText
}
if *license.Features.CustomTermsOfService {
props["EnableCustomTermsOfService"] = strconv.FormatBool(*c.SupportSettings.CustomTermsOfServiceEnabled)
props["CustomTermsOfServiceReAcceptancePeriod"] = strconv.FormatInt(int64(*c.SupportSettings.CustomTermsOfServiceReAcceptancePeriod), 10)

Просмотреть файл

@@ -42,6 +42,10 @@ func desanitize(actual, target *model.Config) {
target.Office365Settings.Secret = actual.Office365Settings.Secret
}
if target.OpenIdSettings.Secret != nil && *target.OpenIdSettings.Secret == model.FAKE_SETTING {
target.OpenIdSettings.Secret = actual.OpenIdSettings.Secret
}
if *target.SqlSettings.DataSource == model.FAKE_SETTING {
*target.SqlSettings.DataSource = *actual.SqlSettings.DataSource
}

Просмотреть файл

@@ -25,6 +25,7 @@ func TestDesanitize(t *testing.T) {
actual.FileSettings.AmazonS3SecretAccessKey = sToP("amazon_s3_secret_access_key")
actual.EmailSettings.SMTPPassword = sToP("smtp_password")
actual.GitLabSettings.Secret = sToP("secret")
actual.OpenIdSettings.Secret = sToP("secret")
actual.SqlSettings.DataSource = sToP("data_source")
actual.SqlSettings.AtRestEncryptKey = sToP("at_rest_encrypt_key")
actual.ElasticsearchSettings.Password = sToP("password")
@@ -46,6 +47,7 @@ func TestDesanitize(t *testing.T) {
target.FileSettings.AmazonS3SecretAccessKey = sToP(model.FAKE_SETTING)
target.EmailSettings.SMTPPassword = sToP(model.FAKE_SETTING)
target.GitLabSettings.Secret = sToP(model.FAKE_SETTING)
target.OpenIdSettings.Secret = sToP(model.FAKE_SETTING)
target.SqlSettings.DataSource = sToP(model.FAKE_SETTING)
target.SqlSettings.AtRestEncryptKey = sToP(model.FAKE_SETTING)
target.ElasticsearchSettings.Password = sToP(model.FAKE_SETTING)
@@ -66,6 +68,7 @@ func TestDesanitize(t *testing.T) {
assert.Equal(t, *actual.FileSettings.AmazonS3SecretAccessKey, *target.FileSettings.AmazonS3SecretAccessKey)
assert.Equal(t, *actual.EmailSettings.SMTPPassword, *target.EmailSettings.SMTPPassword)
assert.Equal(t, *actual.GitLabSettings.Secret, *target.GitLabSettings.Secret)
assert.Equal(t, *actual.OpenIdSettings.Secret, *target.OpenIdSettings.Secret)
assert.Equal(t, *actual.SqlSettings.DataSource, *target.SqlSettings.DataSource)
assert.Equal(t, *actual.SqlSettings.AtRestEncryptKey, *target.SqlSettings.AtRestEncryptKey)
assert.Equal(t, *actual.ElasticsearchSettings.Password, *target.ElasticsearchSettings.Password)

Просмотреть файл

@@ -10,7 +10,9 @@ import (
)
type OauthProvider interface {
GetUserFromJson(data io.Reader) (*model.User, error)
GetUserFromJson(data io.Reader, tokenUser *model.User) (*model.User, error)
GetSSOSettings(config *model.Config, service string) (*model.SSOSettings, error)
GetUserFromIdToken(idToken string) (*model.User, error)
}
var oauthProviders = make(map[string]OauthProvider)

Просмотреть файл

@@ -3148,7 +3148,7 @@
},
{
"id": "api.user.authorize_oauth_user.unsupported.app_error",
"translation": "Unsupported OAuth service provider."
"translation": "{{.Service}} SSO through OAuth 2.0 not available on this server."
},
{
"id": "api.user.autocomplete_users.missing_team_id.app_error",
@@ -3174,10 +3174,6 @@
"id": "api.user.complete_switch_with_oauth.parse.app_error",
"translation": "Could not parse auth data out of {{.Service}} user object."
},
{
"id": "api.user.complete_switch_with_oauth.unavailable.app_error",
"translation": "{{.Service}} SSO through OAuth 2.0 not available on this server."
},
{
"id": "api.user.create_email_token.error",
"translation": "Failed to create token data for email verification"
@@ -3190,10 +3186,6 @@
"id": "api.user.create_oauth_user.create.app_error",
"translation": "Could not create user out of {{.Service}} user object."
},
{
"id": "api.user.create_oauth_user.not_available.app_error",
"translation": "{{.Service}} SSO through OAuth 2.0 not available on this server."
},
{
"id": "api.user.create_password_token.error",
"translation": "Unable to create password recovery token"
@@ -3271,8 +3263,8 @@
"translation": "Authentication Transfer not configured or available on this server."
},
{
"id": "api.user.get_authorization_code.unsupported.app_error",
"translation": "Unsupported OAuth service provider."
"id": "api.user.get_authorization_code.endpoint.app_error",
"translation": ""
},
{
"id": "api.user.get_uploads_for_user.forbidden.app_error",

Просмотреть файл

@@ -31,6 +31,7 @@ type AccessResponse struct {
ExpiresIn int32 `json:"expires_in"`
Scope string `json:"scope"`
RefreshToken string `json:"refresh_token"`
IdToken string `json:"id_token"`
}
// IsValid validates the AccessData and returns an error if it isn't configured

Просмотреть файл

@@ -45,6 +45,7 @@ const (
SERVICE_GITLAB = "gitlab"
SERVICE_GOOGLE = "google"
SERVICE_OFFICE365 = "office365"
SERVICE_OPENID = "openid"
GENERIC_NO_CHANNEL_NOTIFICATION = "generic_no_channel"
GENERIC_NOTIFICATION = "generic"
@@ -230,6 +231,7 @@ const (
OFFICE365_SETTINGS_DEFAULT_USER_API_ENDPOINT = "https://graph.microsoft.com/v1.0/me"
CLOUD_SETTINGS_DEFAULT_CWS_URL = "https://customers.mattermost.com"
OPENID_SETTINGS_DEFAULT_SCOPE = "profile openid email"
LOCAL_MODE_SOCKET_PATH = "/var/tmp/mattermost_local.socket"
)
@@ -960,16 +962,19 @@ func (s *AnalyticsSettings) SetDefaults() {
}
type SSOSettings struct {
Enable *bool `access:"authentication"`
Secret *string `access:"authentication"`
Id *string `access:"authentication"`
Scope *string `access:"authentication"`
AuthEndpoint *string `access:"authentication"`
TokenEndpoint *string `access:"authentication"`
UserApiEndpoint *string `access:"authentication"`
Enable *bool `access:"authentication"`
Secret *string `access:"authentication"`
Id *string `access:"authentication"`
Scope *string `access:"authentication"`
AuthEndpoint *string `access:"authentication"`
TokenEndpoint *string `access:"authentication"`
UserApiEndpoint *string `access:"authentication"`
DiscoveryEndpoint *string `access:"authentication"`
ButtonText *string `access:"authentication"`
ButtonColor *string `access:"authentication"`
}
func (s *SSOSettings) setDefaults(scope, authEndpoint, tokenEndpoint, userApiEndpoint string) {
func (s *SSOSettings) setDefaults(scope, authEndpoint, tokenEndpoint, userApiEndpoint, buttonColor string) {
if s.Enable == nil {
s.Enable = NewBool(false)
}
@@ -986,6 +991,10 @@ func (s *SSOSettings) setDefaults(scope, authEndpoint, tokenEndpoint, userApiEnd
s.Scope = NewString(scope)
}
if s.DiscoveryEndpoint == nil {
s.DiscoveryEndpoint = NewString("")
}
if s.AuthEndpoint == nil {
s.AuthEndpoint = NewString(authEndpoint)
}
@@ -997,17 +1006,26 @@ func (s *SSOSettings) setDefaults(scope, authEndpoint, tokenEndpoint, userApiEnd
if s.UserApiEndpoint == nil {
s.UserApiEndpoint = NewString(userApiEndpoint)
}
if s.ButtonText == nil {
s.ButtonText = NewString("")
}
if s.ButtonColor == nil {
s.ButtonColor = NewString(buttonColor)
}
}
type Office365Settings struct {
Enable *bool `access:"authentication"`
Secret *string `access:"authentication"`
Id *string `access:"authentication"`
Scope *string `access:"authentication"`
AuthEndpoint *string `access:"authentication"`
TokenEndpoint *string `access:"authentication"`
UserApiEndpoint *string `access:"authentication"`
DirectoryId *string `access:"authentication"`
Enable *bool `access:"authentication"`
Secret *string `access:"authentication"`
Id *string `access:"authentication"`
Scope *string `access:"authentication"`
AuthEndpoint *string `access:"authentication"`
TokenEndpoint *string `access:"authentication"`
UserApiEndpoint *string `access:"authentication"`
DiscoveryEndpoint *string `access:"authentication"`
DirectoryId *string `access:"authentication"`
}
func (s *Office365Settings) setDefaults() {
@@ -1027,6 +1045,10 @@ func (s *Office365Settings) setDefaults() {
s.Scope = NewString(OFFICE365_SETTINGS_DEFAULT_SCOPE)
}
if s.DiscoveryEndpoint == nil {
s.DiscoveryEndpoint = NewString("")
}
if s.AuthEndpoint == nil {
s.AuthEndpoint = NewString(OFFICE365_SETTINGS_DEFAULT_AUTH_ENDPOINT)
}
@@ -1050,6 +1072,7 @@ func (s *Office365Settings) SSOSettings() *SSOSettings {
ssoSettings.Secret = s.Secret
ssoSettings.Id = s.Id
ssoSettings.Scope = s.Scope
ssoSettings.DiscoveryEndpoint = s.DiscoveryEndpoint
ssoSettings.AuthEndpoint = s.AuthEndpoint
ssoSettings.TokenEndpoint = s.TokenEndpoint
ssoSettings.UserApiEndpoint = s.UserApiEndpoint
@@ -2948,6 +2971,7 @@ type Config struct {
GitLabSettings SSOSettings
GoogleSettings SSOSettings
Office365Settings Office365Settings
OpenIdSettings SSOSettings
LdapSettings LdapSettings
ComplianceSettings ComplianceSettings
LocalizationSettings LocalizationSettings
@@ -3004,6 +3028,8 @@ func (o *Config) GetSSOService(service string) *SSOSettings {
return &o.GoogleSettings
case SERVICE_OFFICE365:
return o.Office365Settings.SSOSettings()
case SERVICE_OPENID:
return &o.OpenIdSettings
}
return nil
@@ -3039,8 +3065,10 @@ func (o *Config) SetDefaults() {
o.EmailSettings.SetDefaults(isUpdate)
o.PrivacySettings.setDefaults()
o.Office365Settings.setDefaults()
o.GitLabSettings.setDefaults("", "", "", "")
o.GoogleSettings.setDefaults(GOOGLE_SETTINGS_DEFAULT_SCOPE, GOOGLE_SETTINGS_DEFAULT_AUTH_ENDPOINT, GOOGLE_SETTINGS_DEFAULT_TOKEN_ENDPOINT, GOOGLE_SETTINGS_DEFAULT_USER_API_ENDPOINT)
o.Office365Settings.setDefaults()
o.GitLabSettings.setDefaults("", "", "", "", "")
o.GoogleSettings.setDefaults(GOOGLE_SETTINGS_DEFAULT_SCOPE, GOOGLE_SETTINGS_DEFAULT_AUTH_ENDPOINT, GOOGLE_SETTINGS_DEFAULT_TOKEN_ENDPOINT, GOOGLE_SETTINGS_DEFAULT_USER_API_ENDPOINT, "")
o.OpenIdSettings.setDefaults(OPENID_SETTINGS_DEFAULT_SCOPE, "", "", "", "#145DBF")
o.ServiceSettings.SetDefaults(isUpdate)
o.PasswordSettings.SetDefaults()
o.TeamSettings.SetDefaults()
@@ -3691,6 +3719,10 @@ func (o *Config) Sanitize() {
*o.Office365Settings.Secret = FAKE_SETTING
}
if o.OpenIdSettings.Secret != nil && len(*o.OpenIdSettings.Secret) > 0 {
*o.OpenIdSettings.Secret = FAKE_SETTING
}
*o.SqlSettings.DataSource = FAKE_SETTING
*o.SqlSettings.AtRestEncryptKey = FAKE_SETTING

Просмотреть файл

@@ -1322,6 +1322,7 @@ func TestConfigSanitize(t *testing.T) {
*c.FileSettings.AmazonS3SecretAccessKey = "bar"
*c.EmailSettings.SMTPPassword = "baz"
*c.GitLabSettings.Secret = "bingo"
*c.OpenIdSettings.Secret = "secret"
c.SqlSettings.DataSourceReplicas = []string{"stuff"}
c.SqlSettings.DataSourceSearchReplicas = []string{"stuff"}
@@ -1332,6 +1333,7 @@ func TestConfigSanitize(t *testing.T) {
assert.Equal(t, FAKE_SETTING, *c.FileSettings.AmazonS3SecretAccessKey)
assert.Equal(t, FAKE_SETTING, *c.EmailSettings.SMTPPassword)
assert.Equal(t, FAKE_SETTING, *c.GitLabSettings.Secret)
assert.Equal(t, FAKE_SETTING, *c.OpenIdSettings.Secret)
assert.Equal(t, FAKE_SETTING, *c.SqlSettings.DataSource)
assert.Equal(t, FAKE_SETTING, *c.SqlSettings.AtRestEncryptKey)
assert.Equal(t, FAKE_SETTING, *c.ElasticsearchSettings.Password)

Просмотреть файл

@@ -91,7 +91,7 @@ func (glu *GitLabUser) getAuthData() string {
return strconv.FormatInt(glu.Id, 10)
}
func (m *GitLabProvider) GetUserFromJson(data io.Reader) (*model.User, error) {
func (m *GitLabProvider) GetUserFromJson(data io.Reader, tokenUser *model.User) (*model.User, error) {
glu, err := gitLabUserFromJson(data)
if err != nil {
return nil, err
@@ -102,3 +102,11 @@ func (m *GitLabProvider) GetUserFromJson(data io.Reader) (*model.User, error) {
return userFromGitLabUser(glu), nil
}
func (m *GitLabProvider) GetSSOSettings(config *model.Config, service string) (*model.SSOSettings, error) {
return &config.GitLabSettings, nil
}
func (m *GitLabProvider) GetUserFromIdToken(idToken string) (*model.User, error) {
return nil, nil
}

Просмотреть файл

@@ -63,6 +63,7 @@ type Features struct {
MFA *bool `json:"mfa"`
GoogleOAuth *bool `json:"google_oauth"`
Office365OAuth *bool `json:"office365_oauth"`
OpenId *bool `json:"openid"`
Compliance *bool `json:"compliance"`
Cluster *bool `json:"cluster"`
Metrics *bool `json:"metrics"`
@@ -95,6 +96,7 @@ func (f *Features) ToMap() map[string]interface{} {
"mfa": *f.MFA,
"google": *f.GoogleOAuth,
"office365": *f.Office365OAuth,
"openid": *f.OpenId,
"compliance": *f.Compliance,
"cluster": *f.Cluster,
"metrics": *f.Metrics,
@@ -145,6 +147,10 @@ func (f *Features) SetDefaults() {
f.Office365OAuth = NewBool(*f.FutureFeatures)
}
if f.OpenId == nil {
f.OpenId = NewBool(*f.FutureFeatures)
}
if f.Compliance == nil {
f.Compliance = NewBool(*f.FutureFeatures)
}

Просмотреть файл

@@ -34,14 +34,16 @@ func (o *SwitchRequest) EmailToOAuth() bool {
(o.NewService == USER_AUTH_SERVICE_SAML ||
o.NewService == USER_AUTH_SERVICE_GITLAB ||
o.NewService == SERVICE_GOOGLE ||
o.NewService == SERVICE_OFFICE365)
o.NewService == SERVICE_OFFICE365 ||
o.NewService == SERVICE_OPENID)
}
func (o *SwitchRequest) OAuthToEmail() bool {
return (o.CurrentService == USER_AUTH_SERVICE_SAML ||
o.CurrentService == USER_AUTH_SERVICE_GITLAB ||
o.CurrentService == SERVICE_GOOGLE ||
o.CurrentService == SERVICE_OFFICE365) && o.NewService == USER_AUTH_SERVICE_EMAIL
o.CurrentService == SERVICE_OFFICE365 ||
o.CurrentService == SERVICE_OPENID) && o.NewService == USER_AUTH_SERVICE_EMAIL
}
func (o *SwitchRequest) EmailToLdap() bool {

Просмотреть файл

@@ -588,8 +588,12 @@ func (ts *TelemetryService) trackConfig() {
ts.sendTelemetry(TRACK_CONFIG_OAUTH, map[string]interface{}{
"enable_gitlab": cfg.GitLabSettings.Enable,
"openid_gitlab": *cfg.GitLabSettings.Enable && strings.Contains(*cfg.GitLabSettings.Scope, model.SERVICE_OPENID),
"enable_google": cfg.GoogleSettings.Enable,
"openid_google": *cfg.GoogleSettings.Enable && strings.Contains(*cfg.GoogleSettings.Scope, model.SERVICE_OPENID),
"enable_office365": cfg.Office365Settings.Enable,
"openid_office365": *cfg.Office365Settings.Enable && strings.Contains(*cfg.Office365Settings.Scope, model.SERVICE_OPENID),
"enable_openid": cfg.OpenIdSettings.Enable,
})
ts.sendTelemetry(TRACK_CONFIG_SUPPORT, map[string]interface{}{

Просмотреть файл

@@ -139,6 +139,7 @@ func GetClientLicense(l *model.License) map[string]string {
props["Metrics"] = strconv.FormatBool(*l.Features.Metrics)
props["GoogleOAuth"] = strconv.FormatBool(*l.Features.GoogleOAuth)
props["Office365OAuth"] = strconv.FormatBool(*l.Features.Office365OAuth)
props["OpenId"] = strconv.FormatBool(*l.Features.OpenId)
props["Compliance"] = strconv.FormatBool(*l.Features.Compliance)
props["MHPNS"] = strconv.FormatBool(*l.Features.MHPNS)
props["Announcement"] = strconv.FormatBool(*l.Features.Announcement)

Просмотреть файл

@@ -270,7 +270,7 @@ func completeOAuth(c *Context, w http.ResponseWriter, r *http.Request) {
uri := c.GetSiteURLHeader() + "/signup/" + service + "/complete"
body, teamId, props, err := c.App.AuthorizeOAuthUser(w, r, service, code, state, uri)
body, teamId, props, tokenUser, err := c.App.AuthorizeOAuthUser(w, r, service, code, state, uri)
action := ""
if props != nil {
@@ -288,7 +288,7 @@ func completeOAuth(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
user, err := c.App.CompleteOAuth(service, body, teamId, props)
user, err := c.App.CompleteOAuth(service, body, teamId, props, tokenUser)
if err != nil {
err.Translate(c.App.T)
mlog.Error(err.Error())

Просмотреть файл

@@ -549,12 +549,20 @@ func closeBody(r *http.Response) {
type MattermostTestProvider struct {
}
func (m *MattermostTestProvider) GetUserFromJson(data io.Reader) (*model.User, error) {
func (m *MattermostTestProvider) GetUserFromJson(data io.Reader, tokenUser *model.User) (*model.User, error) {
user := model.UserFromJson(data)
user.AuthData = &user.Email
return user, nil
}
func (m *MattermostTestProvider) GetSSOSettings(config *model.Config, service string) (*model.SSOSettings, error) {
return &config.GitLabSettings, nil
}
func (m *MattermostTestProvider) GetUserFromIdToken(token string) (*model.User, error) {
return nil, nil
}
func GenerateTestAppName() string {
return "fakeoauthapp" + model.NewRandomString(10)
}