001package ca.uhn.fhir.rest.server.interceptor.auth;
002
003/*
004 * #%L
005 * HAPI FHIR - Core Library
006 * %%
007 * Copyright (C) 2014 - 2017 University Health Network
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 */
022import java.util.*;
023
024import org.apache.commons.lang3.Validate;
025import org.hl7.fhir.instance.model.api.IBaseResource;
026import org.hl7.fhir.instance.model.api.IIdType;
027
028import ca.uhn.fhir.model.primitive.IdDt;
029import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
030
031public class RuleBuilder implements IAuthRuleBuilder {
032
033        private ArrayList<IAuthRule> myRules;
034
035        public RuleBuilder() {
036                myRules = new ArrayList<IAuthRule>();
037        }
038
039        @Override
040        public IAuthRuleBuilderRule allow() {
041                return allow(null);
042        }
043
044        @Override
045        public IAuthRuleBuilderRule allow(String theRuleName) {
046                return new RuleBuilderRule(PolicyEnum.ALLOW, theRuleName);
047        }
048
049        @Override
050        public IAuthRuleBuilderRuleOpClassifierFinished allowAll() {
051                return allowAll(null);
052        }
053
054        @Override
055        public IAuthRuleBuilderRuleOpClassifierFinished allowAll(String theRuleName) {
056                myRules.add(new RuleImplOp(theRuleName).setOp(RuleOpEnum.ALLOW_ALL));
057                return new RuleBuilderFinished();
058        }
059
060        @Override
061        public List<IAuthRule> build() {
062                return myRules;
063        }
064
065        @Override
066        public IAuthRuleBuilderRule deny() {
067                return deny(null);
068        }
069
070        @Override
071        public IAuthRuleBuilderRule deny(String theRuleName) {
072                return new RuleBuilderRule(PolicyEnum.DENY, theRuleName);
073        }
074
075        @Override
076        public IAuthRuleBuilderRuleOpClassifierFinished denyAll() {
077                return denyAll(null);
078        }
079
080        @Override
081        public IAuthRuleBuilderRuleOpClassifierFinished denyAll(String theRuleName) {
082                myRules.add(new RuleImplOp(theRuleName).setOp(RuleOpEnum.DENY_ALL));
083                return new RuleBuilderFinished();
084        }
085
086        private class RuleBuilderFinished implements IAuthRuleFinished, IAuthRuleBuilderRuleOpClassifierFinished {
087
088                @Override
089                public IAuthRuleBuilder andThen() {
090                        doBuildRule();
091                        return RuleBuilder.this;
092                }
093
094                @Override
095                public List<IAuthRule> build() {
096                        doBuildRule();
097                        return myRules;
098                }
099
100                /**
101                 * Subclasses may override
102                 */
103                protected void doBuildRule() {
104                        // nothing
105                }
106        }
107
108        private class RuleBuilderRule implements IAuthRuleBuilderRule {
109
110                private PolicyEnum myRuleMode;
111                private String myRuleName;
112                private RuleOpEnum myRuleOp;
113
114                public RuleBuilderRule(PolicyEnum theRuleMode, String theRuleName) {
115                        myRuleMode = theRuleMode;
116                        myRuleName = theRuleName;
117                }
118
119                @Override
120                public IAuthRuleBuilderRuleConditional createConditional() {
121                        return new RuleBuilderRuleConditional(RestOperationTypeEnum.CREATE);
122                }
123
124                @Override
125                public IAuthRuleBuilderRuleOp delete() {
126                        myRuleOp = RuleOpEnum.DELETE;
127                        return new RuleBuilderRuleOp();
128                }
129
130                @Override
131                public IAuthRuleBuilderRuleConditional deleteConditional() {
132                        return new RuleBuilderRuleConditional(RestOperationTypeEnum.DELETE);
133                }
134
135                @Override
136                public RuleBuilderFinished metadata() {
137                        RuleImplOp rule = new RuleImplOp(myRuleName);
138                        rule.setOp(RuleOpEnum.METADATA);
139                        rule.setMode(myRuleMode);
140                        myRules.add(rule);
141                        return new RuleBuilderFinished();
142                }
143
144                @Override
145                public IAuthRuleBuilderOperation operation() {
146                        return new RuleBuilderRuleOperation();
147                }
148
149                @Override
150                public IAuthRuleBuilderRuleOp read() {
151                        myRuleOp = RuleOpEnum.READ;
152                        return new RuleBuilderRuleOp();
153                }
154
155                @Override
156                public IAuthRuleBuilderRuleTransaction transaction() {
157                        myRuleOp = RuleOpEnum.TRANSACTION;
158                        return new RuleBuilderRuleTransaction();
159                }
160
161                @Override
162                public IAuthRuleBuilderRuleConditional updateConditional() {
163                        return new RuleBuilderRuleConditional(RestOperationTypeEnum.UPDATE);
164                }
165
166                @Override
167                public IAuthRuleBuilderRuleOp write() {
168                        myRuleOp = RuleOpEnum.WRITE;
169                        return new RuleBuilderRuleOp();
170                }
171
172                private class RuleBuilderRuleConditional implements IAuthRuleBuilderRuleConditional {
173
174                        private AppliesTypeEnum myAppliesTo;
175
176                        private Set<?> myAppliesToTypes;
177                        private RestOperationTypeEnum myOperationType;
178
179                        public RuleBuilderRuleConditional(RestOperationTypeEnum theOperationType) {
180                                myOperationType = theOperationType;
181                        }
182
183                        @Override
184                        public IAuthRuleBuilderRuleConditionalClassifier allResources() {
185                                myAppliesTo = AppliesTypeEnum.ALL_RESOURCES;
186                                return new RuleBuilderRuleConditionalClassifier();
187                        }
188
189                        @Override
190                        public IAuthRuleBuilderRuleConditionalClassifier resourcesOfType(Class<? extends IBaseResource> theType) {
191                                Validate.notNull(theType, "theType must not be null");
192                                myAppliesTo = AppliesTypeEnum.TYPES;
193                                myAppliesToTypes = Collections.singleton(theType);
194                                return new RuleBuilderRuleConditionalClassifier();
195                        }
196
197                        public class RuleBuilderRuleConditionalClassifier extends RuleBuilderFinished implements IAuthRuleBuilderRuleConditionalClassifier {
198
199                                @Override
200                                protected void doBuildRule() {
201                                        RuleImplConditional rule = new RuleImplConditional(myRuleName);
202                                        rule.setMode(myRuleMode);
203                                        rule.setOperationType(myOperationType);
204                                        rule.setAppliesTo(myAppliesTo);
205                                        rule.setAppliesToTypes(myAppliesToTypes);
206                                        myRules.add(rule);
207
208                                }
209                        }
210
211                }
212
213                private class RuleBuilderRuleOp implements IAuthRuleBuilderRuleOp {
214
215                        private AppliesTypeEnum myAppliesTo;
216                        private Set<?> myAppliesToTypes;
217
218                        @Override
219                        public IAuthRuleBuilderRuleOpClassifier allResources() {
220                                myAppliesTo = AppliesTypeEnum.ALL_RESOURCES;
221                                return new RuleBuilderRuleOpClassifier();
222                        }
223
224                        @Override
225                        public IAuthRuleBuilderRuleOpClassifier resourcesOfType(Class<? extends IBaseResource> theType) {
226                                Validate.notNull(theType, "theType must not be null");
227                                myAppliesTo = AppliesTypeEnum.TYPES;
228                                myAppliesToTypes = Collections.singleton(theType);
229                                return new RuleBuilderRuleOpClassifier();
230                        }
231
232                        private class RuleBuilderRuleOpClassifier implements IAuthRuleBuilderRuleOpClassifier {
233
234                                private ClassifierTypeEnum myClassifierType;
235                                private String myInCompartmentName;
236                                private Collection<? extends IIdType> myInCompartmentOwners;
237                                private List<IIdType> myAppliesToInstances;
238
239                                /**
240                                 * Constructor
241                                 */
242                                public RuleBuilderRuleOpClassifier() {
243                                        super();
244                                }
245
246                                /**
247                                 * Constructor
248                                 */
249                                public RuleBuilderRuleOpClassifier(List<IIdType> theAppliesToInstances) {
250                                        myAppliesToInstances = theAppliesToInstances;
251                                        myAppliesTo = AppliesTypeEnum.INSTANCES;
252                                }
253
254                                private IAuthRuleBuilderRuleOpClassifierFinished finished() {
255
256                                        RuleImplOp rule = new RuleImplOp(myRuleName);
257                                        rule.setMode(myRuleMode);
258                                        rule.setOp(myRuleOp);
259                                        rule.setAppliesTo(myAppliesTo);
260                                        rule.setAppliesToTypes(myAppliesToTypes);
261                                        rule.setAppliesToInstances(myAppliesToInstances);
262                                        rule.setClassifierType(myClassifierType);
263                                        rule.setClassifierCompartmentName(myInCompartmentName);
264                                        rule.setClassifierCompartmentOwners(myInCompartmentOwners);
265                                        myRules.add(rule);
266
267                                        return new RuleBuilderFinished();
268                                }
269
270                                @Override
271                                public IAuthRuleBuilderRuleOpClassifierFinished inCompartment(String theCompartmentName, Collection<? extends IIdType> theOwners) {
272                                        Validate.notBlank(theCompartmentName, "theCompartmentName must not be null");
273                                        Validate.notNull(theOwners, "theOwners must not be null");
274                                        Validate.noNullElements(theOwners, "theOwners must not contain any null elements");
275                                        for (IIdType next : theOwners) {
276                                                validateOwner(next);
277                                        }
278                                        myClassifierType = ClassifierTypeEnum.IN_COMPARTMENT;
279                                        return finished();
280                                }
281
282                                @Override
283                                public IAuthRuleBuilderRuleOpClassifierFinished inCompartment(String theCompartmentName, IIdType theOwner) {
284                                        Validate.notBlank(theCompartmentName, "theCompartmentName must not be null");
285                                        Validate.notNull(theOwner, "theOwner must not be null");
286                                        validateOwner(theOwner);
287                                        myInCompartmentName = theCompartmentName;
288                                        myInCompartmentOwners = Collections.singletonList(theOwner);
289                                        myClassifierType = ClassifierTypeEnum.IN_COMPARTMENT;
290                                        return finished();
291                                }
292
293                                private void validateOwner(IIdType theOwner) {
294                                        Validate.notBlank(theOwner.getIdPart(), "owner.getIdPart() must not be null or empty");
295                                        Validate.notBlank(theOwner.getIdPart(), "owner.getResourceType() must not be null or empty");
296                                }
297
298                                @Override
299                                public IAuthRuleBuilderRuleOpClassifierFinished withAnyId() {
300                                        myClassifierType = ClassifierTypeEnum.ANY_ID;
301                                        return finished();
302                                }
303
304                        }
305
306                        @Override
307                        public IAuthRuleFinished instance(String theId) {
308                                Validate.notBlank(theId, "theId must not be null or empty");
309                                return instance(new IdDt(theId));
310                        }
311
312                        @Override
313                        public IAuthRuleFinished instance(IIdType theId) {
314                                Validate.notNull(theId, "theId must not be null");
315                                Validate.notBlank(theId.getValue(), "theId.getValue() must not be null or empty");
316                                Validate.notBlank(theId.getIdPart(), "theId must contain an ID part");
317
318                                return new RuleBuilderRuleOpClassifier(Arrays.asList(theId)).finished();
319                        }
320
321                }
322
323                private class RuleBuilderRuleOperation implements IAuthRuleBuilderOperation {
324
325                        @Override
326                        public IAuthRuleBuilderOperationNamed named(String theOperationName) {
327                                Validate.notBlank(theOperationName, "theOperationName must not be null or empty");
328                                return new RuleBuilderRuleOperationNamed(theOperationName);
329                        }
330
331                        @Override
332                        public IAuthRuleBuilderOperationNamed withAnyName() {
333                                return new RuleBuilderRuleOperationNamed(null);
334                        }
335
336                        private class RuleBuilderRuleOperationNamed implements IAuthRuleBuilderOperationNamed {
337
338                                private String myOperationName;
339
340                                public RuleBuilderRuleOperationNamed(String theOperationName) {
341                                        if (theOperationName != null && !theOperationName.startsWith("$")) {
342                                                myOperationName = '$' + theOperationName;
343                                        } else {
344                                                myOperationName = theOperationName;
345                                        }
346                                }
347
348                                private OperationRule createRule() {
349                                        OperationRule rule = new OperationRule(myRuleName);
350                                        rule.setOperationName(myOperationName);
351                                        rule.setMode(myRuleMode);
352                                        return rule;
353                                }
354
355                                @Override
356                                public IAuthRuleBuilderRuleOpClassifierFinished onInstance(IIdType theInstanceId) {
357                                        Validate.notNull(theInstanceId, "theInstanceId must not be null");
358                                        Validate.notBlank(theInstanceId.getResourceType(), "theInstanceId does not have a resource type");
359                                        Validate.notBlank(theInstanceId.getIdPart(), "theInstanceId does not have an ID part");
360
361                                        OperationRule rule = createRule();
362                                        ArrayList<IIdType> ids = new ArrayList<IIdType>();
363                                        ids.add(theInstanceId);
364                                        rule.appliesToInstances(ids);
365                                        myRules.add(rule);
366                                        return new RuleBuilderFinished();
367                                }
368
369                                @Override
370                                public IAuthRuleBuilderRuleOpClassifierFinished onServer() {
371                                        OperationRule rule = createRule();
372                                        rule.appliesToServer();
373                                        myRules.add(rule);
374                                        return new RuleBuilderFinished();
375                                }
376
377                                @Override
378                                public IAuthRuleBuilderRuleOpClassifierFinished onType(Class<? extends IBaseResource> theType) {
379                                        validateType(theType);
380
381                                        OperationRule rule = createRule();
382                                        rule.appliesToTypes(toTypeSet(theType));
383                                        myRules.add(rule);
384                                        return new RuleBuilderFinished();
385                                }
386
387                                private void validateType(Class<? extends IBaseResource> theType) {
388                                        Validate.notNull(theType, "theType must not be null");
389                                }
390
391                                @Override
392                                public IAuthRuleBuilderRuleOpClassifierFinished onInstancesOfType(Class<? extends IBaseResource> theType) {
393                                        validateType(theType);
394
395                                        OperationRule rule = createRule();
396                                        rule.appliesToInstancesOfType(toTypeSet(theType));
397                                        myRules.add(rule);
398                                        return new RuleBuilderFinished();
399                                }
400
401                                private HashSet<Class<? extends IBaseResource>> toTypeSet(Class<? extends IBaseResource> theType) {
402                                        HashSet<Class<? extends IBaseResource>> appliesToTypes = new HashSet<Class<? extends IBaseResource>>();
403                                        appliesToTypes.add(theType);
404                                        return appliesToTypes;
405                                }
406
407                                @Override
408                                public IAuthRuleFinished onAnyType() {
409                                        OperationRule rule = createRule();
410                                        rule.appliesToAnyType();
411                                        myRules.add(rule);
412                                        return new RuleBuilderFinished();
413                                }
414
415                                @Override
416                                public IAuthRuleFinished onAnyInstance() {
417                                        OperationRule rule = createRule();
418                                        rule.appliesToAnyInstance();
419                                        myRules.add(rule);
420                                        return new RuleBuilderFinished();
421                                }
422
423                        }
424
425                }
426
427                private class RuleBuilderRuleTransaction implements IAuthRuleBuilderRuleTransaction {
428
429                        @Override
430                        public IAuthRuleBuilderRuleTransactionOp withAnyOperation() {
431                                return new RuleBuilderRuleTransactionOp();
432                        }
433
434                        private class RuleBuilderRuleTransactionOp implements IAuthRuleBuilderRuleTransactionOp {
435
436                                @Override
437                                public IAuthRuleBuilderRuleOpClassifierFinished andApplyNormalRules() {
438                                        RuleImplOp rule = new RuleImplOp(myRuleName);
439                                        rule.setMode(myRuleMode);
440                                        rule.setOp(myRuleOp);
441                                        rule.setTransactionAppliesToOp(TransactionAppliesToEnum.ANY_OPERATION);
442                                        myRules.add(rule);
443                                        return new RuleBuilderFinished();
444                                }
445
446                        }
447
448                }
449
450        }
451
452}