001/*-
002 * #%L
003 * Smile CDR - CDR
004 * %%
005 * Copyright (C) 2016 - 2024 Smile CDR, Inc.
006 * %%
007 * All rights reserved.
008 * #L%
009 */
010package ca.cdr.api.security.permission;
011
012import jakarta.annotation.Nonnull;
013import org.apache.commons.lang3.StringUtils;
014
015import java.util.Optional;
016
017import static org.apache.commons.lang3.StringUtils.defaultString;
018
019/**
020 * A narrowing filter - does a resource (of type), have a search param (by name), in a ValueSet (via url).
021 * E.g. <code>Observation/code/https://example/valueSetUrl</code>
022 */
023@SuppressWarnings("JavadocLinkAsPlainText")
024public final class CodeInValueSet extends PermissionArgumentValue
025                implements PermissionArgumentFormat.ICodeInValueSetRestriction {
026        // final for optimizer
027        private final String myResourceType;
028        private final String mySearchParamName;
029        private final String myValueSetUrl;
030
031        CodeInValueSet(String theResourceType, String theSearchParamName, String theValueSetUrl) {
032                myResourceType = theResourceType;
033                mySearchParamName = theSearchParamName;
034                myValueSetUrl = theValueSetUrl;
035        }
036
037        @Override
038        public String getResourceType() {
039                return myResourceType;
040        }
041
042        @Override
043        public String getSearchParamName() {
044                return mySearchParamName;
045        }
046
047        @Override
048        public String getValueSetUrl() {
049                return myValueSetUrl;
050        }
051
052        @Override
053        @Nonnull
054        String getStringValue() {
055                return myResourceType + "/" + mySearchParamName + "/" + myValueSetUrl;
056        }
057
058        static final class Format implements PermissionArgumentFormat<CodeInValueSet> {
059
060                @Override
061                public @Nonnull Optional<CodeInValueSet> parse(String theArgumentString, @Nonnull IdBuilder theIdBuilder) {
062                        if (StringUtils.isEmpty(theArgumentString)) {
063                                return Optional.empty();
064                        }
065
066                        int firstSlashIdx = defaultString(theArgumentString).indexOf('/');
067                        if (firstSlashIdx < 1) {
068                                return Optional.empty();
069                        }
070
071                        String resourceType = theArgumentString.substring(0, firstSlashIdx);
072
073                        int secondSlashIdx = theArgumentString.indexOf('/', firstSlashIdx + 1);
074                        if (secondSlashIdx < 1) {
075                                return Optional.empty();
076                        }
077
078                        String searchParamName = theArgumentString.substring(firstSlashIdx + 1, secondSlashIdx);
079                        String valueSetUrl = theArgumentString.substring(secondSlashIdx + 1);
080
081                        // todo jr beef up the validation - since the / delimiter also occurs within the url,
082                        //  there are many ways an invalid argument can still slip through the current set of checks
083                        if (StringUtils.isNoneBlank(resourceType, searchParamName, valueSetUrl)) {
084                                return Optional.of(new CodeInValueSet(resourceType, searchParamName, valueSetUrl));
085                        }
086
087                        return Optional.empty();
088                }
089
090                @Override
091                public Class<CodeInValueSet> getType() {
092                        return CodeInValueSet.class;
093                }
094
095                @Override
096                public @Nonnull String format(@Nonnull CodeInValueSet theArgument) {
097                        return theArgument.getStringValue();
098                }
099        }
100}