001package ca.cdr.api.model.json;
002
003/*
004 * #%L
005 * Smile CDR - CDR
006 * %%
007 * Copyright (C) 2016 - 2026 Smile CDR, Inc.
008 * %%
009 * All rights reserved.
010 * #L%
011 */
012
013import ca.cdr.api.model.enm.PermissionEnum;
014import ca.uhn.fhir.model.api.IModelJson;
015import com.fasterxml.jackson.annotation.JsonIgnore;
016import com.fasterxml.jackson.annotation.JsonProperty;
017import io.swagger.v3.oas.annotations.media.Schema;
018import jakarta.annotation.Nonnull;
019import org.apache.commons.lang3.builder.EqualsBuilder;
020import org.apache.commons.lang3.builder.HashCodeBuilder;
021import org.slf4j.Logger;
022import org.slf4j.LoggerFactory;
023import org.springframework.security.core.GrantedAuthority;
024
025import java.io.Serial;
026import java.util.Objects;
027
028import static org.apache.commons.lang3.StringUtils.*;
029
030@Schema(
031                name = "GrantedAuthority",
032                description =
033                                "A granted authority is a single user authority (permission) that has been granted to a user. This authority has a permission name, and optionally an argument.")
034public class GrantedAuthorityJson implements GrantedAuthority, IModelJson, Comparable<GrantedAuthorityJson> {
035
036        @Serial
037        private static final long serialVersionUID = 1L;
038
039        private static final Logger ourLog = LoggerFactory.getLogger(GrantedAuthorityJson.class);
040
041        @JsonProperty("permission")
042        @Schema(
043                        description =
044                                        "The name of the permission. See [permissions](/docs/security/roles_and_permissions.html) for "
045                                                        + "information on available permissions.",
046                        accessMode = Schema.AccessMode.READ_ONLY)
047        private PermissionEnum myPermission;
048
049        @JsonProperty("argument")
050        @Schema(
051                        description =
052                                        "The argument for this authority. Note that some permissions do not take an argument while "
053                                                        + "others require an argument. Consult the [permission](/docs/security/roles_and_permissions.html) documentation for more information.",
054                        accessMode = Schema.AccessMode.READ_ONLY)
055        private String myArgument;
056
057        @JsonIgnore
058        private transient Integer myHashCode;
059
060        /**
061         * Constructor
062         */
063        public GrantedAuthorityJson() {
064                super();
065        }
066
067        /**
068         * Constructor
069         */
070        public GrantedAuthorityJson(PermissionEnum thePermission) {
071                this(thePermission, null);
072        }
073
074        /**
075         * Constructor
076         */
077        public GrantedAuthorityJson(PermissionEnum thePermission, String theArgument) {
078                super();
079                myPermission = thePermission;
080                myArgument = theArgument;
081                if (isNotBlank(theArgument)) {
082                        if (!myPermission.isTakesArgument()) {
083                                ourLog.warn("Permission {} does not take any argument", thePermission);
084                        }
085                } else {
086                        if (myPermission.isTakesArgument() && !myPermission.isArgumentOptional()) {
087                                ourLog.warn("Permission {} requires an argument", thePermission);
088                        }
089                }
090        }
091
092        @Override
093        public boolean equals(Object theObj) {
094                if (!(theObj instanceof GrantedAuthorityJson obj)) {
095                        return false;
096                }
097
098                EqualsBuilder b = new EqualsBuilder();
099                b.append(myPermission, obj.myPermission);
100                b.append(Objects.toString(myArgument, ""), Objects.toString(obj.myArgument, ""));
101                return b.isEquals();
102        }
103
104        public String getArgument() {
105                return Objects.toString(myArgument, null);
106        }
107
108        @Override
109        public String getAuthority() {
110                return myPermission.name();
111        }
112
113        public PermissionEnum getPermission() {
114                return myPermission;
115        }
116
117        @Override
118        public int hashCode() {
119                Integer retVal = myHashCode;
120                if (retVal == null) {
121                        HashCodeBuilder b = new HashCodeBuilder();
122                        b.append(myPermission);
123                        b.append(Objects.toString(myArgument, ""));
124                        retVal = b.toHashCode();
125                        myHashCode = retVal;
126                }
127                return retVal;
128        }
129
130        @Override
131        public String toString() {
132                if (isBlank(myArgument)) {
133                        return myPermission.name();
134                } else {
135                        return myPermission.name() + "/" + myArgument;
136                }
137        }
138
139        /**
140         * Create a copy of this authority with the same permission but a different argument
141         */
142        @SuppressWarnings("unused")
143        public GrantedAuthorityJson withArgument(String theArgument) {
144                return new GrantedAuthorityJson(myPermission, theArgument);
145        }
146
147        @Override
148        public int compareTo(@Nonnull GrantedAuthorityJson theOther) {
149                return toString().compareTo(theOther.toString());
150        }
151
152        /**
153         * @param theValue E.g. "ROLE_FHIR_CLIENT" or "FHIR_READ_ALL_IN_COMPARTMENT/Patient/123"
154         * @throws IllegalArgumentException If the permission is invalid
155         */
156        public static GrantedAuthorityJson parse(String theValue) throws IllegalArgumentException {
157                String permName;
158                String argument;
159                int slashIdx = theValue.indexOf('/');
160                if (slashIdx == -1) {
161                        permName = theValue;
162                        argument = null;
163                } else {
164                        permName = theValue.substring(0, slashIdx);
165                        argument = theValue.substring(slashIdx + 1);
166                }
167
168                PermissionEnum permission;
169                try {
170                        permission = PermissionEnum.valueOf(permName);
171                } catch (Exception e) {
172                        throw new IllegalArgumentException("Unknown permission: " + permName);
173                }
174
175                argument = defaultIfBlank(argument, null);
176
177                return new GrantedAuthorityJson(permission, argument);
178        }
179}