
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}