github.com/greenpau/go-authcrunch@v1.0.50/pkg/identity/database.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 "encoding/json" 19 "fmt" 20 "io/ioutil" 21 "os" 22 "path/filepath" 23 "strings" 24 "sync" 25 "time" 26 27 "github.com/greenpau/go-authcrunch/pkg/errors" 28 "github.com/greenpau/go-authcrunch/pkg/requests" 29 "github.com/greenpau/go-authcrunch/pkg/util" 30 fileutil "github.com/greenpau/go-authcrunch/pkg/util/file" 31 "github.com/greenpau/versioned" 32 ) 33 34 var ( 35 app *versioned.PackageManager 36 appVersion string 37 gitBranch string 38 gitCommit string 39 buildUser string 40 buildDate string 41 defaultPolicy = Policy{ 42 User: UserPolicy{ 43 MinLength: 3, 44 MaxLength: 50, 45 AllowNonAlphaNumeric: false, 46 AllowUppercase: false, 47 }, 48 Password: PasswordPolicy{ 49 KeepVersions: 10, 50 MinLength: 8, 51 MaxLength: 128, 52 RequireUppercase: false, 53 RequireLowercase: false, 54 RequireNumber: false, 55 RequireNonAlphaNumeric: false, 56 BlockReuse: false, 57 BlockPasswordChange: false, 58 }, 59 } 60 ) 61 62 func init() { 63 app = versioned.NewPackageManager("authdb") 64 app.Description = "authdb" 65 app.Documentation = "https://github.com/greenpau/go-authcrunch" 66 app.SetVersion(appVersion, "1.0.50") 67 app.SetGitBranch(gitBranch, "main") 68 app.SetGitCommit(gitCommit, "v1.0.49-3-g5faadfc") 69 app.SetBuildUser(buildUser, "") 70 app.SetBuildDate(buildDate, "") 71 } 72 73 // Policy represents database usage policy. 74 type Policy struct { 75 Password PasswordPolicy `json:"password,omitempty" xml:"password,omitempty" yaml:"password,omitempty"` 76 User UserPolicy `json:"user,omitempty" xml:"user,omitempty" yaml:"user,omitempty"` 77 } 78 79 // PasswordPolicy represents database password policy. 80 type PasswordPolicy struct { 81 KeepVersions int `json:"keep_versions" xml:"keep_versions" yaml:"keep_versions"` 82 MinLength int `json:"min_length" xml:"min_length" yaml:"min_length"` 83 MaxLength int `json:"max_length" xml:"max_length" yaml:"max_length"` 84 RequireUppercase bool `json:"require_uppercase" xml:"require_uppercase" yaml:"require_uppercase"` 85 RequireLowercase bool `json:"require_lowercase" xml:"require_lowercase" yaml:"require_lowercase"` 86 RequireNumber bool `json:"require_number" xml:"require_number" yaml:"require_number"` 87 RequireNonAlphaNumeric bool `json:"require_non_alpha_numeric" xml:"require_non_alpha_numeric" yaml:"require_non_alpha_numeric"` 88 BlockReuse bool `json:"block_reuse" xml:"block_reuse" yaml:"block_reuse"` 89 BlockPasswordChange bool `json:"block_password_change" xml:"block_password_change" yaml:"block_password_change"` 90 } 91 92 // UserPolicy represents database username policy 93 type UserPolicy struct { 94 MinLength int `json:"min_length" xml:"min_length" yaml:"min_length"` 95 MaxLength int `json:"max_length" xml:"max_length" yaml:"max_length"` 96 AllowNonAlphaNumeric bool `json:"allow_non_alpha_numeric" xml:"allow_non_alpha_numeric" yaml:"allow_non_alpha_numeric"` 97 AllowUppercase bool `json:"allow_uppercase" xml:"allow_uppercase" yaml:"allow_uppercase"` 98 } 99 100 // Database is user identity database. 101 type Database struct { 102 mu *sync.RWMutex 103 Version string `json:"version,omitempty" xml:"version,omitempty" yaml:"version,omitempty"` 104 Policy Policy `json:"policy,omitempty" xml:"policy,omitempty" yaml:"policy,omitempty"` 105 Revision uint64 `json:"revision,omitempty" xml:"revision,omitempty" yaml:"revision,omitempty"` 106 LastModified time.Time `json:"last_modified,omitempty" xml:"last_modified,omitempty" yaml:"last_modified,omitempty"` 107 Users []*User `json:"users,omitempty" xml:"users,omitempty" yaml:"users,omitempty"` 108 refEmailAddress map[string]*User 109 refUsername map[string]*User 110 refID map[string]*User 111 refAPIKey map[string]*User 112 path string 113 } 114 115 // NewDatabase return an instance of Database. 116 func NewDatabase(fp string) (*Database, error) { 117 if fp == "/dev/null" { 118 return nil, errors.ErrNewDatabase.WithArgs(fp, "null path") 119 } 120 121 db := &Database{ 122 mu: &sync.RWMutex{}, 123 path: fp, 124 refUsername: make(map[string]*User), 125 refID: make(map[string]*User), 126 refEmailAddress: make(map[string]*User), 127 refAPIKey: make(map[string]*User), 128 } 129 fileInfo, err := os.Stat(fp) 130 if err != nil { 131 if !os.IsNotExist(err) { 132 return nil, errors.ErrNewDatabase.WithArgs(fp, err) 133 } 134 if err := os.MkdirAll(filepath.Dir(fp), 0700); err != nil { 135 return nil, errors.ErrNewDatabase.WithArgs(fp, err) 136 } 137 db.Version = app.Version 138 db.enforceDefaultPolicy() 139 if err := db.commit(); err != nil { 140 return nil, errors.ErrNewDatabase.WithArgs(fp, err) 141 } 142 } else { 143 if fileInfo.IsDir() { 144 return nil, errors.ErrNewDatabase.WithArgs(fp, "path points to a directory") 145 } 146 b, err := fileutil.ReadFileBytes(fp) 147 if err != nil { 148 return nil, errors.ErrNewDatabase.WithArgs(fp, err) 149 } 150 if err := json.Unmarshal(b, db); err != nil { 151 return nil, errors.ErrNewDatabase.WithArgs(fp, err) 152 } 153 if changed := db.enforceDefaultPolicy(); changed { 154 if err := db.commit(); err != nil { 155 return nil, errors.ErrNewDatabase.WithArgs(fp, err) 156 } 157 } 158 } 159 160 // db.mu = &sync.RWMutex{} 161 // db.path = fp 162 db.Version = app.Version 163 164 for _, user := range db.Users { 165 if err := user.Valid(); err != nil { 166 return nil, errors.ErrNewDatabaseInvalidUser.WithArgs(user, err) 167 } 168 username := strings.ToLower(user.Username) 169 if _, exists := db.refUsername[username]; exists { 170 return nil, errors.ErrNewDatabaseDuplicateUser.WithArgs(user.Username, user) 171 } 172 if _, exists := db.refID[user.ID]; exists { 173 return nil, errors.ErrNewDatabaseDuplicateUserID.WithArgs(user.ID, user) 174 } 175 db.refUsername[username] = user 176 db.refID[user.ID] = user 177 for _, email := range user.EmailAddresses { 178 emailAddress := strings.ToLower(email.Address) 179 if _, exists := db.refEmailAddress[emailAddress]; exists { 180 return nil, errors.ErrNewDatabaseDuplicateEmail.WithArgs(emailAddress, user) 181 } 182 db.refEmailAddress[emailAddress] = user 183 } 184 for _, p := range user.Passwords { 185 if p.Algorithm == "" { 186 p.Algorithm = "bcrypt" 187 } 188 } 189 for _, apiKey := range user.APIKeys { 190 if _, exists := db.refAPIKey[apiKey.Prefix]; exists { 191 return nil, errors.ErrNewDatabaseDuplicateAPIKey.WithArgs(apiKey.Prefix, user) 192 } 193 db.refAPIKey[apiKey.Prefix] = user 194 } 195 } 196 return db, nil 197 } 198 199 func (db *Database) enforceDefaultPolicy() bool { 200 var changes int 201 if db.Policy.Password.MinLength == 0 { 202 db.Policy.Password.MinLength = defaultPolicy.Password.MinLength 203 changes++ 204 } 205 if db.Policy.Password.MaxLength == 0 { 206 db.Policy.Password.MaxLength = defaultPolicy.Password.MaxLength 207 changes++ 208 } 209 if db.Policy.Password.KeepVersions == 0 { 210 db.Policy.Password.KeepVersions = defaultPolicy.Password.KeepVersions 211 changes++ 212 } 213 if db.Policy.User.MinLength == 0 { 214 db.Policy.User.MinLength = defaultPolicy.User.MinLength 215 changes++ 216 } 217 if db.Policy.User.MaxLength == 0 { 218 db.Policy.User.MaxLength = defaultPolicy.User.MaxLength 219 changes++ 220 } 221 if changes > 0 { 222 return true 223 } 224 return false 225 } 226 227 func (db *Database) checkPolicyCompliance(username, password string) error { 228 if err := db.checkUserPolicyCompliance(username); err != nil { 229 return err 230 } 231 if err := db.checkPasswordPolicyCompliance(password); err != nil { 232 return err 233 } 234 return nil 235 } 236 237 func (db *Database) checkUserPolicyCompliance(s string) error { 238 if len(s) > db.Policy.User.MaxLength || len(s) < db.Policy.User.MinLength { 239 return errors.ErrUserPolicyCompliance 240 } 241 return nil 242 } 243 244 func (db *Database) checkPasswordPolicyCompliance(s string) error { 245 if len(s) > db.Policy.Password.MaxLength || len(s) < db.Policy.Password.MinLength { 246 return errors.ErrPasswordPolicyCompliance 247 } 248 return nil 249 } 250 251 // GetPath returns the path to Database. 252 func (db *Database) GetPath() string { 253 return db.path 254 } 255 256 // AddUser adds user identity to the database. 257 func (db *Database) AddUser(r *requests.Request) error { 258 db.mu.Lock() 259 defer db.mu.Unlock() 260 261 if err := db.checkPolicyCompliance(r.User.Username, r.User.Password); err != nil { 262 return errors.ErrAddUser.WithArgs(r.User.Username, err) 263 } 264 265 user, err := NewUserWithRoles( 266 r.User.Username, r.User.Password, 267 r.User.Email, r.User.FullName, 268 r.User.Roles, 269 ) 270 if err != nil { 271 return errors.ErrAddUser.WithArgs(r.User.Username, err) 272 } 273 for i := 0; i < 10; i++ { 274 id := NewID() 275 if _, exists := db.refID[id]; !exists { 276 user.ID = id 277 break 278 } 279 } 280 username := strings.ToLower(user.Username) 281 if _, exists := db.refUsername[username]; exists { 282 return errors.ErrAddUser.WithArgs(username, "username already in use") 283 } 284 285 emailAddresses := []string{} 286 for _, email := range user.EmailAddresses { 287 emailAddress := strings.ToLower(email.Address) 288 if _, exists := db.refEmailAddress[emailAddress]; exists { 289 return errors.ErrAddUser.WithArgs(emailAddress, "email address already in use") 290 } 291 emailAddresses = append(emailAddresses, emailAddress) 292 } 293 294 if r.Query.ID != "" { 295 // Handle the case where registration ID is being provided with the request. 296 user.Registration = NewRegistration(r.Query.ID) 297 } 298 299 db.refUsername[username] = user 300 db.refID[user.ID] = user 301 for _, emailAddress := range emailAddresses { 302 db.refEmailAddress[emailAddress] = user 303 } 304 db.Users = append(db.Users, user) 305 306 if err := db.commit(); err != nil { 307 return errors.ErrAddUser.WithArgs(username, err) 308 } 309 return nil 310 } 311 312 // GetUsers return a list of user identities. 313 func (db *Database) GetUsers(r *requests.Request) error { 314 db.mu.RLock() 315 defer db.mu.RUnlock() 316 _, err := db.validateUserIdentity(r.User.Username, r.User.Email) 317 if err != nil { 318 return errors.ErrGetUsers.WithArgs(err) 319 } 320 bundle := NewUserMetadataBundle() 321 for _, user := range db.Users { 322 bundle.Add(user.GetMetadata()) 323 } 324 r.Response.Payload = bundle 325 return nil 326 } 327 328 // GetUser return an instance of User. 329 func (db *Database) GetUser(r *requests.Request) error { 330 db.mu.RLock() 331 defer db.mu.RUnlock() 332 user, err := db.validateUserIdentity(r.User.Username, r.User.Email) 333 if err != nil { 334 return errors.ErrGetUsers.WithArgs(err) 335 } 336 r.Response.Payload = user 337 return nil 338 } 339 340 // DeleteUser deletes a user by user id. 341 func (db *Database) DeleteUser(r *requests.Request) error { 342 db.mu.Lock() 343 defer db.mu.Unlock() 344 // user, err := db.validateUserIdentity(r.User.Username, r.User.Email) 345 _, err := db.validateUserIdentity(r.User.Username, r.User.Email) 346 if err != nil { 347 return errors.ErrDeleteUser.WithArgs(r.Query.ID, err) 348 } 349 return errors.ErrDeleteUser.WithArgs(r.Query.ID, "user delete operation is not supported") 350 // TODO: how do we delete a user ??? 351 352 // if err := user.DeletePublicKey(r); err != nil { 353 // return err 354 //} 355 /* 356 if err := db.commit(); err != nil { 357 return errors.ErrDeleteUser.WithArgs(r.Query.ID, err) 358 } 359 return nil 360 */ 361 } 362 363 // AuthenticateUser adds user identity to the database. 364 func (db *Database) AuthenticateUser(r *requests.Request) error { 365 db.mu.RLock() 366 defer db.mu.RUnlock() 367 user, err := db.getUser(r.User.Username) 368 if err != nil { 369 r.Response.Code = 400 370 // Calculate password hash as the means to prevent user discovery. 371 NewPassword(r.User.Password) 372 return errors.ErrAuthFailed.WithArgs(err) 373 } 374 375 switch { 376 case r.User.Password != "": 377 if err := user.VerifyPassword(r.User.Password); err != nil { 378 r.Response.Code = 400 379 return errors.ErrAuthFailed.WithArgs(err) 380 } 381 case r.WebAuthn.Request != "": 382 if err := user.VerifyWebAuthnRequest(r); err != nil { 383 r.Response.Code = 400 384 return errors.ErrAuthFailed.WithArgs(err) 385 } 386 default: 387 r.Response.Code = 400 388 return errors.ErrAuthFailed.WithArgs("malformed auth request") 389 } 390 391 r.Response.Code = 200 392 return nil 393 } 394 395 // getUser return User by either email address or username. 396 func (db *Database) getUser(s string) (*User, error) { 397 if strings.Contains(s, "@") { 398 return db.getUserByEmailAddress(s) 399 } 400 return db.getUserByUsername(s) 401 } 402 403 // getUserByID returns a user by id 404 func (db *Database) getUserByID(s string) (*User, error) { 405 s = strings.ToLower(s) 406 user, exists := db.refID[s] 407 if exists && user != nil { 408 return user, nil 409 } 410 return nil, errors.ErrDatabaseUserNotFound 411 } 412 413 // getUserByUsername returns a user by username 414 func (db *Database) getUserByUsername(s string) (*User, error) { 415 if len(s) < 2 { 416 return nil, errors.ErrDatabaseUserNotFound 417 } 418 s = strings.ToLower(s) 419 user, exists := db.refUsername[s] 420 if exists && user != nil { 421 return user, nil 422 } 423 return nil, errors.ErrDatabaseUserNotFound 424 } 425 426 // getUserByEmailAddress returns a liast of users associated with a specific email 427 // address. 428 func (db *Database) getUserByEmailAddress(s string) (*User, error) { 429 if len(s) < 6 { 430 return nil, errors.ErrDatabaseUserNotFound 431 } 432 s = strings.ToLower(s) 433 user, exists := db.refEmailAddress[s] 434 if exists && user != nil { 435 return user, nil 436 } 437 return nil, errors.ErrDatabaseUserNotFound 438 } 439 440 // GetUserCount returns user count. 441 func (db *Database) GetUserCount() int { 442 db.mu.RLock() 443 defer db.mu.RUnlock() 444 return len(db.Users) 445 } 446 447 // GetAdminUserCount returns user count. 448 func (db *Database) GetAdminUserCount() int { 449 db.mu.RLock() 450 defer db.mu.RUnlock() 451 var counter int 452 for _, user := range db.Users { 453 if user.HasAdminRights() { 454 counter++ 455 } 456 } 457 return counter 458 } 459 460 // Save saves the database. 461 func (db *Database) Save() error { 462 db.mu.Lock() 463 defer db.mu.Unlock() 464 return db.commit() 465 } 466 467 // Copy copies the database to another file. 468 func (db *Database) Copy(fp string) error { 469 db.mu.Lock() 470 defer db.mu.Unlock() 471 path := db.path 472 db.path = fp 473 err := db.commit() 474 db.path = path 475 return err 476 } 477 478 // commit writes the database contents to a file. 479 func (db *Database) commit() error { 480 db.Revision++ 481 db.LastModified = time.Now().UTC() 482 data, err := json.MarshalIndent(db, "", " ") 483 if err != nil { 484 return errors.ErrDatabaseCommit.WithArgs(db.path, err) 485 } 486 if err := ioutil.WriteFile(db.path, []byte(data), 0600); err != nil { 487 return errors.ErrDatabaseCommit.WithArgs(db.path, err) 488 } 489 return nil 490 } 491 492 func (db *Database) validateUserIdentity(username, email string) (*User, error) { 493 user1, err := db.getUserByUsername(username) 494 if err != nil { 495 return nil, err 496 } 497 user2, err := db.getUserByEmailAddress(email) 498 if err != nil { 499 return nil, err 500 } 501 if user1.ID != user2.ID { 502 return nil, errors.ErrDatabaseInvalidUser 503 } 504 return user1, nil 505 } 506 507 // AddPublicKey adds public key, e.g. GPG or SSH, for a user. 508 func (db *Database) AddPublicKey(r *requests.Request) error { 509 db.mu.Lock() 510 defer db.mu.Unlock() 511 user, err := db.validateUserIdentity(r.User.Username, r.User.Email) 512 if err != nil { 513 return errors.ErrAddPublicKey.WithArgs(r.Key.Usage, err) 514 } 515 if err := user.AddPublicKey(r); err != nil { 516 return err 517 } 518 if err := db.commit(); err != nil { 519 return errors.ErrAddPublicKey.WithArgs(r.Key.Usage, err) 520 } 521 return nil 522 } 523 524 // GetPublicKeys returns a list of public keys associated with a user. 525 func (db *Database) GetPublicKeys(r *requests.Request) error { 526 db.mu.RLock() 527 defer db.mu.RUnlock() 528 user, err := db.validateUserIdentity(r.User.Username, r.User.Email) 529 if err != nil { 530 return errors.ErrGetPublicKeys.WithArgs(r.Key.Usage, err) 531 } 532 bundle := NewPublicKeyBundle() 533 for _, k := range user.PublicKeys { 534 if k.Usage != r.Key.Usage { 535 continue 536 } 537 if k.Disabled { 538 continue 539 } 540 bundle.Add(k) 541 } 542 r.Response.Payload = bundle 543 return nil 544 } 545 546 // DeletePublicKey deletes a public key associated with a user by key id. 547 func (db *Database) DeletePublicKey(r *requests.Request) error { 548 db.mu.Lock() 549 defer db.mu.Unlock() 550 user, err := db.validateUserIdentity(r.User.Username, r.User.Email) 551 if err != nil { 552 return errors.ErrDeletePublicKey.WithArgs(r.Key.ID, err) 553 } 554 if err := user.DeletePublicKey(r); err != nil { 555 return err 556 } 557 if err := db.commit(); err != nil { 558 return errors.ErrDeletePublicKey.WithArgs(r.Key.Usage, err) 559 } 560 return nil 561 } 562 563 // AddAPIKey adds API key for a user. 564 func (db *Database) AddAPIKey(r *requests.Request) error { 565 db.mu.Lock() 566 defer db.mu.Unlock() 567 user, err := db.validateUserIdentity(r.User.Username, r.User.Email) 568 if err != nil { 569 return errors.ErrAddAPIKey.WithArgs(r.Key.Usage, err) 570 } 571 s := util.GetRandomString(72) 572 failCount := 0 573 for { 574 hk, err := NewPassword(s) 575 if err != nil { 576 if failCount > 10 { 577 return err 578 } 579 failCount++ 580 continue 581 } 582 keyPrefix := string(s[:24]) 583 if _, exists := db.refAPIKey[keyPrefix]; exists { 584 continue 585 } 586 r.Response.Payload = s 587 r.Key.Payload = hk.Hash 588 r.Key.Prefix = keyPrefix 589 if err := user.AddAPIKey(r); err != nil { 590 return err 591 } 592 db.refAPIKey[keyPrefix] = user 593 break 594 } 595 596 if err := db.commit(); err != nil { 597 return errors.ErrAddAPIKey.WithArgs(r.Key.Usage, err) 598 } 599 return nil 600 } 601 602 // DeleteAPIKey deletes an API key associated with a user by key id. 603 func (db *Database) DeleteAPIKey(r *requests.Request) error { 604 db.mu.Lock() 605 defer db.mu.Unlock() 606 user, err := db.validateUserIdentity(r.User.Username, r.User.Email) 607 if err != nil { 608 return errors.ErrDeleteAPIKey.WithArgs(r.Key.ID, err) 609 } 610 if err := user.DeleteAPIKey(r); err != nil { 611 return err 612 } 613 delete(db.refAPIKey, r.Key.Prefix) 614 if err := db.commit(); err != nil { 615 return errors.ErrDeleteAPIKey.WithArgs(r.Key.Usage, err) 616 } 617 return nil 618 } 619 620 // GetAPIKeys returns a list of API keys associated with a user. 621 func (db *Database) GetAPIKeys(r *requests.Request) error { 622 db.mu.RLock() 623 defer db.mu.RUnlock() 624 user, err := db.validateUserIdentity(r.User.Username, r.User.Email) 625 if err != nil { 626 return errors.ErrGetAPIKeys.WithArgs(r.Key.Usage, err) 627 } 628 bundle := NewAPIKeyBundle() 629 for _, k := range user.APIKeys { 630 if k.Usage != r.Key.Usage { 631 continue 632 } 633 if k.Disabled { 634 continue 635 } 636 bundle.Add(k) 637 } 638 r.Response.Payload = bundle 639 return nil 640 } 641 642 // ChangeUserPassword change user password. 643 func (db *Database) ChangeUserPassword(r *requests.Request) error { 644 db.mu.Lock() 645 defer db.mu.Unlock() 646 user, err := db.validateUserIdentity(r.User.Username, r.User.Email) 647 if err != nil { 648 return errors.ErrChangeUserPassword.WithArgs(err) 649 } 650 if err := db.checkPasswordPolicyCompliance(r.User.Password); err != nil { 651 return errors.ErrChangeUserPassword.WithArgs(err) 652 } 653 if err := user.ChangePassword(r, db.Policy.Password.KeepVersions); err != nil { 654 return err 655 } 656 // if db.Policy.Password.KeepVersions 657 if err := db.commit(); err != nil { 658 return errors.ErrChangeUserPassword.WithArgs(err) 659 } 660 return nil 661 } 662 663 // UpdateUserPassword change user password. 664 func (db *Database) UpdateUserPassword(r *requests.Request) error { 665 db.mu.Lock() 666 defer db.mu.Unlock() 667 user, err := db.validateUserIdentity(r.User.Username, r.User.Email) 668 if err != nil { 669 return errors.ErrUpdateUserPassword.WithArgs(err) 670 } 671 if err := db.checkPasswordPolicyCompliance(r.User.Password); err != nil { 672 return errors.ErrUpdateUserPassword.WithArgs(err) 673 } 674 if err := user.UpdatePassword(r, db.Policy.Password.KeepVersions); err != nil { 675 return err 676 } 677 if err := db.commit(); err != nil { 678 return errors.ErrUpdateUserPassword.WithArgs(err) 679 } 680 return nil 681 } 682 683 // IdentifyUser returns user identity and a list of challenges that should be 684 // satisfied prior to successfully authenticating a user. 685 func (db *Database) IdentifyUser(r *requests.Request) error { 686 db.mu.Lock() 687 defer db.mu.Unlock() 688 user, err := db.getUser(r.User.Username) 689 if err != nil { 690 r.User.Username = "nobody" 691 r.User.Email = "nobody@localhost" 692 r.User.Challenges = []string{"password"} 693 return nil 694 } 695 if r.Flags.Enabled { 696 user.GetFlags(r) 697 } 698 r.User.Username = user.Username 699 r.User.Email = user.GetMailClaim() 700 r.User.FullName = user.GetNameClaim() 701 r.User.Roles = user.GetRolesClaim() 702 r.User.Challenges = user.GetChallenges() 703 r.Response.Code = 200 704 return nil 705 } 706 707 // LookupAPIKey returns username and email associated with the provided API 708 // key. 709 func (db *Database) LookupAPIKey(r *requests.Request) error { 710 if r.Key.Payload == "" { 711 return errors.ErrLookupAPIKeyPayloadEmpty 712 } 713 if len(r.Key.Payload) < 72 { 714 return errors.ErrLookupAPIKeyMalformedPayload 715 } 716 r.Key.Prefix = string(r.Key.Payload[:24]) 717 db.mu.Lock() 718 defer db.mu.Unlock() 719 user, exists := db.refAPIKey[r.Key.Prefix] 720 if !exists { 721 return errors.ErrLookupAPIKeyFailed 722 } 723 if err := user.LookupAPIKey(r); err != nil { 724 return err 725 } 726 r.User.Username = user.Username 727 r.User.Email = user.GetMailClaim() 728 r.Response.Code = 200 729 return nil 730 } 731 732 // AddMfaToken adds MFA token for a user. 733 func (db *Database) AddMfaToken(r *requests.Request) error { 734 db.mu.Lock() 735 defer db.mu.Unlock() 736 user, err := db.validateUserIdentity(r.User.Username, r.User.Email) 737 if err != nil { 738 return errors.ErrAddMfaToken.WithArgs(err) 739 } 740 if err := user.AddMfaToken(r); err != nil { 741 return err 742 } 743 if err := db.commit(); err != nil { 744 return errors.ErrAddMfaToken.WithArgs(err) 745 } 746 return nil 747 } 748 749 // GetMfaTokens returns a list of MFA tokens associated with a user. 750 func (db *Database) GetMfaTokens(r *requests.Request) error { 751 db.mu.RLock() 752 defer db.mu.RUnlock() 753 user, err := db.validateUserIdentity(r.User.Username, r.User.Email) 754 if err != nil { 755 return errors.ErrGetMfaTokens.WithArgs(err) 756 } 757 bundle := NewMfaTokenBundle() 758 for _, token := range user.MfaTokens { 759 if token.Disabled { 760 continue 761 } 762 bundle.Add(token) 763 } 764 r.Response.Payload = bundle 765 return nil 766 } 767 768 // DeleteMfaToken deletes MFA token associated with a user by token id. 769 func (db *Database) DeleteMfaToken(r *requests.Request) error { 770 db.mu.Lock() 771 defer db.mu.Unlock() 772 user, err := db.validateUserIdentity(r.User.Username, r.User.Email) 773 if err != nil { 774 return errors.ErrDeleteMfaToken.WithArgs(r.MfaToken.ID, err) 775 } 776 if err := user.DeleteMfaToken(r); err != nil { 777 return err 778 } 779 if err := db.commit(); err != nil { 780 return errors.ErrDeleteMfaToken.WithArgs(r.MfaToken.ID, err) 781 } 782 return nil 783 } 784 785 // GetUsernamePolicySummary returns the summary of username policy. 786 func (db *Database) GetUsernamePolicySummary() string { 787 var sb strings.Builder 788 var charRestrictions []string 789 sb.WriteString("A username should be") 790 sb.WriteString(fmt.Sprintf(" %d-%d character long string", db.Policy.User.MinLength, db.Policy.User.MaxLength)) 791 if !db.Policy.User.AllowUppercase { 792 charRestrictions = append(charRestrictions, "lowercase") 793 } 794 if !db.Policy.User.AllowNonAlphaNumeric { 795 charRestrictions = append(charRestrictions, "alpha-numeric") 796 } 797 if len(charRestrictions) > 0 { 798 sb.WriteString(fmt.Sprintf(" with %s characters", strings.Join(charRestrictions, ", "))) 799 } 800 return sb.String() 801 } 802 803 // GetUsernamePolicyRegex returns regex for usernames. 804 func (db *Database) GetUsernamePolicyRegex() string { 805 var startChars, allowedChars string 806 if !db.Policy.User.AllowUppercase { 807 startChars = "a-z" 808 allowedChars = "a-z0-9" 809 } else { 810 startChars = "a-zA-Z" 811 allowedChars = "a-zA-Z0-9" 812 } 813 if db.Policy.User.AllowNonAlphaNumeric { 814 allowedChars += "-_." 815 } 816 return fmt.Sprintf("^[%s][%s]{%d,%d}$", startChars, allowedChars, db.Policy.User.MinLength-1, db.Policy.User.MaxLength-1) 817 } 818 819 // GetPasswordPolicySummary returns the summary of password policy. 820 func (db *Database) GetPasswordPolicySummary() string { 821 var sb strings.Builder 822 var charRestrictions []string 823 sb.WriteString("A password should be") 824 sb.WriteString(fmt.Sprintf(" %d-%d character long string", db.Policy.Password.MinLength, db.Policy.Password.MaxLength)) 825 if db.Policy.Password.RequireUppercase { 826 charRestrictions = append(charRestrictions, "uppercase") 827 } 828 if db.Policy.Password.RequireLowercase { 829 charRestrictions = append(charRestrictions, "lowercase") 830 } 831 if db.Policy.Password.RequireNumber { 832 charRestrictions = append(charRestrictions, "numbers") 833 } 834 if db.Policy.Password.RequireNonAlphaNumeric { 835 charRestrictions = append(charRestrictions, "non alpha-numeric") 836 } 837 838 if len(charRestrictions) > 0 { 839 sb.WriteString(fmt.Sprintf(" with %s characters", strings.Join(charRestrictions, ", "))) 840 } 841 return sb.String() 842 } 843 844 // GetPasswordPolicyRegex returns regex for passwords. 845 func (db *Database) GetPasswordPolicyRegex() string { 846 var allowedChars string 847 if db.Policy.Password.RequireUppercase { 848 allowedChars += "(?=.*[A-Z])" 849 } 850 if db.Policy.Password.RequireLowercase { 851 allowedChars += "(?=.*[a-z].*[a-z])" 852 } 853 if db.Policy.Password.RequireNumber { 854 allowedChars += "(?=.*[0-9].*[0-9])" 855 } 856 if db.Policy.Password.RequireNonAlphaNumeric { 857 allowedChars += "(?=.*[~!@#$&*])" 858 } 859 860 return fmt.Sprintf("^%s.{%d,%d}$", allowedChars, db.Policy.Password.MinLength, db.Policy.Password.MaxLength) 861 862 } 863 864 // UserExists checks whether user exists. 865 func (db *Database) UserExists(username, emailAddress string) (bool, error) { 866 username = strings.ToLower(username) 867 emailAddress = strings.ToLower(emailAddress) 868 user1, _ := db.refUsername[username] 869 user2, _ := db.refEmailAddress[emailAddress] 870 switch { 871 case user1 == nil && user2 == nil: 872 return false, nil 873 case user1 == nil: 874 return false, fmt.Errorf("email is registered to a user, while username not found") 875 case user2 == nil: 876 return false, fmt.Errorf("username is registered to a user, while email not found") 877 } 878 if user1.ID != user2.ID { 879 return false, fmt.Errorf("username and email address belong to two different users") 880 } 881 return true, nil 882 }