source: java/net/deterlab/abac/Query.java @ e9b0027

Last change on this file since e9b0027 was a1a9a47, checked in by Ted Faber <faber@…>, 11 years ago

Bump version

  • Property mode set to 100644
File size: 12.9 KB
Line 
1package net.deterlab.abac;
2
3import org.apache.commons.collections15.*;
4
5import edu.uci.ics.jung.graph.*;
6import edu.uci.ics.jung.graph.util.*;
7
8import 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 */
16class 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(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}
Note: See TracBrowser for help on using the repository browser.