001package com.identityworksllc.iiq.common;
002
003import com.fasterxml.jackson.annotation.JsonInclude;
004import com.fasterxml.jackson.core.JsonProcessingException;
005import com.fasterxml.jackson.databind.ObjectMapper;
006import com.fasterxml.jackson.databind.type.MapType;
007import sailpoint.api.SailPointContext;
008import sailpoint.object.*;
009import sailpoint.tools.GeneralException;
010import sailpoint.tools.MapUtil;
011import sailpoint.transformer.IdentityTransformer;
012
013import java.util.HashMap;
014import java.util.Map;
015import java.util.stream.Collectors;
016
017/**
018 * Implements conversion utilities to extract a Map equivalent to a number of
019 * common IIQ objects. These can be passed as arguments to a Rule, for example,
020 * or used via MapMatcher.
021 */
022public class Mapper {
023
024    /**
025     * Returns the default ObjectMapper instance.
026     * TOOD caching and cloning of the ObjectMapper?
027     * @return The ObjectMapper instance
028     */
029    private static ObjectMapper getMapper() {
030        return new ObjectMapper();
031    }
032
033    /**
034     * A static version of the utility implemented via {@link Mappable}
035     * @param anything Whatever object is needed
036     * @return The object converted to a Map
037     * @throws GeneralException if anything fails
038     */
039    public static Map<String, Object> objectToMap(Object anything) throws GeneralException {
040        if (anything == null) {
041            return new HashMap<>();
042        }
043
044        ObjectMapper mapper = getMapper();
045        mapper.addMixIn(anything.getClass(), Mappable.FilterMixin.class);
046        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
047
048        try {
049            String json = mapper.writeValueAsString(anything);
050            MapType javaType = mapper.getTypeFactory().constructMapType(Map.class, String.class, Object.class);
051            return mapper.readValue(json, javaType);
052        } catch(JsonProcessingException e) {
053            throw new GeneralException(e);
054        }
055    }
056
057    /**
058     * Transforms a Map with path-type keys (e.g., 'link.attributes[a]') to a
059     * model map that has nested keys and values.
060     *
061     * @param input The input map
062     * @return The output map
063     * @throws GeneralException if any failures occur
064     */
065    public static Map<String, Object> pathMapToModelMap(Map<String, Object> input) throws GeneralException {
066        Map<String, Object> output = new HashMap<>();
067
068        if (input != null) {
069            for (String key : input.keySet()) {
070                MapUtil.put(output, key, input.get(key));
071            }
072        }
073
074        return output;
075    }
076
077    /**
078     * Transforms the given SailPointObject into a Map. If the object is one of
079     * the other types handled by this class, forwards to that method. Otherwise,
080     * returns the attributes.
081     *
082     * @param context The context, potentially used to load attributes
083     * @param spo The input object
084     * @return The resulting Map-ified object
085     * @throws GeneralException if any failures occur
086     */
087    public static Map<String, Object> toMap(SailPointContext context, Object spo) throws GeneralException {
088        Map<String, Object> result = new HashMap<>();
089        if (spo instanceof Link) {
090            result.putAll(toMap((Link)spo));
091        } else if (spo instanceof Identity) {
092            result.putAll(toMap(context, (Identity)spo));
093        } else if (spo instanceof Application) {
094            result.putAll(toMap((Application)spo));
095        } else if (spo instanceof ProvisioningPlan) {
096            result.putAll(toMap((ProvisioningPlan)spo));
097        } else if (spo instanceof ProvisioningPlan.AbstractRequest) {
098            result.putAll(toMap((ProvisioningPlan.AbstractRequest)spo));
099        } else if (spo instanceof ManagedAttribute) {
100            result.putAll(toMap((ManagedAttribute)spo));
101        } else {
102            Attributes<String, Object> attrs = Utilities.getAttributes(spo);
103            if (attrs != null) {
104                result.putAll(attrs);
105            }
106        }
107        return result;
108    }
109
110    public static Map<String, Object> toMap(AttributeAssignment aa) {
111        Map<String, Object> map = new HashMap<>();
112        map.put("name", aa.getName());
113        map.put("application.id", aa.getApplicationId());
114        map.put("application.name", aa.getApplicationName());
115        map.put("nativeIdentity", aa.getNativeIdentity());
116        map.put("type", Utilities.safeString(aa.getType()));
117        map.put("value", aa.getStringValue());
118        return map;
119    }
120
121    public static Map<String, Object> toMap(Application application) {
122        Map<String, Object> map = new HashMap<>();
123        if (application.getAttributes() != null) {
124            map.putAll(application.getAttributes());
125        }
126        map.put("schemas", application.getSchemas());
127        map.put("accountSchema", application.getAccountSchema());
128        map.put("name", application.getName());
129        map.put("id", application.getId());
130        map.put("connector", application.getConnector());
131        map.put("features", application.getFeaturesString());
132        map.put("beforeProvisioningRule", application.getBeforeProvisioningRule());
133        map.put("afterProvisioningRule", application.getAfterProvisioningRule());
134        map.put("formPath", application.getFormPath());
135        if (application.getProxy() != null) {
136            map.put("proxy", toMap(application.getProxy()));
137        }
138        return map;
139    }
140
141    public static Map<String, Object> toMap(ProvisioningPlan.AbstractRequest request) {
142        return request.toMap();
143    }
144
145    public static Map<String, Object> toMap(ProvisioningPlan plan) {
146        return plan.toMap();
147    }
148
149    public static Map<String, Object> toMap(SailPointContext context, Identity identity) throws GeneralException {
150        IdentityTransformer transformer = new IdentityTransformer(context);
151        transformer.setLinkExpand(true);
152        return transformer.toMap(identity);
153    }
154
155    public static Map<String, Object> toMap(RoleAssignment ra) {
156        Map<String, Object> map = new HashMap<>();
157        map.put("name", ra.getRoleName());
158        map.put("source", ra.getSource());
159        map.put("comments", ra.getComments());
160        map.put("assigner", ra.getAssigner());
161        map.put("assignmentId", ra.getAssignmentId());
162        map.put("target.application.id", Utilities.safeStream(ra.getTargets()).map(RoleTarget::getApplicationId).collect(Collectors.toList()));
163        map.put("target.application.name", Utilities.safeStream(ra.getTargets()).map(RoleTarget::getApplicationName).collect(Collectors.toList()));
164        map.put("target.nativeIdentity", Utilities.safeStream(ra.getTargets()).map(RoleTarget::getNativeIdentity).collect(Collectors.toList()));
165        map.put("target.displayName", Utilities.safeStream(ra.getTargets()).map(RoleTarget::getDisplayName).collect(Collectors.toList()));
166        return map;
167    }
168
169    public static Map<String, Object> toMap(RoleDetection rd) {
170        Map<String, Object> map = new HashMap<>();
171        map.put("name", rd.getRoleName());
172        map.put("assignmentIds", rd.getAssignmentIdList());
173        map.put("target.application.id", Utilities.safeStream(rd.getTargets()).map(RoleTarget::getApplicationId).collect(Collectors.toList()));
174        map.put("target.application.name", Utilities.safeStream(rd.getTargets()).map(RoleTarget::getApplicationName).collect(Collectors.toList()));
175        map.put("target.nativeIdentity", Utilities.safeStream(rd.getTargets()).map(RoleTarget::getNativeIdentity).collect(Collectors.toList()));
176        map.put("target.displayName", Utilities.safeStream(rd.getTargets()).map(RoleTarget::getDisplayName).collect(Collectors.toList()));
177        return map;
178    }
179
180    public static Map<String, Object> toMap(Link owner, ManagedAttribute ma) {
181        Map<String, Object> map = new HashMap<>();
182        if (ma.getAttributes() != null) {
183            map.putAll(ma.getAttributes());
184        }
185        if (owner.getAttributes() != null) {
186            Utilities.safeStream(owner.getAttributes().getKeys()).forEach(k -> {
187                map.put("link." + k, owner.getAttributes().get(k));
188            });
189        }
190        map.put("link.instance", owner.getInstance());
191        map.put("link.nativeIdentity", owner.getNativeIdentity());
192        map.put("value", ma.getValue());
193        map.put("displayName", ma.getDisplayName());
194        map.put("application.name", ma.getApplication().getName());
195        map.put("application.id", ma.getApplicationId());
196        map.put("attribute", ma.getAttribute());
197        return map;
198    }
199
200
201    public static Map<String, Object> toMap(ManagedAttribute ma) {
202        Map<String, Object> map = new HashMap<>();
203        if (ma.getAttributes() != null) {
204            map.putAll(ma.getAttributes());
205        }
206        map.put("value", ma.getValue());
207        map.put("displayName", ma.getDisplayName());
208        map.put("application.name", ma.getApplication().getName());
209        map.put("application.id", ma.getApplicationId());
210        map.put("attribute", ma.getAttribute());
211        return map;
212    }
213
214    public static Map<String, Object> toMap(Link link) {
215        Map<String, Object> map = new HashMap<>();
216        if (link.getAttributes() != null) {
217            map.putAll(link.getAttributes());
218        }
219        map.put("nativeIdentity", link.getNativeIdentity());
220        map.put("displayName", link.getDisplayableName());
221        map.put("instance", link.getInstance());
222        map.put("identity.name", link.getIdentity().getName());
223        map.put("identity.id", link.getIdentity().getId());
224        map.put("application.name", link.getApplicationName());
225        map.put("application.id", link.getApplicationId());
226        return map;
227    }
228
229}