/*
* 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.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.pentaho.platform.api.engine.IPentahoAclEntry;
import org.pentaho.platform.engine.security.messages.Messages;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* Base Pentaho Access Control entry. Subclassed <tt>AbstractBasicAclEntry</tt> from Spring Security project.
* Provides known access controls.
*
* @author mbatchel
*/
@SuppressWarnings( "deprecation" )
public class PentahoAclEntry extends PentahoAbstractBasicAclEntry implements IPentahoAclEntry {
private static final Log logger = LogFactory.getLog( PentahoAclEntry.class );
/**
* Populated lazily in getValidPermissions().
*/
private static int[] validPermissions;
private static final long serialVersionUID = -1123574274303339402L;
private static final int RECIPIENT_STRING = 0;
private static final int RECIPIENT_GRANTEDAUTHORITY = 1;
private static final Map validPermissionsNameMap = new HashMap();
public int recipientType = PentahoAclEntry.RECIPIENT_STRING;
// Prevent breakage downstream by inadvertant or malicious modifications to validPermissions array
private int[] lazyPermissionClone;
static {
Map solutionPermissionsMap = new HashMap();
Map allPermissionsMap = new HashMap();
PentahoAclEntry.validPermissionsNameMap.put( PentahoAclEntry.PERMISSIONS_LIST_SOLUTIONS, Collections
.unmodifiableMap( solutionPermissionsMap ) );
PentahoAclEntry.validPermissionsNameMap.put( PentahoAclEntry.PERMISSIONS_LIST_ALL, Collections
.unmodifiableMap( allPermissionsMap ) );
// TODO gmoran Are two lists really necessary any more?
// TODO mlowery Why does PentahoAclEntry know about what permissions are valid for solutions?
solutionPermissionsMap
.put(
Messages.getInstance().getString( "PentahoAclEntry.USER_ADMINISTER" ), new Integer( PentahoAclEntry.PERM_FULL_CONTROL ) ); //$NON-NLS-1$
solutionPermissionsMap
.put(
Messages.getInstance().getString( "PentahoAclEntry.USER_MANAGE_PERMS" ), new Integer( PentahoAclEntry.PERM_UPDATE_PERMS ) ); //$NON-NLS-1$
solutionPermissionsMap.put(
Messages.getInstance().getString( "PentahoAclEntry.USER_UPDATE" ), new Integer( PentahoAclEntry.PERM_UPDATE ) ); //$NON-NLS-1$
solutionPermissionsMap.put(
Messages.getInstance().getString( "PentahoAclEntry.USER_CREATE" ), new Integer( PentahoAclEntry.PERM_CREATE ) ); //$NON-NLS-1$
solutionPermissionsMap.put(
Messages.getInstance().getString( "PentahoAclEntry.USER_DELETE" ), new Integer( PentahoAclEntry.PERM_DELETE ) ); //$NON-NLS-1$
solutionPermissionsMap
.put(
Messages.getInstance().getString( "PentahoAclEntry.USER_EXECUTE" ), new Integer( PentahoAclEntry.PERM_EXECUTE ) ); //$NON-NLS-1$
solutionPermissionsMap
.put(
Messages.getInstance().getString( "PentahoAclEntry.USER_SUBSCRIBE" ), new Integer( PentahoAclEntry.PERM_SUBSCRIBE ) ); //$NON-NLS-1$
allPermissionsMap.put( Messages.getInstance().getString( "PentahoAclEntry.USER_NONE" ), new Integer( 0 ) ); //$NON-NLS-1$
allPermissionsMap
.put(
Messages.getInstance().getString( "PentahoAclEntry.USER_EXECUTE" ), new Integer( PentahoAclEntry.PERM_EXECUTE ) ); //$NON-NLS-1$
allPermissionsMap
.put(
Messages.getInstance().getString( "PentahoAclEntry.USER_SUBSCRIBE" ), new Integer( PentahoAclEntry.PERM_SUBSCRIBE ) ); //$NON-NLS-1$
allPermissionsMap.put(
Messages.getInstance().getString( "PentahoAclEntry.USER_CREATE" ), new Integer( PentahoAclEntry.PERM_CREATE ) ); //$NON-NLS-1$
allPermissionsMap.put(
Messages.getInstance().getString( "PentahoAclEntry.USER_UPDATE" ), new Integer( PentahoAclEntry.PERM_UPDATE ) ); //$NON-NLS-1$
allPermissionsMap.put(
Messages.getInstance().getString( "PentahoAclEntry.USER_DELETE" ), new Integer( PentahoAclEntry.PERM_DELETE ) ); //$NON-NLS-1$
allPermissionsMap
.put(
Messages.getInstance().getString( "PentahoAclEntry.USER_ALL" ), new Integer( PentahoAclEntry.PERM_FULL_CONTROL ) ); //$NON-NLS-1$
initializePermissionsArray();
}
private static void initializePermissionsArray() {
if ( null == PentahoAclEntry.validPermissions ) {
int maxPower = -1;
Field[] fields = IPentahoAclEntry.class.getDeclaredFields();
for ( Field field : fields ) {
// if field is public static final int
if ( int.class == field.getType() && Modifier.isPublic( field.getModifiers() )
&& Modifier.isStatic( field.getModifiers() ) && Modifier.isFinal( field.getModifiers() )
&& field.getName().startsWith( PERMISSION_PREFIX ) ) {
if ( PentahoAclEntry.logger.isDebugEnabled() ) {
PentahoAclEntry.logger.debug( "Candidate field: " + field.getName() ); //$NON-NLS-1$
}
// power of two (0-based)
double powerOfTwo = -1;
try {
powerOfTwo = Math.log( field.getInt( null ) ) / Math.log( 2 );
} catch ( IllegalArgumentException e ) {
//ignore
} catch ( IllegalAccessException e ) {
//ignore
}
// if log calculation results in an integer
if ( powerOfTwo == (int) powerOfTwo ) {
if ( powerOfTwo > maxPower ) {
if ( PentahoAclEntry.logger.isDebugEnabled() ) {
PentahoAclEntry.logger.debug( "Found new power of two." ); //$NON-NLS-1$
}
maxPower = (int) powerOfTwo;
}
}
}
}
if ( PentahoAclEntry.logger.isDebugEnabled() ) {
PentahoAclEntry.logger.debug( "Max power of two: " + maxPower ); //$NON-NLS-1$
}
int numberOfPermutations = (int) Math.pow( 2, maxPower + 1 );
PentahoAclEntry.validPermissions = new int[numberOfPermutations + 1];
for ( int i = 0; i < numberOfPermutations; i++ ) {
PentahoAclEntry.validPermissions[i] = i;
}
PentahoAclEntry.validPermissions[PentahoAclEntry.validPermissions.length - 1] = PentahoAclEntry.PERM_FULL_CONTROL;
}
}
public PentahoAclEntry() {
super();
}
public PentahoAclEntry( final Object recipient, final int mask ) {
this();
setRecipient( recipient );
setMask( mask );
}
protected void setRecipientType( final int value ) {
this.recipientType = value;
}
protected int getRecipientType() {
return this.recipientType;
}
protected void setRecipientString( final String value ) {
if ( this.recipientType == PentahoAclEntry.RECIPIENT_GRANTEDAUTHORITY ) {
this.setRecipient( new SimpleGrantedAuthority( value ) );
} else {
this.setRecipient( value );
}
}
protected String getRecipientString() {
return this.getRecipient().toString();
}
@Override
public void setRecipient( final Object value ) {
super.setRecipient( value );
if ( value instanceof GrantedAuthority ) {
this.setRecipientType( PentahoAclEntry.RECIPIENT_GRANTEDAUTHORITY );
} else {
this.setRecipientType( PentahoAclEntry.RECIPIENT_STRING );
}
}
/**
* As implemented, this method says that all permission combinations are valid. (Well not all. FULL_CONTROL must
* stand alone. It cannot be combined with other bits.)
*
* <ol>
* <li>Find the permission value (call it p) that is the highest power of two.</li>
* <li>Find n (0-based) such that 2^n = p. (Uses logarithm with base 2.)</li>
* <li>So there are 2^(n+1) permutations of permission bits.</li>
* <li>So the valid permission values list consists of those 2^(n+1) permutations plus the FULL_CONTROL perm bit.
* (i.e. (2^(n+1))+1</li>
* </ol>
*/
@Override
public int[] getValidPermissions() {
if ( lazyPermissionClone == null ) {
lazyPermissionClone = new int[validPermissions.length];
System.arraycopy( validPermissions, 0, lazyPermissionClone, 0, validPermissions.length );
}
return this.lazyPermissionClone;
}
public static void main( final String[] args ) {
PentahoAclEntry e = new PentahoAclEntry();
System.out.println( Arrays.toString( e.getValidPermissions() ) );
System.out.println( Arrays.toString( e.getValidPermissions() ) );
}
@Override
public String printPermissionsBlock( final int i ) {
StringBuffer sb = new StringBuffer();
if ( isPermitted( i, PentahoAclEntry.PERM_EXECUTE ) ) {
sb.append( 'X' );
} else {
sb.append( '-' );
}
if ( isPermitted( i, PentahoAclEntry.PERM_SUBSCRIBE ) ) {
sb.append( 'S' );
} else {
sb.append( '-' );
}
if ( isPermitted( i, PentahoAclEntry.PERM_CREATE ) ) {
sb.append( 'C' );
} else {
sb.append( '-' );
}
if ( isPermitted( i, PentahoAclEntry.PERM_UPDATE ) ) {
sb.append( 'U' );
} else {
sb.append( '-' );
}
if ( isPermitted( i, PentahoAclEntry.PERM_DELETE ) ) {
sb.append( 'D' );
} else {
sb.append( '-' );
}
if ( isPermitted( i, PentahoAclEntry.PERM_UPDATE_PERMS ) ) {
sb.append( 'P' );
} else {
sb.append( '-' );
}
return sb.toString();
}
/**
* @return Returns the validPermissionsNameMap. This method is generally useful for UI work as it returns a Map
* of Permission atomic values (as Integer objects) keyed by a human readable permission name.
*/
public static Map getValidPermissionsNameMap() {
return PentahoAclEntry.getValidPermissionsNameMap( PentahoAclEntry.PERMISSIONS_LIST_SOLUTIONS );
}
/**
* @return Returns the validPermissionsNameMap. This method is generally useful for UI work as it returns a Map
* of Permission atomic values (as Integer objects) keyed by a human readable permission name.
* @param permissionsListType
* - The permissions list for solutions is different than that for other UIs
*/
public static Map getValidPermissionsNameMap( final String permissionsListType ) {
return (Map) PentahoAclEntry.validPermissionsNameMap.get( permissionsListType );
}
public boolean equals( final Object obj ) {
if ( obj instanceof PentahoAclEntry == false ) {
return false;
}
if ( this == obj ) {
return true;
}
//
// MB - If the instanceof above compares to anything other than
// this object, then the static comparison below of getValidPermissions() should
// be re-evaluated.
//
PentahoAclEntry rhs = (PentahoAclEntry) obj;
return new EqualsBuilder().append( getRecipient(), rhs.getRecipient() ).append( getRecipientType(),
rhs.getRecipientType() ).append( getAclObjectIdentity(), rhs.getAclObjectIdentity() ).append(
getAclObjectParentIdentity(), rhs.getAclObjectParentIdentity() )
// .append(getValidPermissions(),rhs.getValidPermissions())
.append( getMask(), rhs.getMask() ).isEquals();
}
public int hashCode() {
return new HashCodeBuilder( 79, 211 ).append( getRecipient() ).append( getRecipientType() ).append(
getAclObjectIdentity() ).append( getAclObjectParentIdentity() )
// MB - Commented out because it's not relevant
// .append(getValidPermissions())
.append( getMask() ).toHashCode();
}
}