001: /* Copyright (c) 2001-2005, The HSQL Development Group
002: * All rights reserved.
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * Redistributions of source code must retain the above copyright notice, this
008: * list of conditions and the following disclaimer.
009: *
010: * Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: *
014: * Neither the name of the HSQL Development Group nor the names of its
015: * contributors may be used to endorse or promote products derived from this
016: * software without specific prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
020: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
021: * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
022: * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
023: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
024: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
025: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
026: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
027: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
028: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029: */
030:
031: package org.hsqldb;
032:
033: import org.hsqldb.lib.HashMappedList;
034: import org.hsqldb.lib.HashSet;
035: import org.hsqldb.lib.HsqlArrayList;
036: import org.hsqldb.lib.IntKeyHashMap;
037: import org.hsqldb.lib.IntValueHashMap;
038: import org.hsqldb.lib.Iterator;
039: import org.hsqldb.lib.StringUtil;
040: import org.hsqldb.lib.Collection;
041: import org.hsqldb.lib.Set;
042:
043: /**
044: * Contains a set of Grantee objects, and supports operations for creating,
045: * finding, modifying and deleting Grantee objects for a Database; plus
046: * Administrative privileges.
047: *
048: * @author boucherb@users
049: * @author fredt@users
050: * @author unsaved@users
051: *
052: * @version 1.8.0
053: * @since 1.8.0
054: * @see Grantee
055: */
056: class GranteeManager implements GrantConstants {
057:
058: /**
059: * The role name reserved for authorization of INFORMATION_SCHEMA and
060: * system objects.
061: */
062: static final String SYSTEM_AUTHORIZATION_NAME = "_SYSTEM";
063:
064: /** The role name reserved for ADMIN users. */
065: static final String DBA_ADMIN_ROLE_NAME = "DBA";
066:
067: /** The role name reserved for the special PUBLIC pseudo-user. */
068: static final String PUBLIC_ROLE_NAME = "PUBLIC";
069:
070: /**
071: * An empty list that is returned from
072: * {@link #listTablePrivileges listTablePrivileges} when
073: * it is detected that neither this <code>User</code> object or
074: * its <code>PUBLIC</code> <code>User</code> object attribute have been
075: * granted any rights on the <code>Table</code> object identified by
076: * the specified <code>HsqlName</code> object.
077: *
078: */
079: static final String[] emptyRightsList = new String[0];
080:
081: /**
082: * MAP: int => HsqlArrayList. <p>
083: *
084: * This map caches the lists of <code>String</code> objects naming the rights
085: * corresponding to each valid set of rights flags, as returned by
086: * {@link #listRightNames listRightNames}
087: *
088: */
089: static final IntKeyHashMap hRightsLists = new IntKeyHashMap();
090:
091: /**
092: * Used to provide access to the RoleManager for Grantee.isAccessible()
093: * lookups
094: */
095: /*
096: * Our map here has the same keys as the UserManager map
097: * EXCEPT that we include all roles, including the SYSTEM_AUTHORIZATION_NAME
098: * because we need o keep track of those permissions, but not his identity.
099: * I.e., our list here is all-inclusive, whether the User or Role is
100: * visible to database users or not.
101: */
102:
103: /**
104: * Map of String-to-Grantee-objects.<p>
105: * Primary object maintained by this class
106: */
107: private HashMappedList map = new HashMappedList();
108:
109: /**
110: * This object's set of Role objects. <p>
111: * role-Strings-to-Grantee-object
112: */
113: private HashMappedList roleMap = new HashMappedList();
114:
115: /**
116: * Construct the GranteeManager for a Database.
117: *
118: * Construct special Grantee objects for PUBLIC and SYS, and add them
119: * to the Grantee map.
120: * We depend on the corresponding User accounts being created
121: * independently so as to remove a dependency to the UserManager class.
122: *
123: * @param inDatabase Only needed to link to the RoleManager later on.
124: */
125: public GranteeManager(Database inDatabase) throws HsqlException {
126: addRole(GranteeManager.DBA_ADMIN_ROLE_NAME);
127: getRole(GranteeManager.DBA_ADMIN_ROLE_NAME).setAdminDirect();
128: }
129:
130: static final IntValueHashMap rightsStringLookup = new IntValueHashMap(
131: 7);
132:
133: static {
134: rightsStringLookup.put(S_R_ALL, ALL);
135: rightsStringLookup.put(S_R_SELECT, SELECT);
136: rightsStringLookup.put(S_R_UPDATE, UPDATE);
137: rightsStringLookup.put(S_R_DELETE, DELETE);
138: rightsStringLookup.put(S_R_INSERT, INSERT);
139: }
140:
141: /**
142: * Grants the rights represented by the rights argument on
143: * the database object identified by the dbobject argument
144: * to the Grantee object identified by name argument.<p>
145: *
146: * Note: For the dbobject argument, Java Class objects are identified
147: * using a String object whose value is the fully qualified name
148: * of the Class, while Table and other objects are
149: * identified by an HsqlName object. A Table
150: * object identifier must be precisely the one obtained by calling
151: * table.getName(); if a different HsqlName
152: * object with an identical name attribute is specified, then
153: * rights checks and tests will fail, since the HsqlName
154: * class implements its {@link HsqlName#hashCode hashCode} and
155: * {@link HsqlName#equals equals} methods based on pure object
156: * identity, rather than on attribute values. <p>
157: */
158: void grant(String name, Object dbobject, int rights)
159: throws HsqlException {
160:
161: Grantee g = get(name);
162:
163: if (g == null) {
164: throw Trace.error(Trace.NO_SUCH_GRANTEE, name);
165: }
166:
167: if (isImmutable(name)) {
168: throw Trace.error(Trace.NONMOD_GRANTEE, name);
169: }
170:
171: g.grant(dbobject, rights);
172: g.updateAllRights();
173:
174: if (g.isRole) {
175: updateAllRights(g);
176: }
177: }
178:
179: /**
180: * Grant a role to this Grantee.
181: */
182: void grant(String name, String role) throws HsqlException {
183:
184: Grantee grantee = get(name);
185:
186: if (grantee == null) {
187: throw Trace.error(Trace.NO_SUCH_GRANTEE, name);
188: }
189:
190: if (isImmutable(name)) {
191: throw Trace.error(Trace.NONMOD_GRANTEE, name);
192: }
193:
194: Grantee r = get(role);
195:
196: if (r == null) {
197: throw Trace.error(Trace.NO_SUCH_ROLE, role);
198: }
199:
200: if (role.equals(name)) {
201: throw Trace.error(Trace.CIRCULAR_GRANT, name);
202: }
203:
204: // boucherb@users 20050515
205: // SQL 2003 Foundation, 4.34.3
206: // No cycles of role grants are allowed.
207: if (r.hasRole(name)) {
208:
209: // boucherb@users
210: // TODO: Correct reporting of actual grant path
211: throw Trace.error(Trace.CIRCULAR_GRANT, Trace
212: .getMessage(Trace.ALREADY_HAVE_ROLE)
213: + " GRANT " + name + " TO " + role);
214: }
215:
216: if (grantee.getDirectRoles().contains(role)) {
217: throw Trace.error(Trace.ALREADY_HAVE_ROLE, role);
218: }
219:
220: grantee.grant(role);
221: grantee.updateAllRights();
222:
223: if (grantee.isRole) {
224: updateAllRights(grantee);
225: }
226: }
227:
228: /**
229: * Revoke a role from a Grantee
230: */
231: void revoke(String name, String role) throws HsqlException {
232:
233: Grantee g = get(name);
234:
235: if (g == null) {
236: throw Trace.error(Trace.NO_SUCH_GRANTEE, name);
237: }
238:
239: g.revoke(role);
240: g.updateAllRights();
241:
242: if (g.isRole) {
243: updateAllRights(g);
244: }
245: }
246:
247: /**
248: * Revokes the rights represented by the rights argument on
249: * the database object identified by the dbobject argument
250: * from the User object identified by the name
251: * argument.<p>
252: * @see #grant
253: */
254: void revoke(String name, Object dbobject, int rights)
255: throws HsqlException {
256:
257: Grantee g = get(name);
258:
259: g.revoke(dbobject, rights);
260: g.updateAllRights();
261:
262: if (g.isRole) {
263: updateAllRights(g);
264: }
265: }
266:
267: /**
268: * Removes a role without any privileges from all grantees
269: */
270: void removeEmptyRole(Grantee role) {
271:
272: String name = role.getName();
273:
274: for (int i = 0; i < map.size(); i++) {
275: Grantee grantee = (Grantee) map.get(i);
276:
277: grantee.roles.remove(name);
278: }
279: }
280:
281: /**
282: * Removes all rights mappings for the database object identified by
283: * the dbobject argument from all Grantee objects in the set.
284: */
285: void removeDbObject(Object dbobject) {
286:
287: for (int i = 0; i < map.size(); i++) {
288: Grantee g = (Grantee) map.get(i);
289:
290: g.revokeDbObject(dbobject);
291: }
292: }
293:
294: /**
295: * First updates all ROLE Grantee objects. Then updates all USER Grantee
296: * Objects.
297: */
298: void updateAllRights(Grantee role) {
299:
300: String name = role.getName();
301:
302: for (int i = 0; i < map.size(); i++) {
303: Grantee grantee = (Grantee) map.get(i);
304:
305: if (grantee.isRole) {
306: grantee.updateNestedRoles(name);
307: }
308: }
309:
310: for (int i = 0; i < map.size(); i++) {
311: Grantee grantee = (Grantee) map.get(i);
312:
313: if (!grantee.isRole) {
314: grantee.updateAllRights();
315: }
316: }
317: }
318:
319: /**
320: */
321: public boolean removeGrantee(String name) {
322:
323: /*
324: * Explicitly can't remove PUBLIC_USER_NAME and system grantees.
325: */
326: if (isReserved(name)) {
327: return false;
328: }
329:
330: Grantee g = (Grantee) map.remove(name);
331:
332: if (g == null) {
333: return false;
334: }
335:
336: g.clearPrivileges();
337: updateAllRights(g);
338:
339: if (g.isRole) {
340: roleMap.remove(name);
341: removeEmptyRole(g);
342: }
343:
344: return true;
345: }
346:
347: /**
348: * We don't have to worry about anything manually creating a reserved
349: * account, because the reserved accounts are created upon DB
350: * initialization. If somebody tries to create one of these accounts
351: * after that, it will fail because the account will already exist.
352: * (We do prevent them from being removed, elsewhere!)
353: */
354: public Grantee addGrantee(String name) throws HsqlException {
355:
356: if (map.containsKey(name)) {
357: throw Trace.error(Trace.GRANTEE_ALREADY_EXISTS, name);
358: }
359:
360: Grantee pubGrantee = null;
361:
362: if (!isReserved(name)) {
363: pubGrantee = get(PUBLIC_ROLE_NAME);
364:
365: if (pubGrantee == null) {
366: Trace.doAssert(false, Trace
367: .getMessage(Trace.MISSING_PUBLIC_GRANTEE));
368: }
369: }
370:
371: Grantee g = new Grantee(name, pubGrantee, this );
372:
373: map.put(name, g);
374:
375: return g;
376: }
377:
378: /**
379: * Returns true if named Grantee object exists.
380: * This will return true for reserved Grantees
381: * SYSTEM_AUTHORIZATION_NAME, ADMIN_ROLE_NAME, PUBLIC_USER_NAME.
382: */
383: boolean isGrantee(String name) {
384: return (map.containsKey(name));
385: }
386:
387: static int getCheckRight(String right) throws HsqlException {
388:
389: int r = getRight(right);
390:
391: if (r != 0) {
392: return r;
393: }
394:
395: throw Trace.error(Trace.NO_SUCH_RIGHT, right);
396: }
397:
398: /**
399: * Translate a string representation or right(s) into its numeric form.
400: */
401: static int getRight(String right) {
402: return rightsStringLookup.get(right, 0);
403: }
404:
405: /**
406: * Returns a comma separated list of right names corresponding to the
407: * right flags set in the right argument. <p>
408: */
409: static String getRightsList(int rights) {
410:
411: // checkValidFlags(right);
412: if (rights == 0) {
413: return null;
414: }
415:
416: if (rights == ALL) {
417: return S_R_ALL;
418: }
419:
420: return StringUtil.getList(getRightsArray(rights), ",", "");
421: }
422:
423: /**
424: * Retrieves the list of right names represented by the right flags
425: * set in the specified <code>Integer</code> object's <code>int</code>
426: * value. <p>
427: *
428: * @param rights An Integer representing a set of right flags
429: * @return an empty list if the specified <code>Integer</code> object is
430: * null, else a list of rights, as <code>String</code> objects,
431: * represented by the rights flag bits set in the specified
432: * <code>Integer</code> object's int value.
433: *
434: */
435: static String[] getRightsArray(int rights) {
436:
437: if (rights == 0) {
438: return emptyRightsList;
439: }
440:
441: String[] list = (String[]) hRightsLists.get(rights);
442:
443: if (list != null) {
444: return list;
445: }
446:
447: list = getRightsArraySub(rights);
448:
449: hRightsLists.put(rights, list);
450:
451: return list;
452: }
453:
454: private static String[] getRightsArraySub(int right) {
455:
456: // checkValidFlags(right);
457: if (right == 0) {
458: return emptyRightsList;
459: }
460:
461: HsqlArrayList a = new HsqlArrayList();
462: Iterator it = rightsStringLookup.keySet().iterator();
463:
464: for (; it.hasNext();) {
465: String rightString = (String) it.next();
466:
467: if (rightString.equals(S_R_ALL)) {
468: continue;
469: }
470:
471: int i = rightsStringLookup.get(rightString, 0);
472:
473: if ((right & i) != 0) {
474: a.add(rightString);
475: }
476: }
477:
478: return (String[]) a.toArray(new String[a.size()]);
479: }
480:
481: /**
482: * Retrieves the set of distinct, fully qualified Java <code>Class</code>
483: * names upon which any grants currently exist to elements in
484: * this collection. <p>
485: * @return the set of distinct, fully qualified Java Class names, as
486: * <code>String</code> objects, upon which grants currently exist
487: * to the elements of this collection
488: *
489: */
490: HashSet getGrantedClassNames() throws HsqlException {
491:
492: int size;
493: Grantee grantee;
494: HashSet out;
495: Iterator e;
496:
497: size = map.size();
498: out = new HashSet();
499:
500: for (int i = 0; i < size; i++) {
501: grantee = (Grantee) map.get(i);
502:
503: if (grantee == null) {
504: continue;
505: }
506:
507: e = grantee.getGrantedClassNames(false).iterator();
508:
509: while (e.hasNext()) {
510: out.add(e.next());
511: }
512: }
513:
514: return out;
515: }
516:
517: public Grantee get(String name) {
518: return (Grantee) map.get(name);
519: }
520:
521: public Collection getGrantees() {
522: return map.values();
523: }
524:
525: public static boolean validRightString(String rightString) {
526: return getRight(rightString) != 0;
527: }
528:
529: public static boolean isImmutable(String name) {
530: return name.equals(SYSTEM_AUTHORIZATION_NAME)
531: || name.equals(DBA_ADMIN_ROLE_NAME);
532: }
533:
534: public static boolean isReserved(String name) {
535:
536: return name.equals(SYSTEM_AUTHORIZATION_NAME)
537: || name.equals(DBA_ADMIN_ROLE_NAME)
538: || name.equals(PUBLIC_ROLE_NAME);
539: }
540:
541: /**
542: * Creates a new Role object under management of this object. <p>
543: *
544: * A set of constraints regarding user creation is imposed: <p>
545: *
546: * <OL>
547: * <LI>Can't create a role with name same as any right.
548: *
549: * <LI>If the specified name is null, then an
550: * ASSERTION_FAILED exception is thrown stating that
551: * the name is null.
552: *
553: * <LI>If this object's collection already contains an element whose
554: * name attribute equals the name argument, then
555: * a GRANTEE_ALREADY_EXISTS or ROLE_ALREADY_EXISTS Trace
556: * is thrown.
557: * (This will catch attempts to create Reserved grantee names).
558: * </OL>
559: */
560: String addRole(String name) throws HsqlException {
561:
562: /*
563: * Role names can't be right names because that would cause
564: * conflicts with "GRANT name TO...". This doesn't apply to
565: * User names or Grantee names in general, since you can't
566: * "GRANT username TO...". That's why this check is only here.
567: */
568: if (name == null) {
569: Trace.doAssert(false, Trace.getMessage(Trace.NULL_NAME));
570: }
571:
572: Grantee g = null;
573:
574: if (GranteeManager.validRightString(name)) {
575: throw Trace.error(Trace.ILLEGAL_ROLE_NAME, name);
576: }
577:
578: g = addGrantee(name);
579: g.isRole = true;
580:
581: boolean result = roleMap.add(name, g);
582:
583: if (!result) {
584: throw Trace.error(Trace.ROLE_ALREADY_EXISTS, name);
585: }
586:
587: // I don't think can get this trace since every roleMap element
588: // will have a Grantee element which was already verified
589: // above. Easier to leave this check here than research it.
590: return name;
591: }
592:
593: /**
594: * Attempts to drop a Role with the specified name
595: * from this object's set. <p>
596: *
597: * A successful drop action consists of: <p>
598: *
599: * <UL>
600: *
601: * <LI>removing the Grantee object with the specified name
602: * from the set.
603: *
604: * <LI>revoking all rights from the removed object<br>
605: * (this ensures that in case there are still references to the
606: * just dropped Grantee object, those references
607: * cannot be used to erronously access database objects).
608: *
609: * </UL> <p>
610: *
611: */
612: void dropRole(String name) throws HsqlException {
613:
614: if (name.equals(GranteeManager.DBA_ADMIN_ROLE_NAME)) {
615: throw Trace.error(Trace.ACCESS_IS_DENIED);
616: }
617:
618: if (!isRole(name)) {
619: throw Trace.error(Trace.NO_SUCH_ROLE, name);
620: }
621:
622: removeGrantee(name);
623: roleMap.remove(name);
624: }
625:
626: public Set getRoleNames() {
627: return roleMap.keySet();
628: }
629:
630: /**
631: * Returns Grantee for the named Role
632: */
633: Grantee getRole(String name) throws HsqlException {
634:
635: if (!isRole(name)) {
636: Trace.doAssert(false, "No role '" + name + "'");
637: }
638:
639: Grantee g = (Grantee) roleMap.get(name);
640:
641: if (g == null) {
642: throw Trace.error(Trace.MISSING_GRANTEE, name);
643: }
644:
645: return g;
646: }
647:
648: boolean isRole(String name) throws HsqlException {
649: return roleMap.containsKey(name);
650: }
651: }
|