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

abac0-leakabac0-meimei-idmei-rt0-nmei_rt0tvf-new-xml
Last change on this file since b89cfd8 was 4560b65, checked in by Ted Faber <faber@…>, 12 years ago

Bump version number

  • Property mode set to 100644
File size: 12.6 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.4
15 */
16class Query {
17    /** Internal graph representation */
18    private Graph<Role,Credential> g;
19    /** Count of vertices in g */
20    private int vertex_count;
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    }
29
30    /**
31     * Run a query against the graph, returning a graph of the results. If the
32     * results are empty or the query fails, an empty graph is returned. When
33     * derived edges are involved, the subgraphs that imply those edges are
34     * included.
35     * @param attr a String containing the role to look for
36     * @param prin a String containing the principal
37     * @return a Graph with the proof or partial proof.
38     */
39    public Graph<Role,Credential> run(String attr, String prin) {
40        Role attribute = null, principal = null;;
41
42        if (!attr.isEmpty()) attribute = new Role(attr);
43        if (!prin.isEmpty()) principal = new Role(prin);
44
45        Graph<Role,Credential> ret =
46            Graphs.<Role,Credential>synchronizedDirectedGraph(
47                    new DirectedSparseGraph<Role,Credential>());
48
49        /* empty attribute, non-empty principal: find everywhere the principal
50         * can go */
51        if (attr.isEmpty() && !prin.isEmpty()) {
52            if (g.containsVertex(principal)) {
53                CollectReversePath collect = new CollectReversePath(ret);
54                forward_dfs(principal, collect);
55            }
56        }
57
58        /* otherwise we're going to do some kind of a reverse dfs */
59        else if (g.containsVertex(attribute)) { 
60            if ( prin == null || prin.isEmpty()) {
61                CollectQueryGraph  collect = new CollectQueryGraph(ret);
62                reverse_dfs(attribute, collect);
63            }
64            else {
65                if ( g.containsVertex(principal)) {
66                    CollectQueryPath  collect = new CollectQueryPath(ret,
67                            principal);
68                    reverse_dfs(attribute, collect);
69                }
70            }
71        }
72
73        vertex_count = ret.getVertexCount();
74
75        return ret;
76    }
77
78    /**
79     * Returns true after running a query that returns a non-empty set of
80     * vertices.
81     * @return a boolean, true after running a query that returns a non-empty
82     * set of vertices.
83     */
84    public boolean successful() {
85        return vertex_count > 0;
86    }
87
88    /**
89     * Returns a collection of principals reachable from a Role when
90     * traversing edges in the reverse direction.
91     * @param n the Role to start from
92     * @return a Set containinf the principals
93     */
94    public Set<Role> find_principals(Role n) {
95        Set<Role> principals = new HashSet<Role>();
96   
97        GetPrincipals f = new GetPrincipals(principals);
98        reverse_dfs(n, f);
99        return principals;
100    }
101
102
103    /**
104     * Interface to pass to the reverse_dfs class that operates on nodes in the
105     * graph as part of the depth first search (DFS).
106     */
107    interface DfsFcn {
108        /**
109         * Called when the node is first encountered on the DFS.
110         * @param x the Role being visited
111         */
112        public void node(Role x);
113        /**
114         * Called when all the node's children have been visited on the DFS.
115         * @param x the Role being visited
116         */
117        public void node_after(Role x);
118    }
119
120    /**
121     * Class used to pull all the principals out of a reverse_dfs searched
122     * graph.  It takes a collection to the constructor and adds all principals
123     * it sees to that collection.  In no collection is passed in, it creates a
124     * local HashSet of vertices.  The collection is accessible via the p
125     * (i.e., principals) member.
126     */
127    private class GetPrincipals implements DfsFcn {
128        /** The principals collected */
129        public Collection<Role> p;
130
131        /**
132         * Initialize the principals collection.
133         * @param vc the initial collection.  May be null.
134         */
135        public GetPrincipals(Collection<Role> vc) {
136            if ( vc != null) { p =vc;}
137            else { p = new HashSet<Role>(); }
138        }
139
140        /**
141         * Adds principals to the collection, called by the DFS.
142         * @param r the Role visited.
143         */
144        public void node(Role r) {
145            if ( r.is_principal() ) p.add(r);
146        }
147        /**
148         * Does nothing.
149         * @param r a Role, a dummy
150         */
151        public void node_after(Role r) { }
152    }
153
154    /**
155     * Collect a subgraph. Add every node and outgoing arc we encounter, and
156     * pull in the linking node and intersection subtrees as well. Because it's
157     * possible that these subtrees have been encountered more than once, we're
158     * careful not to pull them in twice. The created graph is found in the
159     * public ng member.
160     */
161    private class CollectQueryGraph implements DfsFcn {
162        /** The subgraph being collected */
163        public Graph<Role, Credential> ng;
164        /** The linking roles already added to the sub-graph */
165        protected HashSet<Role> linking_roles_seen;
166        /** The intersection roles already added to the sub-graph */
167        protected HashSet<Role> intersection_roles_seen;
168
169        /**
170         * Initialize the graph into which to gather nodes/edges.
171         * @param g A graph to collect
172         */
173        public CollectQueryGraph(Graph<Role,Credential> g) {
174            ng = g;
175            linking_roles_seen = new HashSet<Role>();
176            intersection_roles_seen = new HashSet<Role>();
177        }
178
179
180        /**
181         * Do the collection on each node we visit.
182         * @param r a Role, the current node in the DFS.
183         */
184        public void node(Role r) {
185            if (r.is_linking() && !linking_roles_seen.contains(r)) {
186                // Collect this linking role subgraph
187                reverse_dfs(new Role(r.principal_part()), this);
188                linking_roles_seen.add(r);
189            }
190
191            // collect subgraph for each intersection prereq
192            if (r.is_intersection() && !intersection_roles_seen.contains(r)) {
193                for (Role prereq : r.prereqs())
194                    reverse_dfs(prereq, this);
195                intersection_roles_seen.add(r);
196            }
197
198            // Add this node's children
199            for (Credential c : g.getInEdges(r) ) {
200                Role tail = g.getSource(c);
201                ng.addEdge(c, tail, r); // FIXME tail and r can come from c [?]
202            }
203        }
204        /**
205         * Does nothing.
206         * @param r a Role, a dummy
207         */
208        public void node_after(Role r) { }
209
210    }
211
212    /**
213     * Collect the path from one node to another. It includes vertices that
214     * are traversed to reach the node. It also includes subgraphs that imply
215     * the linking/intersection edges traversed.
216     */
217    private class CollectQueryPath extends CollectQueryGraph {
218        /** Vertices on the path to the principal */
219        HashSet<Role> onPath;
220        /**
221         * Establish the collection graph and the destination.
222         * @param g the graph that collects the result
223         * @param dest the Role to find
224         */
225        public CollectQueryPath(Graph<Role,Credential> g, Role dest) {
226            super(g);
227            onPath = new HashSet<Role>();
228            onPath.add(dest);
229        }
230
231        /**
232         * Does nothing.
233         * @param r a Role, a dummy
234         */
235        public void node(Role r) { }
236
237        /**
238         * Do the collection on each node we visit.
239         * @param r a Role, the current node in the DFS.
240         */
241        public void node_after(Role r) {
242            for (Credential c : g.getInEdges(r)) {
243                Role child = c.tail();
244
245                if (onPath.contains(child)) {
246                    Credential edge = g.findEdge(child, r);
247                    if (edge == null)
248                        throw new RuntimeException("Credential missing from " +
249                                "parent graph, state is messed up");
250
251                    ng.addEdge(edge, child, r);
252                    onPath.add(r);
253
254                    // For linking roles, collect the whole subgraph of the
255                    // authorizer
256                    if (r.is_linking()) {
257                        CollectQueryPath link = new CollectQueryPath(ng, 
258                                new Role(child.principal_part()));
259                        reverse_dfs(new Role(r.principal_part()), link);
260                    }
261
262                    // intersection: collect the subgraph of each prereq
263                    else if (r.is_intersection()) {
264                        CollectQueryPath prereq_finder = 
265                            new CollectQueryPath(ng, child);
266                        for (Role prereq : r.prereqs())
267                            reverse_dfs(prereq, prereq_finder);
268                    }
269                }
270            }
271        }
272    }
273
274    /**
275     * Collect the path that a node can reach. It includes subgraphs that imply
276     * the linked/intersection edges traversed.
277     */
278    private class CollectReversePath extends CollectQueryGraph {
279        /**
280         * Initialize the collection graph.
281         * @param g a Graph that collects the results.
282         */
283        public CollectReversePath(Graph<Role,Credential> g) {
284            super(g);
285        }
286
287        /**
288         * add all of the node's parents
289         * @param r a Role, the current node being visited
290         */
291        public void node(Role r) {
292            for (Credential c : g.getOutEdges(r)) {
293                Role head = g.getDest(c);
294                Credential cred = g.findEdge(r, head);
295                if (cred == null)
296                    throw new RuntimeException("Credential missing from " + 
297                            "parent graph, state is messed up");
298
299                ng.addEdge(cred, r, head);
300            }
301        }
302
303        /**
304         * Do the collection on each node we visit.
305         * @param r a Role, the current node in the DFS.
306         */
307        public void node_after(Role r) {
308            for (Credential c : g.getOutEdges(r)) {
309                Role parent = g.getDest(c);
310
311                // if any of the links we follow is from a linking node, copy
312                // the subgraph that implies said link
313                if (parent.is_linking()) {
314                    CollectQueryPath link = new CollectQueryPath(ng, 
315                            new Role(r.principal_part()));
316                    reverse_dfs(new Role(parent.principal_part()), link);
317                }
318
319                // intersection roles do not need to be treated specially
320                // because the forward path from the principal will traverse all
321                // edges that imply the edge to the intersection node
322            }
323        }
324    }
325
326    /**
327     * Interface to the reverse_dfs member that does allocates the visited map
328     * so the user doesn't have to.
329     * @param r a Role from which to start the search
330     * @param f a DfsFcn that customizes the search operation
331     */
332    private void reverse_dfs(Role r, DfsFcn f) {
333        reverse_dfs(r, new HashSet<Role>(), f);
334    }
335
336    /**
337     * Member function to walk the edges in reverse from a given Role.  Each
338     * Role is visited once and has the node() function of the dfs_fcn called
339     * on it.  Order is not guaranteed.
340     * @param r a Role from which to start the search
341     * @param visited a HashSet of Roles that tells which nodes have been
342     * visited.
343     * @param f a DfsFcn that customizes the search operation
344     */
345    private void reverse_dfs(Role r, HashSet<Role> visited,
346            DfsFcn f) {
347        if (visited.contains(r)) return;
348
349        f.node(r);
350        visited.add(r);
351
352        for (Credential c : g.getInEdges(r)) 
353            reverse_dfs(g.getSource(c), visited, f);
354        f.node_after(r);
355    }
356
357    /**
358     * Interface to the forward_dfs member that allocates the visited map so
359     * the user doesn't have to.
360     * @param r a Role from which to start the search
361     * @param f a DfsFcn that customizes the search operation
362     */
363    private void forward_dfs(Role r, DfsFcn f) {
364        forward_dfs(r, new HashSet<Role>(), f);
365    }
366
367    /**
368     * Member function to walk the edges from a given Role. Each Role is
369     * visited once and has the node() function of the dfs_fcn called on it.
370     * Order is not guaranteed
371     * @param r a Role from which to start the search
372     * @param visited a HashSet of Roles that tells which nodes have been
373     * visited.
374     * @param f a DfsFcn that customizes the search operation
375     */
376    private void forward_dfs(Role r, Set<Role> visited, DfsFcn f) {
377        if (visited.contains(r)) return;
378
379        f.node(r);
380        visited.add(r);
381
382        for (Credential c : g.getOutEdges(r))
383            forward_dfs(g.getDest(c), visited, f);
384        f.node_after(r);
385    }
386}
Note: See TracBrowser for help on using the repository browser.