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

abac0-leakabac0-meicompt_changesgec13mei-idmei-rt0-nmei_rt0mei_rt2mei_rt2_fix_1meiyap-rt1meiyap1rt2tvf-new-xml
Last change on this file since bcf7370 was 8fac851, checked in by Mike Ryan <mikeryan@…>, 13 years ago

queries involving intersection edges return the subgraphs that imply the
derived edges

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