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