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

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

support for creating intersection roles and adding them to the
credential graph. Implied edges are properly created in the credential
graph, but they are not returned in queries.

  • Property mode set to 100644
File size: 8.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 subtrees as well.  Because it's possible that
116     * linking node 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
124        public CollectQueryGraph(Graph<Role,Credential> g) {
125            ng = g;
126            linking_roles_seen = new HashSet<Role>();
127        }
128
129        public void node(Role r) {
130            if (r.is_linking() && !linking_roles_seen.contains(r)) {
131                // Collect this linking role subgraph
132                reverse_dfs(new Role(r.principal_part()), this);
133                linking_roles_seen.add(r);
134            }
135            // Add this node's children
136            for (Credential c : g.getInEdges(r) ) {
137                Role tail = g.getSource(c);
138                ng.addEdge(c, tail, r); // FIXME tail and r can come from c [?]
139            }
140        }
141        public void node_after(Role r) { }
142
143    }
144
145    /**
146     * Collect the path from one node to another. It only includes vertices that
147     * are traversed to reach the node. It also includes subgraphs that imply
148     * the linking edges traversed.
149     */
150    private class CollectQueryPath extends CollectQueryGraph {
151        HashSet<Role> onPath;     // Vertices on the path to the principal
152        public CollectQueryPath(Graph<Role,Credential> g, Role dest) {
153            super(g);
154            onPath = new HashSet<Role>();
155            onPath.add(dest);
156        }
157
158        public void node(Role r) { }
159
160        public void node_after(Role r) {
161            for (Credential c : g.getInEdges(r)) {
162                Role child = c.tail();
163
164                if (onPath.contains(child)) {
165                    Credential edge = g.findEdge(child, r);
166                    if (edge == null)
167                        throw new RuntimeException("Credential missing from parent graph, state is messed up");
168
169                    ng.addEdge(edge, child, r);
170                    onPath.add(r);
171
172                    // For linking roles, collect the whole subgraph of the
173                    // authorizer
174                    if (r.is_linking()) {
175                        CollectQueryPath link = new CollectQueryPath(ng, new Role(child.principal_part()));
176                        reverse_dfs(new Role(r.principal_part()), link);
177                    }
178                }
179            }
180        }
181    }
182
183    /**
184     * Collect the path that a node can reach. It includes subgraphs that imply
185     * the linked edges traversed.
186     */
187    private class CollectReversePath extends CollectQueryGraph {
188        public CollectReversePath(Graph<Role,Credential> g) {
189            super(g);
190        }
191
192        /* add all of the node's parents */
193        public void node(Role r) {
194            for (Credential c : g.getOutEdges(r)) {
195                Role head = g.getDest(c);
196                Credential cred = g.findEdge(r, head);
197                if (cred == null)
198                    throw new RuntimeException("Credential missing from parent graph, state is messed up");
199
200                ng.addEdge(cred, r, head);
201            }
202        }
203
204        public void node_after(Role r) {
205            for (Credential c : g.getOutEdges(r)) {
206                Role parent = g.getDest(c);
207
208                // if any of the links we follow is derived, copy the subgraph
209                // that implies said link
210                if (parent.is_linking()) {
211                    CollectQueryPath link = new CollectQueryPath(ng, new Role(r.principal_part()));
212                    reverse_dfs(new Role(parent.principal_part()), link);
213                }
214            }
215        }
216    }
217
218    /**
219     * Interface to the reverse_dfs member that does allocates the visited map
220     * so the user doesn't have to.
221     * */
222    private void reverse_dfs(Role r, DfsFcn f) {
223        reverse_dfs(r, new HashSet<Role>(), f);
224    }
225
226    /**
227     * Member function to walk the edges in reverse from a given Role.  Each
228     * Role is visited once and has the node() function of the dfs_fcn called
229     * on it.  Order is not guaranteed.
230     */
231    private void reverse_dfs(Role r, HashSet<Role> visited,
232            DfsFcn f) {
233        if (visited.contains(r)) return;
234
235        f.node(r);
236        visited.add(r);
237
238        for (Credential c : g.getInEdges(r)) 
239            reverse_dfs(g.getSource(c), visited, f);
240        f.node_after(r);
241    }
242
243    /**
244     * Interface to the forward_dfs member that allocates the visited map so
245     * the user doesn't have to.
246     */
247    private void forward_dfs(Role r, DfsFcn f) {
248        forward_dfs(r, new HashSet<Role>(), f);
249    }
250
251    /**
252     * Member function to walk the edges from a given Role. Each Role is
253     * visited once and has the node() function of the dfs_fcn called on it.
254     * Order is not guaranteed
255     */
256    private void forward_dfs(Role r, Set<Role> visited, DfsFcn f) {
257        if (visited.contains(r)) return;
258
259        f.node(r);
260        visited.add(r);
261
262        for (Credential c : g.getOutEdges(r))
263            forward_dfs(g.getDest(c), visited, f);
264        f.node_after(r);
265    }
266}
Note: See TracBrowser for help on using the repository browser.