2017-04-12 08:27:57 -04:00
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
2015-06-14 23:53:32 -08:00
// See License.txt for license information.
2017-09-25 09:11:25 -05:00
package sqlstore
2015-06-14 23:53:32 -08:00
import (
2016-03-11 10:48:43 -05:00
"database/sql"
2015-06-14 23:53:32 -08:00
"fmt"
2017-01-30 08:30:02 -05:00
"net/http"
2019-03-19 12:42:08 +00:00
"sort"
2016-01-20 13:36:16 -06:00
"strings"
2016-04-21 22:37:01 -07:00
2019-01-29 15:24:57 -05:00
sq "github.com/Masterminds/squirrel"
2018-06-25 14:34:59 +01:00
"github.com/mattermost/gorp"
2017-09-06 23:05:10 -07:00
"github.com/mattermost/mattermost-server/einterfaces"
"github.com/mattermost/mattermost-server/model"
2017-09-25 09:11:25 -05:00
"github.com/mattermost/mattermost-server/store"
2017-09-06 23:05:10 -07:00
"github.com/mattermost/mattermost-server/utils"
2015-06-14 23:53:32 -08:00
)
2016-01-05 14:53:41 -06:00
const (
2019-06-22 00:14:21 +01:00
PROFILES_IN_CHANNEL_CACHE_SIZE = model . CHANNEL_CACHE_SIZE
PROFILES_IN_CHANNEL_CACHE_SEC = 900 // 15 mins
PROFILE_BY_IDS_CACHE_SIZE = model . SESSION_CACHE_SIZE
PROFILE_BY_IDS_CACHE_SEC = 900 // 15 mins
MAX_GROUP_CHANNELS_FOR_PROFILES = 50
2017-10-31 11:52:10 -04:00
)
var (
USER_SEARCH_TYPE_NAMES_NO_FULL_NAME = [ ] string { "Username" , "Nickname" }
USER_SEARCH_TYPE_NAMES = [ ] string { "Username" , "FirstName" , "LastName" , "Nickname" }
USER_SEARCH_TYPE_ALL_NO_FULL_NAME = [ ] string { "Username" , "Nickname" , "Email" }
USER_SEARCH_TYPE_ALL = [ ] string { "Username" , "FirstName" , "LastName" , "Nickname" , "Email" }
2016-01-05 14:53:41 -06:00
)
2015-06-14 23:53:32 -08:00
type SqlUserStore struct {
2017-06-27 08:02:08 -07:00
SqlStore
2017-09-14 12:01:44 -05:00
metrics einterfaces . MetricsInterface
2019-01-29 15:24:57 -05:00
// usersQuery is a starting point for all queries that return one or more Users.
usersQuery sq . SelectBuilder
2015-06-14 23:53:32 -08:00
}
2016-10-19 14:49:25 -04:00
var profilesInChannelCache * utils . Cache = utils . NewLru ( PROFILES_IN_CHANNEL_CACHE_SIZE )
2016-12-13 19:23:36 -08:00
var profileByIdsCache * utils . Cache = utils . NewLru ( PROFILE_BY_IDS_CACHE_SIZE )
2016-10-19 14:49:25 -04:00
2018-03-05 10:35:26 -05:00
func ( us SqlUserStore ) ClearCaches ( ) {
2016-12-08 07:18:15 -08:00
profilesInChannelCache . Purge ( )
2016-12-13 19:23:36 -08:00
profileByIdsCache . Purge ( )
2018-03-05 10:35:26 -05:00
if us . metrics != nil {
us . metrics . IncrementMemCacheInvalidationCounter ( "Profiles in Channel - Purge" )
us . metrics . IncrementMemCacheInvalidationCounter ( "Profile By Ids - Purge" )
}
2016-12-13 19:23:36 -08:00
}
func ( us SqlUserStore ) InvalidatProfileCacheForUser ( userId string ) {
profileByIdsCache . Remove ( userId )
2018-03-05 10:35:26 -05:00
if us . metrics != nil {
us . metrics . IncrementMemCacheInvalidationCounter ( "Profile By Ids - Remove" )
}
2016-12-08 07:18:15 -08:00
}
2017-09-25 09:11:25 -05:00
func NewSqlUserStore ( sqlStore SqlStore , metrics einterfaces . MetricsInterface ) store . UserStore {
2017-09-14 12:01:44 -05:00
us := & SqlUserStore {
SqlStore : sqlStore ,
metrics : metrics ,
}
2015-06-14 23:53:32 -08:00
2019-03-15 12:28:43 -03:00
us . usersQuery = us . getQueryBuilder ( ) .
2019-06-07 08:09:34 -07:00
Select ( "u.*" , "b.UserId IS NOT NULL AS IsBot" , "COALESCE(b.Description, '') AS BotDescription" ) .
MM-12393 Server side of bot accounts. (#10378)
* bots model, store and api (#9903)
* bots model, store and api
Fixes: MM-13100, MM-13101, MM-13103, MM-13105, MMM-13119
* uncomment tests incorrectly commented, and fix merge issues
* add etags support
* add missing licenses
* remove unused sqlbuilder.go (for now...)
* rejig permissions
* split out READ_BOTS into READ_BOTS and READ_OTHERS_BOTS, the latter
implicitly allowing the former
* make MANAGE_OTHERS_BOTS imply MANAGE_BOTS
* conform to general rest api pattern
* eliminate redundant http.StatusOK
* Update api4/bot.go
Co-Authored-By: lieut-data <jesse.hallam@gmail.com>
* s/model.UserFromBotModel/model.UserFromBot/g
* Update model/bot.go
Co-Authored-By: lieut-data <jesse.hallam@gmail.com>
* Update model/client4.go
Co-Authored-By: lieut-data <jesse.hallam@gmail.com>
* move sessionHasPermissionToManageBot to app/authorization.go
* use api.ApiSessionRequired for createBot
* introduce BOT_DESCRIPTION_MAX_RUNES constant
* MM-13512 Prevent getting a user by email based on privacy settings (#10021)
* MM-13512 Prevent getting a user by email based on privacy settings
* Add additional config settings to tests
* upgrade db to 5.7 (#10019)
* MM-13526 Add validation when setting a user's Locale field (#10022)
* Fix typos (#10024)
* Fixing first user being created with system admin privilages without being explicity specified. (#10014)
* Revert "Support for Embeded chat (#9129)" (#10017)
This reverts commit 3fcecd521a5c6ccfdb52fb4c3fb1f8c6ea528a4e.
* s/DisableBot/UpdateBotActive
* add permissions on upgrade
* Update NOTICE.txt (#10054)
- add new dependency (text)
- handle switch to forked dependency (go-gomail -> go-mail)
- misc copyright owner updates
* avoid leaking bot knowledge without permission
* [GH-6798] added a new api endpoint to get the bulk reactions for posts (#10049)
* 6798 added a new api to get the bulk reactions for posts
* 6798 added the permsission check before getting the reactions
* GH-6798 added a new app function for the new endpoint
* 6798 added a store method to get reactions for multiple posts
* 6798 connected the app function with the new store function
* 6798 fixed the review comments
* MM-13559 Update model.post.is_valid.file_ids.app_error text per report (#10055)
Ticket: https://mattermost.atlassian.net/browse/MM-13559
Report: https://github.com/mattermost/mattermost-server/issues/10023
* Trigger Login Hooks with OAuth (#10061)
* make BotStore.GetAll deterministic even on duplicate CreateAt
* fix spurious TestMuteCommandSpecificChannel test failure
See
https://community-daily.mattermost.com/core/pl/px9p8s3dzbg1pf3ddrm5cr36uw
* fix race in TestExportUserChannels
* TestExportUserChannels: remove SaveMember call, as it is redundant and used to be silently failing anyway
* MM-13117: bot tokens (#10111)
* eliminate redundant Client/AdminClient declarations
* harden TestUpdateChannelScheme to API failures
* eliminate unnecessary config restoration
* minor cleanup
* make TestGenerateMfaSecret config dependency explicit
* TestCreateUserAccessToken for bots
* TestGetUserAccessToken* for bots
* leverage SessionHasPermissionToUserOrBot for user token APIs
* Test(Revoke|Disable|Enable)UserAccessToken
* make EnableUserAccessTokens explicit, so as to not rely on local config.json
* uncomment TestResetPassword, but still skip
* mark assert(Invalid)Token as helper
* fix whitespace issues
* fix mangled comments
* MM-13116: bot plugin api (#10113)
* MM-13117: expose bot API to plugins
This also changes the `CreatorId` column definition to allow for plugin
ids, as the default unless the plugin overrides is to use the plugin id
here. This branch hasn't hit master yet, so no migration needed.
* gofmt issues
* expunge use of BotList in plugin/client API
* introduce model.BotGetOptions
* use botUserId term for clarity
* MM-13129 Adding functionality to deal with orphaned bots (#10238)
* Add way to list orphaned bots.
* Add /assign route to modify ownership of bot accounts.
* Apply suggestions from code review
Co-Authored-By: crspeller <crspeller@gmail.com>
* MM-13120: add IsBot field to returned user objects (#10103)
* MM-13104: forbid bot login (#10251)
* MM-13104: disallow bot login
* fix shadowing
* MM-13136 Disable user bots when user is disabled. (#10293)
* Disable user bots when user is disabled.
* Grammer.
Co-Authored-By: crspeller <crspeller@gmail.com>
* Fixing bot branch for test changes.
* Don't use external dependancies in bot plugin tests.
* Rename bot CreatorId to OwnerId
* Adding ability to re-enable bots
* Fixing IsBot to not attempt to be saved to DB.
* Adding diagnostics and licencing counting for bot accounts.
* Modifying gorp to allow reading of '-' fields.
* Removing unnessisary nil values from UserCountOptions.
* Changing comment to GoDoc format
* Improving user count SQL
* Some improvments from feedback.
* Omit empty on User.IsBot
2019-03-05 07:06:45 -08:00
From ( "Users u" ) .
LeftJoin ( "Bots b ON ( b.UserId = u.Id )" )
2019-01-29 15:24:57 -05:00
2015-06-14 23:53:32 -08:00
for _ , db := range sqlStore . GetAllConns ( ) {
table := db . AddTableWithName ( model . User { } , "Users" ) . SetKeys ( false , "Id" )
table . ColMap ( "Id" ) . SetMaxSize ( 26 )
2016-04-21 22:37:01 -07:00
table . ColMap ( "Username" ) . SetMaxSize ( 64 ) . SetUnique ( true )
2015-06-14 23:53:32 -08:00
table . ColMap ( "Password" ) . SetMaxSize ( 128 )
2016-05-11 11:04:30 -07:00
table . ColMap ( "AuthData" ) . SetMaxSize ( 128 ) . SetUnique ( true )
2015-07-22 15:05:20 -04:00
table . ColMap ( "AuthService" ) . SetMaxSize ( 32 )
2016-04-21 22:37:01 -07:00
table . ColMap ( "Email" ) . SetMaxSize ( 128 ) . SetUnique ( true )
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 )
2017-08-01 11:06:53 -04:00
table . ColMap ( "Roles" ) . SetMaxSize ( 256 )
2015-06-14 23:53:32 -08:00
table . ColMap ( "Props" ) . SetMaxSize ( 4000 )
table . ColMap ( "NotifyProps" ) . SetMaxSize ( 2000 )
2016-01-19 22:00:01 -06:00
table . ColMap ( "Locale" ) . SetMaxSize ( 5 )
2016-03-30 12:49:29 -04:00
table . ColMap ( "MfaSecret" ) . SetMaxSize ( 128 )
2018-01-26 08:37:12 -05:00
table . ColMap ( "Position" ) . SetMaxSize ( 128 )
2018-03-22 06:53:43 -07:00
table . ColMap ( "Timezone" ) . SetMaxSize ( 256 )
2015-06-14 23:53:32 -08:00
}
return us
}
func ( us SqlUserStore ) CreateIndexesIfNotExists ( ) {
2015-07-12 14:56:44 -08:00
us . CreateIndexIfNotExists ( "idx_users_email" , "Users" , "Email" )
2016-10-24 17:05:27 -03:00
us . CreateIndexIfNotExists ( "idx_users_update_at" , "Users" , "UpdateAt" )
us . CreateIndexIfNotExists ( "idx_users_create_at" , "Users" , "CreateAt" )
us . CreateIndexIfNotExists ( "idx_users_delete_at" , "Users" , "DeleteAt" )
2016-10-19 14:49:25 -04:00
2017-10-09 10:16:14 -07:00
if us . DriverName ( ) == model . DATABASE_DRIVER_POSTGRES {
2018-11-09 15:34:31 -05:00
us . CreateIndexIfNotExists ( "idx_users_email_lower_textpattern" , "Users" , "lower(Email) text_pattern_ops" )
us . CreateIndexIfNotExists ( "idx_users_username_lower_textpattern" , "Users" , "lower(Username) text_pattern_ops" )
us . CreateIndexIfNotExists ( "idx_users_nickname_lower_textpattern" , "Users" , "lower(Nickname) text_pattern_ops" )
us . CreateIndexIfNotExists ( "idx_users_firstname_lower_textpattern" , "Users" , "lower(FirstName) text_pattern_ops" )
us . CreateIndexIfNotExists ( "idx_users_lastname_lower_textpattern" , "Users" , "lower(LastName) text_pattern_ops" )
2017-09-27 18:44:22 +01:00
}
2017-10-31 11:52:10 -04:00
us . CreateFullTextIndexIfNotExists ( "idx_users_all_txt" , "Users" , strings . Join ( USER_SEARCH_TYPE_ALL , ", " ) )
us . CreateFullTextIndexIfNotExists ( "idx_users_all_no_full_name_txt" , "Users" , strings . Join ( USER_SEARCH_TYPE_ALL_NO_FULL_NAME , ", " ) )
us . CreateFullTextIndexIfNotExists ( "idx_users_names_txt" , "Users" , strings . Join ( USER_SEARCH_TYPE_NAMES , ", " ) )
us . CreateFullTextIndexIfNotExists ( "idx_users_names_no_full_name_txt" , "Users" , strings . Join ( USER_SEARCH_TYPE_NAMES_NO_FULL_NAME , ", " ) )
2015-06-14 23:53:32 -08:00
}
2017-09-25 09:11:25 -05:00
func ( us SqlUserStore ) Save ( user * model . User ) store . StoreChannel {
2017-10-06 08:12:10 -07:00
return store . Do ( func ( result * store . StoreResult ) {
2015-06-14 23:53:32 -08:00
if len ( user . Id ) > 0 {
2017-09-18 17:40:56 +01:00
result . Err = model . NewAppError ( "SqlUserStore.Save" , "store.sql_user.save.existing.app_error" , nil , "user_id=" + user . Id , http . StatusBadRequest )
2015-06-14 23:53:32 -08:00
return
}
user . PreSave ( )
if result . Err = user . IsValid ( ) ; result . Err != nil {
return
}
if err := us . GetMaster ( ) . Insert ( user ) ; err != nil {
2017-09-06 14:00:56 -04:00
if IsUniqueConstraintError ( err , [ ] string { "Email" , "users_email_key" , "idx_users_email_unique" } ) {
2017-01-30 08:30:02 -05:00
result . Err = model . NewAppError ( "SqlUserStore.Save" , "store.sql_user.save.email_exists.app_error" , nil , "user_id=" + user . Id + ", " + err . Error ( ) , http . StatusBadRequest )
2017-09-06 14:00:56 -04:00
} else if IsUniqueConstraintError ( err , [ ] string { "Username" , "users_username_key" , "idx_users_username_unique" } ) {
2017-01-30 08:30:02 -05:00
result . Err = model . NewAppError ( "SqlUserStore.Save" , "store.sql_user.save.username_exists.app_error" , nil , "user_id=" + user . Id + ", " + err . Error ( ) , http . StatusBadRequest )
2015-06-14 23:53:32 -08:00
} else {
2017-09-18 17:40:56 +01:00
result . Err = model . NewAppError ( "SqlUserStore.Save" , "store.sql_user.save.app_error" , nil , "user_id=" + user . Id + ", " + err . Error ( ) , http . StatusInternalServerError )
2015-06-14 23:53:32 -08:00
}
} else {
result . Data = user
}
2017-10-06 08:12:10 -07:00
} )
2015-06-14 23:53:32 -08:00
}
2019-05-28 09:40:29 +02:00
func ( us SqlUserStore ) Update ( user * model . User , trustedUpdateData bool ) ( * model . UserUpdate , * model . AppError ) {
user . PreUpdate ( )
2015-06-14 23:53:32 -08:00
2019-05-28 09:40:29 +02:00
if err := user . IsValid ( ) ; err != nil {
return nil , err
}
2015-06-14 23:53:32 -08:00
2019-05-28 09:40:29 +02:00
oldUserResult , err := us . GetMaster ( ) . Get ( model . User { } , user . Id )
if err != nil {
return nil , model . NewAppError ( "SqlUserStore.Update" , "store.sql_user.update.finding.app_error" , nil , "user_id=" + user . Id + ", " + err . Error ( ) , http . StatusInternalServerError )
}
2015-06-14 23:53:32 -08:00
2019-05-28 09:40:29 +02:00
if oldUserResult == nil {
return nil , model . NewAppError ( "SqlUserStore.Update" , "store.sql_user.update.find.app_error" , nil , "user_id=" + user . Id , http . StatusBadRequest )
}
2015-06-14 23:53:32 -08:00
2019-05-28 09:40:29 +02:00
oldUser := oldUserResult . ( * model . User )
user . CreateAt = oldUser . CreateAt
user . AuthData = oldUser . AuthData
user . AuthService = oldUser . AuthService
user . Password = oldUser . Password
user . LastPasswordUpdate = oldUser . LastPasswordUpdate
user . LastPictureUpdate = oldUser . LastPictureUpdate
user . EmailVerified = oldUser . EmailVerified
user . FailedAttempts = oldUser . FailedAttempts
user . MfaSecret = oldUser . MfaSecret
user . MfaActive = oldUser . MfaActive
if ! trustedUpdateData {
user . Roles = oldUser . Roles
user . DeleteAt = oldUser . DeleteAt
}
2015-08-20 14:29:40 -07:00
2019-05-28 09:40:29 +02:00
if user . IsOAuthUser ( ) {
if ! trustedUpdateData {
user . Email = oldUser . Email
2015-06-14 23:53:32 -08:00
}
2019-05-28 09:40:29 +02:00
} else if user . IsLDAPUser ( ) && ! trustedUpdateData {
if user . Username != oldUser . Username || user . Email != oldUser . Email {
return nil , model . NewAppError ( "SqlUserStore.Update" , "store.sql_user.update.can_not_change_ldap.app_error" , nil , "user_id=" + user . Id , http . StatusBadRequest )
}
} else if user . Email != oldUser . Email {
user . EmailVerified = false
}
if user . Username != oldUser . Username {
user . UpdateMentionKeysFromUsername ( oldUser . Username )
}
count , err := us . GetMaster ( ) . Update ( user )
if err != nil {
if IsUniqueConstraintError ( err , [ ] string { "Email" , "users_email_key" , "idx_users_email_unique" } ) {
return nil , model . NewAppError ( "SqlUserStore.Update" , "store.sql_user.update.email_taken.app_error" , nil , "user_id=" + user . Id + ", " + err . Error ( ) , http . StatusBadRequest )
} else if IsUniqueConstraintError ( err , [ ] string { "Username" , "users_username_key" , "idx_users_username_unique" } ) {
return nil , model . NewAppError ( "SqlUserStore.Update" , "store.sql_user.update.username_taken.app_error" , nil , "user_id=" + user . Id + ", " + err . Error ( ) , http . StatusBadRequest )
}
return nil , model . NewAppError ( "SqlUserStore.Update" , "store.sql_user.update.updating.app_error" , nil , "user_id=" + user . Id + ", " + err . Error ( ) , http . StatusInternalServerError )
}
if count != 1 {
return nil , model . NewAppError ( "SqlUserStore.Update" , "store.sql_user.update.app_error" , nil , fmt . Sprintf ( "user_id=%v, count=%v" , user . Id , count ) , http . StatusInternalServerError )
}
user . Sanitize ( map [ string ] bool { } )
oldUser . Sanitize ( map [ string ] bool { } )
return & model . UserUpdate { New : user , Old : oldUser } , nil
2015-06-14 23:53:32 -08:00
}
2017-09-25 09:11:25 -05:00
func ( us SqlUserStore ) UpdateLastPictureUpdate ( userId string ) store . StoreChannel {
2017-10-06 08:12:10 -07:00
return store . Do ( func ( result * store . 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 {
2019-01-29 15:24:57 -05:00
result . Err = model . NewAppError ( "SqlUserStore.UpdateLastPictureUpdate" , "store.sql_user.update_last_picture_update.app_error" , nil , "user_id=" + userId , http . StatusInternalServerError )
2015-07-09 16:37:03 -07:00
} else {
result . Data = userId
}
2017-10-06 08:12:10 -07:00
} )
2015-07-09 16:37:03 -07:00
}
2018-10-02 08:04:38 +02:00
func ( us SqlUserStore ) ResetLastPictureUpdate ( userId string ) store . StoreChannel {
return store . Do ( func ( result * store . StoreResult ) {
if _ , err := us . GetMaster ( ) . Exec ( "UPDATE Users SET LastPictureUpdate = :Time, UpdateAt = :Time WHERE Id = :UserId" , map [ string ] interface { } { "Time" : 0 , "UserId" : userId } ) ; err != nil {
2019-01-29 15:24:57 -05:00
result . Err = model . NewAppError ( "SqlUserStore.ResetLastPictureUpdate" , "store.sql_user.update_last_picture_update.app_error" , nil , "user_id=" + userId , http . StatusInternalServerError )
2018-10-02 08:04:38 +02:00
} else {
result . Data = userId
}
} )
}
2017-09-25 09:11:25 -05:00
func ( us SqlUserStore ) UpdateUpdateAt ( userId string ) store . StoreChannel {
2017-10-06 08:12:10 -07:00
return store . Do ( func ( result * store . StoreResult ) {
2016-05-12 18:36:30 -07:00
curTime := model . GetMillis ( )
if _ , err := us . GetMaster ( ) . Exec ( "UPDATE Users SET UpdateAt = :Time WHERE Id = :UserId" , map [ string ] interface { } { "Time" : curTime , "UserId" : userId } ) ; err != nil {
2017-09-18 17:40:56 +01:00
result . Err = model . NewAppError ( "SqlUserStore.UpdateUpdateAt" , "store.sql_user.update_update.app_error" , nil , "user_id=" + userId , http . StatusInternalServerError )
2019-01-29 15:24:57 -05:00
return
2016-05-12 18:36:30 -07:00
}
2019-01-29 15:24:57 -05:00
result . Data = curTime
2017-10-06 08:12:10 -07:00
} )
2016-05-12 18:36:30 -07:00
}
2017-09-25 09:11:25 -05:00
func ( us SqlUserStore ) UpdatePassword ( userId , hashedPassword string ) store . StoreChannel {
2017-10-06 08:12:10 -07:00
return store . Do ( func ( result * store . StoreResult ) {
2015-06-14 23:53:32 -08:00
updateAt := model . GetMillis ( )
2019-02-13 22:08:37 +01:00
if _ , err := us . GetMaster ( ) . Exec ( "UPDATE Users SET Password = :Password, LastPasswordUpdate = :LastPasswordUpdate, UpdateAt = :UpdateAt, AuthData = NULL, AuthService = '', FailedAttempts = 0 WHERE Id = :UserId" , map [ string ] interface { } { "Password" : hashedPassword , "LastPasswordUpdate" : updateAt , "UpdateAt" : updateAt , "UserId" : userId } ) ; err != nil {
2017-09-18 17:40:56 +01:00
result . Err = model . NewAppError ( "SqlUserStore.UpdatePassword" , "store.sql_user.update_password.app_error" , nil , "id=" + userId + ", " + err . Error ( ) , http . StatusInternalServerError )
2015-06-14 23:53:32 -08:00
} else {
result . Data = userId
}
2017-10-06 08:12:10 -07:00
} )
2015-06-14 23:53:32 -08:00
}
2017-09-25 09:11:25 -05:00
func ( us SqlUserStore ) UpdateFailedPasswordAttempts ( userId string , attempts int ) store . StoreChannel {
2017-10-06 08:12:10 -07:00
return store . Do ( func ( result * store . StoreResult ) {
2015-07-29 01:26:10 -08:00
if _ , err := us . GetMaster ( ) . Exec ( "UPDATE Users SET FailedAttempts = :FailedAttempts WHERE Id = :UserId" , map [ string ] interface { } { "FailedAttempts" : attempts , "UserId" : userId } ) ; err != nil {
2017-09-18 17:40:56 +01:00
result . Err = model . NewAppError ( "SqlUserStore.UpdateFailedPasswordAttempts" , "store.sql_user.update_failed_pwd_attempts.app_error" , nil , "user_id=" + userId , http . StatusInternalServerError )
2015-07-29 01:26:10 -08:00
} else {
result . Data = userId
}
2017-10-06 08:12:10 -07:00
} )
2015-07-29 01:26:10 -08:00
}
2019-06-26 10:41:45 +02:00
func ( us SqlUserStore ) UpdateAuthData ( userId string , service string , authData * string , email string , resetMfa bool ) ( string , * model . AppError ) {
email = strings . ToLower ( email )
2016-04-13 14:31:27 -04:00
2019-06-26 10:41:45 +02:00
updateAt := model . GetMillis ( )
2015-12-17 12:44:46 -05:00
2019-06-26 10:41:45 +02:00
query := `
2016-02-03 10:32:37 -05:00
UPDATE
Users
SET
Password = ' ' ,
LastPasswordUpdate = : LastPasswordUpdate ,
UpdateAt = : UpdateAt ,
FailedAttempts = 0 ,
AuthService = : AuthService ,
AuthData = : AuthData `
2019-06-26 10:41:45 +02:00
if len ( email ) != 0 {
query += ", Email = :Email"
}
2016-02-03 10:32:37 -05:00
2019-06-26 10:41:45 +02:00
if resetMfa {
query += ", MfaActive = false, MfaSecret = ''"
}
2016-12-12 08:16:10 -05:00
2019-06-26 10:41:45 +02:00
query += " WHERE Id = :UserId"
2016-02-03 10:32:37 -05:00
2019-06-26 10:41:45 +02:00
if _ , err := us . GetMaster ( ) . Exec ( query , map [ string ] interface { } { "LastPasswordUpdate" : updateAt , "UpdateAt" : updateAt , "UserId" : userId , "AuthService" : service , "AuthData" : authData , "Email" : email } ) ; err != nil {
if IsUniqueConstraintError ( err , [ ] string { "Email" , "users_email_key" , "idx_users_email_unique" , "AuthData" , "users_authdata_key" } ) {
return "" , model . NewAppError ( "SqlUserStore.UpdateAuthData" , "store.sql_user.update_auth_data.email_exists.app_error" , map [ string ] interface { } { "Service" : service , "Email" : email } , "user_id=" + userId + ", " + err . Error ( ) , http . StatusBadRequest )
2015-12-17 12:44:46 -05:00
}
2019-06-26 10:41:45 +02:00
return "" , model . NewAppError ( "SqlUserStore.UpdateAuthData" , "store.sql_user.update_auth_data.app_error" , nil , "id=" + userId + ", " + err . Error ( ) , http . StatusInternalServerError )
}
return userId , nil
2015-12-17 12:44:46 -05:00
}
2017-09-25 09:11:25 -05:00
func ( us SqlUserStore ) UpdateMfaSecret ( userId , secret string ) store . StoreChannel {
2017-10-06 08:12:10 -07:00
return store . Do ( func ( result * store . StoreResult ) {
2016-03-30 12:49:29 -04:00
updateAt := model . GetMillis ( )
if _ , err := us . GetMaster ( ) . Exec ( "UPDATE Users SET MfaSecret = :Secret, UpdateAt = :UpdateAt WHERE Id = :UserId" , map [ string ] interface { } { "Secret" : secret , "UpdateAt" : updateAt , "UserId" : userId } ) ; err != nil {
2017-09-18 17:40:56 +01:00
result . Err = model . NewAppError ( "SqlUserStore.UpdateMfaSecret" , "store.sql_user.update_mfa_secret.app_error" , nil , "id=" + userId + ", " + err . Error ( ) , http . StatusInternalServerError )
2016-03-30 12:49:29 -04:00
} else {
result . Data = userId
}
2017-10-06 08:12:10 -07:00
} )
2016-03-30 12:49:29 -04:00
}
2017-09-25 09:11:25 -05:00
func ( us SqlUserStore ) UpdateMfaActive ( userId string , active bool ) store . StoreChannel {
2017-10-06 08:12:10 -07:00
return store . Do ( func ( result * store . StoreResult ) {
2016-03-30 12:49:29 -04:00
updateAt := model . GetMillis ( )
if _ , err := us . GetMaster ( ) . Exec ( "UPDATE Users SET MfaActive = :Active, UpdateAt = :UpdateAt WHERE Id = :UserId" , map [ string ] interface { } { "Active" : active , "UpdateAt" : updateAt , "UserId" : userId } ) ; err != nil {
2017-09-18 17:40:56 +01:00
result . Err = model . NewAppError ( "SqlUserStore.UpdateMfaActive" , "store.sql_user.update_mfa_active.app_error" , nil , "id=" + userId + ", " + err . Error ( ) , http . StatusInternalServerError )
2016-03-30 12:49:29 -04:00
} else {
result . Data = userId
}
2017-10-06 08:12:10 -07:00
} )
2016-03-30 12:49:29 -04:00
}
2019-04-15 22:53:52 +02:00
func ( us SqlUserStore ) Get ( id string ) ( * model . User , * model . AppError ) {
query := us . usersQuery . Where ( "Id = ?" , id )
2019-01-29 15:24:57 -05:00
2019-04-15 22:53:52 +02:00
queryString , args , err := query . ToSql ( )
if err != nil {
return nil , model . NewAppError ( "SqlUserStore.Get" , "store.sql_user.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
}
2019-01-29 15:24:57 -05:00
2019-04-15 22:53:52 +02:00
user := & model . User { }
if err := us . GetReplica ( ) . SelectOne ( user , queryString , args ... ) ; err == sql . ErrNoRows {
return nil , model . NewAppError ( "SqlUserStore.Get" , store . MISSING_ACCOUNT_ERROR , nil , "user_id=" + id , http . StatusNotFound )
} else if err != nil {
return nil , model . NewAppError ( "SqlUserStore.Get" , "store.sql_user.get.app_error" , nil , "user_id=" + id + ", " + err . Error ( ) , http . StatusInternalServerError )
}
2019-01-29 15:24:57 -05:00
2019-04-15 22:53:52 +02:00
return user , nil
2015-06-14 23:53:32 -08:00
}
2017-09-25 09:11:25 -05:00
func ( us SqlUserStore ) GetAll ( ) store . StoreChannel {
2017-10-06 08:12:10 -07:00
return store . Do ( func ( result * store . StoreResult ) {
2019-01-29 15:24:57 -05:00
query := us . usersQuery . OrderBy ( "Username ASC" )
queryString , args , err := query . ToSql ( )
if err != nil {
result . Err = model . NewAppError ( "SqlUserStore.GetAll" , "store.sql_user.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
return
}
2016-04-21 22:37:01 -07:00
var data [ ] * model . User
2019-01-29 15:24:57 -05:00
if _ , err := us . GetReplica ( ) . Select ( & data , queryString , args ... ) ; err != nil {
2017-09-18 17:40:56 +01:00
result . Err = model . NewAppError ( "SqlUserStore.GetAll" , "store.sql_user.get.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
2019-01-29 15:24:57 -05:00
return
2016-04-21 22:37:01 -07:00
}
result . Data = data
2017-10-06 08:12:10 -07:00
} )
2016-04-21 22:37:01 -07:00
}
2018-09-17 15:51:26 +01:00
func ( us SqlUserStore ) GetAllAfter ( limit int , afterId string ) store . StoreChannel {
return store . Do ( func ( result * store . StoreResult ) {
2019-01-29 15:24:57 -05:00
query := us . usersQuery .
Where ( "Id > ?" , afterId ) .
OrderBy ( "Id ASC" ) .
Limit ( uint64 ( limit ) )
queryString , args , err := query . ToSql ( )
if err != nil {
result . Err = model . NewAppError ( "SqlUserStore.GetAllAfter" , "store.sql_user.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
return
}
2018-09-17 15:51:26 +01:00
var data [ ] * model . User
2019-01-29 15:24:57 -05:00
if _ , err := us . GetReplica ( ) . Select ( & data , queryString , args ... ) ; err != nil {
2018-09-17 15:51:26 +01:00
result . Err = model . NewAppError ( "SqlUserStore.GetAllAfter" , "store.sql_user.get.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
}
result . Data = data
} )
}
2017-09-25 09:11:25 -05:00
func ( s SqlUserStore ) GetEtagForAllProfiles ( ) store . StoreChannel {
2017-10-06 08:12:10 -07:00
return store . Do ( func ( result * store . StoreResult ) {
2016-05-04 06:31:42 -07:00
updateAt , err := s . GetReplica ( ) . SelectInt ( "SELECT UpdateAt FROM Users ORDER BY UpdateAt DESC LIMIT 1" )
2016-04-21 22:37:01 -07:00
if err != nil {
2017-10-09 10:16:14 -07:00
result . Data = fmt . Sprintf ( "%v.%v" , model . CurrentVersion , model . GetMillis ( ) )
2016-04-21 22:37:01 -07:00
} else {
2017-10-09 10:16:14 -07:00
result . Data = fmt . Sprintf ( "%v.%v" , model . CurrentVersion , updateAt )
2016-04-21 22:37:01 -07:00
}
2017-10-06 08:12:10 -07:00
} )
2016-04-21 22:37:01 -07:00
}
2019-01-11 14:50:32 +01:00
func ( us SqlUserStore ) GetAllProfiles ( options * model . UserGetOptions ) store . StoreChannel {
isPostgreSQL := us . DriverName ( ) == model . DATABASE_DRIVER_POSTGRES
2017-10-06 08:12:10 -07:00
return store . Do ( func ( result * store . StoreResult ) {
2019-01-29 15:24:57 -05:00
query := us . usersQuery .
OrderBy ( "u.Username ASC" ) .
Offset ( uint64 ( options . Page * options . PerPage ) ) . Limit ( uint64 ( options . PerPage ) )
2016-05-04 06:31:42 -07:00
2019-04-29 16:56:56 +02:00
query = applyViewRestrictionsFilter ( query , options . ViewRestrictions , true )
2019-01-29 15:24:57 -05:00
query = applyRoleFilter ( query , options . Role , isPostgreSQL )
2019-01-11 14:50:32 +01:00
2019-01-29 15:24:57 -05:00
if options . Inactive {
query = query . Where ( "u.DeleteAt != 0" )
}
2019-01-11 14:50:32 +01:00
2019-01-29 15:24:57 -05:00
queryString , args , err := query . ToSql ( )
if err != nil {
result . Err = model . NewAppError ( "SqlUserStore.GetAllProfiles" , "store.sql_user.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
return
}
2016-05-04 06:31:42 -07:00
2019-01-29 15:24:57 -05:00
var users [ ] * model . User
if _ , err := us . GetReplica ( ) . Select ( & users , queryString , args ... ) ; err != nil {
result . Err = model . NewAppError ( "SqlUserStore.GetAllProfiles" , "store.sql_user.get_profiles.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
return
}
2016-05-04 06:31:42 -07:00
2019-01-29 15:24:57 -05:00
for _ , u := range users {
u . Sanitize ( map [ string ] bool { } )
2016-05-04 06:31:42 -07:00
}
2019-01-29 15:24:57 -05:00
result . Data = users
2017-10-06 08:12:10 -07:00
} )
2016-05-04 06:31:42 -07:00
}
2019-01-29 15:24:57 -05:00
func applyRoleFilter ( query sq . SelectBuilder , role string , isPostgreSQL bool ) sq . SelectBuilder {
if role == "" {
return query
2019-01-11 14:50:32 +01:00
}
2019-01-29 15:24:57 -05:00
roleParam := fmt . Sprintf ( "%%%s%%" , role )
2019-01-11 14:50:32 +01:00
if isPostgreSQL {
2019-01-29 15:24:57 -05:00
return query . Where ( "u.Roles LIKE LOWER(?)" , roleParam )
2019-01-11 14:50:32 +01:00
}
2019-01-29 15:24:57 -05:00
return query . Where ( "u.Roles LIKE ? ESCAPE '*'" , roleParam )
2019-01-11 14:50:32 +01:00
}
2019-05-16 10:12:06 +01:00
func applyChannelGroupConstrainedFilter ( query sq . SelectBuilder , channelId string ) sq . SelectBuilder {
if channelId == "" {
return query
}
return query .
Where ( ` u . Id IN (
SELECT
GroupMembers . UserId
FROM
Channels
JOIN GroupChannels ON GroupChannels . ChannelId = Channels . Id
JOIN UserGroups ON UserGroups . Id = GroupChannels . GroupId
JOIN GroupMembers ON GroupMembers . GroupId = UserGroups . Id
WHERE
Channels . Id = ?
AND GroupChannels . DeleteAt = 0
AND UserGroups . DeleteAt = 0
AND GroupMembers . DeleteAt = 0
GROUP BY
GroupMembers . UserId
) ` , channelId )
}
func applyTeamGroupConstrainedFilter ( query sq . SelectBuilder , teamId string ) sq . SelectBuilder {
if teamId == "" {
return query
}
return query .
Where ( ` u . Id IN (
SELECT
GroupMembers . UserId
FROM
Teams
JOIN GroupTeams ON GroupTeams . TeamId = Teams . Id
JOIN UserGroups ON UserGroups . Id = GroupTeams . GroupId
JOIN GroupMembers ON GroupMembers . GroupId = UserGroups . Id
WHERE
Teams . Id = ?
AND GroupTeams . DeleteAt = 0
AND UserGroups . DeleteAt = 0
AND GroupMembers . DeleteAt = 0
GROUP BY
GroupMembers . UserId
) ` , teamId )
}
2017-09-25 09:11:25 -05:00
func ( s SqlUserStore ) GetEtagForProfiles ( teamId string ) store . StoreChannel {
2017-10-06 08:12:10 -07:00
return store . Do ( func ( result * store . StoreResult ) {
2016-04-21 22:37:01 -07:00
updateAt , err := s . GetReplica ( ) . SelectInt ( "SELECT UpdateAt FROM Users, TeamMembers WHERE TeamMembers.TeamId = :TeamId AND Users.Id = TeamMembers.UserId ORDER BY UpdateAt DESC LIMIT 1" , map [ string ] interface { } { "TeamId" : teamId } )
2015-06-14 23:53:32 -08:00
if err != nil {
2017-10-09 10:16:14 -07:00
result . Data = fmt . Sprintf ( "%v.%v" , model . CurrentVersion , model . GetMillis ( ) )
2015-06-14 23:53:32 -08:00
} else {
2017-10-09 10:16:14 -07:00
result . Data = fmt . Sprintf ( "%v.%v" , model . CurrentVersion , updateAt )
2015-06-14 23:53:32 -08:00
}
2017-10-06 08:12:10 -07:00
} )
2015-06-14 23:53:32 -08:00
}
2019-01-11 14:50:32 +01:00
func ( us SqlUserStore ) GetProfiles ( options * model . UserGetOptions ) store . StoreChannel {
isPostgreSQL := us . DriverName ( ) == model . DATABASE_DRIVER_POSTGRES
2019-01-29 15:24:57 -05:00
return store . Do ( func ( result * store . StoreResult ) {
query := us . usersQuery .
Join ( "TeamMembers tm ON ( tm.UserId = u.Id AND tm.DeleteAt = 0 )" ) .
Where ( "tm.TeamId = ?" , options . InTeamId ) .
OrderBy ( "u.Username ASC" ) .
Offset ( uint64 ( options . Page * options . PerPage ) ) . Limit ( uint64 ( options . PerPage ) )
2019-01-11 14:50:32 +01:00
2019-04-29 16:56:56 +02:00
query = applyViewRestrictionsFilter ( query , options . ViewRestrictions , true )
2019-01-29 15:24:57 -05:00
query = applyRoleFilter ( query , options . Role , isPostgreSQL )
2019-01-11 14:50:32 +01:00
2019-01-29 15:24:57 -05:00
if options . Inactive {
query = query . Where ( "u.DeleteAt != 0" )
}
2019-01-11 14:50:32 +01:00
2019-01-29 15:24:57 -05:00
queryString , args , err := query . ToSql ( )
if err != nil {
result . Err = model . NewAppError ( "SqlUserStore.GetProfiles" , "store.sql_user.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
return
}
2015-06-14 23:53:32 -08:00
2019-01-29 15:24:57 -05:00
var users [ ] * model . User
if _ , err := us . GetReplica ( ) . Select ( & users , queryString , args ... ) ; err != nil {
2017-09-18 17:40:56 +01:00
result . Err = model . NewAppError ( "SqlUserStore.GetProfiles" , "store.sql_user.get_profiles.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
2019-01-29 15:24:57 -05:00
return
}
2015-06-14 23:53:32 -08:00
2019-01-29 15:24:57 -05:00
for _ , u := range users {
u . Sanitize ( map [ string ] bool { } )
2015-06-14 23:53:32 -08:00
}
2019-01-29 15:24:57 -05:00
result . Data = users
2017-10-06 08:12:10 -07:00
} )
2015-06-14 23:53:32 -08:00
}
2016-11-10 06:25:23 -08:00
func ( us SqlUserStore ) InvalidateProfilesInChannelCacheByUser ( userId string ) {
keys := profilesInChannelCache . Keys ( )
for _ , key := range keys {
if cacheItem , ok := profilesInChannelCache . Get ( key ) ; ok {
userMap := cacheItem . ( map [ string ] * model . User )
if _ , userInCache := userMap [ userId ] ; userInCache {
profilesInChannelCache . Remove ( key )
2018-03-05 10:35:26 -05:00
if us . metrics != nil {
us . metrics . IncrementMemCacheInvalidationCounter ( "Profiles in Channel - Remove by User" )
}
2016-11-10 06:25:23 -08:00
}
}
}
}
2016-10-19 14:49:25 -04:00
func ( us SqlUserStore ) InvalidateProfilesInChannelCache ( channelId string ) {
profilesInChannelCache . Remove ( channelId )
2018-03-05 10:35:26 -05:00
if us . metrics != nil {
us . metrics . IncrementMemCacheInvalidationCounter ( "Profiles in Channel - Remove by Channel" )
}
2016-10-19 14:49:25 -04:00
}
2016-04-21 22:37:01 -07:00
2017-09-25 09:11:25 -05:00
func ( us SqlUserStore ) GetProfilesInChannel ( channelId string , offset int , limit int ) store . StoreChannel {
2017-10-06 08:12:10 -07:00
return store . Do ( func ( result * store . StoreResult ) {
2019-01-29 15:24:57 -05:00
query := us . usersQuery .
Join ( "ChannelMembers cm ON ( cm.UserId = u.Id )" ) .
Where ( "cm.ChannelId = ?" , channelId ) .
OrderBy ( "u.Username ASC" ) .
Offset ( uint64 ( offset ) ) . Limit ( uint64 ( limit ) )
2017-02-03 15:17:34 -05:00
2019-01-29 15:24:57 -05:00
queryString , args , err := query . ToSql ( )
if err != nil {
result . Err = model . NewAppError ( "SqlUserStore.GetProfilesInChannel" , "store.sql_user.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
return
}
2017-02-03 15:17:34 -05:00
2019-01-29 15:24:57 -05:00
var users [ ] * model . User
if _ , err := us . GetReplica ( ) . Select ( & users , queryString , args ... ) ; err != nil {
result . Err = model . NewAppError ( "SqlUserStore.GetProfilesInChannel" , "store.sql_user.get_profiles.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
return
}
2017-02-03 15:17:34 -05:00
2019-01-29 15:24:57 -05:00
for _ , u := range users {
u . Sanitize ( map [ string ] bool { } )
2017-02-03 15:17:34 -05:00
}
2019-01-29 15:24:57 -05:00
result . Data = users
2017-10-06 08:12:10 -07:00
} )
2017-02-03 15:17:34 -05:00
}
2018-03-09 05:48:30 -07:00
func ( us SqlUserStore ) GetProfilesInChannelByStatus ( channelId string , offset int , limit int ) store . StoreChannel {
return store . Do ( func ( result * store . StoreResult ) {
2019-01-29 15:24:57 -05:00
query := us . usersQuery .
Join ( "ChannelMembers cm ON ( cm.UserId = u.Id )" ) .
LeftJoin ( "Status s ON ( s.UserId = u.Id )" ) .
Where ( "cm.ChannelId = ?" , channelId ) .
OrderBy ( `
CASE s . Status
2018-03-09 05:48:30 -07:00
WHEN ' online ' THEN 1
WHEN ' away ' THEN 2
WHEN ' dnd ' THEN 3
ELSE 4
2019-01-29 15:24:57 -05:00
END
` ) .
OrderBy ( "u.Username ASC" ) .
Offset ( uint64 ( offset ) ) . Limit ( uint64 ( limit ) )
2018-03-09 05:48:30 -07:00
2019-01-29 15:24:57 -05:00
queryString , args , err := query . ToSql ( )
if err != nil {
result . Err = model . NewAppError ( "SqlUserStore.GetProfilesInChannelByStatus" , "store.sql_user.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
return
}
2018-03-09 05:48:30 -07:00
2019-01-29 15:24:57 -05:00
var users [ ] * model . User
if _ , err := us . GetReplica ( ) . Select ( & users , queryString , args ... ) ; err != nil {
result . Err = model . NewAppError ( "SqlUserStore.GetProfilesInChannelByStatus" , "store.sql_user.get_profiles.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
return
}
2018-03-09 05:48:30 -07:00
2019-01-29 15:24:57 -05:00
for _ , u := range users {
u . Sanitize ( map [ string ] bool { } )
2018-03-09 05:48:30 -07:00
}
2019-01-29 15:24:57 -05:00
result . Data = users
2018-03-09 05:48:30 -07:00
} )
}
2017-09-25 09:11:25 -05:00
func ( us SqlUserStore ) GetAllProfilesInChannel ( channelId string , allowFromCache bool ) store . StoreChannel {
2017-10-06 08:12:10 -07:00
return store . Do ( func ( result * store . StoreResult ) {
2017-02-03 15:17:34 -05:00
if allowFromCache {
2016-10-19 14:49:25 -04:00
if cacheItem , ok := profilesInChannelCache . Get ( channelId ) ; ok {
2017-09-14 12:01:44 -05:00
if us . metrics != nil {
us . metrics . IncrementMemCacheHitCounter ( "Profiles in Channel" )
2016-12-19 10:16:22 -05:00
}
2016-10-19 14:49:25 -04:00
result . Data = cacheItem . ( map [ string ] * model . User )
return
2016-12-19 10:16:22 -05:00
} else {
2017-09-14 12:01:44 -05:00
if us . metrics != nil {
us . metrics . IncrementMemCacheMissCounter ( "Profiles in Channel" )
2016-12-19 10:16:22 -05:00
}
}
} else {
2017-09-14 12:01:44 -05:00
if us . metrics != nil {
us . metrics . IncrementMemCacheMissCounter ( "Profiles in Channel" )
2016-10-19 14:49:25 -04:00
}
}
2019-01-29 15:24:57 -05:00
query := us . usersQuery .
Join ( "ChannelMembers cm ON ( cm.UserId = u.Id )" ) .
Where ( "cm.ChannelId = ?" , channelId ) .
Where ( "u.DeleteAt = 0" ) .
OrderBy ( "u.Username ASC" )
2016-10-19 14:49:25 -04:00
2019-01-29 15:24:57 -05:00
queryString , args , err := query . ToSql ( )
if err != nil {
result . Err = model . NewAppError ( "SqlUserStore.GetAllProfilesInChannel" , "store.sql_user.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
return
}
2016-10-19 14:49:25 -04:00
2019-01-29 15:24:57 -05:00
var users [ ] * model . User
if _ , err := us . GetReplica ( ) . Select ( & users , queryString , args ... ) ; err != nil {
2017-09-18 17:40:56 +01:00
result . Err = model . NewAppError ( "SqlUserStore.GetAllProfilesInChannel" , "store.sql_user.get_profiles.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
2019-01-29 15:24:57 -05:00
return
}
2016-10-19 14:49:25 -04:00
2019-01-29 15:24:57 -05:00
userMap := make ( map [ string ] * model . User )
2016-10-19 14:49:25 -04:00
2019-01-29 15:24:57 -05:00
for _ , u := range users {
u . Sanitize ( map [ string ] bool { } )
userMap [ u . Id ] = u
}
2016-10-19 14:49:25 -04:00
2019-01-29 15:24:57 -05:00
result . Data = userMap
2016-10-19 14:49:25 -04:00
2019-01-29 15:24:57 -05:00
if allowFromCache {
profilesInChannelCache . AddWithExpiresInSecs ( channelId , userMap , PROFILES_IN_CHANNEL_CACHE_SEC )
2016-10-19 14:49:25 -04:00
}
2017-10-06 08:12:10 -07:00
} )
2016-10-19 14:49:25 -04:00
}
2019-05-16 10:12:06 +01:00
func ( us SqlUserStore ) GetProfilesNotInChannel ( teamId string , channelId string , groupConstrained bool , offset int , limit int , viewRestrictions * model . ViewUsersRestrictions ) store . StoreChannel {
2017-10-06 08:12:10 -07:00
return store . Do ( func ( result * store . StoreResult ) {
2019-01-29 15:24:57 -05:00
query := us . usersQuery .
Join ( "TeamMembers tm ON ( tm.UserId = u.Id AND tm.DeleteAt = 0 AND tm.TeamId = ? )" , teamId ) .
LeftJoin ( "ChannelMembers cm ON ( cm.UserId = u.Id AND cm.ChannelId = ? )" , channelId ) .
Where ( "cm.UserId IS NULL" ) .
OrderBy ( "u.Username ASC" ) .
Offset ( uint64 ( offset ) ) . Limit ( uint64 ( limit ) )
2019-04-29 16:56:56 +02:00
query = applyViewRestrictionsFilter ( query , viewRestrictions , true )
2019-05-16 10:12:06 +01:00
if groupConstrained {
query = applyChannelGroupConstrainedFilter ( query , channelId )
}
2019-01-29 15:24:57 -05:00
queryString , args , err := query . ToSql ( )
if err != nil {
result . Err = model . NewAppError ( "SqlUserStore.GetProfilesNotInChannel" , "store.sql_user.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
return
}
2016-04-21 22:37:01 -07:00
2019-01-29 15:24:57 -05:00
var users [ ] * model . User
if _ , err := us . GetReplica ( ) . Select ( & users , queryString , args ... ) ; err != nil {
2017-09-18 17:40:56 +01:00
result . Err = model . NewAppError ( "SqlUserStore.GetProfilesNotInChannel" , "store.sql_user.get_profiles.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
2019-01-29 15:24:57 -05:00
return
}
2017-03-29 21:11:40 -04:00
2019-01-29 15:24:57 -05:00
for _ , u := range users {
u . Sanitize ( map [ string ] bool { } )
2017-03-29 21:11:40 -04:00
}
2019-01-29 15:24:57 -05:00
result . Data = users
2017-10-06 08:12:10 -07:00
} )
2017-03-29 21:11:40 -04:00
}
2019-04-29 16:56:56 +02:00
func ( us SqlUserStore ) GetProfilesWithoutTeam ( offset int , limit int , viewRestrictions * model . ViewUsersRestrictions ) store . StoreChannel {
2017-10-06 08:12:10 -07:00
return store . Do ( func ( result * store . StoreResult ) {
2019-01-29 15:24:57 -05:00
query := us . usersQuery .
Where ( ` (
SELECT
COUNT ( 0 )
FROM
TeamMembers
WHERE
TeamMembers . UserId = u . Id
AND TeamMembers . DeleteAt = 0
) = 0 ` ) .
OrderBy ( "u.Username ASC" ) .
Offset ( uint64 ( offset ) ) . Limit ( uint64 ( limit ) )
2019-04-29 16:56:56 +02:00
query = applyViewRestrictionsFilter ( query , viewRestrictions , true )
2019-01-29 15:24:57 -05:00
queryString , args , err := query . ToSql ( )
if err != nil {
result . Err = model . NewAppError ( "SqlUserStore.GetProfilesWithoutTeam" , "store.sql_user.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
return
}
2017-03-29 21:11:40 -04:00
2019-01-29 15:24:57 -05:00
var users [ ] * model . User
if _ , err := us . GetReplica ( ) . Select ( & users , queryString , args ... ) ; err != nil {
2017-09-18 17:40:56 +01:00
result . Err = model . NewAppError ( "SqlUserStore.GetProfilesWithoutTeam" , "store.sql_user.get_profiles.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
2019-01-29 15:24:57 -05:00
return
}
2016-04-21 22:37:01 -07:00
2019-01-29 15:24:57 -05:00
for _ , u := range users {
u . Sanitize ( map [ string ] bool { } )
2016-04-21 22:37:01 -07:00
}
2019-01-29 15:24:57 -05:00
result . Data = users
2017-10-06 08:12:10 -07:00
} )
2016-04-21 22:37:01 -07:00
}
2019-04-29 16:56:56 +02:00
func ( us SqlUserStore ) GetProfilesByUsernames ( usernames [ ] string , viewRestrictions * model . ViewUsersRestrictions ) store . StoreChannel {
2017-10-06 08:12:10 -07:00
return store . Do ( func ( result * store . StoreResult ) {
2019-01-29 15:24:57 -05:00
query := us . usersQuery
2016-10-19 14:49:25 -04:00
2019-04-29 16:56:56 +02:00
query = applyViewRestrictionsFilter ( query , viewRestrictions , true )
2016-10-19 14:49:25 -04:00
2019-01-29 15:24:57 -05:00
query = query .
Where ( map [ string ] interface { } {
"Username" : usernames ,
} ) .
OrderBy ( "u.Username ASC" )
queryString , args , err := query . ToSql ( )
if err != nil {
result . Err = model . NewAppError ( "SqlUserStore.GetProfilesByUsernames" , "store.sql_user.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
return
2017-04-25 11:00:41 -04:00
}
2016-10-19 14:49:25 -04:00
2019-01-29 15:24:57 -05:00
var users [ ] * model . User
if _ , err := us . GetReplica ( ) . Select ( & users , queryString , args ... ) ; err != nil {
2017-09-18 17:40:56 +01:00
result . Err = model . NewAppError ( "SqlUserStore.GetProfilesByUsernames" , "store.sql_user.get_profiles.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
2019-01-29 15:24:57 -05:00
return
2016-10-19 14:49:25 -04:00
}
2019-01-29 15:24:57 -05:00
result . Data = users
2017-10-06 08:12:10 -07:00
} )
2016-10-19 14:49:25 -04:00
}
type UserWithLastActivityAt struct {
model . User
LastActivityAt int64
}
2019-04-29 16:56:56 +02:00
func ( us SqlUserStore ) GetRecentlyActiveUsersForTeam ( teamId string , offset , limit int , viewRestrictions * model . ViewUsersRestrictions ) store . StoreChannel {
2017-10-06 08:12:10 -07:00
return store . Do ( func ( result * store . StoreResult ) {
2019-01-29 15:24:57 -05:00
query := us . usersQuery .
Column ( "s.LastActivityAt" ) .
Join ( "TeamMembers tm ON (tm.UserId = u.Id AND tm.TeamId = ?)" , teamId ) .
Join ( "Status s ON (s.UserId = u.Id)" ) .
OrderBy ( "s.LastActivityAt DESC" ) .
OrderBy ( "u.Username ASC" ) .
Offset ( uint64 ( offset ) ) . Limit ( uint64 ( limit ) )
2019-04-29 16:56:56 +02:00
query = applyViewRestrictionsFilter ( query , viewRestrictions , true )
2019-01-29 15:24:57 -05:00
queryString , args , err := query . ToSql ( )
if err != nil {
result . Err = model . NewAppError ( "SqlUserStore.GetRecentlyActiveUsers" , "store.sql_user.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
return
}
2016-10-19 14:49:25 -04:00
2019-01-29 15:24:57 -05:00
var users [ ] * UserWithLastActivityAt
if _ , err := us . GetReplica ( ) . Select ( & users , queryString , args ... ) ; err != nil {
2017-06-30 12:07:23 -04:00
result . Err = model . NewAppError ( "SqlUserStore.GetRecentlyActiveUsers" , "store.sql_user.get_recently_active_users.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
2019-01-29 15:24:57 -05:00
return
}
2016-10-19 14:49:25 -04:00
2019-01-29 15:24:57 -05:00
userList := [ ] * model . User { }
2016-10-19 14:49:25 -04:00
2019-01-29 15:24:57 -05:00
for _ , userWithLastActivityAt := range users {
u := userWithLastActivityAt . User
u . Sanitize ( map [ string ] bool { } )
u . LastActivityAt = userWithLastActivityAt . LastActivityAt
userList = append ( userList , & u )
2017-06-30 12:07:23 -04:00
}
2019-01-29 15:24:57 -05:00
result . Data = userList
2017-10-06 08:12:10 -07:00
} )
2017-06-30 12:07:23 -04:00
}
2019-06-26 14:35:02 +01:00
func ( us SqlUserStore ) GetNewUsersForTeam ( teamId string , offset , limit int , viewRestrictions * model . ViewUsersRestrictions ) ( [ ] * model . User , * model . AppError ) {
query := us . usersQuery .
Join ( "TeamMembers tm ON (tm.UserId = u.Id AND tm.TeamId = ?)" , teamId ) .
OrderBy ( "u.CreateAt DESC" ) .
OrderBy ( "u.Username ASC" ) .
Offset ( uint64 ( offset ) ) . Limit ( uint64 ( limit ) )
2017-06-30 12:07:23 -04:00
2019-06-26 14:35:02 +01:00
query = applyViewRestrictionsFilter ( query , viewRestrictions , true )
2019-04-29 16:56:56 +02:00
2019-06-26 14:35:02 +01:00
queryString , args , err := query . ToSql ( )
if err != nil {
return nil , model . NewAppError ( "SqlUserStore.GetNewUsersForTeam" , "store.sql_user.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
}
2019-01-29 15:24:57 -05:00
2019-06-26 14:35:02 +01:00
var users [ ] * model . User
if _ , err := us . GetReplica ( ) . Select ( & users , queryString , args ... ) ; err != nil {
return nil , model . NewAppError ( "SqlUserStore.GetNewUsersForTeam" , "store.sql_user.get_new_users.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
}
2017-06-30 12:07:23 -04:00
2019-06-26 14:35:02 +01:00
for _ , u := range users {
u . Sanitize ( map [ string ] bool { } )
}
2019-01-29 15:24:57 -05:00
2019-06-26 14:35:02 +01:00
return users , nil
2016-10-19 14:49:25 -04:00
}
2019-04-29 16:56:56 +02:00
func ( us SqlUserStore ) GetProfileByIds ( userIds [ ] string , allowFromCache bool , viewRestrictions * model . ViewUsersRestrictions ) store . StoreChannel {
2017-10-06 08:12:10 -07:00
return store . Do ( func ( result * store . StoreResult ) {
2017-02-03 09:30:57 -05:00
users := [ ] * model . User { }
2016-12-13 19:23:36 -08:00
remainingUserIds := make ( [ ] string , 0 )
if allowFromCache {
for _ , userId := range userIds {
if cacheItem , ok := profileByIdsCache . Get ( userId ) ; ok {
2017-05-01 17:40:04 -04:00
u := & model . User { }
* u = * cacheItem . ( * model . User )
2017-02-03 09:30:57 -05:00
users = append ( users , u )
2016-12-13 19:23:36 -08:00
} else {
remainingUserIds = append ( remainingUserIds , userId )
}
}
2017-09-14 12:01:44 -05:00
if us . metrics != nil {
us . metrics . AddMemCacheHitCounter ( "Profile By Ids" , float64 ( len ( users ) ) )
us . metrics . AddMemCacheMissCounter ( "Profile By Ids" , float64 ( len ( remainingUserIds ) ) )
2017-01-03 09:57:38 -05:00
}
2016-12-13 19:23:36 -08:00
} else {
remainingUserIds = userIds
2017-09-14 12:01:44 -05:00
if us . metrics != nil {
us . metrics . AddMemCacheMissCounter ( "Profile By Ids" , float64 ( len ( remainingUserIds ) ) )
2017-01-03 09:57:38 -05:00
}
2016-12-13 19:23:36 -08:00
}
2016-04-21 22:37:01 -07:00
2016-12-13 19:23:36 -08:00
// If everything came from the cache then just return
if len ( remainingUserIds ) == 0 {
2017-02-03 09:30:57 -05:00
result . Data = users
2016-12-13 19:23:36 -08:00
return
}
2019-01-29 15:24:57 -05:00
query := us . usersQuery .
Where ( map [ string ] interface { } {
"u.Id" : remainingUserIds ,
} ) .
OrderBy ( "u.Username ASC" )
2016-04-21 22:37:01 -07:00
2019-04-29 16:56:56 +02:00
query = applyViewRestrictionsFilter ( query , viewRestrictions , true )
2019-01-29 15:24:57 -05:00
queryString , args , err := query . ToSql ( )
if err != nil {
result . Err = model . NewAppError ( "SqlUserStore.GetProfileByIds" , "store.sql_user.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
return
2016-04-21 22:37:01 -07:00
}
2019-01-29 15:24:57 -05:00
if _ , err := us . GetReplica ( ) . Select ( & users , queryString , args ... ) ; err != nil {
2017-09-18 17:40:56 +01:00
result . Err = model . NewAppError ( "SqlUserStore.GetProfileByIds" , "store.sql_user.get_profiles.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
2019-01-29 15:24:57 -05:00
return
}
2017-04-19 15:38:35 -04:00
2019-01-29 15:24:57 -05:00
for _ , u := range users {
u . Sanitize ( map [ string ] bool { } )
2016-04-21 22:37:01 -07:00
2019-01-29 15:24:57 -05:00
cpy := & model . User { }
* cpy = * u
profileByIdsCache . AddWithExpiresInSecs ( cpy . Id , cpy , PROFILE_BY_IDS_CACHE_SEC )
2016-04-21 22:37:01 -07:00
}
2019-01-29 15:24:57 -05:00
result . Data = users
2017-10-06 08:12:10 -07:00
} )
2016-04-21 22:37:01 -07:00
}
2019-06-22 00:14:21 +01:00
type UserWithChannel struct {
model . User
ChannelId string
}
func ( us SqlUserStore ) GetProfileByGroupChannelIdsForUser ( userId string , channelIds [ ] string ) ( map [ string ] [ ] * model . User , * model . AppError ) {
if len ( channelIds ) > MAX_GROUP_CHANNELS_FOR_PROFILES {
channelIds = channelIds [ 0 : MAX_GROUP_CHANNELS_FOR_PROFILES ]
}
isMemberQuery := fmt . Sprintf ( `
EXISTS (
SELECT
1
FROM
ChannelMembers
WHERE
UserId = ' % s '
AND
ChannelId = cm . ChannelId
) ` , userId )
query := us . getQueryBuilder ( ) .
Select ( "u.*, cm.ChannelId" ) .
From ( "Users u" ) .
Join ( "ChannelMembers cm ON u.Id = cm.UserId" ) .
Join ( "Channels c ON cm.ChannelId = c.Id" ) .
Where ( sq . Eq { "c.Type" : model . CHANNEL_GROUP , "cm.ChannelId" : channelIds } ) .
Where ( isMemberQuery ) .
Where ( sq . NotEq { "u.Id" : userId } ) .
OrderBy ( "u.Username ASC" )
queryString , args , err := query . ToSql ( )
if err != nil {
return nil , model . NewAppError ( "SqlUserStore.GetProfileByGroupChannelIdsForUser" , "store.sql_user.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
}
usersWithChannel := [ ] * UserWithChannel { }
if _ , err := us . GetReplica ( ) . Select ( & usersWithChannel , queryString , args ... ) ; err != nil {
return nil , model . NewAppError ( "SqlUserStore.GetProfileByGroupChannelIdsForUser" , "store.sql_user.get_profile_by_group_channel_ids_for_user.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
}
usersByChannelId := map [ string ] [ ] * model . User { }
for _ , user := range usersWithChannel {
if val , ok := usersByChannelId [ user . ChannelId ] ; ok {
usersByChannelId [ user . ChannelId ] = append ( val , & user . User )
} else {
usersByChannelId [ user . ChannelId ] = [ ] * model . User { & user . User }
}
}
return usersByChannelId , nil
}
2017-09-25 09:11:25 -05:00
func ( us SqlUserStore ) GetSystemAdminProfiles ( ) store . StoreChannel {
2017-10-06 08:12:10 -07:00
return store . Do ( func ( result * store . StoreResult ) {
2019-01-29 15:24:57 -05:00
query := us . usersQuery .
Where ( "Roles LIKE ?" , "%system_admin%" ) .
OrderBy ( "u.Username ASC" )
2015-10-05 17:33:45 -07:00
2019-01-29 15:24:57 -05:00
queryString , args , err := query . ToSql ( )
if err != nil {
result . Err = model . NewAppError ( "SqlUserStore.GetSystemAdminProfiles" , "store.sql_user.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
return
}
2015-10-05 17:33:45 -07:00
2019-01-29 15:24:57 -05:00
var users [ ] * model . User
if _ , err := us . GetReplica ( ) . Select ( & users , queryString , args ... ) ; err != nil {
result . Err = model . NewAppError ( "SqlUserStore.GetSystemAdminProfiles" , "store.sql_user.get_sysadmin_profiles.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
return
}
2015-10-05 17:33:45 -07:00
2019-01-29 15:24:57 -05:00
userMap := make ( map [ string ] * model . User )
2015-10-05 17:33:45 -07:00
2019-01-29 15:24:57 -05:00
for _ , u := range users {
u . Sanitize ( map [ string ] bool { } )
userMap [ u . Id ] = u
2015-10-05 17:33:45 -07:00
}
2019-01-29 15:24:57 -05:00
result . Data = userMap
2017-10-06 08:12:10 -07:00
} )
2015-10-05 17:33:45 -07:00
}
2019-06-12 19:30:50 +02:00
func ( us SqlUserStore ) GetByEmail ( email string ) ( * model . User , * model . AppError ) {
email = strings . ToLower ( email )
2016-04-13 14:31:27 -04:00
2019-06-12 19:30:50 +02:00
query := us . usersQuery . Where ( "Email = ?" , email )
2019-01-29 15:24:57 -05:00
2019-06-12 19:30:50 +02:00
queryString , args , err := query . ToSql ( )
if err != nil {
return nil , model . NewAppError ( "SqlUserStore.GetByEmail" , "store.sql_user.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
}
2015-06-14 23:53:32 -08:00
2019-06-12 19:30:50 +02:00
user := model . User { }
if err := us . GetReplica ( ) . SelectOne ( & user , queryString , args ... ) ; err != nil {
return nil , model . NewAppError ( "SqlUserStore.GetByEmail" , store . MISSING_ACCOUNT_ERROR , nil , "email=" + email + ", " + err . Error ( ) , http . StatusInternalServerError )
}
2015-06-14 23:53:32 -08:00
2019-06-12 19:30:50 +02:00
return & user , nil
2015-06-14 23:53:32 -08:00
}
2019-06-12 19:30:50 +02:00
func ( us SqlUserStore ) GetByAuth ( authData * string , authService string ) ( * model . User , * model . AppError ) {
if authData == nil || * authData == "" {
return nil , model . NewAppError ( "SqlUserStore.GetByAuth" , store . MISSING_AUTH_ACCOUNT_ERROR , nil , "authData='', authService=" + authService , http . StatusBadRequest )
}
2016-05-11 11:04:30 -07:00
2019-06-12 19:30:50 +02:00
query := us . usersQuery .
Where ( "u.AuthData = ?" , authData ) .
Where ( "u.AuthService = ?" , authService )
2015-07-15 12:48:50 -04:00
2019-06-12 19:30:50 +02:00
queryString , args , err := query . ToSql ( )
if err != nil {
return nil , model . NewAppError ( "SqlUserStore.GetByAuth" , "store.sql_user.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
}
2015-07-15 12:48:50 -04:00
2019-06-12 19:30:50 +02:00
user := model . User { }
if err := us . GetReplica ( ) . SelectOne ( & user , queryString , args ... ) ; err == sql . ErrNoRows {
return nil , model . NewAppError ( "SqlUserStore.GetByAuth" , store . MISSING_AUTH_ACCOUNT_ERROR , nil , "authData=" + * authData + ", authService=" + authService + ", " + err . Error ( ) , http . StatusInternalServerError )
} else if err != nil {
return nil , model . NewAppError ( "SqlUserStore.GetByAuth" , "store.sql_user.get_by_auth.other.app_error" , nil , "authData=" + * authData + ", authService=" + authService + ", " + err . Error ( ) , http . StatusInternalServerError )
}
return & user , nil
2015-07-15 12:48:50 -04:00
}
2019-06-26 10:41:45 +02:00
func ( us SqlUserStore ) GetAllUsingAuthService ( authService string ) ( [ ] * model . User , * model . AppError ) {
query := us . usersQuery .
Where ( "u.AuthService = ?" , authService ) .
OrderBy ( "u.Username ASC" )
2016-06-03 09:33:59 -04:00
2019-06-26 10:41:45 +02:00
queryString , args , err := query . ToSql ( )
if err != nil {
return nil , model . NewAppError ( "SqlUserStore.GetAllUsingAuthService" , "store.sql_user.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
}
2019-01-29 15:24:57 -05:00
2019-06-26 10:41:45 +02:00
var users [ ] * model . User
if _ , err := us . GetReplica ( ) . Select ( & users , queryString , args ... ) ; err != nil {
return nil , model . NewAppError ( "SqlUserStore.GetAllUsingAuthService" , "store.sql_user.get_by_auth.other.app_error" , nil , "authService=" + authService + ", " + err . Error ( ) , http . StatusInternalServerError )
}
2016-06-03 09:33:59 -04:00
2019-06-26 10:41:45 +02:00
return users , nil
2016-06-03 09:33:59 -04:00
}
2017-09-25 09:11:25 -05:00
func ( us SqlUserStore ) GetByUsername ( username string ) store . StoreChannel {
2017-10-06 08:12:10 -07:00
return store . Do ( func ( result * store . StoreResult ) {
2019-01-29 15:24:57 -05:00
query := us . usersQuery . Where ( "u.Username = ?" , username )
2015-06-14 23:53:32 -08:00
2019-01-29 15:24:57 -05:00
queryString , args , err := query . ToSql ( )
if err != nil {
result . Err = model . NewAppError ( "SqlUserStore.GetByUsername" , "store.sql_user.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
return
}
user := model . User { }
if err := us . GetReplica ( ) . SelectOne ( & user , queryString , args ... ) ; err != nil {
result . Err = model . NewAppError ( "SqlUserStore.GetByUsername" , "store.sql_user.get_by_username.app_error" , nil , err . Error ( ) + " -- " + queryString , http . StatusInternalServerError )
return
2015-06-14 23:53:32 -08:00
}
result . Data = & user
2017-10-06 08:12:10 -07:00
} )
2015-06-14 23:53:32 -08:00
}
2018-05-10 09:46:09 -07:00
func ( us SqlUserStore ) GetForLogin ( loginId string , allowSignInWithUsername , allowSignInWithEmail bool ) store . StoreChannel {
2017-10-06 08:12:10 -07:00
return store . Do ( func ( result * store . StoreResult ) {
2019-01-29 15:24:57 -05:00
query := us . usersQuery
if allowSignInWithUsername && allowSignInWithEmail {
query = query . Where ( "Username = ? OR Email = ?" , loginId , loginId )
} else if allowSignInWithUsername {
query = query . Where ( "Username = ?" , loginId )
} else if allowSignInWithEmail {
query = query . Where ( "Email = ?" , loginId )
} else {
result . Err = model . NewAppError ( "SqlUserStore.GetForLogin" , "store.sql_user.get_for_login.app_error" , nil , "" , http . StatusInternalServerError )
return
}
queryString , args , err := query . ToSql ( )
if err != nil {
result . Err = model . NewAppError ( "SqlUserStore.GetForLogin" , "store.sql_user.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
return
2016-05-03 14:10:36 -04:00
}
users := [ ] * model . User { }
2019-01-29 15:24:57 -05:00
if _ , err := us . GetReplica ( ) . Select ( & users , queryString , args ... ) ; err != nil {
2017-09-18 17:40:56 +01:00
result . Err = model . NewAppError ( "SqlUserStore.GetForLogin" , "store.sql_user.get_for_login.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
2019-01-29 15:24:57 -05:00
return
}
if len ( users ) == 0 {
2017-09-18 17:40:56 +01:00
result . Err = model . NewAppError ( "SqlUserStore.GetForLogin" , "store.sql_user.get_for_login.app_error" , nil , "" , http . StatusInternalServerError )
2019-01-29 15:24:57 -05:00
return
2016-05-03 14:10:36 -04:00
}
2019-01-29 15:24:57 -05:00
if len ( users ) > 1 {
result . Err = model . NewAppError ( "SqlUserStore.GetForLogin" , "store.sql_user.get_for_login.multiple_users" , nil , "" , http . StatusInternalServerError )
return
}
result . Data = users [ 0 ]
2017-10-06 08:12:10 -07:00
} )
2016-05-03 14:10:36 -04:00
}
2019-06-12 19:30:50 +02:00
func ( us SqlUserStore ) VerifyEmail ( userId , email string ) ( string , * model . AppError ) {
curTime := model . GetMillis ( )
if _ , err := us . GetMaster ( ) . Exec ( "UPDATE Users SET Email = :email, EmailVerified = true, UpdateAt = :Time WHERE Id = :UserId" , map [ string ] interface { } { "email" : email , "Time" : curTime , "UserId" : userId } ) ; err != nil {
return "" , model . NewAppError ( "SqlUserStore.VerifyEmail" , "store.sql_user.verify_email.app_error" , nil , "userId=" + userId + ", " + err . Error ( ) , http . StatusInternalServerError )
}
2015-06-14 23:53:32 -08:00
2019-06-12 19:30:50 +02:00
return userId , nil
2015-08-26 12:49:07 -04:00
}
2015-09-23 15:52:59 -07:00
2019-05-24 15:16:15 +02:00
func ( us SqlUserStore ) PermanentDelete ( userId string ) * model . AppError {
if _ , err := us . GetMaster ( ) . Exec ( "DELETE FROM Users WHERE Id = :UserId" , map [ string ] interface { } { "UserId" : userId } ) ; err != nil {
return model . NewAppError ( "SqlUserStore.PermanentDelete" , "store.sql_user.permanent_delete.app_error" , nil , "userId=" + userId + ", " + err . Error ( ) , http . StatusInternalServerError )
}
return nil
2015-11-15 18:18:02 -08:00
}
2016-01-21 12:14:17 -05:00
2019-06-26 10:41:45 +02:00
func ( us SqlUserStore ) Count ( options model . UserCountOptions ) ( int64 , * model . AppError ) {
query := sq . Select ( "COUNT(DISTINCT u.Id)" ) . From ( "Users AS u" )
MM-12393 Server side of bot accounts. (#10378)
* bots model, store and api (#9903)
* bots model, store and api
Fixes: MM-13100, MM-13101, MM-13103, MM-13105, MMM-13119
* uncomment tests incorrectly commented, and fix merge issues
* add etags support
* add missing licenses
* remove unused sqlbuilder.go (for now...)
* rejig permissions
* split out READ_BOTS into READ_BOTS and READ_OTHERS_BOTS, the latter
implicitly allowing the former
* make MANAGE_OTHERS_BOTS imply MANAGE_BOTS
* conform to general rest api pattern
* eliminate redundant http.StatusOK
* Update api4/bot.go
Co-Authored-By: lieut-data <jesse.hallam@gmail.com>
* s/model.UserFromBotModel/model.UserFromBot/g
* Update model/bot.go
Co-Authored-By: lieut-data <jesse.hallam@gmail.com>
* Update model/client4.go
Co-Authored-By: lieut-data <jesse.hallam@gmail.com>
* move sessionHasPermissionToManageBot to app/authorization.go
* use api.ApiSessionRequired for createBot
* introduce BOT_DESCRIPTION_MAX_RUNES constant
* MM-13512 Prevent getting a user by email based on privacy settings (#10021)
* MM-13512 Prevent getting a user by email based on privacy settings
* Add additional config settings to tests
* upgrade db to 5.7 (#10019)
* MM-13526 Add validation when setting a user's Locale field (#10022)
* Fix typos (#10024)
* Fixing first user being created with system admin privilages without being explicity specified. (#10014)
* Revert "Support for Embeded chat (#9129)" (#10017)
This reverts commit 3fcecd521a5c6ccfdb52fb4c3fb1f8c6ea528a4e.
* s/DisableBot/UpdateBotActive
* add permissions on upgrade
* Update NOTICE.txt (#10054)
- add new dependency (text)
- handle switch to forked dependency (go-gomail -> go-mail)
- misc copyright owner updates
* avoid leaking bot knowledge without permission
* [GH-6798] added a new api endpoint to get the bulk reactions for posts (#10049)
* 6798 added a new api to get the bulk reactions for posts
* 6798 added the permsission check before getting the reactions
* GH-6798 added a new app function for the new endpoint
* 6798 added a store method to get reactions for multiple posts
* 6798 connected the app function with the new store function
* 6798 fixed the review comments
* MM-13559 Update model.post.is_valid.file_ids.app_error text per report (#10055)
Ticket: https://mattermost.atlassian.net/browse/MM-13559
Report: https://github.com/mattermost/mattermost-server/issues/10023
* Trigger Login Hooks with OAuth (#10061)
* make BotStore.GetAll deterministic even on duplicate CreateAt
* fix spurious TestMuteCommandSpecificChannel test failure
See
https://community-daily.mattermost.com/core/pl/px9p8s3dzbg1pf3ddrm5cr36uw
* fix race in TestExportUserChannels
* TestExportUserChannels: remove SaveMember call, as it is redundant and used to be silently failing anyway
* MM-13117: bot tokens (#10111)
* eliminate redundant Client/AdminClient declarations
* harden TestUpdateChannelScheme to API failures
* eliminate unnecessary config restoration
* minor cleanup
* make TestGenerateMfaSecret config dependency explicit
* TestCreateUserAccessToken for bots
* TestGetUserAccessToken* for bots
* leverage SessionHasPermissionToUserOrBot for user token APIs
* Test(Revoke|Disable|Enable)UserAccessToken
* make EnableUserAccessTokens explicit, so as to not rely on local config.json
* uncomment TestResetPassword, but still skip
* mark assert(Invalid)Token as helper
* fix whitespace issues
* fix mangled comments
* MM-13116: bot plugin api (#10113)
* MM-13117: expose bot API to plugins
This also changes the `CreatorId` column definition to allow for plugin
ids, as the default unless the plugin overrides is to use the plugin id
here. This branch hasn't hit master yet, so no migration needed.
* gofmt issues
* expunge use of BotList in plugin/client API
* introduce model.BotGetOptions
* use botUserId term for clarity
* MM-13129 Adding functionality to deal with orphaned bots (#10238)
* Add way to list orphaned bots.
* Add /assign route to modify ownership of bot accounts.
* Apply suggestions from code review
Co-Authored-By: crspeller <crspeller@gmail.com>
* MM-13120: add IsBot field to returned user objects (#10103)
* MM-13104: forbid bot login (#10251)
* MM-13104: disallow bot login
* fix shadowing
* MM-13136 Disable user bots when user is disabled. (#10293)
* Disable user bots when user is disabled.
* Grammer.
Co-Authored-By: crspeller <crspeller@gmail.com>
* Fixing bot branch for test changes.
* Don't use external dependancies in bot plugin tests.
* Rename bot CreatorId to OwnerId
* Adding ability to re-enable bots
* Fixing IsBot to not attempt to be saved to DB.
* Adding diagnostics and licencing counting for bot accounts.
* Modifying gorp to allow reading of '-' fields.
* Removing unnessisary nil values from UserCountOptions.
* Changing comment to GoDoc format
* Improving user count SQL
* Some improvments from feedback.
* Omit empty on User.IsBot
2019-03-05 07:06:45 -08:00
2019-06-26 10:41:45 +02:00
if ! options . IncludeDeleted {
query = query . Where ( "u.DeleteAt = 0" )
}
MM-12393 Server side of bot accounts. (#10378)
* bots model, store and api (#9903)
* bots model, store and api
Fixes: MM-13100, MM-13101, MM-13103, MM-13105, MMM-13119
* uncomment tests incorrectly commented, and fix merge issues
* add etags support
* add missing licenses
* remove unused sqlbuilder.go (for now...)
* rejig permissions
* split out READ_BOTS into READ_BOTS and READ_OTHERS_BOTS, the latter
implicitly allowing the former
* make MANAGE_OTHERS_BOTS imply MANAGE_BOTS
* conform to general rest api pattern
* eliminate redundant http.StatusOK
* Update api4/bot.go
Co-Authored-By: lieut-data <jesse.hallam@gmail.com>
* s/model.UserFromBotModel/model.UserFromBot/g
* Update model/bot.go
Co-Authored-By: lieut-data <jesse.hallam@gmail.com>
* Update model/client4.go
Co-Authored-By: lieut-data <jesse.hallam@gmail.com>
* move sessionHasPermissionToManageBot to app/authorization.go
* use api.ApiSessionRequired for createBot
* introduce BOT_DESCRIPTION_MAX_RUNES constant
* MM-13512 Prevent getting a user by email based on privacy settings (#10021)
* MM-13512 Prevent getting a user by email based on privacy settings
* Add additional config settings to tests
* upgrade db to 5.7 (#10019)
* MM-13526 Add validation when setting a user's Locale field (#10022)
* Fix typos (#10024)
* Fixing first user being created with system admin privilages without being explicity specified. (#10014)
* Revert "Support for Embeded chat (#9129)" (#10017)
This reverts commit 3fcecd521a5c6ccfdb52fb4c3fb1f8c6ea528a4e.
* s/DisableBot/UpdateBotActive
* add permissions on upgrade
* Update NOTICE.txt (#10054)
- add new dependency (text)
- handle switch to forked dependency (go-gomail -> go-mail)
- misc copyright owner updates
* avoid leaking bot knowledge without permission
* [GH-6798] added a new api endpoint to get the bulk reactions for posts (#10049)
* 6798 added a new api to get the bulk reactions for posts
* 6798 added the permsission check before getting the reactions
* GH-6798 added a new app function for the new endpoint
* 6798 added a store method to get reactions for multiple posts
* 6798 connected the app function with the new store function
* 6798 fixed the review comments
* MM-13559 Update model.post.is_valid.file_ids.app_error text per report (#10055)
Ticket: https://mattermost.atlassian.net/browse/MM-13559
Report: https://github.com/mattermost/mattermost-server/issues/10023
* Trigger Login Hooks with OAuth (#10061)
* make BotStore.GetAll deterministic even on duplicate CreateAt
* fix spurious TestMuteCommandSpecificChannel test failure
See
https://community-daily.mattermost.com/core/pl/px9p8s3dzbg1pf3ddrm5cr36uw
* fix race in TestExportUserChannels
* TestExportUserChannels: remove SaveMember call, as it is redundant and used to be silently failing anyway
* MM-13117: bot tokens (#10111)
* eliminate redundant Client/AdminClient declarations
* harden TestUpdateChannelScheme to API failures
* eliminate unnecessary config restoration
* minor cleanup
* make TestGenerateMfaSecret config dependency explicit
* TestCreateUserAccessToken for bots
* TestGetUserAccessToken* for bots
* leverage SessionHasPermissionToUserOrBot for user token APIs
* Test(Revoke|Disable|Enable)UserAccessToken
* make EnableUserAccessTokens explicit, so as to not rely on local config.json
* uncomment TestResetPassword, but still skip
* mark assert(Invalid)Token as helper
* fix whitespace issues
* fix mangled comments
* MM-13116: bot plugin api (#10113)
* MM-13117: expose bot API to plugins
This also changes the `CreatorId` column definition to allow for plugin
ids, as the default unless the plugin overrides is to use the plugin id
here. This branch hasn't hit master yet, so no migration needed.
* gofmt issues
* expunge use of BotList in plugin/client API
* introduce model.BotGetOptions
* use botUserId term for clarity
* MM-13129 Adding functionality to deal with orphaned bots (#10238)
* Add way to list orphaned bots.
* Add /assign route to modify ownership of bot accounts.
* Apply suggestions from code review
Co-Authored-By: crspeller <crspeller@gmail.com>
* MM-13120: add IsBot field to returned user objects (#10103)
* MM-13104: forbid bot login (#10251)
* MM-13104: disallow bot login
* fix shadowing
* MM-13136 Disable user bots when user is disabled. (#10293)
* Disable user bots when user is disabled.
* Grammer.
Co-Authored-By: crspeller <crspeller@gmail.com>
* Fixing bot branch for test changes.
* Don't use external dependancies in bot plugin tests.
* Rename bot CreatorId to OwnerId
* Adding ability to re-enable bots
* Fixing IsBot to not attempt to be saved to DB.
* Adding diagnostics and licencing counting for bot accounts.
* Modifying gorp to allow reading of '-' fields.
* Removing unnessisary nil values from UserCountOptions.
* Changing comment to GoDoc format
* Improving user count SQL
* Some improvments from feedback.
* Omit empty on User.IsBot
2019-03-05 07:06:45 -08:00
2019-06-26 10:41:45 +02:00
if options . IncludeBotAccounts {
if options . ExcludeRegularUsers {
query = query . Join ( "Bots ON u.Id = Bots.UserId" )
MM-12393 Server side of bot accounts. (#10378)
* bots model, store and api (#9903)
* bots model, store and api
Fixes: MM-13100, MM-13101, MM-13103, MM-13105, MMM-13119
* uncomment tests incorrectly commented, and fix merge issues
* add etags support
* add missing licenses
* remove unused sqlbuilder.go (for now...)
* rejig permissions
* split out READ_BOTS into READ_BOTS and READ_OTHERS_BOTS, the latter
implicitly allowing the former
* make MANAGE_OTHERS_BOTS imply MANAGE_BOTS
* conform to general rest api pattern
* eliminate redundant http.StatusOK
* Update api4/bot.go
Co-Authored-By: lieut-data <jesse.hallam@gmail.com>
* s/model.UserFromBotModel/model.UserFromBot/g
* Update model/bot.go
Co-Authored-By: lieut-data <jesse.hallam@gmail.com>
* Update model/client4.go
Co-Authored-By: lieut-data <jesse.hallam@gmail.com>
* move sessionHasPermissionToManageBot to app/authorization.go
* use api.ApiSessionRequired for createBot
* introduce BOT_DESCRIPTION_MAX_RUNES constant
* MM-13512 Prevent getting a user by email based on privacy settings (#10021)
* MM-13512 Prevent getting a user by email based on privacy settings
* Add additional config settings to tests
* upgrade db to 5.7 (#10019)
* MM-13526 Add validation when setting a user's Locale field (#10022)
* Fix typos (#10024)
* Fixing first user being created with system admin privilages without being explicity specified. (#10014)
* Revert "Support for Embeded chat (#9129)" (#10017)
This reverts commit 3fcecd521a5c6ccfdb52fb4c3fb1f8c6ea528a4e.
* s/DisableBot/UpdateBotActive
* add permissions on upgrade
* Update NOTICE.txt (#10054)
- add new dependency (text)
- handle switch to forked dependency (go-gomail -> go-mail)
- misc copyright owner updates
* avoid leaking bot knowledge without permission
* [GH-6798] added a new api endpoint to get the bulk reactions for posts (#10049)
* 6798 added a new api to get the bulk reactions for posts
* 6798 added the permsission check before getting the reactions
* GH-6798 added a new app function for the new endpoint
* 6798 added a store method to get reactions for multiple posts
* 6798 connected the app function with the new store function
* 6798 fixed the review comments
* MM-13559 Update model.post.is_valid.file_ids.app_error text per report (#10055)
Ticket: https://mattermost.atlassian.net/browse/MM-13559
Report: https://github.com/mattermost/mattermost-server/issues/10023
* Trigger Login Hooks with OAuth (#10061)
* make BotStore.GetAll deterministic even on duplicate CreateAt
* fix spurious TestMuteCommandSpecificChannel test failure
See
https://community-daily.mattermost.com/core/pl/px9p8s3dzbg1pf3ddrm5cr36uw
* fix race in TestExportUserChannels
* TestExportUserChannels: remove SaveMember call, as it is redundant and used to be silently failing anyway
* MM-13117: bot tokens (#10111)
* eliminate redundant Client/AdminClient declarations
* harden TestUpdateChannelScheme to API failures
* eliminate unnecessary config restoration
* minor cleanup
* make TestGenerateMfaSecret config dependency explicit
* TestCreateUserAccessToken for bots
* TestGetUserAccessToken* for bots
* leverage SessionHasPermissionToUserOrBot for user token APIs
* Test(Revoke|Disable|Enable)UserAccessToken
* make EnableUserAccessTokens explicit, so as to not rely on local config.json
* uncomment TestResetPassword, but still skip
* mark assert(Invalid)Token as helper
* fix whitespace issues
* fix mangled comments
* MM-13116: bot plugin api (#10113)
* MM-13117: expose bot API to plugins
This also changes the `CreatorId` column definition to allow for plugin
ids, as the default unless the plugin overrides is to use the plugin id
here. This branch hasn't hit master yet, so no migration needed.
* gofmt issues
* expunge use of BotList in plugin/client API
* introduce model.BotGetOptions
* use botUserId term for clarity
* MM-13129 Adding functionality to deal with orphaned bots (#10238)
* Add way to list orphaned bots.
* Add /assign route to modify ownership of bot accounts.
* Apply suggestions from code review
Co-Authored-By: crspeller <crspeller@gmail.com>
* MM-13120: add IsBot field to returned user objects (#10103)
* MM-13104: forbid bot login (#10251)
* MM-13104: disallow bot login
* fix shadowing
* MM-13136 Disable user bots when user is disabled. (#10293)
* Disable user bots when user is disabled.
* Grammer.
Co-Authored-By: crspeller <crspeller@gmail.com>
* Fixing bot branch for test changes.
* Don't use external dependancies in bot plugin tests.
* Rename bot CreatorId to OwnerId
* Adding ability to re-enable bots
* Fixing IsBot to not attempt to be saved to DB.
* Adding diagnostics and licencing counting for bot accounts.
* Modifying gorp to allow reading of '-' fields.
* Removing unnessisary nil values from UserCountOptions.
* Changing comment to GoDoc format
* Improving user count SQL
* Some improvments from feedback.
* Omit empty on User.IsBot
2019-03-05 07:06:45 -08:00
}
2019-06-26 10:41:45 +02:00
} else {
query = query . LeftJoin ( "Bots ON u.Id = Bots.UserId" ) . Where ( "Bots.UserId IS NULL" )
if options . ExcludeRegularUsers {
// Currenty this doesn't make sense because it will always return 0
return int64 ( 0 ) , model . NewAppError ( "SqlUserStore.Count" , "store.sql_user.count.app_error" , nil , "" , http . StatusInternalServerError )
2016-01-21 12:14:17 -05:00
}
2019-06-26 10:41:45 +02:00
}
2016-01-21 12:14:17 -05:00
2019-06-26 10:41:45 +02:00
if options . TeamId != "" {
query = query . LeftJoin ( "TeamMembers AS tm ON u.Id = tm.UserId" ) . Where ( "tm.TeamId = ? AND tm.DeleteAt = 0" , options . TeamId )
}
query = applyViewRestrictionsFilter ( query , options . ViewRestrictions , false )
MM-12393 Server side of bot accounts. (#10378)
* bots model, store and api (#9903)
* bots model, store and api
Fixes: MM-13100, MM-13101, MM-13103, MM-13105, MMM-13119
* uncomment tests incorrectly commented, and fix merge issues
* add etags support
* add missing licenses
* remove unused sqlbuilder.go (for now...)
* rejig permissions
* split out READ_BOTS into READ_BOTS and READ_OTHERS_BOTS, the latter
implicitly allowing the former
* make MANAGE_OTHERS_BOTS imply MANAGE_BOTS
* conform to general rest api pattern
* eliminate redundant http.StatusOK
* Update api4/bot.go
Co-Authored-By: lieut-data <jesse.hallam@gmail.com>
* s/model.UserFromBotModel/model.UserFromBot/g
* Update model/bot.go
Co-Authored-By: lieut-data <jesse.hallam@gmail.com>
* Update model/client4.go
Co-Authored-By: lieut-data <jesse.hallam@gmail.com>
* move sessionHasPermissionToManageBot to app/authorization.go
* use api.ApiSessionRequired for createBot
* introduce BOT_DESCRIPTION_MAX_RUNES constant
* MM-13512 Prevent getting a user by email based on privacy settings (#10021)
* MM-13512 Prevent getting a user by email based on privacy settings
* Add additional config settings to tests
* upgrade db to 5.7 (#10019)
* MM-13526 Add validation when setting a user's Locale field (#10022)
* Fix typos (#10024)
* Fixing first user being created with system admin privilages without being explicity specified. (#10014)
* Revert "Support for Embeded chat (#9129)" (#10017)
This reverts commit 3fcecd521a5c6ccfdb52fb4c3fb1f8c6ea528a4e.
* s/DisableBot/UpdateBotActive
* add permissions on upgrade
* Update NOTICE.txt (#10054)
- add new dependency (text)
- handle switch to forked dependency (go-gomail -> go-mail)
- misc copyright owner updates
* avoid leaking bot knowledge without permission
* [GH-6798] added a new api endpoint to get the bulk reactions for posts (#10049)
* 6798 added a new api to get the bulk reactions for posts
* 6798 added the permsission check before getting the reactions
* GH-6798 added a new app function for the new endpoint
* 6798 added a store method to get reactions for multiple posts
* 6798 connected the app function with the new store function
* 6798 fixed the review comments
* MM-13559 Update model.post.is_valid.file_ids.app_error text per report (#10055)
Ticket: https://mattermost.atlassian.net/browse/MM-13559
Report: https://github.com/mattermost/mattermost-server/issues/10023
* Trigger Login Hooks with OAuth (#10061)
* make BotStore.GetAll deterministic even on duplicate CreateAt
* fix spurious TestMuteCommandSpecificChannel test failure
See
https://community-daily.mattermost.com/core/pl/px9p8s3dzbg1pf3ddrm5cr36uw
* fix race in TestExportUserChannels
* TestExportUserChannels: remove SaveMember call, as it is redundant and used to be silently failing anyway
* MM-13117: bot tokens (#10111)
* eliminate redundant Client/AdminClient declarations
* harden TestUpdateChannelScheme to API failures
* eliminate unnecessary config restoration
* minor cleanup
* make TestGenerateMfaSecret config dependency explicit
* TestCreateUserAccessToken for bots
* TestGetUserAccessToken* for bots
* leverage SessionHasPermissionToUserOrBot for user token APIs
* Test(Revoke|Disable|Enable)UserAccessToken
* make EnableUserAccessTokens explicit, so as to not rely on local config.json
* uncomment TestResetPassword, but still skip
* mark assert(Invalid)Token as helper
* fix whitespace issues
* fix mangled comments
* MM-13116: bot plugin api (#10113)
* MM-13117: expose bot API to plugins
This also changes the `CreatorId` column definition to allow for plugin
ids, as the default unless the plugin overrides is to use the plugin id
here. This branch hasn't hit master yet, so no migration needed.
* gofmt issues
* expunge use of BotList in plugin/client API
* introduce model.BotGetOptions
* use botUserId term for clarity
* MM-13129 Adding functionality to deal with orphaned bots (#10238)
* Add way to list orphaned bots.
* Add /assign route to modify ownership of bot accounts.
* Apply suggestions from code review
Co-Authored-By: crspeller <crspeller@gmail.com>
* MM-13120: add IsBot field to returned user objects (#10103)
* MM-13104: forbid bot login (#10251)
* MM-13104: disallow bot login
* fix shadowing
* MM-13136 Disable user bots when user is disabled. (#10293)
* Disable user bots when user is disabled.
* Grammer.
Co-Authored-By: crspeller <crspeller@gmail.com>
* Fixing bot branch for test changes.
* Don't use external dependancies in bot plugin tests.
* Rename bot CreatorId to OwnerId
* Adding ability to re-enable bots
* Fixing IsBot to not attempt to be saved to DB.
* Adding diagnostics and licencing counting for bot accounts.
* Modifying gorp to allow reading of '-' fields.
* Removing unnessisary nil values from UserCountOptions.
* Changing comment to GoDoc format
* Improving user count SQL
* Some improvments from feedback.
* Omit empty on User.IsBot
2019-03-05 07:06:45 -08:00
2019-06-26 10:41:45 +02:00
if us . DriverName ( ) == model . DATABASE_DRIVER_POSTGRES {
query = query . PlaceholderFormat ( sq . Dollar )
}
MM-12393 Server side of bot accounts. (#10378)
* bots model, store and api (#9903)
* bots model, store and api
Fixes: MM-13100, MM-13101, MM-13103, MM-13105, MMM-13119
* uncomment tests incorrectly commented, and fix merge issues
* add etags support
* add missing licenses
* remove unused sqlbuilder.go (for now...)
* rejig permissions
* split out READ_BOTS into READ_BOTS and READ_OTHERS_BOTS, the latter
implicitly allowing the former
* make MANAGE_OTHERS_BOTS imply MANAGE_BOTS
* conform to general rest api pattern
* eliminate redundant http.StatusOK
* Update api4/bot.go
Co-Authored-By: lieut-data <jesse.hallam@gmail.com>
* s/model.UserFromBotModel/model.UserFromBot/g
* Update model/bot.go
Co-Authored-By: lieut-data <jesse.hallam@gmail.com>
* Update model/client4.go
Co-Authored-By: lieut-data <jesse.hallam@gmail.com>
* move sessionHasPermissionToManageBot to app/authorization.go
* use api.ApiSessionRequired for createBot
* introduce BOT_DESCRIPTION_MAX_RUNES constant
* MM-13512 Prevent getting a user by email based on privacy settings (#10021)
* MM-13512 Prevent getting a user by email based on privacy settings
* Add additional config settings to tests
* upgrade db to 5.7 (#10019)
* MM-13526 Add validation when setting a user's Locale field (#10022)
* Fix typos (#10024)
* Fixing first user being created with system admin privilages without being explicity specified. (#10014)
* Revert "Support for Embeded chat (#9129)" (#10017)
This reverts commit 3fcecd521a5c6ccfdb52fb4c3fb1f8c6ea528a4e.
* s/DisableBot/UpdateBotActive
* add permissions on upgrade
* Update NOTICE.txt (#10054)
- add new dependency (text)
- handle switch to forked dependency (go-gomail -> go-mail)
- misc copyright owner updates
* avoid leaking bot knowledge without permission
* [GH-6798] added a new api endpoint to get the bulk reactions for posts (#10049)
* 6798 added a new api to get the bulk reactions for posts
* 6798 added the permsission check before getting the reactions
* GH-6798 added a new app function for the new endpoint
* 6798 added a store method to get reactions for multiple posts
* 6798 connected the app function with the new store function
* 6798 fixed the review comments
* MM-13559 Update model.post.is_valid.file_ids.app_error text per report (#10055)
Ticket: https://mattermost.atlassian.net/browse/MM-13559
Report: https://github.com/mattermost/mattermost-server/issues/10023
* Trigger Login Hooks with OAuth (#10061)
* make BotStore.GetAll deterministic even on duplicate CreateAt
* fix spurious TestMuteCommandSpecificChannel test failure
See
https://community-daily.mattermost.com/core/pl/px9p8s3dzbg1pf3ddrm5cr36uw
* fix race in TestExportUserChannels
* TestExportUserChannels: remove SaveMember call, as it is redundant and used to be silently failing anyway
* MM-13117: bot tokens (#10111)
* eliminate redundant Client/AdminClient declarations
* harden TestUpdateChannelScheme to API failures
* eliminate unnecessary config restoration
* minor cleanup
* make TestGenerateMfaSecret config dependency explicit
* TestCreateUserAccessToken for bots
* TestGetUserAccessToken* for bots
* leverage SessionHasPermissionToUserOrBot for user token APIs
* Test(Revoke|Disable|Enable)UserAccessToken
* make EnableUserAccessTokens explicit, so as to not rely on local config.json
* uncomment TestResetPassword, but still skip
* mark assert(Invalid)Token as helper
* fix whitespace issues
* fix mangled comments
* MM-13116: bot plugin api (#10113)
* MM-13117: expose bot API to plugins
This also changes the `CreatorId` column definition to allow for plugin
ids, as the default unless the plugin overrides is to use the plugin id
here. This branch hasn't hit master yet, so no migration needed.
* gofmt issues
* expunge use of BotList in plugin/client API
* introduce model.BotGetOptions
* use botUserId term for clarity
* MM-13129 Adding functionality to deal with orphaned bots (#10238)
* Add way to list orphaned bots.
* Add /assign route to modify ownership of bot accounts.
* Apply suggestions from code review
Co-Authored-By: crspeller <crspeller@gmail.com>
* MM-13120: add IsBot field to returned user objects (#10103)
* MM-13104: forbid bot login (#10251)
* MM-13104: disallow bot login
* fix shadowing
* MM-13136 Disable user bots when user is disabled. (#10293)
* Disable user bots when user is disabled.
* Grammer.
Co-Authored-By: crspeller <crspeller@gmail.com>
* Fixing bot branch for test changes.
* Don't use external dependancies in bot plugin tests.
* Rename bot CreatorId to OwnerId
* Adding ability to re-enable bots
* Fixing IsBot to not attempt to be saved to DB.
* Adding diagnostics and licencing counting for bot accounts.
* Modifying gorp to allow reading of '-' fields.
* Removing unnessisary nil values from UserCountOptions.
* Changing comment to GoDoc format
* Improving user count SQL
* Some improvments from feedback.
* Omit empty on User.IsBot
2019-03-05 07:06:45 -08:00
2019-06-26 10:41:45 +02:00
queryString , args , err := query . ToSql ( )
if err != nil {
return int64 ( 0 ) , model . NewAppError ( "SqlUserStore.Get" , "store.sql_user.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
}
count , err := us . GetReplica ( ) . SelectInt ( queryString , args ... )
if err != nil {
return int64 ( 0 ) , model . NewAppError ( "SqlUserStore.Count" , "store.sql_user.get_total_users_count.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
}
return count , nil
2016-01-21 12:14:17 -05:00
}
2016-03-11 00:14:55 -03:00
2017-09-25 09:11:25 -05:00
func ( us SqlUserStore ) AnalyticsActiveCount ( timePeriod int64 ) store . StoreChannel {
2017-10-06 08:12:10 -07:00
return store . Do ( func ( result * store . StoreResult ) {
2017-01-20 15:24:53 -05:00
time := model . GetMillis ( ) - timePeriod
query := "SELECT COUNT(*) FROM Status WHERE LastActivityAt > :Time"
v , err := us . GetReplica ( ) . SelectInt ( query , map [ string ] interface { } { "Time" : time } )
if err != nil {
2017-09-18 17:40:56 +01:00
result . Err = model . NewAppError ( "SqlUserStore.AnalyticsDailyActiveUsers" , "store.sql_user.analytics_daily_active_users.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
2017-01-20 15:24:53 -05:00
} else {
result . Data = v
}
2017-10-06 08:12:10 -07:00
} )
2017-01-20 15:24:53 -05:00
}
2019-06-26 16:48:20 +02:00
func ( us SqlUserStore ) GetUnreadCount ( userId string ) ( int64 , error ) {
query := `
2017-03-07 16:05:01 -05:00
SELECT SUM ( CASE WHEN c . Type = 'D' THEN ( c . TotalMsgCount - cm . MsgCount ) ELSE cm . MentionCount END )
FROM Channels c
INNER JOIN ChannelMembers cm
2019-06-26 16:48:20 +02:00
ON cm . ChannelId = c . Id
AND cm . UserId = : UserId
AND c . DeleteAt = 0
`
count , err := us . GetReplica ( ) . SelectInt ( query , map [ string ] interface { } { "UserId" : userId } )
if err != nil {
return count , model . NewAppError ( "SqlUserStore.GetMentionCount" , "store.sql_user.get_unread_count.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
}
return count , nil
2016-03-11 00:14:55 -03:00
}
2016-08-31 12:52:14 -04:00
2017-09-25 09:11:25 -05:00
func ( us SqlUserStore ) GetUnreadCountForChannel ( userId string , channelId string ) store . StoreChannel {
2017-10-06 08:12:10 -07:00
return store . Do ( func ( result * store . StoreResult ) {
2019-03-18 11:34:34 -03:00
if count , err := us . GetReplica ( ) . SelectInt ( "SELECT SUM(CASE WHEN c.Type = 'D' THEN (c.TotalMsgCount - cm.MsgCount) ELSE cm.MentionCount END) FROM Channels c INNER JOIN ChannelMembers cm ON c.Id = cm.ChannelId AND cm.ChannelId = :ChannelId AND cm.UserId = :UserId" , map [ string ] interface { } { "ChannelId" : channelId , "UserId" : userId } ) ; err != nil {
2017-09-18 17:40:56 +01:00
result . Err = model . NewAppError ( "SqlUserStore.GetMentionCountForChannel" , "store.sql_user.get_unread_count_for_channel.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
2016-08-31 12:52:14 -04:00
} else {
result . Data = count
}
2017-10-06 08:12:10 -07:00
} )
2016-08-31 12:52:14 -04:00
}
2016-10-19 14:49:25 -04:00
2019-06-26 16:27:30 +01:00
func ( us SqlUserStore ) GetAnyUnreadPostCountForChannel ( userId string , channelId string ) ( int64 , * model . AppError ) {
count , err := us . GetReplica ( ) . SelectInt ( "SELECT SUM(c.TotalMsgCount - cm.MsgCount) FROM Channels c INNER JOIN ChannelMembers cm ON c.Id = cm.ChannelId AND cm.ChannelId = :ChannelId AND cm.UserId = :UserId" , map [ string ] interface { } { "ChannelId" : channelId , "UserId" : userId } )
if err != nil {
return count , model . NewAppError ( "SqlUserStore.GetMentionCountForChannel" , "store.sql_user.get_unread_count_for_channel.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
}
return count , nil
2018-09-26 16:34:12 +02:00
}
2019-06-26 15:51:02 +01:00
func ( us SqlUserStore ) Search ( teamId string , term string , options * model . UserSearchOptions ) ( [ ] * model . User , * model . AppError ) {
query := us . usersQuery .
OrderBy ( "Username ASC" ) .
Limit ( uint64 ( options . Limit ) )
2017-03-23 06:34:22 -04:00
2019-06-26 15:51:02 +01:00
if teamId != "" {
query = query . Join ( "TeamMembers tm ON ( tm.UserId = u.Id AND tm.DeleteAt = 0 AND tm.TeamId = ? )" , teamId )
}
result := us . performSearch ( query , term , options )
if result . Err != nil {
return nil , result . Err
}
return result . Data . ( [ ] * model . User ) , nil
2017-03-23 06:34:22 -04:00
}
2018-10-17 11:24:12 -04:00
func ( us SqlUserStore ) SearchWithoutTeam ( term string , options * model . UserSearchOptions ) store . StoreChannel {
2017-10-06 08:12:10 -07:00
return store . Do ( func ( result * store . StoreResult ) {
2019-01-29 15:24:57 -05:00
query := us . usersQuery .
Where ( ` (
SELECT
COUNT ( 0 )
FROM
TeamMembers
WHERE
TeamMembers . UserId = u . Id
AND TeamMembers . DeleteAt = 0
) = 0 ` ) .
OrderBy ( "u.Username ASC" ) .
Limit ( uint64 ( options . Limit ) )
* result = us . performSearch ( query , term , options )
2017-10-06 08:12:10 -07:00
} )
2017-04-03 18:11:12 +01:00
}
2018-10-17 11:24:12 -04:00
func ( us SqlUserStore ) SearchNotInTeam ( notInTeamId string , term string , options * model . UserSearchOptions ) store . StoreChannel {
2017-10-06 08:12:10 -07:00
return store . Do ( func ( result * store . StoreResult ) {
2019-01-29 15:24:57 -05:00
query := us . usersQuery .
LeftJoin ( "TeamMembers tm ON ( tm.UserId = u.Id AND tm.DeleteAt = 0 AND tm.TeamId = ? )" , notInTeamId ) .
Where ( "tm.UserId IS NULL" ) .
OrderBy ( "u.Username ASC" ) .
Limit ( uint64 ( options . Limit ) )
2016-10-19 14:49:25 -04:00
2019-05-16 10:12:06 +01:00
if options . GroupConstrained {
query = applyTeamGroupConstrainedFilter ( query , notInTeamId )
}
2019-01-29 15:24:57 -05:00
* result = us . performSearch ( query , term , options )
2017-10-06 08:12:10 -07:00
} )
2016-10-19 14:49:25 -04:00
}
2018-10-17 11:24:12 -04:00
func ( us SqlUserStore ) SearchNotInChannel ( teamId string , channelId string , term string , options * model . UserSearchOptions ) store . StoreChannel {
2017-10-06 08:12:10 -07:00
return store . Do ( func ( result * store . StoreResult ) {
2019-01-29 15:24:57 -05:00
query := us . usersQuery .
LeftJoin ( "ChannelMembers cm ON ( cm.UserId = u.Id AND cm.ChannelId = ? )" , channelId ) .
Where ( "cm.UserId IS NULL" ) .
OrderBy ( "Username ASC" ) .
Limit ( uint64 ( options . Limit ) )
if teamId != "" {
query = query . Join ( "TeamMembers tm ON ( tm.UserId = u.Id AND tm.DeleteAt = 0 AND tm.TeamId = ? )" , teamId )
}
2019-05-16 10:12:06 +01:00
if options . GroupConstrained {
query = applyChannelGroupConstrainedFilter ( query , channelId )
}
2019-01-29 15:24:57 -05:00
* result = us . performSearch ( query , term , options )
2017-10-06 08:12:10 -07:00
} )
2016-10-19 14:49:25 -04:00
}
2018-10-17 11:24:12 -04:00
func ( us SqlUserStore ) SearchInChannel ( channelId string , term string , options * model . UserSearchOptions ) store . StoreChannel {
2017-10-06 08:12:10 -07:00
return store . Do ( func ( result * store . StoreResult ) {
2019-01-29 15:24:57 -05:00
query := us . usersQuery .
Join ( "ChannelMembers cm ON ( cm.UserId = u.Id AND cm.ChannelId = ? )" , channelId ) .
OrderBy ( "Username ASC" ) .
Limit ( uint64 ( options . Limit ) )
2016-10-19 14:49:25 -04:00
2019-01-29 15:24:57 -05:00
* result = us . performSearch ( query , term , options )
2017-10-06 08:12:10 -07:00
} )
2016-10-19 14:49:25 -04:00
}
2018-01-05 15:59:10 +00:00
var escapeLikeSearchChar = [ ] string {
2017-09-27 18:44:22 +01:00
"%" ,
"_" ,
2017-01-03 17:11:44 -05:00
}
2018-01-05 15:59:10 +00:00
var ignoreLikeSearchChar = [ ] string {
2017-09-27 18:44:22 +01:00
"*" ,
2016-11-21 12:17:53 -05:00
}
2018-01-05 15:59:10 +00:00
var spaceFulltextSearchChar = [ ] string {
"<" ,
">" ,
"+" ,
"-" ,
"(" ,
")" ,
"~" ,
":" ,
"*" ,
"\"" ,
"!" ,
"@" ,
}
2019-01-29 15:24:57 -05:00
func generateSearchQuery ( query sq . SelectBuilder , terms [ ] string , fields [ ] string , isPostgreSQL bool ) sq . SelectBuilder {
for _ , term := range terms {
2017-10-31 11:52:10 -04:00
searchFields := [ ] string { }
2019-01-29 15:24:57 -05:00
termArgs := [ ] interface { } { }
2017-10-31 11:52:10 -04:00
for _ , field := range fields {
2017-10-11 17:20:27 +08:00
if isPostgreSQL {
2019-01-29 15:24:57 -05:00
searchFields = append ( searchFields , fmt . Sprintf ( "lower(%s) LIKE lower(?) escape '*' " , field ) )
2017-10-11 17:20:27 +08:00
} else {
2019-01-29 15:24:57 -05:00
searchFields = append ( searchFields , fmt . Sprintf ( "%s LIKE ? escape '*' " , field ) )
2017-10-11 17:20:27 +08:00
}
2019-01-29 15:24:57 -05:00
termArgs = append ( termArgs , fmt . Sprintf ( "%s%%" , strings . TrimLeft ( term , "@" ) ) )
2017-10-11 17:20:27 +08:00
}
2019-01-29 15:24:57 -05:00
query = query . Where ( fmt . Sprintf ( "(%s)" , strings . Join ( searchFields , " OR " ) ) , termArgs ... )
2019-01-11 14:50:32 +01:00
}
2019-01-29 15:24:57 -05:00
return query
2017-10-11 17:20:27 +08:00
}
2019-01-29 15:24:57 -05:00
func ( us SqlUserStore ) performSearch ( query sq . SelectBuilder , term string , options * model . UserSearchOptions ) store . StoreResult {
2017-09-25 09:11:25 -05:00
result := store . StoreResult { }
2016-10-19 14:49:25 -04:00
2017-09-27 18:44:22 +01:00
// These chars must be removed from the like query.
2018-01-05 15:59:10 +00:00
for _ , c := range ignoreLikeSearchChar {
2017-09-27 18:44:22 +01:00
term = strings . Replace ( term , c , "" , - 1 )
2017-01-03 17:11:44 -05:00
}
2017-09-27 18:44:22 +01:00
// These chars must be escaped in the like query.
2018-01-05 15:59:10 +00:00
for _ , c := range escapeLikeSearchChar {
2017-09-27 18:44:22 +01:00
term = strings . Replace ( term , c , "*" + c , - 1 )
2016-10-31 09:10:20 -04:00
}
2018-10-17 11:24:12 -04:00
searchType := USER_SEARCH_TYPE_NAMES_NO_FULL_NAME
if options . AllowEmails {
if options . AllowFullNames {
searchType = USER_SEARCH_TYPE_ALL
} else {
searchType = USER_SEARCH_TYPE_ALL_NO_FULL_NAME
}
} else {
if options . AllowFullNames {
searchType = USER_SEARCH_TYPE_NAMES
} else {
searchType = USER_SEARCH_TYPE_NAMES_NO_FULL_NAME
}
2016-11-02 14:38:34 -04:00
}
2019-01-29 15:24:57 -05:00
isPostgreSQL := us . DriverName ( ) == model . DATABASE_DRIVER_POSTGRES
query = applyRoleFilter ( query , options . Role , isPostgreSQL )
if ! options . AllowInactive {
query = query . Where ( "u.DeleteAt = 0" )
2019-01-11 14:50:32 +01:00
}
2019-01-29 15:24:57 -05:00
if strings . TrimSpace ( term ) != "" {
query = generateSearchQuery ( query , strings . Fields ( term ) , searchType , isPostgreSQL )
2016-11-02 14:38:34 -04:00
}
2019-04-29 16:56:56 +02:00
query = applyViewRestrictionsFilter ( query , options . ViewRestrictions , true )
2019-01-29 15:24:57 -05:00
queryString , args , err := query . ToSql ( )
if err != nil {
result . Err = model . NewAppError ( "SqlUserStore.Search" , "store.sql_user.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
return result
2016-10-19 14:49:25 -04:00
}
var users [ ] * model . User
2019-01-29 15:24:57 -05:00
if _ , err := us . GetReplica ( ) . Select ( & users , queryString , args ... ) ; err != nil {
2017-10-31 11:52:10 -04:00
result . Err = model . NewAppError ( "SqlUserStore.Search" , "store.sql_user.search.app_error" , nil ,
fmt . Sprintf ( "term=%v, search_type=%v, %v" , term , searchType , err . Error ( ) ) , http . StatusInternalServerError )
2016-10-19 14:49:25 -04:00
} else {
for _ , u := range users {
2017-04-19 15:38:35 -04:00
u . Sanitize ( map [ string ] bool { } )
2016-10-19 14:49:25 -04:00
}
result . Data = users
}
return result
}
2017-02-24 17:33:59 +00:00
2017-09-25 09:11:25 -05:00
func ( us SqlUserStore ) AnalyticsGetInactiveUsersCount ( ) store . StoreChannel {
2017-10-06 08:12:10 -07:00
return store . Do ( func ( result * store . StoreResult ) {
2017-02-24 17:33:59 +00:00
if count , err := us . GetReplica ( ) . SelectInt ( "SELECT COUNT(Id) FROM Users WHERE DeleteAt > 0" ) ; err != nil {
2017-09-18 17:40:56 +01:00
result . Err = model . NewAppError ( "SqlUserStore.AnalyticsGetInactiveUsersCount" , "store.sql_user.analytics_get_inactive_users_count.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
2017-02-24 17:33:59 +00:00
} else {
result . Data = count
}
2017-10-06 08:12:10 -07:00
} )
2017-02-24 17:33:59 +00:00
}
2017-09-25 09:11:25 -05:00
func ( us SqlUserStore ) AnalyticsGetSystemAdminCount ( ) store . StoreChannel {
2017-10-06 08:12:10 -07:00
return store . Do ( func ( result * store . StoreResult ) {
2017-02-24 17:33:59 +00:00
if count , err := us . GetReplica ( ) . SelectInt ( "SELECT count(*) FROM Users WHERE Roles LIKE :Roles and DeleteAt = 0" , map [ string ] interface { } { "Roles" : "%system_admin%" } ) ; err != nil {
2017-09-18 17:40:56 +01:00
result . Err = model . NewAppError ( "SqlUserStore.AnalyticsGetSystemAdminCount" , "store.sql_user.analytics_get_system_admin_count.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
2017-02-24 17:33:59 +00:00
} else {
result . Data = count
}
2017-10-06 08:12:10 -07:00
} )
2017-02-24 17:33:59 +00:00
}
2017-03-30 02:10:51 +01:00
2019-05-16 10:12:06 +01:00
func ( us SqlUserStore ) GetProfilesNotInTeam ( teamId string , groupConstrained bool , offset int , limit int , viewRestrictions * model . ViewUsersRestrictions ) store . StoreChannel {
2017-10-06 08:12:10 -07:00
return store . Do ( func ( result * store . StoreResult ) {
2019-01-29 15:24:57 -05:00
query := us . usersQuery .
LeftJoin ( "TeamMembers tm ON ( tm.UserId = u.Id AND tm.DeleteAt = 0 AND tm.TeamId = ? )" , teamId ) .
Where ( "tm.UserId IS NULL" ) .
OrderBy ( "u.Username ASC" ) .
Offset ( uint64 ( offset ) ) . Limit ( uint64 ( limit ) )
2017-03-30 02:10:51 +01:00
2019-04-29 16:56:56 +02:00
query = applyViewRestrictionsFilter ( query , viewRestrictions , true )
2019-05-16 10:12:06 +01:00
if groupConstrained {
query = applyTeamGroupConstrainedFilter ( query , teamId )
}
2019-01-29 15:24:57 -05:00
queryString , args , err := query . ToSql ( )
if err != nil {
result . Err = model . NewAppError ( "SqlUserStore.GetProfilesNotInTeam" , "store.sql_user.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
return
}
2017-03-30 02:10:51 +01:00
2019-01-29 15:24:57 -05:00
var users [ ] * model . User
if _ , err := us . GetReplica ( ) . Select ( & users , queryString , args ... ) ; err != nil {
result . Err = model . NewAppError ( "SqlUserStore.GetProfilesNotInTeam" , "store.sql_user.get_profiles.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
return
}
2017-03-30 02:10:51 +01:00
2019-01-29 15:24:57 -05:00
for _ , u := range users {
u . Sanitize ( map [ string ] bool { } )
2017-03-30 02:10:51 +01:00
}
2019-01-29 15:24:57 -05:00
result . Data = users
2017-10-06 08:12:10 -07:00
} )
2017-03-30 02:10:51 +01:00
}
2017-09-25 09:11:25 -05:00
func ( us SqlUserStore ) GetEtagForProfilesNotInTeam ( teamId string ) store . StoreChannel {
2017-10-06 08:12:10 -07:00
return store . Do ( func ( result * store . StoreResult ) {
2017-03-30 02:10:51 +01:00
[MM-13721] Fix the etag function GetEtagForProfilesNotInTeam (#10360)
* Initial solution for Draft PR
* Reformat tabs to spaces for readability
* Remove my comments and debugging lines for Core Commiter to see changes
more easily
* Remove all comments and show only code changes
* Match indentation spacing of new query to old query to make diff reading
easier for core committer
* Remove everything except what want to show core commiter
* Restrucure query and resulting etag value.
get number of users not in team from SELECT subquery.
etag return values is now of the form
<model.CurrenVersion>.<UpdateAt>.<number_profiles_not_int_team>
* Remove skipped test for solution to https://mattermost.atlassian.net/browse/MM-13721.
Remove comment for failing description of test
store u4 with prepended "u4". Similar to u1, u2, u3. This is easier
for debugging when looking in the database
Added skipped test:
check that etag does not change when a user, not in team 1, is added
to different team. UpdateAt will change, but users in the set does
not
* Remove skipped test for solution to https://mattermost.atlassian.net/browse/MM-13721.
Remove comment for failing description of test
store u4 with prepended "u4". Similar to u1, u2, u3. This is easier
for debugging when looking in the database
Added skipped test:
check that etag does not change when a user, not in team 1, is added
to different team. UpdateAt will change, but users in the set does
not
* remove commented out tests
* Restructure and simplify the SQL query for GetEtagForProfilesNotInTeam.
- Build the query to get all profiles not in a specified team
- select latest UpdateAt Value by getting Max value from UpdateAt field.
- select Number of profiles not in Team from count of the returned Ids
The previous query required building a complex query with multiple
joins and repeated code in a select subquery, and derived table
* Format SQL styling indentation, spaces around equal signs, and new lines
* Add description for skipped test
2019-02-27 20:51:06 -06:00
var querystr string
querystr = `
2019-03-19 12:42:08 +00:00
SELECT
[MM-13721] Fix the etag function GetEtagForProfilesNotInTeam (#10360)
* Initial solution for Draft PR
* Reformat tabs to spaces for readability
* Remove my comments and debugging lines for Core Commiter to see changes
more easily
* Remove all comments and show only code changes
* Match indentation spacing of new query to old query to make diff reading
easier for core committer
* Remove everything except what want to show core commiter
* Restrucure query and resulting etag value.
get number of users not in team from SELECT subquery.
etag return values is now of the form
<model.CurrenVersion>.<UpdateAt>.<number_profiles_not_int_team>
* Remove skipped test for solution to https://mattermost.atlassian.net/browse/MM-13721.
Remove comment for failing description of test
store u4 with prepended "u4". Similar to u1, u2, u3. This is easier
for debugging when looking in the database
Added skipped test:
check that etag does not change when a user, not in team 1, is added
to different team. UpdateAt will change, but users in the set does
not
* Remove skipped test for solution to https://mattermost.atlassian.net/browse/MM-13721.
Remove comment for failing description of test
store u4 with prepended "u4". Similar to u1, u2, u3. This is easier
for debugging when looking in the database
Added skipped test:
check that etag does not change when a user, not in team 1, is added
to different team. UpdateAt will change, but users in the set does
not
* remove commented out tests
* Restructure and simplify the SQL query for GetEtagForProfilesNotInTeam.
- Build the query to get all profiles not in a specified team
- select latest UpdateAt Value by getting Max value from UpdateAt field.
- select Number of profiles not in Team from count of the returned Ids
The previous query required building a complex query with multiple
joins and repeated code in a select subquery, and derived table
* Format SQL styling indentation, spaces around equal signs, and new lines
* Add description for skipped test
2019-02-27 20:51:06 -06:00
CONCAT ( MAX ( UpdateAt ) , '.' , COUNT ( Id ) ) as etag
2019-03-19 12:42:08 +00:00
FROM
[MM-13721] Fix the etag function GetEtagForProfilesNotInTeam (#10360)
* Initial solution for Draft PR
* Reformat tabs to spaces for readability
* Remove my comments and debugging lines for Core Commiter to see changes
more easily
* Remove all comments and show only code changes
* Match indentation spacing of new query to old query to make diff reading
easier for core committer
* Remove everything except what want to show core commiter
* Restrucure query and resulting etag value.
get number of users not in team from SELECT subquery.
etag return values is now of the form
<model.CurrenVersion>.<UpdateAt>.<number_profiles_not_int_team>
* Remove skipped test for solution to https://mattermost.atlassian.net/browse/MM-13721.
Remove comment for failing description of test
store u4 with prepended "u4". Similar to u1, u2, u3. This is easier
for debugging when looking in the database
Added skipped test:
check that etag does not change when a user, not in team 1, is added
to different team. UpdateAt will change, but users in the set does
not
* Remove skipped test for solution to https://mattermost.atlassian.net/browse/MM-13721.
Remove comment for failing description of test
store u4 with prepended "u4". Similar to u1, u2, u3. This is easier
for debugging when looking in the database
Added skipped test:
check that etag does not change when a user, not in team 1, is added
to different team. UpdateAt will change, but users in the set does
not
* remove commented out tests
* Restructure and simplify the SQL query for GetEtagForProfilesNotInTeam.
- Build the query to get all profiles not in a specified team
- select latest UpdateAt Value by getting Max value from UpdateAt field.
- select Number of profiles not in Team from count of the returned Ids
The previous query required building a complex query with multiple
joins and repeated code in a select subquery, and derived table
* Format SQL styling indentation, spaces around equal signs, and new lines
* Add description for skipped test
2019-02-27 20:51:06 -06:00
Users as u
2019-03-19 12:42:08 +00:00
LEFT JOIN TeamMembers tm
ON tm . UserId = u . Id
AND tm . TeamId = : TeamId
[MM-13721] Fix the etag function GetEtagForProfilesNotInTeam (#10360)
* Initial solution for Draft PR
* Reformat tabs to spaces for readability
* Remove my comments and debugging lines for Core Commiter to see changes
more easily
* Remove all comments and show only code changes
* Match indentation spacing of new query to old query to make diff reading
easier for core committer
* Remove everything except what want to show core commiter
* Restrucure query and resulting etag value.
get number of users not in team from SELECT subquery.
etag return values is now of the form
<model.CurrenVersion>.<UpdateAt>.<number_profiles_not_int_team>
* Remove skipped test for solution to https://mattermost.atlassian.net/browse/MM-13721.
Remove comment for failing description of test
store u4 with prepended "u4". Similar to u1, u2, u3. This is easier
for debugging when looking in the database
Added skipped test:
check that etag does not change when a user, not in team 1, is added
to different team. UpdateAt will change, but users in the set does
not
* Remove skipped test for solution to https://mattermost.atlassian.net/browse/MM-13721.
Remove comment for failing description of test
store u4 with prepended "u4". Similar to u1, u2, u3. This is easier
for debugging when looking in the database
Added skipped test:
check that etag does not change when a user, not in team 1, is added
to different team. UpdateAt will change, but users in the set does
not
* remove commented out tests
* Restructure and simplify the SQL query for GetEtagForProfilesNotInTeam.
- Build the query to get all profiles not in a specified team
- select latest UpdateAt Value by getting Max value from UpdateAt field.
- select Number of profiles not in Team from count of the returned Ids
The previous query required building a complex query with multiple
joins and repeated code in a select subquery, and derived table
* Format SQL styling indentation, spaces around equal signs, and new lines
* Add description for skipped test
2019-02-27 20:51:06 -06:00
AND tm . DeleteAt = 0
2019-03-19 12:42:08 +00:00
WHERE
[MM-13721] Fix the etag function GetEtagForProfilesNotInTeam (#10360)
* Initial solution for Draft PR
* Reformat tabs to spaces for readability
* Remove my comments and debugging lines for Core Commiter to see changes
more easily
* Remove all comments and show only code changes
* Match indentation spacing of new query to old query to make diff reading
easier for core committer
* Remove everything except what want to show core commiter
* Restrucure query and resulting etag value.
get number of users not in team from SELECT subquery.
etag return values is now of the form
<model.CurrenVersion>.<UpdateAt>.<number_profiles_not_int_team>
* Remove skipped test for solution to https://mattermost.atlassian.net/browse/MM-13721.
Remove comment for failing description of test
store u4 with prepended "u4". Similar to u1, u2, u3. This is easier
for debugging when looking in the database
Added skipped test:
check that etag does not change when a user, not in team 1, is added
to different team. UpdateAt will change, but users in the set does
not
* Remove skipped test for solution to https://mattermost.atlassian.net/browse/MM-13721.
Remove comment for failing description of test
store u4 with prepended "u4". Similar to u1, u2, u3. This is easier
for debugging when looking in the database
Added skipped test:
check that etag does not change when a user, not in team 1, is added
to different team. UpdateAt will change, but users in the set does
not
* remove commented out tests
* Restructure and simplify the SQL query for GetEtagForProfilesNotInTeam.
- Build the query to get all profiles not in a specified team
- select latest UpdateAt Value by getting Max value from UpdateAt field.
- select Number of profiles not in Team from count of the returned Ids
The previous query required building a complex query with multiple
joins and repeated code in a select subquery, and derived table
* Format SQL styling indentation, spaces around equal signs, and new lines
* Add description for skipped test
2019-02-27 20:51:06 -06:00
tm . UserId IS NULL
`
etag , err := us . GetReplica ( ) . SelectStr ( querystr , map [ string ] interface { } { "TeamId" : teamId } )
2017-03-30 02:10:51 +01:00
if err != nil {
2017-10-09 10:16:14 -07:00
result . Data = fmt . Sprintf ( "%v.%v" , model . CurrentVersion , model . GetMillis ( ) )
2017-03-30 02:10:51 +01:00
} else {
[MM-13721] Fix the etag function GetEtagForProfilesNotInTeam (#10360)
* Initial solution for Draft PR
* Reformat tabs to spaces for readability
* Remove my comments and debugging lines for Core Commiter to see changes
more easily
* Remove all comments and show only code changes
* Match indentation spacing of new query to old query to make diff reading
easier for core committer
* Remove everything except what want to show core commiter
* Restrucure query and resulting etag value.
get number of users not in team from SELECT subquery.
etag return values is now of the form
<model.CurrenVersion>.<UpdateAt>.<number_profiles_not_int_team>
* Remove skipped test for solution to https://mattermost.atlassian.net/browse/MM-13721.
Remove comment for failing description of test
store u4 with prepended "u4". Similar to u1, u2, u3. This is easier
for debugging when looking in the database
Added skipped test:
check that etag does not change when a user, not in team 1, is added
to different team. UpdateAt will change, but users in the set does
not
* Remove skipped test for solution to https://mattermost.atlassian.net/browse/MM-13721.
Remove comment for failing description of test
store u4 with prepended "u4". Similar to u1, u2, u3. This is easier
for debugging when looking in the database
Added skipped test:
check that etag does not change when a user, not in team 1, is added
to different team. UpdateAt will change, but users in the set does
not
* remove commented out tests
* Restructure and simplify the SQL query for GetEtagForProfilesNotInTeam.
- Build the query to get all profiles not in a specified team
- select latest UpdateAt Value by getting Max value from UpdateAt field.
- select Number of profiles not in Team from count of the returned Ids
The previous query required building a complex query with multiple
joins and repeated code in a select subquery, and derived table
* Format SQL styling indentation, spaces around equal signs, and new lines
* Add description for skipped test
2019-02-27 20:51:06 -06:00
result . Data = fmt . Sprintf ( "%v.%v" , model . CurrentVersion , etag )
2017-03-30 02:10:51 +01:00
}
2017-10-06 08:12:10 -07:00
} )
2017-03-30 02:10:51 +01:00
}
2018-06-25 14:34:59 +01:00
func ( us SqlUserStore ) ClearAllCustomRoleAssignments ( ) store . StoreChannel {
return store . Do ( func ( result * store . StoreResult ) {
builtInRoles := model . MakeDefaultRoles ( )
lastUserId := strings . Repeat ( "0" , 26 )
2018-09-03 14:08:40 +02:00
for {
2018-06-25 14:34:59 +01:00
var transaction * gorp . Transaction
var err error
if transaction , err = us . GetMaster ( ) . Begin ( ) ; err != nil {
result . Err = model . NewAppError ( "SqlUserStore.ClearAllCustomRoleAssignments" , "store.sql_user.clear_all_custom_role_assignments.open_transaction.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
return
}
2019-02-23 19:44:52 +00:00
defer finalizeTransaction ( transaction )
2018-06-25 14:34:59 +01:00
var users [ ] * model . User
if _ , err := transaction . Select ( & users , "SELECT * from Users WHERE Id > :Id ORDER BY Id LIMIT 1000" , map [ string ] interface { } { "Id" : lastUserId } ) ; err != nil {
result . Err = model . NewAppError ( "SqlUserStore.ClearAllCustomRoleAssignments" , "store.sql_user.clear_all_custom_role_assignments.select.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
return
}
if len ( users ) == 0 {
break
}
for _ , user := range users {
lastUserId = user . Id
var newRoles [ ] string
for _ , role := range strings . Fields ( user . Roles ) {
for name := range builtInRoles {
if name == role {
newRoles = append ( newRoles , role )
break
}
}
}
newRolesString := strings . Join ( newRoles , " " )
if newRolesString != user . Roles {
if _ , err := transaction . Exec ( "UPDATE Users SET Roles = :Roles WHERE Id = :Id" , map [ string ] interface { } { "Roles" : newRolesString , "Id" : user . Id } ) ; err != nil {
result . Err = model . NewAppError ( "SqlUserStore.ClearAllCustomRoleAssignments" , "store.sql_user.clear_all_custom_role_assignments.update.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
return
}
}
}
if err := transaction . Commit ( ) ; err != nil {
result . Err = model . NewAppError ( "SqlUserStore.ClearAllCustomRoleAssignments" , "store.sql_user.clear_all_custom_role_assignments.commit_transaction.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
return
}
}
} )
}
2018-08-08 12:04:36 +02:00
func ( us SqlUserStore ) InferSystemInstallDate ( ) store . StoreChannel {
return store . Do ( func ( result * store . StoreResult ) {
createAt , err := us . GetReplica ( ) . SelectInt ( "SELECT CreateAt FROM Users WHERE CreateAt IS NOT NULL ORDER BY CreateAt ASC LIMIT 1" )
if err != nil {
result . Err = model . NewAppError ( "SqlUserStore.GetSystemInstallDate" , "store.sql_user.get_system_install_date.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
return
}
result . Data = createAt
} )
}
2019-03-19 12:42:08 +00:00
2019-06-26 10:41:45 +02:00
func ( us SqlUserStore ) GetUsersBatchForIndexing ( startTime , endTime int64 , limit int ) ( [ ] * model . UserForIndexing , * model . AppError ) {
var users [ ] * model . User
usersQuery , args , _ := us . usersQuery .
Where ( sq . GtOrEq { "u.CreateAt" : startTime } ) .
Where ( sq . Lt { "u.CreateAt" : endTime } ) .
OrderBy ( "u.CreateAt" ) .
Limit ( uint64 ( limit ) ) .
ToSql ( )
_ , err1 := us . GetSearchReplica ( ) . Select ( & users , usersQuery , args ... )
if err1 != nil {
return nil , model . NewAppError ( "SqlUserStore.GetUsersBatchForIndexing" , "store.sql_user.get_users_batch_for_indexing.get_users.app_error" , nil , err1 . Error ( ) , http . StatusInternalServerError )
}
2019-03-19 12:42:08 +00:00
2019-06-26 10:41:45 +02:00
userIds := [ ] string { }
for _ , user := range users {
userIds = append ( userIds , user . Id )
}
2019-03-19 12:42:08 +00:00
2019-06-26 10:41:45 +02:00
var channelMembers [ ] * model . ChannelMember
channelMembersQuery , args , _ := us . getQueryBuilder ( ) .
Select ( `
2019-05-22 12:31:26 +02:00
cm . ChannelId ,
cm . UserId ,
cm . Roles ,
cm . LastViewedAt ,
cm . MsgCount ,
cm . MentionCount ,
cm . NotifyProps ,
cm . LastUpdateAt ,
cm . SchemeUser ,
cm . SchemeAdmin ,
( cm . SchemeGuest IS NOT NULL AND cm . SchemeGuest ) as SchemeGuest
` ) .
2019-06-26 10:41:45 +02:00
From ( "ChannelMembers cm" ) .
Join ( "Channels c ON cm.ChannelId = c.Id" ) .
Where ( sq . Eq { "c.Type" : "O" , "cm.UserId" : userIds } ) .
ToSql ( )
_ , err2 := us . GetSearchReplica ( ) . Select ( & channelMembers , channelMembersQuery , args ... )
2019-03-19 12:42:08 +00:00
2019-06-26 10:41:45 +02:00
if err2 != nil {
return nil , model . NewAppError ( "SqlUserStore.GetUsersBatchForIndexing" , "store.sql_user.get_users_batch_for_indexing.get_channel_members.app_error" , nil , err2 . Error ( ) , http . StatusInternalServerError )
}
2019-03-19 12:42:08 +00:00
2019-06-26 10:41:45 +02:00
var teamMembers [ ] * model . TeamMember
teamMembersQuery , args , _ := us . getQueryBuilder ( ) .
Select ( "TeamId, UserId, Roles, DeleteAt, (SchemeGuest IS NOT NULL AND SchemeGuest) as SchemeGuest, SchemeUser, SchemeAdmin" ) .
From ( "TeamMembers" ) .
Where ( sq . Eq { "UserId" : userIds , "DeleteAt" : 0 } ) .
ToSql ( )
_ , err3 := us . GetSearchReplica ( ) . Select ( & teamMembers , teamMembersQuery , args ... )
2019-03-19 12:42:08 +00:00
2019-06-26 10:41:45 +02:00
if err3 != nil {
return nil , model . NewAppError ( "SqlUserStore.GetUsersBatchForIndexing" , "store.sql_user.get_users_batch_for_indexing.get_team_members.app_error" , nil , err3 . Error ( ) , http . StatusInternalServerError )
}
2019-03-19 12:42:08 +00:00
2019-06-26 10:41:45 +02:00
userMap := map [ string ] * model . UserForIndexing { }
for _ , user := range users {
userMap [ user . Id ] = & model . UserForIndexing {
Id : user . Id ,
Username : user . Username ,
Nickname : user . Nickname ,
FirstName : user . FirstName ,
LastName : user . LastName ,
CreateAt : user . CreateAt ,
DeleteAt : user . DeleteAt ,
TeamsIds : [ ] string { } ,
ChannelsIds : [ ] string { } ,
2019-03-19 12:42:08 +00:00
}
2019-06-26 10:41:45 +02:00
}
2019-03-19 12:42:08 +00:00
2019-06-26 10:41:45 +02:00
for _ , c := range channelMembers {
if userMap [ c . UserId ] != nil {
userMap [ c . UserId ] . ChannelsIds = append ( userMap [ c . UserId ] . ChannelsIds , c . ChannelId )
2019-03-19 12:42:08 +00:00
}
2019-06-26 10:41:45 +02:00
}
for _ , t := range teamMembers {
if userMap [ t . UserId ] != nil {
userMap [ t . UserId ] . TeamsIds = append ( userMap [ t . UserId ] . TeamsIds , t . TeamId )
}
}
2019-03-19 12:42:08 +00:00
2019-06-26 10:41:45 +02:00
usersForIndexing := [ ] * model . UserForIndexing { }
for _ , user := range userMap {
usersForIndexing = append ( usersForIndexing , user )
}
sort . Slice ( usersForIndexing , func ( i , j int ) bool {
return usersForIndexing [ i ] . CreateAt < usersForIndexing [ j ] . CreateAt
2019-03-19 12:42:08 +00:00
} )
2019-06-26 10:41:45 +02:00
return usersForIndexing , nil
2019-03-19 12:42:08 +00:00
}
2019-04-09 07:09:57 -04:00
func ( us SqlUserStore ) GetTeamGroupUsers ( teamID string ) store . StoreChannel {
return store . Do ( func ( result * store . StoreResult ) {
2019-05-16 10:12:06 +01:00
query := applyTeamGroupConstrainedFilter ( us . usersQuery , teamID )
2019-04-09 07:09:57 -04:00
queryString , args , err := query . ToSql ( )
if err != nil {
result . Err = model . NewAppError ( "SqlUserStore.UsersPermittedToTeam" , "store.sql_user.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
return
}
var users [ ] * model . User
if _ , err := us . GetReplica ( ) . Select ( & users , queryString , args ... ) ; err != nil {
result . Err = model . NewAppError ( "SqlUserStore.UsersPermittedToTeam" , "store.sql_user.get_profiles.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
return
}
for _ , u := range users {
u . Sanitize ( map [ string ] bool { } )
}
result . Data = users
} )
}
2019-06-26 18:42:21 +02:00
func ( us SqlUserStore ) GetChannelGroupUsers ( channelID string ) ( [ ] * model . User , * model . AppError ) {
query := applyChannelGroupConstrainedFilter ( us . usersQuery , channelID )
2019-04-09 07:09:57 -04:00
2019-06-26 18:42:21 +02:00
queryString , args , err := query . ToSql ( )
if err != nil {
return nil , model . NewAppError ( "SqlUserStore.GetChannelGroupUsers" , "store.sql_user.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
}
2019-04-09 07:09:57 -04:00
2019-06-26 18:42:21 +02:00
var users [ ] * model . User
if _ , err := us . GetReplica ( ) . Select ( & users , queryString , args ... ) ; err != nil {
return nil , model . NewAppError ( "SqlUserStore.GetChannelGroupUsers" , "store.sql_user.get_profiles.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
}
2019-04-09 07:09:57 -04:00
2019-06-26 18:42:21 +02:00
for _ , u := range users {
u . Sanitize ( map [ string ] bool { } )
}
2019-04-09 07:09:57 -04:00
2019-06-26 18:42:21 +02:00
return users , nil
2019-04-09 07:09:57 -04:00
}
2019-04-29 16:56:56 +02:00
func applyViewRestrictionsFilter ( query sq . SelectBuilder , restrictions * model . ViewUsersRestrictions , distinct bool ) sq . SelectBuilder {
if restrictions == nil {
return query
}
// If you have no access to teams or channels, return and empty result.
if restrictions . Teams != nil && len ( restrictions . Teams ) == 0 && restrictions . Channels != nil && len ( restrictions . Channels ) == 0 {
return query . Where ( "1 = 0" )
}
teams := make ( [ ] interface { } , len ( restrictions . Teams ) )
for i , v := range restrictions . Teams {
teams [ i ] = v
}
channels := make ( [ ] interface { } , len ( restrictions . Channels ) )
for i , v := range restrictions . Channels {
channels [ i ] = v
}
resultQuery := query
if restrictions . Teams != nil && len ( restrictions . Teams ) > 0 {
resultQuery = resultQuery . Join ( fmt . Sprintf ( "TeamMembers rtm ON ( rtm.UserId = u.Id AND rtm.DeleteAt = 0 AND rtm.TeamId IN (%s))" , sq . Placeholders ( len ( teams ) ) ) , teams ... )
}
if restrictions . Channels != nil && len ( restrictions . Channels ) > 0 {
resultQuery = resultQuery . Join ( fmt . Sprintf ( "ChannelMembers rcm ON ( rcm.UserId = u.Id AND rcm.ChannelId IN (%s))" , sq . Placeholders ( len ( channels ) ) ) , channels ... )
}
if distinct {
return resultQuery . Distinct ( )
}
return resultQuery
}