source: java/net/deterlab/abac/Query.java @ 84f0e7a

abac0-leakabac0-meicompt_changesgec13mei-idmei-rt0-nmei_rt0mei_rt2mei_rt2_fix_1meiyap-rt1meiyap1rt2tvf-new-xml
Last change on this file since 84f0e7a was 84f0e7a, checked in by Ted Faber <faber@…>, 13 years ago

Initial run at unifying the interface.

  • Property mode set to 100644
File size: 9.8 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 */
14public class Query {
15    private Graph<Role,Credential> g;
16    private int vertex_count;
17
18    public Query(Graph<Role,Credential> g) {
19        this.g = g;
20    }
21
22    /**
23     * Run a query against the graph, returning a graph of the results. If the
24     * results are empty or the query fails, an empty graph is returned. When
25     * derived edges are involved, the subgraphs that imply those edges are
26     * included.
27     */
28    public Graph<Role,Credential> run(String attr, String prin) {
29        Role attribute = null, principal = null;;
30
31        if (!attr.isEmpty()) attribute = new Role(attr);
32        if (!prin.isEmpty()) principal = new Role(prin);
33
34        Graph<Role,Credential> ret =
35            Graphs.<Role,Credential>synchronizedDirectedGraph(
36                    new DirectedSparseGraph<Role,Credential>());
37
38        /* empty attribute, non-empty principal: find everywhere the principal
39         * can go */
40        if (attr.isEmpty() && !prin.isEmpty()) {
41            if (g.containsVertex(principal)) {
42                CollectReversePath collect = new CollectReversePath(ret);
43                forward_dfs(principal, collect);
44            }
45        }
46
47        /* otherwise we're going to do some kind of a reverse dfs */
48        else if (g.containsVertex(attribute)) { 
49            if ( prin == null || prin.isEmpty()) {
50                CollectQueryGraph  collect = new CollectQueryGraph(ret);
51                reverse_dfs(attribute, collect);
52            }
53            else {
54                if ( g.containsVertex(principal)) {
55                    CollectQueryPath  collect = new CollectQueryPath(ret,
56                            principal);
57                    reverse_dfs(attribute, collect);
58                }
59            }
60        }
61
62        vertex_count = ret.getVertexCount();
63
64        return ret;
65    }
66
67    /**
68     * Returns true after running a query that returns a non-empty set of
69     * vertices.
70     */
71    public boolean successful() {
72        return vertex_count > 0;
73    }
74
75    /**
76     * Returns a collection of principals reachable from a Role when
77     * traversing edges in the reverse direction.
78     */
79    public Set<Role> find_principals(Role n) {
80        Set<Role> principals = new HashSet<Role>();
81   
82        GetPrincipals f = new GetPrincipals(principals);
83        reverse_dfs(n, f);
84        return principals;
85    }
86
87
88    /**
89     * Interface to pass to the reverse_dfs class.  The node function gets
90     * called for every node visited by the dfs.
91     */
92    interface DfsFcn {
93        public void node(Role x);
94        public void node_after(Role x);
95    }
96
97    /**
98     * Class used to pull all the principals out of a reverse_dfs searched
99     * graph.  It takes a collection to the constructor and adds all principals
100     * it sees to that collection.  In no collection is passed in, it creates a
101     * local HashSet of vertices.  The collection is accessible via the p
102     * (i.e., principals) member.
103     */
104    private class GetPrincipals implements DfsFcn {
105        public Collection<Role> p;
106
107        public GetPrincipals(Collection<Role> vc) {
108            if ( vc != null) { p =vc;}
109            else { p = new HashSet<Role>(); }
110        }
111
112        public void node(Role r) {
113            if ( r.is_principal() ) p.add(r);
114        }
115        public void node_after(Role r) { }
116    }
117
118    /**
119     * Collect a subgraph. Add every node and outgoing arc we encounter, and
120     * pull in the linking node and intersection subtrees as well. Because it's
121     * possible that these subtrees have been encountered more than once, we're
122     * careful not to pull them in twice. The created graph is found in the
123     * public ng member.
124     */
125    private class CollectQueryGraph implements DfsFcn {
126        public Graph<Role, Credential> ng;
127        protected HashSet<Role> linking_roles_seen;
128        protected HashSet<Role> intersection_roles_seen;
129
130        public CollectQueryGraph(Graph<Role,Credential> g) {
131            ng = g;
132            linking_roles_seen = new HashSet<Role>();
133            intersection_roles_seen = new HashSet<Role>();
134        }
135
136        public void node(Role r) {
137            if (r.is_linking() && !linking_roles_seen.contains(r)) {
138                // Collect this linking role subgraph
139                reverse_dfs(new Role(r.principal_part()), this);
140                linking_roles_seen.add(r);
141            }
142
143            // collect subgraph for each intersection prereq
144            if (r.is_intersection() && !intersection_roles_seen.contains(r)) {
145                for (Role prereq : r.prereqs())
146                    reverse_dfs(prereq, this);
147                intersection_roles_seen.add(r);
148            }
149
150            // Add this node's children
151            for (Credential c : g.getInEdges(r) ) {
152                Role tail = g.getSource(c);
153                ng.addEdge(c, tail, r); // FIXME tail and r can come from c [?]
154            }
155        }
156        public void node_after(Role r) { }
157
158    }
159
160    /**
161     * Collect the path from one node to another. It only includes vertices that
162     * are traversed to reach the node. It also includes subgraphs that imply
163     * the linking/intersection edges traversed.
164     */
165    private class CollectQueryPath extends CollectQueryGraph {
166        HashSet<Role> onPath;     // Vertices on the path to the principal
167        public CollectQueryPath(Graph<Role,Credential> g, Role dest) {
168            super(g);
169            onPath = new HashSet<Role>();
170            onPath.add(dest);
171        }
172
173        public void node(Role r) { }
174
175        public void node_after(Role r) {
176            for (Credential c : g.getInEdges(r)) {
177                Role child = c.tail();
178
179                if (onPath.contains(child)) {
180                    Credential edge = g.findEdge(child, r);
181                    if (edge == null)
182                        throw new RuntimeException("Credential missing from parent graph, state is messed up");
183
184                    ng.addEdge(edge, child, r);
185                    onPath.add(r);
186
187                    // For linking roles, collect the whole subgraph of the
188                    // authorizer
189                    if (r.is_linking()) {
190                        CollectQueryPath link = new CollectQueryPath(ng, new Role(child.principal_part()));
191                        reverse_dfs(new Role(r.principal_part()), link);
192                    }
193
194                    // intersection: collect the subgraph of each prereq
195                    else if (r.is_intersection()) {
196                        CollectQueryPath prereq_finder = new CollectQueryPath(ng, child);
197                        for (Role prereq : r.prereqs())
198                            reverse_dfs(prereq, prereq_finder);
199                    }
200                }
201            }
202        }
203    }
204
205    /**
206     * Collect the path that a node can reach. It includes subgraphs that imply
207     * the linked/intersection edges traversed.
208     */
209    private class CollectReversePath extends CollectQueryGraph {
210        public CollectReversePath(Graph<Role,Credential> g) {
211            super(g);
212        }
213
214        /* add all of the node's parents */
215        public void node(Role r) {
216            for (Credential c : g.getOutEdges(r)) {
217                Role head = g.getDest(c);
218                Credential cred = g.findEdge(r, head);
219                if (cred == null)
220                    throw new RuntimeException("Credential missing from parent graph, state is messed up");
221
222                ng.addEdge(cred, r, head);
223            }
224        }
225
226        public void node_after(Role r) {
227            for (Credential c : g.getOutEdges(r)) {
228                Role parent = g.getDest(c);
229
230                // if any of the links we follow is from a linking node, copy
231                // the subgraph that implies said link
232                if (parent.is_linking()) {
233                    CollectQueryPath link = new CollectQueryPath(ng, new Role(r.principal_part()));
234                    reverse_dfs(new Role(parent.principal_part()), link);
235                }
236
237                // intersection roles do not need to be treated specially
238                // because the forward path from the principal will traverse all
239                // edges that imply the edge to the intersection node
240            }
241        }
242    }
243
244    /**
245     * Interface to the reverse_dfs member that does allocates the visited map
246     * so the user doesn't have to.
247     * */
248    private void reverse_dfs(Role r, DfsFcn f) {
249        reverse_dfs(r, new HashSet<Role>(), f);
250    }
251
252    /**
253     * Member function to walk the edges in reverse from a given Role.  Each
254     * Role is visited once and has the node() function of the dfs_fcn called
255     * on it.  Order is not guaranteed.
256     */
257    private void reverse_dfs(Role r, HashSet<Role> visited,
258            DfsFcn f) {
259        if (visited.contains(r)) return;
260
261        f.node(r);
262        visited.add(r);
263
264        for (Credential c : g.getInEdges(r)) 
265            reverse_dfs(g.getSource(c), visited, f);
266        f.node_after(r);
267    }
268
269    /**
270     * Interface to the forward_dfs member that allocates the visited map so
271     * the user doesn't have to.
272     */
273    private void forward_dfs(Role r, DfsFcn f) {
274        forward_dfs(r, new HashSet<Role>(), f);
275    }
276
277    /**
278     * Member function to walk the edges from a given Role. Each Role is
279     * visited once and has the node() function of the dfs_fcn called on it.
280     * Order is not guaranteed
281     */
282    private void forward_dfs(Role r, Set<Role> visited, DfsFcn f) {
283        if (visited.contains(r)) return;
284
285        f.node(r);
286        visited.add(r);
287
288        for (Credential c : g.getOutEdges(r))
289            forward_dfs(g.getDest(c), visited, f);
290        f.node_after(r);
291    }
292}
Note: See TracBrowser for help on using the repository browser.