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 ca.cdr.api.model.enm.PermissionEnum;
013import ca.cdr.api.model.json.GrantedAuthorityJson;
014import jakarta.annotation.Nonnull;
015import jakarta.annotation.Nullable;
016import org.apache.commons.lang3.Validate;
017import org.hl7.fhir.instance.model.api.IIdType;
018
019import java.util.Collection;
020import java.util.Optional;
021import java.util.regex.Pattern;
022
023/**
024 * Public interface for the various permission argument formats.
025 * @param <F> The type this format parses/formats
026 */
027public interface PermissionArgumentFormat<F extends PermissionArgumentValue> {
028
029        char FILTER_DELIMITER = '?';
030        char TYPE_COMPARTMENT_DELIMITER = ':';
031        String RESOURCE_TYPE_LIST_DELIMITER = " ";
032        String RESOURCE_TYPE_PATTERN = "[A-Z][a-zA-Z\\d]*";
033        /**
034         * format for resource ids - used for compartments.
035         * The spec says [a-zA-Z0-9.\-]{1,36}, but hybrid might do any crazy thing,
036         * so we only verify we don't have any of our permission-format chars.
037         * @see ca.uhn.fhir.model.primitive.IdDt */
038        Pattern ID_FORMAT = Pattern.compile(RESOURCE_TYPE_PATTERN + "/[^" + FILTER_DELIMITER + TYPE_COMPARTMENT_DELIMITER
039                        + RESOURCE_TYPE_LIST_DELIMITER + "]+");
040
041        /**
042         * Typesafe authority builder.
043         * Validates the argument format against the declared format for thePermissionEnum
044         * @param thePermissionEnum the permission
045         * @param theArgumentValue the concrete parsed argument type - use {@link NoArgument#INSTANCE} for no-arg.
046         */
047        static GrantedAuthorityJson buildAuthority(
048                        @Nonnull PermissionEnum thePermissionEnum, @Nonnull PermissionArgumentValue theArgumentValue) {
049                String formattedArg = thePermissionEnum.getFormat().formatIfValid(theArgumentValue);
050                return new GrantedAuthorityJson(thePermissionEnum, formattedArg);
051        }
052
053        /**
054         * Parse a permission argument string.
055         * @param theArgumentString The string to parse.  null is treated as empty.
056         * @param theIdBuilder factory for parsing IIdType types
057         * @return the parsed arg value if the input matches this format
058         */
059        @Nonnull
060        Optional<F> parse(@Nullable String theArgumentString, @Nonnull IdBuilder theIdBuilder);
061
062        @Nonnull
063        default <T extends PermissionArgumentValue> String formatIfValid(@Nonnull T theArgument) {
064                Validate.isInstanceOf(this.getType(), theArgument);
065                //noinspection unchecked
066                return format((F) theArgument);
067        }
068
069        /**
070         * @return the type of the value bean we parse
071         */
072        Class<F> getType();
073
074        /**
075         * format the value back into a string for persistence.
076         * @param theArgument the arg value bean
077         * @return the formatted string - empty will be the empty string, not null
078         */
079        @Nonnull
080        String format(@Nonnull F theArgument);
081
082        /* -------------- */
083        /* format components */
084        /* -------------- */
085        /** Empty marker for facets of permission arguments */
086        interface IPermissionComponent {}
087
088        /** The permission is scoped to only apply to a type */
089        interface IResourceTypeRestriction extends IPermissionComponent {
090                /** A resource type - e.g. Patient */
091                String getResourceType();
092        }
093
094        /** the permission is scoped to a compartment */
095        interface ICompartmentRestriction extends IPermissionComponent {
096                /** Get compartment owner */
097                IIdType getOwner();
098
099                /** The type of the compartment - i.e. the type of the compartment owner.  E.g Patient, Device, etc. */
100                default String getCompartmentType() {
101                        return getOwner().getResourceType();
102                }
103        }
104
105        interface ICompartmentsRestriction extends IPermissionComponent {
106                /** Get compartment owners */
107                Collection<IIdType> getOwners();
108        }
109
110        /** The permission is scoped to a single instance */
111        interface IInstanceRestriction extends IPermissionComponent, IResourceTypeRestriction {
112                /** The scope instance */
113                IIdType getInstanceId();
114
115                /** The type of the instance */
116                @Override
117                default String getResourceType() {
118                        return getInstanceId().getResourceType();
119                }
120        }
121
122        /** The permission has an optional FHIR filter */
123        interface IOptionalFilterRestriction extends IPermissionComponent {
124                /** The fhir query - optional */
125                Optional<String> getFilter();
126                /** Is this permission scoped by a fhir query? */
127                default boolean hasFilter() {
128                        return getFilter().isPresent();
129                }
130        }
131
132        interface ICodeInValueSetRestriction extends IPermissionComponent {
133                /** A resource type - e.g., Patient */
134                String getResourceType();
135
136                /** a search parameter */
137                String getSearchParamName();
138
139                /** a URL identifying a value set */
140                String getValueSetUrl();
141        }
142
143        interface IOperationRestriction extends IPermissionComponent {
144                /** the name of an operation */
145                String getOperationName();
146        }
147
148        interface IExportResourceTypesRestriction extends IPermissionComponent {
149                /** indicates that the user has permission to export all types */
150                boolean canExportAllResourceTypes();
151
152                /** the names of the types that the user has permission to export */
153                Collection<String> getResourceTypes();
154        }
155
156        interface ICompartmentWithExportResourceTypesRestriction extends IPermissionComponent, ICompartmentRestriction {
157
158                /** the names of the types that the user has permission to export */
159                Collection<String> getResourceTypes();
160        }
161
162        interface ICompartmentsWithExportResourceTypesRestriction
163                        extends IPermissionComponent, ICompartmentsRestriction, IExportResourceTypesRestriction {}
164
165        interface INodeAndModuleIdRestriction extends IPermissionComponent {
166                /** the nodeId that the user has permission for */
167                String getNodeId();
168                /** the moduleId that the user has permission for */
169                String getModuleId();
170        }
171}