/** * '$RCSfile$' * Purpose: A Class that loads eml-access.xml file containing ACL * for a metadata document into relational DB * Copyright: 2000 Regents of the University of California and the * National Center for Ecological Analysis and Synthesis * Authors: Jivka Bojilova * * '$Author$' * '$Date$' * '$Revision$' * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package edu.ucsb.nceas.metacat.accesscontrol; import java.io.IOException; import java.io.StringReader; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Vector; import org.apache.log4j.Logger; import org.xml.sax.ContentHandler; import org.xml.sax.ErrorHandler; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; import org.xml.sax.helpers.XMLReaderFactory; import edu.ucsb.nceas.metacat.DBUtil; import edu.ucsb.nceas.metacat.IdentifierManager; import edu.ucsb.nceas.metacat.McdbDocNotFoundException; import edu.ucsb.nceas.metacat.McdbException; import edu.ucsb.nceas.metacat.PermissionController; import edu.ucsb.nceas.metacat.database.DBConnection; import edu.ucsb.nceas.metacat.database.DBConnectionPool; import edu.ucsb.nceas.metacat.properties.PropertyService; import edu.ucsb.nceas.metacat.shared.AccessException; import edu.ucsb.nceas.metacat.util.DocumentUtil; import edu.ucsb.nceas.utilities.PropertyNotFoundException; import edu.ucsb.nceas.utilities.access.AccessControlInterface; import edu.ucsb.nceas.utilities.access.DocInfoHandler; import edu.ucsb.nceas.utilities.access.XMLAccessDAO; /** * A Class that loads eml-access.xml file containing ACL for a metadata * document into relational DB. It extends DefaultHandler class to handle * SAX parsing events when processing the XML stream. */ public class AccessControlForSingleFile implements AccessControlInterface { private String _guid; private Logger logMetacat = Logger.getLogger(AccessControlForSingleFile.class); /** * Construct an instance of the AccessControlForSingleFile class. This * instance will represent one file only. * * @param myAccessNumber * the docid or docid with dev will be controlled */ public AccessControlForSingleFile(String accessionNumber) throws AccessControlException { // this is a local metacat id String docid = DocumentUtil.getDocIdFromString(accessionNumber); // get the revision String revision = DocumentUtil.getRevisionStringFromString(accessionNumber); int rev = -1; if (revision != null) { // we were given it rev = Integer.valueOf(revision); } else { // look up the latest try { rev = DBUtil.getLatestRevisionInDocumentTable(docid); } catch (SQLException e) { AccessControlException ace = new AccessControlException(e.getMessage()); ace.initCause(e); throw ace; } if (rev <= 0) { // look in the revisions table try { rev = DBUtil.getMaxRevFromRevisionTable(docid); } catch (SQLException e) { AccessControlException ace = new AccessControlException(e.getMessage()); ace.initCause(e); throw ace; } } } // find the guid for this docid.rev try { _guid = IdentifierManager.getInstance().getGUID(docid, rev); } catch (McdbDocNotFoundException e) { AccessControlException ace = new AccessControlException(e.getMessage()); ace.initCause(e); throw ace; } // couldn't find it? if (_guid == null || _guid.equals("")) { throw new AccessControlException("Guid cannot be null"); } logMetacat.debug("AccessControlForSingleFile() - docid: " + _guid); } /** * Insert a single access record into the database based on access DAO * object * * @param xmlAccessDAO * dao object holding info to insert */ public void insertPermissions(XMLAccessDAO xmlAccessDAO) throws AccessControlException, PermOrderException{ insertPermissions(xmlAccessDAO.getPrincipalName(), xmlAccessDAO.getPermission(), xmlAccessDAO.getPermType(), xmlAccessDAO.getPermOrder(), xmlAccessDAO.getAccessFileId(), xmlAccessDAO.getSubTreeId()); } /** * Insert a single access record into the database. * * @param principalName * the principal credentials * @param permission * the permission * @param permType * the permission type * @param permOrder * the permission order */ public void insertPermissions(String principalName, Long permission, String permType, String permOrder, String accessFileId, String subTreeId) throws AccessControlException, PermOrderException { try { // The addXMLAccess method will create the permission record if it does not exist. // It will bitwise OR to permissions if the principal already has a record for this // doc id. XMLAccessAccess xmlAccessAccess = new XMLAccessAccess(); //System.out.println("permission in accessControlForSingleFile.insertPermissions: " + permission); xmlAccessAccess.addXMLAccess(_guid, principalName, new Long(permission), permType, permOrder, accessFileId, subTreeId); } catch (AccessException ae) { throw new AccessControlException("AccessControlForSingleFile.insertPermissions - " + "DB access error when inserting permissions: " + ae.getMessage()); } } /** * Replace existing permissions with a given block of permissions for this * document. * * @param accessBlock * the xml access block. This is the same structure as that * returned by the getdocumentinfo action in metacat. */ public void insertPermissions(String accessBlock) throws AccessControlException { try { // use DocInfoHandler to parse the access section into DAO objects XMLReader parser = null; DocInfoHandler docInfoHandler = new DocInfoHandler(_guid); ContentHandler chandler = docInfoHandler; // Get an instance of the parser String parserName = PropertyService.getProperty("xml.saxparser"); parser = XMLReaderFactory.createXMLReader(parserName); // Turn off validation parser.setFeature("http://xml.org/sax/features/validation", false); parser.setContentHandler((ContentHandler)chandler); parser.setErrorHandler((ErrorHandler)chandler); parser.parse(new InputSource(new StringReader(accessBlock))); XMLAccessAccess xmlAccessAccess = new XMLAccessAccess(); // replace all access on the document Vector accessControlList = docInfoHandler.getAccessControlList(); xmlAccessAccess.replaceAccess(_guid, accessControlList); } catch (PropertyNotFoundException pnfe) { throw new AccessControlException("AccessControlForSingleFile.insertPermissions - " + "property error when replacing permissions: " + pnfe.getMessage()); } catch (AccessException ae) { throw new AccessControlException("AccessControlForSingleFile.insertPermissions - " + "DB access error when replacing permissions: " + ae.getMessage()); } catch (SAXException se) { throw new AccessControlException("AccessControlForSingleFile.insertPermissions - " + "SAX error when replacing permissions: " + se.getMessage()); } catch(IOException ioe) { throw new AccessControlException("AccessControlForSingleFile.insertPermissions - " + "I/O error when replacing permissions: " + ioe.getMessage()); } } /** * Check if access control comination for * docid/principal/permission/permorder/permtype already exists. * * @param xmlAccessDAO * the dao object holding the access we want to check for. * @return true if the Access Control for this file already exists in the DB */ public boolean accessControlExists(XMLAccessDAO xmlAccessDAO) throws AccessControlException { boolean exists = false; PreparedStatement pstmt = null; DBConnection conn = null; int serialNumber = -1; try { //check out DBConnection conn=DBConnectionPool.getDBConnection ("AccessControlForSingleFiel.accessControlExists"); serialNumber=conn.getCheckOutSerialNumber(); pstmt = conn.prepareStatement( "SELECT * FROM xml_access " + "WHERE guid = ? " + "AND principal_name = ? " + "AND permission = ? " + "AND perm_type = ? " + "AND perm_order = ? "); // Bind the values to the query pstmt.setString(1, _guid); pstmt.setString(2, xmlAccessDAO.getPrincipalName()); pstmt.setLong(3, xmlAccessDAO.getPermission()); pstmt.setString(4, xmlAccessDAO.getPermType()); pstmt.setString(5, xmlAccessDAO.getPermOrder()); pstmt.execute(); ResultSet rs = pstmt.getResultSet(); exists = rs.next(); } catch (SQLException sqle){ throw new AccessControlException("AccessControlForSingleFile.accessControlExists - SQL error when " + "checking if access control exists: " + sqle.getMessage()); } finally { try { if(pstmt != null) { pstmt.close(); } } catch (SQLException sqle) { logMetacat.error("AccessControlForSingleFile.accessControlExists - Could not close " + "prepared statement: " +sqle.getMessage()); } finally { DBConnectionPool.returnDBConnection(conn, serialNumber); } } return exists; } /** * Get Access Control List information for document from db connetion. User * or Group should have permissions for reading access control information * for a document specified by * * @param user * name of user connected to Metacat system * @param groups * names of user's groups to which user belongs */ public String getACL(String user, String[] groups) throws AccessControlException { StringBuffer output = new StringBuffer(); boolean hasPermission = false; try { hasPermission = isOwned(user); if (!hasPermission) { // get the docid for this guid String docid = IdentifierManager.getInstance().getLocalId(_guid); PermissionController controller = new PermissionController(docid); hasPermission = controller.hasPermission(user, groups, READSTRING); } // if the user has permissions, get the access dao list for this doc and return // it as a string. Otherwise, get the string for an empty access dao list // (which will return the access section with no allow or deny sections) if (hasPermission) { // Get a list of all access dao objects for this docid XMLAccessAccess xmlAccessAccess = new XMLAccessAccess(); Vector xmlAccessDAOList = xmlAccessAccess.getXMLAccessForDoc(_guid); output.append(getAccessString(xmlAccessDAOList)); } else { output.append(getAccessString(new Vector())); } return output.toString(); } catch (SQLException sqle) { throw new AccessControlException("AccessControlForSingleFile.getACL() - SQL error when " + "getting ACL: " + sqle.getMessage()); } catch (AccessException ae) { throw new AccessControlException("AccessControlForSingleFile.getACL() - DB access error when " + "getting ACL: " + ae.getMessage()); } catch (McdbException mcdb) { throw new AccessControlException("AccessControlForSingleFile.getACL() - MCDB error when " + "getting ACL: " + mcdb.getMessage()); } } /** * Get the access xml for all access on this docid * * @return string representation of access */ public String getAccessString() throws AccessControlException { Vector xmlAccessDAOList = null; try { // Get a list of all access dao objects for this docid XMLAccessAccess xmlAccessAccess = new XMLAccessAccess(); xmlAccessDAOList = xmlAccessAccess.getXMLAccessForDoc(_guid); } catch (AccessException ae) { throw new AccessControlException("AccessControlForSingleFile.getAccessString() - DB access error when " + "getting access string: " + ae.getMessage()); } return getAccessString(xmlAccessDAOList); } /** * Put together an xml representation of the objects in a given access dao list * @param xmlAccessDAOList list of xml access DAO objects * @return string representation of access */ public String getAccessString(Vector xmlAccessDAOList) throws AccessControlException { StringBuffer output = new StringBuffer(); StringBuffer tmpOutput = new StringBuffer(); StringBuffer allowOutput = new StringBuffer(); StringBuffer denyOutput = new StringBuffer(); String principal = null; int permission = -1; String permOrder = ALLOWFIRST; String permType = null; String accessfileid = null; String subtreeid = null; // We assume that all the records will have the same permission order, so we can just // grab the perm order from the first one. if (xmlAccessDAOList.size() > 0) { permOrder = xmlAccessDAOList.get(0).getPermOrder(); accessfileid = xmlAccessDAOList.get(0).getAccessFileId(); subtreeid = xmlAccessDAOList.get(0).getSubTreeId(); } // get the docid for this guid String docid = _guid; try { docid = IdentifierManager.getInstance().getLocalId(_guid); } catch (McdbDocNotFoundException e) { logMetacat.warn("Could not lookup docid for guid, defaulting to guid: " + _guid, e); } catch (SQLException e){ throw new AccessControlException("Couldn't identify the local id of the object with the specified identifier "+_guid+" since "+e.getMessage()); } output.append("\n"); for (XMLAccessDAO xmlAccessDAO : xmlAccessDAOList) { principal = xmlAccessDAO.getPrincipalName(); permission = xmlAccessDAO.getPermission().intValue(); //System.out.println("accessControlForSingleFile.getAccessString: permission is set to: " + permission); permType = xmlAccessDAO.getPermType(); tmpOutput.append(" <" + permType + ">\n"); tmpOutput.append(" " + principal + "\n"); if ((permission & READ) == READ) { tmpOutput.append(" read\n"); } if ((permission & WRITE) == WRITE) { tmpOutput.append(" write\n"); } if ((permission & ALL) == ALL) { tmpOutput.append(" all\n"); } if ((permission & CHMOD) == CHMOD) { tmpOutput.append(" chmod\n"); } tmpOutput.append(" \n"); if (permType.equals(ALLOW)) { allowOutput.append(tmpOutput); } else if (permType.equals(DENY)) { denyOutput.append(tmpOutput); } tmpOutput = new StringBuffer(); } // This just orders the allow/deny sections based on the permOrder. Not // required, but convenient later when parsing the output. if (permOrder.equals(ALLOWFIRST)) { output.append(allowOutput); output.append(denyOutput); } else if (permOrder.equals(DENYFIRST)) { output.append(denyOutput); output.append(allowOutput); } output.append(""); return output.toString(); } /** * check if the docid represented in this class is owned by the user * * @param user * the user credentials * @return true if doc is owned by user, false otherwise */ private boolean isOwned(String user) throws SQLException { PreparedStatement pstmt = null; DBConnection conn = null; int serialNumber = -1; try { // check out DBConnection conn = DBConnectionPool.getDBConnection("AccessControlList.isOwned"); serialNumber = conn.getCheckOutSerialNumber(); String query = "SELECT id.guid FROM xml_documents xd, identifier id " + "WHERE xd.docid = id.docid " + "AND xd.rev = id.rev " + "AND id.guid = ? " + "AND user_owner = ? "; pstmt = conn.prepareStatement(query ); pstmt.setString(1, _guid); pstmt.setString(2, user); pstmt.execute(); ResultSet rs = pstmt.getResultSet(); boolean hasRow = rs.next(); return hasRow; } finally { try { if (pstmt != null) { pstmt.close(); } } finally { DBConnectionPool.returnDBConnection(conn, serialNumber); } } } }