/** * Licensed to Apereo under one or more contributor license agreements. See the NOTICE file * distributed with this work for additional information regarding copyright ownership. Apereo * licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use * this file except in compliance with the License. You may obtain a copy of the License at the * following location: * * <p>http://www.apache.org/licenses/LICENSE-2.0 * * <p>Unless required by applicable law or agreed to in writing, software distributed under the * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the License for the specific language governing permissions and * limitations under the License. */ package org.apereo.portal.security.provider; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Timestamp; import java.sql.Types; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apereo.portal.AuthorizationException; import org.apereo.portal.jdbc.RDBMServices; import org.apereo.portal.security.IPermission; import org.apereo.portal.security.IPermissionStore; import org.springframework.stereotype.Repository; /** * Reference implementation of IPermissionStore. Performs CRUD operations on the UP_Permission * table. * */ @Repository public class RDBMPermissionImpl implements IPermissionStore { private static final Log log = LogFactory.getLog(RDBMPermissionImpl.class); // sql Strings: private static String PERMISSION_TABLE = "UP_PERMISSION"; private static String OWNER_COLUMN = "OWNER"; private static String PRINCIPAL_TYPE_COLUMN = "PRINCIPAL_TYPE"; private static String PRINCIPAL_KEY_COLUMN = "PRINCIPAL_KEY"; private static String ACTIVITY_COLUMN = "ACTIVITY"; private static String TARGET_COLUMN = "TARGET"; private static String TYPE_COLUMN = "PERMISSION_TYPE"; private static String EFFECTIVE_COLUMN = "EFFECTIVE"; private static String EXPIRES_COLUMN = "EXPIRES"; private static String allPermissionColumnsSql; private static String deletePermissionSql; private static String findPermissionSql; private static String insertPermissionSql; private static String selectPermissionSql; private static String updatePermissionSql; public static String PRINCIPAL_SEPARATOR = "."; /** * Signifies that the principal is an {@see IEntityGroup}. The {@see IPermission} contract does * not define a method like <code>setPrincipalType()</code>, but {@see RDBMPermissionImpl} (this * class) <i>does</i> have a notion of principal type. Specify <code> * (PRINCIPAL_TYPE_GROUP|PRINCIPAL_TYPE_USER)PRINCIPAL_SEPARATOR<principal.key></code> to * {@see IPermission.setPrincipal} for use with {@see RDBMPermissionImpl}. */ public static int PRINCIPAL_TYPE_GROUP = 1; /** * Signifies that the principal is an {@see IPerson}. The {@see IPermission} contract does not * define a method like <code>setPrincipalType()</code>, but {@see RDBMPermissionImpl} (this * class) <i>does</i> have a notion of principal type. Specify <code> * (PRINCIPAL_TYPE_GROUP|PRINCIPAL_TYPE_USER)PRINCIPAL_SEPARATOR<principal.key></code> to * {@see IPermission.setPrincipal} for use with {@see RDBMPermissionImpl}. */ public static int PRINCIPAL_TYPE_PERSON = 2; /** * Add the IPermissions to the store. * * @param perms org.apereo.portal.security.IPermission[] * @exception AuthorizationException - wraps an Exception specific to the store. */ public void add(IPermission[] perms) throws AuthorizationException { if (perms.length > 0) { try { primAdd(perms); } catch (Exception ex) { log.error("Exception adding permissions " + perms, ex); throw new AuthorizationException(ex); } } } /** * Add the IPermission to the store. * * @param perm org.apereo.portal.security.IPermission * @exception AuthorizationException - wraps an Exception specific to the store. */ public void add(IPermission perm) throws AuthorizationException { Connection conn = null; int rc = 0; try { conn = RDBMServices.getConnection(); String sQuery = getInsertPermissionSql(); PreparedStatement ps = conn.prepareStatement(sQuery); try { primAdd(perm, ps); if (log.isDebugEnabled()) log.debug("RDBMPermissionImpl.add(): " + ps); rc = ps.executeUpdate(); if (rc != 1) { throw new AuthorizationException("Problem adding Permission " + perm); } } finally { ps.close(); } } catch (Exception ex) { log.error("Exception adding permission [" + perm + "]", ex); throw new AuthorizationException("Problem adding Permission " + perm); } finally { RDBMServices.releaseConnection(conn); } } /** * Delete the IPermissions from the store. * * @param perms org.apereo.portal.security.IPermission[] * @exception AuthorizationException - wraps an Exception specific to the store. */ public void delete(IPermission[] perms) throws AuthorizationException { if (perms.length > 0) { try { primDelete(perms); } catch (Exception ex) { log.error("Exception deleting permissions " + Arrays.toString(perms), ex); throw new AuthorizationException( "Exception deleting permissions " + Arrays.toString(perms), ex); } } } /** * Delete a single IPermission from the store. * * @param perm org.apereo.portal.security.IPermission * @exception AuthorizationException - wraps an Exception specific to the store. */ public void delete(IPermission perm) throws AuthorizationException { Connection conn = null; try { conn = RDBMServices.getConnection(); String sQuery = getDeletePermissionSql(); PreparedStatement ps = conn.prepareStatement(sQuery); try { primDelete(perm, ps); } finally { ps.close(); } } catch (Exception ex) { log.error("Exception deleting permission [" + perm + "]", ex); throw new AuthorizationException("Problem deleting Permission " + perm, ex); } finally { RDBMServices.releaseConnection(conn); } } /** @return java.lang.String */ private static String getAllPermissionColumnsSql() { if (allPermissionColumnsSql == null) { StringBuffer sqlBuff = new StringBuffer(200); sqlBuff.append(OWNER_COLUMN); sqlBuff.append(", "); sqlBuff.append(PRINCIPAL_TYPE_COLUMN); sqlBuff.append(", "); sqlBuff.append(PRINCIPAL_KEY_COLUMN); sqlBuff.append(", "); sqlBuff.append(ACTIVITY_COLUMN); sqlBuff.append(", "); sqlBuff.append(TARGET_COLUMN); sqlBuff.append(", "); sqlBuff.append(TYPE_COLUMN); sqlBuff.append(", "); sqlBuff.append(EFFECTIVE_COLUMN); sqlBuff.append(", "); sqlBuff.append(EXPIRES_COLUMN); allPermissionColumnsSql = sqlBuff.toString(); } return allPermissionColumnsSql; } /** @return java.lang.String */ private static String getDeletePermissionSql() { if (deletePermissionSql == null) { StringBuffer sqlBuff = new StringBuffer(200); sqlBuff.append("DELETE FROM "); sqlBuff.append(PERMISSION_TABLE); sqlBuff.append(" WHERE "); sqlBuff.append(OWNER_COLUMN); sqlBuff.append(" = ? AND "); sqlBuff.append(PRINCIPAL_TYPE_COLUMN); sqlBuff.append(" = ? AND "); sqlBuff.append(PRINCIPAL_KEY_COLUMN); sqlBuff.append(" = ? AND "); sqlBuff.append(ACTIVITY_COLUMN); sqlBuff.append(" = ? AND "); sqlBuff.append(TARGET_COLUMN); sqlBuff.append(" = ? "); deletePermissionSql = sqlBuff.toString(); } return deletePermissionSql; } /** * Insert the method's description here. Creation date: (11/6/01 5:19:57 PM) * * @return java.lang.String */ private static java.lang.String getFindPermissionSql() { if (findPermissionSql == null) { StringBuffer sqlBuff = new StringBuffer(getSelectPermissionSql()); sqlBuff.append("WHERE "); sqlBuff.append(OWNER_COLUMN); sqlBuff.append(" = ? AND "); sqlBuff.append(PRINCIPAL_TYPE_COLUMN); sqlBuff.append(" = ? AND "); sqlBuff.append(PRINCIPAL_KEY_COLUMN); sqlBuff.append(" = ? AND "); sqlBuff.append(ACTIVITY_COLUMN); sqlBuff.append(" = ? AND "); sqlBuff.append(TARGET_COLUMN); sqlBuff.append(" = ? "); sqlBuff.append(TYPE_COLUMN); sqlBuff.append(" = ? "); findPermissionSql = sqlBuff.toString(); } return findPermissionSql; } /** @return java.lang.String */ private static String getInsertPermissionSql() { if (insertPermissionSql == null) { StringBuffer sqlBuff = new StringBuffer(200); sqlBuff.append("INSERT INTO "); sqlBuff.append(PERMISSION_TABLE); sqlBuff.append(" ("); sqlBuff.append(getAllPermissionColumnsSql()); sqlBuff.append(") VALUES (?, ?, ?, ?, ?, ?, ?, ?)"); insertPermissionSql = sqlBuff.toString(); } return insertPermissionSql; } /** * Returns the principal key portion of the IPermission principal. * * @return String * @param principalString */ private String getPrincipalKey(String principalString) { return principalString.substring(principalString.indexOf(PRINCIPAL_SEPARATOR) + 1); } /** * Returns the principal key portion of the IPermission principal. * * @return String * @param perm org.apereo.portal.security.IPermission * @exception AuthorizationException */ private String getPrincipalKey(IPermission perm) throws AuthorizationException { return getPrincipalKey(perm.getPrincipal()); } /** * Returns the principal type portion of the principal. * * @return int * @param principalString */ private int getPrincipalType(String principalString) { return Integer.parseInt( principalString.substring(0, principalString.indexOf(PRINCIPAL_SEPARATOR))); } /** * Returns the principal type portion of the IPermission principal. * * @return int * @param perm org.apereo.portal.security.IPermission * @exception AuthorizationException */ private int getPrincipalType(IPermission perm) throws AuthorizationException { return getPrincipalType(perm.getPrincipal()); } /** @return java.lang.String */ private static String getSelectPermissionSql() { if (selectPermissionSql == null) { StringBuffer sqlBuff = new StringBuffer(200); sqlBuff.append("SELECT "); sqlBuff.append(getAllPermissionColumnsSql()); sqlBuff.append(" FROM "); sqlBuff.append(PERMISSION_TABLE); sqlBuff.append(" "); selectPermissionSql = sqlBuff.toString(); } return selectPermissionSql; } /** @return java.lang.String */ private static String getUpdatePermissionSql() { if (updatePermissionSql == null) { StringBuffer sqlBuff = new StringBuffer(300); sqlBuff.append("UPDATE "); sqlBuff.append(PERMISSION_TABLE); sqlBuff.append(" SET "); sqlBuff.append(TYPE_COLUMN); sqlBuff.append(" = ?, "); sqlBuff.append(EFFECTIVE_COLUMN); sqlBuff.append(" = ?, "); sqlBuff.append(EXPIRES_COLUMN); sqlBuff.append(" = ? WHERE "); sqlBuff.append(OWNER_COLUMN); sqlBuff.append(" = ? AND "); sqlBuff.append(PRINCIPAL_TYPE_COLUMN); sqlBuff.append(" = ? AND "); sqlBuff.append(PRINCIPAL_KEY_COLUMN); sqlBuff.append(" = ? AND "); sqlBuff.append(ACTIVITY_COLUMN); sqlBuff.append(" = ? AND "); sqlBuff.append(TARGET_COLUMN); sqlBuff.append(" = ? "); updatePermissionSql = sqlBuff.toString(); } return updatePermissionSql; } /** * @return org.apereo.portal.security.IPermission * @param rs java.sql.ResultSet */ private IPermission instanceFromResultSet(ResultSet rs) throws SQLException { Timestamp ts = null; IPermission perm = newInstance(rs.getString(OWNER_COLUMN)); perm.setPrincipal( rs.getString(PRINCIPAL_TYPE_COLUMN) + "." + rs.getString(PRINCIPAL_KEY_COLUMN)); perm.setActivity(rs.getString(ACTIVITY_COLUMN)); perm.setTarget(rs.getString(TARGET_COLUMN)); perm.setType(rs.getString(TYPE_COLUMN)); ts = rs.getTimestamp(EFFECTIVE_COLUMN); if (ts != null) { perm.setEffective(new Date(getTimestampMillis(ts))); } ts = rs.getTimestamp(EXPIRES_COLUMN); if (ts != null) { perm.setExpires(new Date(getTimestampMillis(ts))); } return perm; } /** Factory method for IPermissions */ public IPermission newInstance(String owner) { return new PermissionImpl(owner); } /** * Add the IPermissions to the store. * * @param perms org.apereo.portal.security.IPermission[] * @exception Exception */ private void primAdd(IPermission[] perms) throws Exception { Connection conn = null; int rc = 0; try { conn = RDBMServices.getConnection(); String sQuery = getInsertPermissionSql(); PreparedStatement ps = conn.prepareStatement(sQuery); try { RDBMServices.setAutoCommit(conn, false); for (int i = 0; i < perms.length; i++) { primAdd(perms[i], ps); if (log.isDebugEnabled()) log.debug("RDBMPermissionImpl.primAdd(): " + ps); rc = ps.executeUpdate(); if (rc != 1) { String errMsg = "Problem adding " + perms[i] + " RC: " + rc; log.error(errMsg); RDBMServices.rollback(conn); throw new AuthorizationException(errMsg); } } } finally { ps.close(); } RDBMServices.commit(conn); } catch (Exception ex) { log.error("Exception adding permissions " + perms, ex); RDBMServices.rollback(conn); throw ex; } finally { try { RDBMServices.setAutoCommit(conn, true); } finally { RDBMServices.releaseConnection(conn); } } } /** * Set the params on the PreparedStatement and execute the insert. * * @param perm org.apereo.portal.security.IPermission * @param ps java.sql.PreparedStatement - the PreparedStatement for inserting a Permission row. * @exception Exception */ private void primAdd(IPermission perm, PreparedStatement ps) throws Exception { java.sql.Timestamp ts = null; // NON-NULL COLUMNS: ps.clearParameters(); ps.setString(1, perm.getOwner()); ps.setInt(2, getPrincipalType(perm)); ps.setString(3, getPrincipalKey(perm)); ps.setString(4, perm.getActivity()); ps.setString(5, perm.getTarget()); // TYPE: if (perm.getType() == null) { ps.setNull(6, Types.VARCHAR); } else { ps.setString(6, perm.getType()); } // EFFECTIVE: if (perm.getEffective() == null) { ps.setNull(7, Types.TIMESTAMP); } else { ts = new java.sql.Timestamp(perm.getEffective().getTime()); ps.setTimestamp(7, ts); } // EXPIRES: if (perm.getExpires() == null) { ps.setNull(8, Types.TIMESTAMP); } else { ts = new java.sql.Timestamp(perm.getExpires().getTime()); ps.setTimestamp(8, ts); } } /** * Delete the IPermissions from the store. * * @param perms org.apereo.portal.security.IPermission[] * @exception Exception */ private void primDelete(IPermission[] perms) throws Exception { Connection conn = null; try { conn = RDBMServices.getConnection(); String sQuery = getDeletePermissionSql(); PreparedStatement ps = conn.prepareStatement(sQuery); try { RDBMServices.setAutoCommit(conn, false); for (int i = 0; i < perms.length; i++) { primDelete(perms[i], ps); } } finally { ps.close(); } RDBMServices.commit(conn); } catch (Exception ex) { log.error("Exception deleting permissions [" + perms + "]", ex); RDBMServices.rollback(conn); throw ex; } finally { try { RDBMServices.setAutoCommit(conn, true); } finally { RDBMServices.releaseConnection(conn); } } } /** * Set the params on the PreparedStatement and execute the delete. * * @param perm org.apereo.portal.security.IPermission * @param ps java.sql.PreparedStatement - the PreparedStatement for deleting a Permission row. * @return int - the return code from the PreparedStatement * @exception Exception */ private int primDelete(IPermission perm, PreparedStatement ps) throws Exception { ps.clearParameters(); ps.setString(1, perm.getOwner()); ps.setInt(2, getPrincipalType(perm)); ps.setString(3, getPrincipalKey(perm)); ps.setString(4, perm.getActivity()); ps.setString(5, perm.getTarget()); if (log.isDebugEnabled()) log.debug("RDBMPermissionImpl.primDelete(): " + ps); return ps.executeUpdate(); } /** * Update the IPermissions in the store. * * @param perms org.apereo.portal.security.IPermission[] * @exception Exception */ private void primUpdate(IPermission[] perms) throws Exception { Connection conn = null; try { conn = RDBMServices.getConnection(); String sQuery = getUpdatePermissionSql(); PreparedStatement ps = conn.prepareStatement(sQuery); try { RDBMServices.setAutoCommit(conn, false); for (int i = 0; i < perms.length; i++) { primUpdate(perms[i], ps); } } finally { ps.close(); } RDBMServices.commit(conn); } catch (Exception ex) { log.error("Exception updating permissions " + perms, ex); RDBMServices.rollback(conn); throw ex; } finally { try { RDBMServices.setAutoCommit(conn, true); } finally { RDBMServices.releaseConnection(conn); } } } /** * Set the params on the PreparedStatement and execute the update. * * @param perm org.apereo.portal.security.IPermission * @param ps java.sql.PreparedStatement - the PreparedStatement for updating a Permission row. * @return int - the return code from the PreparedStatement * @exception Exception */ private int primUpdate(IPermission perm, PreparedStatement ps) throws Exception { java.sql.Timestamp ts = null; // UPDATE COLUMNS: ps.clearParameters(); // TYPE: if (perm.getType() == null) { ps.setNull(1, Types.VARCHAR); } else { ps.setString(1, perm.getType()); } // EFFECTIVE: if (perm.getEffective() == null) { ps.setNull(2, Types.TIMESTAMP); } else { ts = new java.sql.Timestamp(perm.getEffective().getTime()); ps.setTimestamp(2, ts); } // EXPIRES: if (perm.getExpires() == null) { ps.setNull(3, Types.TIMESTAMP); } else { ts = new java.sql.Timestamp(perm.getExpires().getTime()); ps.setTimestamp(3, ts); } // WHERE COLUMNS: ps.setString(4, perm.getOwner()); ps.setInt(5, getPrincipalType(perm)); ps.setString(6, getPrincipalKey(perm)); ps.setString(7, perm.getActivity()); ps.setString(8, perm.getTarget()); if (log.isDebugEnabled()) log.debug("RDBMPermissionImpl.primUpdate(): " + ps); return ps.executeUpdate(); } private void prepareSelectQuery( PreparedStatement stmt, String owner, String principal, String activity, String target, String type) throws SQLException { int i = 1; if (owner != null) { stmt.setString(i++, owner); } if (principal != null) { stmt.setInt(i++, getPrincipalType(principal)); stmt.setString(i++, getPrincipalKey(principal)); } if (activity != null) { stmt.setString(i++, activity); } if (target != null) { stmt.setString(i++, target); } if (type != null) { stmt.setString(i++, type); } } private String getSelectQuery( String owner, String principal, String activity, String target, String type) { StringBuffer sqlQuery = new StringBuffer(getSelectPermissionSql()); sqlQuery.append(" WHERE "); if (owner != null) { sqlQuery.append(OWNER_COLUMN); sqlQuery.append(" = ? "); } else { sqlQuery.append("1 = 1 "); } if (principal != null) { sqlQuery.append("AND "); sqlQuery.append(PRINCIPAL_TYPE_COLUMN); sqlQuery.append(" = ? AND "); sqlQuery.append(PRINCIPAL_KEY_COLUMN); sqlQuery.append(" = ? "); } if (activity != null) { sqlQuery.append("AND "); sqlQuery.append(ACTIVITY_COLUMN); sqlQuery.append(" = ? "); } if (target != null) { sqlQuery.append("AND "); sqlQuery.append(TARGET_COLUMN); sqlQuery.append(" = ? "); } if (type != null) { sqlQuery.append("AND "); sqlQuery.append(TYPE_COLUMN); sqlQuery.append(" = ? "); } if (log.isTraceEnabled()) { log.trace( "Computed SQL query [" + sqlQuery + "] for owner=[" + owner + "] and principal=[" + principal + "] and activity=[" + activity + "] and target=[" + target + "] and type=[" + type + "]"); } return sqlQuery.toString(); } /** * Select the Permissions from the store. * * @param owner String - the Permission owner * @param principal String - the Permission principal * @param activity String - the Permission activity * @exception AuthorizationException - wraps an Exception specific to the store. */ public IPermission[] select( String owner, String principal, String activity, String target, String type) throws AuthorizationException { Connection conn = null; PreparedStatement stmt = null; ResultSet rs = null; List<IPermission> perms = new ArrayList<IPermission>(); String query = getSelectQuery(owner, principal, activity, target, type); try { conn = RDBMServices.getConnection(); stmt = conn.prepareStatement(query); prepareSelectQuery(stmt, owner, principal, activity, target, type); try { rs = stmt.executeQuery(); try { while (rs.next()) { perms.add(instanceFromResultSet(rs)); } } finally { rs.close(); } } finally { stmt.close(); } } catch (SQLException sqle) { log.error("Problem retrieving permissions", sqle); throw new AuthorizationException( "Problem retrieving Permissions [" + sqle.getMessage() + "] for query=[" + query + "] for owner=[" + owner + "] and principal=[" + principal + "] and activity=[" + activity + "] and target=[" + target + "] and type=[" + type + "]", sqle); } finally { RDBMServices.releaseConnection(conn); } if (log.isTraceEnabled()) { log.trace( "RDBMPermissionImpl.select(): [" + query + "] for owner=[" + owner + "] and principal=[" + principal + "] and activity=[" + activity + "] and target=[" + target + "] and type=[" + type + "] returned permissions [" + perms + "]"); } return ((IPermission[]) perms.toArray(new IPermission[perms.size()])); } /** * Update the IPermissions in the store. * * @param perms org.apereo.portal.security.IPermission[] * @exception AuthorizationException - wraps an Exception specific to the store. */ public void update(IPermission[] perms) throws AuthorizationException { if (perms.length > 0) { try { primUpdate(perms); } catch (Exception ex) { log.error("Exception updating permissions " + perms, ex); throw new AuthorizationException(ex); } } } /** * Update a single IPermission in the store. * * @param perm org.apereo.portal.security.IPermission * @exception AuthorizationException - wraps an Exception specific to the store. */ public void update(IPermission perm) throws AuthorizationException { Connection conn = null; try { conn = RDBMServices.getConnection(); String sQuery = getUpdatePermissionSql(); if (log.isDebugEnabled()) log.debug("RDBMPermissionImpl.update(): " + sQuery); PreparedStatement ps = conn.prepareStatement(sQuery); try { primUpdate(perm, ps); } finally { ps.close(); } } catch (Exception ex) { log.error("Exception updating permission [" + perm + "]", ex); throw new AuthorizationException("Problem updating Permission " + perm); } finally { RDBMServices.releaseConnection(conn); } } /** @return long */ private static long getTimestampMillis(Timestamp ts) { return ts.getTime(); } }