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