github.com/greenpau/go-authcrunch@v1.1.4/pkg/user/user.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 user 16 17 import ( 18 "encoding/json" 19 "fmt" 20 "regexp" 21 "strings" 22 "time" 23 24 "github.com/greenpau/go-authcrunch/pkg/errors" 25 cfgutil "github.com/greenpau/go-authcrunch/pkg/util/cfg" 26 datautil "github.com/greenpau/go-authcrunch/pkg/util/data" 27 ) 28 29 /* 30 var reservedFields = map[string]interface{}{ 31 "email": true, 32 "role": true, 33 "groups": true, 34 "group": true, 35 "app_metadata": true, 36 "realm_access": true, 37 "paths": true, 38 "acl": true, 39 } 40 */ 41 42 // User is a user with claims and status. 43 type User struct { 44 Claims *Claims `json:"claims,omitempty" xml:"claims,omitempty" yaml:"claims,omitempty"` 45 Token string `json:"token,omitempty" xml:"token,omitempty" yaml:"token,omitempty"` 46 TokenName string `json:"token_name,omitempty" xml:"token_name,omitempty" yaml:"token_name,omitempty"` 47 TokenSource string `json:"token_source,omitempty" xml:"token_source,omitempty" yaml:"token_source,omitempty"` 48 Authenticator Authenticator `json:"authenticator,omitempty" xml:"authenticator,omitempty" yaml:"authenticator,omitempty"` 49 Checkpoints []*Checkpoint `json:"checkpoints,omitempty" xml:"checkpoints,omitempty" yaml:"checkpoints,omitempty"` 50 Authorized bool `json:"authorized,omitempty" xml:"authorized,omitempty" yaml:"authorized,omitempty"` 51 FrontendLinks []string `json:"frontend_links,omitempty" xml:"frontend_links,omitempty" yaml:"frontend_links,omitempty"` 52 Locked bool `json:"locked,omitempty" xml:"locked,omitempty" yaml:"locked,omitempty"` 53 Cached bool `json:"cached,omitempty" xml:"cached,omitempty" yaml:"cached,omitempty"` 54 requestHeaders map[string]string 55 requestIdentity map[string]interface{} 56 // Holds the map for all the claims parsed from a token. 57 mkv map[string]interface{} 58 // Holds the map for a subset of claims necessary for ACL evaluation. 59 tkv map[string]interface{} 60 // Holds the map of the user roles. 61 rkv map[string]interface{} 62 } 63 64 // Checkpoint represents additional checks that a user needs to pass. Once 65 // a user passes the checks, the Authorized is set to true. The checks 66 // could be the acceptance of the terms of use, multi-factor authentication, 67 // etc. 68 type Checkpoint struct { 69 ID int `json:"id,omitempty" xml:"id,omitempty" yaml:"id,omitempty"` 70 Name string `json:"name,omitempty" xml:"name,omitempty" yaml:"name,omitempty"` 71 Type string `json:"type,omitempty" xml:"type,omitempty" yaml:"type,omitempty"` 72 Parameters string `json:"parameters,omitempty" xml:"parameters,omitempty" yaml:"parameters,omitempty"` 73 Passed bool `json:"passed,omitempty" xml:"passed,omitempty" yaml:"passed,omitempty"` 74 FailedAttempts int `json:"failed_attempts,omitempty" xml:"failed_attempts,omitempty" yaml:"failed_attempts,omitempty"` 75 } 76 77 // Authenticator represents authentication backend 78 type Authenticator struct { 79 Name string `json:"name,omitempty" xml:"name,omitempty" yaml:"name,omitempty"` 80 Realm string `json:"realm,omitempty" xml:"realm,omitempty" yaml:"realm,omitempty"` 81 Method string `json:"method,omitempty" xml:"method,omitempty" yaml:"method,omitempty"` 82 TempSecret string `json:"temp_secret,omitempty" xml:"temp_secret,omitempty" yaml:"temp_secret,omitempty"` 83 TempSessionID string `json:"temp_session_id,omitempty" xml:"temp_session_id,omitempty" yaml:"temp_session_id,omitempty"` 84 TempChallenge string `json:"temp_challenge,omitempty" xml:"temp_challenge,omitempty" yaml:"temp_challenge,omitempty"` 85 URL string `json:"url,omitempty" xml:"url,omitempty" yaml:"url,omitempty"` 86 } 87 88 // Claims represents custom and standard JWT claims associated with User. 89 type Claims struct { 90 Audience []string `json:"aud,omitempty" xml:"aud,omitempty" yaml:"aud,omitempty"` 91 ExpiresAt int64 `json:"exp,omitempty" xml:"exp,omitempty" yaml:"exp,omitempty"` 92 ID string `json:"jti,omitempty" xml:"jti,omitempty" yaml:"jti,omitempty"` 93 IssuedAt int64 `json:"iat,omitempty" xml:"iat,omitempty" yaml:"iat,omitempty"` 94 Issuer string `json:"iss,omitempty" xml:"iss,omitempty" yaml:"iss,omitempty"` 95 NotBefore int64 `json:"nbf,omitempty" xml:"nbf,omitempty" yaml:"nbf,omitempty"` 96 Subject string `json:"sub,omitempty" xml:"sub,omitempty" yaml:"sub,omitempty"` 97 Name string `json:"name,omitempty" xml:"name,omitempty" yaml:"name,omitempty"` 98 Email string `json:"email,omitempty" xml:"email,omitempty" yaml:"email,omitempty"` 99 Roles []string `json:"roles,omitempty" xml:"roles,omitempty" yaml:"roles,omitempty"` 100 Origin string `json:"origin,omitempty" xml:"origin,omitempty" yaml:"origin,omitempty"` 101 Scopes []string `json:"scopes,omitempty" xml:"scopes,omitempty" yaml:"scopes,omitempty"` 102 Organizations []string `json:"org,omitempty" xml:"org,omitempty" yaml:"org,omitempty"` 103 AccessList *AccessListClaim `json:"acl,omitempty" xml:"acl,omitempty" yaml:"acl,omitempty"` 104 Address string `json:"addr,omitempty" xml:"addr,omitempty" yaml:"addr,omitempty"` 105 PictureURL string `json:"picture,omitempty" xml:"picture,omitempty" yaml:"picture,omitempty"` 106 Metadata map[string]interface{} `json:"metadata,omitempty" xml:"metadata,omitempty" yaml:"metadata,omitempty"` 107 custom map[string]interface{} 108 } 109 110 // AccessListClaim represents custom acl/paths claim 111 type AccessListClaim struct { 112 Paths map[string]interface{} `json:"paths,omitempty" xml:"paths,omitempty" yaml:"paths,omitempty"` 113 } 114 115 // Valid validates user claims. 116 func (c Claims) Valid() error { 117 if c.ExpiresAt < time.Now().Unix() { 118 return errors.ErrExpiredToken 119 } 120 return nil 121 } 122 123 /* 124 func (c *Claims) MarshalJSON() ([]byte, error) { 125 m := make(map[string]interface{}) 126 if len(c.Audience) > 0 { 127 m["aud"] = c.Audience 128 } 129 if c.ExpiresAt > 0 { 130 m["exp"] = c.ExpiresAt 131 } 132 if c.IssuedAt > 0 { 133 m["iat"] = c.IssuedAt 134 } 135 if c.NotBefore > 0 { 136 m["nbf"] = c.NotBefore 137 } 138 if c.ID != "" { 139 m["jti"] = c.ID 140 } 141 if c.Issuer != "" { 142 m["iss"] = c.Issuer 143 } 144 if c.Subject != "" { 145 m["sub"] = c.Subject 146 } 147 if c.Name != "" { 148 m["sub"] = c.Name 149 } 150 if c.Email != "" { 151 m["email"] = c.Email 152 } 153 if len(c.Roles) > 0 { 154 m["roles"] = c.Roles 155 } 156 if c.Origin != "" { 157 m["origin"] = c.Origin 158 } 159 if len(c.Scopes) > 0 { 160 m["scopes"] = c.Scopes 161 } 162 if len(c.Organizations) > 0 { 163 m["org"] = c.Organizations 164 } 165 if c.AccessList != nil { 166 m["acl"] = c.AccessList 167 } 168 if c.Address != "" { 169 m["addr"] = c.Address 170 } 171 if c.PictureURL != "" { 172 m["picture"] = c.PictureURL 173 } 174 if c.Metadata != nil { 175 m["metadata"] = c.Metadata 176 } 177 if c.custom != nil { 178 for k, v := range c.custom { 179 m[k] = v 180 } 181 } 182 j, err := json.Marshal(m) 183 if err != nil { 184 return nil, err 185 } 186 return j, nil 187 } 188 */ 189 190 // AsMap converts Claims struct to dictionary. 191 func (u *User) AsMap() map[string]interface{} { 192 return u.mkv 193 } 194 195 // GetData return user claim felds and their values for the evaluation by an ACL. 196 func (u *User) GetData() map[string]interface{} { 197 return u.tkv 198 } 199 200 // SetRequestHeaders sets request headers associated with the user. 201 func (u *User) SetRequestHeaders(m map[string]string) { 202 u.requestHeaders = m 203 return 204 } 205 206 // GetRequestHeaders returns request headers associated with the user. 207 func (u *User) GetRequestHeaders() map[string]string { 208 return u.requestHeaders 209 } 210 211 // SetRequestIdentity sets request identity associated with the user. 212 func (u *User) SetRequestIdentity(m map[string]interface{}) { 213 u.requestIdentity = m 214 return 215 } 216 217 // GetRequestIdentity returns request identity associated with the user. 218 func (u *User) GetRequestIdentity() map[string]interface{} { 219 return u.requestIdentity 220 } 221 222 // BuildRequestIdentity builds request identity associated with the user. 223 func (u *User) BuildRequestIdentity(s string) map[string]interface{} { 224 m := make(map[string]interface{}) 225 m["roles"] = strings.Join(u.Claims.Roles, " ") 226 if u.Claims.ID != "" { 227 m["claim_id"] = u.Claims.ID 228 } 229 if u.Claims.Subject != "" { 230 m["sub"] = u.Claims.Subject 231 } 232 if u.Claims.Email != "" { 233 m["email"] = u.Claims.Email 234 } 235 236 switch s { 237 case "sub", "subject": 238 m["id"] = u.Claims.Subject 239 case "id": 240 m["id"] = u.Claims.ID 241 default: 242 if u.Claims.Email == "" { 243 m["id"] = u.Claims.Subject 244 } else { 245 m["id"] = u.Claims.Email 246 } 247 } 248 249 if u.Claims.Name != "" { 250 m["name"] = u.Claims.Name 251 } 252 if u.Claims.Email != "" { 253 m["email"] = u.Claims.Email 254 } 255 256 u.SetRequestIdentity(m) 257 return m 258 } 259 260 // SetExpiresAtClaim sets ExpiresAt claim. 261 func (u *User) SetExpiresAtClaim(i int64) { 262 u.Claims.ExpiresAt = i 263 u.mkv["exp"] = i 264 } 265 266 // SetIssuedAtClaim sets IssuedAt claim. 267 func (u *User) SetIssuedAtClaim(i int64) { 268 u.Claims.IssuedAt = i 269 u.mkv["iat"] = i 270 } 271 272 // SetNotBeforeClaim sets NotBefore claim. 273 func (u *User) SetNotBeforeClaim(i int64) { 274 u.Claims.NotBefore = i 275 u.mkv["nbf"] = i 276 } 277 278 // SetRolesClaim sets Roles claim 279 func (u *User) SetRolesClaim(roles []string) { 280 u.Claims.Roles = roles 281 u.tkv["roles"] = roles 282 u.mkv["roles"] = roles 283 for k := range u.rkv { 284 delete(u.rkv, k) 285 } 286 for _, roleName := range roles { 287 u.rkv[roleName] = true 288 } 289 } 290 291 // HasRole checks whether a user has any of the provided roles. 292 func (u *User) HasRole(roles ...string) bool { 293 for _, role := range roles { 294 if _, exists := u.rkv[role]; exists { 295 return true 296 } 297 } 298 return false 299 } 300 301 // HasRolePattern checks whether a user has a role matching the provided pattern. 302 func (u *User) HasRolePattern(ptrn *regexp.Regexp) bool { 303 for roleName := range u.rkv { 304 if ptrn.MatchString(roleName) { 305 return true 306 } 307 } 308 return false 309 } 310 311 // HasRoles checks whether a user has all of the provided roles. 312 func (u *User) HasRoles(roles ...string) bool { 313 for _, role := range roles { 314 if _, exists := u.rkv[role]; !exists { 315 return false 316 } 317 } 318 return true 319 } 320 321 // AddFrontendLinks adds frontend links to User instance. 322 func (u *User) AddFrontendLinks(v interface{}) error { 323 var entries []string 324 switch data := v.(type) { 325 case string: 326 entries = append(entries, data) 327 case []string: 328 entries = data 329 case []interface{}: 330 for _, entry := range data { 331 switch entry.(type) { 332 case string: 333 entries = append(entries, entry.(string)) 334 default: 335 return errors.ErrCheckpointInvalidType.WithArgs(data, data) 336 } 337 } 338 default: 339 return errors.ErrFrontendLinkInvalidType.WithArgs(data, data) 340 } 341 m := make(map[string]bool) 342 for _, entry := range entries { 343 m[entry] = true 344 } 345 for _, link := range u.FrontendLinks { 346 if _, exists := m[link]; exists { 347 m[link] = false 348 } 349 } 350 for _, entry := range entries { 351 if m[entry] { 352 u.FrontendLinks = append(u.FrontendLinks, entry) 353 } 354 } 355 return nil 356 } 357 358 // GetClaimValueByField returns the value of the provides claims field. 359 func (u *User) GetClaimValueByField(k string) string { 360 if u.mkv == nil { 361 return "" 362 } 363 data := datautil.GetValueFromMapByPath(k, u.mkv) 364 switch v := data.(type) { 365 case string: 366 return v 367 case []string: 368 return strings.Join(v, ", ") 369 case []interface{}: 370 var entries []string 371 for _, entry := range v { 372 switch s := entry.(type) { 373 case string: 374 entries = append(entries, s) 375 } 376 } 377 return strings.Join(entries, ", ") 378 } 379 return fmt.Sprintf("%v", data) 380 } 381 382 // NewCheckpoints returns Checkpoint instances. 383 func NewCheckpoints(v interface{}) ([]*Checkpoint, error) { 384 var entries []string 385 checkpoints := []*Checkpoint{} 386 switch data := v.(type) { 387 case string: 388 entries = append(entries, data) 389 case []string: 390 entries = data 391 case []interface{}: 392 for _, entry := range data { 393 switch entry.(type) { 394 case string: 395 entries = append(entries, entry.(string)) 396 default: 397 return nil, errors.ErrCheckpointInvalidType.WithArgs(data, data) 398 } 399 } 400 default: 401 return nil, errors.ErrCheckpointInvalidType.WithArgs(data, data) 402 } 403 for i, entry := range entries { 404 c, err := NewCheckpoint(entry) 405 if err != nil { 406 return nil, errors.ErrCheckpointInvalidInput.WithArgs(entry, err) 407 } 408 c.ID = i 409 checkpoints = append(checkpoints, c) 410 } 411 if len(checkpoints) < 1 { 412 return nil, errors.ErrCheckpointEmpty 413 } 414 return checkpoints, nil 415 } 416 417 // NewCheckpoint returns Checkpoint instance. 418 func NewCheckpoint(s string) (*Checkpoint, error) { 419 c := &Checkpoint{} 420 args, err := cfgutil.DecodeArgs(s) 421 if err != nil { 422 return nil, err 423 } 424 if len(args) < 1 { 425 return nil, fmt.Errorf("too short") 426 } 427 if args[0] == "require" { 428 args = args[1:] 429 } 430 if len(args) < 1 { 431 return nil, fmt.Errorf("too short") 432 } 433 434 switch args[0] { 435 case "mfa": 436 c.Name = "Multi-factor authentication" 437 c.Type = "mfa" 438 case "password": 439 c.Name = "Authenticate with password" 440 c.Type = "password" 441 //case "consent": 442 // c.Name = "Acceptance and consent" 443 // c.Type = "consent" 444 default: 445 return nil, fmt.Errorf("unsupported keyword: %s", args[0]) 446 } 447 return c, nil 448 } 449 450 func unpackUserData(data interface{}) (map[string]interface{}, error) { 451 var m map[string]interface{} 452 switch v := data.(type) { 453 case string: 454 if err := json.Unmarshal([]byte(v), &m); err != nil { 455 return nil, err 456 } 457 case []uint8: 458 if err := json.Unmarshal(v, &m); err != nil { 459 return nil, err 460 } 461 case map[string]interface{}: 462 m = v 463 } 464 465 if len(m) == 0 { 466 return nil, errors.ErrInvalidUserDataType 467 } 468 return m, nil 469 } 470 471 func (c *Claims) unpackAudience(k string, v interface{}, mkv, tkv map[string]interface{}) error { 472 switch audiences := v.(type) { 473 case string: 474 c.Audience = append(c.Audience, audiences) 475 case []interface{}: 476 for _, audience := range audiences { 477 switch audience.(type) { 478 case string: 479 c.Audience = append(c.Audience, audience.(string)) 480 default: 481 return errors.ErrInvalidAudience.WithArgs(audience) 482 } 483 } 484 case []string: 485 for _, audience := range audiences { 486 c.Audience = append(c.Audience, audience) 487 } 488 default: 489 return errors.ErrInvalidAudienceType.WithArgs(v) 490 } 491 switch len(c.Audience) { 492 case 0: 493 case 1: 494 tkv[k] = c.Audience 495 mkv[k] = c.Audience[0] 496 default: 497 tkv[k] = c.Audience 498 mkv[k] = c.Audience 499 } 500 return nil 501 } 502 503 func (c *Claims) unpackExpiresAt(k string, v interface{}, mkv map[string]interface{}) error { 504 switch exp := v.(type) { 505 case float64: 506 c.ExpiresAt = int64(exp) 507 case int: 508 c.ExpiresAt = int64(exp) 509 case int64: 510 c.ExpiresAt = exp 511 case json.Number: 512 i, _ := exp.Int64() 513 c.ExpiresAt = i 514 default: 515 return errors.ErrInvalidClaimExpiresAt.WithArgs(v) 516 } 517 mkv[k] = c.ExpiresAt 518 return nil 519 } 520 521 func (c *Claims) unpackID(k string, v interface{}, mkv, tkv map[string]interface{}) error { 522 switch v.(type) { 523 case string: 524 c.ID = v.(string) 525 default: 526 return errors.ErrInvalidIDClaimType.WithArgs(v) 527 } 528 tkv[k] = c.ID 529 mkv[k] = c.ID 530 return nil 531 } 532 533 func (c *Claims) unpackIssuedAt(k string, v interface{}, mkv map[string]interface{}) error { 534 switch exp := v.(type) { 535 case float64: 536 c.IssuedAt = int64(exp) 537 case int: 538 c.IssuedAt = int64(exp) 539 case int64: 540 c.IssuedAt = exp 541 case json.Number: 542 i, _ := exp.Int64() 543 c.IssuedAt = i 544 default: 545 return errors.ErrInvalidClaimIssuedAt.WithArgs(v) 546 } 547 mkv[k] = c.IssuedAt 548 return nil 549 } 550 551 func (c *Claims) unpackIssuer(k string, v interface{}, mkv, tkv map[string]interface{}) error { 552 switch v.(type) { 553 case string: 554 c.Issuer = v.(string) 555 default: 556 return errors.ErrInvalidIssuerClaimType.WithArgs(v) 557 } 558 tkv[k] = c.Issuer 559 mkv[k] = c.Issuer 560 return nil 561 } 562 563 func (c *Claims) unpackNotBefore(k string, v interface{}, mkv map[string]interface{}) error { 564 switch exp := v.(type) { 565 case float64: 566 c.NotBefore = int64(exp) 567 case int: 568 c.NotBefore = int64(exp) 569 case int64: 570 c.NotBefore = exp 571 case json.Number: 572 i, _ := exp.Int64() 573 c.NotBefore = i 574 default: 575 return errors.ErrInvalidClaimNotBefore.WithArgs(v) 576 } 577 mkv[k] = c.NotBefore 578 return nil 579 } 580 581 func (c *Claims) unpackSubject(k string, v interface{}, mkv, tkv map[string]interface{}) error { 582 switch v.(type) { 583 case string: 584 c.Subject = v.(string) 585 default: 586 return errors.ErrInvalidSubjectClaimType.WithArgs(v) 587 } 588 tkv[k] = c.Subject 589 mkv[k] = c.Subject 590 return nil 591 } 592 593 func (c *Claims) unpackMail(k string, v interface{}, mkv, tkv map[string]interface{}) error { 594 switch v.(type) { 595 case string: 596 c.Email = v.(string) 597 default: 598 return errors.ErrInvalidEmailClaimType.WithArgs(k, v) 599 } 600 tkv["email"] = c.Email 601 mkv["email"] = c.Email 602 return nil 603 } 604 605 func (c *Claims) unpackName(k string, v interface{}, mkv, tkv map[string]interface{}) error { 606 switch names := v.(type) { 607 case string: 608 c.Name = names 609 case []interface{}: 610 packedNames := []string{} 611 for _, n := range names { 612 switch n.(type) { 613 case string: 614 packedNames = append(packedNames, n.(string)) 615 default: 616 return errors.ErrInvalidNameClaimType.WithArgs(v) 617 } 618 } 619 c.Name = strings.Join(packedNames, " ") 620 default: 621 return errors.ErrInvalidNameClaimType.WithArgs(v) 622 } 623 tkv[k] = c.Name 624 mkv[k] = c.Name 625 return nil 626 } 627 628 func (c *Claims) unpackRoles(v interface{}) error { 629 switch roles := v.(type) { 630 case []interface{}: 631 for _, role := range roles { 632 switch role.(type) { 633 case string: 634 c.Roles = append(c.Roles, role.(string)) 635 default: 636 return errors.ErrInvalidRole.WithArgs(role) 637 } 638 } 639 case []string: 640 for _, role := range roles { 641 c.Roles = append(c.Roles, role) 642 } 643 case string: 644 for _, role := range strings.Split(roles, " ") { 645 c.Roles = append(c.Roles, role) 646 } 647 default: 648 return errors.ErrInvalidRoleType.WithArgs(v) 649 } 650 return nil 651 } 652 653 func (c *Claims) unpackScopes(k string, v interface{}, mkv, tkv map[string]interface{}) error { 654 switch scopes := v.(type) { 655 case []interface{}: 656 for _, scope := range scopes { 657 switch scope.(type) { 658 case string: 659 c.Scopes = append(c.Scopes, scope.(string)) 660 default: 661 return errors.ErrInvalidScope.WithArgs(scope) 662 } 663 } 664 case []string: 665 for _, scope := range scopes { 666 c.Scopes = append(c.Scopes, scope) 667 } 668 case string: 669 for _, scope := range strings.Split(scopes, " ") { 670 c.Scopes = append(c.Scopes, scope) 671 } 672 default: 673 return errors.ErrInvalidScopeType.WithArgs(v) 674 } 675 tkv["scopes"] = c.Scopes 676 mkv["scopes"] = strings.Join(c.Scopes, " ") 677 return nil 678 } 679 680 func (c *Claims) unpackOrg(k string, v interface{}, mkv, tkv map[string]interface{}) error { 681 switch orgs := v.(type) { 682 case []interface{}: 683 for _, org := range orgs { 684 switch org.(type) { 685 case string: 686 c.Organizations = append(c.Organizations, org.(string)) 687 default: 688 return errors.ErrInvalidOrg.WithArgs(org) 689 } 690 } 691 case []string: 692 for _, org := range orgs { 693 c.Organizations = append(c.Organizations, org) 694 } 695 case string: 696 for _, org := range strings.Split(orgs, " ") { 697 c.Organizations = append(c.Organizations, org) 698 } 699 default: 700 return errors.ErrInvalidOrgType.WithArgs(v) 701 } 702 tkv[k] = c.Organizations 703 mkv[k] = strings.Join(c.Organizations, " ") 704 return nil 705 } 706 707 func (c *Claims) unpackAddr(k string, v interface{}, mkv, tkv map[string]interface{}) error { 708 switch v.(type) { 709 case string: 710 c.Address = v.(string) 711 default: 712 return errors.ErrInvalidAddrType.WithArgs(v) 713 } 714 tkv[k] = c.Address 715 mkv[k] = c.Address 716 return nil 717 } 718 719 func (c *Claims) unpackOrigin(k string, v interface{}, mkv, tkv map[string]interface{}) error { 720 switch v.(type) { 721 case string: 722 c.Origin = v.(string) 723 default: 724 return errors.ErrInvalidOriginClaimType.WithArgs(v) 725 } 726 tkv[k] = c.Origin 727 mkv[k] = c.Origin 728 return nil 729 } 730 731 func (c *Claims) unpackPicture(k string, v interface{}, mkv, tkv map[string]interface{}) error { 732 switch v.(type) { 733 case string: 734 c.PictureURL = v.(string) 735 default: 736 return errors.ErrInvalidPictureClaimType.WithArgs(v) 737 } 738 mkv[k] = c.PictureURL 739 return nil 740 } 741 742 func (c *Claims) unpackAppMetadata(v interface{}) error { 743 switch v.(type) { 744 case map[string]interface{}: 745 appMetadata := v.(map[string]interface{}) 746 if _, authzExists := appMetadata["authorization"]; authzExists { 747 switch appMetadata["authorization"].(type) { 748 case map[string]interface{}: 749 appMetadataAuthz := appMetadata["authorization"].(map[string]interface{}) 750 if _, rolesExists := appMetadataAuthz["roles"]; rolesExists { 751 switch roles := appMetadataAuthz["roles"].(type) { 752 case []interface{}: 753 for _, role := range roles { 754 switch role.(type) { 755 case string: 756 c.Roles = append(c.Roles, role.(string)) 757 default: 758 return errors.ErrInvalidRole.WithArgs(role) 759 } 760 } 761 case []string: 762 for _, role := range roles { 763 c.Roles = append(c.Roles, role) 764 } 765 default: 766 return errors.ErrInvalidAppMetadataRoleType.WithArgs(appMetadataAuthz["roles"]) 767 } 768 } 769 } 770 } 771 } 772 return nil 773 } 774 775 func (c *Claims) unpackRealmAccess(v interface{}) error { 776 switch v.(type) { 777 case map[string]interface{}: 778 realmAccess := v.(map[string]interface{}) 779 if _, rolesExists := realmAccess["roles"]; rolesExists { 780 switch roles := realmAccess["roles"].(type) { 781 case []interface{}: 782 for _, role := range roles { 783 switch role.(type) { 784 case string: 785 c.Roles = append(c.Roles, role.(string)) 786 default: 787 return errors.ErrInvalidRole.WithArgs(role) 788 } 789 } 790 case []string: 791 for _, role := range roles { 792 c.Roles = append(c.Roles, role) 793 } 794 } 795 } 796 } 797 return nil 798 } 799 800 func (c *Claims) unpackAccessListPaths(v interface{}) error { 801 switch v.(type) { 802 case []interface{}: 803 paths := v.([]interface{}) 804 for _, path := range paths { 805 switch path.(type) { 806 case string: 807 if c.AccessList == nil { 808 c.AccessList = &AccessListClaim{} 809 } 810 if c.AccessList.Paths == nil { 811 c.AccessList.Paths = make(map[string]interface{}) 812 } 813 c.AccessList.Paths[path.(string)] = make(map[string]interface{}) 814 default: 815 return errors.ErrInvalidAccessListPath.WithArgs(path) 816 } 817 } 818 } 819 return nil 820 } 821 822 func (c *Claims) unpackAccessList(v interface{}) error { 823 switch v.(type) { 824 case map[string]interface{}: 825 acl := v.(map[string]interface{}) 826 if _, pathsExists := acl["paths"]; pathsExists { 827 switch acl["paths"].(type) { 828 case map[string]interface{}: 829 paths := acl["paths"].(map[string]interface{}) 830 for path := range paths { 831 if c.AccessList == nil { 832 c.AccessList = &AccessListClaim{} 833 } 834 if c.AccessList.Paths == nil { 835 c.AccessList.Paths = make(map[string]interface{}) 836 } 837 c.AccessList.Paths[path] = make(map[string]interface{}) 838 } 839 case []interface{}: 840 paths := acl["paths"].([]interface{}) 841 for _, path := range paths { 842 switch path.(type) { 843 case string: 844 if c.AccessList == nil { 845 c.AccessList = &AccessListClaim{} 846 } 847 if c.AccessList.Paths == nil { 848 c.AccessList.Paths = make(map[string]interface{}) 849 } 850 c.AccessList.Paths[path.(string)] = make(map[string]interface{}) 851 default: 852 return errors.ErrInvalidAccessListPath.WithArgs(path) 853 } 854 } 855 } 856 } 857 } 858 return nil 859 } 860 861 func (c *Claims) unpackMetadata(k string, v interface{}, mkv, tkv map[string]interface{}) error { 862 switch v.(type) { 863 case map[string]interface{}: 864 c.Metadata = v.(map[string]interface{}) 865 default: 866 return errors.ErrInvalidMetadataClaimType.WithArgs(v) 867 } 868 mkv[k] = c.Metadata 869 return nil 870 } 871 872 // NewUser returns a user with associated standard and custom claims. 873 func NewUser(data interface{}) (*User, error) { 874 u := &User{} 875 m, unpackErr := unpackUserData(data) 876 if unpackErr != nil { 877 return nil, unpackErr 878 } 879 c := &Claims{} 880 mkv := make(map[string]interface{}) 881 tkv := make(map[string]interface{}) 882 883 for k, v := range m { 884 switch k { 885 case "aud": 886 if err := c.unpackAudience(k, v, mkv, tkv); err != nil { 887 return nil, err 888 } 889 case "exp": 890 if err := c.unpackExpiresAt(k, v, mkv); err != nil { 891 return nil, err 892 } 893 case "jti": 894 if err := c.unpackID(k, v, mkv, tkv); err != nil { 895 return nil, err 896 } 897 case "iat": 898 if err := c.unpackIssuedAt(k, v, mkv); err != nil { 899 return nil, err 900 } 901 case "iss": 902 if err := c.unpackIssuer(k, v, mkv, tkv); err != nil { 903 return nil, err 904 } 905 case "nbf": 906 if err := c.unpackNotBefore(k, v, mkv); err != nil { 907 return nil, err 908 } 909 case "sub": 910 if err := c.unpackSubject(k, v, mkv, tkv); err != nil { 911 return nil, err 912 } 913 case "email", "mail": 914 if err := c.unpackMail(k, v, mkv, tkv); err != nil { 915 return nil, err 916 } 917 case "name": 918 if err := c.unpackName(k, v, mkv, tkv); err != nil { 919 return nil, err 920 } 921 case "roles", "role", "groups", "group": 922 if err := c.unpackRoles(v); err != nil { 923 return nil, err 924 } 925 case "scopes", "scope": 926 if err := c.unpackScopes(k, v, mkv, tkv); err != nil { 927 return nil, err 928 } 929 case "org": 930 if err := c.unpackOrg(k, v, mkv, tkv); err != nil { 931 return nil, err 932 } 933 case "addr": 934 if err := c.unpackAddr(k, v, mkv, tkv); err != nil { 935 return nil, err 936 } 937 case "origin": 938 if err := c.unpackOrigin(k, v, mkv, tkv); err != nil { 939 return nil, err 940 } 941 case "picture": 942 if err := c.unpackPicture(k, v, mkv, tkv); err != nil { 943 return nil, err 944 } 945 case "app_metadata": 946 if err := c.unpackAppMetadata(v); err != nil { 947 return nil, err 948 } 949 case "realm_access": 950 if err := c.unpackRealmAccess(v); err != nil { 951 return nil, err 952 } 953 case "paths": 954 if err := c.unpackAccessListPaths(v); err != nil { 955 return nil, err 956 } 957 case "acl": 958 if err := c.unpackAccessList(v); err != nil { 959 return nil, err 960 } 961 case "metadata": 962 if err := c.unpackMetadata(k, v, mkv, tkv); err != nil { 963 return nil, err 964 } 965 case "frontend_links", "challenges": 966 default: 967 if c.custom == nil { 968 c.custom = make(map[string]interface{}) 969 } 970 c.custom[k] = v 971 mkv[k] = v 972 } 973 } 974 975 if c.AccessList != nil && c.AccessList.Paths != nil { 976 tkv["acl"] = map[string]interface{}{ 977 "paths": c.AccessList.Paths, 978 } 979 mkv["acl"] = map[string]interface{}{ 980 "paths": c.AccessList.Paths, 981 } 982 } 983 984 if len(c.Roles) == 0 { 985 c.Roles = append(c.Roles, "anonymous") 986 c.Roles = append(c.Roles, "guest") 987 } 988 989 if (len(c.Email) > 0) && (len(c.Name) > 0) { 990 if strings.Contains(c.Name, c.Email) { 991 c.Name = strings.TrimSpace(strings.ReplaceAll(c.Name, c.Email, "")) 992 tkv["name"] = c.Name 993 mkv["name"] = c.Name 994 } 995 } 996 997 if len(c.Roles) > 0 { 998 tkv["roles"] = c.Roles 999 mkv["roles"] = c.Roles 1000 } 1001 1002 u.rkv = make(map[string]interface{}) 1003 for _, roleName := range c.Roles { 1004 u.rkv[roleName] = true 1005 } 1006 1007 u.Claims = c 1008 u.mkv = mkv 1009 u.tkv = tkv 1010 return u, nil 1011 }