github.com/greenpau/go-authcrunch@v1.0.50/pkg/identity/user.go (about) 1 // Copyright 2022 Paul Greenberg greenpau@outlook.com 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package identity 16 17 import ( 18 "github.com/greenpau/go-authcrunch/pkg/errors" 19 "github.com/greenpau/go-authcrunch/pkg/requests" 20 "strings" 21 "time" 22 ) 23 24 // UserMetadata is metadata associated with a user. 25 type UserMetadata struct { 26 ID string `json:"id,omitempty" xml:"id,omitempty" yaml:"id,omitempty"` 27 Enabled bool `json:"enabled,omitempty" xml:"enabled,omitempty" yaml:"enabled,omitempty"` 28 Username string `json:"username,omitempty" xml:"username,omitempty" yaml:"username,omitempty"` 29 Title string `json:"title,omitempty" xml:"title,omitempty" yaml:"title,omitempty"` 30 Name string `json:"name,omitempty" xml:"name,omitempty" yaml:"name,omitempty"` 31 Email string `json:"email,omitempty" xml:"email,omitempty" yaml:"email,omitempty"` 32 Created time.Time `json:"created,omitempty" xml:"created,omitempty" yaml:"created,omitempty"` 33 LastModified time.Time `json:"last_modified,omitempty" xml:"last_modified,omitempty" yaml:"last_modified,omitempty"` 34 Revision int `json:"revision,omitempty" xml:"revision,omitempty" yaml:"revision,omitempty"` 35 Avatar string `json:"avatar,omitempty" xml:"avatar,omitempty" yaml:"avatar,omitempty"` 36 } 37 38 // UserMetadataBundle is a collection of public users. 39 type UserMetadataBundle struct { 40 users []*UserMetadata 41 size int 42 } 43 44 // User is a user identity. 45 type User struct { 46 ID string `json:"id,omitempty" xml:"id,omitempty" yaml:"id,omitempty"` 47 Enabled bool `json:"enabled,omitempty" xml:"enabled,omitempty" yaml:"enabled,omitempty"` 48 Human bool `json:"human,omitempty" xml:"human,omitempty" yaml:"human,omitempty"` 49 Username string `json:"username,omitempty" xml:"username,omitempty" yaml:"username,omitempty"` 50 Title string `json:"title,omitempty" xml:"title,omitempty" yaml:"title,omitempty"` 51 Name *Name `json:"name,omitempty" xml:"name,omitempty" yaml:"name,omitempty"` 52 Organization *Organization `json:"organization,omitempty" xml:"organization,omitempty" yaml:"organization,omitempty"` 53 Names []*Name `json:"names,omitempty" xml:"names,omitempty" yaml:"names,omitempty"` 54 Organizations []*Organization `json:"organizations,omitempty" xml:"organizations,omitempty" yaml:"organizations,omitempty"` 55 StreetAddress []*Location `json:"street_address,omitempty" xml:"street_address,omitempty" yaml:"street_address,omitempty"` 56 EmailAddress *EmailAddress `json:"email_address,omitempty" xml:"email_address,omitempty" yaml:"email_address,omitempty"` 57 EmailAddresses []*EmailAddress `json:"email_addresses,omitempty" xml:"email_addresses,omitempty" yaml:"email_addresses,omitempty"` 58 Passwords []*Password `json:"passwords,omitempty" xml:"passwords,omitempty" yaml:"passwords,omitempty"` 59 PublicKeys []*PublicKey `json:"public_keys,omitempty" xml:"public_keys,omitempty" yaml:"public_keys,omitempty"` 60 APIKeys []*APIKey `json:"api_keys,omitempty" xml:"api_keys,omitempty" yaml:"api_keys,omitempty"` 61 MfaTokens []*MfaToken `json:"mfa_tokens,omitempty" xml:"mfa_tokens,omitempty" yaml:"mfa_tokens,omitempty"` 62 Lockout *LockoutState `json:"lockout,omitempty" xml:"lockout,omitempty" yaml:"lockout,omitempty"` 63 Avatar *Image `json:"avatar,omitempty" xml:"avatar,omitempty" yaml:"avatar,omitempty"` 64 Created time.Time `json:"created,omitempty" xml:"created,omitempty" yaml:"created,omitempty"` 65 LastModified time.Time `json:"last_modified,omitempty" xml:"last_modified,omitempty" yaml:"last_modified,omitempty"` 66 Revision int `json:"revision,omitempty" xml:"revision,omitempty" yaml:"revision,omitempty"` 67 Roles []*Role `json:"roles,omitempty" xml:"roles,omitempty" yaml:"roles,omitempty"` 68 Registration *Registration `json:"registration,omitempty" xml:"registration,omitempty" yaml:"registration,omitempty"` 69 rolesRef map[string]interface{} 70 } 71 72 // NewUserMetadataBundle returns an instance of UserMetadataBundle. 73 func NewUserMetadataBundle() *UserMetadataBundle { 74 return &UserMetadataBundle{ 75 users: []*UserMetadata{}, 76 } 77 } 78 79 // Add adds UserMetadata to UserMetadataBundle. 80 func (b *UserMetadataBundle) Add(k *UserMetadata) { 81 b.users = append(b.users, k) 82 b.size++ 83 } 84 85 // Get returns UserMetadata instances of the UserMetadataBundle. 86 func (b *UserMetadataBundle) Get() []*UserMetadata { 87 return b.users 88 } 89 90 // Size returns the number of UserMetadata instances in UserMetadataBundle. 91 func (b *UserMetadataBundle) Size() int { 92 return b.size 93 } 94 95 // NewUser returns an instance of User. 96 func NewUser(s string) *User { 97 user := &User{ 98 ID: NewID(), 99 Username: s, 100 Created: time.Now().UTC(), 101 LastModified: time.Now().UTC(), 102 } 103 return user 104 } 105 106 // NewUserWithRoles returns User with additional fields. 107 func NewUserWithRoles(username, password, email, fullName string, roles []string) (*User, error) { 108 user := NewUser(username) 109 if err := user.AddPassword(password, 0); err != nil { 110 return nil, err 111 } 112 if err := user.AddEmailAddress(email); err != nil { 113 return nil, err 114 } 115 if err := user.AddRoles(roles); err != nil { 116 return nil, err 117 } 118 fullName = strings.TrimSpace(fullName) 119 if fullName != "" { 120 name, err := ParseName(fullName) 121 if err != nil { 122 return nil, err 123 } 124 err = user.AddName(name) 125 if err != nil { 126 return nil, err 127 } 128 } 129 if err := user.Valid(); err != nil { 130 return nil, err 131 } 132 user.Revision = 0 133 return user, nil 134 } 135 136 // Valid returns true if a user conforms to a standard. 137 func (user *User) Valid() error { 138 if len(user.ID) != 36 { 139 return errors.ErrUserIDInvalidLength.WithArgs(len(user.ID)) 140 } 141 if user.Username == "" { 142 return errors.ErrUsernameEmpty 143 } 144 if len(user.Passwords) < 1 { 145 return errors.ErrUserPasswordNotFound 146 } 147 return nil 148 } 149 150 // AddPassword returns creates and adds password for a user identity. 151 func (user *User) AddPassword(s string, keepVersions int) error { 152 var passwords []*Password 153 password, err := NewPassword(s) 154 if err != nil { 155 return err 156 } 157 158 // Check if the existing password is the same as the one provided. 159 if len(user.Passwords) > 0 { 160 if user.Passwords[0].Hash == password.Hash { 161 return nil 162 } 163 } 164 165 if keepVersions < 1 { 166 keepVersions = 9 167 } 168 passwords = append(passwords, password) 169 if len(user.Passwords) > 0 { 170 for i, p := range user.Passwords { 171 if !p.Disabled { 172 p.Disable() 173 } 174 passwords = append(passwords, p) 175 if i > keepVersions { 176 break 177 } 178 } 179 } 180 user.Passwords = passwords 181 user.Revise() 182 return nil 183 } 184 185 // AddEmailAddress returns creates and adds password for a user identity. 186 func (user *User) AddEmailAddress(s string) error { 187 email, err := NewEmailAddress(s) 188 if err != nil { 189 return err 190 } 191 if len(user.EmailAddresses) == 0 { 192 user.EmailAddress = email 193 user.EmailAddresses = append(user.EmailAddresses, email) 194 user.Revise() 195 return nil 196 } 197 for _, e := range user.EmailAddresses { 198 if email.Address == e.Address { 199 return nil 200 } 201 } 202 user.EmailAddresses = append(user.EmailAddresses, email) 203 user.Revise() 204 return nil 205 } 206 207 // HasEmailAddresses checks whether a user has email address. 208 func (user *User) HasEmailAddresses() bool { 209 if len(user.EmailAddresses) == 0 { 210 return false 211 } 212 return true 213 } 214 215 // HasRoles checks whether a user has a role. 216 func (user *User) HasRoles() bool { 217 if len(user.Roles) == 0 { 218 return false 219 } 220 return true 221 } 222 223 // HasRole checks whether a user has a specific role. 224 func (user *User) HasRole(s string) bool { 225 if len(user.Roles) == 0 { 226 return false 227 } 228 role, err := NewRole(s) 229 if err != nil { 230 return false 231 } 232 233 for _, r := range user.Roles { 234 if (r.Name == role.Name) && (r.Organization == role.Organization) { 235 return true 236 } 237 } 238 return false 239 } 240 241 // AddRoles adds roles to a user identity. 242 func (user *User) AddRoles(roles []string) error { 243 for _, role := range roles { 244 if err := user.AddRole(role); err != nil { 245 return err 246 } 247 } 248 return nil 249 } 250 251 // AddRole adds a role to a user identity. 252 func (user *User) AddRole(s string) error { 253 role, err := NewRole(s) 254 if err != nil { 255 return err 256 } 257 if len(user.Roles) == 0 { 258 user.Roles = append(user.Roles, role) 259 user.Revise() 260 return nil 261 } 262 for _, r := range user.Roles { 263 if (r.Name == role.Name) && (r.Organization == role.Organization) { 264 return nil 265 } 266 } 267 user.Roles = append(user.Roles, role) 268 user.Revise() 269 return nil 270 } 271 272 // VerifyPassword verifies provided password matches to the one in the database. 273 func (user *User) VerifyPassword(s string) error { 274 if len(user.Passwords) == 0 { 275 return errors.ErrUserPasswordNotFound 276 } 277 for _, p := range user.Passwords { 278 if p.Disabled || p.Expired { 279 continue 280 } 281 if p.Match(s) { 282 return nil 283 } 284 } 285 return errors.ErrUserPasswordInvalid 286 } 287 288 // VerifyWebAuthnRequest authenticated WebAuthn requests. 289 func (user *User) VerifyWebAuthnRequest(r *requests.Request) error { 290 req, err := unpackWebAuthnRequest(r.WebAuthn.Request) 291 if err != nil { 292 return err 293 } 294 for _, token := range user.MfaTokens { 295 if token.Disabled { 296 continue 297 } 298 if token.Type != "u2f" { 299 continue 300 } 301 if _, exists := token.Parameters["u2f_id"]; !exists { 302 continue 303 } 304 if req.ID != token.Parameters["u2f_id"] { 305 continue 306 } 307 resp, err := token.WebAuthnRequest(r.WebAuthn.Request) 308 if err != nil { 309 return errors.ErrWebAuthnVerifyRequest 310 } 311 if resp == nil { 312 return errors.ErrWebAuthnVerifyRequest 313 } 314 if resp.ClientData.Challenge != r.WebAuthn.Challenge { 315 return errors.ErrWebAuthnVerifyRequest 316 } 317 return nil 318 } 319 return errors.ErrWebAuthnVerifyRequest 320 } 321 322 // GetMailClaim returns primary email address. 323 func (user *User) GetMailClaim() string { 324 if len(user.EmailAddresses) == 0 { 325 return "" 326 } 327 for _, mail := range user.EmailAddresses { 328 if mail.Primary() { 329 return mail.Address 330 } 331 } 332 return user.EmailAddresses[0].Address 333 } 334 335 // GetNameClaim returns name field of a claim. 336 func (user *User) GetNameClaim() string { 337 if user.Name == nil { 338 return "" 339 } 340 if name := user.Name.GetNameClaim(); name != "" { 341 return name 342 } 343 return "" 344 } 345 346 // GetRolesClaim returns name field of a claim. 347 func (user *User) GetRolesClaim() []string { 348 var roles []string 349 if len(user.Roles) == 0 { 350 return roles 351 } 352 for _, role := range user.Roles { 353 roles = append(roles, role.String()) 354 } 355 return roles 356 } 357 358 // GetFullName returns the primary full name for a user. 359 func (user *User) GetFullName() string { 360 if user.Name == nil { 361 return "" 362 } 363 return user.Name.GetFullName() 364 } 365 366 // AddName adds Name for a user identity. 367 func (user *User) AddName(name *Name) error { 368 if len(user.Names) == 0 { 369 user.Name = name 370 user.Names = append(user.Names, name) 371 return nil 372 } 373 for _, n := range user.Names { 374 if name.GetFullName() == n.GetFullName() { 375 return nil 376 } 377 } 378 user.Names = append(user.Names, name) 379 user.Revise() 380 return nil 381 } 382 383 // AddPublicKey adds public key, e.g. GPG or SSH, to a user identity. 384 func (user *User) AddPublicKey(r *requests.Request) error { 385 key, err := NewPublicKey(r) 386 if err != nil { 387 return errors.ErrAddPublicKey.WithArgs(r.Key.Usage, err) 388 } 389 for _, k := range user.PublicKeys { 390 if k.Type != key.Type { 391 continue 392 } 393 if k.Fingerprint != key.Fingerprint { 394 continue 395 } 396 return errors.ErrAddPublicKey.WithArgs(r.Key.Usage, "already exists") 397 } 398 user.PublicKeys = append(user.PublicKeys, key) 399 user.Revise() 400 return nil 401 } 402 403 // DeletePublicKey deletes a public key associated with a user. 404 func (user *User) DeletePublicKey(r *requests.Request) error { 405 var found bool 406 keys := []*PublicKey{} 407 for _, k := range user.PublicKeys { 408 if k.ID == r.Key.ID { 409 found = true 410 continue 411 } 412 keys = append(keys, k) 413 } 414 if !found { 415 return errors.ErrDeletePublicKey.WithArgs(r.Key.ID, "not found") 416 } 417 user.PublicKeys = keys 418 user.Revise() 419 return nil 420 } 421 422 // AddAPIKey adds API key to a user identity. 423 func (user *User) AddAPIKey(r *requests.Request) error { 424 key, err := NewAPIKey(r) 425 if err != nil { 426 return errors.ErrAddAPIKey.WithArgs(r.Key.Usage, err) 427 } 428 user.APIKeys = append(user.APIKeys, key) 429 user.Revise() 430 return nil 431 } 432 433 // DeleteAPIKey deletes an API key associated with a user. 434 func (user *User) DeleteAPIKey(r *requests.Request) error { 435 var found bool 436 keys := []*APIKey{} 437 for _, k := range user.APIKeys { 438 if k.ID == r.Key.ID { 439 found = true 440 r.Key.Prefix = k.Prefix 441 continue 442 } 443 keys = append(keys, k) 444 } 445 if !found { 446 return errors.ErrDeleteAPIKey.WithArgs(r.Key.ID, "not found") 447 } 448 user.APIKeys = keys 449 user.Revise() 450 return nil 451 } 452 453 // LookupAPIKey performs the lookup of API key. 454 func (user *User) LookupAPIKey(r *requests.Request) error { 455 for _, k := range user.APIKeys { 456 if k.Prefix == r.Key.Prefix { 457 if k.Match(r.Key.Payload) { 458 return nil 459 } 460 return errors.ErrLookupAPIKeyFailed 461 } 462 } 463 return errors.ErrLookupAPIKeyFailed 464 } 465 466 // AddMfaToken adds MFA token to a user identity. 467 func (user *User) AddMfaToken(r *requests.Request) error { 468 token, err := NewMfaToken(r) 469 if err != nil { 470 return errors.ErrAddMfaToken.WithArgs(err) 471 } 472 for _, k := range user.MfaTokens { 473 if k.Secret == token.Secret { 474 return errors.ErrAddMfaToken.WithArgs(errors.ErrDuplicateMfaTokenSecret) 475 } 476 if k.Comment == token.Comment { 477 return errors.ErrAddMfaToken.WithArgs(errors.ErrDuplicateMfaTokenComment) 478 } 479 } 480 user.MfaTokens = append(user.MfaTokens, token) 481 user.Revise() 482 return nil 483 } 484 485 // DeleteMfaToken deletes MFA token associated with a user. 486 func (user *User) DeleteMfaToken(r *requests.Request) error { 487 var found bool 488 tokens := []*MfaToken{} 489 for _, k := range user.MfaTokens { 490 if k.ID == r.MfaToken.ID { 491 found = true 492 continue 493 } 494 tokens = append(tokens, k) 495 } 496 if !found { 497 return errors.ErrDeleteMfaToken.WithArgs(r.MfaToken.ID, "not found") 498 } 499 user.MfaTokens = tokens 500 user.Revise() 501 return nil 502 } 503 504 // GetFlags populates request context with metadata about a user. 505 func (user *User) GetFlags(r *requests.Request) { 506 for _, token := range user.MfaTokens { 507 if token.Disabled { 508 continue 509 } 510 r.Flags.MfaConfigured = true 511 switch token.Type { 512 case "totp": 513 r.Flags.MfaApp = true 514 case "u2f": 515 r.Flags.MfaUniversal = true 516 } 517 } 518 } 519 520 // ChangePassword changes user password. 521 func (user *User) ChangePassword(r *requests.Request, keepVersions int) error { 522 if err := user.VerifyPassword(r.User.OldPassword); err != nil { 523 return errors.ErrChangeUserPassword.WithArgs(err) 524 } 525 if err := user.AddPassword(r.User.Password, keepVersions); err != nil { 526 return errors.ErrChangeUserPassword.WithArgs(err) 527 } 528 return nil 529 } 530 531 // UpdatePassword update user password. 532 func (user *User) UpdatePassword(r *requests.Request, keepVersions int) error { 533 if !strings.HasPrefix(r.User.Password, "bcrypt:") { 534 // Check whether the existing password matches the newly provided password, 535 // and skip updating if it is. 536 if user.VerifyPassword(r.User.Password) == nil { 537 return nil 538 } 539 } 540 if err := user.AddPassword(r.User.Password, keepVersions); err != nil { 541 return errors.ErrUpdateUserPassword.WithArgs(err) 542 } 543 return nil 544 } 545 546 // GetMetadata returns user metadata. 547 func (user *User) GetMetadata() *UserMetadata { 548 m := &UserMetadata{ 549 ID: user.ID, 550 Enabled: user.Enabled, 551 Username: user.Username, 552 Title: user.Title, 553 Created: user.Created, 554 LastModified: user.LastModified, 555 Revision: user.Revision, 556 } 557 if user.Avatar != nil { 558 m.Avatar = user.Avatar.Path 559 } 560 if user.EmailAddress != nil { 561 m.Email = user.EmailAddress.ToString() 562 } 563 if user.Name != nil { 564 m.Name = user.Name.ToString() 565 } 566 return m 567 } 568 569 // GetChallenges returns a list of challenges that should be 570 // satisfied prior to successfully authenticating a user. 571 func (user *User) GetChallenges() []string { 572 var challenges []string 573 challenges = append(challenges, "password") 574 if len(user.MfaTokens) > 0 { 575 challenges = append(challenges, "mfa") 576 } 577 return challenges 578 } 579 580 // Revise increments revision number and last modified timestamp. 581 func (user *User) Revise() { 582 user.Revision++ 583 user.LastModified = time.Now().UTC() 584 } 585 586 // HasAdminRights returns true if the user has admin rights. 587 func (user *User) HasAdminRights() bool { 588 for _, role := range user.Roles { 589 if role.Name == "admin" && role.Organization == "authp" { 590 return true 591 } 592 } 593 return false 594 }