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