2017-04-12 08:27:57 -04:00
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
2017-01-13 13:53:37 -05:00
// See License.txt for license information.
package app
import (
2018-04-27 12:49:45 -07:00
"fmt"
2017-04-24 12:40:17 -04:00
"net/http"
2018-04-27 12:49:45 -07:00
"github.com/mattermost/mattermost-server/mlog"
2017-09-06 23:05:10 -07:00
"github.com/mattermost/mattermost-server/model"
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 = ""
2017-09-06 17:12:54 -05:00
if result := <- a . Srv . Store . Session ( ) . Save ( session ) ; result . Err != nil {
2017-01-19 09:00:13 -05:00
return nil , result . Err
} else {
session := result . Data . ( * model . Session )
2017-10-26 14:21:22 -05:00
a . AddSessionToCache ( session )
2017-01-19 09:00:13 -05:00
return session , nil
}
}
2017-09-06 17:12:54 -05:00
func ( a * App ) GetSession ( token string ) ( * model . Session , * model . AppError ) {
2017-09-19 18:31:35 -05:00
metrics := a . Metrics
2017-01-13 13:53:37 -05:00
var session * model . Session
2017-10-26 14:21:22 -05:00
if ts , ok := a . sessionCache . Get ( token ) ; ok {
2017-01-13 13:53:37 -05:00
session = ts . ( * model . Session )
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 {
2017-09-06 17:12:54 -05:00
if sessionResult := <- a . Srv . Store . Session ( ) . Get ( token ) ; sessionResult . Err == nil {
2017-01-13 13:53:37 -05:00
session = sessionResult . Data . ( * model . Session )
2017-07-31 12:59:32 -04:00
if session != nil {
if session . Token != token {
return nil , model . NewAppError ( "GetSession" , "api.context.invalid_token.error" , map [ string ] interface { } { "Token" : token , "Error" : "" } , "" , http . StatusUnauthorized )
}
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
}
2018-04-02 08:22:25 -07:00
} else if sessionResult . Err . StatusCode == http . StatusInternalServerError {
return nil , sessionResult . Err
2017-01-13 13:53:37 -05:00
}
}
2017-07-31 12:59:32 -04:00
if session == nil {
var err * model . AppError
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
}
return nil , model . NewAppError ( "GetSession" , "api.context.invalid_token.error" , map [ string ] interface { } { "Token" : token } , detailedError , statusCode )
2017-07-31 12:59:32 -04:00
}
}
2017-01-13 13:53:37 -05:00
if session == nil || session . IsExpired ( ) {
2017-07-31 12:59:32 -04:00
return nil , model . NewAppError ( "GetSession" , "api.context.invalid_token.error" , map [ string ] interface { } { "Token" : token } , "" , http . StatusUnauthorized )
2017-01-13 13:53:37 -05:00
}
2018-02-06 17:25:49 -06:00
license := a . License ( )
2017-10-18 15:36:43 -07:00
if * a . Config ( ) . ServiceSettings . SessionIdleTimeoutInMinutes > 0 &&
2018-02-06 17:25:49 -06:00
license != nil && * license . Features . Compliance &&
2017-09-28 09:04:52 -04:00
session != nil && ! session . IsOAuth && ! session . IsMobileApp ( ) &&
session . Props [ model . SESSION_PROP_TYPE ] != model . SESSION_TYPE_USER_ACCESS_TOKEN {
2017-10-18 15:36:43 -07:00
timeout := int64 ( * a . Config ( ) . ServiceSettings . SessionIdleTimeoutInMinutes ) * 1000 * 60
2017-09-28 09:04:52 -04:00
if model . GetMillis ( ) - session . LastActivityAt > timeout {
a . RevokeSessionById ( session . Id )
return nil , model . NewAppError ( "GetSession" , "api.context.invalid_token.error" , map [ string ] interface { } { "Token" : token } , "idle timeout" , http . StatusUnauthorized )
}
}
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 ) {
if result := <- a . Srv . Store . Session ( ) . GetSessions ( userId ) ; result . Err != nil {
2017-01-19 09:00:13 -05:00
return nil , result . Err
} else {
return result . Data . ( [ ] * model . Session ) , nil
}
}
2017-09-06 17:12:54 -05:00
func ( a * App ) RevokeAllSessions ( userId string ) * model . AppError {
if result := <- a . Srv . Store . Session ( ) . GetSessions ( userId ) ; result . Err != nil {
2017-01-19 09:00:13 -05:00
return result . Err
} else {
sessions := result . Data . ( [ ] * model . Session )
for _ , session := range sessions {
if session . IsOAuth {
2017-09-06 17:12:54 -05:00
a . RevokeAccessToken ( session . Token )
2017-01-19 09:00:13 -05:00
} else {
2017-09-06 17:12:54 -05:00
if result := <- a . Srv . Store . Session ( ) . Remove ( session . Id ) ; result . Err != nil {
2017-01-19 09:00:13 -05:00
return result . Err
}
}
2017-10-24 09:00:05 -07:00
a . RevokeWebrtcToken ( session . Id )
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
}
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
2017-09-19 18:31:35 -05: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 ,
}
2017-09-19 18:31:35 -05:00
a . Cluster . SendClusterMessage ( msg )
2017-01-13 13:53:37 -05:00
}
}
2017-09-27 11:52:34 -05:00
func ( a * App ) ClearSessionCacheForUserSkipClusterSend ( userId string ) {
2017-10-26 14:21:22 -05:00
keys := a . sessionCache . Keys ( )
2017-01-13 13:53:37 -05:00
for _ , key := range keys {
2017-10-26 14:21:22 -05:00
if ts , ok := a . sessionCache . Get ( key ) ; ok {
2017-01-13 13:53:37 -05:00
session := ts . ( * model . Session )
if session . UserId == userId {
2017-10-26 14:21:22 -05:00
a . sessionCache . Remove ( key )
2018-03-05 10:35:26 -05:00
if a . Metrics != nil {
a . Metrics . IncrementMemCacheInvalidationCounterSession ( )
}
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
}
2017-10-26 14:21:22 -05:00
func ( a * App ) AddSessionToCache ( session * model . Session ) {
a . sessionCache . AddWithExpiresInSecs ( session . Token , session , int64 ( * a . Config ( ) . ServiceSettings . SessionCacheInMinutes * 60 ) )
2017-01-13 13:53:37 -05:00
}
2017-10-26 14:21:22 -05:00
func ( a * App ) SessionCacheLength ( ) int {
return a . sessionCache . Len ( )
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 {
if result := <- a . Srv . Store . Session ( ) . GetSessions ( userId ) ; result . Err != nil {
2017-01-13 15:17:50 -05:00
return result . Err
} else {
sessions := result . Data . ( [ ] * model . Session )
for _ , session := range sessions {
if session . DeviceId == deviceId && session . Id != currentSessionId {
2018-04-27 12:49:45 -07:00
mlog . Debug ( fmt . Sprintf ( "Revoking sessionId=%v for userId=%v re-login with same device Id" , session . Id , userId ) , mlog . String ( "user_id" , userId ) )
2017-09-06 17:12:54 -05:00
if err := a . RevokeSession ( session ) ; err != nil {
2017-01-13 15:17:50 -05:00
// Soft error so we still remove the other sessions
2018-04-27 12:49:45 -07:00
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 ) {
if result := <- a . Srv . Store . Session ( ) . Get ( sessionId ) ; result . Err != nil {
result . Err . StatusCode = http . StatusBadRequest
return nil , result . Err
} else {
return result . Data . ( * model . Session ) , nil
}
}
2017-09-06 17:12:54 -05:00
func ( a * App ) RevokeSessionById ( sessionId string ) * model . AppError {
if result := <- a . Srv . Store . Session ( ) . Get ( sessionId ) ; result . Err != nil {
2017-02-17 10:31:01 -05:00
result . Err . StatusCode = http . StatusBadRequest
2017-01-13 15:17:50 -05:00
return result . Err
} else {
2017-09-06 17:12:54 -05:00
return a . RevokeSession ( result . Data . ( * model . Session ) )
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 {
2017-09-06 17:12:54 -05:00
if result := <- a . Srv . Store . Session ( ) . Remove ( session . Id ) ; result . Err != nil {
2017-01-13 15:17:50 -05:00
return result . Err
}
}
2017-10-24 09:00:05 -07:00
a . RevokeWebrtcToken ( session . Id )
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 {
if result := <- a . Srv . Store . Session ( ) . UpdateDeviceId ( sessionId , deviceId , expiresAt ) ; result . Err != nil {
2017-01-13 15:17:50 -05:00
return result . Err
}
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
}
2017-09-06 17:12:54 -05:00
if result := <- a . Srv . Store . Session ( ) . UpdateLastActivityAt ( session . Id , now ) ; result . Err != nil {
2018-04-27 12:49:45 -07:00
mlog . Error ( fmt . Sprintf ( "Failed to update LastActivityAt for user_id=%v and session_id=%v, err=%v" , session . UserId , session . Id , result . Err ) , mlog . String ( "user_id" , session . UserId ) )
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
2017-09-06 17:12:54 -05:00
func ( a * App ) CreateUserAccessToken ( token * model . UserAccessToken ) ( * model . UserAccessToken , * model . AppError ) {
2017-10-18 15:36:43 -07:00
if ! * a . Config ( ) . ServiceSettings . EnableUserAccessTokens {
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 ( )
2017-09-06 17:12:54 -05:00
uchan := a . Srv . Store . User ( ) . Get ( token . UserId )
2017-08-03 08:51:52 -04:00
2017-09-06 17:12:54 -05:00
if result := <- a . Srv . Store . UserAccessToken ( ) . Save ( token ) ; result . Err != nil {
2017-07-31 12:59:32 -04:00
return nil , result . Err
} else {
2017-08-03 08:51:52 -04:00
token = result . Data . ( * model . UserAccessToken )
}
if result := <- uchan ; result . Err != nil {
2018-04-27 12:49:45 -07:00
mlog . Error ( result . Err . Error ( ) )
2017-08-03 08:51:52 -04:00
} else {
user := result . Data . ( * model . User )
2018-07-21 01:52:20 +09:00
if err := a . SendUserAccessTokenAddedEmail ( user . Email , user . Locale , a . GetSiteURL ( ) ) ; err != nil {
2018-04-27 12:49:45 -07:00
mlog . Error ( err . Error ( ) )
2017-08-03 08:51:52 -04: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 ) {
2017-10-18 15:36:43 -07:00
if ! * a . Config ( ) . ServiceSettings . EnableUserAccessTokens {
2017-07-31 12:59:32 -04:00
return nil , model . NewAppError ( "createSessionForUserAccessToken" , "app.user_access_token.invalid_or_missing" , nil , "EnableUserAccessTokens=false" , http . StatusUnauthorized )
}
var token * model . UserAccessToken
2017-09-06 17:12:54 -05:00
if result := <- a . Srv . Store . UserAccessToken ( ) . GetByToken ( tokenString ) ; result . Err != nil {
2017-07-31 12:59:32 -04:00
return nil , model . NewAppError ( "createSessionForUserAccessToken" , "app.user_access_token.invalid_or_missing" , nil , result . Err . Error ( ) , http . StatusUnauthorized )
} else {
token = result . Data . ( * model . UserAccessToken )
2017-10-19 08:10:29 -04:00
2017-10-30 11:57:24 -05:00
if ! token . IsActive {
2017-10-19 08:10:29 -04:00
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
}
var user * model . User
2017-09-06 17:12:54 -05:00
if result := <- a . Srv . Store . User ( ) . Get ( token . UserId ) ; result . Err != nil {
2017-07-31 12:59:32 -04:00
return nil , result . Err
} else {
user = result . Data . ( * model . User )
}
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 )
session . SetExpireInDays ( model . SESSION_USER_ACCESS_TOKEN_EXPIRY )
2017-09-06 17:12:54 -05:00
if result := <- a . Srv . Store . Session ( ) . Save ( session ) ; result . Err != nil {
2017-07-31 12:59:32 -04:00
return nil , result . Err
} else {
session := result . Data . ( * model . Session )
2017-10-26 14:21:22 -05:00
a . AddSessionToCache ( session )
2017-07-31 12:59:32 -04:00
return session , nil
}
}
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
2017-09-06 17:12:54 -05:00
if result := <- a . Srv . Store . Session ( ) . Get ( token . Token ) ; result . Err == nil {
2017-07-31 12:59:32 -04:00
session = result . Data . ( * model . Session )
}
2017-09-06 17:12:54 -05:00
if result := <- a . Srv . Store . UserAccessToken ( ) . Delete ( token . Id ) ; result . Err != nil {
2017-07-31 12:59:32 -04:00
return result . Err
}
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
if result := <- a . Srv . Store . Session ( ) . Get ( token . Token ) ; result . Err == nil {
session = result . Data . ( * model . Session )
}
if result := <- a . Srv . Store . UserAccessToken ( ) . UpdateTokenDisable ( token . Id ) ; result . Err != nil {
return result . Err
}
if session == nil {
return nil
}
return a . RevokeSession ( session )
}
func ( a * App ) EnableUserAccessToken ( token * model . UserAccessToken ) * model . AppError {
var session * model . Session
if result := <- a . Srv . Store . Session ( ) . Get ( token . Token ) ; result . Err == nil {
session = result . Data . ( * model . Session )
}
if result := <- a . Srv . Store . UserAccessToken ( ) . UpdateTokenEnable ( token . Id ) ; result . Err != nil {
return result . Err
}
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 ) {
if result := <- a . Srv . Store . UserAccessToken ( ) . GetAll ( page * perPage , perPage ) ; result . Err != nil {
return nil , result . Err
} else {
tokens := result . Data . ( [ ] * model . UserAccessToken )
for _ , token := range tokens {
token . Token = ""
}
return tokens , nil
}
}
2017-09-06 17:12:54 -05:00
func ( a * App ) GetUserAccessTokensForUser ( userId string , page , perPage int ) ( [ ] * model . UserAccessToken , * model . AppError ) {
if result := <- a . Srv . Store . UserAccessToken ( ) . GetByUser ( userId , page * perPage , perPage ) ; result . Err != nil {
2017-07-31 12:59:32 -04:00
return nil , result . Err
} else {
tokens := result . Data . ( [ ] * model . UserAccessToken )
for _ , token := range tokens {
token . Token = ""
}
return tokens , nil
}
}
2017-09-06 17:12:54 -05:00
func ( a * App ) GetUserAccessToken ( tokenId string , sanitize bool ) ( * model . UserAccessToken , * model . AppError ) {
if result := <- a . Srv . Store . UserAccessToken ( ) . Get ( tokenId ) ; result . Err != nil {
2017-07-31 12:59:32 -04:00
return nil , result . Err
} else {
token := result . Data . ( * model . UserAccessToken )
if sanitize {
token . Token = ""
}
return token , nil
}
}
2018-01-11 16:30:55 -05:00
func ( a * App ) SearchUserAccessTokens ( term string ) ( [ ] * model . UserAccessToken , * model . AppError ) {
if result := <- a . Srv . Store . UserAccessToken ( ) . Search ( term ) ; result . Err != nil {
return nil , result . Err
} else {
tokens := result . Data . ( [ ] * model . UserAccessToken )
for _ , token := range tokens {
token . Token = ""
}
return tokens , nil
}
}