001package ca.uhn.fhir.rest.server.interceptor.auth;
002
003/*
004 * #%L
005 * HAPI FHIR - Server Framework
006 * %%
007 * Copyright (C) 2014 - 2022 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.interceptor.model.RequestPartitionId;
024import ca.uhn.fhir.model.api.annotation.ResourceDef;
025import ca.uhn.fhir.model.primitive.IdDt;
026import ca.uhn.fhir.rest.api.Constants;
027import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
028import ca.uhn.fhir.rest.api.server.RequestDetails;
029import com.google.common.collect.Lists;
030import org.apache.commons.lang3.Validate;
031import org.hl7.fhir.instance.model.api.IBaseResource;
032import org.hl7.fhir.instance.model.api.IIdType;
033
034import javax.annotation.Nonnull;
035import java.util.ArrayList;
036import java.util.Arrays;
037import java.util.Collection;
038import java.util.Collections;
039import java.util.HashSet;
040import java.util.List;
041import java.util.Optional;
042import java.util.Set;
043import java.util.concurrent.ConcurrentHashMap;
044
045import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
046
047public class RuleBuilder implements IAuthRuleBuilder {
048
049        private static final ConcurrentHashMap<Class<? extends IBaseResource>, String> ourTypeToName = new ConcurrentHashMap<>();
050        private final ArrayList<IAuthRule> myRules;
051        private IAuthRuleBuilderRule myAllow;
052        private IAuthRuleBuilderRule myDeny;
053
054        public RuleBuilder() {
055                myRules = new ArrayList<>();
056        }
057
058        @Override
059        public IAuthRuleBuilderRule allow() {
060                if (myAllow == null) {
061                        myAllow = allow(null);
062                }
063                return myAllow;
064        }
065
066        @Override
067        public IAuthRuleBuilderRule allow(String theRuleName) {
068                return new RuleBuilderRule(PolicyEnum.ALLOW, theRuleName);
069        }
070
071        @Override
072        public IAuthRuleBuilderRuleOpClassifierFinished allowAll() {
073                return allowAll(null);
074        }
075
076        @Override
077        public IAuthRuleBuilderRuleOpClassifierFinished allowAll(String theRuleName) {
078                RuleImplOp rule = new RuleImplOp(theRuleName);
079                rule.setOp(RuleOpEnum.ALL);
080                rule.setMode(PolicyEnum.ALLOW);
081                myRules.add(rule);
082                return new RuleBuilderFinished(rule);
083        }
084
085        @Override
086        public List<IAuthRule> build() {
087                return myRules;
088        }
089
090        @Override
091        public IAuthRuleBuilderRule deny() {
092                if (myDeny == null) {
093                        myDeny = deny(null);
094                }
095                return myDeny;
096        }
097
098        @Override
099        public IAuthRuleBuilderRule deny(String theRuleName) {
100                return new RuleBuilderRule(PolicyEnum.DENY, theRuleName);
101        }
102
103        @Override
104        public IAuthRuleBuilderRuleOpClassifierFinished denyAll() {
105                return denyAll(null);
106        }
107
108        @Override
109        public IAuthRuleBuilderRuleOpClassifierFinished denyAll(String theRuleName) {
110                RuleImplOp rule = new RuleImplOp(theRuleName);
111                rule.setOp(RuleOpEnum.ALL);
112                rule.setMode(PolicyEnum.DENY);
113                myRules.add(rule);
114                return new RuleBuilderFinished(rule);
115        }
116
117        private class RuleBuilderFinished implements IAuthRuleFinished, IAuthRuleBuilderRuleOpClassifierFinished, IAuthRuleBuilderRuleOpClassifierFinishedWithTenantId {
118
119                protected final BaseRule myOpRule;
120                private List<IAuthRuleTester> myTesters;
121
122                RuleBuilderFinished(BaseRule theRule) {
123                        assert theRule != null;
124                        myOpRule = theRule;
125                }
126
127                @Override
128                public IAuthRuleBuilder andThen() {
129                        doBuildRule();
130                        return RuleBuilder.this;
131                }
132
133                @Override
134                public List<IAuthRule> build() {
135                        doBuildRule();
136                        return myRules;
137                }
138
139                /**
140                 * Subclasses may override
141                 */
142                protected void doBuildRule() {
143                        // nothing
144                }
145
146                @Override
147                public IAuthRuleBuilderRuleOpClassifierFinishedWithTenantId forTenantIds(String... theTenantIds) {
148                        return forTenantIds(Arrays.asList(defaultIfNull(theTenantIds, Constants.EMPTY_STRING_ARRAY)));
149                }
150
151                @Override
152                public IAuthRuleBuilderRuleOpClassifierFinishedWithTenantId forTenantIds(final Collection<String> theTenantIds) {
153                        withTester(new TenantCheckingTester(theTenantIds, true));
154                        return this;
155                }
156
157                List<IAuthRuleTester> getTesters() {
158                        if (myTesters == null) {
159                                return Collections.emptyList();
160                        }
161                        return myTesters;
162                }
163
164                @Override
165                public IAuthRuleBuilderRuleOpClassifierFinishedWithTenantId notForTenantIds(String... theTenantIds) {
166                        return notForTenantIds(Arrays.asList(defaultIfNull(theTenantIds, Constants.EMPTY_STRING_ARRAY)));
167                }
168
169                @Override
170                public IAuthRuleBuilderRuleOpClassifierFinishedWithTenantId notForTenantIds(final Collection<String> theTenantIds) {
171                        withTester(new TenantCheckingTester(theTenantIds, false));
172                        return this;
173                }
174
175                @Override
176                public IAuthRuleFinished withTester(IAuthRuleTester theTester) {
177                        if (theTester != null) {
178                                if (myTesters == null) {
179                                        myTesters = new ArrayList<>();
180                                }
181                                myTesters.add(theTester);
182                                myOpRule.addTester(theTester);
183                        }
184
185                        return this;
186                }
187
188                private class TenantCheckingTester implements IAuthRuleTester {
189                        private final Collection<String> myTenantIds;
190                        private final boolean myOutcome;
191
192                        public TenantCheckingTester(Collection<String> theTenantIds, boolean theOutcome) {
193                                myTenantIds = theTenantIds;
194                                myOutcome = theOutcome;
195                        }
196
197                        @Override
198                        public boolean matches(RestOperationTypeEnum theOperation, RequestDetails theRequestDetails, IIdType theInputResourceId, IBaseResource theInputResource) {
199                                if (!myTenantIds.contains(theRequestDetails.getTenantId())) {
200                                        return !myOutcome;
201                                }
202
203                                return matchesResource(theInputResource);
204                        }
205
206                        @Override
207                        public boolean matchesOutput(RestOperationTypeEnum theOperation, RequestDetails theRequestDetails, IBaseResource theOutputResource) {
208                                if (!myTenantIds.contains(theRequestDetails.getTenantId())) {
209                                        return !myOutcome;
210                                }
211
212                                return matchesResource(theOutputResource);
213                        }
214
215                        private boolean matchesResource(IBaseResource theResource) {
216                                if (theResource != null) {
217                                        RequestPartitionId partitionId = (RequestPartitionId) theResource.getUserData(Constants.RESOURCE_PARTITION_ID);
218                                        if (partitionId != null) {
219                                                String partitionNameOrNull = partitionId.getFirstPartitionNameOrNull();
220                                                if (partitionNameOrNull == null || !myTenantIds.contains(partitionNameOrNull)) {
221                                                        return !myOutcome;
222                                                }
223                                        }
224                                }
225
226                                return myOutcome;
227                        }
228                }
229        }
230
231        private class RuleBuilderRule implements IAuthRuleBuilderRule {
232
233                private final PolicyEnum myRuleMode;
234                private final String myRuleName;
235                private RuleBuilderRuleOp myReadRuleBuilder;
236                private RuleBuilderRuleOp myWriteRuleBuilder;
237
238                RuleBuilderRule(PolicyEnum theRuleMode, String theRuleName) {
239                        myRuleMode = theRuleMode;
240                        myRuleName = theRuleName;
241                }
242
243                @Override
244                public IAuthRuleBuilderRuleConditional createConditional() {
245                        return new RuleBuilderRuleConditional(RestOperationTypeEnum.CREATE);
246                }
247
248                @Override
249                public IAuthRuleBuilderRuleOpDelete delete() {
250                        return new RuleBuilderRuleOp(RuleOpEnum.DELETE);
251                }
252
253                @Override
254                public IAuthRuleBuilderRuleConditional deleteConditional() {
255                        return new RuleBuilderRuleConditional(RestOperationTypeEnum.DELETE);
256                }
257
258                @Override
259                public RuleBuilderFinished metadata() {
260                        RuleImplOp rule = new RuleImplOp(myRuleName);
261                        rule.setOp(RuleOpEnum.METADATA);
262                        rule.setMode(myRuleMode);
263                        myRules.add(rule);
264                        return new RuleBuilderFinished(rule);
265                }
266
267                @Override
268                public IAuthRuleBuilderOperation operation() {
269                        return new RuleBuilderRuleOperation();
270                }
271
272                @Override
273                public IAuthRuleBuilderPatch patch() {
274                        return new PatchBuilder();
275                }
276
277                @Override
278                public IAuthRuleBuilderRuleOp read() {
279                        if (myReadRuleBuilder == null) {
280                                myReadRuleBuilder = new RuleBuilderRuleOp(RuleOpEnum.READ);
281                        }
282                        return myReadRuleBuilder;
283                }
284
285                @Override
286                public IAuthRuleBuilderRuleTransaction transaction() {
287                        return new RuleBuilderRuleTransaction();
288                }
289
290                @Override
291                public IAuthRuleBuilderRuleConditional updateConditional() {
292                        return new RuleBuilderRuleConditional(RestOperationTypeEnum.UPDATE);
293                }
294
295                @Override
296                public IAuthRuleBuilderRuleOp write() {
297                        if (myWriteRuleBuilder == null) {
298                                myWriteRuleBuilder = new RuleBuilderRuleOp(RuleOpEnum.WRITE);
299                        }
300                        return myWriteRuleBuilder;
301                }
302
303                @Override
304                public IAuthRuleBuilderRuleOp create() {
305                        if (myWriteRuleBuilder == null) {
306                                myWriteRuleBuilder = new RuleBuilderRuleOp(RuleOpEnum.CREATE);
307                        }
308                        return myWriteRuleBuilder;
309                }
310
311                @Override
312                public IAuthRuleBuilderGraphQL graphQL() {
313                        return new RuleBuilderGraphQL();
314                }
315
316                @Override
317                public IAuthRuleBuilderRuleBulkExport bulkExport() {
318                        return new RuleBuilderBulkExport();
319                }
320
321                private class RuleBuilderRuleConditional implements IAuthRuleBuilderRuleConditional {
322
323                        private AppliesTypeEnum myAppliesTo;
324                        private Set<String> myAppliesToTypes;
325                        private final RestOperationTypeEnum myOperationType;
326
327                        RuleBuilderRuleConditional(RestOperationTypeEnum theOperationType) {
328                                myOperationType = theOperationType;
329                        }
330
331                        @Override
332                        public IAuthRuleBuilderRuleConditionalClassifier allResources() {
333                                myAppliesTo = AppliesTypeEnum.ALL_RESOURCES;
334                                return new RuleBuilderRuleConditionalClassifier();
335                        }
336
337                        @Override
338                        public IAuthRuleBuilderRuleConditionalClassifier resourcesOfType(Class<? extends IBaseResource> theType) {
339                                Validate.notNull(theType, "theType must not be null");
340
341                                String typeName = toTypeName(theType);
342                                return resourcesOfType(typeName);
343                        }
344
345                        @Override
346                        public IAuthRuleBuilderRuleConditionalClassifier resourcesOfType(String theType) {
347                                myAppliesTo = AppliesTypeEnum.TYPES;
348                                myAppliesToTypes = Collections.singleton(theType);
349                                return new RuleBuilderRuleConditionalClassifier();
350                        }
351
352                        public class RuleBuilderRuleConditionalClassifier extends RuleBuilderFinished implements IAuthRuleBuilderRuleConditionalClassifier {
353
354                                RuleBuilderRuleConditionalClassifier() {
355                                        super(new RuleImplConditional(myRuleName));
356                                }
357
358                                @Override
359                                protected void doBuildRule() {
360                                        RuleImplConditional rule = (RuleImplConditional) myOpRule;
361                                        rule.setMode(myRuleMode);
362                                        rule.setOperationType(myOperationType);
363                                        rule.setAppliesTo(myAppliesTo);
364                                        rule.setAppliesToTypes(myAppliesToTypes);
365                                        rule.addTesters(getTesters());
366                                        myRules.add(rule);
367
368                                }
369                        }
370
371                }
372
373                private class RuleBuilderRuleOp implements IAuthRuleBuilderRuleOp, IAuthRuleBuilderRuleOpDelete {
374
375                        private final RuleOpEnum myRuleOp;
376                        private RuleBuilderRuleOpClassifier myInstancesBuilder;
377                        private boolean myOnCascade;
378                        private boolean myOnExpunge;
379
380                        RuleBuilderRuleOp(RuleOpEnum theRuleOp) {
381                                myRuleOp = theRuleOp;
382                        }
383
384                        @Override
385                        public IAuthRuleBuilderRuleOpClassifier allResources() {
386                                return new RuleBuilderRuleOpClassifier(AppliesTypeEnum.ALL_RESOURCES, null);
387                        }
388
389                        @Override
390                        public IAuthRuleFinished instance(String theId) {
391                                Validate.notBlank(theId, "theId must not be null or empty");
392                                return instance(new IdDt(theId));
393                        }
394
395                        @Override
396                        public IAuthRuleFinished instance(IIdType theId) {
397                                Validate.notNull(theId, "theId must not be null");
398                                Validate.notBlank(theId.getValue(), "theId.getValue() must not be null or empty");
399                                Validate.notBlank(theId.getIdPart(), "theId must contain an ID part");
400
401                                List<IIdType> instances = Lists.newArrayList(theId);
402                                return instances(instances);
403                        }
404
405                        @Override
406                        public RuleBuilderFinished instances(Collection<IIdType> theInstances) {
407                                Validate.notNull(theInstances, "theInstances must not be null");
408                                Validate.notEmpty(theInstances, "theInstances must not be empty");
409
410                                if (myInstancesBuilder == null) {
411                                        RuleBuilderRuleOpClassifier instancesBuilder = new RuleBuilderRuleOpClassifier(theInstances);
412                                        myInstancesBuilder = instancesBuilder;
413                                        return instancesBuilder.finished();
414                                } else {
415                                        return myInstancesBuilder.addInstances(theInstances);
416                                }
417                        }
418
419
420                        @Override
421                        public IAuthRuleBuilderRuleOpClassifier resourcesOfType(Class<? extends IBaseResource> theType) {
422                                Validate.notNull(theType, "theType must not be null");
423                                String resourceName = toTypeName(theType);
424                                return resourcesOfType(resourceName);
425                        }
426
427                        @Override
428                        public IAuthRuleBuilderRuleOpClassifier resourcesOfType(String theType) {
429                                Validate.notNull(theType, "theType must not be null");
430                                return new RuleBuilderRuleOpClassifier(AppliesTypeEnum.TYPES, Collections.singleton(theType));
431                        }
432
433                        @Override
434                        public IAuthRuleBuilderRuleOp onCascade() {
435                                myOnCascade = true;
436                                return this;
437                        }
438
439                        @Override
440                        public IAuthRuleBuilderRuleOp onExpunge() {
441                                myOnExpunge = true;
442                                return this;
443                        }
444
445                        private class RuleBuilderRuleOpClassifier implements IAuthRuleBuilderRuleOpClassifier {
446
447                                private final AppliesTypeEnum myAppliesTo;
448                                private final Set<String> myAppliesToTypes;
449                                private ClassifierTypeEnum myClassifierType;
450                                private String myInCompartmentName;
451                                private Collection<? extends IIdType> myInCompartmentOwners;
452                                private Collection<IIdType> myAppliesToInstances;
453                                private RuleImplOp myRule;
454                                private AdditionalCompartmentSearchParameters myAdditionalSearchParamsForCompartmentTypes = new AdditionalCompartmentSearchParameters();
455
456                                /**
457                                 * Constructor
458                                 */
459                                RuleBuilderRuleOpClassifier(AppliesTypeEnum theAppliesTo, Set<String> theAppliesToTypes) {
460                                        super();
461                                        myAppliesTo = theAppliesTo;
462                                        myAppliesToTypes = theAppliesToTypes;
463                                }
464
465                                /**
466                                 * Constructor
467                                 */
468                                RuleBuilderRuleOpClassifier(Collection<IIdType> theAppliesToInstances) {
469                                        myAppliesToInstances = theAppliesToInstances;
470                                        myAppliesTo = AppliesTypeEnum.INSTANCES;
471                                        myAppliesToTypes = null;
472                                }
473
474                                private RuleBuilderFinished finished() {
475                                        Validate.isTrue(myRule == null, "Can not call finished() twice");
476                                        myRule = new RuleImplOp(myRuleName);
477                                        myRule.setMode(myRuleMode);
478                                        myRule.setOp(myRuleOp);
479                                        myRule.setAppliesTo(myAppliesTo);
480                                        myRule.setAppliesToTypes(myAppliesToTypes);
481                                        myRule.setAppliesToInstances(myAppliesToInstances);
482                                        myRule.setClassifierType(myClassifierType);
483                                        myRule.setClassifierCompartmentName(myInCompartmentName);
484                                        myRule.setClassifierCompartmentOwners(myInCompartmentOwners);
485                                        myRule.setAppliesToDeleteCascade(myOnCascade);
486                                        myRule.setAppliesToDeleteExpunge(myOnExpunge);
487                                        myRule.setAdditionalSearchParamsForCompartmentTypes(myAdditionalSearchParamsForCompartmentTypes);
488                                        myRules.add(myRule);
489
490                                        return new RuleBuilderFinished(myRule);
491                                }
492
493                                @Override
494                                public IAuthRuleBuilderRuleOpClassifierFinished inCompartment(String theCompartmentName, Collection<? extends IIdType> theOwners) {
495                                        return inCompartmentWithAdditionalSearchParams(theCompartmentName, theOwners, new AdditionalCompartmentSearchParameters());
496                                }
497
498                                @Override
499                                public IAuthRuleBuilderRuleOpClassifierFinished inCompartmentWithAdditionalSearchParams(String theCompartmentName, Collection<? extends IIdType> theOwners, AdditionalCompartmentSearchParameters theAdditionalTypeSearchParams) {
500                                        Validate.notBlank(theCompartmentName, "theCompartmentName must not be null");
501                                        Validate.notNull(theOwners, "theOwners must not be null");
502                                        Validate.noNullElements(theOwners, "theOwners must not contain any null elements");
503                                        for (IIdType next : theOwners) {
504                                                validateOwner(next);
505                                        }
506                                        myInCompartmentName = theCompartmentName;
507                                        myInCompartmentOwners = theOwners;
508                                        myAdditionalSearchParamsForCompartmentTypes = theAdditionalTypeSearchParams;
509                                        myClassifierType = ClassifierTypeEnum.IN_COMPARTMENT;
510                                        return finished();
511                                }
512
513                                @Override
514                                public IAuthRuleBuilderRuleOpClassifierFinished inCompartment(String theCompartmentName, IIdType theOwner) {
515                                        return inCompartmentWithAdditionalSearchParams(theCompartmentName, theOwner, new AdditionalCompartmentSearchParameters());
516                                }
517
518                                @Override
519                                public IAuthRuleBuilderRuleOpClassifierFinished inCompartmentWithAdditionalSearchParams(String theCompartmentName, IIdType theOwner, AdditionalCompartmentSearchParameters theAdditionalTypeSearchParamNames) {
520                                        Validate.notBlank(theCompartmentName, "theCompartmentName must not be null");
521                                        Validate.notNull(theOwner, "theOwner must not be null");
522                                        validateOwner(theOwner);
523                                        myClassifierType = ClassifierTypeEnum.IN_COMPARTMENT;
524                                        myInCompartmentName = theCompartmentName;
525                                        myAdditionalSearchParamsForCompartmentTypes = theAdditionalTypeSearchParamNames;
526                                        Optional<RuleImplOp> oRule = findMatchingRule();
527                                        if (oRule.isPresent()) {
528                                                RuleImplOp rule = oRule.get();
529                                                rule.setAdditionalSearchParamsForCompartmentTypes(myAdditionalSearchParamsForCompartmentTypes);
530                                                rule.addClassifierCompartmentOwner(theOwner);
531                                                return new RuleBuilderFinished(rule);
532                                        }
533                                        myInCompartmentOwners = Collections.singletonList(theOwner);
534                                        return finished();
535                                }
536
537
538                                private Optional<RuleImplOp> findMatchingRule() {
539                                        return myRules.stream()
540                                                .filter(RuleImplOp.class::isInstance)
541                                                .map(RuleImplOp.class::cast)
542                                                .filter(rule -> rule.matches(myRuleOp, myAppliesTo, myAppliesToInstances, myAppliesToTypes, myClassifierType, myInCompartmentName))
543                                                .findFirst();
544                                }
545
546                                private void validateOwner(IIdType theOwner) {
547                                        Validate.notBlank(theOwner.getIdPart(), "owner.getIdPart() must not be null or empty");
548                                        Validate.notBlank(theOwner.getIdPart(), "owner.getResourceType() must not be null or empty");
549                                }
550
551                                @Override
552                                public IAuthRuleBuilderRuleOpClassifierFinished withAnyId() {
553                                        myClassifierType = ClassifierTypeEnum.ANY_ID;
554                                        return finished();
555                                }
556
557                                RuleBuilderFinished addInstances(Collection<IIdType> theInstances) {
558                                        myAppliesToInstances.addAll(theInstances);
559                                        return new RuleBuilderFinished(myRule);
560                                }
561                        }
562
563                }
564
565                private class RuleBuilderRuleOperation implements IAuthRuleBuilderOperation {
566
567                        @Override
568                        public IAuthRuleBuilderOperationNamed named(String theOperationName) {
569                                Validate.notBlank(theOperationName, "theOperationName must not be null or empty");
570                                return new RuleBuilderRuleOperationNamed(theOperationName);
571                        }
572
573                        @Override
574                        public IAuthRuleBuilderOperationNamed withAnyName() {
575                                return new RuleBuilderRuleOperationNamed(null);
576                        }
577
578                        private class RuleBuilderRuleOperationNamed implements IAuthRuleBuilderOperationNamed {
579
580                                private final String myOperationName;
581
582                                RuleBuilderRuleOperationNamed(String theOperationName) {
583                                        if (theOperationName != null && !theOperationName.startsWith("$")) {
584                                                myOperationName = '$' + theOperationName;
585                                        } else {
586                                                myOperationName = theOperationName;
587                                        }
588                                }
589
590                                private OperationRule createRule() {
591                                        OperationRule rule = new OperationRule(myRuleName);
592                                        rule.setOperationName(myOperationName);
593                                        rule.setMode(myRuleMode);
594                                        return rule;
595                                }
596
597                                @Override
598                                public IAuthRuleBuilderOperationNamedAndScoped onAnyInstance() {
599                                        OperationRule rule = createRule();
600                                        rule.appliesToAnyInstance();
601                                        return new RuleBuilderOperationNamedAndScoped(rule);
602                                }
603
604                                @Override
605                                public IAuthRuleBuilderOperationNamedAndScoped atAnyLevel() {
606                                        OperationRule rule = createRule();
607                                        rule.appliesAtAnyLevel(true);
608                                        return new RuleBuilderOperationNamedAndScoped(rule);
609                                }
610
611                                @Override
612                                public IAuthRuleBuilderOperationNamedAndScoped onAnyType() {
613                                        OperationRule rule = createRule();
614                                        rule.appliesToAnyType();
615                                        return new RuleBuilderOperationNamedAndScoped(rule);
616                                }
617
618                                @Override
619                                public IAuthRuleBuilderOperationNamedAndScoped onInstance(IIdType theInstanceId) {
620                                        Validate.notNull(theInstanceId, "theInstanceId must not be null");
621                                        Validate.notBlank(theInstanceId.getResourceType(), "theInstanceId does not have a resource type");
622                                        Validate.notBlank(theInstanceId.getIdPart(), "theInstanceId does not have an ID part");
623
624                                        OperationRule rule = createRule();
625                                        ArrayList<IIdType> ids = new ArrayList<>();
626                                        ids.add(theInstanceId);
627                                        rule.appliesToInstances(ids);
628                                        return new RuleBuilderOperationNamedAndScoped(rule);
629                                }
630
631                                @Override
632                                public IAuthRuleBuilderOperationNamedAndScoped onInstancesOfType(Class<? extends IBaseResource> theType) {
633                                        validateType(theType);
634
635                                        OperationRule rule = createRule();
636                                        rule.appliesToInstancesOfType(toTypeSet(theType));
637                                        return new RuleBuilderOperationNamedAndScoped(rule);
638                                }
639
640                                @Override
641                                public IAuthRuleBuilderOperationNamedAndScoped onServer() {
642                                        OperationRule rule = createRule();
643                                        rule.appliesToServer();
644                                        return new RuleBuilderOperationNamedAndScoped(rule);
645                                }
646
647                                @Override
648                                public IAuthRuleBuilderOperationNamedAndScoped onType(Class<? extends IBaseResource> theType) {
649                                        validateType(theType);
650
651                                        OperationRule rule = createRule();
652                                        rule.appliesToTypes(toTypeSet(theType));
653                                        return new RuleBuilderOperationNamedAndScoped(rule);
654                                }
655
656                                private HashSet<Class<? extends IBaseResource>> toTypeSet(Class<? extends IBaseResource> theType) {
657                                        HashSet<Class<? extends IBaseResource>> appliesToTypes = new HashSet<>();
658                                        appliesToTypes.add(theType);
659                                        return appliesToTypes;
660                                }
661
662                                private void validateType(Class<? extends IBaseResource> theType) {
663                                        Validate.notNull(theType, "theType must not be null");
664                                }
665
666                                private class RuleBuilderOperationNamedAndScoped implements IAuthRuleBuilderOperationNamedAndScoped {
667
668                                        private final OperationRule myRule;
669
670                                        RuleBuilderOperationNamedAndScoped(OperationRule theRule) {
671                                                myRule = theRule;
672                                        }
673
674                                        @Override
675                                        public IAuthRuleBuilderRuleOpClassifierFinished andAllowAllResponses() {
676                                                myRule.allowAllResponses();
677                                                myRules.add(myRule);
678                                                return new RuleBuilderFinished(myRule);
679                                        }
680
681                                        @Override
682                                        public IAuthRuleBuilderRuleOpClassifierFinished andRequireExplicitResponseAuthorization() {
683                                                myRules.add(myRule);
684                                                return new RuleBuilderFinished(myRule);
685                                        }
686                                }
687
688                        }
689
690                }
691
692                private class RuleBuilderRuleTransaction implements IAuthRuleBuilderRuleTransaction {
693
694                        @Override
695                        public IAuthRuleBuilderRuleTransactionOp withAnyOperation() {
696                                return new RuleBuilderRuleTransactionOp();
697                        }
698
699                        private class RuleBuilderRuleTransactionOp implements IAuthRuleBuilderRuleTransactionOp {
700
701                                @Override
702                                public IAuthRuleBuilderRuleOpClassifierFinished andApplyNormalRules() {
703                                        // Allow transaction
704                                        RuleImplOp rule = new RuleImplOp(myRuleName);
705                                        rule.setMode(myRuleMode);
706                                        rule.setOp(RuleOpEnum.TRANSACTION);
707                                        rule.setTransactionAppliesToOp(TransactionAppliesToEnum.ANY_OPERATION);
708                                        myRules.add(rule);
709                                        return new RuleBuilderFinished(rule);
710                                }
711
712                        }
713
714                }
715
716                private class PatchBuilder implements IAuthRuleBuilderPatch {
717
718                        PatchBuilder() {
719                                super();
720                        }
721
722                        @Override
723                        public IAuthRuleFinished allRequests() {
724                                BaseRule rule = new RuleImplPatch(myRuleName)
725                                        .setAllRequests(true)
726                                        .setMode(myRuleMode);
727                                myRules.add(rule);
728                                return new RuleBuilderFinished(rule);
729                        }
730                }
731
732                private class RuleBuilderGraphQL implements IAuthRuleBuilderGraphQL {
733                        @Override
734                        public IAuthRuleFinished any() {
735                                RuleImplOp rule = new RuleImplOp(myRuleName);
736                                rule.setOp(RuleOpEnum.GRAPHQL);
737                                rule.setMode(myRuleMode);
738                                myRules.add(rule);
739                                return new RuleBuilderFinished(rule);
740                        }
741                }
742
743                private class RuleBuilderBulkExport implements IAuthRuleBuilderRuleBulkExport {
744
745                        @Override
746                        public IAuthRuleBuilderRuleBulkExportWithTarget groupExportOnGroup(@Nonnull String theFocusResourceId) {
747                                RuleBulkExportImpl rule = new RuleBulkExportImpl(myRuleName);
748                                rule.setAppliesToGroupExportOnGroup(theFocusResourceId);
749                                rule.setMode(myRuleMode);
750                                myRules.add(rule);
751
752                                return new RuleBuilderBulkExportWithTarget(rule);
753                        }
754
755                        @Override
756                        public IAuthRuleBuilderRuleBulkExportWithTarget patientExportOnGroup(@Nonnull String theFocusResourceId) {
757                                RuleBulkExportImpl rule = new RuleBulkExportImpl(myRuleName);
758                                rule.setAppliesToPatientExportOnGroup(theFocusResourceId);
759                                rule.setMode(myRuleMode);
760                                myRules.add(rule);
761
762                                return new RuleBuilderBulkExportWithTarget(rule);
763                        }
764
765                        @Override
766                        public IAuthRuleBuilderRuleBulkExportWithTarget systemExport() {
767                                RuleBulkExportImpl rule = new RuleBulkExportImpl(myRuleName);
768                                rule.setAppliesToSystem();
769                                rule.setMode(myRuleMode);
770                                myRules.add(rule);
771
772                                return new RuleBuilderBulkExportWithTarget(rule);
773                        }
774
775                        @Override
776                        public IAuthRuleBuilderRuleBulkExportWithTarget any() {
777                                RuleBulkExportImpl rule = new RuleBulkExportImpl(myRuleName);
778                                rule.setAppliesToAny();
779                                rule.setMode(myRuleMode);
780                                myRules.add(rule);
781
782                                return new RuleBuilderBulkExportWithTarget(rule);
783                        }
784
785                        private class RuleBuilderBulkExportWithTarget extends RuleBuilderFinished implements IAuthRuleBuilderRuleBulkExportWithTarget {
786                                private final RuleBulkExportImpl myRule;
787
788                                private RuleBuilderBulkExportWithTarget(RuleBulkExportImpl theRule) {
789                                        super(theRule);
790                                        myRule = theRule;
791
792                                }
793
794                                @Override
795                                public IAuthRuleBuilderRuleBulkExportWithTarget withResourceTypes(Collection<String> theResourceTypes) {
796                                        myRule.setResourceTypes(theResourceTypes);
797                                        return this;
798                                }
799                        }
800                }
801        }
802
803        private static String toTypeName(Class<? extends IBaseResource> theType) {
804                String retVal = ourTypeToName.get(theType);
805                if (retVal == null) {
806                        ResourceDef resourceDef = theType.getAnnotation(ResourceDef.class);
807                        retVal = resourceDef.name();
808                        Validate.notBlank(retVal, "Could not determine resource type of class %s", theType);
809                        ourTypeToName.put(theType, retVal);
810                }
811                return retVal;
812        }
813
814}