/* * AuthorizeManager.java * * Version: $Revision: 3705 $ * * Date: $Date: 2009-04-11 18:02:24 +0100 (Sat, 11 Apr 2009) $ * * Copyright (c) 2002-2005, Hewlett-Packard Company and Massachusetts * Institute of Technology. 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 Hewlett-Packard Company nor the name of the * Massachusetts Institute of Technology nor the names of their * 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 THE COPYRIGHT * HOLDERS 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.dspace.authorize; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import org.dspace.content.DSpaceObject; import org.dspace.core.Constants; import org.dspace.core.Context; import org.dspace.eperson.EPerson; import org.dspace.eperson.Group; import org.dspace.storage.rdbms.DatabaseManager; import org.dspace.storage.rdbms.TableRow; import org.dspace.storage.rdbms.TableRowIterator; /** * AuthorizeManager handles all authorization checks for DSpace. For better * security, DSpace assumes that you do not have the right to do something * unless that permission is spelled out somewhere. That "somewhere" is the * ResourcePolicy table. The AuthorizeManager is given a user, an object, and an * action, and it then does a lookup in the ResourcePolicy table to see if there * are any policies giving the user permission to do that action. * <p> * ResourcePolicies now apply to single objects (such as submit (ADD) permission * to a collection.) * <p> * Note: If an eperson is a member of the administrator group (id 1), then they * are automatically given permission for all requests another special group is * group 0, which is anonymous - all EPeople are members of group 0. */ public class AuthorizeManager { /** * Utility method, checks that the current user of the given context can * perform all of the specified actions on the given object. An * <code>AuthorizeException</code> if all the authorizations fail. * * @param c * context with the current user * @param o * DSpace object user is attempting to perform action on * @param actions * array of action IDs from * <code>org.dspace.core.Constants</code> * @throws AuthorizeException * if any one of the specified actions cannot be performed by * the current user on the given object. * @throws SQLException * if there's a database problem */ public static void authorizeAnyOf(Context c, DSpaceObject o, int[] actions) throws AuthorizeException, SQLException { AuthorizeException ex = null; for (int i = 0; i < actions.length; i++) { try { authorizeAction(c, o, actions[i]); return; } catch (AuthorizeException e) { if (ex == null) { ex = e; } } } throw ex; } /** * Checks that the context's current user can perform the given action on * the given object. Throws an exception if the user is not authorized, * otherwise the method call does nothing. * * @param c * context * @param o * a DSpaceObject * @param action * action to perform from <code>org.dspace.core.Constants</code> * * @throws AuthorizeException * if the user is denied */ public static void authorizeAction(Context c, DSpaceObject o, int action) throws AuthorizeException, SQLException { if (o == null) { // action can be -1 due to a null entry String actionText; if (action == -1) { actionText = "null"; } else { actionText = Constants.actionText[action]; } EPerson e = c.getCurrentUser(); int userid; if (e == null) { userid = 0; } else { userid = e.getID(); } throw new AuthorizeException( "Authorization attempted on null DSpace object " + actionText + " by user " + userid); } if (!authorize(c, o, action, c.getCurrentUser())) { // denied, assemble and throw exception int otype = o.getType(); int oid = o.getID(); int userid; EPerson e = c.getCurrentUser(); if (e == null) { userid = 0; } else { userid = e.getID(); } // AuthorizeException j = new AuthorizeException("Denied"); // j.printStackTrace(); // action can be -1 due to a null entry String actionText; if (action == -1) { actionText = "null"; } else { actionText = Constants.actionText[action]; } throw new AuthorizeException("Authorization denied for action " + actionText + " on " + Constants.typeText[otype] + ":" + oid + " by user " + userid, o, action); } } /** * same authorize, returns boolean for those who don't want to deal with * catching exceptions. * * @param c * DSpace context, containing current user * @param o * DSpaceObject * @param a * action being attempted, from * <code>org.dspace.core.Constants</code> * * @return <code>true</code> if the current user in the context is * authorized to perform the given action on the given object */ public static boolean authorizeActionBoolean(Context c, DSpaceObject o, int a) throws SQLException { boolean isAuthorized = true; if (o == null) { return false; } try { authorizeAction(c, o, a); } catch (AuthorizeException e) { isAuthorized = false; } return isAuthorized; } /** * Check to see if the given user can perform the given action on the given * object. Always returns true if the ignore authorization flat is set in * the current context. * * @param c * current context. User is irrelevant; "ignore authorization" * flag is relevant * @param o * object action is being attempted on * @param action * ID of action being attempted, from * <code>org.dspace.core.Constants</code> * @param e * user attempting action * @return <code>true</code> if user is authorized to perform the given * action, <code>false</code> otherwise * @throws SQLException */ private static boolean authorize(Context c, DSpaceObject o, int action, EPerson e) throws SQLException { int userid; // return FALSE if there is no DSpaceObject if (o == null) { return false; } // is authorization disabled for this context? if (c.ignoreAuthorization()) { return true; } // is eperson set? if not, userid = 0 (anonymous) if (e == null) { userid = 0; } else { userid = e.getID(); // perform isadmin check since user // is user part of admin group? if (isAdmin(c)) { return true; } } for (ResourcePolicy rp : getPoliciesActionFilter(c, o, action)) { // check policies for date validity if (rp.isDateValid()) { if ((rp.getEPersonID() != -1) && (rp.getEPersonID() == userid)) { return true; // match } if ((rp.getGroupID() != -1) && (Group.isMember(c, rp.getGroupID()))) { // group was set, and eperson is a member // of that group return true; } } } // default authorization is denial return false; } /////////////////////////////////////////////// // admin check methods /////////////////////////////////////////////// /** * Check to see if the current user is an admin. Always return * <code>true</code> if c.ignoreAuthorization is set. Anonymous users * can't be Admins (EPerson set to NULL) * * @param c * current context * * @return <code>true</code> if user is an admin or ignore authorization * flag set */ public static boolean isAdmin(Context c) throws SQLException { // if we're ignoring authorization, user is member of admin if (c.ignoreAuthorization()) { return true; } EPerson e = c.getCurrentUser(); if (e == null) { return false; // anonymous users can't be admins.... } else { return Group.isMember(c, 1); } } /////////////////////////////////////////////// // policy manipulation methods /////////////////////////////////////////////// /** * Add a policy for an individual eperson * * @param c * context. Current user irrelevant * @param o * DSpaceObject to add policy to * @param actionID * ID of action from <code>org.dspace.core.Constants</code> * @param e * eperson who can perform the action * * @throws AuthorizeException * if current user in context is not authorized to add policies */ public static void addPolicy(Context c, DSpaceObject o, int actionID, EPerson e) throws SQLException, AuthorizeException { ResourcePolicy rp = ResourcePolicy.create(c); rp.setResource(o); rp.setAction(actionID); rp.setEPerson(e); rp.update(); } /** * Add a policy for a group * * @param c * current context * @param o * object to add policy for * @param actionID * ID of action from <code>org.dspace.core.Constants</code> * @param g * group to add policy for * @throws SQLException * if there's a database problem * @throws AuthorizeException * if the current user is not authorized to add this policy */ public static void addPolicy(Context c, DSpaceObject o, int actionID, Group g) throws SQLException, AuthorizeException { ResourcePolicy rp = ResourcePolicy.create(c); rp.setResource(o); rp.setAction(actionID); rp.setGroup(g); rp.update(); } /** * Return a List of the policies for an object * * @param c current context * @param o object to retrieve policies for * * @return List of <code>ResourcePolicy</code> objects */ public static List<ResourcePolicy> getPolicies(Context c, DSpaceObject o) throws SQLException { TableRowIterator tri = DatabaseManager.queryTable(c, "resourcepolicy", "SELECT * FROM resourcepolicy WHERE resource_type_id= ? AND resource_id= ? ", o.getType(),o.getID()); List<ResourcePolicy> policies = new ArrayList(); try { while (tri.hasNext()) { TableRow row = tri.next(); // first check the cache (FIXME: is this right?) ResourcePolicy cachepolicy = (ResourcePolicy) c.fromCache( ResourcePolicy.class, row.getIntColumn("policy_id")); if (cachepolicy != null) { policies.add(cachepolicy); } else { policies.add(new ResourcePolicy(c, row)); } } } finally { if (tri != null) tri.close(); } return policies; } /** * Return a List of the policies for a group * * @param c current context * @param g group to retrieve policies for * * @return List of <code>ResourcePolicy</code> objects */ public static List<ResourcePolicy> getPoliciesForGroup(Context c, Group g) throws SQLException { TableRowIterator tri = DatabaseManager.queryTable(c, "resourcepolicy", "SELECT * FROM resourcepolicy WHERE epersongroup_id= ? ", g.getID()); List<ResourcePolicy> policies = new ArrayList<ResourcePolicy>(); try { while (tri.hasNext()) { TableRow row = tri.next(); // first check the cache (FIXME: is this right?) ResourcePolicy cachepolicy = (ResourcePolicy) c.fromCache( ResourcePolicy.class, row.getIntColumn("policy_id")); if (cachepolicy != null) { policies.add(cachepolicy); } else { policies.add(new ResourcePolicy(c, row)); } } } finally { if (tri != null) tri.close(); } return policies; } /** * Return a list of policies for an object that match the action * * @param c * context * @param o * DSpaceObject policies relate to * @param actionID * action (defined in class Constants) * @throws SQLException * if there's a database problem */ public static List<ResourcePolicy> getPoliciesActionFilter(Context c, DSpaceObject o, int actionID) throws SQLException { TableRowIterator tri = DatabaseManager.queryTable(c, "resourcepolicy", "SELECT * FROM resourcepolicy WHERE resource_type_id= ? "+ "AND resource_id= ? AND action_id= ? ", o.getType(), o.getID(),actionID); List<ResourcePolicy> policies = new ArrayList<ResourcePolicy>(); try { while (tri.hasNext()) { TableRow row = tri.next(); // first check the cache (FIXME: is this right?) ResourcePolicy cachepolicy = (ResourcePolicy) c.fromCache( ResourcePolicy.class, row.getIntColumn("policy_id")); if (cachepolicy != null) { policies.add(cachepolicy); } else { policies.add(new ResourcePolicy(c, row)); } } } finally { if (tri != null) tri.close(); } return policies; } /** * Add policies to an object to match those from a previous object * * @param c context * @param src * source of policies * @param dest * destination of inherited policies * @throws SQLException * if there's a database problem * @throws AuthorizeException * if the current user is not authorized to add these policies */ public static void inheritPolicies(Context c, DSpaceObject src, DSpaceObject dest) throws SQLException, AuthorizeException { // find all policies for the source object List<ResourcePolicy> policies = getPolicies(c, src); addPolicies(c, policies, dest); } /** * Copies policies from a list of resource policies to a given DSpaceObject * * @param c * DSpace context * @param policies * List of ResourcePolicy objects * @param dest * object to have policies added * @throws SQLException * if there's a database problem * @throws AuthorizeException * if the current user is not authorized to add these policies */ public static void addPolicies(Context c, List<ResourcePolicy> policies, DSpaceObject dest) throws SQLException, AuthorizeException { // now add them to the destination object for (ResourcePolicy srp : policies) { ResourcePolicy drp = ResourcePolicy.create(c); // copy over values drp.setResource(dest); drp.setAction(srp.getAction()); drp.setEPerson(srp.getEPerson()); drp.setGroup(srp.getGroup()); drp.setStartDate(srp.getStartDate()); drp.setEndDate(srp.getEndDate()); // and write out new policy drp.update(); } } /** * removes ALL policies for an object. FIXME doesn't check authorization * * @param c * DSpace context * @param o * object to remove policies for * @throws SQLException * if there's a database problem */ public static void removeAllPolicies(Context c, DSpaceObject o) throws SQLException { // FIXME: authorization check? DatabaseManager.updateQuery(c, "DELETE FROM resourcepolicy WHERE " + "resource_type_id= ? AND resource_id= ? ", o.getType(), o.getID()); } /** * Remove all policies from an object that match a given action. FIXME * doesn't check authorization * * @param context * current context * @param dso * object to remove policies from * @param actionID * ID of action to match from * <code>org.dspace.core.Constants</code>, or -1=all * @throws SQLException * if there's a database problem */ public static void removePoliciesActionFilter(Context context, DSpaceObject dso, int actionID) throws SQLException { if (actionID == -1) { // remove all policies from object removeAllPolicies(context, dso); } else { DatabaseManager.updateQuery(context, "DELETE FROM resourcepolicy WHERE resource_type_id= ? AND "+ "resource_id= ? AND action_id= ? ", dso.getType(), dso.getID(), actionID); } } /** * Removes all policies relating to a particular group. FIXME doesn't check * authorization * * @param c * current context * @param groupID * ID of the group * @throws SQLException * if there's a database problem */ public static void removeGroupPolicies(Context c, int groupID) throws SQLException { DatabaseManager.updateQuery(c, "DELETE FROM resourcepolicy WHERE " + "epersongroup_id= ? ", groupID); } /** * Removes all policies from a group for a particular object that belong to * a Group. FIXME doesn't check authorization * * @param c * current context * @param o * the object * @param g * the group * @throws SQLException * if there's a database problem */ public static void removeGroupPolicies(Context c, DSpaceObject o, Group g) throws SQLException { DatabaseManager.updateQuery(c, "DELETE FROM resourcepolicy WHERE " + "resource_type_id= ? AND resource_id= ? AND epersongroup_id= ? ", o.getType(), o.getID(), g.getID()); } /** * Returns all groups authorized to perform an action on an object. Returns * empty array if no matches. * * @param c * current context * @param o * object * @param actionID * ID of action frm <code>org.dspace.core.Constants</code> * @return array of <code>Group</code>s that can perform the specified * action on the specified object * @throws java.sql.SQLException * if there's a database problem */ public static Group[] getAuthorizedGroups(Context c, DSpaceObject o, int actionID) throws java.sql.SQLException { // do query matching groups, actions, and objects TableRowIterator tri = DatabaseManager.queryTable(c, "resourcepolicy", "SELECT * FROM resourcepolicy WHERE resource_type_id= ? "+ "AND resource_id= ? AND action_id= ? ",o.getType(),o.getID(),actionID); List<Group> groups = new ArrayList<Group>(); try { while (tri.hasNext()) { TableRow row = tri.next(); // first check the cache (FIXME: is this right?) ResourcePolicy cachepolicy = (ResourcePolicy) c.fromCache( ResourcePolicy.class, row.getIntColumn("policy_id")); ResourcePolicy myPolicy = null; if (cachepolicy != null) { myPolicy = cachepolicy; } else { myPolicy = new ResourcePolicy(c, row); } // now do we have a group? Group myGroup = myPolicy.getGroup(); if (myGroup != null) { groups.add(myGroup); } } } finally { if (tri != null) tri.close(); } Group[] groupArray = new Group[groups.size()]; groupArray = groups.toArray(groupArray); return groupArray; } }