View Javadoc
1   package io.oasp.module.security.common.base.accesscontrol;
2   
3   import io.oasp.module.security.common.api.accesscontrol.AccessControl;
4   import io.oasp.module.security.common.api.accesscontrol.AccessControlGroup;
5   import io.oasp.module.security.common.api.accesscontrol.AccessControlPermission;
6   import io.oasp.module.security.common.api.accesscontrol.AccessControlProvider;
7   import io.oasp.module.security.common.api.accesscontrol.AccessControlSchema;
8   
9   import java.util.HashMap;
10  import java.util.HashSet;
11  import java.util.List;
12  import java.util.Map;
13  import java.util.Set;
14  
15  import org.slf4j.Logger;
16  import org.slf4j.LoggerFactory;
17  
18  /**
19   * This is the abstract base implementation of {@link AccessControlProvider}.<br/>
20   * ATTENTION:<br/>
21   * You need to call {@link #initialize(AccessControlSchema)} from the derived implementation.
22   *
23   * @author hohwille
24   */
25  public abstract class AbstractAccessControlProvider implements AccessControlProvider {
26  
27    /** Logger instance. */
28    private static final Logger LOG = LoggerFactory.getLogger(AbstractAccessControlProvider.class);
29  
30    /** @see #getAccessControl(String) */
31    private final Map<String, AccessControl> id2nodeMap;
32  
33    /**
34     * The constructor.
35     */
36    public AbstractAccessControlProvider() {
37  
38      super();
39      this.id2nodeMap = new HashMap<>();
40    }
41  
42    /**
43     * Performs the required initialization of this class.
44     *
45     * @param config is the {@link AccessControlSchema}.
46     */
47    protected void initialize(AccessControlSchema config) {
48  
49      LOG.debug("Initializing.");
50      List<AccessControlGroup> groups = config.getGroups();
51      if (groups.size() == 0) {
52        throw new IllegalStateException("AccessControlSchema is empty - please configure at least one group!");
53      }
54      Set<AccessControlGroup> toplevelGroups = new HashSet<>(groups);
55      for (AccessControlGroup group : groups) {
56        collectAccessControls(group, toplevelGroups);
57        checkForCyclicDependencies(group, new HashSet<AccessControlGroup>());
58      }
59    }
60  
61    /**
62     * Checks that the given {@link AccessControlGroup} has no cyclic {@link AccessControlGroup#getInherits() inheritance
63     * graph}.
64     *
65     * @param group is the {@link AccessControlGroup} to check.
66     * @param visitedGroups is where the {@link AccessControlGroup} are collected to detect cycles.
67     */
68    protected void checkForCyclicDependencies(AccessControlGroup group, Set<AccessControlGroup> visitedGroups) {
69  
70      boolean added = visitedGroups.add(group);
71      if (!added) {
72        // mmm NodeCycleException would be very helpful here...
73        throw new IllegalStateException("Cyclic inheritance of access control groups detected for " + group);
74      }
75      for (AccessControlGroup inheritedGroup : group.getInherits()) {
76        checkForCyclicDependencies(inheritedGroup, visitedGroups);
77      }
78    }
79  
80    /**
81     * Called from {@link #initialize(AccessControlSchema)} to collect all {@link AccessControl}s recursively.
82     *
83     * @param group the {@link AccessControlGroup} to traverse.
84     * @param toplevelGroups is the {@link Set} of all {@link AccessControlGroup}s from
85     *        {@link AccessControlSchema#getGroups()}.
86     */
87    protected void collectAccessControls(AccessControlGroup group, Set<AccessControlGroup> toplevelGroups) {
88  
89      // TODO hohwille Java HashMap buggy???
90      // if (!toplevelGroups.contains(group)) {
91      // throw new IllegalStateException("Invalid group not declared as top-level group in schema: " + group);
92      // }
93      AccessControl old = this.id2nodeMap.put(group.getId(), group);
94      if (old != null) {
95        LOG.debug("Already visited access control group {}", group);
96        if (old != group) {
97          throw new IllegalStateException("Invalid security configuration: duplicate groups with id " + group.getId()
98              + "!");
99        }
100       // group has already been visited, stop recursion...
101       return;
102     } else {
103       LOG.debug("Registered access control group {}", group);
104     }
105     for (AccessControlPermission permission : group.getPermissions()) {
106       old = this.id2nodeMap.put(permission.getId(), permission);
107       if (old != null) {
108         // throw new IllegalStateException("Invalid security configuration: duplicate permission with id "
109         // + permission.getId() + "!");
110         LOG.warn("Security configuration contains duplicate permission with id {}.", permission.getId());
111       } else {
112         LOG.debug("Registered access control permission {}", permission);
113       }
114     }
115     for (AccessControlGroup inheritedGroup : group.getInherits()) {
116       collectAccessControls(inheritedGroup, toplevelGroups);
117     }
118   }
119 
120   @Override
121   public AccessControl getAccessControl(String nodeId) {
122 
123     return this.id2nodeMap.get(nodeId);
124   }
125 
126   @Override
127   public boolean collectAccessControlIds(String groupId, Set<String> permissions) {
128 
129     AccessControl node = getAccessControl(groupId);
130     if (node instanceof AccessControlGroup) {
131       collectPermissionIds((AccessControlGroup) node, permissions);
132     } else {
133       // node does not exist or is a flat AccessControlPermission
134       permissions.add(groupId);
135     }
136     return (node != null);
137   }
138 
139   /**
140    * Recursive implementation of {@link #collectAccessControlIds(String, Set)} for {@link AccessControlGroup}s.
141    *
142    * @param group is the {@link AccessControlGroup} to traverse.
143    * @param permissions is the {@link Set} used to collect.
144    */
145   public void collectPermissionIds(AccessControlGroup group, Set<String> permissions) {
146 
147     boolean added = permissions.add(group.getId());
148     if (!added) {
149       // we have already visited this node, stop recursion...
150       return;
151     }
152     for (AccessControlPermission permission : group.getPermissions()) {
153       permissions.add(permission.getId());
154     }
155     for (AccessControlGroup inheritedGroup : group.getInherits()) {
156       collectPermissionIds(inheritedGroup, permissions);
157     }
158   }
159 
160   @Override
161   public boolean collectAccessControls(String groupId, Set<AccessControl> permissions) {
162 
163     AccessControl node = getAccessControl(groupId);
164     if (node == null) {
165       return false;
166     }
167     if (node instanceof AccessControlGroup) {
168       collectPermissionNodes((AccessControlGroup) node, permissions);
169     } else {
170       // node is a flat AccessControlPermission
171       permissions.add(node);
172     }
173     return true;
174   }
175 
176   /**
177    * Recursive implementation of {@link #collectAccessControls(String, Set)} for {@link AccessControlGroup}s.
178    *
179    * @param group is the {@link AccessControlGroup} to traverse.
180    * @param permissions is the {@link Set} used to collect.
181    */
182   public void collectPermissionNodes(AccessControlGroup group, Set<AccessControl> permissions) {
183 
184     boolean added = permissions.add(group);
185     if (!added) {
186       // we have already visited this node, stop recursion...
187       return;
188     }
189     for (AccessControlPermission permission : group.getPermissions()) {
190       permissions.add(permission);
191     }
192     for (AccessControlGroup inheritedGroup : group.getInherits()) {
193       collectPermissionNodes(inheritedGroup, permissions);
194     }
195   }
196 
197 }