001package ca.uhn.fhir.rest.server.interceptor.auth;
002
003/*-
004 * #%L
005 * HAPI FHIR - Server Framework
006 * %%
007 * Copyright (C) 2014 - 2023 Smile CDR, Inc.
008 * %%
009 * Licensed under the Apache License, Version 2.0 (the "License");
010 * you may not use this file except in compliance with the License.
011 * You may obtain a copy of the License at
012 *
013 *      http://www.apache.org/licenses/LICENSE-2.0
014 *
015 * Unless required by applicable law or agreed to in writing, software
016 * distributed under the License is distributed on an "AS IS" BASIS,
017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
018 * See the License for the specific language governing permissions and
019 * limitations under the License.
020 * #L%
021 */
022
023import ca.uhn.fhir.rest.api.server.RequestDetails;
024import org.apache.commons.lang3.Validate;
025
026import javax.annotation.Nonnull;
027import javax.annotation.Nullable;
028import java.util.ArrayList;
029import java.util.List;
030
031/**
032 * Return type for {@link SearchNarrowingInterceptor#buildAuthorizedList(RequestDetails)}
033 */
034public class AuthorizedList {
035
036        private List<String> myAllowedCompartments;
037        private List<String> myAllowedInstances;
038        private List<AllowedCodeInValueSet> myAllowedCodeInValueSets;
039
040        @Nullable
041        List<String> getAllowedCompartments() {
042                return myAllowedCompartments;
043        }
044
045        @Nullable
046        List<AllowedCodeInValueSet> getAllowedCodeInValueSets() {
047                return myAllowedCodeInValueSets;
048        }
049
050        @Nullable
051        List<String> getAllowedInstances() {
052                return myAllowedInstances;
053        }
054
055        /**
056         * Adds a compartment that the user should be allowed to access
057         *
058         * @param theCompartment The compartment name, e.g. "Patient/123" (in this example the user would be allowed to access Patient/123 as well as Observations where Observation.subject="Patient/123"m, etc.
059         * @return Returns <code>this</code> for easy method chaining
060         */
061        public AuthorizedList addCompartment(String theCompartment) {
062                Validate.notNull(theCompartment, "theCompartment must not be null");
063                if (myAllowedCompartments == null) {
064                        myAllowedCompartments = new ArrayList<>();
065                }
066                myAllowedCompartments.add(theCompartment);
067
068                return this;
069        }
070
071        /**
072         * Adds a compartment that the user should be allowed to access
073         *
074         * @param theCompartments The compartment names, e.g. "Patient/123" (in this example the user would be allowed to access Patient/123 as well as Observations where Observation.subject="Patient/123"m, etc.
075         * @return Returns <code>this</code> for easy method chaining
076         */
077        public AuthorizedList addCompartments(String... theCompartments) {
078                Validate.notNull(theCompartments, "theCompartments must not be null");
079                for (String next : theCompartments) {
080                        addCompartment(next);
081                }
082                return this;
083        }
084
085        /**
086         * Adds a resource that the user should be allowed to access
087         *
088         * @param theResource The resource name, e.g. "Patient/123" (in this example the user would be allowed to access Patient/123 but not Observations where Observation.subject="Patient/123"m, etc.
089         * @return Returns <code>this</code> for easy method chaining
090         */
091        public AuthorizedList addResource(String theResource) {
092                Validate.notNull(theResource, "theResource must not be null");
093                if (myAllowedInstances == null) {
094                        myAllowedInstances = new ArrayList<>();
095                }
096                myAllowedInstances.add(theResource);
097
098                return this;
099        }
100
101        /**
102         * Adds a resource that the user should be allowed to access
103         *
104         * @param theResources The resource names, e.g. "Patient/123" (in this example the user would be allowed to access Patient/123 but not Observations where Observation.subject="Patient/123"m, etc.
105         * @return Returns <code>this</code> for easy method chaining
106         */
107        public AuthorizedList addResources(String... theResources) {
108                Validate.notNull(theResources, "theResources must not be null");
109                for (String next : theResources) {
110                        addResource(next);
111                }
112                return this;
113        }
114
115        /**
116         * If specified, any search for <code>theResourceName</code> will automatically include a parameter indicating that
117         * the token search parameter <code>theSearchParameterName</code> must have a value in the ValueSet with URL <code>theValueSetUrl</code>.
118         *
119         * @param theResourceName        The resource name, e.g. <code>Observation</code>
120         * @param theSearchParameterName The search parameter name, e.g. <code>code</code>
121         * @param theValueSetUrl         The valueset URL, e.g. <code>http://my-value-set</code>
122         * @return Returns a reference to <code>this</code> for easy chaining
123         * @see AuthorizationInterceptor If search narrowing by code is being used for security reasons, consider also using AuthorizationInterceptor as a failsafe to ensure that no inapproproiate resources are returned
124         * @since 6.0.0
125         */
126        public AuthorizedList addCodeInValueSet(@Nonnull String theResourceName, @Nonnull String theSearchParameterName, @Nonnull String theValueSetUrl) {
127                Validate.notBlank(theResourceName, "theResourceName must not be missing or null");
128                Validate.notBlank(theSearchParameterName, "theSearchParameterName must not be missing or null");
129                Validate.notBlank(theValueSetUrl, "theResourceUrl must not be missing or null");
130
131                return doAddCodeInValueSet(theResourceName, theSearchParameterName, theValueSetUrl, false);
132        }
133
134        /**
135         * If specified, any search for <code>theResourceName</code> will automatically include a parameter indicating that
136         * the token search parameter <code>theSearchParameterName</code> must have a value not in the ValueSet with URL <code>theValueSetUrl</code>.
137         *
138         * @param theResourceName        The resource name, e.g. <code>Observation</code>
139         * @param theSearchParameterName The search parameter name, e.g. <code>code</code>
140         * @param theValueSetUrl         The valueset URL, e.g. <code>http://my-value-set</code>
141         * @return Returns a reference to <code>this</code> for easy chaining
142         * @see AuthorizationInterceptor If search narrowing by code is being used for security reasons, consider also using AuthorizationInterceptor as a failsafe to ensure that no inapproproiate resources are returned
143         * @since 6.0.0
144         */
145        public AuthorizedList addCodeNotInValueSet(@Nonnull String theResourceName, @Nonnull String theSearchParameterName, @Nonnull String theValueSetUrl) {
146                Validate.notBlank(theResourceName, "theResourceName must not be missing or null");
147                Validate.notBlank(theSearchParameterName, "theSearchParameterName must not be missing or null");
148                Validate.notBlank(theValueSetUrl, "theResourceUrl must not be missing or null");
149
150                return doAddCodeInValueSet(theResourceName, theSearchParameterName, theValueSetUrl, true);
151        }
152
153        private AuthorizedList doAddCodeInValueSet(String theResourceName, String theSearchParameterName, String theValueSetUrl, boolean negate) {
154                if (myAllowedCodeInValueSets == null) {
155                        myAllowedCodeInValueSets = new ArrayList<>();
156                }
157                myAllowedCodeInValueSets.add(new AllowedCodeInValueSet(theResourceName, theSearchParameterName, theValueSetUrl, negate));
158
159                return this;
160        }
161}