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