001package ca.cdr.api.model.json; 002 003/* 004 * #%L 005 * Smile CDR - CDR 006 * %% 007 * Copyright (C) 2016 - 2024 Smile CDR, Inc. 008 * %% 009 * All rights reserved. 010 * #L% 011 */ 012 013import ca.cdr.api.model.enm.PermissionEnum; 014import ca.cdr.api.model.enm.UserTwoFactorAuthEnum; 015import ca.uhn.fhir.context.ConfigurationException; 016import com.fasterxml.jackson.annotation.JsonProperty; 017import com.fasterxml.jackson.annotation.JsonPropertyOrder; 018import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 019import com.fasterxml.jackson.databind.annotation.JsonSerialize; 020import io.swagger.v3.oas.annotations.Operation; 021import io.swagger.v3.oas.annotations.Parameter; 022import io.swagger.v3.oas.annotations.media.Schema; 023import jakarta.annotation.Nonnull; 024import org.apache.commons.lang3.SerializationUtils; 025import org.apache.commons.lang3.Validate; 026import org.apache.commons.lang3.builder.ToStringBuilder; 027import org.apache.commons.lang3.builder.ToStringStyle; 028import org.hl7.fhir.dstu3.model.DateType; 029import org.springframework.security.core.userdetails.UserDetails; 030 031import java.io.Serializable; 032import java.util.ArrayList; 033import java.util.Collection; 034import java.util.Date; 035import java.util.List; 036import java.util.Set; 037import java.util.TreeSet; 038import java.util.stream.Collectors; 039 040import static org.apache.commons.lang3.StringUtils.defaultString; 041import static org.apache.commons.lang3.StringUtils.isNotBlank; 042 043/** 044 * Perhaps if anyone is wondering why we have such a vague fields such as associatedResources and/or defaultLaunchContexts, in our model in the first place. 045 * It's because the SMART spec has this concept of launch contexts, where the launch context is simply the patient/encounter/location that should 046 * be opened when the app starts. 047 * And there's nothing more implied there, just "this is what you should open", so the reason could be anything: 048 * - the user is that patient 049 * - the user has picked that patient from a list 050 * - the user is the parent of the patient 051 * - etc 052 */ 053@Schema(name = "UserDetails", description = "A user definition") 054@JsonPropertyOrder({ 055 "pid", 056 "nodeId", 057 "moduleId", 058 "username", 059 "familyName", 060 "givenName", 061 "notes", 062 "lastActive", 063 "lastConnected", 064 "credentialExpiry", 065 "accountLocked", 066 "systemUser", 067 "authorities", 068 "associatedResources", 069 "defaultLaunchContexts" 070}) 071public class UserDetailsJson implements UserDetails, Cloneable, IHasAuthorities, IModelJson { 072 073 private static final long serialVersionUID = 2L; 074 075 /* 076 * ****************************************************************** 077 * Note: If you add properties, add them to the copy constructor too! 078 * ****************************************************************** 079 */ 080 081 @JsonProperty("accountDisabled") 082 @Parameter(description = "Is this account currently disabled?") 083 private Boolean myAccountDisabled; 084 085 @JsonProperty("notes") 086 @Parameter(description = "Any notes regarding this user") 087 private String myNotes; 088 089 @JsonProperty("email") 090 @Parameter(description = "The user email address") 091 private String myEmail; 092 093 @JsonProperty("accountExpiry") 094 @JsonSerialize(using = JsonDateSerializer.class) 095 @JsonDeserialize(using = JsonDateWithEmptyDeserializer.class) 096 @Parameter(description = "The expiry date for the user (if any) or null if the account should not expire") 097 private Date myAccountExpiry; 098 099 @JsonProperty("accountLocked") 100 @Parameter(description = "Is this account currently locked?") 101 private Boolean myAccountLocked; 102 103 @JsonProperty("failedLoginAttempts") 104 @Parameter(description = "Number of consecutive failed login attempts") 105 private int myFailedLoginAttempts; 106 107 @JsonProperty("authorities") 108 @Parameter(description = "Any authorities (permissions) granted to this user") 109 private List<GrantedAuthorityJson> myAuthorities; 110 111 @JsonProperty("associatedResources") 112 @Schema( 113 description = 114 "A collection of \"associated resource\" IDs. Associated resources are FHIR resources with some connection to the given user, such as a Patient or Practitioner resource representing the actual user.", 115 accessMode = Schema.AccessMode.READ_ONLY) 116 private List<AssociatedResourceJson> myAssociatedResources; 117 118 @JsonProperty("credentialExpiry") 119 @JsonSerialize(using = JsonDateSerializer.class) 120 @JsonDeserialize(using = JsonDateWithEmptyDeserializer.class) 121 @Parameter(description = "If set, provides the date that the user credentials will expire") 122 private Date myCredentialExpiry; 123 124 @JsonProperty("familyName") 125 @Parameter(description = "The user's family (last) name") 126 private String myFamilyName; 127 128 @JsonProperty("givenName") 129 @Parameter(description = "The user's given (first) name") 130 private String myGivenName; 131 132 @JsonProperty(value = "lastActive") 133 @JsonSerialize(using = JsonDateSerializer.class) 134 @JsonDeserialize(using = JsonDateDeserializer.class) 135 @Schema( 136 accessMode = Schema.AccessMode.READ_ONLY, 137 description = 138 "The date at which the user account was last used. Note that this property is read-only, and is only updated once per day, so it is accurate only to the date.") 139 private Date myLastActive; 140 141 @JsonProperty(value = "lastConnected") 142 @JsonSerialize(using = JsonDateSerializer.class) 143 @JsonDeserialize(using = JsonDateDeserializer.class) 144 @Schema( 145 accessMode = Schema.AccessMode.READ_ONLY, 146 description = 147 "The date at which the user last logged in. This property is read-only and is accurate to the minute.") 148 private Date myLastConnected; 149 150 @Schema( 151 accessMode = Schema.AccessMode.READ_ONLY, 152 description = 153 "The module ID associated with this user account. This is the module ID associated with the Inbound Security module that is responsible for authenticating this user.") 154 @JsonProperty("moduleId") 155 private String myModuleId; 156 157 @Schema( 158 accessMode = Schema.AccessMode.READ_ONLY, 159 description = 160 "The node ID associated with this user. This is the master node ID associated with the Inbound Security module that is responsible for authenticating this user.") 161 @JsonProperty("nodeId") 162 private String myNodeId; 163 164 @JsonProperty("password") 165 @Schema( 166 description = 167 "The user password (note that this property will not be populated when sessions are made available to user code)", 168 readOnly = true) 169 private String myPassword; 170 171 @JsonProperty("pid") 172 @Schema(accessMode = Schema.AccessMode.READ_ONLY, description = "The PID (internal ID) for this user") 173 private Long myPid; 174 175 @JsonProperty("username") 176 @Schema(accessMode = Schema.AccessMode.READ_ONLY, description = "The username for this user") 177 private String myUsername; 178 179 @JsonProperty("usernameNamespace") 180 @Schema(accessMode = Schema.AccessMode.READ_ONLY, description = "The username namespace associated with this user") 181 private String myUsernameNamespace; 182 183 @Schema( 184 accessMode = Schema.AccessMode.READ_ONLY, 185 description = 186 "If this is set, the user cannot be renamed or deleted (this property may only be set by the system)") 187 @JsonProperty("systemUser") 188 private boolean mySystemUser; 189 190 @Schema( 191 accessMode = Schema.AccessMode.READ_ONLY, 192 description = 193 "If this value is set, the user is backed by an external user directory (this property may only be set by the system)") 194 @JsonProperty("external") 195 private boolean myExternal; 196 197 @JsonProperty("defaultLaunchContexts") 198 @Schema( 199 accessMode = Schema.AccessMode.READ_ONLY, 200 description = "The SMART launch contexts associated with this account") 201 private List<LaunchContextJson> myDefaultLaunchContexts; 202 203 @JsonProperty("serviceAccount") 204 private Boolean myServiceAccount; 205 206 @JsonProperty("twoFactorAuthStatus") 207 private UserTwoFactorAuthEnum myTwoFactorAuthStatus; 208 209 /** 210 * Constructor 211 */ 212 public UserDetailsJson() { 213 super(); 214 } 215 216 /** 217 * Constructor 218 */ 219 public UserDetailsJson(UserDetailsJson theCopyObject) { 220 this(); 221 222 setPid(theCopyObject.getPid()); 223 224 setUsername(theCopyObject.getUsername()); 225 setUsernameNamespace(theCopyObject.getUsernameNamespace()); 226 setPassword(theCopyObject.getPassword()); 227 228 setNodeId(theCopyObject.getNodeId()); 229 setModuleId(theCopyObject.getModuleId()); 230 231 setFamilyName(theCopyObject.getFamilyName()); 232 setGivenName(theCopyObject.getGivenName()); 233 setEmail(theCopyObject.getEmail()); 234 235 setNotes(theCopyObject.getNotes()); 236 setFailedLoginAttempts(theCopyObject.getFailedLoginAttempts()); 237 setAccountExpiry(theCopyObject.getAccountExpiry()); 238 setAccountDisabled(theCopyObject.isAccountDisabled()); 239 setAccountLocked(theCopyObject.isAccountLocked()); 240 setServiceAccount(theCopyObject.isServiceAccount()); 241 setSystemUser(theCopyObject.isSystemUser()); 242 setCredentialExpiry(theCopyObject.getCredentialExpiry()); 243 setLastActive(theCopyObject.getLastActive()); 244 setLastConnected(theCopyObject.getLastConnected()); 245 246 setAuthorities(theCopyObject.getAuthorities()); 247 setExternal(theCopyObject.isExternal()); 248 249 setTwoFactorAuthStatus(theCopyObject.getTwoFactorAuthStatus()); 250 251 getDefaultLaunchContexts().addAll(theCopyObject.getDefaultLaunchContexts()); 252 getAssociatedResources().addAll(theCopyObject.getAssociatedResources()); 253 } 254 255 /* 256 * ****************************************************************** 257 * Note: If you add properties, add them to the copy constructor too! 258 * ****************************************************************** 259 */ 260 261 public UserTwoFactorAuthEnum getTwoFactorAuthStatus() { 262 return myTwoFactorAuthStatus; 263 } 264 265 public UserDetailsJson setTwoFactorAuthStatus(UserTwoFactorAuthEnum theTwoFactorAuthStatus) { 266 myTwoFactorAuthStatus = theTwoFactorAuthStatus; 267 return this; 268 } 269 270 public void addAssociatedResource(String theType, String theResourceId) { 271 Validate.notBlank(theType, "The type must not be null"); 272 AssociatedResourceTypeEnum type; 273 try { 274 type = AssociatedResourceTypeEnum.valueOf(theType); 275 } catch (Exception e) { 276 throw new ConfigurationException("Invalid type: " + theType); 277 } 278 addAssociatedResource(type, theResourceId); 279 } 280 281 public void addAssociatedResource(AssociatedResourceTypeEnum theType, String theResourceId) { 282 Validate.notNull(theType, "The type must not be null"); 283 Validate.notBlank(theResourceId, "The resource ID must not be null"); 284 285 getAssociatedResources() 286 .add(new AssociatedResourceJson() 287 .setType(AssociatedResourceTypeEnum.SELF) 288 .setResourceId(theResourceId)); 289 } 290 291 public void addAuthorities(GrantedAuthorityJson... theAuthority) { 292 for (GrantedAuthorityJson next : theAuthority) { 293 addAuthority(next); 294 } 295 } 296 297 public UserDetailsJson addAuthority(PermissionEnum thePermission) { 298 getAuthorities().add(new GrantedAuthorityJson(thePermission)); 299 return this; 300 } 301 302 public void addAuthority(GrantedAuthorityJson theAuthority) { 303 Validate.notNull(theAuthority, "theAuthority must not be null"); 304 getAuthorities().add(theAuthority); 305 } 306 307 public LaunchContextJson addDefaultLaunchContext() { 308 LaunchContextJson ctx = new LaunchContextJson(); 309 getDefaultLaunchContexts().add(ctx); 310 return ctx; 311 } 312 313 @SuppressWarnings("MethodDoesntCallSuperMethod") 314 @Override 315 public UserDetailsJson clone() { 316 return SerializationUtils.clone(this); 317 } 318 319 public Date getAccountExpiry() { 320 return myAccountExpiry; 321 } 322 323 public void setAccountExpiry(Date theAccountExpiry) { 324 myAccountExpiry = theAccountExpiry; 325 } 326 327 public boolean hasAccountExpiryDate() { 328 return myAccountExpiry != null; 329 } 330 331 public String getAccountExpiryDateAsIsoString() { 332 if (myAccountExpiry == null) { 333 return ""; 334 } 335 return new DateType(myAccountExpiry).getValueAsString(); 336 } 337 338 public List<AssociatedResourceJson> getAssociatedResources() { 339 if (myAssociatedResources == null) { 340 myAssociatedResources = new ArrayList<>(); 341 } 342 return myAssociatedResources; 343 } 344 345 public boolean hasAuthorities() { 346 return myAuthorities != null; 347 } 348 349 @Override 350 @Nonnull 351 public List<GrantedAuthorityJson> getAuthorities() { 352 if (myAuthorities == null) { 353 myAuthorities = new ArrayList<>(); 354 } 355 return myAuthorities; 356 } 357 358 public UserDetailsJson setAuthorities(Collection<GrantedAuthorityJson> theAuthorities) { 359 myAuthorities = new ArrayList<>(theAuthorities); 360 return this; 361 } 362 363 public boolean hasCredentialExpiryDate() { 364 return myCredentialExpiry != null; 365 } 366 367 public Date getCredentialExpiry() { 368 return myCredentialExpiry; 369 } 370 371 public void setCredentialExpiry(Date theCredentialExpiry) { 372 myCredentialExpiry = theCredentialExpiry; 373 } 374 375 public String getCredentialExpiryDateAsIsoString() { 376 if (myCredentialExpiry == null) { 377 return ""; 378 } 379 return new DateType(myCredentialExpiry).getValueAsString(); 380 } 381 382 public boolean hasDefaultLaunchContexts() { 383 return myDefaultLaunchContexts != null; 384 } 385 386 public List<LaunchContextJson> getDefaultLaunchContexts() { 387 if (myDefaultLaunchContexts == null) { 388 myDefaultLaunchContexts = new ArrayList<>(); 389 } 390 return myDefaultLaunchContexts; 391 } 392 393 @SuppressWarnings("unused") 394 public String getDefaultLaunchContextsCommaSeparated(String theType) { 395 StringBuilder b = new StringBuilder(); 396 getDefaultLaunchContexts().stream() 397 .filter(t -> t.getContextType().equals(theType)) 398 .filter(t -> isNotBlank(t.getResourceId())) 399 .forEach(t -> { 400 if (!b.isEmpty()) { 401 b.append(", "); 402 } 403 b.append(t.getResourceId()); 404 }); 405 return b.toString(); 406 } 407 408 public boolean hasEmail() { 409 return myEmail != null; 410 } 411 412 public String getEmail() { 413 return myEmail; 414 } 415 416 public UserDetailsJson setEmail(String theEmail) { 417 myEmail = theEmail; 418 return this; 419 } 420 421 public boolean hasFamilyName() { 422 return myFamilyName != null; 423 } 424 425 public String getFamilyName() { 426 return myFamilyName; 427 } 428 429 public UserDetailsJson setFamilyName(String theFamilyName) { 430 myFamilyName = theFamilyName; 431 return this; 432 } 433 434 public String getFullName() { 435 return (defaultString(myGivenName) + ' ' + defaultString(myFamilyName)).trim(); 436 } 437 438 public boolean hasGivenName() { 439 return myGivenName != null; 440 } 441 442 public String getGivenName() { 443 return myGivenName; 444 } 445 446 public UserDetailsJson setGivenName(String theGivenName) { 447 myGivenName = theGivenName; 448 return this; 449 } 450 451 public Date getLastActive() { 452 return myLastActive; 453 } 454 455 public void setLastActive(Date theLastActive) { 456 myLastActive = theLastActive; 457 } 458 459 public Date getLastConnected() { 460 return myLastConnected; 461 } 462 463 public void setLastConnected(Date theLastConnected) { 464 myLastConnected = theLastConnected; 465 } 466 467 public String getModuleId() { 468 return myModuleId; 469 } 470 471 public UserDetailsJson setModuleId(String theModuleId) { 472 myModuleId = theModuleId; 473 return this; 474 } 475 476 public String getNodeId() { 477 return myNodeId; 478 } 479 480 public UserDetailsJson setNodeId(String theNodeId) { 481 myNodeId = theNodeId; 482 return this; 483 } 484 485 public boolean hasNotes() { 486 return myNotes != null; 487 } 488 489 public String getNotes() { 490 return myNotes; 491 } 492 493 public void setNotes(String theNotes) { 494 myNotes = theNotes; 495 } 496 497 @Override 498 public String getPassword() { 499 return myPassword; 500 } 501 502 public UserDetailsJson setPassword(String thePassword) { 503 myPassword = thePassword; 504 return this; 505 } 506 507 public boolean hasPassword() { 508 return myPassword != null; 509 } 510 511 public Long getPid() { 512 return myPid; 513 } 514 515 public UserDetailsJson setPid(Long thePid) { 516 myPid = thePid; 517 return this; 518 } 519 520 public Set<String> getAuthorityNames() { 521 return getAuthorities().stream() 522 .map(t -> t.getPermission().name()) 523 .collect(Collectors.toCollection(TreeSet::new)); 524 } 525 526 public Boolean getServiceUser() { 527 if (myServiceAccount == null) { 528 return false; 529 } 530 return myServiceAccount; 531 } 532 533 public boolean hasUsername() { 534 return myUsername != null; 535 } 536 537 @Override 538 public String getUsername() { 539 return myUsername; 540 } 541 542 public UserDetailsJson setUsername(String theUsername) { 543 myUsername = theUsername; 544 return this; 545 } 546 547 @Override 548 public List<GrantedAuthorityJson> getPermissions() { 549 return getAuthorities(); 550 } 551 552 @Operation(summary = "hasAuthority", description = "Does the user have the given permission?") 553 public boolean hasAuthority( 554 @Parameter(name = "thePermission", description = "The name of the permission, e.g. 'ROLE_FHIR_CLIENT'") 555 String thePermission) { 556 for (GrantedAuthorityJson next : getAuthorities()) { 557 if (next.getPermission().name().equals(thePermission)) { 558 return true; 559 } 560 } 561 return false; 562 } 563 564 public boolean hasAccountDisabled() { 565 return myAccountDisabled != null; 566 } 567 568 public boolean isAccountDisabled() { 569 if (myAccountDisabled == null) { 570 return false; 571 } 572 return myAccountDisabled; 573 } 574 575 public void setAccountDisabled(boolean theAccountDisabled) { 576 myAccountDisabled = theAccountDisabled; 577 } 578 579 public boolean hasAccountLocked() { 580 return myAccountLocked != null; 581 } 582 583 public boolean isAccountLocked() { 584 if (myAccountLocked == null) { 585 return false; 586 } 587 return myAccountLocked; 588 } 589 590 public void lockAccount() { 591 setAccountLocked(true); 592 } 593 594 public void setAccountLocked(boolean theAccountLocked) { 595 myAccountLocked = theAccountLocked; 596 } 597 598 public void setFailedLoginAttempts(int theFailedLoginAttempts) { 599 myFailedLoginAttempts = theFailedLoginAttempts; 600 } 601 602 public int getFailedLoginAttempts() { 603 return myFailedLoginAttempts; 604 } 605 606 public int incrementFailedLoginAttempts() { 607 setFailedLoginAttempts(getFailedLoginAttempts() + 1); 608 return getFailedLoginAttempts(); 609 } 610 611 public void resetFailedLoginAttempts() { 612 setFailedLoginAttempts(0); 613 } 614 615 @Override 616 public boolean isAccountNonExpired() { 617 return myAccountExpiry == null || myAccountExpiry.getTime() > System.currentTimeMillis(); 618 } 619 620 @Override 621 public boolean isAccountNonLocked() { 622 return !isAccountLocked(); 623 } 624 625 @Override 626 public boolean isCredentialsNonExpired() { 627 return myCredentialExpiry == null || myCredentialExpiry.getTime() > System.currentTimeMillis(); 628 } 629 630 @Override 631 public boolean isEnabled() { 632 return !isAccountDisabled(); 633 } 634 635 public boolean isExternal() { 636 return myExternal; 637 } 638 639 public void setExternal(boolean theExternal) { 640 myExternal = theExternal; 641 } 642 643 public boolean hasServiceAccount() { 644 return myServiceAccount != null; 645 } 646 647 public boolean isServiceAccount() { 648 if (myServiceAccount == null) { 649 return false; 650 } 651 return myServiceAccount; 652 } 653 654 public void setServiceAccount(boolean theServiceAccount) { 655 myServiceAccount = theServiceAccount; 656 } 657 658 public boolean isSystemUser() { 659 return mySystemUser; 660 } 661 662 public UserDetailsJson setSystemUser(boolean theSystemUser) { 663 mySystemUser = theSystemUser; 664 return this; 665 } 666 667 @Override 668 public String toString() { 669 ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.NO_CLASS_NAME_STYLE); 670 b.append("pid", myPid); 671 b.append("username", myUsername); 672 b.append("familyName", myFamilyName); 673 b.append("givenName", myGivenName); 674 b.append("authorities", myAuthorities); 675 b.append("lastActive", myLastActive); 676 b.append("lastConnected", myLastConnected); 677 b.append("failedLoginAttempts", myFailedLoginAttempts); 678 if (myAccountDisabled != null && myAccountDisabled) { 679 b.append("accountDisabled", myAccountDisabled); 680 } 681 if (myAccountExpiry != null) { 682 b.append("accountExpiryDate", myAccountExpiry); 683 } 684 if (myAccountLocked != null && myAccountLocked) { 685 b.append("accountLocked", myAccountLocked); 686 } 687 if (myCredentialExpiry != null) { 688 b.append("credentialExpiryDate", myCredentialExpiry); 689 } 690 if (mySystemUser) { 691 b.append("systemUser", mySystemUser); 692 } 693 if (myExternal) { 694 b.append("external", myExternal); 695 } 696 b.append("nodeId", myNodeId); 697 b.append("moduleId", myModuleId); 698 return b.toString(); 699 } 700 701 public String getUsernameNamespace() { 702 return myUsernameNamespace; 703 } 704 705 public void setUsernameNamespace(String theUsernameNamespace) { 706 myUsernameNamespace = theUsernameNamespace; 707 } 708 709 public boolean hasUsernameNamespace() { 710 return myUsernameNamespace != null; 711 } 712 713 @Operation( 714 summary = "getOrCreateDefaultLaunchContext", 715 description = "Returns the first default launch context for the given type, creating one if none exists") 716 public LaunchContextJson getOrCreateDefaultLaunchContext( 717 @Parameter(name = "theContextType", description = "The context type, e.g. \"patient\" or \"practitioner\"") 718 String theContextType) { 719 for (LaunchContextJson next : getDefaultLaunchContexts()) { 720 if (next.getContextType().equals(theContextType)) { 721 return next; 722 } 723 } 724 725 LaunchContextJson retVal = new LaunchContextJson().setContextType(theContextType); 726 getDefaultLaunchContexts().add(retVal); 727 return retVal; 728 } 729 730 @Operation( 731 summary = "getOrCreateDefaultLaunchContext", 732 description = "Returns the first default launch context for the given type, creating one if none exists") 733 public LaunchContextJson getOrCreateDefaultLaunchContext( 734 @Parameter(name = "theContextType", description = "The context type, e.g. \"patient\" or \"practitioner\"") 735 String theContextType, 736 @Parameter(name = "theIndex", description = "The index, starting at 0") int theIndex) { 737 738 int foundIndex = 0; 739 for (LaunchContextJson next : getDefaultLaunchContexts()) { 740 if (next.getContextType().equals(theContextType)) { 741 if (theIndex == foundIndex) { 742 return next; 743 } else { 744 foundIndex++; 745 } 746 } 747 } 748 749 LaunchContextJson retVal = new LaunchContextJson().setContextType(theContextType); 750 getDefaultLaunchContexts().add(retVal); 751 return retVal; 752 } 753 754 /** 755 * What type of relationship does the user have to the 756 * given resource 757 */ 758 public enum AssociatedResourceTypeEnum { 759 760 /** 761 * This associated resource is the user themself 762 */ 763 SELF 764 } 765 766 /** 767 * This structure represents a link between a user in the auth database 768 * and a resource in the FHIR database. This can be used, for example, 769 * to specify that a particular user is a specific Patient in the 770 * CDR. That linkage can then be applied in order to make 771 * security/permission decisions. 772 */ 773 @Schema( 774 name = "AssociatedResource", 775 description = "This structure represents a link between a user in the auth database " 776 + "and a resource in the FHIR database. This can be used, for example, " 777 + "to specify that a particular user is a specific Patient in the " 778 + "CDR. That linkage can then be applied in order to make " 779 + "security/permission decisions.") 780 public static class AssociatedResourceJson implements Serializable, IModelJson { 781 782 @JsonProperty("type") 783 @Schema(description = "The relationship between the user and the resource") 784 private AssociatedResourceTypeEnum myType; 785 786 @JsonProperty("resourceId") 787 @Schema(description = "The resource ID itself, e.g. 'Patient/123'", example = "Patient/123") 788 private String myResourceId; 789 790 public String getResourceId() { 791 return myResourceId; 792 } 793 794 public AssociatedResourceJson setResourceId(String theResourceId) { 795 myResourceId = theResourceId; 796 return this; 797 } 798 799 public AssociatedResourceTypeEnum getType() { 800 return myType; 801 } 802 803 public AssociatedResourceJson setType(String theType) { 804 myType = AssociatedResourceTypeEnum.valueOf(theType); 805 return this; 806 } 807 808 public AssociatedResourceJson setType(AssociatedResourceTypeEnum theType) { 809 myType = theType; 810 return this; 811 } 812 } 813 814 public static PermissionEnum toPermissionEnum(String thePermission) { 815 try { 816 return PermissionEnum.valueOf(thePermission); 817 } catch (Exception e) { 818 throw new ConfigurationException("Invalid permission name: " + thePermission); 819 } 820 } 821}