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 org.apache.commons.lang3.builder.ToStringBuilder;
023import org.apache.commons.lang3.builder.ToStringStyle;
024
025/**
026 * Tester that a resource matches a provided query filter.
027 */
028public class FhirQueryRuleTester implements IAuthRuleTester {
029        private final String myQueryParameters;
030
031        public FhirQueryRuleTester(String theQueryParameters) {
032                myQueryParameters = theQueryParameters;
033        }
034
035        @Override
036        public boolean matches(RuleTestRequest theRuleTestRequest) {
037                return checkMatch(theRuleTestRequest);
038        }
039
040        @Override
041        public boolean matchesOutput(RuleTestRequest theRuleTestRequest) {
042                return checkMatch(theRuleTestRequest);
043        }
044
045        private boolean checkMatch(RuleTestRequest theRuleTestRequest) {
046                // look for a matcher
047                IAuthorizationSearchParamMatcher matcher = theRuleTestRequest.ruleApplier.getSearchParamMatcher();
048                if (matcher == null) {
049                        theRuleTestRequest
050                                        .ruleApplier
051                                        .getTroubleshootingLog()
052                                        .warn("No matcher provided.  Can't apply filter permission.");
053                        return false;
054                }
055
056                // this is a bit weird.
057                // A tester narrows a rule -- i.e. a rule only applies if the main logic matches AND the testers all match
058                // But this rule would have matched without this tester, so true means abstain.
059                if (theRuleTestRequest.resource == null) {
060                        // we aren't looking at a resource yet.  treat as no-op
061                        return true;
062                }
063
064                // we use the target type since the rule might apply to all types, a type set, or instances, and that has
065                // already been checked.
066                IAuthorizationSearchParamMatcher.MatchResult mr = matcher.match(
067                                theRuleTestRequest.resource.fhirType() + "?" + myQueryParameters, theRuleTestRequest.resource);
068
069                switch (mr.match) {
070                        case MATCH:
071                                return true;
072                        case UNSUPPORTED:
073                                theRuleTestRequest
074                                                .ruleApplier
075                                                .getTroubleshootingLog()
076                                                .warn("Unsupported matcher expression {}: {}.", myQueryParameters, mr.unsupportedReason);
077                                // unsupported doesn't match unless this is a deny request, and we need to be safe!
078                                return (theRuleTestRequest.mode == PolicyEnum.DENY);
079                        case NO_MATCH:
080                        default:
081                                return false;
082                }
083        }
084
085        @Override
086        public String toString() {
087                return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
088                                .append("filter", myQueryParameters)
089                                .toString();
090        }
091}