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