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.consent; 021 022import ca.uhn.fhir.rest.api.server.RequestDetails; 023import jakarta.annotation.Nonnull; 024import org.hl7.fhir.instance.model.api.IBaseResource; 025 026import java.util.Collection; 027import java.util.List; 028import java.util.function.Function; 029import java.util.stream.Stream; 030 031/** 032 * IConsentService combiner over several delegates with pluggable combination strategy 033 */ 034public class MultiDelegateConsentService implements IConsentService { 035 private final Collection<IConsentService> myDelegates; 036 private final Function<Stream<ConsentOutcome>, ConsentOutcome> myVoteCombiner; 037 038 /** 039 * Combine several consent services allowing any to veto. 040 */ 041 public static @Nonnull MultiDelegateConsentService withParallelVoting( 042 @Nonnull List<IConsentService> theDelegateConsentServices) { 043 return new MultiDelegateConsentService(ConsentOutcome::parallelReduce, theDelegateConsentServices); 044 } 045 046 /** 047 * Combine several consent services with first non-PROCEED vote win. 048 */ 049 public static @Nonnull MultiDelegateConsentService withSerialVoting( 050 @Nonnull List<IConsentService> theDelegateConsentServices) { 051 return new MultiDelegateConsentService(ConsentOutcome::serialReduce, theDelegateConsentServices); 052 } 053 054 private MultiDelegateConsentService( 055 Function<Stream<ConsentOutcome>, ConsentOutcome> theVoteCombiner, 056 Collection<IConsentService> theDelegates) { 057 myVoteCombiner = theVoteCombiner; 058 myDelegates = theDelegates; 059 } 060 061 @Override 062 public ConsentOutcome startOperation(RequestDetails theRequestDetails, IConsentContextServices theContextServices) { 063 return myVoteCombiner.apply(myDelegates.stream() 064 .map(nextDelegate -> nextDelegate.startOperation(theRequestDetails, theContextServices))); 065 } 066 067 /** 068 * @return true if any of the delegates return true. 069 */ 070 @Override 071 public boolean shouldProcessCanSeeResource( 072 RequestDetails theRequestDetails, IConsentContextServices theContextServices) { 073 return myDelegates.stream() 074 .map(nextDelegate -> nextDelegate.shouldProcessCanSeeResource(theRequestDetails, theContextServices)) 075 .filter(nextShould -> nextShould) 076 .findFirst() 077 .orElse(Boolean.FALSE); 078 } 079 080 @Override 081 public ConsentOutcome canSeeResource( 082 RequestDetails theRequestDetails, IBaseResource theResource, IConsentContextServices theContextServices) { 083 return myVoteCombiner.apply(myDelegates.stream() 084 .map(nextDelegate -> nextDelegate.canSeeResource(theRequestDetails, theResource, theContextServices))); 085 } 086 087 @Override 088 public ConsentOutcome willSeeResource( 089 RequestDetails theRequestDetails, IBaseResource theResource, IConsentContextServices theContextServices) { 090 return myVoteCombiner.apply(myDelegates.stream() 091 .map(nextDelegate -> nextDelegate.willSeeResource(theRequestDetails, theResource, theContextServices))); 092 } 093}