github.com/greenpau/go-identity@v1.1.6/database.go (about)

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