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