github.com/jlevesy/mattermost-server@v5.3.2-0.20181003190404-7468f35cb0c8+incompatible/app/oauth.go (about)

     1  // Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
     2  // See License.txt for license information.
     3  
     4  package app
     5  
     6  import (
     7  	"bytes"
     8  	b64 "encoding/base64"
     9  	"fmt"
    10  	"io"
    11  	"io/ioutil"
    12  	"net/http"
    13  	"net/url"
    14  	"strconv"
    15  	"strings"
    16  	"time"
    17  
    18  	"github.com/mattermost/mattermost-server/einterfaces"
    19  	"github.com/mattermost/mattermost-server/mlog"
    20  	"github.com/mattermost/mattermost-server/model"
    21  	"github.com/mattermost/mattermost-server/store"
    22  	"github.com/mattermost/mattermost-server/utils"
    23  )
    24  
    25  const (
    26  	OAUTH_COOKIE_MAX_AGE_SECONDS = 30 * 60 // 30 minutes
    27  	COOKIE_OAUTH                 = "MMOAUTH"
    28  )
    29  
    30  func (a *App) CreateOAuthApp(app *model.OAuthApp) (*model.OAuthApp, *model.AppError) {
    31  	if !a.Config().ServiceSettings.EnableOAuthServiceProvider {
    32  		return nil, model.NewAppError("CreateOAuthApp", "api.oauth.register_oauth_app.turn_off.app_error", nil, "", http.StatusNotImplemented)
    33  	}
    34  
    35  	secret := model.NewId()
    36  	app.ClientSecret = secret
    37  
    38  	if result := <-a.Srv.Store.OAuth().SaveApp(app); result.Err != nil {
    39  		return nil, result.Err
    40  	} else {
    41  		return result.Data.(*model.OAuthApp), nil
    42  	}
    43  }
    44  
    45  func (a *App) GetOAuthApp(appId string) (*model.OAuthApp, *model.AppError) {
    46  	if !a.Config().ServiceSettings.EnableOAuthServiceProvider {
    47  		return nil, model.NewAppError("GetOAuthApp", "api.oauth.allow_oauth.turn_off.app_error", nil, "", http.StatusNotImplemented)
    48  	}
    49  
    50  	if result := <-a.Srv.Store.OAuth().GetApp(appId); result.Err != nil {
    51  		return nil, result.Err
    52  	} else {
    53  		return result.Data.(*model.OAuthApp), nil
    54  	}
    55  }
    56  
    57  func (a *App) UpdateOauthApp(oldApp, updatedApp *model.OAuthApp) (*model.OAuthApp, *model.AppError) {
    58  	if !a.Config().ServiceSettings.EnableOAuthServiceProvider {
    59  		return nil, model.NewAppError("UpdateOauthApp", "api.oauth.allow_oauth.turn_off.app_error", nil, "", http.StatusNotImplemented)
    60  	}
    61  
    62  	updatedApp.Id = oldApp.Id
    63  	updatedApp.CreatorId = oldApp.CreatorId
    64  	updatedApp.CreateAt = oldApp.CreateAt
    65  	updatedApp.ClientSecret = oldApp.ClientSecret
    66  
    67  	if result := <-a.Srv.Store.OAuth().UpdateApp(updatedApp); result.Err != nil {
    68  		return nil, result.Err
    69  	} else {
    70  		return result.Data.([2]*model.OAuthApp)[0], nil
    71  	}
    72  }
    73  
    74  func (a *App) DeleteOAuthApp(appId string) *model.AppError {
    75  	if !a.Config().ServiceSettings.EnableOAuthServiceProvider {
    76  		return model.NewAppError("DeleteOAuthApp", "api.oauth.allow_oauth.turn_off.app_error", nil, "", http.StatusNotImplemented)
    77  	}
    78  
    79  	if err := (<-a.Srv.Store.OAuth().DeleteApp(appId)).Err; err != nil {
    80  		return err
    81  	}
    82  
    83  	a.InvalidateAllCaches()
    84  
    85  	return nil
    86  }
    87  
    88  func (a *App) GetOAuthApps(page, perPage int) ([]*model.OAuthApp, *model.AppError) {
    89  	if !a.Config().ServiceSettings.EnableOAuthServiceProvider {
    90  		return nil, model.NewAppError("GetOAuthApps", "api.oauth.allow_oauth.turn_off.app_error", nil, "", http.StatusNotImplemented)
    91  	}
    92  
    93  	if result := <-a.Srv.Store.OAuth().GetApps(page*perPage, perPage); result.Err != nil {
    94  		return nil, result.Err
    95  	} else {
    96  		return result.Data.([]*model.OAuthApp), nil
    97  	}
    98  }
    99  
   100  func (a *App) GetOAuthAppsByCreator(userId string, page, perPage int) ([]*model.OAuthApp, *model.AppError) {
   101  	if !a.Config().ServiceSettings.EnableOAuthServiceProvider {
   102  		return nil, model.NewAppError("GetOAuthAppsByUser", "api.oauth.allow_oauth.turn_off.app_error", nil, "", http.StatusNotImplemented)
   103  	}
   104  
   105  	if result := <-a.Srv.Store.OAuth().GetAppByUser(userId, page*perPage, perPage); result.Err != nil {
   106  		return nil, result.Err
   107  	} else {
   108  		return result.Data.([]*model.OAuthApp), nil
   109  	}
   110  }
   111  
   112  func (a *App) GetOAuthImplicitRedirect(userId string, authRequest *model.AuthorizeRequest) (string, *model.AppError) {
   113  	session, err := a.GetOAuthAccessTokenForImplicitFlow(userId, authRequest)
   114  	if err != nil {
   115  		return "", err
   116  	}
   117  
   118  	values := &url.Values{}
   119  	values.Add("access_token", session.Token)
   120  	values.Add("token_type", "bearer")
   121  	values.Add("expires_in", strconv.FormatInt((session.ExpiresAt-model.GetMillis())/1000, 10))
   122  	values.Add("scope", authRequest.Scope)
   123  	values.Add("state", authRequest.State)
   124  
   125  	return fmt.Sprintf("%s#%s", authRequest.RedirectUri, values.Encode()), nil
   126  }
   127  
   128  func (a *App) GetOAuthCodeRedirect(userId string, authRequest *model.AuthorizeRequest) (string, *model.AppError) {
   129  	authData := &model.AuthData{UserId: userId, ClientId: authRequest.ClientId, CreateAt: model.GetMillis(), RedirectUri: authRequest.RedirectUri, State: authRequest.State, Scope: authRequest.Scope}
   130  	authData.Code = model.NewId() + model.NewId()
   131  
   132  	if result := <-a.Srv.Store.OAuth().SaveAuthData(authData); result.Err != nil {
   133  		return authRequest.RedirectUri + "?error=server_error&state=" + authRequest.State, nil
   134  	}
   135  
   136  	return authRequest.RedirectUri + "?code=" + url.QueryEscape(authData.Code) + "&state=" + url.QueryEscape(authData.State), nil
   137  }
   138  
   139  func (a *App) AllowOAuthAppAccessToUser(userId string, authRequest *model.AuthorizeRequest) (string, *model.AppError) {
   140  	if !a.Config().ServiceSettings.EnableOAuthServiceProvider {
   141  		return "", model.NewAppError("AllowOAuthAppAccessToUser", "api.oauth.allow_oauth.turn_off.app_error", nil, "", http.StatusNotImplemented)
   142  	}
   143  
   144  	if len(authRequest.Scope) == 0 {
   145  		authRequest.Scope = model.DEFAULT_SCOPE
   146  	}
   147  
   148  	var oauthApp *model.OAuthApp
   149  	if result := <-a.Srv.Store.OAuth().GetApp(authRequest.ClientId); result.Err != nil {
   150  		return "", result.Err
   151  	} else {
   152  		oauthApp = result.Data.(*model.OAuthApp)
   153  	}
   154  
   155  	if !oauthApp.IsValidRedirectURL(authRequest.RedirectUri) {
   156  		return "", model.NewAppError("AllowOAuthAppAccessToUser", "api.oauth.allow_oauth.redirect_callback.app_error", nil, "", http.StatusBadRequest)
   157  	}
   158  
   159  	var redirectURI string
   160  	var err *model.AppError
   161  
   162  	switch authRequest.ResponseType {
   163  	case model.AUTHCODE_RESPONSE_TYPE:
   164  		redirectURI, err = a.GetOAuthCodeRedirect(userId, authRequest)
   165  	case model.IMPLICIT_RESPONSE_TYPE:
   166  		redirectURI, err = a.GetOAuthImplicitRedirect(userId, authRequest)
   167  	default:
   168  		return authRequest.RedirectUri + "?error=unsupported_response_type&state=" + authRequest.State, nil
   169  	}
   170  
   171  	if err != nil {
   172  		mlog.Error(err.Error())
   173  		return authRequest.RedirectUri + "?error=server_error&state=" + authRequest.State, nil
   174  	}
   175  
   176  	// this saves the OAuth2 app as authorized
   177  	authorizedApp := model.Preference{
   178  		UserId:   userId,
   179  		Category: model.PREFERENCE_CATEGORY_AUTHORIZED_OAUTH_APP,
   180  		Name:     authRequest.ClientId,
   181  		Value:    authRequest.Scope,
   182  	}
   183  
   184  	if result := <-a.Srv.Store.Preference().Save(&model.Preferences{authorizedApp}); result.Err != nil {
   185  		mlog.Error(result.Err.Error())
   186  		return authRequest.RedirectUri + "?error=server_error&state=" + authRequest.State, nil
   187  	}
   188  
   189  	return redirectURI, nil
   190  }
   191  
   192  func (a *App) GetOAuthAccessTokenForImplicitFlow(userId string, authRequest *model.AuthorizeRequest) (*model.Session, *model.AppError) {
   193  	if !a.Config().ServiceSettings.EnableOAuthServiceProvider {
   194  		return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.disabled.app_error", nil, "", http.StatusNotImplemented)
   195  	}
   196  
   197  	var oauthApp *model.OAuthApp
   198  	oauthApp, err := a.GetOAuthApp(authRequest.ClientId)
   199  	if err != nil {
   200  		return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.credentials.app_error", nil, "", http.StatusNotFound)
   201  	}
   202  
   203  	user, err := a.GetUser(userId)
   204  	if err != nil {
   205  		return nil, err
   206  	}
   207  
   208  	session, err := a.newSession(oauthApp.Name, user)
   209  	if err != nil {
   210  		return nil, err
   211  	}
   212  
   213  	accessData := &model.AccessData{ClientId: authRequest.ClientId, UserId: user.Id, Token: session.Token, RefreshToken: "", RedirectUri: authRequest.RedirectUri, ExpiresAt: session.ExpiresAt, Scope: authRequest.Scope}
   214  
   215  	if result := <-a.Srv.Store.OAuth().SaveAccessData(accessData); result.Err != nil {
   216  		mlog.Error(fmt.Sprint(result.Err))
   217  		return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.internal_saving.app_error", nil, "", http.StatusInternalServerError)
   218  	}
   219  
   220  	return session, nil
   221  }
   222  
   223  func (a *App) GetOAuthAccessTokenForCodeFlow(clientId, grantType, redirectUri, code, secret, refreshToken string) (*model.AccessResponse, *model.AppError) {
   224  	if !a.Config().ServiceSettings.EnableOAuthServiceProvider {
   225  		return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.disabled.app_error", nil, "", http.StatusNotImplemented)
   226  	}
   227  
   228  	var oauthApp *model.OAuthApp
   229  	if result := <-a.Srv.Store.OAuth().GetApp(clientId); result.Err != nil {
   230  		return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.credentials.app_error", nil, "", http.StatusNotFound)
   231  	} else {
   232  		oauthApp = result.Data.(*model.OAuthApp)
   233  	}
   234  
   235  	if oauthApp.ClientSecret != secret {
   236  		return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.credentials.app_error", nil, "", http.StatusForbidden)
   237  	}
   238  
   239  	var user *model.User
   240  	var accessData *model.AccessData
   241  	var accessRsp *model.AccessResponse
   242  	if grantType == model.ACCESS_TOKEN_GRANT_TYPE {
   243  
   244  		var authData *model.AuthData
   245  		if result := <-a.Srv.Store.OAuth().GetAuthData(code); result.Err != nil {
   246  			return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.expired_code.app_error", nil, "", http.StatusInternalServerError)
   247  		} else {
   248  			authData = result.Data.(*model.AuthData)
   249  		}
   250  
   251  		if authData.IsExpired() {
   252  			<-a.Srv.Store.OAuth().RemoveAuthData(authData.Code)
   253  			return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.expired_code.app_error", nil, "", http.StatusForbidden)
   254  		}
   255  
   256  		if authData.RedirectUri != redirectUri {
   257  			return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.redirect_uri.app_error", nil, "", http.StatusBadRequest)
   258  		}
   259  
   260  		if result := <-a.Srv.Store.User().Get(authData.UserId); result.Err != nil {
   261  			return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.internal_user.app_error", nil, "", http.StatusNotFound)
   262  		} else {
   263  			user = result.Data.(*model.User)
   264  		}
   265  
   266  		if result := <-a.Srv.Store.OAuth().GetPreviousAccessData(user.Id, clientId); result.Err != nil {
   267  			return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.internal.app_error", nil, "", http.StatusInternalServerError)
   268  		} else if result.Data != nil {
   269  			accessData := result.Data.(*model.AccessData)
   270  			if accessData.IsExpired() {
   271  				if access, err := a.newSessionUpdateToken(oauthApp.Name, accessData, user); err != nil {
   272  					return nil, err
   273  				} else {
   274  					accessRsp = access
   275  				}
   276  			} else {
   277  				//return the same token and no need to create a new session
   278  				accessRsp = &model.AccessResponse{
   279  					AccessToken:  accessData.Token,
   280  					TokenType:    model.ACCESS_TOKEN_TYPE,
   281  					RefreshToken: accessData.RefreshToken,
   282  					ExpiresIn:    int32((accessData.ExpiresAt - model.GetMillis()) / 1000),
   283  				}
   284  			}
   285  		} else {
   286  			// create a new session and return new access token
   287  			var session *model.Session
   288  			if result, err := a.newSession(oauthApp.Name, user); err != nil {
   289  				return nil, err
   290  			} else {
   291  				session = result
   292  			}
   293  
   294  			accessData = &model.AccessData{ClientId: clientId, UserId: user.Id, Token: session.Token, RefreshToken: model.NewId(), RedirectUri: redirectUri, ExpiresAt: session.ExpiresAt, Scope: authData.Scope}
   295  
   296  			if result := <-a.Srv.Store.OAuth().SaveAccessData(accessData); result.Err != nil {
   297  				mlog.Error(fmt.Sprint(result.Err))
   298  				return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.internal_saving.app_error", nil, "", http.StatusInternalServerError)
   299  			}
   300  
   301  			accessRsp = &model.AccessResponse{
   302  				AccessToken:  session.Token,
   303  				TokenType:    model.ACCESS_TOKEN_TYPE,
   304  				RefreshToken: accessData.RefreshToken,
   305  				ExpiresIn:    int32(*a.Config().ServiceSettings.SessionLengthSSOInDays * 60 * 60 * 24),
   306  			}
   307  		}
   308  
   309  		<-a.Srv.Store.OAuth().RemoveAuthData(authData.Code)
   310  	} else {
   311  		// when grantType is refresh_token
   312  		if result := <-a.Srv.Store.OAuth().GetAccessDataByRefreshToken(refreshToken); result.Err != nil {
   313  			return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.refresh_token.app_error", nil, "", http.StatusNotFound)
   314  		} else {
   315  			accessData = result.Data.(*model.AccessData)
   316  		}
   317  
   318  		if result := <-a.Srv.Store.User().Get(accessData.UserId); result.Err != nil {
   319  			return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.internal_user.app_error", nil, "", http.StatusNotFound)
   320  		} else {
   321  			user = result.Data.(*model.User)
   322  		}
   323  
   324  		if access, err := a.newSessionUpdateToken(oauthApp.Name, accessData, user); err != nil {
   325  			return nil, err
   326  		} else {
   327  			accessRsp = access
   328  		}
   329  	}
   330  
   331  	return accessRsp, nil
   332  }
   333  
   334  func (a *App) newSession(appName string, user *model.User) (*model.Session, *model.AppError) {
   335  	// set new token an session
   336  	session := &model.Session{UserId: user.Id, Roles: user.Roles, IsOAuth: true}
   337  	session.GenerateCSRF()
   338  	session.SetExpireInDays(*a.Config().ServiceSettings.SessionLengthSSOInDays)
   339  	session.AddProp(model.SESSION_PROP_PLATFORM, appName)
   340  	session.AddProp(model.SESSION_PROP_OS, "OAuth2")
   341  	session.AddProp(model.SESSION_PROP_BROWSER, "OAuth2")
   342  
   343  	if result := <-a.Srv.Store.Session().Save(session); result.Err != nil {
   344  		return nil, model.NewAppError("newSession", "api.oauth.get_access_token.internal_session.app_error", nil, "", http.StatusInternalServerError)
   345  	} else {
   346  		session = result.Data.(*model.Session)
   347  		a.AddSessionToCache(session)
   348  	}
   349  
   350  	return session, nil
   351  }
   352  
   353  func (a *App) newSessionUpdateToken(appName string, accessData *model.AccessData, user *model.User) (*model.AccessResponse, *model.AppError) {
   354  	var session *model.Session
   355  	<-a.Srv.Store.Session().Remove(accessData.Token) //remove the previous session
   356  
   357  	if result, err := a.newSession(appName, user); err != nil {
   358  		return nil, err
   359  	} else {
   360  		session = result
   361  	}
   362  
   363  	accessData.Token = session.Token
   364  	accessData.RefreshToken = model.NewId()
   365  	accessData.ExpiresAt = session.ExpiresAt
   366  	if result := <-a.Srv.Store.OAuth().UpdateAccessData(accessData); result.Err != nil {
   367  		mlog.Error(fmt.Sprint(result.Err))
   368  		return nil, model.NewAppError("newSessionUpdateToken", "web.get_access_token.internal_saving.app_error", nil, "", http.StatusInternalServerError)
   369  	}
   370  	accessRsp := &model.AccessResponse{
   371  		AccessToken:  session.Token,
   372  		RefreshToken: accessData.RefreshToken,
   373  		TokenType:    model.ACCESS_TOKEN_TYPE,
   374  		ExpiresIn:    int32(*a.Config().ServiceSettings.SessionLengthSSOInDays * 60 * 60 * 24),
   375  	}
   376  
   377  	return accessRsp, nil
   378  }
   379  
   380  func (a *App) GetOAuthLoginEndpoint(w http.ResponseWriter, r *http.Request, service, teamId, action, redirectTo, loginHint string) (string, *model.AppError) {
   381  	stateProps := map[string]string{}
   382  	stateProps["action"] = action
   383  	if len(teamId) != 0 {
   384  		stateProps["team_id"] = teamId
   385  	}
   386  
   387  	if len(redirectTo) != 0 {
   388  		stateProps["redirect_to"] = redirectTo
   389  	}
   390  
   391  	if authUrl, err := a.GetAuthorizationCode(w, r, service, stateProps, loginHint); err != nil {
   392  		return "", err
   393  	} else {
   394  		return authUrl, nil
   395  	}
   396  }
   397  
   398  func (a *App) GetOAuthSignupEndpoint(w http.ResponseWriter, r *http.Request, service, teamId string) (string, *model.AppError) {
   399  	stateProps := map[string]string{}
   400  	stateProps["action"] = model.OAUTH_ACTION_SIGNUP
   401  	if len(teamId) != 0 {
   402  		stateProps["team_id"] = teamId
   403  	}
   404  
   405  	if authUrl, err := a.GetAuthorizationCode(w, r, service, stateProps, ""); err != nil {
   406  		return "", err
   407  	} else {
   408  		return authUrl, nil
   409  	}
   410  }
   411  
   412  func (a *App) GetAuthorizedAppsForUser(userId string, page, perPage int) ([]*model.OAuthApp, *model.AppError) {
   413  	if !a.Config().ServiceSettings.EnableOAuthServiceProvider {
   414  		return nil, model.NewAppError("GetAuthorizedAppsForUser", "api.oauth.allow_oauth.turn_off.app_error", nil, "", http.StatusNotImplemented)
   415  	}
   416  
   417  	if result := <-a.Srv.Store.OAuth().GetAuthorizedApps(userId, page*perPage, perPage); result.Err != nil {
   418  		return nil, result.Err
   419  	} else {
   420  		apps := result.Data.([]*model.OAuthApp)
   421  		for k, a := range apps {
   422  			a.Sanitize()
   423  			apps[k] = a
   424  		}
   425  
   426  		return apps, nil
   427  	}
   428  }
   429  
   430  func (a *App) DeauthorizeOAuthAppForUser(userId, appId string) *model.AppError {
   431  	if !a.Config().ServiceSettings.EnableOAuthServiceProvider {
   432  		return model.NewAppError("DeauthorizeOAuthAppForUser", "api.oauth.allow_oauth.turn_off.app_error", nil, "", http.StatusNotImplemented)
   433  	}
   434  
   435  	// revoke app sessions
   436  	if result := <-a.Srv.Store.OAuth().GetAccessDataByUserForApp(userId, appId); result.Err != nil {
   437  		return result.Err
   438  	} else {
   439  		accessData := result.Data.([]*model.AccessData)
   440  
   441  		for _, ad := range accessData {
   442  			if err := a.RevokeAccessToken(ad.Token); err != nil {
   443  				return err
   444  			}
   445  
   446  			if rad := <-a.Srv.Store.OAuth().RemoveAccessData(ad.Token); rad.Err != nil {
   447  				return rad.Err
   448  			}
   449  		}
   450  	}
   451  
   452  	// Deauthorize the app
   453  	if err := (<-a.Srv.Store.Preference().Delete(userId, model.PREFERENCE_CATEGORY_AUTHORIZED_OAUTH_APP, appId)).Err; err != nil {
   454  		return err
   455  	}
   456  
   457  	return nil
   458  }
   459  
   460  func (a *App) RegenerateOAuthAppSecret(app *model.OAuthApp) (*model.OAuthApp, *model.AppError) {
   461  	if !a.Config().ServiceSettings.EnableOAuthServiceProvider {
   462  		return nil, model.NewAppError("RegenerateOAuthAppSecret", "api.oauth.allow_oauth.turn_off.app_error", nil, "", http.StatusNotImplemented)
   463  	}
   464  
   465  	app.ClientSecret = model.NewId()
   466  	if update := <-a.Srv.Store.OAuth().UpdateApp(app); update.Err != nil {
   467  		return nil, update.Err
   468  	}
   469  
   470  	return app, nil
   471  }
   472  
   473  func (a *App) RevokeAccessToken(token string) *model.AppError {
   474  	session, _ := a.GetSession(token)
   475  	schan := a.Srv.Store.Session().Remove(token)
   476  
   477  	if result := <-a.Srv.Store.OAuth().GetAccessData(token); result.Err != nil {
   478  		return model.NewAppError("RevokeAccessToken", "api.oauth.revoke_access_token.get.app_error", nil, "", http.StatusBadRequest)
   479  	}
   480  
   481  	tchan := a.Srv.Store.OAuth().RemoveAccessData(token)
   482  
   483  	if result := <-tchan; result.Err != nil {
   484  		return model.NewAppError("RevokeAccessToken", "api.oauth.revoke_access_token.del_token.app_error", nil, "", http.StatusInternalServerError)
   485  	}
   486  
   487  	if result := <-schan; result.Err != nil {
   488  		return model.NewAppError("RevokeAccessToken", "api.oauth.revoke_access_token.del_session.app_error", nil, "", http.StatusInternalServerError)
   489  	}
   490  
   491  	if session != nil {
   492  		a.ClearSessionCacheForUser(session.UserId)
   493  	}
   494  
   495  	return nil
   496  }
   497  
   498  func (a *App) CompleteOAuth(service string, body io.ReadCloser, teamId string, props map[string]string) (*model.User, *model.AppError) {
   499  	defer body.Close()
   500  
   501  	action := props["action"]
   502  
   503  	switch action {
   504  	case model.OAUTH_ACTION_SIGNUP:
   505  		return a.CreateOAuthUser(service, body, teamId)
   506  	case model.OAUTH_ACTION_LOGIN:
   507  		return a.LoginByOAuth(service, body, teamId)
   508  	case model.OAUTH_ACTION_EMAIL_TO_SSO:
   509  		return a.CompleteSwitchWithOAuth(service, body, props["email"])
   510  	case model.OAUTH_ACTION_SSO_TO_EMAIL:
   511  		return a.LoginByOAuth(service, body, teamId)
   512  	default:
   513  		return a.LoginByOAuth(service, body, teamId)
   514  	}
   515  }
   516  
   517  func (a *App) LoginByOAuth(service string, userData io.Reader, teamId string) (*model.User, *model.AppError) {
   518  	buf := bytes.Buffer{}
   519  	buf.ReadFrom(userData)
   520  
   521  	authData := ""
   522  	provider := einterfaces.GetOauthProvider(service)
   523  	if provider == nil {
   524  		return nil, model.NewAppError("LoginByOAuth", "api.user.login_by_oauth.not_available.app_error",
   525  			map[string]interface{}{"Service": strings.Title(service)}, "", http.StatusNotImplemented)
   526  	} else {
   527  		authUser := provider.GetUserFromJson(bytes.NewReader(buf.Bytes()))
   528  
   529  		if authUser.AuthData != nil {
   530  			authData = *authUser.AuthData
   531  		} else {
   532  			authData = ""
   533  		}
   534  	}
   535  
   536  	if len(authData) == 0 {
   537  		return nil, model.NewAppError("LoginByOAuth", "api.user.login_by_oauth.parse.app_error",
   538  			map[string]interface{}{"Service": service}, "", http.StatusBadRequest)
   539  	}
   540  
   541  	user, err := a.GetUserByAuth(&authData, service)
   542  	if err != nil {
   543  		if err.Id == store.MISSING_AUTH_ACCOUNT_ERROR {
   544  			return a.CreateOAuthUser(service, bytes.NewReader(buf.Bytes()), teamId)
   545  		}
   546  		return nil, err
   547  	}
   548  
   549  	if err = a.UpdateOAuthUserAttrs(bytes.NewReader(buf.Bytes()), user, provider, service); err != nil {
   550  		return nil, err
   551  	}
   552  
   553  	if len(teamId) > 0 {
   554  		err = a.AddUserToTeamByTeamId(teamId, user)
   555  	}
   556  
   557  	if err != nil {
   558  		return nil, err
   559  	}
   560  
   561  	return user, nil
   562  }
   563  
   564  func (a *App) CompleteSwitchWithOAuth(service string, userData io.ReadCloser, email string) (*model.User, *model.AppError) {
   565  	authData := ""
   566  	ssoEmail := ""
   567  	provider := einterfaces.GetOauthProvider(service)
   568  	if provider == nil {
   569  		return nil, model.NewAppError("CompleteSwitchWithOAuth", "api.user.complete_switch_with_oauth.unavailable.app_error",
   570  			map[string]interface{}{"Service": strings.Title(service)}, "", http.StatusNotImplemented)
   571  	} else {
   572  		ssoUser := provider.GetUserFromJson(userData)
   573  		ssoEmail = ssoUser.Email
   574  
   575  		if ssoUser.AuthData != nil {
   576  			authData = *ssoUser.AuthData
   577  		}
   578  	}
   579  
   580  	if len(authData) == 0 {
   581  		return nil, model.NewAppError("CompleteSwitchWithOAuth", "api.user.complete_switch_with_oauth.parse.app_error",
   582  			map[string]interface{}{"Service": service}, "", http.StatusBadRequest)
   583  	}
   584  
   585  	if len(email) == 0 {
   586  		return nil, model.NewAppError("CompleteSwitchWithOAuth", "api.user.complete_switch_with_oauth.blank_email.app_error", nil, "", http.StatusBadRequest)
   587  	}
   588  
   589  	var user *model.User
   590  	if result := <-a.Srv.Store.User().GetByEmail(email); result.Err != nil {
   591  		return nil, result.Err
   592  	} else {
   593  		user = result.Data.(*model.User)
   594  	}
   595  
   596  	if err := a.RevokeAllSessions(user.Id); err != nil {
   597  		return nil, err
   598  	}
   599  
   600  	if result := <-a.Srv.Store.User().UpdateAuthData(user.Id, service, &authData, ssoEmail, true); result.Err != nil {
   601  		return nil, result.Err
   602  	}
   603  
   604  	a.Go(func() {
   605  		if err := a.SendSignInChangeEmail(user.Email, strings.Title(service)+" SSO", user.Locale, a.GetSiteURL()); err != nil {
   606  			mlog.Error(err.Error())
   607  		}
   608  	})
   609  
   610  	return user, nil
   611  }
   612  
   613  func (a *App) CreateOAuthStateToken(extra string) (*model.Token, *model.AppError) {
   614  	token := model.NewToken(model.TOKEN_TYPE_OAUTH, extra)
   615  
   616  	if result := <-a.Srv.Store.Token().Save(token); result.Err != nil {
   617  		return nil, result.Err
   618  	}
   619  
   620  	return token, nil
   621  }
   622  
   623  func (a *App) GetOAuthStateToken(token string) (*model.Token, *model.AppError) {
   624  	if result := <-a.Srv.Store.Token().GetByToken(token); result.Err != nil {
   625  		return nil, model.NewAppError("GetOAuthStateToken", "api.oauth.invalid_state_token.app_error", nil, result.Err.Error(), http.StatusBadRequest)
   626  	} else {
   627  		token := result.Data.(*model.Token)
   628  		if token.Type != model.TOKEN_TYPE_OAUTH {
   629  			return nil, model.NewAppError("GetOAuthStateToken", "api.oauth.invalid_state_token.app_error", nil, "", http.StatusBadRequest)
   630  		}
   631  
   632  		return token, nil
   633  	}
   634  }
   635  
   636  func generateOAuthStateTokenExtra(email, action, cookie string) string {
   637  	return email + ":" + action + ":" + cookie
   638  }
   639  
   640  func (a *App) GetAuthorizationCode(w http.ResponseWriter, r *http.Request, service string, props map[string]string, loginHint string) (string, *model.AppError) {
   641  	sso := a.Config().GetSSOService(service)
   642  	if sso == nil || !sso.Enable {
   643  		return "", model.NewAppError("GetAuthorizationCode", "api.user.get_authorization_code.unsupported.app_error", nil, "service="+service, http.StatusNotImplemented)
   644  	}
   645  
   646  	secure := false
   647  	if GetProtocol(r) == "https" {
   648  		secure = true
   649  	}
   650  
   651  	cookieValue := model.NewId()
   652  	expiresAt := time.Unix(model.GetMillis()/1000+int64(OAUTH_COOKIE_MAX_AGE_SECONDS), 0)
   653  	oauthCookie := &http.Cookie{
   654  		Name:     COOKIE_OAUTH,
   655  		Value:    cookieValue,
   656  		Path:     "/",
   657  		MaxAge:   OAUTH_COOKIE_MAX_AGE_SECONDS,
   658  		Expires:  expiresAt,
   659  		HttpOnly: true,
   660  		Secure:   secure,
   661  	}
   662  
   663  	http.SetCookie(w, oauthCookie)
   664  
   665  	clientId := sso.Id
   666  	endpoint := sso.AuthEndpoint
   667  	scope := sso.Scope
   668  
   669  	tokenExtra := generateOAuthStateTokenExtra(props["email"], props["action"], cookieValue)
   670  	stateToken, err := a.CreateOAuthStateToken(tokenExtra)
   671  	if err != nil {
   672  		return "", err
   673  	}
   674  
   675  	props["token"] = stateToken.Token
   676  	state := b64.StdEncoding.EncodeToString([]byte(model.MapToJson(props)))
   677  
   678  	siteUrl := a.GetSiteURL()
   679  	if strings.TrimSpace(siteUrl) == "" {
   680  		siteUrl = GetProtocol(r) + "://" + r.Host
   681  	}
   682  
   683  	redirectUri := siteUrl + "/signup/" + service + "/complete"
   684  
   685  	authUrl := endpoint + "?response_type=code&client_id=" + clientId + "&redirect_uri=" + url.QueryEscape(redirectUri) + "&state=" + url.QueryEscape(state)
   686  
   687  	if len(scope) > 0 {
   688  		authUrl += "&scope=" + utils.UrlEncode(scope)
   689  	}
   690  
   691  	if len(loginHint) > 0 {
   692  		authUrl += "&login_hint=" + utils.UrlEncode(loginHint)
   693  	}
   694  
   695  	return authUrl, nil
   696  }
   697  
   698  func (a *App) AuthorizeOAuthUser(w http.ResponseWriter, r *http.Request, service, code, state, redirectUri string) (io.ReadCloser, string, map[string]string, *model.AppError) {
   699  	sso := a.Config().GetSSOService(service)
   700  	if sso == nil || !sso.Enable {
   701  		return nil, "", nil, model.NewAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.unsupported.app_error", nil, "service="+service, http.StatusNotImplemented)
   702  	}
   703  
   704  	stateStr := ""
   705  	if b, err := b64.StdEncoding.DecodeString(state); err != nil {
   706  		return nil, "", nil, model.NewAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.invalid_state.app_error", nil, err.Error(), http.StatusBadRequest)
   707  	} else {
   708  		stateStr = string(b)
   709  	}
   710  
   711  	stateProps := model.MapFromJson(strings.NewReader(stateStr))
   712  
   713  	expectedToken, err := a.GetOAuthStateToken(stateProps["token"])
   714  	if err != nil {
   715  		return nil, "", stateProps, err
   716  	}
   717  
   718  	stateEmail := stateProps["email"]
   719  	stateAction := stateProps["action"]
   720  	if stateAction == model.OAUTH_ACTION_EMAIL_TO_SSO && stateEmail == "" {
   721  		return nil, "", stateProps, model.NewAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.invalid_state.app_error", nil, "", http.StatusBadRequest)
   722  	}
   723  
   724  	cookieValue := ""
   725  	if cookie, err := r.Cookie(COOKIE_OAUTH); err != nil {
   726  		return nil, "", stateProps, model.NewAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.invalid_state.app_error", nil, "", http.StatusBadRequest)
   727  	} else {
   728  		cookieValue = cookie.Value
   729  	}
   730  
   731  	expectedTokenExtra := generateOAuthStateTokenExtra(stateEmail, stateAction, cookieValue)
   732  	if expectedTokenExtra != expectedToken.Extra {
   733  		return nil, "", stateProps, model.NewAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.invalid_state.app_error", nil, "", http.StatusBadRequest)
   734  	}
   735  
   736  	a.DeleteToken(expectedToken)
   737  
   738  	cookie := &http.Cookie{
   739  		Name:     COOKIE_OAUTH,
   740  		Value:    "",
   741  		Path:     "/",
   742  		MaxAge:   -1,
   743  		HttpOnly: true,
   744  	}
   745  
   746  	http.SetCookie(w, cookie)
   747  
   748  	teamId := stateProps["team_id"]
   749  
   750  	p := url.Values{}
   751  	p.Set("client_id", sso.Id)
   752  	p.Set("client_secret", sso.Secret)
   753  	p.Set("code", code)
   754  	p.Set("grant_type", model.ACCESS_TOKEN_GRANT_TYPE)
   755  	p.Set("redirect_uri", redirectUri)
   756  
   757  	req, _ := http.NewRequest("POST", sso.TokenEndpoint, strings.NewReader(p.Encode()))
   758  
   759  	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
   760  	req.Header.Set("Accept", "application/json")
   761  
   762  	var ar *model.AccessResponse
   763  	var bodyBytes []byte
   764  	if resp, err := a.HTTPService.MakeClient(true).Do(req); err != nil {
   765  		return nil, "", stateProps, model.NewAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.token_failed.app_error", nil, err.Error(), http.StatusInternalServerError)
   766  	} else {
   767  		bodyBytes, _ = ioutil.ReadAll(resp.Body)
   768  		resp.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))
   769  
   770  		ar = model.AccessResponseFromJson(resp.Body)
   771  		consumeAndClose(resp)
   772  
   773  		if ar == nil || resp.StatusCode != http.StatusOK {
   774  			return nil, "", stateProps, model.NewAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.bad_response.app_error", nil, "response_body="+string(bodyBytes), http.StatusInternalServerError)
   775  		}
   776  	}
   777  
   778  	if strings.ToLower(ar.TokenType) != model.ACCESS_TOKEN_TYPE {
   779  		return nil, "", stateProps, model.NewAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.bad_token.app_error", nil, "token_type="+ar.TokenType+", response_body="+string(bodyBytes), http.StatusInternalServerError)
   780  	}
   781  
   782  	if len(ar.AccessToken) == 0 {
   783  		return nil, "", stateProps, model.NewAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.missing.app_error", nil, "response_body="+string(bodyBytes), http.StatusInternalServerError)
   784  	}
   785  
   786  	p = url.Values{}
   787  	p.Set("access_token", ar.AccessToken)
   788  	req, _ = http.NewRequest("GET", sso.UserApiEndpoint, strings.NewReader(""))
   789  
   790  	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
   791  	req.Header.Set("Accept", "application/json")
   792  	req.Header.Set("Authorization", "Bearer "+ar.AccessToken)
   793  
   794  	if resp, err := a.HTTPService.MakeClient(true).Do(req); err != nil {
   795  		return nil, "", stateProps, model.NewAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.service.app_error", map[string]interface{}{"Service": service}, err.Error(), http.StatusInternalServerError)
   796  	} else {
   797  		bodyBytes, _ = ioutil.ReadAll(resp.Body)
   798  		if resp.StatusCode != http.StatusOK {
   799  			bodyString := string(bodyBytes)
   800  			mlog.Error("Error getting OAuth user: " + bodyString)
   801  			if service == model.SERVICE_GITLAB && resp.StatusCode == http.StatusForbidden && strings.Contains(bodyString, "Terms of Service") {
   802  				return nil, "", stateProps, model.NewAppError("AuthorizeOAuthUser", "oauth.gitlab.tos.error", nil, "", http.StatusBadRequest)
   803  			}
   804  		}
   805  		resp.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))
   806  		return resp.Body, teamId, stateProps, nil
   807  	}
   808  
   809  }
   810  
   811  func (a *App) SwitchEmailToOAuth(w http.ResponseWriter, r *http.Request, email, password, code, service string) (string, *model.AppError) {
   812  	if a.License() != nil && !*a.Config().ServiceSettings.ExperimentalEnableAuthenticationTransfer {
   813  		return "", model.NewAppError("emailToOAuth", "api.user.email_to_oauth.not_available.app_error", nil, "", http.StatusForbidden)
   814  	}
   815  
   816  	var user *model.User
   817  	var err *model.AppError
   818  	if user, err = a.GetUserByEmail(email); err != nil {
   819  		return "", err
   820  	}
   821  
   822  	if err := a.CheckPasswordAndAllCriteria(user, password, code); err != nil {
   823  		return "", err
   824  	}
   825  
   826  	stateProps := map[string]string{}
   827  	stateProps["action"] = model.OAUTH_ACTION_EMAIL_TO_SSO
   828  	stateProps["email"] = email
   829  
   830  	if service == model.USER_AUTH_SERVICE_SAML {
   831  		return a.GetSiteURL() + "/login/sso/saml?action=" + model.OAUTH_ACTION_EMAIL_TO_SSO + "&email=" + utils.UrlEncode(email), nil
   832  	} else {
   833  		if authUrl, err := a.GetAuthorizationCode(w, r, service, stateProps, ""); err != nil {
   834  			return "", err
   835  		} else {
   836  			return authUrl, nil
   837  		}
   838  	}
   839  }
   840  
   841  func (a *App) SwitchOAuthToEmail(email, password, requesterId string) (string, *model.AppError) {
   842  	if a.License() != nil && !*a.Config().ServiceSettings.ExperimentalEnableAuthenticationTransfer {
   843  		return "", model.NewAppError("oauthToEmail", "api.user.oauth_to_email.not_available.app_error", nil, "", http.StatusForbidden)
   844  	}
   845  
   846  	var user *model.User
   847  	var err *model.AppError
   848  	if user, err = a.GetUserByEmail(email); err != nil {
   849  		return "", err
   850  	}
   851  
   852  	if user.Id != requesterId {
   853  		return "", model.NewAppError("SwitchOAuthToEmail", "api.user.oauth_to_email.context.app_error", nil, "", http.StatusForbidden)
   854  	}
   855  
   856  	if err := a.UpdatePassword(user, password); err != nil {
   857  		return "", err
   858  	}
   859  
   860  	T := utils.GetUserTranslations(user.Locale)
   861  
   862  	a.Go(func() {
   863  		if err := a.SendSignInChangeEmail(user.Email, T("api.templates.signin_change_email.body.method_email"), user.Locale, a.GetSiteURL()); err != nil {
   864  			mlog.Error(err.Error())
   865  		}
   866  	})
   867  
   868  	if err := a.RevokeAllSessions(requesterId); err != nil {
   869  		return "", err
   870  	}
   871  
   872  	return "/login?extra=signin_change", nil
   873  }