001package com.identityworksllc.iiq.common.request;
002
003import org.apache.commons.logging.Log;
004import org.apache.commons.logging.LogFactory;
005import sailpoint.api.Identitizer;
006import sailpoint.api.RequestManager;
007import sailpoint.api.SailPointContext;
008import sailpoint.object.Attributes;
009import sailpoint.object.Identity;
010import sailpoint.object.Request;
011import sailpoint.object.RequestDefinition;
012import sailpoint.request.AbstractRequestExecutor;
013import sailpoint.request.RequestPermanentException;
014import sailpoint.request.RequestTemporaryException;
015import sailpoint.tools.GeneralException;
016import sailpoint.tools.Util;
017
018import java.util.Date;
019import java.util.Map;
020
021/**
022 * Request executor to do an async refresh on a single user. The Request must
023 * contain at least a String identityName. All other arguments on the Request
024 * will be interpreted as input options to the Identitizer API.
025 *
026 * If no other arguments are supplied beyond an 'identityName', the refresh will
027 * run with the following default options set to true:
028 *
029 * - promoteAttributes
030 * - correlateEntitlements (refresh assigned / detected roles)
031 * - provisionIfChanged
032 * - processTriggers
033 * - refreshManagerStatus
034 *
035 * The following options will always be set to true:
036 *
037 * - noCheckPendingWorkflow
038 */
039public class IdentityRefreshExecutor extends AbstractRequestExecutor {
040    /**
041     * Launches an Identity Refresh request against the given Identity, with the given options.
042     * The "IDW Identity Refresh Request" request definition must be installed in the IIQ
043     * system for this to work properly.
044     *
045     * @param context The IIQ context to use to launch the request
046     * @param targetIdentity The target Identity to refresh
047     * @param options Any refresh options (may be null)
048     * @throws GeneralException if launching the request fails
049     * @throws IllegalArgumentException if the inputs are improper
050     */
051    public static void launchRequest(SailPointContext context, Identity targetIdentity, Map<String, Object> options) throws GeneralException {
052        if (targetIdentity == null) {
053            throw new IllegalArgumentException("targetIdentity is null");
054        }
055        launchRequest(context, targetIdentity.getName(), options);
056    }
057
058    /**
059     * Launches an Identity Refresh request against the given Identity, with the given options.
060     * The "IDW Identity Refresh Request" request definition must be installed in the IIQ
061     * system for this to work properly.
062     *
063     * @param context The IIQ context to use to launch the request
064     * @param targetIdentityName The target Identity to refresh
065     * @param options Any refresh options (may be null)
066     * @throws GeneralException if launching the request fails
067     * @throws IllegalArgumentException if the inputs are improper
068     */
069    public static void launchRequest(SailPointContext context, String targetIdentityName, Map<String, Object> options) throws GeneralException {
070        launchRequest(context, targetIdentityName, options, 0L);
071    }
072
073    /**
074     * Launches an Identity Refresh request against the given Identity, with the given options.
075     * The "IDW Identity Refresh Request" request definition must be installed in the IIQ
076     * system for this to work properly.
077     *
078     * @param context The IIQ context to use to launch the request
079     * @param targetIdentityName The name of the target Identity to refresh
080     * @param options Any refresh options (may be null)
081     * @param launchTimestamp The epoch timestamp at which this request should be launched; less than 1 indicates immediate
082     * @throws GeneralException if launching the request fails
083     * @throws IllegalArgumentException if the inputs are improper
084     */
085    public static void launchRequest(SailPointContext context, String targetIdentityName, Map<String, Object> options, long launchTimestamp) throws GeneralException {
086        if (Util.isNullOrEmpty(targetIdentityName)) {
087            throw new IllegalArgumentException("targetIdentityName is null or empty");
088        }
089
090        Request input = new Request();
091        input.setName("Refresh Identity " + targetIdentityName);
092        input.setDefinition(context.getObjectByName(RequestDefinition.class, REQUEST_DEFINITION));
093
094        if (launchTimestamp > 0) {
095            input.setNextLaunch(new Date(launchTimestamp));
096        } else {
097            input.setNextLaunch(new Date());
098        }
099
100        Attributes<String, Object> arguments = new Attributes<>();
101        if (options != null) {
102            arguments.putAll(options);
103        }
104        arguments.put("identityName", targetIdentityName);
105
106        input.setAttributes(arguments);
107
108        RequestManager.addRequest(context, input);
109    }
110
111    /**
112     * The name of the request definition associated with this executor
113     */
114    @SuppressWarnings("unused")
115    public static final String REQUEST_DEFINITION = "IDW Identity Refresh Request";
116
117    /**
118     * Logger
119     */
120    private static final Log log = LogFactory.getLog(IdentityRefreshExecutor.class);
121
122    /**
123     * Invokes the Identitizer to refresh the Identity with the given parameters.
124     * The Request invoking this handler must at least supply an 'identityName'
125     * in its attributes. All other Request attributes will be interpreted as
126     * arguments to the Identitizer.
127     *
128     * @param context The IIQ context for this thread
129     * @param request The request to execute
130     * @param attributes Unknown, some extra attributes?
131     * @throws RequestPermanentException if the request fails in a way that cannot be retried
132     * @throws RequestTemporaryException if the request fails in a way that can be retried (e.g., temporary connection failure)
133     */
134    @Override
135    public void execute(SailPointContext context, Request request, Attributes<String, Object> attributes) throws RequestPermanentException, RequestTemporaryException {
136        String identityName = Util.otoa(Util.get(request.getAttributes(), "identityName"));
137        if (Util.isNullOrEmpty(identityName)) {
138            throw new RequestPermanentException("An identity name is required for a " + this.getClass().getName() + " request");
139        }
140        try {
141            Identity identity = context.getObject(Identity.class, identityName);
142            if (identity == null) {
143                throw new RequestPermanentException("Identity " + identityName + " does not exist or is not accessible");
144            }
145
146            Attributes<String, Object> refreshArguments = new Attributes<>();
147            if (request.getAttributes() != null) {
148                refreshArguments.putAll(request.getAttributes());
149                refreshArguments.remove("identityName");
150            }
151
152            if (refreshArguments.isEmpty()) {
153                refreshArguments.put(Identitizer.ARG_PROMOTE_ATTRIBUTES, "true");
154                refreshArguments.put(Identitizer.ARG_CORRELATE_ENTITLEMENTS, "true");
155                refreshArguments.put(Identitizer.ARG_PROVISION_IF_CHANGED, "true");
156                refreshArguments.put(Identitizer.ARG_PROCESS_TRIGGERS, "true");
157                refreshArguments.put(Identitizer.ARG_REFRESH_MANAGER_STATUS, "true");
158            }
159
160            // Always do this, so that we don't silently fail to refresh the user
161            // if there is a pending workflow already running.
162            refreshArguments.put(Identitizer.ARG_NO_CHECK_PENDING_WORKFLOW, "true");
163
164            if (log.isInfoEnabled()) {
165                log.info("Executing requested refresh for identity = " + identity + ", arguments = " + refreshArguments);
166            }
167
168            Identitizer identitizer = new Identitizer(context, refreshArguments);
169            identitizer.refresh(identity);
170        } catch(GeneralException e) {
171            log.error("Caught an exception processing an identity refresh on " + identityName, e);
172            throw new RequestPermanentException(e);
173        }
174    }
175}