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 ©User 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 }