/* * This program is free software; you can redistribute it and/or modify it under the * terms of the GNU General Public License, version 2 as published by the Free Software * Foundation. * * You should have received a copy of the GNU General Public License along with this * program; if not, you can obtain a copy at http://www.gnu.org/licenses/gpl-2.0.html * or from the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * 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. * * * Copyright 2006 - 2016 Pentaho Corporation. All rights reserved. */ package org.pentaho.platform.engine.security.acls; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.pentaho.platform.api.engine.IPentahoAclObjectIdentity; import org.pentaho.platform.api.engine.IPentahoBasicAclEntry; import java.util.Arrays; /** * This is a port from spring-security 2.0.8.RELEASE * @see https://github.com/spring-projects/spring-security/blob/2.0.8.RELEASE/core/src/main/java/org/springframework/security/acl/basic/AbstractBasicAclEntry.java */ public abstract class PentahoAbstractBasicAclEntry implements IPentahoBasicAclEntry { //~ Static fields/initializers ===================================================================================== private static final Log logger = LogFactory.getLog( PentahoAbstractBasicAclEntry.class ); //~ Instance fields ================================================================================================ private IPentahoAclObjectIdentity aclObjectIdentity; private IPentahoAclObjectIdentity aclObjectParentIdentity; private Object recipient; private int[] validPermissions; private int mask = 0; // default means no permissions //~ Constructors =================================================================================================== public PentahoAbstractBasicAclEntry( Object recipient, IPentahoAclObjectIdentity aclObjectIdentity, IPentahoAclObjectIdentity aclObjectParentIdentity, int mask ) { assert recipient != null; assert aclObjectIdentity != null; validPermissions = getValidPermissions(); Arrays.sort( validPermissions ); for ( int i = 0; i < validPermissions.length; i++ ) { if ( logger.isDebugEnabled() ) { logger.debug( "Valid permission: " + printPermissionsBlock( validPermissions[i] ) + " " + printBinary( validPermissions[i] ) + " (" + validPermissions[i] + ")" ); } } this.recipient = recipient; this.aclObjectIdentity = aclObjectIdentity; this.aclObjectParentIdentity = aclObjectParentIdentity; this.mask = mask; } /** * A protected constructor for use by Hibernate. */ protected PentahoAbstractBasicAclEntry() { validPermissions = getValidPermissions(); Arrays.sort( validPermissions ); } //~ Methods ======================================================================================================== public int addPermission( int permissionToAdd ) { return addPermissions( new int[] { permissionToAdd } ); } public int addPermissions( int[] permissionsToAdd ) { if ( logger.isDebugEnabled() ) { logger.debug( "BEFORE Permissions: " + printPermissionsBlock( mask ) + " " + printBinary( mask ) + " (" + mask + ")" ); } for ( int i = 0; i < permissionsToAdd.length; i++ ) { if ( logger.isDebugEnabled() ) { logger.debug( "Add permission: " + printPermissionsBlock( permissionsToAdd[i] ) + " " + printBinary( permissionsToAdd[i] ) + " (" + permissionsToAdd[i] + ")" ); } this.mask |= permissionsToAdd[i]; } if ( Arrays.binarySearch( validPermissions, this.mask ) < 0 ) { throw new IllegalArgumentException( "Resulting permission set will be invalid." ); } else { if ( logger.isDebugEnabled() ) { logger.debug( "AFTER Permissions: " + printPermissionsBlock( mask ) + " " + printBinary( mask ) + " (" + mask + ")" ); } return this.mask; } } public int deletePermission( int permissionToDelete ) { return deletePermissions( new int[] { permissionToDelete } ); } public int deletePermissions( int[] permissionsToDelete ) { if ( logger.isDebugEnabled() ) { logger.debug( "BEFORE Permissions: " + printPermissionsBlock( mask ) + " " + printBinary( mask ) + " (" + mask + ")" ); } for ( int i = 0; i < permissionsToDelete.length; i++ ) { if ( logger.isDebugEnabled() ) { logger.debug( "Delete permission: " + printPermissionsBlock( permissionsToDelete[i] ) + " " + printBinary( permissionsToDelete[i] ) + " (" + permissionsToDelete[i] + ")" ); } this.mask &= ~permissionsToDelete[i]; } if ( Arrays.binarySearch( validPermissions, this.mask ) < 0 ) { throw new IllegalArgumentException( "Resulting permission set will be invalid." ); } else { if ( logger.isDebugEnabled() ) { logger.debug( "AFTER Permissions: " + printPermissionsBlock( mask ) + " " + printBinary( mask ) + " (" + mask + ")" ); } return this.mask; } } public IPentahoAclObjectIdentity getAclObjectIdentity() { return this.aclObjectIdentity; } public IPentahoAclObjectIdentity getAclObjectParentIdentity() { return this.aclObjectParentIdentity; } public int getMask() { return this.mask; } public Object getRecipient() { return this.recipient; } /** * Subclasses must indicate the permissions they support. Each base permission should be an integer with a * base 2. ie: the first permission is 2^^0 (1), the second permission is 2^^1 (2), the third permission is 2^^2 * (4) etc. Each base permission should be exposed by the subclass as a <code>public static final int</code>. It * is further recommended that valid combinations of permissions are also exposed as <code>public static final * int</code>s.<P>This method returns all permission integers that are allowed to be used together. <B>This * must include any combinations of valid permissions</b>. So if the permissions indicated by 2^^2 (4) and 2^^1 * (2) can be used together, one of the integers returned by this method must be 6 (4 + 2). Otherwise attempts to * set the permission will be rejected, as the final resulting mask will be rejected.</p> * <P>Whilst it may seem unduly time onerous to return every valid permission <B>combination</B>, doing so * delivers maximum flexibility in ensuring ACLs only reflect logical combinations. For example, it would be * inappropriate to grant a "read" and "write" permission along with an "unrestricted" permission, as the latter * implies the former permissions.</p> * * @return <b>every</b> valid combination of permissions */ public abstract int[] getValidPermissions(); public boolean isPermitted( int permissionToCheck ) { return isPermitted( this.mask, permissionToCheck ); } protected boolean isPermitted( int maskToCheck, int permissionToCheck ) { return ( ( maskToCheck & permissionToCheck ) == permissionToCheck ); } private String printBinary( int i ) { String s = Integer.toString( i, 2 ); String pattern = "................................"; String temp1 = pattern.substring( 0, pattern.length() - s.length() ); String temp2 = temp1 + s; return temp2.replace( '0', '.' ); } /** * Outputs the permissions in a human-friendly format. For example, this method may return "CR-D" to * indicate the passed integer permits create, permits read, does not permit update, and permits delete. * * @param i the integer containing the mask which should be printed * * @return the human-friend formatted block */ public abstract String printPermissionsBlock( int i ); /** * Outputs the permissions in human-friendly format for the current <code>AbstractBasicAclEntry</code>'s * mask. * * @return the human-friendly formatted block for this instance */ public String printPermissionsBlock() { return printPermissionsBlock( this.mask ); } public void setAclObjectIdentity( IPentahoAclObjectIdentity aclObjectIdentity ) { this.aclObjectIdentity = aclObjectIdentity; } public void setAclObjectParentIdentity( IPentahoAclObjectIdentity aclObjectParentIdentity ) { this.aclObjectParentIdentity = aclObjectParentIdentity; } public void setMask( int mask ) { this.mask = mask; } public void setRecipient( Object recipient ) { this.recipient = recipient; } public String toString() { StringBuffer sb = new StringBuffer(); sb.append( getClass().getName() ); sb.append( "[" ).append( aclObjectIdentity ).append( "," ).append( recipient ); sb.append( "=" ).append( printPermissionsBlock( mask ) ).append( " " ); sb.append( printBinary( mask ) ).append( " (" ); sb.append( mask ).append( ")" ).append( "]" ); return sb.toString(); } public int togglePermission( int permissionToToggle ) { this.mask ^= permissionToToggle; if ( Arrays.binarySearch( validPermissions, this.mask ) < 0 ) { throw new IllegalArgumentException( "Resulting permission set will be invalid." ); } else { return this.mask; } } }