1 | package net.deterlab.abac; |
---|
2 | |
---|
3 | import org.apache.commons.collections15.*; |
---|
4 | |
---|
5 | import edu.uci.ics.jung.graph.*; |
---|
6 | import edu.uci.ics.jung.graph.util.*; |
---|
7 | |
---|
8 | import java.util.*; |
---|
9 | |
---|
10 | /** |
---|
11 | * A class for making queries against the graph. It supports direct queries as |
---|
12 | * well as reachability in either direction. See the run method for details. |
---|
13 | * @author <a href="http://abac.deterlab.net">ISI ABAC team</a> |
---|
14 | * @version 1.5 |
---|
15 | */ |
---|
16 | class Query { |
---|
17 | /** Internal graph representation */ |
---|
18 | private Graph<Role,Credential> g; |
---|
19 | /** Count of vertices in the most recent calculate path */ |
---|
20 | private boolean valid; |
---|
21 | |
---|
22 | /** |
---|
23 | * Create a query to be run against the credential graph. |
---|
24 | * @param g a Graph represneting the credentials and implicit connections. |
---|
25 | */ |
---|
26 | public Query(Graph<Role,Credential> g) { |
---|
27 | this.g = g; |
---|
28 | valid = false; |
---|
29 | } |
---|
30 | |
---|
31 | /** |
---|
32 | * Run a query against the graph, returning a graph of the results. If the |
---|
33 | * results are empty or the query fails, an empty graph is returned. When |
---|
34 | * derived edges are involved, the subgraphs that imply those edges are |
---|
35 | * included. |
---|
36 | * @param attr a String containing the role to look for |
---|
37 | * @param prin a String containing the principal |
---|
38 | * @return a Graph with the proof or partial proof. |
---|
39 | */ |
---|
40 | public Graph<Role,Credential> run(String attr, String prin) { |
---|
41 | Role attribute = (!attr.isEmpty()) ? new Role(attr) : null; |
---|
42 | Role principal = (!prin.isEmpty()) ? new Role(prin) : null; |
---|
43 | Graph<Role,Credential> ret = |
---|
44 | Graphs.<Role,Credential>synchronizedDirectedGraph( |
---|
45 | new DirectedSparseGraph<Role,Credential>()); |
---|
46 | ArrayList<Graph<Role, Credential> > subs = |
---|
47 | new ArrayList<Graph<Role, Credential> >(); |
---|
48 | Set<Role> hasAttr = null; |
---|
49 | |
---|
50 | // System.out.println("In run " + attr + " " + prin); |
---|
51 | |
---|
52 | if (attribute == null && principal == null ) { |
---|
53 | valid = false; |
---|
54 | return ret; |
---|
55 | } |
---|
56 | else if (principal != null ) { |
---|
57 | /* This branch happens if attribute is null or not, and either is |
---|
58 | * fine for the bfs routine. */ |
---|
59 | valid = bfs(principal, attribute, ret, |
---|
60 | new forwardSearch<Role, Credential>()); |
---|
61 | } else { |
---|
62 | /* attribute is != null here */ |
---|
63 | valid = bfs(attribute, principal, ret, |
---|
64 | new reverseSearch<Role, Credential>()); |
---|
65 | } |
---|
66 | |
---|
67 | if ( principal == null ) hasAttr = find_principals(attribute); |
---|
68 | else { |
---|
69 | hasAttr =new HashSet<Role>(); |
---|
70 | hasAttr.add(principal); |
---|
71 | } |
---|
72 | |
---|
73 | /* Now ret contains the primary path of the proof, for each linking |
---|
74 | * role or intersection on that path, we construct a Query object to |
---|
75 | * make that subproof. */ |
---|
76 | for (Credential c: ret.getEdges() ) { |
---|
77 | Role head = c.head(); |
---|
78 | Role tail = c.tail(); |
---|
79 | |
---|
80 | if ( head.is_linking()) { |
---|
81 | Query subq = new Query(g); |
---|
82 | subs.add(subq.run(head.linked_role(), tail.principal())); |
---|
83 | if ( !subq.successful()) |
---|
84 | throw new RuntimeException("Cannot prove sub-proof: " + |
---|
85 | head.linked_role() + " <- " + tail.principal() + |
---|
86 | " something is very wrong!"); |
---|
87 | } else if ( head.is_intersection() ) { |
---|
88 | try { |
---|
89 | for (Role r: head.prereqs()) { |
---|
90 | for (Role p : hasAttr) { |
---|
91 | Query subq = new Query(g); |
---|
92 | subs.add(subq.run(r.toString(), p.toString())); |
---|
93 | if ( !subq.successful()) |
---|
94 | throw new RuntimeException( |
---|
95 | "Cannot prove sub-proof: " + |
---|
96 | r.toString() + " <- " + p + |
---|
97 | " something is very wrong!"); |
---|
98 | } |
---|
99 | } |
---|
100 | } |
---|
101 | catch (ABACException ignored) { } |
---|
102 | } |
---|
103 | } |
---|
104 | /* Now subs has all the subsidiary proofs in it, merge 'em */ |
---|
105 | for (Graph<Role, Credential> sg: subs) { |
---|
106 | for (Role r: sg.getVertices()) |
---|
107 | if (!ret.containsVertex(r)) |
---|
108 | ret.addVertex(r); |
---|
109 | for (Credential c: sg.getEdges() ) |
---|
110 | if ( !ret.containsEdge(c)) |
---|
111 | ret.addEdge(c, c.head(), c.tail()); |
---|
112 | } |
---|
113 | return ret; |
---|
114 | } |
---|
115 | |
---|
116 | |
---|
117 | |
---|
118 | /** |
---|
119 | * Returns true after running a query that returns a non-empty set of |
---|
120 | * vertices. |
---|
121 | * @return a boolean, true after running a query that returns a non-empty |
---|
122 | * set of vertices. |
---|
123 | */ |
---|
124 | public boolean successful() { |
---|
125 | return valid; |
---|
126 | } |
---|
127 | |
---|
128 | /** |
---|
129 | * Returns a collection of principals reachable from a Role when |
---|
130 | * traversing edges in the reverse direction. |
---|
131 | * @param n the Role to start from |
---|
132 | * @return a Set containinf the principals |
---|
133 | */ |
---|
134 | public Set<Role> find_principals(Role n) { |
---|
135 | Set<Role> principals = new HashSet<Role>(); |
---|
136 | bfs(n, null, null, new principalSearch<Role,Credential>(principals)); |
---|
137 | return principals; |
---|
138 | } |
---|
139 | |
---|
140 | /** |
---|
141 | * Class extended to specialize the behavior of the bfs routine at the core |
---|
142 | * of the query system. There are two ways to customize - the traversal |
---|
143 | * order which controls if edges are followed in the orientation of the |
---|
144 | * graph or in the reverse orientation and a member called when an edge is |
---|
145 | * added to an output graph. |
---|
146 | */ |
---|
147 | private abstract class bfsSearchDirection<V,E> { |
---|
148 | /** |
---|
149 | * Return a collection of outgoing edges under the search direction's |
---|
150 | * interpretation. |
---|
151 | * @param g the Graph<V, E> being traversed |
---|
152 | * @param v a V, the vertex from which the edges come |
---|
153 | * @return a collection of outgoing edges under the search direction's |
---|
154 | * interpretation. |
---|
155 | */ |
---|
156 | public abstract Collection<E> forwardEdges(Graph<V, E> g, V v); |
---|
157 | /** |
---|
158 | * Return a collection of incoming edges under the search direction's |
---|
159 | * interpretation. |
---|
160 | * @param g the Graph<V, E> being traversed |
---|
161 | * @param v a V, the vertex into which the edges come |
---|
162 | * @return a collection of outgoing edges under the search direction's |
---|
163 | * interpretation. |
---|
164 | */ |
---|
165 | public abstract Collection<E> backwardEdges(Graph<V, E> g, V v); |
---|
166 | /** |
---|
167 | * Return the source of this edge under the search direction's |
---|
168 | * interpretation. |
---|
169 | * @param g the Graph<V, E> being traversed |
---|
170 | * @param e an E, the edge being considered |
---|
171 | * @return the source of this edge under the search direction's |
---|
172 | * interpretation. |
---|
173 | **/ |
---|
174 | public abstract V getSource(Graph<V, E> g, E e); |
---|
175 | /** |
---|
176 | * Return the destination of this edge under the search direction's |
---|
177 | * interpretation. |
---|
178 | * @param g the Graph<V, E> being traversed |
---|
179 | * @param e an E, the edge being considered |
---|
180 | * @return the destination of this edge under the search direction's |
---|
181 | * interpretation. |
---|
182 | **/ |
---|
183 | public abstract V getDest(Graph<V, E> g, E e); |
---|
184 | /** |
---|
185 | * Called when an edge is added; do nothing. |
---|
186 | * @param e an E edge being added. |
---|
187 | */ |
---|
188 | public void keptEdge(E e) { } |
---|
189 | } |
---|
190 | |
---|
191 | /** |
---|
192 | * Class that runs the BFS along the graph as defined. |
---|
193 | */ |
---|
194 | private class forwardSearch<V,E> extends bfsSearchDirection<V,E> { |
---|
195 | /** |
---|
196 | * Return a collection of outgoing edges; outgoing is defined by the |
---|
197 | * graph. |
---|
198 | * @param g the Graph<V, E> being traversed |
---|
199 | * @param v a V, the vertex from which the edges come |
---|
200 | * @return a collection of outgoing edges |
---|
201 | */ |
---|
202 | public Collection<E> forwardEdges(Graph<V, E> g, V v) { |
---|
203 | return g.getOutEdges(v); |
---|
204 | } |
---|
205 | /** |
---|
206 | * Return a collection of incoming edges; incoming is defined by the |
---|
207 | * graph. |
---|
208 | * @param g the Graph<V, E> being traversed |
---|
209 | * @param v a V, the vertex into which the edges come |
---|
210 | * @return a collection of outgoing edges |
---|
211 | */ |
---|
212 | public Collection<E> backwardEdges(Graph<V, E> g, V v) { |
---|
213 | return g.getInEdges(v); |
---|
214 | } |
---|
215 | /** |
---|
216 | * Return the source of this edge as defined by the underlying graph. |
---|
217 | * @param g the Graph<V, E> being traversed |
---|
218 | * @param e an E, the edge being considered |
---|
219 | * @return the source of this edge |
---|
220 | */ |
---|
221 | public V getSource(Graph<V, E> g, E e) { |
---|
222 | return g.getSource(e); |
---|
223 | } |
---|
224 | /** |
---|
225 | * Return the destination of this edge as defined by the underlying |
---|
226 | * graph. |
---|
227 | * @param g the Graph<V, E> being traversed |
---|
228 | * @param e an E, the edge being considered |
---|
229 | * @return the destination of this edge |
---|
230 | */ |
---|
231 | public V getDest(Graph<V, E> g, E e) { |
---|
232 | return g.getDest(e); |
---|
233 | } |
---|
234 | /** |
---|
235 | * Called when an edge is added; do nothing. |
---|
236 | * @param e an E edge being added. |
---|
237 | */ |
---|
238 | public void keptEdge(E e) { } |
---|
239 | } |
---|
240 | |
---|
241 | /** |
---|
242 | * Class that runs the BFS along reversed links. |
---|
243 | */ |
---|
244 | private class reverseSearch<V,E> extends bfsSearchDirection<V,E> { |
---|
245 | /** |
---|
246 | * Return a collection of outgoing edges; outgoing is reversed with |
---|
247 | * respect to the graph. |
---|
248 | * @param g the Graph<V, E> being traversed |
---|
249 | * @param v a V, the vertex from which the edges come |
---|
250 | * @return a collection of outgoing edges |
---|
251 | */ |
---|
252 | public Collection<E> forwardEdges(Graph<V, E> g, V v) { |
---|
253 | return g.getInEdges(v); |
---|
254 | } |
---|
255 | /** |
---|
256 | * Return a collection of incoming edges; incoming is reversed with |
---|
257 | * respect to the graph. |
---|
258 | * @param g the Graph<V, E> being traversed |
---|
259 | * @param v a V, the vertex into which the edges come |
---|
260 | * @return a collection of outgoing edges |
---|
261 | */ |
---|
262 | public Collection<E> backwardEdges(Graph<V, E> g, V v) { |
---|
263 | return g.getOutEdges(v); |
---|
264 | } |
---|
265 | /** |
---|
266 | * Return the source of this edge in the reverse of the underlying |
---|
267 | * graph. |
---|
268 | * @param g the Graph<V, E> being traversed |
---|
269 | * @param e an E, the edge being considered |
---|
270 | * @return the source of this edge |
---|
271 | */ |
---|
272 | public V getSource(Graph<V, E> g, E e) { |
---|
273 | return g.getDest(e); |
---|
274 | } |
---|
275 | /** |
---|
276 | * Return the destination of this edge in the reverse of the underlying |
---|
277 | * graph. |
---|
278 | * @param g the Graph<V, E> being traversed |
---|
279 | * @param e an E, the edge being considered |
---|
280 | * @return the destination of this edge |
---|
281 | */ |
---|
282 | public V getDest(Graph<V, E> g, E e) { |
---|
283 | return g.getSource(e); |
---|
284 | } |
---|
285 | } |
---|
286 | |
---|
287 | /** |
---|
288 | * Class that traverses the graph in reverse asd collects all principal |
---|
289 | * nodes found. |
---|
290 | */ |
---|
291 | private class principalSearch<V, E> |
---|
292 | extends reverseSearch<V, E> { |
---|
293 | /** This collects the principals */ |
---|
294 | private Set<Role> p; |
---|
295 | /** |
---|
296 | * Create a principalSearch that inserts into s |
---|
297 | * @param s a Set of Roles to update (may be null). |
---|
298 | */ |
---|
299 | public principalSearch(Set<Role> s) { |
---|
300 | if ( (p = s) == null ) p = new HashSet<Role>(); |
---|
301 | } |
---|
302 | /** |
---|
303 | * Put principals on valid edges into the attached set. |
---|
304 | * @param e an E (edge) being inserted into the final proof graph |
---|
305 | */ |
---|
306 | public void keptEdge(E e) { |
---|
307 | if ( !(e instanceof Credential) ) return; |
---|
308 | |
---|
309 | Credential c = (Credential) e; |
---|
310 | if (c.tail().is_principal()) p.add(c.tail()); |
---|
311 | } |
---|
312 | } |
---|
313 | |
---|
314 | /** |
---|
315 | * Conduct a breadth first search along the underlying graph from src to |
---|
316 | * dest. If a valid path is found, add it to path (which mat be null). |
---|
317 | * The dir parameter controls the direction of the traversal and may invoke |
---|
318 | * a side effect when an edge is added to path. Though the graph may be |
---|
319 | * traversed in either direction, path is always in the same sense as the |
---|
320 | * underlying graph. If dest is null the bfs runs until all |
---|
321 | * reachable nodes have been visited and that graph is returned as a |
---|
322 | * success. |
---|
323 | * @param src the Role to start the search from |
---|
324 | * @param dest the Role to find a path to |
---|
325 | * @param path a Graph<Role, Credential> in which the result path is |
---|
326 | * returned |
---|
327 | * @param dir a bfsSearchDirection<Role, Credential> that defines direction |
---|
328 | * of search and side effects |
---|
329 | * @return true if the path is found |
---|
330 | */ |
---|
331 | private boolean bfs(Role src, Role dest, |
---|
332 | Graph<Role, Credential> path, |
---|
333 | bfsSearchDirection<Role, Credential> dir) { |
---|
334 | Queue<Role> q = new ArrayDeque<Role>(); |
---|
335 | DirectedGraph<Role, Credential> bfs = |
---|
336 | new DirectedSparseGraph<Role, Credential>(); |
---|
337 | HashSet<Role> visited = new HashSet<Role>(); |
---|
338 | boolean foundDest = false; |
---|
339 | |
---|
340 | if (src == null || !g.containsVertex(src)) |
---|
341 | return false; |
---|
342 | |
---|
343 | q.add(src); |
---|
344 | visited.add(src); |
---|
345 | bfs.addVertex(src); |
---|
346 | |
---|
347 | while ( q.size() > 0 && !foundDest) { |
---|
348 | Role x = q.remove(); |
---|
349 | for (Credential e: dir.forwardEdges(g, x)) { |
---|
350 | Role y = dir.getDest(g, e); |
---|
351 | if ( !visited.contains(y)) { |
---|
352 | visited.add(y); |
---|
353 | bfs.addVertex(y); |
---|
354 | bfs.addEdge(e, e.tail(), e.head()); |
---|
355 | if ( dest != null && y.equals(dest)) { |
---|
356 | foundDest = true; |
---|
357 | break; |
---|
358 | } |
---|
359 | q.add(y); |
---|
360 | } |
---|
361 | } |
---|
362 | } |
---|
363 | /* If foundDest is true, we know that the path from src to dest is |
---|
364 | * there, so backtrack from dest to source, adding to path as we go. |
---|
365 | * If we didn't find anything return the BFS as a partial proof. Note |
---|
366 | * that whatever direction the search progresses, this builds a return |
---|
367 | * graph that has edges directed the same way as edges in g. |
---|
368 | * |
---|
369 | * If path is null, don't add the nodes to it (duh) but do decide which |
---|
370 | * nodes would be kept in case dir needs that info. E.g. |
---|
371 | * findPrincipals does not need the path to the principals, just their |
---|
372 | * identity. |
---|
373 | */ |
---|
374 | if ( foundDest ) { |
---|
375 | Role v = dest; |
---|
376 | if (path != null && !path.containsVertex(dest)) |
---|
377 | path.addVertex(dest); |
---|
378 | |
---|
379 | while (!v.equals(src)) { |
---|
380 | Collection<Credential> backEdges = dir.backwardEdges(bfs, v); |
---|
381 | |
---|
382 | if (backEdges.size() != 1 ) |
---|
383 | System.err.println("What!?"); |
---|
384 | for (Credential e: backEdges) { |
---|
385 | Role u = dir.getSource(bfs, e); |
---|
386 | |
---|
387 | if ( path != null && !path.containsVertex(u)) |
---|
388 | path.addVertex(u); |
---|
389 | if ( path != null && !path.containsEdge(e)) |
---|
390 | path.addEdge(e, e.tail(), e.head()); |
---|
391 | dir.keptEdge(e); |
---|
392 | v = u; |
---|
393 | } |
---|
394 | } |
---|
395 | } else { |
---|
396 | for (Role v: bfs.getVertices() ) |
---|
397 | if (path != null && !path.containsVertex(v)) |
---|
398 | path.addVertex(v); |
---|
399 | for (Credential e: bfs.getEdges()) { |
---|
400 | if ( path != null && !path.containsEdge(e)) |
---|
401 | path.addEdge(e, e.tail(), e.head()); |
---|
402 | dir.keptEdge(e); |
---|
403 | } |
---|
404 | } |
---|
405 | return foundDest || dest == null; |
---|
406 | } |
---|
407 | } |
---|