2015-06-14 23:53:32 -08:00
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
package store
import (
"fmt"
"github.com/mattermost/platform/model"
"github.com/mattermost/platform/utils"
2015-08-27 16:01:17 -07:00
"strings"
2015-06-14 23:53:32 -08:00
)
type SqlUserStore struct {
* SqlStore
}
func NewSqlUserStore ( sqlStore * SqlStore ) UserStore {
us := & SqlUserStore { sqlStore }
for _ , db := range sqlStore . GetAllConns ( ) {
table := db . AddTableWithName ( model . User { } , "Users" ) . SetKeys ( false , "Id" )
table . ColMap ( "Id" ) . SetMaxSize ( 26 )
table . ColMap ( "TeamId" ) . SetMaxSize ( 26 )
table . ColMap ( "Username" ) . SetMaxSize ( 64 )
table . ColMap ( "Password" ) . SetMaxSize ( 128 )
table . ColMap ( "AuthData" ) . SetMaxSize ( 128 )
2015-07-22 15:05:20 -04:00
table . ColMap ( "AuthService" ) . SetMaxSize ( 32 )
2015-06-14 23:53:32 -08:00
table . ColMap ( "Email" ) . SetMaxSize ( 128 )
2015-07-09 13:59:19 -04:00
table . ColMap ( "Nickname" ) . SetMaxSize ( 64 )
2015-07-09 17:06:04 -04:00
table . ColMap ( "FirstName" ) . SetMaxSize ( 64 )
table . ColMap ( "LastName" ) . SetMaxSize ( 64 )
2015-06-14 23:53:32 -08:00
table . ColMap ( "Roles" ) . SetMaxSize ( 64 )
table . ColMap ( "Props" ) . SetMaxSize ( 4000 )
table . ColMap ( "NotifyProps" ) . SetMaxSize ( 2000 )
2015-09-23 10:12:40 -04:00
table . ColMap ( "ThemeProps" ) . SetMaxSize ( 2000 )
2015-06-14 23:53:32 -08:00
table . SetUniqueTogether ( "Email" , "TeamId" )
table . SetUniqueTogether ( "Username" , "TeamId" )
}
return us
}
2015-07-09 13:59:19 -04:00
func ( us SqlUserStore ) UpgradeSchemaIfNeeded ( ) {
2015-09-23 10:12:40 -04:00
us . CreateColumnIfNotExists ( "Users" , "ThemeProps" , "varchar(2000)" , "character varying(2000)" , "{}" )
2015-06-14 23:53:32 -08:00
}
func ( us SqlUserStore ) CreateIndexesIfNotExists ( ) {
2015-07-12 14:56:44 -08:00
us . CreateIndexIfNotExists ( "idx_users_team_id" , "Users" , "TeamId" )
us . CreateIndexIfNotExists ( "idx_users_email" , "Users" , "Email" )
2015-06-14 23:53:32 -08:00
}
func ( us SqlUserStore ) Save ( user * model . User ) StoreChannel {
storeChannel := make ( StoreChannel )
go func ( ) {
result := StoreResult { }
if len ( user . Id ) > 0 {
result . Err = model . NewAppError ( "SqlUserStore.Save" , "Must call update for exisiting user" , "user_id=" + user . Id )
storeChannel <- result
close ( storeChannel )
return
}
user . PreSave ( )
if result . Err = user . IsValid ( ) ; result . Err != nil {
storeChannel <- result
close ( storeChannel )
return
}
2015-07-12 14:56:44 -08:00
if count , err := us . GetMaster ( ) . SelectInt ( "SELECT COUNT(0) FROM Users WHERE TeamId = :TeamId AND DeleteAt = 0" , map [ string ] interface { } { "TeamId" : user . TeamId } ) ; err != nil {
2015-06-14 23:53:32 -08:00
result . Err = model . NewAppError ( "SqlUserStore.Save" , "Failed to get current team member count" , "teamId=" + user . TeamId + ", " + err . Error ( ) )
storeChannel <- result
close ( storeChannel )
return
} else if int ( count ) > utils . Cfg . TeamSettings . MaxUsersPerTeam {
2015-09-13 07:27:58 -07:00
result . Err = model . NewAppError ( "SqlUserStore.Save" , "This team has reached the maxmium number of allowed accounts. Contact your systems administrator to set a higher limit." , "teamId=" + user . TeamId )
2015-06-14 23:53:32 -08:00
storeChannel <- result
close ( storeChannel )
return
}
if err := us . GetMaster ( ) . Insert ( user ) ; err != nil {
2015-07-12 18:19:03 -08:00
if IsUniqueConstraintError ( err . Error ( ) , "Email" , "users_email_teamid_key" ) {
2015-06-14 23:53:32 -08:00
result . Err = model . NewAppError ( "SqlUserStore.Save" , "An account with that email already exists." , "user_id=" + user . Id + ", " + err . Error ( ) )
2015-07-12 18:19:03 -08:00
} else if IsUniqueConstraintError ( err . Error ( ) , "Username" , "users_username_teamid_key" ) {
2015-06-14 23:53:32 -08:00
result . Err = model . NewAppError ( "SqlUserStore.Save" , "An account with that username already exists." , "user_id=" + user . Id + ", " + err . Error ( ) )
} else {
result . Err = model . NewAppError ( "SqlUserStore.Save" , "We couldn't save the account." , "user_id=" + user . Id + ", " + err . Error ( ) )
}
} else {
result . Data = user
}
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
2015-07-06 11:20:40 -08:00
func ( us SqlUserStore ) Update ( user * model . User , allowActiveUpdate bool ) StoreChannel {
2015-06-14 23:53:32 -08:00
storeChannel := make ( StoreChannel )
go func ( ) {
result := StoreResult { }
user . PreUpdate ( )
if result . Err = user . IsValid ( ) ; result . Err != nil {
storeChannel <- result
close ( storeChannel )
return
}
if oldUserResult , err := us . GetMaster ( ) . Get ( model . User { } , user . Id ) ; err != nil {
result . Err = model . NewAppError ( "SqlUserStore.Update" , "We encounted an error finding the account" , "user_id=" + user . Id + ", " + err . Error ( ) )
} else if oldUserResult == nil {
result . Err = model . NewAppError ( "SqlUserStore.Update" , "We couldn't find the existing account to update" , "user_id=" + user . Id )
} else {
oldUser := oldUserResult . ( * model . User )
user . CreateAt = oldUser . CreateAt
user . AuthData = oldUser . AuthData
user . Password = oldUser . Password
user . LastPasswordUpdate = oldUser . LastPasswordUpdate
2015-07-16 08:55:37 -07:00
user . LastPictureUpdate = oldUser . LastPictureUpdate
2015-06-14 23:53:32 -08:00
user . TeamId = oldUser . TeamId
user . LastActivityAt = oldUser . LastActivityAt
user . LastPingAt = oldUser . LastPingAt
user . EmailVerified = oldUser . EmailVerified
2015-07-29 01:26:10 -08:00
user . FailedAttempts = oldUser . FailedAttempts
2015-06-14 23:53:32 -08:00
2015-07-06 11:20:40 -08:00
if ! allowActiveUpdate {
2015-06-14 23:53:32 -08:00
user . Roles = oldUser . Roles
user . DeleteAt = oldUser . DeleteAt
}
if user . Email != oldUser . Email {
user . EmailVerified = false
}
2015-08-20 14:29:40 -07:00
if user . Username != oldUser . Username {
nonUsernameKeys := [ ] string { }
splitKeys := strings . Split ( user . NotifyProps [ "mention_keys" ] , "," )
for _ , key := range splitKeys {
2015-08-27 16:01:17 -07:00
if key != oldUser . Username && key != "@" + oldUser . Username {
2015-08-20 14:29:40 -07:00
nonUsernameKeys = append ( nonUsernameKeys , key )
}
}
user . NotifyProps [ "mention_keys" ] = strings . Join ( nonUsernameKeys , "," ) + user . Username + ",@" + user . Username
}
2015-06-14 23:53:32 -08:00
if count , err := us . GetMaster ( ) . Update ( user ) ; err != nil {
2015-07-29 10:45:33 -07:00
if IsUniqueConstraintError ( err . Error ( ) , "Email" , "users_email_teamid_key" ) {
result . Err = model . NewAppError ( "SqlUserStore.Update" , "This email is already taken. Please choose another" , "user_id=" + user . Id + ", " + err . Error ( ) )
} else if IsUniqueConstraintError ( err . Error ( ) , "Username" , "users_username_teamid_key" ) {
2015-07-29 09:42:13 -07:00
result . Err = model . NewAppError ( "SqlUserStore.Update" , "This username is already taken. Please choose another." , "user_id=" + user . Id + ", " + err . Error ( ) )
} else {
result . Err = model . NewAppError ( "SqlUserStore.Update" , "We encounted an error updating the account" , "user_id=" + user . Id + ", " + err . Error ( ) )
}
2015-06-14 23:53:32 -08:00
} else if count != 1 {
2015-07-05 23:28:45 -08:00
result . Err = model . NewAppError ( "SqlUserStore.Update" , "We couldn't update the account" , fmt . Sprintf ( "user_id=%v, count=%v" , user . Id , count ) )
2015-06-14 23:53:32 -08:00
} else {
result . Data = [ 2 ] * model . User { user , oldUser }
}
}
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
2015-07-16 08:55:37 -07:00
func ( us SqlUserStore ) UpdateLastPictureUpdate ( userId string ) StoreChannel {
2015-07-09 16:37:03 -07:00
storeChannel := make ( StoreChannel )
go func ( ) {
result := StoreResult { }
2015-07-16 08:55:37 -07:00
curTime := model . GetMillis ( )
2015-07-27 17:04:44 -04:00
if _ , err := us . GetMaster ( ) . Exec ( "UPDATE Users SET LastPictureUpdate = :Time, UpdateAt = :Time WHERE Id = :UserId" , map [ string ] interface { } { "Time" : curTime , "UserId" : userId } ) ; err != nil {
2015-07-09 16:37:03 -07:00
result . Err = model . NewAppError ( "SqlUserStore.UpdateUpdateAt" , "We couldn't update the update_at" , "user_id=" + userId )
} else {
result . Data = userId
}
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
2015-06-14 23:53:32 -08:00
func ( us SqlUserStore ) UpdateLastPingAt ( userId string , time int64 ) StoreChannel {
storeChannel := make ( StoreChannel )
go func ( ) {
result := StoreResult { }
2015-07-12 14:56:44 -08:00
if _ , err := us . GetMaster ( ) . Exec ( "UPDATE Users SET LastPingAt = :LastPingAt WHERE Id = :UserId" , map [ string ] interface { } { "LastPingAt" : time , "UserId" : userId } ) ; err != nil {
2015-06-14 23:53:32 -08:00
result . Err = model . NewAppError ( "SqlUserStore.UpdateLastPingAt" , "We couldn't update the last_ping_at" , "user_id=" + userId )
} else {
result . Data = userId
}
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
func ( us SqlUserStore ) UpdateLastActivityAt ( userId string , time int64 ) StoreChannel {
storeChannel := make ( StoreChannel )
go func ( ) {
result := StoreResult { }
2015-07-12 14:56:44 -08:00
if _ , err := us . GetMaster ( ) . Exec ( "UPDATE Users SET LastActivityAt = :LastActivityAt WHERE Id = :UserId" , map [ string ] interface { } { "LastActivityAt" : time , "UserId" : userId } ) ; err != nil {
2015-06-14 23:53:32 -08:00
result . Err = model . NewAppError ( "SqlUserStore.UpdateLastActivityAt" , "We couldn't update the last_activity_at" , "user_id=" + userId )
} else {
result . Data = userId
}
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
func ( us SqlUserStore ) UpdateUserAndSessionActivity ( userId string , sessionId string , time int64 ) StoreChannel {
storeChannel := make ( StoreChannel )
go func ( ) {
result := StoreResult { }
2015-07-12 18:19:03 -08:00
if _ , err := us . GetMaster ( ) . Exec ( "UPDATE Users SET LastActivityAt = :UserLastActivityAt WHERE Id = :UserId" , map [ string ] interface { } { "UserLastActivityAt" : time , "UserId" : userId } ) ; err != nil {
result . Err = model . NewAppError ( "SqlUserStore.UpdateLastActivityAt" , "We couldn't update the last_activity_at" , "1 user_id=" + userId + " session_id=" + sessionId + " err=" + err . Error ( ) )
} else if _ , err := us . GetMaster ( ) . Exec ( "UPDATE Sessions SET LastActivityAt = :SessionLastActivityAt WHERE Id = :SessionId" , map [ string ] interface { } { "SessionLastActivityAt" : time , "SessionId" : sessionId } ) ; err != nil {
result . Err = model . NewAppError ( "SqlUserStore.UpdateLastActivityAt" , "We couldn't update the last_activity_at" , "2 user_id=" + userId + " session_id=" + sessionId + " err=" + err . Error ( ) )
2015-06-14 23:53:32 -08:00
} else {
result . Data = userId
}
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
func ( us SqlUserStore ) UpdatePassword ( userId , hashedPassword string ) StoreChannel {
storeChannel := make ( StoreChannel )
go func ( ) {
result := StoreResult { }
updateAt := model . GetMillis ( )
2015-07-29 01:26:10 -08:00
if _ , err := us . GetMaster ( ) . Exec ( "UPDATE Users SET Password = :Password, LastPasswordUpdate = :LastPasswordUpdate, UpdateAt = :UpdateAt, FailedAttempts = 0 WHERE Id = :UserId" , map [ string ] interface { } { "Password" : hashedPassword , "LastPasswordUpdate" : updateAt , "UpdateAt" : updateAt , "UserId" : userId } ) ; err != nil {
2015-06-14 23:53:32 -08:00
result . Err = model . NewAppError ( "SqlUserStore.UpdatePassword" , "We couldn't update the user password" , "id=" + userId + ", " + err . Error ( ) )
} else {
result . Data = userId
}
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
2015-07-29 01:26:10 -08:00
func ( us SqlUserStore ) UpdateFailedPasswordAttempts ( userId string , attempts int ) StoreChannel {
storeChannel := make ( StoreChannel )
go func ( ) {
result := StoreResult { }
if _ , err := us . GetMaster ( ) . Exec ( "UPDATE Users SET FailedAttempts = :FailedAttempts WHERE Id = :UserId" , map [ string ] interface { } { "FailedAttempts" : attempts , "UserId" : userId } ) ; err != nil {
result . Err = model . NewAppError ( "SqlUserStore.UpdateFailedPasswordAttempts" , "We couldn't update the failed_attempts" , "user_id=" + userId )
} else {
result . Data = userId
}
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
2015-06-14 23:53:32 -08:00
func ( us SqlUserStore ) Get ( id string ) StoreChannel {
storeChannel := make ( StoreChannel )
go func ( ) {
result := StoreResult { }
if obj , err := us . GetReplica ( ) . Get ( model . User { } , id ) ; err != nil {
result . Err = model . NewAppError ( "SqlUserStore.Get" , "We encounted an error finding the account" , "user_id=" + id + ", " + err . Error ( ) )
} else if obj == nil {
result . Err = model . NewAppError ( "SqlUserStore.Get" , "We couldn't find the existing account" , "user_id=" + id )
} else {
result . Data = obj . ( * model . User )
}
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
func ( s SqlUserStore ) GetEtagForProfiles ( teamId string ) StoreChannel {
storeChannel := make ( StoreChannel )
go func ( ) {
result := StoreResult { }
2015-07-12 14:56:44 -08:00
updateAt , err := s . GetReplica ( ) . SelectInt ( "SELECT UpdateAt FROM Users WHERE TeamId = :TeamId ORDER BY UpdateAt DESC LIMIT 1" , map [ string ] interface { } { "TeamId" : teamId } )
2015-06-14 23:53:32 -08:00
if err != nil {
2015-09-17 13:01:40 -07:00
result . Data = fmt . Sprintf ( "%v.%v" , model . CurrentVersion , model . GetMillis ( ) )
2015-06-14 23:53:32 -08:00
} else {
2015-09-17 13:01:40 -07:00
result . Data = fmt . Sprintf ( "%v.%v" , model . CurrentVersion , updateAt )
2015-06-14 23:53:32 -08:00
}
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
func ( us SqlUserStore ) GetProfiles ( teamId string ) StoreChannel {
storeChannel := make ( StoreChannel )
go func ( ) {
result := StoreResult { }
var users [ ] * model . User
2015-07-12 14:56:44 -08:00
if _ , err := us . GetReplica ( ) . Select ( & users , "SELECT * FROM Users WHERE TeamId = :TeamId" , map [ string ] interface { } { "TeamId" : teamId } ) ; err != nil {
2015-06-14 23:53:32 -08:00
result . Err = model . NewAppError ( "SqlUserStore.GetProfiles" , "We encounted an error while finding user profiles" , err . Error ( ) )
} else {
userMap := make ( map [ string ] * model . User )
for _ , u := range users {
u . Password = ""
u . AuthData = ""
userMap [ u . Id ] = u
}
result . Data = userMap
}
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
func ( us SqlUserStore ) GetByEmail ( teamId string , email string ) StoreChannel {
storeChannel := make ( StoreChannel )
go func ( ) {
result := StoreResult { }
user := model . User { }
2015-07-12 14:56:44 -08:00
if err := us . GetReplica ( ) . SelectOne ( & user , "SELECT * FROM Users WHERE TeamId = :TeamId AND Email = :Email" , map [ string ] interface { } { "TeamId" : teamId , "Email" : email } ) ; err != nil {
2015-06-14 23:53:32 -08:00
result . Err = model . NewAppError ( "SqlUserStore.GetByEmail" , "We couldn't find the existing account" , "teamId=" + teamId + ", email=" + email + ", " + err . Error ( ) )
}
result . Data = & user
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
2015-07-15 12:48:50 -04:00
func ( us SqlUserStore ) GetByAuth ( teamId string , authData string , authService string ) StoreChannel {
storeChannel := make ( StoreChannel )
go func ( ) {
result := StoreResult { }
user := model . User { }
2015-07-23 10:15:53 -08:00
if err := us . GetReplica ( ) . SelectOne ( & user , "SELECT * FROM Users WHERE TeamId = :TeamId AND AuthData = :AuthData AND AuthService = :AuthService" , map [ string ] interface { } { "TeamId" : teamId , "AuthData" : authData , "AuthService" : authService } ) ; err != nil {
2015-07-15 12:48:50 -04:00
result . Err = model . NewAppError ( "SqlUserStore.GetByAuth" , "We couldn't find the existing account" , "teamId=" + teamId + ", authData=" + authData + ", authService=" + authService + ", " + err . Error ( ) )
}
result . Data = & user
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
2015-06-14 23:53:32 -08:00
func ( us SqlUserStore ) GetByUsername ( teamId string , username string ) StoreChannel {
storeChannel := make ( StoreChannel )
go func ( ) {
result := StoreResult { }
user := model . User { }
2015-07-12 14:56:44 -08:00
if err := us . GetReplica ( ) . SelectOne ( & user , "SELECT * FROM Users WHERE TeamId = :TeamId AND Username = :Username" , map [ string ] interface { } { "TeamId" : teamId , "Username" : username } ) ; err != nil {
2015-06-14 23:53:32 -08:00
result . Err = model . NewAppError ( "SqlUserStore.GetByUsername" , "We couldn't find the existing account" , "teamId=" + teamId + ", username=" + username + ", " + err . Error ( ) )
}
result . Data = & user
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
func ( us SqlUserStore ) VerifyEmail ( userId string ) StoreChannel {
storeChannel := make ( StoreChannel )
go func ( ) {
result := StoreResult { }
2015-07-12 18:19:03 -08:00
if _ , err := us . GetMaster ( ) . Exec ( "UPDATE Users SET EmailVerified = '1' WHERE Id = :UserId" , map [ string ] interface { } { "UserId" : userId } ) ; err != nil {
2015-06-14 23:53:32 -08:00
result . Err = model . NewAppError ( "SqlUserStore.VerifyEmail" , "Unable to update verify email field" , "userId=" + userId + ", " + err . Error ( ) )
}
result . Data = userId
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
2015-08-26 12:49:07 -04:00
func ( us SqlUserStore ) GetForExport ( teamId string ) StoreChannel {
storeChannel := make ( StoreChannel )
go func ( ) {
result := StoreResult { }
var users [ ] * model . User
if _ , err := us . GetReplica ( ) . Select ( & users , "SELECT * FROM Users WHERE TeamId = :TeamId" , map [ string ] interface { } { "TeamId" : teamId } ) ; err != nil {
result . Err = model . NewAppError ( "SqlUserStore.GetProfiles" , "We encounted an error while finding user profiles" , err . Error ( ) )
} else {
for _ , u := range users {
u . Password = ""
u . AuthData = ""
}
result . Data = users
}
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
2015-09-23 15:52:59 -07:00
func ( us SqlUserStore ) GetTotalUsersCount ( ) StoreChannel {
storeChannel := make ( StoreChannel )
go func ( ) {
result := StoreResult { }
if count , err := us . GetReplica ( ) . SelectInt ( "SELECT COUNT(Id) FROM Users" ) ; err != nil {
result . Err = model . NewAppError ( "SqlUserStore.GetTotalUsersCount" , "We could not count the users" , err . Error ( ) )
} else {
result . Data = count
}
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}