All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.cesecore.roles.AccessRulesHelper Maven / Gradle / Ivy

/*************************************************************************
 *                                                                       *
 *  CESeCore: CE Security Core                                           *
 *                                                                       *
 *  This software is free software; you can redistribute it and/or       *
 *  modify it under the terms of the GNU Lesser General Public           *
 *  License as published by the Free Software Foundation; either         *
 *  version 2.1 of the License, or any later version.                    *
 *                                                                       *
 *  See terms of license at gnu.org.                                     *
 *                                                                       *
 *************************************************************************/
package org.cesecore.roles;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map.Entry;

import org.apache.log4j.Logger;

/**
 * Helper methods for interactions with maps of access rules and resources.
 *
 * @version $Id: AccessRulesHelper.java 28354 2018-02-22 12:14:54Z anatom $
 */
public abstract class AccessRulesHelper {

    private static final Logger log = Logger.getLogger(AccessRulesHelper.class);

    /** @return true if the provided map of access rules allows access to all the given resources */
    public static boolean hasAccessToResources(final HashMap accessRules, final String...resources) {
        if (resources!=null) {
            for (final String resource : resources) {
                if (!AccessRulesHelper.hasAccessToResource(accessRules, resource)) {
                    return false;
                }
            }
        }
        return true;
    }

    /** @return true if the provided map of access rules allows access to the given resource */
    public static boolean hasAccessToResource(final HashMap accessRules, final String resource) {
        if (resource==null || resource.charAt(0)!='/') {
            return false;
        }
        // Normalize from "/a/b/c" to "/a/b/c/"
        final String resourceWithTrailingSlash = resource.endsWith("/") ? resource : resource + "/";
        int lastSlashIndex = resourceWithTrailingSlash.length()+1;
        while ((lastSlashIndex = resourceWithTrailingSlash.lastIndexOf('/', lastSlashIndex-1))!=-1) {
            final String subString = resourceWithTrailingSlash.substring(0, lastSlashIndex);
            Boolean state = accessRules.get(subString);
            if (state==null) {
                // Check if the non-normalized form is present
                state = accessRules.get(subString + "/");
            }
            if (state!=null) {
            	if (log.isTraceEnabled()) {
            	    log.trace("hasAccessToResource: "+resource+", "+state.booleanValue());
            	}
                return state.booleanValue();
            }
        }
        if (log.isTraceEnabled()) {
            log.trace("hasAccessToResource: "+resource+", "+false);
        }
        return false;
    }

    /** Normalize access rules tree (make sure rules always end with a '/') */
    public static void normalizeResources(final HashMap accessRules) {
        // For each rule, check if there are higher level rules (e.g. shorter path) with the same access state
        for (final String resource : new ArrayList<>(accessRules.keySet())) {
            if (!resource.endsWith("/")) {
                final String resourceWithTrailingSlash = resource + "/";
                final Boolean value = accessRules.remove(resource);
                accessRules.put(resourceWithTrailingSlash, value);
            }
        }
    }

    /** Normalize access rules (make sure rules always end with a '/') */
    public static String normalizeResource(final String resource) {
        if (!resource.endsWith("/")) {
            return resource + "/";
        }
        return resource;
    }

    /** Remove redundant rules. Assumes parameter is in normalized form. */
    public static void minimizeAccessRules(final HashMap accessRules) {
        // For each rule, check if there are higher level rules (e.g. shorter path) with the same access state
        for (final String resourceWithTrailingSlash : new ArrayList<>(accessRules.keySet())) {
            final Boolean currentState = accessRules.get(resourceWithTrailingSlash);
            if (currentState==null) {
                // Already removed from map
                continue;
            }
            int lastSlashIndex = resourceWithTrailingSlash.length()+1;
            while ((lastSlashIndex = resourceWithTrailingSlash.lastIndexOf('/', lastSlashIndex-1))!=-1) {
                if (lastSlashIndex==resourceWithTrailingSlash.length()-1) {
                    continue;
                }
                final String subString = resourceWithTrailingSlash.substring(0, lastSlashIndex+1);
                final Boolean state = accessRules.get(subString);
                if (state!=null) {
                    if (state.booleanValue()==currentState.booleanValue()) {
                        // A short path already provides this rule
                        accessRules.remove(resourceWithTrailingSlash);
                    } else {
                        // The rule is needed, since it reverts a short paths state
                    }
                    break;
                }
            }
        }
        // Remove all top level deny rules (if nothing is explicitly permitted, we don't need to deny it)
        for (final String resourceWithTrailingSlash : new ArrayList<>(accessRules.keySet())) {
            final Boolean currentState = accessRules.get(resourceWithTrailingSlash);
            if (currentState!=null && !currentState.booleanValue()) {
                boolean needed = false;
                int lastSlashIndex = resourceWithTrailingSlash.length()+1;
                while ((lastSlashIndex = resourceWithTrailingSlash.lastIndexOf('/', lastSlashIndex-1))!=-1) {
                    if (lastSlashIndex==resourceWithTrailingSlash.length()-1) {
                        continue;
                    }
                    final String subString = resourceWithTrailingSlash.substring(0, lastSlashIndex+1);
                    if (accessRules.get(subString)!=null) {
                        needed = true;
                        break;
                    }
                }
                if (!needed) {
                    accessRules.remove(resourceWithTrailingSlash);
                }
            }
        }
    }

