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 }