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