    /** @return the rules for all resources granted by either sets of normalized accessRules. (The union of the sets.) */
    public static HashMap getAccessRulesUnion(final HashMap accessRules1, final HashMap accessRules2) {
        final HashMap accessRules = new HashMap<>();
        /*
         * Simple example of algorithm:
         *
         * /a/   allow
         * /a/b/ deny    (remove this deny, since it is granted by other role)
         * /b/   deny    (keep since it is not granted by other role)
         *
         * /a/   allow
         * /a/c/ deny    (remove this deny, since it is granted by other role)
         * /c/d  deny    (keep since it is not granted by other role)
         * →
         * /a/   allow
         * /b/   deny
         * /c/d  deny
         */
        // Keep allow rules from accessRules1 and deny rules from accessRules1 that are not granted by accessRules2
        for (final Entry entry : accessRules1.entrySet()) {
            if (entry.getValue().booleanValue() || !hasAccessToResource(accessRules2, entry.getKey())) {
                accessRules.put(entry.getKey(), entry.getValue());
            }
        }
        // Keep allow rules from accessRules1 and deny rules from accessRules1 that are not granted by accessRules2
        for (final Entry entry : accessRules2.entrySet()) {
            if (entry.getValue().booleanValue() || !hasAccessToResource(accessRules1, entry.getKey())) {
                accessRules.put(entry.getKey(), entry.getValue());
            }
        }
        minimizeAccessRules(accessRules);
        return accessRules;
    }

    /** @return the rules for all resources granted by both sets of normalized accessRules. (The intersection of the sets.) */
    public static HashMap getAccessRulesIntersection(final HashMap accessRules1, final HashMap accessRules2) {
        final HashMap accessRules = new HashMap<>();
        /*
         * Simple example of algorithm:
         *
         * /a/   allow
         * /a/b/ deny
         * /b/   deny
         * /c/d/ allow
         *
         * /a/   allow
         * /a/c/ deny
         * /c/d/ deny
         * →
         * /a/   allow
         * /a/b/ deny
         * /a/c/ deny
         * /b/   deny
         * /c/d/ deny
         */
        // Keep deny rules from accessRules1 and allow rules from accessRules1 and that are also granted by accessRules2
        for (final Entry entry : accessRules1.entrySet()) {
            if (!entry.getValue().booleanValue() || hasAccessToResource(accessRules2, entry.getKey())) {
                accessRules.put(entry.getKey(), entry.getValue());
            }
        }
        // Keep deny rules from accessRules2 and allow rules from accessRules2 and that are also granted by accessRules1
        for (final Entry entry : accessRules2.entrySet()) {
            if (!entry.getValue().booleanValue()) {
                accessRules.put(entry.getKey(), entry.getValue());
            } else if (hasAccessToResource(accessRules1, entry.getKey())) {
                final Boolean currentValue = accessRules.get(entry.getKey());
                if (currentValue==null || currentValue.booleanValue()) {
                    // Only overwrite empty or allow rules
                    accessRules.put(entry.getKey(), entry.getValue());
                }
            }
        }
        minimizeAccessRules(accessRules);
        return accessRules;
    }

    /** Sort the provided access rules. (Useful for more readable persistence format.) */
    public static void sortAccessRules(final LinkedHashMap accessRules) {
        final List> sortEntryList = getAsListSortedByKey(accessRules);
        accessRules.clear();
        for (final Entry entry : sortEntryList) {
            accessRules.put(entry.getKey(), entry.getValue());
        }
    }

    /** @return the map sorted by keys */
    public static  List> getAsListSortedByKey(final HashMap accessRulesMap) {
        final List> accessRulesList = new ArrayList<>(accessRulesMap.entrySet());
        Collections.sort(accessRulesList, new Comparator>() {
            @Override
            public int compare(final Entry entry1, final Entry entry2) {
                return String.valueOf(entry1.getKey()).compareTo(String.valueOf(entry2.getKey()));
            }
        });
        return accessRulesList;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy