github.com/demisto/mattermost-server@v4.9.0-rc3+incompatible/model/user.go (about)

     1  // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
     2  // See License.txt for license information.
     3  
     4  package model
     5  
     6  import (
     7  	"encoding/json"
     8  	"fmt"
     9  	"io"
    10  	"net/http"
    11  	"regexp"
    12  	"strings"
    13  	"unicode/utf8"
    14  
    15  	"golang.org/x/crypto/bcrypt"
    16  )
    17  
    18  const (
    19  	ME                           = "me"
    20  	USER_NOTIFY_ALL              = "all"
    21  	USER_NOTIFY_MENTION          = "mention"
    22  	USER_NOTIFY_NONE             = "none"
    23  	DESKTOP_NOTIFY_PROP          = "desktop"
    24  	DESKTOP_SOUND_NOTIFY_PROP    = "desktop_sound"
    25  	DESKTOP_DURATION_NOTIFY_PROP = "desktop_duration"
    26  	MARK_UNREAD_NOTIFY_PROP      = "mark_unread"
    27  	PUSH_NOTIFY_PROP             = "push"
    28  	PUSH_STATUS_NOTIFY_PROP      = "push_status"
    29  	EMAIL_NOTIFY_PROP            = "email"
    30  	CHANNEL_MENTIONS_NOTIFY_PROP = "channel"
    31  	COMMENTS_NOTIFY_PROP         = "comments"
    32  	MENTION_KEYS_NOTIFY_PROP     = "mention_keys"
    33  	COMMENTS_NOTIFY_NEVER        = "never"
    34  	COMMENTS_NOTIFY_ROOT         = "root"
    35  	COMMENTS_NOTIFY_ANY          = "any"
    36  
    37  	DEFAULT_LOCALE          = "en"
    38  	USER_AUTH_SERVICE_EMAIL = "email"
    39  
    40  	USER_EMAIL_MAX_LENGTH     = 128
    41  	USER_NICKNAME_MAX_RUNES   = 64
    42  	USER_POSITION_MAX_RUNES   = 128
    43  	USER_FIRST_NAME_MAX_RUNES = 64
    44  	USER_LAST_NAME_MAX_RUNES  = 64
    45  	USER_AUTH_DATA_MAX_LENGTH = 128
    46  	USER_NAME_MAX_LENGTH      = 64
    47  	USER_NAME_MIN_LENGTH      = 1
    48  	USER_PASSWORD_MAX_LENGTH  = 72
    49  )
    50  
    51  type User struct {
    52  	Id                 string    `json:"id"`
    53  	CreateAt           int64     `json:"create_at,omitempty"`
    54  	UpdateAt           int64     `json:"update_at,omitempty"`
    55  	DeleteAt           int64     `json:"delete_at"`
    56  	Username           string    `json:"username"`
    57  	Password           string    `json:"password,omitempty"`
    58  	AuthData           *string   `json:"auth_data,omitempty"`
    59  	AuthService        string    `json:"auth_service"`
    60  	Email              string    `json:"email"`
    61  	EmailVerified      bool      `json:"email_verified,omitempty"`
    62  	Nickname           string    `json:"nickname"`
    63  	FirstName          string    `json:"first_name"`
    64  	LastName           string    `json:"last_name"`
    65  	Position           string    `json:"position"`
    66  	Roles              string    `json:"roles"`
    67  	AllowMarketing     bool      `json:"allow_marketing,omitempty"`
    68  	Props              StringMap `json:"props,omitempty"`
    69  	NotifyProps        StringMap `json:"notify_props,omitempty"`
    70  	LastPasswordUpdate int64     `json:"last_password_update,omitempty"`
    71  	LastPictureUpdate  int64     `json:"last_picture_update,omitempty"`
    72  	FailedAttempts     int       `json:"failed_attempts,omitempty"`
    73  	Locale             string    `json:"locale"`
    74  	Timezone           StringMap `json:"timezone"`
    75  	MfaActive          bool      `json:"mfa_active,omitempty"`
    76  	MfaSecret          string    `json:"mfa_secret,omitempty"`
    77  	LastActivityAt     int64     `db:"-" json:"last_activity_at,omitempty"`
    78  }
    79  
    80  type UserPatch struct {
    81  	Username    *string   `json:"username"`
    82  	Nickname    *string   `json:"nickname"`
    83  	FirstName   *string   `json:"first_name"`
    84  	LastName    *string   `json:"last_name"`
    85  	Position    *string   `json:"position"`
    86  	Email       *string   `json:"email"`
    87  	Props       StringMap `json:"props,omitempty"`
    88  	NotifyProps StringMap `json:"notify_props,omitempty"`
    89  	Locale      *string   `json:"locale"`
    90  	Timezone    StringMap `json:"timezone"`
    91  }
    92  
    93  type UserAuth struct {
    94  	Password    string  `json:"password,omitempty"`
    95  	AuthData    *string `json:"auth_data,omitempty"`
    96  	AuthService string  `json:"auth_service,omitempty"`
    97  }
    98  
    99  // IsValid validates the user and returns an error if it isn't configured
   100  // correctly.
   101  func (u *User) IsValid() *AppError {
   102  
   103  	if len(u.Id) != 26 {
   104  		return InvalidUserError("id", "")
   105  	}
   106  
   107  	if u.CreateAt == 0 {
   108  		return InvalidUserError("create_at", u.Id)
   109  	}
   110  
   111  	if u.UpdateAt == 0 {
   112  		return InvalidUserError("update_at", u.Id)
   113  	}
   114  
   115  	if !IsValidUsername(u.Username) {
   116  		return InvalidUserError("username", u.Id)
   117  	}
   118  
   119  	if len(u.Email) > USER_EMAIL_MAX_LENGTH || len(u.Email) == 0 {
   120  		return InvalidUserError("email", u.Id)
   121  	}
   122  
   123  	if utf8.RuneCountInString(u.Nickname) > USER_NICKNAME_MAX_RUNES {
   124  		return InvalidUserError("nickname", u.Id)
   125  	}
   126  
   127  	if utf8.RuneCountInString(u.Position) > USER_POSITION_MAX_RUNES {
   128  		return InvalidUserError("position", u.Id)
   129  	}
   130  
   131  	if utf8.RuneCountInString(u.FirstName) > USER_FIRST_NAME_MAX_RUNES {
   132  		return InvalidUserError("first_name", u.Id)
   133  	}
   134  
   135  	if utf8.RuneCountInString(u.LastName) > USER_LAST_NAME_MAX_RUNES {
   136  		return InvalidUserError("last_name", u.Id)
   137  	}
   138  
   139  	if u.AuthData != nil && len(*u.AuthData) > USER_AUTH_DATA_MAX_LENGTH {
   140  		return InvalidUserError("auth_data", u.Id)
   141  	}
   142  
   143  	if u.AuthData != nil && len(*u.AuthData) > 0 && len(u.AuthService) == 0 {
   144  		return InvalidUserError("auth_data_type", u.Id)
   145  	}
   146  
   147  	if len(u.Password) > 0 && u.AuthData != nil && len(*u.AuthData) > 0 {
   148  		return InvalidUserError("auth_data_pwd", u.Id)
   149  	}
   150  
   151  	if len(u.Password) > USER_PASSWORD_MAX_LENGTH {
   152  		return InvalidUserError("password_limit", u.Id)
   153  	}
   154  
   155  	return nil
   156  }
   157  
   158  func InvalidUserError(fieldName string, userId string) *AppError {
   159  	id := fmt.Sprintf("model.user.is_valid.%s.app_error", fieldName)
   160  	details := ""
   161  	if userId != "" {
   162  		details = "user_id=" + userId
   163  	}
   164  	return NewAppError("User.IsValid", id, nil, details, http.StatusBadRequest)
   165  }
   166  
   167  func NormalizeUsername(username string) string {
   168  	return strings.ToLower(username)
   169  }
   170  
   171  func NormalizeEmail(email string) string {
   172  	return strings.ToLower(email)
   173  }
   174  
   175  // PreSave will set the Id and Username if missing.  It will also fill
   176  // in the CreateAt, UpdateAt times.  It will also hash the password.  It should
   177  // be run before saving the user to the db.
   178  func (u *User) PreSave() {
   179  	if u.Id == "" {
   180  		u.Id = NewId()
   181  	}
   182  
   183  	if u.Username == "" {
   184  		u.Username = NewId()
   185  	}
   186  
   187  	if u.AuthData != nil && *u.AuthData == "" {
   188  		u.AuthData = nil
   189  	}
   190  
   191  	u.Username = NormalizeUsername(u.Username)
   192  	u.Email = NormalizeEmail(u.Email)
   193  
   194  	u.CreateAt = GetMillis()
   195  	u.UpdateAt = u.CreateAt
   196  
   197  	u.LastPasswordUpdate = u.CreateAt
   198  
   199  	u.MfaActive = false
   200  
   201  	if u.Locale == "" {
   202  		u.Locale = DEFAULT_LOCALE
   203  	}
   204  
   205  	if u.Props == nil {
   206  		u.Props = make(map[string]string)
   207  	}
   208  
   209  	if u.NotifyProps == nil || len(u.NotifyProps) == 0 {
   210  		u.SetDefaultNotifications()
   211  	}
   212  
   213  	if u.Timezone == nil {
   214  		u.Timezone = DefaultUserTimezone()
   215  	}
   216  
   217  	if len(u.Password) > 0 {
   218  		u.Password = HashPassword(u.Password)
   219  	}
   220  }
   221  
   222  // PreUpdate should be run before updating the user in the db.
   223  func (u *User) PreUpdate() {
   224  	u.Username = NormalizeUsername(u.Username)
   225  	u.Email = NormalizeEmail(u.Email)
   226  	u.UpdateAt = GetMillis()
   227  
   228  	if u.AuthData != nil && *u.AuthData == "" {
   229  		u.AuthData = nil
   230  	}
   231  
   232  	if u.NotifyProps == nil || len(u.NotifyProps) == 0 {
   233  		u.SetDefaultNotifications()
   234  	} else if _, ok := u.NotifyProps["mention_keys"]; ok {
   235  		// Remove any blank mention keys
   236  		splitKeys := strings.Split(u.NotifyProps["mention_keys"], ",")
   237  		goodKeys := []string{}
   238  		for _, key := range splitKeys {
   239  			if len(key) > 0 {
   240  				goodKeys = append(goodKeys, strings.ToLower(key))
   241  			}
   242  		}
   243  		u.NotifyProps["mention_keys"] = strings.Join(goodKeys, ",")
   244  	}
   245  }
   246  
   247  func (u *User) SetDefaultNotifications() {
   248  	u.NotifyProps = make(map[string]string)
   249  	u.NotifyProps["email"] = "true"
   250  	u.NotifyProps["push"] = USER_NOTIFY_MENTION
   251  	u.NotifyProps["desktop"] = USER_NOTIFY_MENTION
   252  	u.NotifyProps["desktop_sound"] = "true"
   253  	u.NotifyProps["mention_keys"] = u.Username + ",@" + u.Username
   254  	u.NotifyProps["channel"] = "true"
   255  	u.NotifyProps["push_status"] = STATUS_AWAY
   256  	u.NotifyProps["comments"] = "never"
   257  	u.NotifyProps["first_name"] = "false"
   258  }
   259  
   260  func (user *User) UpdateMentionKeysFromUsername(oldUsername string) {
   261  	nonUsernameKeys := []string{}
   262  	splitKeys := strings.Split(user.NotifyProps["mention_keys"], ",")
   263  	for _, key := range splitKeys {
   264  		if key != oldUsername && key != "@"+oldUsername {
   265  			nonUsernameKeys = append(nonUsernameKeys, key)
   266  		}
   267  	}
   268  
   269  	user.NotifyProps["mention_keys"] = user.Username + ",@" + user.Username
   270  	if len(nonUsernameKeys) > 0 {
   271  		user.NotifyProps["mention_keys"] += "," + strings.Join(nonUsernameKeys, ",")
   272  	}
   273  }
   274  
   275  func (u *User) Patch(patch *UserPatch) {
   276  	if patch.Username != nil {
   277  		u.Username = *patch.Username
   278  	}
   279  
   280  	if patch.Nickname != nil {
   281  		u.Nickname = *patch.Nickname
   282  	}
   283  
   284  	if patch.FirstName != nil {
   285  		u.FirstName = *patch.FirstName
   286  	}
   287  
   288  	if patch.LastName != nil {
   289  		u.LastName = *patch.LastName
   290  	}
   291  
   292  	if patch.Position != nil {
   293  		u.Position = *patch.Position
   294  	}
   295  
   296  	if patch.Email != nil {
   297  		u.Email = *patch.Email
   298  	}
   299  
   300  	if patch.Props != nil {
   301  		u.Props = patch.Props
   302  	}
   303  
   304  	if patch.NotifyProps != nil {
   305  		u.NotifyProps = patch.NotifyProps
   306  	}
   307  
   308  	if patch.Locale != nil {
   309  		u.Locale = *patch.Locale
   310  	}
   311  
   312  	if patch.Timezone != nil {
   313  		u.Timezone = patch.Timezone
   314  	}
   315  }
   316  
   317  // ToJson convert a User to a json string
   318  func (u *User) ToJson() string {
   319  	b, _ := json.Marshal(u)
   320  	return string(b)
   321  }
   322  
   323  func (u *UserPatch) ToJson() string {
   324  	b, _ := json.Marshal(u)
   325  	return string(b)
   326  }
   327  
   328  func (u *UserAuth) ToJson() string {
   329  	b, _ := json.Marshal(u)
   330  	return string(b)
   331  }
   332  
   333  // Generate a valid strong etag so the browser can cache the results
   334  func (u *User) Etag(showFullName, showEmail bool) string {
   335  	return Etag(u.Id, u.UpdateAt, showFullName, showEmail)
   336  }
   337  
   338  // Remove any private data from the user object
   339  func (u *User) Sanitize(options map[string]bool) {
   340  	u.Password = ""
   341  	u.AuthData = NewString("")
   342  	u.MfaSecret = ""
   343  
   344  	if len(options) != 0 && !options["email"] {
   345  		u.Email = ""
   346  	}
   347  	if len(options) != 0 && !options["fullname"] {
   348  		u.FirstName = ""
   349  		u.LastName = ""
   350  	}
   351  	if len(options) != 0 && !options["passwordupdate"] {
   352  		u.LastPasswordUpdate = 0
   353  	}
   354  	if len(options) != 0 && !options["authservice"] {
   355  		u.AuthService = ""
   356  	}
   357  }
   358  
   359  func (u *User) ClearNonProfileFields() {
   360  	u.Password = ""
   361  	u.AuthData = NewString("")
   362  	u.MfaSecret = ""
   363  	u.EmailVerified = false
   364  	u.AllowMarketing = false
   365  	u.NotifyProps = StringMap{}
   366  	u.LastPasswordUpdate = 0
   367  	u.FailedAttempts = 0
   368  }
   369  
   370  func (u *User) SanitizeProfile(options map[string]bool) {
   371  	u.ClearNonProfileFields()
   372  
   373  	u.Sanitize(options)
   374  }
   375  
   376  func (u *User) MakeNonNil() {
   377  	if u.Props == nil {
   378  		u.Props = make(map[string]string)
   379  	}
   380  
   381  	if u.NotifyProps == nil {
   382  		u.NotifyProps = make(map[string]string)
   383  	}
   384  }
   385  
   386  func (u *User) AddNotifyProp(key string, value string) {
   387  	u.MakeNonNil()
   388  
   389  	u.NotifyProps[key] = value
   390  }
   391  
   392  func (u *User) GetFullName() string {
   393  	if u.FirstName != "" && u.LastName != "" {
   394  		return u.FirstName + " " + u.LastName
   395  	} else if u.FirstName != "" {
   396  		return u.FirstName
   397  	} else if u.LastName != "" {
   398  		return u.LastName
   399  	} else {
   400  		return ""
   401  	}
   402  }
   403  
   404  func (u *User) GetDisplayName(nameFormat string) string {
   405  	displayName := u.Username
   406  
   407  	if nameFormat == SHOW_NICKNAME_FULLNAME {
   408  		if u.Nickname != "" {
   409  			displayName = u.Nickname
   410  		} else if fullName := u.GetFullName(); fullName != "" {
   411  			displayName = fullName
   412  		}
   413  	} else if nameFormat == SHOW_FULLNAME {
   414  		if fullName := u.GetFullName(); fullName != "" {
   415  			displayName = fullName
   416  		}
   417  	}
   418  
   419  	return displayName
   420  }
   421  
   422  func (u *User) GetRoles() []string {
   423  	return strings.Fields(u.Roles)
   424  }
   425  
   426  func (u *User) GetRawRoles() string {
   427  	return u.Roles
   428  }
   429  
   430  func IsValidUserRoles(userRoles string) bool {
   431  
   432  	roles := strings.Fields(userRoles)
   433  
   434  	for _, r := range roles {
   435  		if !IsValidRoleName(r) {
   436  			return false
   437  		}
   438  	}
   439  
   440  	// Exclude just the system_admin role explicitly to prevent mistakes
   441  	if len(roles) == 1 && roles[0] == "system_admin" {
   442  		return false
   443  	}
   444  
   445  	return true
   446  }
   447  
   448  // Make sure you acually want to use this function. In context.go there are functions to check permissions
   449  // This function should not be used to check permissions.
   450  func (u *User) IsInRole(inRole string) bool {
   451  	return IsInRole(u.Roles, inRole)
   452  }
   453  
   454  // Make sure you acually want to use this function. In context.go there are functions to check permissions
   455  // This function should not be used to check permissions.
   456  func IsInRole(userRoles string, inRole string) bool {
   457  	roles := strings.Split(userRoles, " ")
   458  
   459  	for _, r := range roles {
   460  		if r == inRole {
   461  			return true
   462  		}
   463  	}
   464  
   465  	return false
   466  }
   467  
   468  func (u *User) IsSSOUser() bool {
   469  	return u.AuthService != "" && u.AuthService != USER_AUTH_SERVICE_EMAIL
   470  }
   471  
   472  func (u *User) IsOAuthUser() bool {
   473  	return u.AuthService == USER_AUTH_SERVICE_GITLAB
   474  }
   475  
   476  func (u *User) IsLDAPUser() bool {
   477  	return u.AuthService == USER_AUTH_SERVICE_LDAP
   478  }
   479  
   480  func (u *User) IsSAMLUser() bool {
   481  	return u.AuthService == USER_AUTH_SERVICE_SAML
   482  }
   483  
   484  // UserFromJson will decode the input and return a User
   485  func UserFromJson(data io.Reader) *User {
   486  	var user *User
   487  	json.NewDecoder(data).Decode(&user)
   488  	return user
   489  }
   490  
   491  func UserPatchFromJson(data io.Reader) *UserPatch {
   492  	var user *UserPatch
   493  	json.NewDecoder(data).Decode(&user)
   494  	return user
   495  }
   496  
   497  func UserAuthFromJson(data io.Reader) *UserAuth {
   498  	var user *UserAuth
   499  	json.NewDecoder(data).Decode(&user)
   500  	return user
   501  }
   502  
   503  func UserMapToJson(u map[string]*User) string {
   504  	b, _ := json.Marshal(u)
   505  	return string(b)
   506  }
   507  
   508  func UserMapFromJson(data io.Reader) map[string]*User {
   509  	var users map[string]*User
   510  	json.NewDecoder(data).Decode(&users)
   511  	return users
   512  }
   513  
   514  func UserListToJson(u []*User) string {
   515  	b, _ := json.Marshal(u)
   516  	return string(b)
   517  }
   518  
   519  func UserListFromJson(data io.Reader) []*User {
   520  	var users []*User
   521  	json.NewDecoder(data).Decode(&users)
   522  	return users
   523  }
   524  
   525  // HashPassword generates a hash using the bcrypt.GenerateFromPassword
   526  func HashPassword(password string) string {
   527  	hash, err := bcrypt.GenerateFromPassword([]byte(password), 10)
   528  	if err != nil {
   529  		panic(err)
   530  	}
   531  
   532  	return string(hash)
   533  }
   534  
   535  // ComparePassword compares the hash
   536  func ComparePassword(hash string, password string) bool {
   537  
   538  	if len(password) == 0 || len(hash) == 0 {
   539  		return false
   540  	}
   541  
   542  	err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
   543  	return err == nil
   544  }
   545  
   546  var validUsernameChars = regexp.MustCompile(`^[a-z0-9\.\-_]+$`)
   547  
   548  var restrictedUsernames = []string{
   549  	"all",
   550  	"channel",
   551  	"matterbot",
   552  }
   553  
   554  func IsValidUsername(s string) bool {
   555  	if len(s) < USER_NAME_MIN_LENGTH || len(s) > USER_NAME_MAX_LENGTH {
   556  		return false
   557  	}
   558  
   559  	if !validUsernameChars.MatchString(s) {
   560  		return false
   561  	}
   562  
   563  	for _, restrictedUsername := range restrictedUsernames {
   564  		if s == restrictedUsername {
   565  			return false
   566  		}
   567  	}
   568  
   569  	return true
   570  }
   571  
   572  func CleanUsername(s string) string {
   573  	s = NormalizeUsername(strings.Replace(s, " ", "-", -1))
   574  
   575  	for _, value := range reservedName {
   576  		if s == value {
   577  			s = strings.Replace(s, value, "", -1)
   578  		}
   579  	}
   580  
   581  	s = strings.TrimSpace(s)
   582  
   583  	for _, c := range s {
   584  		char := fmt.Sprintf("%c", c)
   585  		if !validUsernameChars.MatchString(char) {
   586  			s = strings.Replace(s, char, "-", -1)
   587  		}
   588  	}
   589  
   590  	s = strings.Trim(s, "-")
   591  
   592  	if !IsValidUsername(s) {
   593  		s = "a" + NewId()
   594  	}
   595  
   596  	return s
   597  }
   598  
   599  func IsValidUserNotifyLevel(notifyLevel string) bool {
   600  	return notifyLevel == CHANNEL_NOTIFY_ALL ||
   601  		notifyLevel == CHANNEL_NOTIFY_MENTION ||
   602  		notifyLevel == CHANNEL_NOTIFY_NONE
   603  }
   604  
   605  func IsValidPushStatusNotifyLevel(notifyLevel string) bool {
   606  	return notifyLevel == STATUS_ONLINE ||
   607  		notifyLevel == STATUS_AWAY ||
   608  		notifyLevel == STATUS_OFFLINE
   609  }
   610  
   611  func IsValidCommentsNotifyLevel(notifyLevel string) bool {
   612  	return notifyLevel == COMMENTS_NOTIFY_ANY ||
   613  		notifyLevel == COMMENTS_NOTIFY_ROOT ||
   614  		notifyLevel == COMMENTS_NOTIFY_NEVER
   615  }