// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. package users import ( "context" "encoding/base64" "fmt" "github.com/mattermost/mattermost/server/public/model" "github.com/mattermost/mattermost/server/public/shared/i18n" "github.com/mattermost/mattermost/server/public/shared/mlog" "github.com/mattermost/mattermost/server/public/shared/request" "github.com/mattermost/mattermost/server/v8/channels/store" "github.com/mattermost/mattermost/server/v8/platform/shared/mfa" "github.com/pkg/errors" ) type UserCreateOptions struct { Guest bool FromImport bool } // CreateUser creates a user func (us *UserService) CreateUser(rctx request.CTX, user *model.User, opts UserCreateOptions) (*model.User, error) { if opts.FromImport { return us.createUser(rctx, user) } user.Roles = model.SystemUserRoleId if opts.Guest { user.Roles = model.SystemGuestRoleId } if !user.IsLDAPUser() && !user.IsSAMLUser() && !user.IsGuest() && !CheckUserDomain(user, *us.config().TeamSettings.RestrictCreationToDomains) { return nil, AcceptedDomainError } if !user.IsLDAPUser() && !user.IsSAMLUser() && user.IsGuest() && !CheckUserDomain(user, *us.config().GuestAccountsSettings.RestrictCreationToDomains) { return nil, AcceptedDomainError } // Below is a special case where the first user in the entire // system is granted the system_admin role if ok, err := us.store.IsEmpty(true); err != nil { return nil, errors.Wrap(UserStoreIsEmptyError, err.Error()) } else if ok { user.Roles = model.SystemAdminRoleId + " " + model.SystemUserRoleId } if _, ok := i18n.GetSupportedLocales()[user.Locale]; !ok { user.Locale = *us.config().LocalizationSettings.DefaultClientLocale } return us.createUser(rctx, user) } func (us *UserService) createUser(rctx request.CTX, user *model.User) (*model.User, error) { user.MakeNonNil() if err := us.isPasswordValid(user.Password); user.AuthService == "" && err != nil { return nil, err } ruser, err := us.store.Save(rctx, user) if err != nil { return nil, err } if user.EmailVerified { if err := us.verifyUserEmail(ruser.Id, user.Email); err != nil { mlog.Warn("Failed to set email verified", mlog.Err(err)) } } // Determine whether to send the created user a welcome email ruser.DisableWelcomeEmail = user.DisableWelcomeEmail ruser.Sanitize(map[string]bool{}) return ruser, nil } func (us *UserService) verifyUserEmail(userID, email string) error { if _, err := us.store.VerifyEmail(userID, email); err != nil { return VerifyUserError } return nil } func (us *UserService) GetUser(userID string) (*model.User, error) { return us.store.Get(context.Background(), userID) } func (us *UserService) GetUsers(rctx request.CTX, userIDs []string) ([]*model.User, error) { return us.store.GetMany(rctx, userIDs) } func (us *UserService) GetUserByUsername(username string) (*model.User, error) { return us.store.GetByUsername(username) } func (us *UserService) GetUserByEmail(email string) (*model.User, error) { return us.store.GetByEmail(email) } func (us *UserService) GetUserByRemoteID(remoteID string) (*model.User, error) { return us.store.GetByRemoteID(remoteID) } func (us *UserService) GetUserByAuth(authData *string, authService string) (*model.User, error) { return us.store.GetByAuth(authData, authService) } func (us *UserService) GetUsersFromProfiles(options *model.UserGetOptions) ([]*model.User, error) { return us.store.GetAllProfiles(options) } func (us *UserService) GetUsersByUsernames(usernames []string, options *model.UserGetOptions) ([]*model.User, error) { return us.store.GetProfilesByUsernames(usernames, options.ViewRestrictions) } func (us *UserService) GetUsersPage(options *model.UserGetOptions, asAdmin bool) ([]*model.User, error) { users, err := us.GetUsersFromProfiles(options) if err != nil { return nil, err } return us.sanitizeProfiles(users, asAdmin), nil } func (us *UserService) GetUsersEtag(restrictionsHash string) string { return fmt.Sprintf("%v.%v.%v.%v", us.store.GetEtagForAllProfiles(), us.config().PrivacySettings.ShowFullName, us.config().PrivacySettings.ShowEmailAddress, restrictionsHash) } func (us *UserService) GetUsersByIds(rctx request.CTX, userIDs []string, options *store.UserGetByIdsOpts) ([]*model.User, error) { allowFromCache := options.ViewRestrictions == nil users, err := us.store.GetProfileByIds(rctx, userIDs, options, allowFromCache) if err != nil { return nil, err } return us.sanitizeProfiles(users, options.IsAdmin), nil } func (us *UserService) GetUsersInTeam(options *model.UserGetOptions) ([]*model.User, error) { return us.store.GetProfiles(options) } func (us *UserService) GetUsersNotInTeam(teamID string, groupConstrained bool, offset int, limit int, viewRestrictions *model.ViewUsersRestrictions) ([]*model.User, error) { return us.store.GetProfilesNotInTeam(teamID, groupConstrained, offset, limit, viewRestrictions) } func (us *UserService) GetUsersInTeamPage(options *model.UserGetOptions, asAdmin bool) ([]*model.User, error) { users, err := us.GetUsersInTeam(options) if err != nil { return nil, err } return us.sanitizeProfiles(users, asAdmin), nil } func (us *UserService) GetUsersNotInTeamPage(teamID string, groupConstrained bool, page int, perPage int, asAdmin bool, viewRestrictions *model.ViewUsersRestrictions) ([]*model.User, error) { users, err := us.GetUsersNotInTeam(teamID, groupConstrained, page*perPage, perPage, viewRestrictions) if err != nil { return nil, err } return us.sanitizeProfiles(users, asAdmin), nil } func (us *UserService) GetUsersInTeamEtag(teamID string, restrictionsHash string) string { return fmt.Sprintf("%v.%v.%v.%v", us.store.GetEtagForProfiles(teamID), us.config().PrivacySettings.ShowFullName, us.config().PrivacySettings.ShowEmailAddress, restrictionsHash) } func (us *UserService) GetUsersNotInTeamEtag(teamID string, restrictionsHash string) string { return fmt.Sprintf("%v.%v.%v.%v", us.store.GetEtagForProfilesNotInTeam(teamID), us.config().PrivacySettings.ShowFullName, us.config().PrivacySettings.ShowEmailAddress, restrictionsHash) } func (us *UserService) GetUsersWithoutTeamPage(options *model.UserGetOptions, asAdmin bool) ([]*model.User, error) { users, err := us.GetUsersWithoutTeam(options) if err != nil { return nil, err } return us.sanitizeProfiles(users, asAdmin), nil } func (us *UserService) GetUsersWithoutTeam(options *model.UserGetOptions) ([]*model.User, error) { users, err := us.store.GetProfilesWithoutTeam(options) if err != nil { return nil, err } return users, nil } func (us *UserService) UpdateUser(rctx request.CTX, user *model.User, allowRoleUpdate bool) (*model.UserUpdate, error) { return us.store.Update(rctx, user, allowRoleUpdate) } func (us *UserService) UpdateUserNotifyProps(userID string, props map[string]string) error { return us.store.UpdateNotifyProps(userID, props) } func (us *UserService) DeactivateAllGuests() ([]string, error) { users, err := us.store.DeactivateGuests() if err != nil { return nil, err } return users, nil } func (us *UserService) InvalidateCacheForUser(userID string) { us.store.InvalidateProfilesInChannelCacheByUser(userID) us.store.InvalidateProfileCacheForUser(userID) if us.cluster != nil { msg := &model.ClusterMessage{ Event: model.ClusterEventInvalidateCacheForUser, SendType: model.ClusterSendBestEffort, Data: []byte(userID), } us.cluster.SendClusterMessage(msg) } } func (us *UserService) GenerateMfaSecret(user *model.User) (*model.MfaSecret, error) { secret, img, err := mfa.New(us.store).GenerateSecret(*us.config().ServiceSettings.SiteURL, user.Email, user.Id) if err != nil { return nil, err } // Make sure the old secret is not cached on any cluster nodes. us.InvalidateCacheForUser(user.Id) mfaSecret := &model.MfaSecret{Secret: secret, QRCode: base64.StdEncoding.EncodeToString(img)} return mfaSecret, nil } func (us *UserService) ActivateMfa(user *model.User, token string) error { return mfa.New(us.store).Activate(user.MfaSecret, user.Id, token) } func (us *UserService) DeactivateMfa(user *model.User) error { return mfa.New(us.store).Deactivate(user.Id) } func (us *UserService) PromoteGuestToUser(user *model.User) error { return us.store.PromoteGuestToUser(user.Id) } func (us *UserService) DemoteUserToGuest(user *model.User) (*model.User, error) { return us.store.DemoteUserToGuest(user.Id) }