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}