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

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

Missed a couple cases (ant didn't rebuild)

  • 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                try {
194                    for (Role prereq : r.prereqs())
195                        reverse_dfs(prereq, this);
196                }
197                catch (ABACException ignored) { }
198                intersection_roles_seen.add(r);
199            }
200
201            // Add this node's children
202            for (Credential c : g.getInEdges(r) ) {
203                Role tail = g.getSource(c);
204                ng.addEdge(c, tail, r); // FIXME tail and r can come from c [?]
205            }
206        }
207        /**
208         * Does nothing.
209         * @param r a Role, a dummy
210         */
211        public void node_after(Role r) { }
212
213    }
214
215    /**
216     * Collect the path from one node to another. It includes vertices that
217     * are traversed to reach the node. It also includes subgraphs that imply
218     * the linking/intersection edges traversed.
219     */
220    private class CollectQueryPath extends CollectQueryGraph {
221        /** Vertices on the path to the principal */
222        HashSet<Role> onPath;
223        /**
224         * Establish the collection graph and the destination.
225         * @param g the graph that collects the result
226         * @param dest the Role to find
227         */
228        public CollectQueryPath(Graph<Role,Credential> g, Role dest) {
229            super(g);
230            onPath = new HashSet<Role>();
231            onPath.add(dest);
232        }
233
234        /**
235         * Does nothing.
236         * @param r a Role, a dummy
237         */
238        public void node(Role r) { }
239
240        /**
241         * Do the collection on each node we visit.
242         * @param r a Role, the current node in the DFS.
243         */
244        public void node_after(Role r) {
245            for (Credential c : g.getInEdges(r)) {
246                Role child = c.tail();
247
248                if (onPath.contains(child)) {
249                    Credential edge = g.findEdge(child, r);
250                    if (edge == null)
251                        throw new RuntimeException("Credential missing from " +
252                                "parent graph, state is messed up");
253
254                    ng.addEdge(edge, child, r);
255                    onPath.add(r);
256
257                    // For linking roles, collect the whole subgraph of the
258                    // authorizer
259                    if (r.is_linking()) {
260                        CollectQueryPath link = new CollectQueryPath(ng, 
261                                new Role(child.principal_part()));
262                        reverse_dfs(new Role(r.principal_part()), link);
263                    }
264
265                    // intersection: collect the subgraph of each prereq
266                    else if (r.is_intersection()) {
267                        CollectQueryPath prereq_finder = 
268                            new CollectQueryPath(ng, child);
269                        try {
270                            for (Role prereq : r.prereqs())
271                                reverse_dfs(prereq, prereq_finder);
272                        }
273                        catch (ABACException ignored ) { }
274                    }
275                }
276            }
277        }
278    }
279
280    /**
281     * Collect the path that a node can reach. It includes subgraphs that imply
282     * the linked/intersection edges traversed.
283     */
284    private class CollectReversePath extends CollectQueryGraph {
285        /**
286         * Initialize the collection graph.
287         * @param g a Graph that collects the results.
288         */
289        public CollectReversePath(Graph<Role,Credential> g) {
290            super(g);
291        }
292
293        /**
294         * add all of the node's parents
295         * @param r a Role, the current node being visited
296         */
297        public void node(Role r) {
298            for (Credential c : g.getOutEdges(r)) {
299                Role head = g.getDest(c);
300                Credential cred = g.findEdge(r, head);
301                if (cred == null)
302                    throw new RuntimeException("Credential missing from " + 
303                            "parent graph, state is messed up");
304
305                ng.addEdge(cred, r, head);
306            }
307        }
308
309        /**
310         * Do the collection on each node we visit.
311         * @param r a Role, the current node in the DFS.
312         */
313        public void node_after(Role r) {
314            for (Credential c : g.getOutEdges(r)) {
315                Role parent = g.getDest(c);
316
317                // if any of the links we follow is from a linking node, copy
318                // the subgraph that implies said link
319                if (parent.is_linking()) {
320                    CollectQueryPath link = new CollectQueryPath(ng, 
321                            new Role(r.principal_part()));
322                    reverse_dfs(new Role(parent.principal_part()), link);
323                }
324
325                // intersection roles do not need to be treated specially
326                // because the forward path from the principal will traverse all
327                // edges that imply the edge to the intersection node
328            }
329        }
330    }
331
332    /**
333     * Interface to the reverse_dfs member that does allocates the visited map
334     * so the user doesn't have to.
335     * @param r a Role from which to start the search
336     * @param f a DfsFcn that customizes the search operation
337     */
338    private void reverse_dfs(Role r, DfsFcn f) {
339        reverse_dfs(r, new HashSet<Role>(), f);
340    }
341
342    /**
343     * Member function to walk the edges in reverse from a given Role.  Each
344     * Role is visited once and has the node() function of the dfs_fcn called
345     * on it.  Order is not guaranteed.
346     * @param r a Role from which to start the search
347     * @param visited a HashSet of Roles that tells which nodes have been
348     * visited.
349     * @param f a DfsFcn that customizes the search operation
350     */
351    private void reverse_dfs(Role r, HashSet<Role> visited,
352            DfsFcn f) {
353        if (visited.contains(r)) return;
354
355        f.node(r);
356        visited.add(r);
357
358        for (Credential c : g.getInEdges(r)) 
359            reverse_dfs(g.getSource(c), visited, f);
360        f.node_after(r);
361    }
362
363    /**
364     * Interface to the forward_dfs member that allocates the visited map so
365     * the user doesn't have to.
366     * @param r a Role from which to start the search
367     * @param f a DfsFcn that customizes the search operation
368     */
369    private void forward_dfs(Role r, DfsFcn f) {
370        forward_dfs(r, new HashSet<Role>(), f);
371    }
372
373    /**
374     * Member function to walk the edges from a given Role. Each Role is
375     * visited once and has the node() function of the dfs_fcn called on it.
376     * Order is not guaranteed
377     * @param r a Role from which to start the search
378     * @param visited a HashSet of Roles that tells which nodes have been
379     * visited.
380     * @param f a DfsFcn that customizes the search operation
381     */
382    private void forward_dfs(Role r, Set<Role> visited, DfsFcn f) {
383        if (visited.contains(r)) return;
384
385        f.node(r);
386        visited.add(r);
387
388        for (Credential c : g.getOutEdges(r))
389            forward_dfs(g.getDest(c), visited, f);
390        f.node_after(r);
391    }
392}
Note: See TracBrowser for help on using the repository browser.