blob: 83a1d1276592eb04c7aa15c12845d37cabbdf741 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1996-2006 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package sun.security.acl;
27
28import java.io.*;
29import java.util.*;
30import java.security.Principal;
31import java.security.acl.*;
32
33/**
34 * An Access Control List (ACL) is encapsulated by this class.
35 * @author Satish Dharmaraj
36 */
37public class AclImpl extends OwnerImpl implements Acl {
38 //
39 // Maintain four tables. one each for positive and negative
40 // ACLs. One each depending on whether the entity is a group
41 // or principal.
42 //
43 private Hashtable<Principal, AclEntry> allowedUsersTable =
44 new Hashtable<Principal, AclEntry>(23);
45 private Hashtable<Principal, AclEntry> allowedGroupsTable =
46 new Hashtable<Principal, AclEntry>(23);
47 private Hashtable<Principal, AclEntry> deniedUsersTable =
48 new Hashtable<Principal, AclEntry>(23);
49 private Hashtable<Principal, AclEntry> deniedGroupsTable =
50 new Hashtable<Principal, AclEntry>(23);
51 private String aclName = null;
52 private Vector<Permission> zeroSet = new Vector<Permission>(1,1);
53
54
55 /**
56 * Constructor for creating an empty ACL.
57 */
58 public AclImpl(Principal owner, String name) {
59 super(owner);
60 try {
61 setName(owner, name);
62 } catch (Exception e) {}
63 }
64
65 /**
66 * Sets the name of the ACL.
67 * @param caller the principal who is invoking this method.
68 * @param name the name of the ACL.
69 * @exception NotOwnerException if the caller principal is
70 * not on the owners list of the Acl.
71 */
72 public void setName(Principal caller, String name)
73 throws NotOwnerException
74 {
75 if (!isOwner(caller))
76 throw new NotOwnerException();
77
78 aclName = name;
79 }
80
81 /**
82 * Returns the name of the ACL.
83 * @return the name of the ACL.
84 */
85 public String getName() {
86 return aclName;
87 }
88
89 /**
90 * Adds an ACL entry to this ACL. An entry associates a
91 * group or a principal with a set of permissions. Each
92 * user or group can have one positive ACL entry and one
93 * negative ACL entry. If there is one of the type (negative
94 * or positive) already in the table, a false value is returned.
95 * The caller principal must be a part of the owners list of
96 * the ACL in order to invoke this method.
97 * @param caller the principal who is invoking this method.
98 * @param entry the ACL entry that must be added to the ACL.
99 * @return true on success, false if the entry is already present.
100 * @exception NotOwnerException if the caller principal
101 * is not on the owners list of the Acl.
102 */
103 public synchronized boolean addEntry(Principal caller, AclEntry entry)
104 throws NotOwnerException
105 {
106 if (!isOwner(caller))
107 throw new NotOwnerException();
108
109 Hashtable<Principal, AclEntry> aclTable = findTable(entry);
110 Principal key = entry.getPrincipal();
111
112 if (aclTable.get(key) != null)
113 return false;
114
115 aclTable.put(key, entry);
116 return true;
117 }
118
119 /**
120 * Removes an ACL entry from this ACL.
121 * The caller principal must be a part of the owners list of the ACL
122 * in order to invoke this method.
123 * @param caller the principal who is invoking this method.
124 * @param entry the ACL entry that must be removed from the ACL.
125 * @return true on success, false if the entry is not part of the ACL.
126 * @exception NotOwnerException if the caller principal is not
127 * the owners list of the Acl.
128 */
129 public synchronized boolean removeEntry(Principal caller, AclEntry entry)
130 throws NotOwnerException
131 {
132 if (!isOwner(caller))
133 throw new NotOwnerException();
134
135 Hashtable<Principal, AclEntry> aclTable = findTable(entry);
136 Principal key = entry.getPrincipal();
137
138 AclEntry o = aclTable.remove(key);
139 return (o != null);
140 }
141
142 /**
143 * This method returns the set of allowed permissions for the
144 * specified principal. This set of allowed permissions is calculated
145 * as follows:
146 *
147 * If there is no entry for a group or a principal an empty permission
148 * set is assumed.
149 *
150 * The group positive permission set is the union of all
151 * the positive permissions of each group that the individual belongs to.
152 * The group negative permission set is the union of all
153 * the negative permissions of each group that the individual belongs to.
154 * If there is a specific permission that occurs in both
155 * the postive permission set and the negative permission set,
156 * it is removed from both. The group positive and negatoive permission
157 * sets are calculated.
158 *
159 * The individial positive permission set and the individual negative
160 * permission set is then calculated. Again abscence of an entry means
161 * the empty set.
162 *
163 * The set of permissions granted to the principal is then calculated using
164 * the simple rule: Individual permissions always override the Group permissions.
165 * Specifically, individual negative permission set (specific
166 * denial of permissions) overrides the group positive permission set.
167 * And the individual positive permission set override the group negative
168 * permission set.
169 *
170 * @param user the principal for which the ACL entry is returned.
171 * @return The resulting permission set that the principal is allowed.
172 */
173 public synchronized Enumeration<Permission> getPermissions(Principal user) {
174
175 Enumeration<Permission> individualPositive;
176 Enumeration<Permission> individualNegative;
177 Enumeration<Permission> groupPositive;
178 Enumeration<Permission> groupNegative;
179
180 //
181 // canonicalize the sets. That is remove common permissions from
182 // positive and negative sets.
183 //
184 groupPositive =
185 subtract(getGroupPositive(user), getGroupNegative(user));
186 groupNegative =
187 subtract(getGroupNegative(user), getGroupPositive(user));
188 individualPositive =
189 subtract(getIndividualPositive(user), getIndividualNegative(user));
190 individualNegative =
191 subtract(getIndividualNegative(user), getIndividualPositive(user));
192
193 //
194 // net positive permissions is individual positive permissions
195 // plus (group positive - individual negative).
196 //
197 Enumeration<Permission> temp1 =
198 subtract(groupPositive, individualNegative);
199 Enumeration<Permission> netPositive =
200 union(individualPositive, temp1);
201
202 // recalculate the enumeration since we lost it in performing the
203 // subtraction
204 //
205 individualPositive =
206 subtract(getIndividualPositive(user), getIndividualNegative(user));
207 individualNegative =
208 subtract(getIndividualNegative(user), getIndividualPositive(user));
209
210 //
211 // net negative permissions is individual negative permissions
212 // plus (group negative - individual positive).
213 //
214 temp1 = subtract(groupNegative, individualPositive);
215 Enumeration<Permission> netNegative = union(individualNegative, temp1);
216
217 return subtract(netPositive, netNegative);
218 }
219
220 /**
221 * This method checks whether or not the specified principal
222 * has the required permission. If permission is denied
223 * permission false is returned, a true value is returned otherwise.
224 * This method does not authenticate the principal. It presumes that
225 * the principal is a valid authenticated principal.
226 * @param principal the name of the authenticated principal
227 * @param permission the permission that the principal must have.
228 * @return true of the principal has the permission desired, false
229 * otherwise.
230 */
231 public boolean checkPermission(Principal principal, Permission permission)
232 {
233 Enumeration<Permission> permSet = getPermissions(principal);
234 while (permSet.hasMoreElements()) {
235 Permission p = permSet.nextElement();
236 if (p.equals(permission))
237 return true;
238 }
239 return false;
240 }
241
242 /**
243 * returns an enumeration of the entries in this ACL.
244 */
245 public synchronized Enumeration<AclEntry> entries() {
246 return new AclEnumerator(this,
247 allowedUsersTable, allowedGroupsTable,
248 deniedUsersTable, deniedGroupsTable);
249 }
250
251 /**
252 * return a stringified version of the
253 * ACL.
254 */
255 public String toString() {
256 StringBuffer sb = new StringBuffer();
257 Enumeration<AclEntry> entries = entries();
258 while (entries.hasMoreElements()) {
259 AclEntry entry = entries.nextElement();
260 sb.append(entry.toString().trim());
261 sb.append("\n");
262 }
263
264 return sb.toString();
265 }
266
267 //
268 // Find the table that this entry belongs to. There are 4
269 // tables that are maintained. One each for postive and
270 // negative ACLs and one each for groups and users.
271 // This method figures out which
272 // table is the one that this AclEntry belongs to.
273 //
274 private Hashtable<Principal, AclEntry> findTable(AclEntry entry) {
275 Hashtable<Principal, AclEntry> aclTable = null;
276
277 Principal p = entry.getPrincipal();
278 if (p instanceof Group) {
279 if (entry.isNegative())
280 aclTable = deniedGroupsTable;
281 else
282 aclTable = allowedGroupsTable;
283 } else {
284 if (entry.isNegative())
285 aclTable = deniedUsersTable;
286 else
287 aclTable = allowedUsersTable;
288 }
289 return aclTable;
290 }
291
292 //
293 // returns the set e1 U e2.
294 //
295 private static Enumeration<Permission> union(Enumeration<Permission> e1,
296 Enumeration<Permission> e2) {
297 Vector<Permission> v = new Vector<Permission>(20, 20);
298
299 while (e1.hasMoreElements())
300 v.addElement(e1.nextElement());
301
302 while (e2.hasMoreElements()) {
303 Permission o = e2.nextElement();
304 if (!v.contains(o))
305 v.addElement(o);
306 }
307
308 return v.elements();
309 }
310
311 //
312 // returns the set e1 - e2.
313 //
314 private Enumeration<Permission> subtract(Enumeration<Permission> e1,
315 Enumeration<Permission> e2) {
316 Vector<Permission> v = new Vector<Permission>(20, 20);
317
318 while (e1.hasMoreElements())
319 v.addElement(e1.nextElement());
320
321 while (e2.hasMoreElements()) {
322 Permission o = e2.nextElement();
323 if (v.contains(o))
324 v.removeElement(o);
325 }
326
327 return v.elements();
328 }
329
330 private Enumeration<Permission> getGroupPositive(Principal user) {
331 Enumeration<Permission> groupPositive = zeroSet.elements();
332 Enumeration<Principal> e = allowedGroupsTable.keys();
333 while (e.hasMoreElements()) {
334 Group g = (Group)e.nextElement();
335 if (g.isMember(user)) {
336 AclEntry ae = allowedGroupsTable.get(g);
337 groupPositive = union(ae.permissions(), groupPositive);
338 }
339 }
340 return groupPositive;
341 }
342
343 private Enumeration<Permission> getGroupNegative(Principal user) {
344 Enumeration<Permission> groupNegative = zeroSet.elements();
345 Enumeration<Principal> e = deniedGroupsTable.keys();
346 while (e.hasMoreElements()) {
347 Group g = (Group)e.nextElement();
348 if (g.isMember(user)) {
349 AclEntry ae = deniedGroupsTable.get(g);
350 groupNegative = union(ae.permissions(), groupNegative);
351 }
352 }
353 return groupNegative;
354 }
355
356 private Enumeration<Permission> getIndividualPositive(Principal user) {
357 Enumeration<Permission> individualPositive = zeroSet.elements();
358 AclEntry ae = allowedUsersTable.get(user);
359 if (ae != null)
360 individualPositive = ae.permissions();
361 return individualPositive;
362 }
363
364 private Enumeration<Permission> getIndividualNegative(Principal user) {
365 Enumeration<Permission> individualNegative = zeroSet.elements();
366 AclEntry ae = deniedUsersTable.get(user);
367 if (ae != null)
368 individualNegative = ae.permissions();
369 return individualNegative;
370 }
371}
372
373final class AclEnumerator implements Enumeration<AclEntry> {
374 Acl acl;
375 Enumeration<AclEntry> u1, u2, g1, g2;
376
377 AclEnumerator(Acl acl, Hashtable<?,AclEntry> u1, Hashtable<?,AclEntry> g1,
378 Hashtable<?,AclEntry> u2, Hashtable<?,AclEntry> g2) {
379 this.acl = acl;
380 this.u1 = u1.elements();
381 this.u2 = u2.elements();
382 this.g1 = g1.elements();
383 this.g2 = g2.elements();
384 }
385
386 public boolean hasMoreElements() {
387 return (u1.hasMoreElements() ||
388 u2.hasMoreElements() ||
389 g1.hasMoreElements() ||
390 g2.hasMoreElements());
391 }
392
393 public AclEntry nextElement()
394 {
395 AclEntry o;
396 synchronized (acl) {
397 if (u1.hasMoreElements())
398 return u1.nextElement();
399 if (u2.hasMoreElements())
400 return u2.nextElement();
401 if (g1.hasMoreElements())
402 return g1.nextElement();
403 if (g2.hasMoreElements())
404 return g2.nextElement();
405 }
406 throw new NoSuchElementException("Acl Enumerator");
407 }
408}