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