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