org.hsqldb.GranteeManager Maven / Gradle / Ivy
Show all versions of hsqldb Show documentation
/* Copyright (c) 2001-2008, The HSQL Development Group
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the HSQL Development Group nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.hsqldb;
import org.hsqldb.lib.HashMappedList;
import org.hsqldb.lib.HashSet;
import org.hsqldb.lib.HsqlArrayList;
import org.hsqldb.lib.IntKeyHashMap;
import org.hsqldb.lib.IntValueHashMap;
import org.hsqldb.lib.Iterator;
import org.hsqldb.lib.StringUtil;
import org.hsqldb.lib.Collection;
import org.hsqldb.lib.Set;
/**
* Contains a set of Grantee objects, and supports operations for creating,
* finding, modifying and deleting Grantee objects for a Database; plus
* Administrative privileges.
*
* @author boucherb@users
* @author fredt@users
* @author unsaved@users
*
* @version 1.8.0
* @since 1.8.0
* @see Grantee
*/
class GranteeManager implements GrantConstants {
/**
* The role name reserved for authorization of INFORMATION_SCHEMA and
* system objects.
*/
static final String SYSTEM_AUTHORIZATION_NAME = "_SYSTEM";
/** The role name reserved for ADMIN users. */
static final String DBA_ADMIN_ROLE_NAME = "DBA";
/** The role name reserved for the special PUBLIC pseudo-user. */
static final String PUBLIC_ROLE_NAME = "PUBLIC";
/**
* An empty list that is returned from
* {@link #listTablePrivileges listTablePrivileges} when
* it is detected that neither this User
object or
* its PUBLIC
User
object attribute have been
* granted any rights on the Table
object identified by
* the specified HsqlName
object.
*
*/
static final String[] emptyRightsList = new String[0];
/**
* MAP: int => HsqlArrayList.
*
* This map caches the lists of String
objects naming the rights
* corresponding to each valid set of rights flags, as returned by
* {@link #listRightNames listRightNames}
*
*/
static final IntKeyHashMap hRightsLists = new IntKeyHashMap();
/**
* Used to provide access to the RoleManager for Grantee.isAccessible()
* lookups
*/
/*
* Our map here has the same keys as the UserManager map
* EXCEPT that we include all roles, including the SYSTEM_AUTHORIZATION_NAME
* because we need o keep track of those permissions, but not his identity.
* I.e., our list here is all-inclusive, whether the User or Role is
* visible to database users or not.
*/
/**
* Map of String-to-Grantee-objects.
* Primary object maintained by this class
*/
private HashMappedList map = new HashMappedList();
/**
* This object's set of Role objects.
* role-Strings-to-Grantee-object
*/
private HashMappedList roleMap = new HashMappedList();
/**
* Construct the GranteeManager for a Database.
*
* Construct special Grantee objects for PUBLIC and SYS, and add them
* to the Grantee map.
* We depend on the corresponding User accounts being created
* independently so as to remove a dependency to the UserManager class.
*
* @param inDatabase Only needed to link to the RoleManager later on.
*/
public GranteeManager(Database inDatabase) throws HsqlException {
addRole(GranteeManager.DBA_ADMIN_ROLE_NAME);
getRole(GranteeManager.DBA_ADMIN_ROLE_NAME).setAdminDirect();
}
static final IntValueHashMap rightsStringLookup = new IntValueHashMap(7);
static {
rightsStringLookup.put(S_R_ALL, ALL);
rightsStringLookup.put(S_R_SELECT, SELECT);
rightsStringLookup.put(S_R_UPDATE, UPDATE);
rightsStringLookup.put(S_R_DELETE, DELETE);
rightsStringLookup.put(S_R_INSERT, INSERT);
}
/**
* Grants the rights represented by the rights argument on
* the database object identified by the dbobject argument
* to the Grantee object identified by name argument.
*
* Note: For the dbobject argument, Java Class objects are identified
* using a String object whose value is the fully qualified name
* of the Class, while Table and other objects are
* identified by an HsqlName object. A Table
* object identifier must be precisely the one obtained by calling
* table.getName(); if a different HsqlName
* object with an identical name attribute is specified, then
* rights checks and tests will fail, since the HsqlName
* class implements its {@link HsqlName#hashCode hashCode} and
* {@link HsqlName#equals equals} methods based on pure object
* identity, rather than on attribute values.
*/
void grant(String name, Object dbobject,
int rights) throws HsqlException {
Grantee g = get(name);
if (g == null) {
throw Trace.error(Trace.NO_SUCH_GRANTEE, name);
}
if (isImmutable(name)) {
throw Trace.error(Trace.NONMOD_GRANTEE, name);
}
g.grant(dbobject, rights);
g.updateAllRights();
if (g.isRole) {
updateAllRights(g);
}
}
/**
* Grant a role to this Grantee.
*/
void grant(String name, String role) throws HsqlException {
Grantee grantee = get(name);
if (grantee == null) {
throw Trace.error(Trace.NO_SUCH_GRANTEE, name);
}
if (isImmutable(name)) {
throw Trace.error(Trace.NONMOD_GRANTEE, name);
}
Grantee r = get(role);
if (r == null) {
throw Trace.error(Trace.NO_SUCH_ROLE, role);
}
if (role.equals(name)) {
throw Trace.error(Trace.CIRCULAR_GRANT, name);
}
// boucherb@users 20050515
// SQL 2003 Foundation, 4.34.3
// No cycles of role grants are allowed.
if (r.hasRole(name)) {
// boucherb@users
// TODO: Correct reporting of actual grant path
throw Trace.error(Trace.CIRCULAR_GRANT,
Trace.getMessage(Trace.ALREADY_HAVE_ROLE)
+ " GRANT " + name + " TO " + role);
}
if (grantee.getDirectRoles().contains(role)) {
throw Trace.error(Trace.ALREADY_HAVE_ROLE, role);
}
grantee.grant(role);
grantee.updateAllRights();
if (grantee.isRole) {
updateAllRights(grantee);
}
}
/**
* Revoke a role from a Grantee
*/
void revoke(String name, String role) throws HsqlException {
Grantee g = get(name);
if (g == null) {
throw Trace.error(Trace.NO_SUCH_GRANTEE, name);
}
g.revoke(role);
g.updateAllRights();
if (g.isRole) {
updateAllRights(g);
}
}
/**
* Revokes the rights represented by the rights argument on
* the database object identified by the dbobject argument
* from the User object identified by the name
* argument.
* @see #grant
*/
void revoke(String name, Object dbobject,
int rights) throws HsqlException {
Grantee g = get(name);
g.revoke(dbobject, rights);
g.updateAllRights();
if (g.isRole) {
updateAllRights(g);
}
}
/**
* Removes a role without any privileges from all grantees
*/
void removeEmptyRole(Grantee role) {
String name = role.getName();
for (int i = 0; i < map.size(); i++) {
Grantee grantee = (Grantee) map.get(i);
grantee.roles.remove(name);
}
}
/**
* Removes all rights mappings for the database object identified by
* the dbobject argument from all Grantee objects in the set.
*/
void removeDbObject(Object dbobject) {
for (int i = 0; i < map.size(); i++) {
Grantee g = (Grantee) map.get(i);
g.revokeDbObject(dbobject);
}
}
/**
* First updates all ROLE Grantee objects. Then updates all USER Grantee
* Objects.
*/
void updateAllRights(Grantee role) {
String name = role.getName();
for (int i = 0; i < map.size(); i++) {
Grantee grantee = (Grantee) map.get(i);
if (grantee.isRole) {
grantee.updateNestedRoles(name);
}
}
for (int i = 0; i < map.size(); i++) {
Grantee grantee = (Grantee) map.get(i);
if (!grantee.isRole) {
grantee.updateAllRights();
}
}
}
/**
*/
public boolean removeGrantee(String name) {
/*
* Explicitly can't remove PUBLIC_USER_NAME and system grantees.
*/
if (isReserved(name)) {
return false;
}
Grantee g = (Grantee) map.remove(name);
if (g == null) {
return false;
}
g.clearPrivileges();
updateAllRights(g);
if (g.isRole) {
roleMap.remove(name);
removeEmptyRole(g);
}
return true;
}
/**
* We don't have to worry about anything manually creating a reserved
* account, because the reserved accounts are created upon DB
* initialization. If somebody tries to create one of these accounts
* after that, it will fail because the account will already exist.
* (We do prevent them from being removed, elsewhere!)
*/
public Grantee addGrantee(String name) throws HsqlException {
if (map.containsKey(name)) {
throw Trace.error(Trace.GRANTEE_ALREADY_EXISTS, name);
}
Grantee pubGrantee = null;
if (!isReserved(name)) {
pubGrantee = get(PUBLIC_ROLE_NAME);
if (pubGrantee == null) {
Trace.doAssert(
false, Trace.getMessage(Trace.MISSING_PUBLIC_GRANTEE));
}
}
Grantee g = new Grantee(name, pubGrantee, this);
map.put(name, g);
return g;
}
/**
* Returns true if named Grantee object exists.
* This will return true for reserved Grantees
* SYSTEM_AUTHORIZATION_NAME, ADMIN_ROLE_NAME, PUBLIC_USER_NAME.
*/
boolean isGrantee(String name) {
return (map.containsKey(name));
}
static int getCheckRight(String right) throws HsqlException {
int r = getRight(right);
if (r != 0) {
return r;
}
throw Trace.error(Trace.NO_SUCH_RIGHT, right);
}
/**
* Translate a string representation or right(s) into its numeric form.
*/
static int getRight(String right) {
return rightsStringLookup.get(right, 0);
}
/**
* Returns a comma separated list of right names corresponding to the
* right flags set in the right argument.
*/
static String getRightsList(int rights) {
// checkValidFlags(right);
if (rights == 0) {
return null;
}
if (rights == ALL) {
return S_R_ALL;
}
return StringUtil.getList(getRightsArray(rights), ",", "");
}
/**
* Retrieves the list of right names represented by the right flags
* set in the specified Integer
object's int
* value.
*
* @param rights An Integer representing a set of right flags
* @return an empty list if the specified Integer
object is
* null, else a list of rights, as String
objects,
* represented by the rights flag bits set in the specified
* Integer
object's int value.
*
*/
static String[] getRightsArray(int rights) {
if (rights == 0) {
return emptyRightsList;
}
String[] list = (String[]) hRightsLists.get(rights);
if (list != null) {
return list;
}
list = getRightsArraySub(rights);
hRightsLists.put(rights, list);
return list;
}
private static String[] getRightsArraySub(int right) {
// checkValidFlags(right);
if (right == 0) {
return emptyRightsList;
}
HsqlArrayList a = new HsqlArrayList();
Iterator it = rightsStringLookup.keySet().iterator();
for (; it.hasNext(); ) {
String rightString = (String) it.next();
if (rightString.equals(S_R_ALL)) {
continue;
}
int i = rightsStringLookup.get(rightString, 0);
if ((right & i) != 0) {
a.add(rightString);
}
}
return (String[]) a.toArray(new String[a.size()]);
}
/**
* Retrieves the set of distinct, fully qualified Java Class
* names upon which any grants currently exist to elements in
* this collection.
* @return the set of distinct, fully qualified Java Class names, as
* String
objects, upon which grants currently exist
* to the elements of this collection
*
*/
HashSet getGrantedClassNames() throws HsqlException {
int size;
Grantee grantee;
HashSet out;
Iterator e;
size = map.size();
out = new HashSet();
for (int i = 0; i < size; i++) {
grantee = (Grantee) map.get(i);
if (grantee == null) {
continue;
}
e = grantee.getGrantedClassNames(false).iterator();
while (e.hasNext()) {
out.add(e.next());
}
}
return out;
}
public Grantee get(String name) {
return (Grantee) map.get(name);
}
public Collection getGrantees() {
return map.values();
}
public static boolean validRightString(String rightString) {
return getRight(rightString) != 0;
}
public static boolean isImmutable(String name) {
return name.equals(SYSTEM_AUTHORIZATION_NAME)
|| name.equals(DBA_ADMIN_ROLE_NAME);
}
public static boolean isReserved(String name) {
return name.equals(SYSTEM_AUTHORIZATION_NAME)
|| name.equals(DBA_ADMIN_ROLE_NAME)
|| name.equals(PUBLIC_ROLE_NAME);
}
/**
* Creates a new Role object under management of this object.
*
* A set of constraints regarding user creation is imposed:
*
*
* - Can't create a role with name same as any right.
*
*
- If the specified name is null, then an
* ASSERTION_FAILED exception is thrown stating that
* the name is null.
*
*
- If this object's collection already contains an element whose
* name attribute equals the name argument, then
* a GRANTEE_ALREADY_EXISTS or ROLE_ALREADY_EXISTS Trace
* is thrown.
* (This will catch attempts to create Reserved grantee names).
*
*/
String addRole(String name) throws HsqlException {
/*
* Role names can't be right names because that would cause
* conflicts with "GRANT name TO...". This doesn't apply to
* User names or Grantee names in general, since you can't
* "GRANT username TO...". That's why this check is only here.
*/
if (name == null) {
Trace.doAssert(false, Trace.getMessage(Trace.NULL_NAME));
}
Grantee g = null;
if (GranteeManager.validRightString(name)) {
throw Trace.error(Trace.ILLEGAL_ROLE_NAME, name);
}
g = addGrantee(name);
g.isRole = true;
boolean result = roleMap.add(name, g);
if (!result) {
throw Trace.error(Trace.ROLE_ALREADY_EXISTS, name);
}
// I don't think can get this trace since every roleMap element
// will have a Grantee element which was already verified
// above. Easier to leave this check here than research it.
return name;
}
/**
* Attempts to drop a Role with the specified name
* from this object's set.
*
* A successful drop action consists of:
*
*
*
* - removing the Grantee object with the specified name
* from the set.
*
*
- revoking all rights from the removed object
* (this ensures that in case there are still references to the
* just dropped Grantee object, those references
* cannot be used to erronously access database objects).
*
*
*
*/
void dropRole(String name) throws HsqlException {
if (name.equals(GranteeManager.DBA_ADMIN_ROLE_NAME)) {
throw Trace.error(Trace.ACCESS_IS_DENIED);
}
if (!isRole(name)) {
throw Trace.error(Trace.NO_SUCH_ROLE, name);
}
removeGrantee(name);
roleMap.remove(name);
}
public Set getRoleNames() {
return roleMap.keySet();
}
/**
* Returns Grantee for the named Role
*/
Grantee getRole(String name) throws HsqlException {
if (!isRole(name)) {
Trace.doAssert(false, "No role '" + name + "'");
}
Grantee g = (Grantee) roleMap.get(name);
if (g == null) {
throw Trace.error(Trace.MISSING_GRANTEE, name);
}
return g;
}
boolean isRole(String name) throws HsqlException {
return roleMap.containsKey(name);
}
}