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}