001package com.identityworksllc.iiq.common.access;
002
003import com.identityworksllc.iiq.common.CommonSecurityConfig;
004import sailpoint.object.Identity;
005import sailpoint.rest.plugin.BasePluginResource;
006import sailpoint.tools.GeneralException;
007import sailpoint.tools.Util;
008import sailpoint.web.UserContext;
009
010import java.util.HashMap;
011import java.util.Map;
012
013/**
014 * Access check input
015 */
016public final class AccessCheckInput {
017    /**
018     * Configuration
019     */
020    private CommonSecurityConfig configuration;
021
022    /**
023     * The debug flag
024     */
025    private boolean debug;
026
027    /**
028     * The state from this access check
029     */
030    private Map<String, Object> state;
031
032    /**
033     * The target Identity being checked (may be null)
034     */
035    private transient Identity target;
036
037    /**
038     * The target Identity name
039     */
040    private String targetName;
041
042    /**
043     * The target object type
044     * TODO use this
045     */
046    private String targetType;
047
048    /**
049     * The name of the thing being checked
050     */
051    private String thingName;
052
053    /**
054     * The UserContext, specifying which user is the subject of the access check.
055     * If this is also a {@link sailpoint.plugin.PluginContext}, as it would be if
056     * it is a {@link BasePluginResource}, then it will be used for plugin-specific
057     * checks.
058     */
059    private UserContext userContext;
060
061    /**
062     * Constructs a basic access check input
063     */
064    public AccessCheckInput() {
065        this.thingName = AccessCheck.ANONYMOUS_THING;
066        this.debug = false;
067    }
068
069    /**
070     * Copy constructor allowing override of an input
071     *
072     * @param parent The parent config
073     * @param config The 'child' config to replace with
074     */
075    public AccessCheckInput(AccessCheckInput parent, CommonSecurityConfig config) {
076        this(parent.userContext, parent.target, parent.thingName, config, parent.state);
077    }
078
079    /**
080     * Access check input taking a plugin or target
081     *
082     * @param userContext    The user context (likely a BasePluginResource)
083     * @param config         The config
084     */
085    public AccessCheckInput(UserContext userContext, CommonSecurityConfig config) {
086        this(userContext, null, AccessCheck.ANONYMOUS_THING, config, null);
087    }
088    /**
089     * Access check input taking a plugin or target
090     *
091     * @param userContext    The user context (likely a BasePluginResource)
092     * @param target         The target
093     * @param config         The config
094     */
095    public AccessCheckInput(UserContext userContext, Identity target, CommonSecurityConfig config) {
096        this(userContext, target, AccessCheck.ANONYMOUS_THING, config, null);
097    }
098
099    /**
100     * Access check input taking a plugin or target
101     *
102     * @param userContext    The user context (likely a BasePluginResource)
103     * @param target         The target
104     * @param thingName      The thing name
105     * @param config         The config
106     */
107    public AccessCheckInput(UserContext userContext, Identity target, String thingName, CommonSecurityConfig config) {
108        this(userContext, target, thingName, config, null);
109    }
110
111    /**
112     * Access check input taking a plugin or target
113     *
114     * @param userContext    The user context (likely a {@link BasePluginResource} or {@link com.identityworksllc.iiq.common.auth.DummyAuthContext})
115     * @param target         The target
116     * @param thingName      The thing name
117     * @param config         The config
118     * @param state          Any persistent state in the access checks
119     */
120    public AccessCheckInput(UserContext userContext, Identity target, String thingName, CommonSecurityConfig config, Map<String, Object> state) {
121        this.userContext = userContext;
122        this.target = target;
123        if (this.target != null) {
124            this.targetName = target.getName();
125        }
126        this.configuration = config;
127        if (thingName == null || thingName.isEmpty()) {
128            this.thingName = AccessCheck.ANONYMOUS_THING;
129        } else {
130            this.thingName = thingName;
131        }
132        this.state = (state != null) ? state : new HashMap<>();
133        this.debug = false;
134    }
135
136    /**
137     * Gets the configuration object
138     * @return The common security configuration object
139     * @see CommonSecurityConfig
140     */
141    public CommonSecurityConfig getConfiguration() {
142        return configuration;
143    }
144
145    /**
146     * @deprecated Use {@link #getUserContext()} instead
147     * @return The configured plugin resource / user context
148     */
149    @Deprecated
150    public UserContext getPluginResource() {
151        return userContext;
152    }
153
154    /**
155     * Gets the state {@link Map}
156     * @return The state Map
157     */
158    public Map<String, Object> getState() {
159        return state;
160    }
161
162    /**
163     * Gets the stored target Identity if one exists. If one does not exist,
164     * returns the subject Identity.
165     *
166     * @return The target Identity
167     * @throws GeneralException if anything fails
168     */
169    public Identity getTarget() throws GeneralException {
170        if (this.target != null) {
171            return target;
172        } else if (Util.isNotNullOrEmpty(targetName)) {
173            this.target = userContext.getContext().getObject(Identity.class, targetName);
174            return target;
175        } else {
176            return userContext.getLoggedInUser();
177        }
178    }
179
180    /**
181     * Gets the currently configured thing name
182     * @return The configured thing name
183     */
184    public String getThingName() {
185        return thingName;
186    }
187
188    /**
189     * Gets the user context
190     * @return The user context, containing the subject user
191     */
192    public UserContext getUserContext() {
193        return userContext;
194    }
195
196    /**
197     * Returns the value of the debug flag on this access check request
198     * @return The debug flag
199     */
200    public boolean isDebug() {
201        return debug;
202    }
203
204    /**
205     * Puts a value into the access check state map
206     * @param name The key
207     * @param value The value
208     * @return This object, for chaining
209     */
210    public AccessCheckInput putState(String name, Object value) {
211        if (this.state == null) {
212            this.state = new HashMap<>();
213        }
214
215        this.state.put(name, value);
216        return this;
217    }
218
219    /**
220     * Sets the common security configuration as a Map, which will be decoded.
221     *
222     * @see CommonSecurityConfig#decode(Map)
223     * @see com.identityworksllc.iiq.common.ObjectMapper#decode(Map)
224     * @param configuration The configuration to decode and store
225     * @return This object, for chaining
226     * @throws GeneralException if the configuration cannot be decoded
227     */
228    public AccessCheckInput setConfiguration(Map<String, Object> configuration) throws GeneralException {
229        this.configuration = CommonSecurityConfig.decode(configuration);
230        return this;
231    }
232
233    /**
234     * Sets the common security configuration
235     * @param configuration The common security configuration
236     * @return This object, for chaining
237     */
238    public AccessCheckInput setConfiguration(CommonSecurityConfig configuration) {
239        this.configuration = configuration;
240        return this;
241    }
242
243    /**
244     * Sets the debug flag on the access check
245     * @param debug The debug flag to set
246     */
247    public AccessCheckInput setDebug(boolean debug) {
248        this.debug = debug;
249        return this;
250    }
251
252    /**
253     * Sets the plugin resource, simply forwarding to {@link #setUserContext(UserContext)},
254     * because {@link BasePluginResource} is an instance of {@link UserContext}.
255     *
256     * @param pluginResource The plugin resource to set
257     * @return This object, for chaining
258     * @throws GeneralException if a targetName has been set and loading the Identity fails
259     */
260    @Deprecated
261    public AccessCheckInput setPluginResource(BasePluginResource pluginResource) throws GeneralException {
262        return setUserContext(pluginResource);
263    }
264
265    /**
266     * Sets the state map, which will be provided to any access check rules or
267     * access check scripts.
268     *
269     * @param state The access check state to set; this map will be copied
270     * @return This object, for chaining
271     */
272    public AccessCheckInput setState(Map<String, Object> state) {
273        if (state == null) {
274            this.state = new HashMap<>();
275        } else {
276            this.state = new HashMap<>(state);
277        }
278        return this;
279    }
280
281    /**
282     * Sets the target Identity and target name
283     *
284     * @param target The target Identity
285     * @return This object, for chaining
286     */
287    public AccessCheckInput setTarget(Identity target) {
288        this.target = target;
289        this.targetName = target.getName();
290        return this;
291    }
292
293    /**
294     * Sets the target name or ID. This will be resolved to an {@link Identity}
295     * on the first call to {@link #getTarget()}.
296     *
297     * @param targetName The target's name or ID
298     * @return This object, for chaining
299     */
300    public AccessCheckInput setTarget(String targetName){
301        this.targetName = targetName;
302        return this;
303    }
304
305    /**
306     * Sets the thing name, for caching and display purposes
307     * @param thingName The thing name
308     * @return This object, for chaining
309     */
310    public AccessCheckInput setThingName(String thingName) {
311        this.thingName = thingName;
312        return this;
313    }
314
315    /**
316     * Sets the user context, containing the 'subject' of the access check
317     * @param userContext The context specifying the subject of the access check
318     * @return This object, for chaining
319     */
320    public AccessCheckInput setUserContext(UserContext userContext) {
321        this.userContext = userContext;
322        return this;
323    }
324
325    /**
326     * Validates the configuration before it executes
327     * @throws AccessCheckException if validation fails
328     */
329    public void validate() throws AccessCheckException {
330        if (this.userContext == null) {
331            throw new AccessCheckException("UserContext is required");
332        }
333        if (this.configuration == null) {
334            throw new AccessCheckException("Configuration is required");
335        }
336    }
337}