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