summaryrefslogtreecommitdiffstats
path: root/app/oauth.go
diff options
context:
space:
mode:
Diffstat (limited to 'app/oauth.go')
-rw-r--r--app/oauth.go649
1 files changed, 647 insertions, 2 deletions
diff --git a/app/oauth.go b/app/oauth.go
index 3e8b0b8d2..260e4ac00 100644
--- a/app/oauth.go
+++ b/app/oauth.go
@@ -1,14 +1,386 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
import (
+ "bytes"
+ "crypto/tls"
+ b64 "encoding/base64"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "net/url"
+ "strings"
+
+ l4g "github.com/alecthomas/log4go"
+ "github.com/mattermost/platform/einterfaces"
"github.com/mattermost/platform/model"
+ "github.com/mattermost/platform/store"
+ "github.com/mattermost/platform/utils"
)
-func RevokeAccessToken(token string) *model.AppError {
+func CreateOAuthApp(app *model.OAuthApp) (*model.OAuthApp, *model.AppError) {
+ if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider {
+ return nil, model.NewAppError("CreateOAuthApp", "api.oauth.register_oauth_app.turn_off.app_error", nil, "", http.StatusNotImplemented)
+ }
+
+ secret := model.NewId()
+ app.ClientSecret = secret
+
+ if result := <-Srv.Store.OAuth().SaveApp(app); result.Err != nil {
+ return nil, result.Err
+ } else {
+ return result.Data.(*model.OAuthApp), nil
+ }
+}
+
+func GetOAuthApp(appId string) (*model.OAuthApp, *model.AppError) {
+ if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider {
+ return nil, model.NewAppError("GetOAuthApp", "api.oauth.allow_oauth.turn_off.app_error", nil, "", http.StatusNotImplemented)
+ }
+
+ if result := <-Srv.Store.OAuth().GetApp(appId); result.Err != nil {
+ return nil, result.Err
+ } else {
+ return result.Data.(*model.OAuthApp), nil
+ }
+}
+
+func DeleteOAuthApp(appId string) *model.AppError {
+ if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider {
+ return model.NewAppError("DeleteOAuthApp", "api.oauth.allow_oauth.turn_off.app_error", nil, "", http.StatusNotImplemented)
+ }
+
+ if err := (<-Srv.Store.OAuth().DeleteApp(appId)).Err; err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func GetOAuthApps(page, perPage int) ([]*model.OAuthApp, *model.AppError) {
+ if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider {
+ return nil, model.NewAppError("GetOAuthApps", "api.oauth.allow_oauth.turn_off.app_error", nil, "", http.StatusNotImplemented)
+ }
+
+ if result := <-Srv.Store.OAuth().GetApps(page*perPage, perPage); result.Err != nil {
+ return nil, result.Err
+ } else {
+ return result.Data.([]*model.OAuthApp), nil
+ }
+}
+
+func GetOAuthAppsByCreator(userId string, page, perPage int) ([]*model.OAuthApp, *model.AppError) {
+ if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider {
+ return nil, model.NewAppError("GetOAuthAppsByUser", "api.oauth.allow_oauth.turn_off.app_error", nil, "", http.StatusNotImplemented)
+ }
+
+ if result := <-Srv.Store.OAuth().GetAppByUser(userId, page*perPage, perPage); result.Err != nil {
+ return nil, result.Err
+ } else {
+ return result.Data.([]*model.OAuthApp), nil
+ }
+}
+
+func AllowOAuthAppAccessToUser(userId, responseType, clientId, redirectUri, scope, state string) (string, *model.AppError) {
+ if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider {
+ return "", model.NewAppError("AllowOAuthAppAccessToUser", "api.oauth.allow_oauth.turn_off.app_error", nil, "", http.StatusNotImplemented)
+ }
+
+ if len(scope) == 0 {
+ scope = model.DEFAULT_SCOPE
+ }
+
+ var oauthApp *model.OAuthApp
+ if result := <-Srv.Store.OAuth().GetApp(clientId); result.Err != nil {
+ return "", result.Err
+ } else {
+ oauthApp = result.Data.(*model.OAuthApp)
+ }
+
+ if !oauthApp.IsValidRedirectURL(redirectUri) {
+ return "", model.NewAppError("AllowOAuthAppAccessToUser", "api.oauth.allow_oauth.redirect_callback.app_error", nil, "", http.StatusBadRequest)
+ }
+
+ if responseType != model.AUTHCODE_RESPONSE_TYPE {
+ return redirectUri + "?error=unsupported_response_type&state=" + state, nil
+ }
+
+ authData := &model.AuthData{UserId: userId, ClientId: clientId, CreateAt: model.GetMillis(), RedirectUri: redirectUri, State: state, Scope: scope}
+ authData.Code = model.HashPassword(fmt.Sprintf("%v:%v:%v:%v", clientId, redirectUri, authData.CreateAt, userId))
+
+ // this saves the OAuth2 app as authorized
+ authorizedApp := model.Preference{
+ UserId: userId,
+ Category: model.PREFERENCE_CATEGORY_AUTHORIZED_OAUTH_APP,
+ Name: clientId,
+ Value: scope,
+ }
+
+ if result := <-Srv.Store.Preference().Save(&model.Preferences{authorizedApp}); result.Err != nil {
+ return redirectUri + "?error=server_error&state=" + state, nil
+ }
+
+ if result := <-Srv.Store.OAuth().SaveAuthData(authData); result.Err != nil {
+ return redirectUri + "?error=server_error&state=" + state, nil
+ }
+
+ return redirectUri + "?code=" + url.QueryEscape(authData.Code) + "&state=" + url.QueryEscape(authData.State), nil
+}
+
+func GetOAuthAccessToken(clientId, grantType, redirectUri, code, secret, refreshToken string) (*model.AccessResponse, *model.AppError) {
+ if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider {
+ return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.disabled.app_error", nil, "", http.StatusNotImplemented)
+ }
+
+ var oauthApp *model.OAuthApp
+ if result := <-Srv.Store.OAuth().GetApp(clientId); result.Err != nil {
+ return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.credentials.app_error", nil, "", http.StatusNotFound)
+ } else {
+ oauthApp = result.Data.(*model.OAuthApp)
+ }
+
+ if oauthApp.ClientSecret != secret {
+ return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.credentials.app_error", nil, "", http.StatusForbidden)
+ }
+
+ var user *model.User
+ var accessData *model.AccessData
+ var accessRsp *model.AccessResponse
+ if grantType == model.ACCESS_TOKEN_GRANT_TYPE {
+
+ var authData *model.AuthData
+ if result := <-Srv.Store.OAuth().GetAuthData(code); result.Err != nil {
+ return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.expired_code.app_error", nil, "", http.StatusInternalServerError)
+ } else {
+ authData = result.Data.(*model.AuthData)
+ }
+
+ if authData.IsExpired() {
+ <-Srv.Store.OAuth().RemoveAuthData(authData.Code)
+ return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.expired_code.app_error", nil, "", http.StatusForbidden)
+ }
+
+ if authData.RedirectUri != redirectUri {
+ return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.redirect_uri.app_error", nil, "", http.StatusBadRequest)
+ }
+
+ if !model.ComparePassword(code, fmt.Sprintf("%v:%v:%v:%v", clientId, redirectUri, authData.CreateAt, authData.UserId)) {
+ return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.expired_code.app_error", nil, "", http.StatusBadRequest)
+ }
+
+ if result := <-Srv.Store.User().Get(authData.UserId); result.Err != nil {
+ return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.internal_user.app_error", nil, "", http.StatusNotFound)
+ } else {
+ user = result.Data.(*model.User)
+ }
+ if result := <-Srv.Store.OAuth().GetPreviousAccessData(user.Id, clientId); result.Err != nil {
+ return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.internal.app_error", nil, "", http.StatusInternalServerError)
+ } else if result.Data != nil {
+ accessData := result.Data.(*model.AccessData)
+ if accessData.IsExpired() {
+ if access, err := newSessionUpdateToken(oauthApp.Name, accessData, user); err != nil {
+ return nil, err
+ } else {
+ accessRsp = access
+ }
+ } else {
+ //return the same token and no need to create a new session
+ accessRsp = &model.AccessResponse{
+ AccessToken: accessData.Token,
+ TokenType: model.ACCESS_TOKEN_TYPE,
+ ExpiresIn: int32((accessData.ExpiresAt - model.GetMillis()) / 1000),
+ }
+ }
+ } else {
+ // create a new session and return new access token
+ var session *model.Session
+ if result, err := newSession(oauthApp.Name, user); err != nil {
+ return nil, err
+ } else {
+ session = result
+ }
+
+ accessData = &model.AccessData{ClientId: clientId, UserId: user.Id, Token: session.Token, RefreshToken: model.NewId(), RedirectUri: redirectUri, ExpiresAt: session.ExpiresAt, Scope: authData.Scope}
+
+ if result := <-Srv.Store.OAuth().SaveAccessData(accessData); result.Err != nil {
+ l4g.Error(result.Err)
+ return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.internal_saving.app_error", nil, "", http.StatusInternalServerError)
+ }
+
+ accessRsp = &model.AccessResponse{
+ AccessToken: session.Token,
+ TokenType: model.ACCESS_TOKEN_TYPE,
+ RefreshToken: accessData.RefreshToken,
+ ExpiresIn: int32(*utils.Cfg.ServiceSettings.SessionLengthSSOInDays * 60 * 60 * 24),
+ }
+ }
+
+ <-Srv.Store.OAuth().RemoveAuthData(authData.Code)
+ } else {
+ // when grantType is refresh_token
+ if result := <-Srv.Store.OAuth().GetAccessDataByRefreshToken(refreshToken); result.Err != nil {
+ return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.refresh_token.app_error", nil, "", http.StatusNotFound)
+ } else {
+ accessData = result.Data.(*model.AccessData)
+ }
+
+ if result := <-Srv.Store.User().Get(accessData.UserId); result.Err != nil {
+ return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.internal_user.app_error", nil, "", http.StatusNotFound)
+ } else {
+ user = result.Data.(*model.User)
+ }
+
+ if access, err := newSessionUpdateToken(oauthApp.Name, accessData, user); err != nil {
+ return nil, err
+ } else {
+ accessRsp = access
+ }
+ }
+
+ return accessRsp, nil
+}
+
+func newSession(appName string, user *model.User) (*model.Session, *model.AppError) {
+ // set new token an session
+ session := &model.Session{UserId: user.Id, Roles: user.Roles, IsOAuth: true}
+ session.SetExpireInDays(*utils.Cfg.ServiceSettings.SessionLengthSSOInDays)
+ session.AddProp(model.SESSION_PROP_PLATFORM, appName)
+ session.AddProp(model.SESSION_PROP_OS, "OAuth2")
+ session.AddProp(model.SESSION_PROP_BROWSER, "OAuth2")
+
+ if result := <-Srv.Store.Session().Save(session); result.Err != nil {
+ return nil, model.NewAppError("newSession", "api.oauth.get_access_token.internal_session.app_error", nil, "", http.StatusInternalServerError)
+ } else {
+ session = result.Data.(*model.Session)
+ AddSessionToCache(session)
+ }
+
+ return session, nil
+}
+
+func newSessionUpdateToken(appName string, accessData *model.AccessData, user *model.User) (*model.AccessResponse, *model.AppError) {
+ var session *model.Session
+ <-Srv.Store.Session().Remove(accessData.Token) //remove the previous session
+
+ if result, err := newSession(appName, user); err != nil {
+ return nil, err
+ } else {
+ session = result
+ }
+
+ accessData.Token = session.Token
+ accessData.ExpiresAt = session.ExpiresAt
+ if result := <-Srv.Store.OAuth().UpdateAccessData(accessData); result.Err != nil {
+ l4g.Error(result.Err)
+ return nil, model.NewAppError("newSessionUpdateToken", "web.get_access_token.internal_saving.app_error", nil, "", http.StatusInternalServerError)
+ }
+ accessRsp := &model.AccessResponse{
+ AccessToken: session.Token,
+ TokenType: model.ACCESS_TOKEN_TYPE,
+ ExpiresIn: int32(*utils.Cfg.ServiceSettings.SessionLengthSSOInDays * 60 * 60 * 24),
+ }
+
+ return accessRsp, nil
+}
+
+func GetOAuthLoginEndpoint(service, teamId, redirectTo, loginHint string) (string, *model.AppError) {
+ stateProps := map[string]string{}
+ stateProps["action"] = model.OAUTH_ACTION_LOGIN
+ if len(teamId) != 0 {
+ stateProps["team_id"] = teamId
+ }
+
+ if len(redirectTo) != 0 {
+ stateProps["redirect_to"] = redirectTo
+ }
+
+ if authUrl, err := GetAuthorizationCode(service, stateProps, loginHint); err != nil {
+ return "", err
+ } else {
+ return authUrl, nil
+ }
+}
+
+func GetOAuthSignupEndpoint(service, teamId string) (string, *model.AppError) {
+ stateProps := map[string]string{}
+ stateProps["action"] = model.OAUTH_ACTION_SIGNUP
+ if len(teamId) != 0 {
+ stateProps["team_id"] = teamId
+ }
+
+ if authUrl, err := GetAuthorizationCode(service, stateProps, ""); err != nil {
+ return "", err
+ } else {
+ return authUrl, nil
+ }
+}
+
+func GetAuthorizedAppsForUser(userId string, page, perPage int) ([]*model.OAuthApp, *model.AppError) {
+ if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider {
+ return nil, model.NewAppError("GetAuthorizedAppsForUser", "api.oauth.allow_oauth.turn_off.app_error", nil, "", http.StatusNotImplemented)
+ }
+
+ if result := <-Srv.Store.OAuth().GetAuthorizedApps(userId, page*perPage, perPage); result.Err != nil {
+ return nil, result.Err
+ } else {
+ apps := result.Data.([]*model.OAuthApp)
+ for k, a := range apps {
+ a.Sanitize()
+ apps[k] = a
+ }
+
+ return apps, nil
+ }
+}
+
+func DeauthorizeOAuthAppForUser(userId, appId string) *model.AppError {
+ if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider {
+ return model.NewAppError("DeauthorizeOAuthAppForUser", "api.oauth.allow_oauth.turn_off.app_error", nil, "", http.StatusNotImplemented)
+ }
+
+ // revoke app sessions
+ if result := <-Srv.Store.OAuth().GetAccessDataByUserForApp(userId, appId); result.Err != nil {
+ return result.Err
+ } else {
+ accessData := result.Data.([]*model.AccessData)
+
+ for _, a := range accessData {
+ if err := RevokeAccessToken(a.Token); err != nil {
+ return err
+ }
+
+ if rad := <-Srv.Store.OAuth().RemoveAccessData(a.Token); rad.Err != nil {
+ return rad.Err
+ }
+ }
+ }
+
+ // Deauthorize the app
+ if err := (<-Srv.Store.Preference().Delete(userId, model.PREFERENCE_CATEGORY_AUTHORIZED_OAUTH_APP, appId)).Err; err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func RegenerateOAuthAppSecret(app *model.OAuthApp) (*model.OAuthApp, *model.AppError) {
+ if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider {
+ return nil, model.NewAppError("RegenerateOAuthAppSecret", "api.oauth.allow_oauth.turn_off.app_error", nil, "", http.StatusNotImplemented)
+ }
+
+ app.ClientSecret = model.NewId()
+ if update := <-Srv.Store.OAuth().UpdateApp(app); update.Err != nil {
+ return nil, update.Err
+ }
+
+ return app, nil
+}
+
+func RevokeAccessToken(token string) *model.AppError {
session, _ := GetSession(token)
schan := Srv.Store.Session().Remove(token)
@@ -32,3 +404,276 @@ func RevokeAccessToken(token string) *model.AppError {
return nil
}
+
+func CompleteOAuth(service string, body io.ReadCloser, teamId string, props map[string]string) (*model.User, *model.AppError) {
+ defer func() {
+ ioutil.ReadAll(body)
+ body.Close()
+ }()
+
+ action := props["action"]
+
+ switch action {
+ case model.OAUTH_ACTION_SIGNUP:
+ return CreateOAuthUser(service, body, teamId)
+ case model.OAUTH_ACTION_LOGIN:
+ return LoginByOAuth(service, body, teamId)
+ case model.OAUTH_ACTION_EMAIL_TO_SSO:
+ return CompleteSwitchWithOAuth(service, body, props["email"])
+ case model.OAUTH_ACTION_SSO_TO_EMAIL:
+ return LoginByOAuth(service, body, teamId)
+ default:
+ return LoginByOAuth(service, body, teamId)
+ }
+}
+
+func LoginByOAuth(service string, userData io.Reader, teamId string) (*model.User, *model.AppError) {
+ buf := bytes.Buffer{}
+ buf.ReadFrom(userData)
+
+ authData := ""
+ provider := einterfaces.GetOauthProvider(service)
+ if provider == nil {
+ return nil, model.NewAppError("LoginByOAuth", "api.user.login_by_oauth.not_available.app_error",
+ map[string]interface{}{"Service": strings.Title(service)}, "", http.StatusNotImplemented)
+ } else {
+ authData = provider.GetAuthDataFromJson(bytes.NewReader(buf.Bytes()))
+ }
+
+ if len(authData) == 0 {
+ return nil, model.NewAppError("LoginByOAuth", "api.user.login_by_oauth.parse.app_error",
+ map[string]interface{}{"Service": service}, "", http.StatusBadRequest)
+ }
+
+ user, err := GetUserByAuth(&authData, service)
+ if err != nil {
+ if err.Id == store.MISSING_AUTH_ACCOUNT_ERROR {
+ return CreateOAuthUser(service, bytes.NewReader(buf.Bytes()), teamId)
+ }
+ return nil, err
+ }
+
+ if err = UpdateOAuthUserAttrs(bytes.NewReader(buf.Bytes()), user, provider, service); err != nil {
+ return nil, err
+ }
+
+ if len(teamId) > 0 {
+ err = AddUserToTeamByTeamId(teamId, user)
+ }
+
+ if err != nil {
+ return nil, err
+ }
+
+ return user, nil
+}
+
+func CompleteSwitchWithOAuth(service string, userData io.ReadCloser, email string) (*model.User, *model.AppError) {
+ authData := ""
+ ssoEmail := ""
+ 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)
+ } else {
+ ssoUser := provider.GetUserFromJson(userData)
+ ssoEmail = ssoUser.Email
+
+ if ssoUser.AuthData != nil {
+ authData = *ssoUser.AuthData
+ }
+ }
+
+ if len(authData) == 0 {
+ return nil, model.NewAppError("CompleteSwitchWithOAuth", "api.user.complete_switch_with_oauth.parse.app_error",
+ map[string]interface{}{"Service": service}, "", http.StatusBadRequest)
+ }
+
+ if len(email) == 0 {
+ return nil, model.NewAppError("CompleteSwitchWithOAuth", "api.user.complete_switch_with_oauth.blank_email.app_error", nil, "", http.StatusBadRequest)
+ }
+
+ var user *model.User
+ if result := <-Srv.Store.User().GetByEmail(email); result.Err != nil {
+ return nil, result.Err
+ } else {
+ user = result.Data.(*model.User)
+ }
+
+ if err := RevokeAllSessions(user.Id); err != nil {
+ return nil, err
+ }
+
+ if result := <-Srv.Store.User().UpdateAuthData(user.Id, service, &authData, ssoEmail, true); result.Err != nil {
+ return nil, result.Err
+ }
+
+ go func() {
+ if err := SendSignInChangeEmail(user.Email, strings.Title(service)+" SSO", user.Locale, utils.GetSiteURL()); err != nil {
+ l4g.Error(err.Error())
+ }
+ }()
+
+ return user, nil
+}
+
+func GetAuthorizationCode(service string, props map[string]string, loginHint string) (string, *model.AppError) {
+ sso := utils.Cfg.GetSSOService(service)
+ if sso != nil && !sso.Enable {
+ return "", model.NewAppError("GetAuthorizationCode", "api.user.get_authorization_code.unsupported.app_error", nil, "service="+service, http.StatusNotImplemented)
+ }
+
+ clientId := sso.Id
+ endpoint := sso.AuthEndpoint
+ scope := sso.Scope
+
+ props["hash"] = model.HashPassword(clientId)
+ state := b64.StdEncoding.EncodeToString([]byte(model.MapToJson(props)))
+
+ redirectUri := utils.GetSiteURL() + "/signup/" + service + "/complete"
+
+ authUrl := endpoint + "?response_type=code&client_id=" + clientId + "&redirect_uri=" + url.QueryEscape(redirectUri) + "&state=" + url.QueryEscape(state)
+
+ if len(scope) > 0 {
+ authUrl += "&scope=" + utils.UrlEncode(scope)
+ }
+
+ if len(loginHint) > 0 {
+ authUrl += "&login_hint=" + utils.UrlEncode(loginHint)
+ }
+
+ return authUrl, nil
+}
+
+func AuthorizeOAuthUser(service, code, state, redirectUri string) (io.ReadCloser, string, map[string]string, *model.AppError) {
+ sso := utils.Cfg.GetSSOService(service)
+ if sso == nil || !sso.Enable {
+ return nil, "", nil, model.NewLocAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.unsupported.app_error", nil, "service="+service)
+ }
+
+ stateStr := ""
+ if b, err := b64.StdEncoding.DecodeString(state); err != nil {
+ return nil, "", nil, model.NewLocAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.invalid_state.app_error", nil, err.Error())
+ } else {
+ stateStr = string(b)
+ }
+
+ stateProps := model.MapFromJson(strings.NewReader(stateStr))
+
+ if !model.ComparePassword(stateProps["hash"], sso.Id) {
+ return nil, "", nil, model.NewLocAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.invalid_state.app_error", nil, "")
+ }
+
+ teamId := stateProps["team_id"]
+
+ p := url.Values{}
+ p.Set("client_id", sso.Id)
+ p.Set("client_secret", sso.Secret)
+ p.Set("code", code)
+ p.Set("grant_type", model.ACCESS_TOKEN_GRANT_TYPE)
+ p.Set("redirect_uri", redirectUri)
+
+ tr := &http.Transport{
+ TLSClientConfig: &tls.Config{InsecureSkipVerify: *utils.Cfg.ServiceSettings.EnableInsecureOutgoingConnections},
+ }
+ client := &http.Client{Transport: tr}
+ req, _ := http.NewRequest("POST", sso.TokenEndpoint, strings.NewReader(p.Encode()))
+
+ req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
+ req.Header.Set("Accept", "application/json")
+
+ var ar *model.AccessResponse
+ var respBody []byte
+ if resp, err := client.Do(req); err != nil {
+ return nil, "", nil, model.NewLocAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.token_failed.app_error", nil, err.Error())
+ } else {
+ ar = model.AccessResponseFromJson(resp.Body)
+ defer func() {
+ ioutil.ReadAll(resp.Body)
+ resp.Body.Close()
+ }()
+ if ar == nil {
+ return nil, "", nil, model.NewLocAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.bad_response.app_error", nil, "")
+ }
+ }
+
+ if strings.ToLower(ar.TokenType) != model.ACCESS_TOKEN_TYPE {
+ return nil, "", nil, model.NewLocAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.bad_token.app_error", nil, "token_type="+ar.TokenType+", response_body="+string(respBody))
+ }
+
+ if len(ar.AccessToken) == 0 {
+ return nil, "", nil, model.NewLocAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.missing.app_error", nil, "")
+ }
+
+ p = url.Values{}
+ p.Set("access_token", ar.AccessToken)
+ req, _ = http.NewRequest("GET", sso.UserApiEndpoint, strings.NewReader(""))
+
+ req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
+ req.Header.Set("Accept", "application/json")
+ req.Header.Set("Authorization", "Bearer "+ar.AccessToken)
+
+ if resp, err := client.Do(req); err != nil {
+ return nil, "", nil, model.NewLocAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.service.app_error",
+ map[string]interface{}{"Service": service}, err.Error())
+ } else {
+ return resp.Body, teamId, stateProps, nil
+ }
+
+}
+
+func SwitchEmailToOAuth(email, password, code, service string) (string, *model.AppError) {
+ var user *model.User
+ var err *model.AppError
+ if user, err = GetUserByEmail(email); err != nil {
+ return "", err
+ }
+
+ if err := CheckPasswordAndAllCriteria(user, password, code); err != nil {
+ return "", err
+ }
+
+ stateProps := map[string]string{}
+ stateProps["action"] = model.OAUTH_ACTION_EMAIL_TO_SSO
+ stateProps["email"] = email
+
+ if service == model.USER_AUTH_SERVICE_SAML {
+ return utils.GetSiteURL() + "/login/sso/saml?action=" + model.OAUTH_ACTION_EMAIL_TO_SSO + "&email=" + email, nil
+ } else {
+ if authUrl, err := GetAuthorizationCode(service, stateProps, ""); err != nil {
+ return "", err
+ } else {
+ return authUrl, nil
+ }
+ }
+}
+
+func SwitchOAuthToEmail(email, password, requesterId string) (string, *model.AppError) {
+ var user *model.User
+ var err *model.AppError
+ if user, err = GetUserByEmail(email); err != nil {
+ return "", err
+ }
+
+ if user.Id != requesterId {
+ return "", model.NewAppError("SwitchOAuthToEmail", "api.user.oauth_to_email.context.app_error", nil, "", http.StatusForbidden)
+ }
+
+ if err := UpdatePassword(user, password); err != nil {
+ return "", err
+ }
+
+ T := utils.GetUserTranslations(user.Locale)
+
+ go func() {
+ if err := SendSignInChangeEmail(user.Email, T("api.templates.signin_change_email.body.method_email"), user.Locale, utils.GetSiteURL()); err != nil {
+ l4g.Error(err.Error())
+ }
+ }()
+
+ if err := RevokeAllSessions(requesterId); err != nil {
+ return "", err
+ }
+
+ return "/login?extra=signin_change", nil
+}