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