2019-11-29 12:59:40 +01:00
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
2017-01-13 13:53:37 -05:00
package app
import (
2020-07-15 09:26:28 -04:00
"errors"
2020-05-06 15:41:10 -04:00
"math"
2017-04-24 12:40:17 -04:00
"net/http"
2020-11-23 14:34:10 -05:00
"os"
2020-06-02 05:17:00 -04:00
"time"
2017-04-24 12:40:17 -04:00
2020-05-06 15:41:10 -04:00
"github.com/mattermost/mattermost-server/v5/audit"
2019-11-28 14:39:38 +01:00
"github.com/mattermost/mattermost-server/v5/mlog"
"github.com/mattermost/mattermost-server/v5/model"
2020-07-15 09:26:28 -04:00
"github.com/mattermost/mattermost-server/v5/store"
2017-01-13 13:53:37 -05:00
)
2017-09-06 17:12:54 -05:00
func ( a * App ) CreateSession ( session * model . Session ) ( * model . Session , * model . AppError ) {
2017-07-31 12:59:32 -04:00
session . Token = ""
2020-02-13 13:26:58 +01:00
session , err := a . Srv ( ) . Store . Session ( ) . Save ( session )
2019-05-30 12:10:24 -04:00
if err != nil {
2020-07-15 09:26:28 -04:00
var invErr * store . ErrInvalidInput
switch {
case errors . As ( err , & invErr ) :
return nil , model . NewAppError ( "CreateSession" , "app.session.save.existing.app_error" , nil , invErr . Error ( ) , http . StatusBadRequest )
default :
return nil , model . NewAppError ( "CreateSession" , "app.session.save.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
}
2018-10-16 00:33:22 +05:30
}
2017-01-19 09:00:13 -05:00
2018-10-16 00:33:22 +05:30
a . AddSessionToCache ( session )
2017-01-19 09:00:13 -05:00
2018-10-16 00:33:22 +05:30
return session , nil
2017-01-19 09:00:13 -05:00
}
2020-11-23 14:34:10 -05:00
func ( a * App ) GetCloudSession ( token string ) ( * model . Session , * model . AppError ) {
apiKey := os . Getenv ( "MM_CLOUD_API_KEY" )
if apiKey != "" && apiKey == token {
// Need a bare-bones session object for later checks
session := & model . Session {
Token : token ,
IsOAuth : false ,
}
session . AddProp ( model . SESSION_PROP_TYPE , model . SESSION_TYPE_CLOUD_KEY )
return session , nil
}
return nil , model . NewAppError ( "GetCloudSession" , "api.context.invalid_token.error" , map [ string ] interface { } { "Token" : token , "Error" : "" } , "The provided token is invalid" , http . StatusUnauthorized )
}
2017-09-06 17:12:54 -05:00
func ( a * App ) GetSession ( token string ) ( * model . Session , * model . AppError ) {
2020-02-13 13:26:58 +01:00
metrics := a . Metrics ( )
2017-01-13 13:53:37 -05:00
var session * model . Session
2019-05-30 18:22:19 -03:00
var err * model . AppError
2020-06-02 05:17:00 -04:00
if err := a . Srv ( ) . sessionCache . Get ( token , & session ) ; err == nil {
2017-01-13 13:53:37 -05:00
if metrics != nil {
2017-03-06 10:15:37 -05:00
metrics . IncrementMemCacheHitCounterSession ( )
2017-01-13 13:53:37 -05:00
}
} else {
if metrics != nil {
2017-03-06 10:15:37 -05:00
metrics . IncrementMemCacheMissCounterSession ( )
2017-01-13 13:53:37 -05:00
}
}
if session == nil {
2020-07-15 09:26:28 -04:00
var nErr error
if session , nErr = a . Srv ( ) . Store . Session ( ) . Get ( token ) ; nErr == nil {
2017-07-31 12:59:32 -04:00
if session != nil {
if session . Token != token {
2020-08-21 21:07:07 +05:30
return nil , model . NewAppError ( "GetSession" , "api.context.invalid_token.error" , map [ string ] interface { } { "Token" : token , "Error" : "" } , "session token is different from the one in DB" , http . StatusUnauthorized )
2017-07-31 12:59:32 -04:00
}
if ! session . IsExpired ( ) {
2017-10-26 14:21:22 -05:00
a . AddSessionToCache ( session )
2017-07-31 12:59:32 -04:00
}
2017-01-13 13:53:37 -05:00
}
2020-07-15 09:26:28 -04:00
} else if nfErr := new ( store . ErrNotFound ) ; ! errors . As ( nErr , & nfErr ) {
return nil , model . NewAppError ( "GetSession" , "app.session.get.app_error" , nil , nErr . Error ( ) , http . StatusInternalServerError )
2017-01-13 13:53:37 -05:00
}
}
2017-07-31 12:59:32 -04:00
if session == nil {
2017-09-06 17:12:54 -05:00
session , err = a . createSessionForUserAccessToken ( token )
2017-07-31 12:59:32 -04:00
if err != nil {
2018-04-20 08:44:01 -04:00
detailedError := ""
statusCode := http . StatusUnauthorized
if err . Id != "app.user_access_token.invalid_or_missing" {
detailedError = err . Error ( )
statusCode = err . StatusCode
2020-08-21 21:07:07 +05:30
} else {
mlog . Warn ( "Error while creating session for user access token" , mlog . Err ( err ) )
2018-04-20 08:44:01 -04:00
}
2020-08-21 21:07:07 +05:30
return nil , model . NewAppError ( "GetSession" , "api.context.invalid_token.error" , map [ string ] interface { } { "Token" : token , "Error" : detailedError } , "" , statusCode )
2017-07-31 12:59:32 -04:00
}
}
2017-01-13 13:53:37 -05:00
if session == nil || session . IsExpired ( ) {
2020-08-21 21:07:07 +05:30
return nil , model . NewAppError ( "GetSession" , "api.context.invalid_token.error" , map [ string ] interface { } { "Token" : token , "Error" : "" } , "session is either nil or expired" , http . StatusUnauthorized )
2017-01-13 13:53:37 -05:00
}
2019-11-04 13:47:59 +01:00
if * a . Config ( ) . ServiceSettings . SessionIdleTimeoutInMinutes > 0 &&
2020-05-19 12:26:54 -04:00
! session . IsOAuth && ! session . IsMobileApp ( ) &&
2020-05-06 15:41:10 -04:00
session . Props [ model . SESSION_PROP_TYPE ] != model . SESSION_TYPE_USER_ACCESS_TOKEN &&
! * a . Config ( ) . ServiceSettings . ExtendSessionLengthWithActivity {
2017-09-28 09:04:52 -04:00
2017-10-18 15:36:43 -07:00
timeout := int64 ( * a . Config ( ) . ServiceSettings . SessionIdleTimeoutInMinutes ) * 1000 * 60
2019-03-15 10:44:27 -07:00
if ( model . GetMillis ( ) - session . LastActivityAt ) > timeout {
2020-08-19 23:27:48 +05:30
// Revoking the session is an asynchronous task anyways since we are not checking
// for the return value of the call before returning the error.
// So moving this to a goroutine has 2 advantages:
// 1. We are treating this as a proper asynchronous task.
// 2. This also fixes a race condition in the web hub, where GetSession
// gets called from (*WebConn).isMemberOfTeam and revoking a session involves
// clearing the webconn cache, which needs the hub again.
a . Srv ( ) . Go ( func ( ) {
err := a . RevokeSessionById ( session . Id )
if err != nil {
mlog . Warn ( "Error while revoking session" , mlog . Err ( err ) )
}
} )
2020-08-21 21:07:07 +05:30
return nil , model . NewAppError ( "GetSession" , "api.context.invalid_token.error" , map [ string ] interface { } { "Token" : token , "Error" : "" } , "idle timeout" , http . StatusUnauthorized )
2017-09-28 09:04:52 -04:00
}
}
2017-01-13 13:53:37 -05:00
return session , nil
}
2017-09-06 17:12:54 -05:00
func ( a * App ) GetSessions ( userId string ) ( [ ] * model . Session , * model . AppError ) {
2018-10-16 00:33:22 +05:30
2020-07-15 09:26:28 -04:00
sessions , err := a . Srv ( ) . Store . Session ( ) . GetSessions ( userId )
if err != nil {
return nil , model . NewAppError ( "GetSessions" , "app.session.get_sessions.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
}
return sessions , nil
2017-01-19 09:00:13 -05:00
}
2019-07-22 22:13:39 +02:00
func ( a * App ) UpdateSessionsIsGuest ( userId string , isGuest bool ) {
2020-02-13 13:26:58 +01:00
sessions , err := a . Srv ( ) . Store . Session ( ) . GetSessions ( userId )
2019-07-22 22:13:39 +02:00
if err != nil {
2019-10-06 14:01:42 +01:00
mlog . Error ( "Unable to get user sessions" , mlog . String ( "user_id" , userId ) , mlog . Err ( err ) )
2019-07-22 22:13:39 +02:00
}
for _ , session := range sessions {
if isGuest {
session . AddProp ( model . SESSION_PROP_IS_GUEST , "true" )
} else {
session . AddProp ( model . SESSION_PROP_IS_GUEST , "false" )
}
2020-02-13 13:26:58 +01:00
err := a . Srv ( ) . Store . Session ( ) . UpdateProps ( session )
2019-07-22 22:13:39 +02:00
if err != nil {
2019-10-06 14:01:42 +01:00
mlog . Error ( "Unable to update isGuest session" , mlog . Err ( err ) )
2019-07-22 22:13:39 +02:00
continue
}
a . AddSessionToCache ( session )
}
}
2017-09-06 17:12:54 -05:00
func ( a * App ) RevokeAllSessions ( userId string ) * model . AppError {
2020-02-13 13:26:58 +01:00
sessions , err := a . Srv ( ) . Store . Session ( ) . GetSessions ( userId )
2019-06-04 13:15:31 +09:00
if err != nil {
2020-07-15 09:26:28 -04:00
return model . NewAppError ( "RevokeAllSessions" , "app.session.get_sessions.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
2018-10-16 00:33:22 +05:30
}
for _ , session := range sessions {
if session . IsOAuth {
a . RevokeAccessToken ( session . Token )
} else {
2020-02-13 13:26:58 +01:00
if err := a . Srv ( ) . Store . Session ( ) . Remove ( session . Id ) ; err != nil {
2020-07-15 09:26:28 -04:00
return model . NewAppError ( "RevokeAllSessions" , "app.session.remove.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
2018-10-16 00:33:22 +05:30
}
2017-01-19 09:00:13 -05:00
}
}
2017-09-19 18:31:35 -05:00
a . ClearSessionCacheForUser ( userId )
2017-01-19 09:00:13 -05:00
return nil
}
2019-07-01 23:28:46 +02:00
// RevokeSessionsFromAllUsers will go through all the sessions active
// in the server and revoke them
func ( a * App ) RevokeSessionsFromAllUsers ( ) * model . AppError {
// revoke tokens before sessions so they can't be used to relogin
2020-07-17 06:56:08 -04:00
nErr := a . Srv ( ) . Store . OAuth ( ) . RemoveAllAccessData ( )
if nErr != nil {
return model . NewAppError ( "RevokeSessionsFromAllUsers" , "app.oauth.remove_access_data.app_error" , nil , nErr . Error ( ) , http . StatusInternalServerError )
2019-07-01 23:28:46 +02:00
}
2020-02-13 13:26:58 +01:00
err := a . Srv ( ) . Store . Session ( ) . RemoveAllSessions ( )
2019-07-01 23:28:46 +02:00
if err != nil {
2020-07-15 09:26:28 -04:00
return model . NewAppError ( "RevokeSessionsFromAllUsers" , "app.session.remove_all_sessions_for_team.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
2019-07-01 23:28:46 +02:00
}
a . ClearSessionCacheForAllUsers ( )
return nil
}
2017-09-19 18:31:35 -05:00
func ( a * App ) ClearSessionCacheForUser ( userId string ) {
2017-09-27 11:52:34 -05:00
a . ClearSessionCacheForUserSkipClusterSend ( userId )
2017-01-13 13:53:37 -05:00
2020-02-13 13:26:58 +01:00
if a . Cluster ( ) != nil {
2017-06-19 08:44:04 -07:00
msg := & model . ClusterMessage {
Event : model . CLUSTER_EVENT_CLEAR_SESSION_CACHE_FOR_USER ,
2017-10-25 11:48:15 -07:00
SendType : model . CLUSTER_SEND_RELIABLE ,
2017-06-19 08:44:04 -07:00
Data : userId ,
}
2020-02-13 13:26:58 +01:00
a . Cluster ( ) . SendClusterMessage ( msg )
2017-01-13 13:53:37 -05:00
}
}
2019-07-01 23:28:46 +02:00
func ( a * App ) ClearSessionCacheForAllUsers ( ) {
a . ClearSessionCacheForAllUsersSkipClusterSend ( )
2020-02-13 13:26:58 +01:00
if a . Cluster ( ) != nil {
2019-07-01 23:28:46 +02:00
msg := & model . ClusterMessage {
Event : model . CLUSTER_EVENT_CLEAR_SESSION_CACHE_FOR_ALL_USERS ,
SendType : model . CLUSTER_SEND_RELIABLE ,
}
2020-02-13 13:26:58 +01:00
a . Cluster ( ) . SendClusterMessage ( msg )
2019-07-01 23:28:46 +02:00
}
}
2017-09-27 11:52:34 -05:00
func ( a * App ) ClearSessionCacheForUserSkipClusterSend ( userId string ) {
2020-06-02 05:17:00 -04:00
if keys , err := a . Srv ( ) . sessionCache . Keys ( ) ; err == nil {
var session * model . Session
for _ , key := range keys {
if err := a . Srv ( ) . sessionCache . Get ( key , & session ) ; err == nil {
if session . UserId == userId {
a . Srv ( ) . sessionCache . Remove ( key )
if a . Metrics ( ) != nil {
a . Metrics ( ) . IncrementMemCacheInvalidationCounterSession ( )
}
2018-03-05 10:35:26 -05:00
}
2017-01-13 13:53:37 -05:00
}
}
}
2017-09-27 11:52:34 -05:00
a . InvalidateWebConnSessionCacheForUser ( userId )
2017-01-13 13:53:37 -05:00
}
2019-07-01 23:28:46 +02:00
func ( a * App ) ClearSessionCacheForAllUsersSkipClusterSend ( ) {
mlog . Info ( "Purging sessions cache" )
2020-02-13 13:26:58 +01:00
a . Srv ( ) . sessionCache . Purge ( )
2019-07-01 23:28:46 +02:00
}
2017-10-26 14:21:22 -05:00
func ( a * App ) AddSessionToCache ( session * model . Session ) {
2020-06-02 05:17:00 -04:00
a . Srv ( ) . sessionCache . SetWithExpiry ( session . Token , session , time . Duration ( int64 ( * a . Config ( ) . ServiceSettings . SessionCacheInMinutes ) ) * time . Minute )
2017-01-13 13:53:37 -05:00
}
2017-10-26 14:21:22 -05:00
func ( a * App ) SessionCacheLength ( ) int {
2020-06-02 05:17:00 -04:00
if l , err := a . Srv ( ) . sessionCache . Len ( ) ; err == nil {
return l
}
return 0
2017-01-13 13:53:37 -05:00
}
2017-01-13 15:17:50 -05:00
2017-09-06 17:12:54 -05:00
func ( a * App ) RevokeSessionsForDeviceId ( userId string , deviceId string , currentSessionId string ) * model . AppError {
2020-02-13 13:26:58 +01:00
sessions , err := a . Srv ( ) . Store . Session ( ) . GetSessions ( userId )
2019-06-04 13:15:31 +09:00
if err != nil {
2020-07-15 09:26:28 -04:00
return model . NewAppError ( "RevokeSessionsForDeviceId" , "app.session.get_sessions.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
2018-10-16 00:33:22 +05:30
}
for _ , session := range sessions {
if session . DeviceId == deviceId && session . Id != currentSessionId {
2019-10-06 14:01:42 +01:00
mlog . Debug ( "Revoking sessionId for userId. Re-login with the same device Id" , mlog . String ( "session_id" , session . Id ) , mlog . String ( "user_id" , userId ) )
2018-10-16 00:33:22 +05:30
if err := a . RevokeSession ( session ) ; err != nil {
// Soft error so we still remove the other sessions
mlog . Error ( err . Error ( ) )
2017-01-13 15:17:50 -05:00
}
}
}
return nil
}
2017-10-04 11:04:17 -04:00
func ( a * App ) GetSessionById ( sessionId string ) ( * model . Session , * model . AppError ) {
2020-02-13 13:26:58 +01:00
session , err := a . Srv ( ) . Store . Session ( ) . Get ( sessionId )
2019-05-30 18:22:19 -03:00
if err != nil {
2020-07-15 09:26:28 -04:00
return nil , model . NewAppError ( "GetSessionById" , "app.session.get.app_error" , nil , err . Error ( ) , http . StatusBadRequest )
2017-10-04 11:04:17 -04:00
}
2020-07-15 09:26:28 -04:00
2019-05-30 18:22:19 -03:00
return session , nil
2017-10-04 11:04:17 -04:00
}
2017-09-06 17:12:54 -05:00
func ( a * App ) RevokeSessionById ( sessionId string ) * model . AppError {
2020-02-13 13:26:58 +01:00
session , err := a . Srv ( ) . Store . Session ( ) . Get ( sessionId )
2019-05-30 18:22:19 -03:00
if err != nil {
2020-07-15 09:26:28 -04:00
return model . NewAppError ( "RevokeSessionById" , "app.session.get.app_error" , nil , err . Error ( ) , http . StatusBadRequest )
2017-01-13 15:17:50 -05:00
}
2019-05-30 18:22:19 -03:00
return a . RevokeSession ( session )
2018-10-16 00:33:22 +05:30
2017-01-13 15:17:50 -05:00
}
2017-09-06 17:12:54 -05:00
func ( a * App ) RevokeSession ( session * model . Session ) * model . AppError {
2017-01-13 15:17:50 -05:00
if session . IsOAuth {
2017-09-06 17:12:54 -05:00
if err := a . RevokeAccessToken ( session . Token ) ; err != nil {
2017-01-13 15:17:50 -05:00
return err
}
} else {
2020-02-13 13:26:58 +01:00
if err := a . Srv ( ) . Store . Session ( ) . Remove ( session . Id ) ; err != nil {
2020-07-15 09:26:28 -04:00
return model . NewAppError ( "RevokeSession" , "app.session.remove.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
2017-01-13 15:17:50 -05:00
}
}
2017-09-19 18:31:35 -05:00
a . ClearSessionCacheForUser ( session . UserId )
2017-01-13 15:17:50 -05:00
return nil
}
2017-09-06 17:12:54 -05:00
func ( a * App ) AttachDeviceId ( sessionId string , deviceId string , expiresAt int64 ) * model . AppError {
2020-02-13 13:26:58 +01:00
_ , err := a . Srv ( ) . Store . Session ( ) . UpdateDeviceId ( sessionId , deviceId , expiresAt )
2019-06-03 10:16:50 -04:00
if err != nil {
2020-07-15 09:26:28 -04:00
return model . NewAppError ( "AttachDeviceId" , "app.session.update_device_id.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
2017-01-13 15:17:50 -05:00
}
return nil
}
2017-05-10 09:48:50 -04:00
2017-09-06 17:12:54 -05:00
func ( a * App ) UpdateLastActivityAtIfNeeded ( session model . Session ) {
2017-05-10 09:48:50 -04:00
now := model . GetMillis ( )
2018-06-27 17:19:06 -04:00
a . UpdateWebConnUserActivity ( session , now )
2017-05-10 09:48:50 -04:00
if now - session . LastActivityAt < model . SESSION_ACTIVITY_TIMEOUT {
return
}
2020-02-13 13:26:58 +01:00
if err := a . Srv ( ) . Store . Session ( ) . UpdateLastActivityAt ( session . Id , now ) ; err != nil {
2019-10-06 14:01:42 +01:00
mlog . Error ( "Failed to update LastActivityAt" , mlog . String ( "user_id" , session . UserId ) , mlog . String ( "session_id" , session . Id ) , mlog . Err ( err ) )
2017-05-10 09:48:50 -04:00
}
session . LastActivityAt = now
2017-10-26 14:21:22 -05:00
a . AddSessionToCache ( & session )
2017-05-10 09:48:50 -04:00
}
2017-07-31 12:59:32 -04:00
2020-05-06 15:41:10 -04:00
// ExtendSessionExpiryIfNeeded extends Session.ExpiresAt based on session lengths in config.
// A new ExpiresAt is only written if enough time has elapsed since last update.
// Returns true only if the session was extended.
func ( a * App ) ExtendSessionExpiryIfNeeded ( session * model . Session ) bool {
if session == nil || session . IsExpired ( ) {
return false
}
sessionLength := a . GetSessionLengthInMillis ( session )
// Only extend the expiry if the lessor of 1% or 1 day has elapsed within the
// current session duration.
threshold := int64 ( math . Min ( float64 ( sessionLength ) * 0.01 , float64 ( 24 * 60 * 60 * 1000 ) ) )
// Minimum session length is 1 day as of this writing, therefore a minimum ~14 minutes threshold.
// However we'll add a sanity check here in case that changes. Minimum 5 minute threshold,
// meaning we won't write a new expiry more than every 5 minutes.
if threshold < 5 * 60 * 1000 {
threshold = 5 * 60 * 1000
}
now := model . GetMillis ( )
elapsed := now - ( session . ExpiresAt - sessionLength )
if elapsed < threshold {
return false
}
auditRec := a . MakeAuditRecord ( "extendSessionExpiry" , audit . Fail )
defer a . LogAuditRec ( auditRec , nil )
auditRec . AddMeta ( "session" , session )
newExpiry := now + sessionLength
if err := a . Srv ( ) . Store . Session ( ) . UpdateExpiresAt ( session . Id , newExpiry ) ; err != nil {
mlog . Error ( "Failed to update ExpiresAt" , mlog . String ( "user_id" , session . UserId ) , mlog . String ( "session_id" , session . Id ) , mlog . Err ( err ) )
auditRec . AddMeta ( "err" , err . Error ( ) )
return false
}
// Update local cache. No need to invalidate cache for cluster as the session cache timeout
// ensures each node will get an extended expiry within the next 10 minutes.
// Worst case is another node may generate a redundant expiry update.
session . ExpiresAt = newExpiry
a . AddSessionToCache ( session )
2020-08-04 16:10:37 -04:00
mlog . Debug ( "Session extended" , mlog . String ( "user_id" , session . UserId ) , mlog . String ( "session_id" , session . Id ) ,
mlog . Int64 ( "newExpiry" , newExpiry ) , mlog . Int64 ( "session_length" , sessionLength ) )
2020-05-06 15:41:10 -04:00
auditRec . Success ( )
auditRec . AddMeta ( "extended_session" , session )
return true
}
// GetSessionLengthInMillis returns the session length, in milliseconds,
// based on the type of session (Mobile, SSO, Web/LDAP).
func ( a * App ) GetSessionLengthInMillis ( session * model . Session ) int64 {
if session == nil {
return 0
}
var days int
if session . IsMobileApp ( ) {
days = * a . Config ( ) . ServiceSettings . SessionLengthMobileInDays
2020-06-30 10:34:05 -04:00
} else if session . IsSSOLogin ( ) {
2020-05-06 15:41:10 -04:00
days = * a . Config ( ) . ServiceSettings . SessionLengthSSOInDays
} else {
days = * a . Config ( ) . ServiceSettings . SessionLengthWebInDays
}
return int64 ( days * 24 * 60 * 60 * 1000 )
}
2020-08-04 16:10:37 -04:00
// SetSessionExpireInDays sets the session's expiry the specified number of days
// relative to either the session creation date or the current time, depending
// on the `ExtendSessionOnActivity` config setting.
func ( a * App ) SetSessionExpireInDays ( session * model . Session , days int ) {
if session . CreateAt == 0 || * a . Config ( ) . ServiceSettings . ExtendSessionLengthWithActivity {
session . ExpiresAt = model . GetMillis ( ) + ( 1000 * 60 * 60 * 24 * int64 ( days ) )
} else {
session . ExpiresAt = session . CreateAt + ( 1000 * 60 * 60 * 24 * int64 ( days ) )
}
}
2017-09-06 17:12:54 -05:00
func ( a * App ) CreateUserAccessToken ( token * model . UserAccessToken ) ( * model . UserAccessToken , * model . AppError ) {
2019-05-21 08:44:29 -05:00
2020-10-26 06:41:27 -03:00
user , nErr := a . Srv ( ) . Store . User ( ) . Get ( token . UserId )
if nErr != nil {
var nfErr * store . ErrNotFound
switch {
case errors . As ( nErr , & nfErr ) :
return nil , model . NewAppError ( "CreateUserAccessToken" , MISSING_ACCOUNT_ERROR , nil , nfErr . Error ( ) , http . StatusNotFound )
default :
return nil , model . NewAppError ( "CreateUserAccessToken" , "app.user.get.app_error" , nil , nErr . Error ( ) , http . StatusInternalServerError )
}
2019-05-21 08:44:29 -05:00
}
if ! * a . Config ( ) . ServiceSettings . EnableUserAccessTokens && ! user . IsBot {
2017-07-31 12:59:32 -04:00
return nil , model . NewAppError ( "CreateUserAccessToken" , "app.user_access_token.disabled" , nil , "" , http . StatusNotImplemented )
}
token . Token = model . NewId ( )
2020-10-26 06:41:27 -03:00
token , nErr = a . Srv ( ) . Store . UserAccessToken ( ) . Save ( token )
2020-07-23 04:58:49 -04:00
if nErr != nil {
var appErr * model . AppError
switch {
case errors . As ( nErr , & appErr ) :
return nil , appErr
default :
2020-10-26 06:41:27 -03:00
return nil , model . NewAppError ( "CreateUserAccessToken" , "app.user_access_token.save.app_error" , nil , nErr . Error ( ) , http . StatusInternalServerError )
2020-07-23 04:58:49 -04:00
}
2017-08-03 08:51:52 -04:00
}
2019-05-31 23:12:03 -07:00
// Don't send emails to bot users.
if ! user . IsBot {
2020-07-07 10:03:21 +02:00
if err := a . Srv ( ) . EmailService . sendUserAccessTokenAddedEmail ( user . Email , user . Locale , a . GetSiteURL ( ) ) ; err != nil {
2020-02-13 13:26:58 +01:00
a . Log ( ) . Error ( "Unable to send user access token added email" , mlog . Err ( err ) , mlog . String ( "user_id" , user . Id ) )
2019-05-31 23:12:03 -07:00
}
2017-07-31 12:59:32 -04:00
}
2017-08-03 08:51:52 -04:00
return token , nil
2017-07-31 12:59:32 -04:00
}
2017-09-06 17:12:54 -05:00
func ( a * App ) createSessionForUserAccessToken ( tokenString string ) ( * model . Session , * model . AppError ) {
2020-07-23 04:58:49 -04:00
token , nErr := a . Srv ( ) . Store . UserAccessToken ( ) . GetByToken ( tokenString )
if nErr != nil {
return nil , model . NewAppError ( "createSessionForUserAccessToken" , "app.user_access_token.invalid_or_missing" , nil , nErr . Error ( ) , http . StatusUnauthorized )
2018-10-16 00:33:22 +05:30
}
2017-10-19 08:10:29 -04:00
2018-10-16 00:33:22 +05:30
if ! token . IsActive {
return nil , model . NewAppError ( "createSessionForUserAccessToken" , "app.user_access_token.invalid_or_missing" , nil , "inactive_token" , http . StatusUnauthorized )
2017-07-31 12:59:32 -04:00
}
2020-10-26 06:41:27 -03:00
user , nErr := a . Srv ( ) . Store . User ( ) . Get ( token . UserId )
if nErr != nil {
var nfErr * store . ErrNotFound
switch {
case errors . As ( nErr , & nfErr ) :
return nil , model . NewAppError ( "createSessionForUserAccessToken" , MISSING_ACCOUNT_ERROR , nil , nfErr . Error ( ) , http . StatusNotFound )
default :
return nil , model . NewAppError ( "createSessionForUserAccessToken" , "app.user.get.app_error" , nil , nErr . Error ( ) , http . StatusInternalServerError )
}
2017-07-31 12:59:32 -04:00
}
2019-06-13 11:54:09 -07:00
if ! * a . Config ( ) . ServiceSettings . EnableUserAccessTokens && ! user . IsBot {
return nil , model . NewAppError ( "createSessionForUserAccessToken" , "app.user_access_token.invalid_or_missing" , nil , "EnableUserAccessTokens=false" , http . StatusUnauthorized )
}
2017-07-31 12:59:32 -04:00
if user . DeleteAt != 0 {
return nil , model . NewAppError ( "createSessionForUserAccessToken" , "app.user_access_token.invalid_or_missing" , nil , "inactive_user_id=" + user . Id , http . StatusUnauthorized )
}
session := & model . Session {
Token : token . Token ,
UserId : user . Id ,
Roles : user . GetRawRoles ( ) ,
IsOAuth : false ,
}
session . AddProp ( model . SESSION_PROP_USER_ACCESS_TOKEN_ID , token . Id )
session . AddProp ( model . SESSION_PROP_TYPE , model . SESSION_TYPE_USER_ACCESS_TOKEN )
2019-06-13 11:54:09 -07:00
if user . IsBot {
session . AddProp ( model . SESSION_PROP_IS_BOT , model . SESSION_PROP_IS_BOT_VALUE )
}
2019-07-22 22:13:39 +02:00
if user . IsGuest ( ) {
session . AddProp ( model . SESSION_PROP_IS_GUEST , "true" )
} else {
session . AddProp ( model . SESSION_PROP_IS_GUEST , "false" )
}
2020-08-04 16:10:37 -04:00
a . SetSessionExpireInDays ( session , model . SESSION_USER_ACCESS_TOKEN_EXPIRY )
2017-07-31 12:59:32 -04:00
2020-07-23 04:58:49 -04:00
session , nErr = a . Srv ( ) . Store . Session ( ) . Save ( session )
2020-07-15 09:26:28 -04:00
if nErr != nil {
var invErr * store . ErrInvalidInput
switch {
case errors . As ( nErr , & invErr ) :
return nil , model . NewAppError ( "CreateSession" , "app.session.save.existing.app_error" , nil , invErr . Error ( ) , http . StatusBadRequest )
default :
return nil , model . NewAppError ( "CreateSession" , "app.session.save.app_error" , nil , nErr . Error ( ) , http . StatusInternalServerError )
}
2018-10-16 00:33:22 +05:30
}
2017-07-31 12:59:32 -04:00
2018-10-16 00:33:22 +05:30
a . AddSessionToCache ( session )
return session , nil
2017-07-31 12:59:32 -04:00
}
2017-09-06 17:12:54 -05:00
func ( a * App ) RevokeUserAccessToken ( token * model . UserAccessToken ) * model . AppError {
2017-07-31 12:59:32 -04:00
var session * model . Session
2020-02-13 13:26:58 +01:00
session , _ = a . Srv ( ) . Store . Session ( ) . Get ( token . Token )
2017-07-31 12:59:32 -04:00
2020-02-13 13:26:58 +01:00
if err := a . Srv ( ) . Store . UserAccessToken ( ) . Delete ( token . Id ) ; err != nil {
2020-07-23 04:58:49 -04:00
return model . NewAppError ( "RevokeUserAccessToken" , "app.user_access_token.delete.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
2017-07-31 12:59:32 -04:00
}
if session == nil {
return nil
}
2017-09-06 17:12:54 -05:00
return a . RevokeSession ( session )
2017-07-31 12:59:32 -04:00
}
2017-10-19 08:10:29 -04:00
func ( a * App ) DisableUserAccessToken ( token * model . UserAccessToken ) * model . AppError {
var session * model . Session
2020-02-13 13:26:58 +01:00
session , _ = a . Srv ( ) . Store . Session ( ) . Get ( token . Token )
2017-10-19 08:10:29 -04:00
2020-02-13 13:26:58 +01:00
if err := a . Srv ( ) . Store . UserAccessToken ( ) . UpdateTokenDisable ( token . Id ) ; err != nil {
2020-07-23 04:58:49 -04:00
return model . NewAppError ( "DisableUserAccessToken" , "app.user_access_token.update_token_disable.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
2017-10-19 08:10:29 -04:00
}
if session == nil {
return nil
}
return a . RevokeSession ( session )
}
func ( a * App ) EnableUserAccessToken ( token * model . UserAccessToken ) * model . AppError {
var session * model . Session
2020-02-13 13:26:58 +01:00
session , _ = a . Srv ( ) . Store . Session ( ) . Get ( token . Token )
2017-10-19 08:10:29 -04:00
2020-02-13 13:26:58 +01:00
err := a . Srv ( ) . Store . UserAccessToken ( ) . UpdateTokenEnable ( token . Id )
2019-06-28 11:02:51 +01:00
if err != nil {
2020-07-23 04:58:49 -04:00
return model . NewAppError ( "EnableUserAccessToken" , "app.user_access_token.update_token_enable.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
2017-10-19 08:10:29 -04:00
}
if session == nil {
return nil
}
return nil
}
2018-01-05 14:46:48 -05:00
func ( a * App ) GetUserAccessTokens ( page , perPage int ) ( [ ] * model . UserAccessToken , * model . AppError ) {
2020-02-13 13:26:58 +01:00
tokens , err := a . Srv ( ) . Store . UserAccessToken ( ) . GetAll ( page * perPage , perPage )
2019-06-20 22:34:55 +05:30
if err != nil {
2020-07-23 04:58:49 -04:00
return nil , model . NewAppError ( "GetUserAccessTokens" , "app.user_access_token.get_all.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
2018-01-05 14:46:48 -05:00
}
2019-06-20 22:34:55 +05:30
2018-10-16 00:33:22 +05:30
for _ , token := range tokens {
token . Token = ""
}
return tokens , nil
2018-01-05 14:46:48 -05:00
}
2017-09-06 17:12:54 -05:00
func ( a * App ) GetUserAccessTokensForUser ( userId string , page , perPage int ) ( [ ] * model . UserAccessToken , * model . AppError ) {
2020-02-13 13:26:58 +01:00
tokens , err := a . Srv ( ) . Store . UserAccessToken ( ) . GetByUser ( userId , page * perPage , perPage )
2019-06-21 18:49:56 +04:00
if err != nil {
2020-07-23 04:58:49 -04:00
return nil , model . NewAppError ( "GetUserAccessTokensForUser" , "app.user_access_token.get_by_user.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
2017-07-31 12:59:32 -04:00
}
2018-10-16 00:33:22 +05:30
for _ , token := range tokens {
token . Token = ""
}
return tokens , nil
2017-07-31 12:59:32 -04:00
}
2017-09-06 17:12:54 -05:00
func ( a * App ) GetUserAccessToken ( tokenId string , sanitize bool ) ( * model . UserAccessToken , * model . AppError ) {
2020-02-13 13:26:58 +01:00
token , err := a . Srv ( ) . Store . UserAccessToken ( ) . Get ( tokenId )
2019-06-20 23:52:10 +05:30
if err != nil {
2020-07-23 04:58:49 -04:00
var nfErr * store . ErrNotFound
switch {
case errors . As ( err , & nfErr ) :
return nil , model . NewAppError ( "GetUserAccessToken" , "app.user_access_token.get_by_user.app_error" , nil , nfErr . Error ( ) , http . StatusNotFound )
default :
return nil , model . NewAppError ( "GetUserAccessToken" , "app.user_access_token.get_by_user.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
}
2017-07-31 12:59:32 -04:00
}
2019-06-20 23:52:10 +05:30
2018-10-16 00:33:22 +05:30
if sanitize {
token . Token = ""
}
return token , nil
2017-07-31 12:59:32 -04:00
}
2018-01-11 16:30:55 -05:00
func ( a * App ) SearchUserAccessTokens ( term string ) ( [ ] * model . UserAccessToken , * model . AppError ) {
2020-02-13 13:26:58 +01:00
tokens , err := a . Srv ( ) . Store . UserAccessToken ( ) . Search ( term )
2019-06-24 07:12:23 -07:00
if err != nil {
2020-07-23 04:58:49 -04:00
return nil , model . NewAppError ( "SearchUserAccessTokens" , "app.user_access_token.search.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
2018-01-11 16:30:55 -05:00
}
2018-10-16 00:33:22 +05:30
for _ , token := range tokens {
token . Token = ""
}
return tokens , nil
2018-01-11 16:30:55 -05:00
}