001package com.identityworksllc.iiq.common.task; 002 003import com.identityworksllc.iiq.common.iterators.BatchingIterator; 004import org.apache.commons.logging.Log; 005import org.apache.commons.logging.LogFactory; 006import sailpoint.api.Identitizer; 007import sailpoint.api.SailPointContext; 008import sailpoint.object.Attributes; 009import sailpoint.object.Filter; 010import sailpoint.object.GroupDefinition; 011import sailpoint.object.Identity; 012import sailpoint.object.QueryOptions; 013import sailpoint.object.Source; 014import sailpoint.object.TaskResult; 015import sailpoint.object.TaskSchedule; 016import sailpoint.task.AbstractTaskExecutor; 017import sailpoint.task.TaskMonitor; 018import sailpoint.tools.GeneralException; 019import sailpoint.tools.Message; 020import sailpoint.tools.Util; 021 022import java.util.ArrayList; 023import java.util.HashMap; 024import java.util.HashSet; 025import java.util.Iterator; 026import java.util.List; 027import java.util.Map; 028import java.util.Set; 029import java.util.concurrent.atomic.AtomicBoolean; 030import java.util.concurrent.atomic.AtomicInteger; 031 032import static com.identityworksllc.iiq.common.CommonConstants.ARG_STRING_FALSE; 033import static com.identityworksllc.iiq.common.CommonConstants.ARG_STRING_TRUE; 034 035/** 036 * Assigned role differences detection task. It will report on changes only, 037 * refresh all users with the changes, or save a big list of IDs to a 038 * specific population (or all three). 039 */ 040public class AssignedDetectedRoleDifferenceCleanup extends AbstractTaskExecutor { 041 042 /** 043 * The HQL to run to detect differences 044 */ 045 private final static String HQL = "select i.id, iar.name, req.name \n" + 046 "from Identity i\n" + 047 "inner join i.assignedRoles iar\n" + 048 "inner join iar.requirements req\n" + 049 "where\n" + 050 "not exists (\n" + 051 " select 1 from i.bundles b where b.name = req.name\n" + 052 ")\n"; 053 054 /** 055 * Logger 056 */ 057 private final Log log; 058 059 /** 060 * Flag indicating that the task is terminated 061 */ 062 private final AtomicBoolean terminated; 063 064 /** 065 * Constructor 066 */ 067 public AssignedDetectedRoleDifferenceCleanup() { 068 this.terminated = new AtomicBoolean(false); 069 this.log = LogFactory.getLog(AssignedDetectedRoleDifferenceCleanup.class); 070 } 071 072 /** 073 * Executor for this task: scans for users missing required IT roles and either 074 * refreshes them or reports on them. 075 * 076 * @param context The context from IIQ 077 * @param taskSchedule The task schedule 078 * @param taskResult The task result 079 * @param attributes The task attributes 080 * @throws Exception if anything fails 081 */ 082 @Override 083 public void execute(SailPointContext context, TaskSchedule taskSchedule, TaskResult taskResult, Attributes<String, Object> attributes) throws Exception { 084 boolean doRefresh = attributes.getBoolean("doRefresh", false); 085 boolean doReport = attributes.getBoolean("doReport", true); 086 087 String populationName = attributes.getString("savePopulationName"); 088 089 TaskMonitor monitor = new TaskMonitor(context, taskResult); 090 091 AtomicInteger missingRoles = new AtomicInteger(); 092 093 log.info("Running HQL: " + HQL); 094 QueryOptions qo = new QueryOptions(); 095 Iterator<Object[]> results = context.search(Identity.class, qo, HQL); 096 097 Set<String> identitiesToRefresh = new HashSet<>(); 098 099 List<Map<String, Object>> resultList = new ArrayList<>(); 100 101 while(results.hasNext()) { 102 Object[] row = results.next(); 103 104 String id = Util.otoa(row[0]); 105 String assignedRoleName = Util.otoa(row[1]); 106 String missingRequirement = Util.otoa(row[2]); 107 108 Map<String, Object> logData = new HashMap<>(); 109 logData.put("identity", id); 110 logData.put("businessRole", assignedRoleName); 111 logData.put("requiredRoleMissing", missingRequirement); 112 113 if (log.isDebugEnabled()) { 114 log.debug("Missing IT role for assigned Business role: " + logData); 115 } 116 117 resultList.add(logData); 118 119 identitiesToRefresh.add(id); 120 121 monitor.updateProgress("Processing Identity " + id); 122 123 missingRoles.incrementAndGet(); 124 125 if (terminated.get()) { 126 Util.flushIterator(results); 127 break; 128 } 129 } 130 131 // Constructs a big gross population containing all of the IDs of all users with 132 // missing entitlements. 133 if (Util.isNotNullOrEmpty(populationName)) { 134 log.info("Creating ID List population: " + populationName); 135 136 GroupDefinition groupDefinition = context.getObjectByName(GroupDefinition.class, populationName); 137 if (groupDefinition == null) { 138 groupDefinition = new GroupDefinition(); 139 groupDefinition.setName(populationName); 140 groupDefinition.setPrivate(false); 141 } 142 143 try (BatchingIterator<String> iterator = new BatchingIterator<>(identitiesToRefresh.iterator(), 250)) { 144 List<Filter> idFilters = new ArrayList<>(); 145 146 while (iterator.hasNext()) { 147 List<String> batch = iterator.next(); 148 idFilters.add(Filter.in("id", batch)); 149 } 150 151 groupDefinition.setFilter(Filter.or(idFilters)); 152 } 153 154 if (!terminated.get()) { 155 context.saveObject(groupDefinition); 156 } 157 } 158 159 // Do refreshes if that's enabled 160 if (doRefresh && !terminated.get()) { 161 Attributes<String, Object> args = new Attributes<>(); 162 args.put(Identitizer.ARG_PROVISION, ARG_STRING_TRUE); 163 args.put(Identitizer.ARG_CHECK_HISTORY, ARG_STRING_TRUE); 164 args.put(Identitizer.ARG_CORRELATE_ENTITLEMENTS, ARG_STRING_TRUE); 165 args.put(Identitizer.ARG_REFRESH_IDENTITY_ENTITLEMENTS, ARG_STRING_TRUE); 166 args.put(Identitizer.ARG_PROCESS_TRIGGERS, ARG_STRING_FALSE); 167 args.put(Identitizer.ARG_REFRESH_PROVISIONING_REQUESTS, ARG_STRING_TRUE); 168 args.put(Identitizer.ARG_REFRESH_ROLE_METADATA, ARG_STRING_TRUE); 169 args.put(Identitizer.ARG_REFRESH_SOURCE, Source.Task); 170 args.put(Identitizer.ARG_REFRESH_SOURCE_WHO, context.getUserName()); 171 172 Identitizer identitizer = new Identitizer(context, args); 173 174 for(String id : identitiesToRefresh) { 175 if (terminated.get()) { 176 log.warn("Task was terminated"); 177 break; 178 } 179 180 Identity identity = context.getObject(Identity.class, id); 181 182 if (log.isDebugEnabled()) { 183 log.debug("Refreshing Identity " + identity.getDisplayableName()); 184 } 185 186 monitor.updateProgress("Refreshing Identity " + identity.getDisplayableName()); 187 188 try { 189 identitizer.refresh(identity); 190 } catch(GeneralException e) { 191 TaskResult primaryResult = monitor.lockMasterResult(); 192 try { 193 primaryResult.addException(e); 194 } finally { 195 monitor.commitMasterResult(); 196 } 197 198 log.error("Caught an error refreshing Identity", e); 199 } 200 } 201 202 identitizer.cleanup(); 203 } 204 205 TaskResult primaryResult = monitor.lockMasterResult(); 206 try { 207 if (terminated.get()) { 208 primaryResult.addMessage(Message.warn("Task was terminated by user request")); 209 } 210 if (doReport) { 211 primaryResult.setAttribute("report", resultList); 212 } 213 primaryResult.setInt("affectedIdentities", identitiesToRefresh.size()); 214 primaryResult.setInt("missingRoles", missingRoles.get()); 215 } finally { 216 monitor.commitMasterResult(); 217 } 218 } 219 220 /** 221 * Invoked by IIQ on termination. Sets the terminated flag to true so that 222 * the task knows to stop. 223 * 224 * @return always boolean true 225 */ 226 @Override 227 public boolean terminate() { 228 terminated.getAndSet(true); 229 return true; 230 } 231}