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 onInstances(Collection<IIdType> theInstanceIds) { 741 Validate.notNull(theInstanceIds, "theInstanceIds must not be null"); 742 theInstanceIds.forEach(instanceId -> Validate.notBlank( 743 instanceId.getResourceType(), 744 "at least one of theInstanceIds does not have a resource type")); 745 theInstanceIds.forEach(instanceId -> Validate.notBlank( 746 instanceId.getIdPart(), "at least one of theInstanceIds does not have an ID part")); 747 748 final OperationRule rule = createRule(); 749 rule.appliesToInstances(new ArrayList<>(theInstanceIds)); 750 return new RuleBuilderOperationNamedAndScoped(rule); 751 } 752 753 @Override 754 public IAuthRuleBuilderOperationNamedAndScoped onInstancesOfType( 755 Class<? extends IBaseResource> theType) { 756 validateType(theType); 757 758 OperationRule rule = createRule(); 759 rule.appliesToInstancesOfType(toTypeSet(theType)); 760 return new RuleBuilderOperationNamedAndScoped(rule); 761 } 762 763 @Override 764 public IAuthRuleBuilderOperationNamedAndScoped onServer() { 765 OperationRule rule = createRule(); 766 rule.appliesToServer(); 767 return new RuleBuilderOperationNamedAndScoped(rule); 768 } 769 770 @Override 771 public IAuthRuleBuilderOperationNamedAndScoped onType(Class<? extends IBaseResource> theType) { 772 validateType(theType); 773 774 OperationRule rule = createRule(); 775 rule.appliesToTypes(toTypeSet(theType)); 776 return new RuleBuilderOperationNamedAndScoped(rule); 777 } 778 779 private HashSet<Class<? extends IBaseResource>> toTypeSet(Class<? extends IBaseResource> theType) { 780 HashSet<Class<? extends IBaseResource>> appliesToTypes = new HashSet<>(); 781 appliesToTypes.add(theType); 782 return appliesToTypes; 783 } 784 785 private void validateType(Class<? extends IBaseResource> theType) { 786 Validate.notNull(theType, "theType must not be null"); 787 } 788 789 private class RuleBuilderOperationNamedAndScoped implements IAuthRuleBuilderOperationNamedAndScoped { 790 791 private final OperationRule myRule; 792 793 RuleBuilderOperationNamedAndScoped(OperationRule theRule) { 794 myRule = theRule; 795 } 796 797 @Override 798 public IAuthRuleBuilderRuleOpClassifierFinished andAllowAllResponses() { 799 myRule.allowAllResponses(); 800 myRules.add(myRule); 801 return new RuleBuilderFinished(myRule); 802 } 803 804 @Override 805 public IAuthRuleBuilderRuleOpClassifierFinished andAllowAllResponsesWithAllResourcesAccess() { 806 myRule.allowAllResponses(); 807 myRule.allowAllResourcesAccess(); 808 myRules.add(myRule); 809 return new RuleBuilderFinished(myRule); 810 } 811 812 @Override 813 public IAuthRuleBuilderRuleOpClassifierFinished andRequireExplicitResponseAuthorization() { 814 myRules.add(myRule); 815 return new RuleBuilderFinished(myRule); 816 } 817 } 818 } 819 } 820 821 private class RuleBuilderRuleTransaction implements IAuthRuleBuilderRuleTransaction { 822 823 @Override 824 public IAuthRuleBuilderRuleTransactionOp withAnyOperation() { 825 return new RuleBuilderRuleTransactionOp(); 826 } 827 828 private class RuleBuilderRuleTransactionOp implements IAuthRuleBuilderRuleTransactionOp { 829 830 @Override 831 public IAuthRuleBuilderRuleOpClassifierFinished andApplyNormalRules() { 832 // Allow transaction 833 RuleImplOp rule = new RuleImplOp(myRuleName); 834 rule.setMode(myRuleMode); 835 rule.setOp(RuleOpEnum.TRANSACTION); 836 rule.setTransactionAppliesToOp(TransactionAppliesToEnum.ANY_OPERATION); 837 myRules.add(rule); 838 return new RuleBuilderFinished(rule); 839 } 840 } 841 } 842 843 private class PatchBuilder implements IAuthRuleBuilderPatch { 844 845 PatchBuilder() { 846 super(); 847 } 848 849 @Override 850 public IAuthRuleFinished allRequests() { 851 BaseRule rule = 852 new RuleImplPatch(myRuleName).setAllRequests(true).setMode(myRuleMode); 853 myRules.add(rule); 854 return new RuleBuilderFinished(rule); 855 } 856 } 857 858 private class UpdateHistoryRewriteBuilder implements IAuthRuleBuilderUpdateHistoryRewrite { 859 860 UpdateHistoryRewriteBuilder() { 861 super(); 862 } 863 864 @Override 865 public IAuthRuleFinished allRequests() { 866 BaseRule rule = new RuleImplUpdateHistoryRewrite(myRuleName) 867 .setAllRequests(true) 868 .setMode(myRuleMode); 869 myRules.add(rule); 870 return new RuleBuilderFinished(rule); 871 } 872 } 873 874 private class RuleBuilderGraphQL implements IAuthRuleBuilderGraphQL { 875 @Override 876 public IAuthRuleFinished any() { 877 RuleImplOp rule = new RuleImplOp(myRuleName); 878 rule.setOp(RuleOpEnum.GRAPHQL); 879 rule.setMode(myRuleMode); 880 myRules.add(rule); 881 return new RuleBuilderFinished(rule); 882 } 883 } 884 885 private class RuleBuilderBulkExport implements IAuthRuleBuilderRuleBulkExport { 886 private RuleBulkExportImpl myRuleBulkExport; 887 888 @Override 889 public IAuthRuleBuilderRuleBulkExportWithTarget groupExportOnGroup(@Nonnull String theFocusResourceId) { 890 RuleBulkExportImpl rule = new RuleBulkExportImpl(myRuleName); 891 rule.setAppliesToGroupExportOnGroup(theFocusResourceId); 892 rule.setMode(myRuleMode); 893 myRules.add(rule); 894 895 return new RuleBuilderBulkExportWithTarget(rule); 896 } 897 898 @Override 899 public IAuthRuleBuilderRuleBulkExportWithTarget patientExportOnAllPatients() { 900 if (myRuleBulkExport == null) { 901 RuleBulkExportImpl rule = new RuleBulkExportImpl(myRuleName); 902 rule.setMode(myRuleMode); 903 myRuleBulkExport = rule; 904 } 905 myRuleBulkExport.setAppliesToPatientExportAllPatients(); 906 907 // prevent duplicate rules being added 908 if (!myRules.contains(myRuleBulkExport)) { 909 myRules.add(myRuleBulkExport); 910 } 911 912 return new RuleBuilderBulkExportWithTarget(myRuleBulkExport); 913 } 914 915 @Override 916 public IAuthRuleBuilderRuleBulkExportWithTarget patientExportOnPatient(@Nonnull String theFocusResourceId) { 917 if (myRuleBulkExport == null) { 918 RuleBulkExportImpl rule = new RuleBulkExportImpl(myRuleName); 919 rule.setAppliesToPatientExport(theFocusResourceId); 920 rule.setMode(myRuleMode); 921 myRuleBulkExport = rule; 922 } else { 923 myRuleBulkExport.setAppliesToPatientExport(theFocusResourceId); 924 } 925 926 // prevent duplicate rules being added 927 if (!myRules.contains(myRuleBulkExport)) { 928 myRules.add(myRuleBulkExport); 929 } 930 931 return new RuleBuilderBulkExportWithTarget(myRuleBulkExport); 932 } 933 934 @Override 935 public IAuthRuleBuilderRuleBulkExportWithTarget patientExportOnPatientStrings( 936 @Nonnull Collection<String> theFocusResourceIds) { 937 if (myRuleBulkExport == null) { 938 RuleBulkExportImpl rule = new RuleBulkExportImpl(myRuleName); 939 rule.setAppliesToPatientExport(theFocusResourceIds); 940 rule.setMode(myRuleMode); 941 myRuleBulkExport = rule; 942 } else { 943 myRuleBulkExport.setAppliesToPatientExport(theFocusResourceIds); 944 } 945 946 // prevent duplicate rules being added 947 if (!myRules.contains(myRuleBulkExport)) { 948 myRules.add(myRuleBulkExport); 949 } 950 951 return new RuleBuilderBulkExportWithTarget(myRuleBulkExport); 952 } 953 954 @Override 955 public IAuthRuleBuilderRuleBulkExportWithTarget patientExportOnGroup(@Nonnull String theFocusResourceId) { 956 RuleBulkExportImpl rule = new RuleBulkExportImpl(myRuleName); 957 rule.setAppliesToPatientExportOnGroup(theFocusResourceId); 958 rule.setMode(myRuleMode); 959 myRules.add(rule); 960 961 return new RuleBuilderBulkExportWithTarget(rule); 962 } 963 964 @Override 965 public IAuthRuleBuilderRuleBulkExportWithTarget systemExport() { 966 RuleBulkExportImpl rule = new RuleBulkExportImpl(myRuleName); 967 rule.setAppliesToSystem(); 968 rule.setMode(myRuleMode); 969 myRules.add(rule); 970 971 return new RuleBuilderBulkExportWithTarget(rule); 972 } 973 974 @Override 975 public IAuthRuleBuilderRuleBulkExportWithTarget any() { 976 RuleBulkExportImpl rule = new RuleBulkExportImpl(myRuleName); 977 rule.setAppliesToAny(); 978 rule.setMode(myRuleMode); 979 myRules.add(rule); 980 981 return new RuleBuilderBulkExportWithTarget(rule); 982 } 983 984 private class RuleBuilderBulkExportWithTarget extends RuleBuilderFinished 985 implements IAuthRuleBuilderRuleBulkExportWithTarget { 986 private final RuleBulkExportImpl myRule; 987 988 private RuleBuilderBulkExportWithTarget(RuleBulkExportImpl theRule) { 989 super(theRule); 990 myRule = theRule; 991 } 992 993 @Override 994 public IAuthRuleBuilderRuleBulkExportWithTarget withResourceTypes(Collection<String> theResourceTypes) { 995 myRule.setResourceTypes(theResourceTypes); 996 return this; 997 } 998 } 999 } 1000 } 1001 1002 private static String toTypeName(Class<? extends IBaseResource> theType) { 1003 String retVal = ourTypeToName.get(theType); 1004 if (retVal == null) { 1005 ResourceDef resourceDef = theType.getAnnotation(ResourceDef.class); 1006 retVal = resourceDef.name(); 1007 Validate.notBlank(retVal, "Could not determine resource type of class %s", theType); 1008 ourTypeToName.put(theType, retVal); 1009 } 1010 return retVal; 1011 } 1012}