001package com.identityworksllc.iiq.common; 002 003import sailpoint.object.ProvisioningPlan; 004 005import java.util.Comparator; 006import java.util.HashMap; 007import java.util.Map; 008import java.util.function.IntFunction; 009import java.util.function.ToIntFunction; 010 011/** 012 * Implements comparators to sort account requests within a provisioning plan. 013 * 014 * The comparators safely handle the case where two requests both have the property 015 * (e.g. both are enables), preserving existing ordering where the sort algorithm 016 * does so. 017 * 018 * Additionally, the "first" comparator methods will always retain Create requests 019 * at the start of the sequence. 020 * 021 * The motive behind creating this is the Salesforce connector. It executes the 022 * operations given blindly in the order they are received in the plan. However, 023 * certain operations must always precede others, resulting in a lot of code to 024 * handle this situation. 025 */ 026@SuppressWarnings("unused") 027public class PlanComparators { 028 029 /** 030 * A map containing the default ordering for AccountRequests 031 */ 032 private static final Map<ProvisioningPlan.AccountRequest.Operation, Integer> ACCOUNT_REQUEST_ORDERING = new HashMap<>(); 033 034 /** 035 * A map containing the default ordering for AttributeRequests 036 */ 037 private static final Map<ProvisioningPlan.Operation, Integer> ATTRIBUTE_REQUEST_ORDERING = new HashMap<>(); 038 039 /** 040 * Performs a default sort of requests, implementing the sequence (Create, Enable, Unlock, 041 * Modify, Lock, Disable, Delete) 042 * @return Sorter 043 */ 044 public static Comparator<ProvisioningPlan.AttributeRequest> defaultAttributeSequence() { 045 return Comparator.comparing(v -> ATTRIBUTE_REQUEST_ORDERING.get(v.getOperation())); 046 } 047 048 /** 049 * Performs a default sort of requests, implementing the sequence (Create, Enable, Unlock, 050 * Modify, Lock, Disable, Delete) 051 * @return Sorter 052 */ 053 public static Comparator<ProvisioningPlan.AccountRequest> defaultSequence() { 054 return Comparator.comparing(v -> { 055 if (v.getOperation() == null) { 056 return ACCOUNT_REQUEST_ORDERING.get(ProvisioningPlan.AccountRequest.Operation.Modify); 057 } else { 058 return ACCOUNT_REQUEST_ORDERING.get(v.getOperation()); 059 } 060 }); 061 } 062 063 /** 064 * Moves disables to the start of the sequence, before any other operation except create 065 * @return Sorter 066 */ 067 public static Comparator<ProvisioningPlan.AccountRequest> disablesFirst() { 068 return genericSort(ar -> { 069 if (ar.getOperation().equals(ProvisioningPlan.AccountRequest.Operation.Create)) { 070 // Lowest sort 071 return -100; 072 } else if (ar.getOperation().equals(ProvisioningPlan.AccountRequest.Operation.Disable)) { 073 // Next lowest sort 074 return -10; 075 } else { 076 // Don't care 077 return 0; 078 } 079 }); 080 } 081 082 /** 083 * Moves disables to the end of the sequence 084 * @return Sorter 085 */ 086 public static Comparator<ProvisioningPlan.AccountRequest> disablesLast() { 087 return genericSort(ar -> { 088 if (ar.getOperation().equals(ProvisioningPlan.AccountRequest.Operation.Create)) { 089 // Lowest sort 090 return -100; 091 } else if (ar.getOperation().equals(ProvisioningPlan.AccountRequest.Operation.Disable)) { 092 // Highest sort 093 return 100; 094 } else { 095 // Don't care 096 return 0; 097 } 098 }); 099 } 100 101 /** 102 * Moves enables to the start of the sequence, before any other operation except create 103 * @return Sorter 104 */ 105 public static Comparator<ProvisioningPlan.AccountRequest> enablesFirst() { 106 return genericSort(ar -> { 107 if (ar.getOperation().equals(ProvisioningPlan.AccountRequest.Operation.Create)) { 108 // Lowest sort 109 return -100; 110 } else if (ar.getOperation().equals(ProvisioningPlan.AccountRequest.Operation.Enable)) { 111 // Next lowest sort 112 return -10; 113 } else { 114 // Don't care 115 return 0; 116 } 117 }); 118 } 119 120 /** 121 * Moves enables to the end of the sequence 122 * @return Sorter 123 */ 124 public static Comparator<ProvisioningPlan.AccountRequest> enablesLast() { 125 return genericSort(ar -> { 126 if (ar.getOperation().equals(ProvisioningPlan.AccountRequest.Operation.Create)) { 127 // Lowest sort 128 return -100; 129 } else if (ar.getOperation().equals(ProvisioningPlan.AccountRequest.Operation.Enable)) { 130 // Highest sort 131 return 100; 132 } else { 133 // Don't care 134 return 0; 135 } 136 }); 137 } 138 139 /** 140 * A generic sorter that will use a {@link ToIntFunction} to transform the given AccountRequest into an integer value, then sort those. 141 * @param function The function to transform an AccountRequest into an appropriate integer 142 * @return A comparator to sort AccountRequests by that 143 */ 144 public static Comparator<ProvisioningPlan.AccountRequest> genericSort(final ToIntFunction<ProvisioningPlan.AccountRequest> function) { 145 return (o1, o2) -> { 146 int o1i = function.applyAsInt(o1); 147 int o2i = function.applyAsInt(o2); 148 149 return (o1i - o2i); 150 }; 151 } 152 153 /** 154 * Moves an account request containing the given attribute request to the start of the 155 * sequence, before any other operation except create. 156 * 157 * @param which The attribute name of the AttributeRequest to float to the top 158 * @return Sorter 159 */ 160 public static Comparator<ProvisioningPlan.AccountRequest> specificFirst(final String which) { 161 return genericSort(ar -> { 162 if (ar.getOperation().equals(ProvisioningPlan.AccountRequest.Operation.Create)) { 163 // Lowest sort 164 return -100; 165 } else if (ar.getAttributeRequest(which) != null) { 166 // Next lowest sort 167 return -10; 168 } else { 169 // Don't care 170 return 0; 171 } 172 }); 173 } 174 175 /** 176 * Moves any specific account requests containing the given attribute to the end of the sequence 177 * @param which Which attribute to look for 178 * @return Sorter 179 */ 180 public static Comparator<ProvisioningPlan.AccountRequest> specificLast(final String which) { 181 return genericSort(ar -> { 182 if (ar.getOperation().equals(ProvisioningPlan.AccountRequest.Operation.Create)) { 183 // Lowest sort 184 return -100; 185 } else if (ar.getAttributeRequest(which) != null) { 186 // Highest sort 187 return 100; 188 } else { 189 // Don't care 190 return 0; 191 } 192 }); 193 } 194 195 static { 196 ACCOUNT_REQUEST_ORDERING.put(ProvisioningPlan.AccountRequest.Operation.Create, 0); 197 ACCOUNT_REQUEST_ORDERING.put(ProvisioningPlan.AccountRequest.Operation.Enable, 1); 198 ACCOUNT_REQUEST_ORDERING.put(ProvisioningPlan.AccountRequest.Operation.Unlock, 2); 199 ACCOUNT_REQUEST_ORDERING.put(ProvisioningPlan.AccountRequest.Operation.Modify, 3); 200 ACCOUNT_REQUEST_ORDERING.put(ProvisioningPlan.AccountRequest.Operation.Lock, 4); 201 ACCOUNT_REQUEST_ORDERING.put(ProvisioningPlan.AccountRequest.Operation.Disable, 5); 202 ACCOUNT_REQUEST_ORDERING.put(ProvisioningPlan.AccountRequest.Operation.Delete, 6); 203 204 ATTRIBUTE_REQUEST_ORDERING.put(ProvisioningPlan.Operation.Remove, 0); 205 ATTRIBUTE_REQUEST_ORDERING.put(ProvisioningPlan.Operation.Revoke, 0); 206 ATTRIBUTE_REQUEST_ORDERING.put(ProvisioningPlan.Operation.Retain, 1); 207 ATTRIBUTE_REQUEST_ORDERING.put(ProvisioningPlan.Operation.Set, 2); 208 ATTRIBUTE_REQUEST_ORDERING.put(ProvisioningPlan.Operation.Add, 3); 209 } 210 211}