/*
* 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 - 2008 Pentaho Corporation. All rights reserved.
*
* Created Jan 19, 2006
* @author mbatchel
*/
package org.pentaho.platform.engine.security.acls;
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;
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.GrantedAuthority;
import org.springframework.security.GrantedAuthorityImpl;
import org.springframework.security.acl.basic.AbstractBasicAclEntry;
/**
* 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 AbstractBasicAclEntry 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) {
} catch (IllegalAccessException e) {
}
// 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 GrantedAuthorityImpl(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();
}
}