001package com.identityworksllc.iiq.common.access;
002
003import com.identityworksllc.iiq.common.ConfigurationMerger;
004import com.identityworksllc.iiq.common.Metered;
005import sailpoint.api.SailPointContext;
006import sailpoint.object.Configuration;
007import sailpoint.tools.GeneralException;
008import sailpoint.tools.Util;
009
010import java.util.HashMap;
011import java.util.Map;
012
013import static com.identityworksllc.iiq.common.access.DelegatedAccessController.getDelegatedAccessConfig;
014
015/**
016 * Assembles the controls (Common Security assertions) that are present on the
017 * given purpose string, or any of its parent substrings.
018 *
019 * Additionally, any controls in the special key 'global' will be added.
020 *
021 * More specific assertions will override less specific ones.
022 */
023public class DelegatedAccessAssembler {
024
025    private final SailPointContext context;
026
027    public DelegatedAccessAssembler(SailPointContext context) {
028        this.context = context;
029    }
030
031    /**
032     * Assembles the thing controls for the given target and purpose
033     *
034     * @param purpose The input purpose
035     * @return The assembled / merged set of controls for the given purpose
036     * @throws GeneralException if any failures occur assembling controls
037     */
038    public Map<String, Object> assembleControls(String purpose) throws GeneralException {
039        return Metered.meter("DelegatedAccessAssembler.assembleControls", () -> {
040            Configuration delegatedAccessConfig = getDelegatedAccessConfig(context);
041
042            Map<String, Object> controls = new HashMap<>();
043
044            if (delegatedAccessConfig.get("global") instanceof Map) {
045                @SuppressWarnings("unchecked")
046                Map<String, Object> globalControls = (Map<String, Object>) delegatedAccessConfig.get("global");
047
048                controls.putAll(globalControls);
049            }
050
051            if (Util.isNotNullOrEmpty(purpose)) {
052                String[] purposePieces = purpose.split(DelegatedAccessConstants.TOKEN_DIVIDER);
053                String assembledPurpose = "";
054                // Loop over each substring of the purpose, such as a, a:b, a:b:c, etc.
055                // TODO support wildcards, such as a:*:c
056                for(String purposePiece : purposePieces) {
057                    if (!assembledPurpose.isEmpty()) {
058                        assembledPurpose += DelegatedAccessConstants.TOKEN_DIVIDER;
059                    }
060                    assembledPurpose += purposePiece;
061                    if (delegatedAccessConfig.get(assembledPurpose) instanceof Map) {
062                        @SuppressWarnings("unchecked")
063                        Map<String, Object> purposeControls = (Map<String, Object>) delegatedAccessConfig.get(assembledPurpose);
064
065                        // TODO handle anyOf / allOf nested values
066                        controls = ConfigurationMerger.mergeConfigurations(controls, purposeControls);
067                    }
068                }
069            }
070            return controls;
071        });
072    }
073}