mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-31 17:39:05 +00:00
RM-1231: Could not Revert the rejected record
* record security was not being correctly reset during record reject * added additional logging to help debug access denied exceptions git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/HEAD@73455 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -31,6 +31,8 @@ import net.sf.acegisecurity.vote.AccessDecisionVoter;
|
||||
|
||||
import org.alfresco.module.org_alfresco_module_rm.capability.policy.ConfigAttributeDefinition;
|
||||
import org.alfresco.module.org_alfresco_module_rm.capability.policy.Policy;
|
||||
import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
|
||||
import org.alfresco.module.org_alfresco_module_rm.security.RMMethodSecurityInterceptor;
|
||||
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
||||
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
|
||||
import org.alfresco.repo.transaction.TransactionalResourceHelper;
|
||||
@@ -124,6 +126,9 @@ public class RMEntryVoter extends RMSecurityCommon
|
||||
@SuppressWarnings("rawtypes")
|
||||
public int vote(Authentication authentication, Object object, net.sf.acegisecurity.ConfigAttributeDefinition config)
|
||||
{
|
||||
// logging
|
||||
RMMethodSecurityInterceptor.isRMSecurityChecked(true);
|
||||
|
||||
MethodInvocation mi = (MethodInvocation)object;
|
||||
|
||||
if (TransactionalResourceHelper.isResourcePresent("voting"))
|
||||
@@ -175,6 +180,9 @@ public class RMEntryVoter extends RMSecurityCommon
|
||||
// Whatever is found first takes precedence
|
||||
if (cad.getTypeString().equals(ConfigAttributeDefinition.RM_DENY))
|
||||
{
|
||||
// log message
|
||||
RMMethodSecurityInterceptor.addMessage("RM_DENY: check that a security policy has been set for this method");
|
||||
|
||||
return AccessDecisionVoter.ACCESS_DENIED;
|
||||
}
|
||||
else if (cad.getTypeString().equals(ConfigAttributeDefinition.RM_ABSTAIN))
|
||||
@@ -235,6 +243,9 @@ public class RMEntryVoter extends RMSecurityCommon
|
||||
{
|
||||
case AccessDecisionVoter.ACCESS_DENIED:
|
||||
{
|
||||
// log message
|
||||
RMMethodSecurityInterceptor.addMessage("Policy " + cad.getPolicyName() + " denied.");
|
||||
|
||||
return AccessDecisionVoter.ACCESS_DENIED;
|
||||
}
|
||||
case AccessDecisionVoter.ACCESS_ABSTAIN:
|
||||
|
@@ -23,6 +23,7 @@ import net.sf.acegisecurity.vote.AccessDecisionVoter;
|
||||
import org.alfresco.module.org_alfresco_module_rm.caveat.RMCaveatConfigComponent;
|
||||
import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService;
|
||||
import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
|
||||
import org.alfresco.module.org_alfresco_module_rm.security.RMMethodSecurityInterceptor;
|
||||
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
||||
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
|
||||
import org.alfresco.service.cmr.repository.AssociationRef;
|
||||
@@ -219,15 +220,22 @@ public class RMSecurityCommon implements ApplicationContextAware
|
||||
|
||||
if (permissionService.hasPermission(nodeRef, RMPermissionModel.READ_RECORDS) == AccessStatus.DENIED)
|
||||
{
|
||||
// log message
|
||||
RMMethodSecurityInterceptor.addMessage("User does not have read record permission on node, access denied. (nodeRef={0}, user={1})", nodeRef, AuthenticationUtil.getRunAsUser());
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("\t\tUser does not have read record permission on node, access denied. (nodeRef=" + nodeRef.toString() + ", user=" + AuthenticationUtil.getRunAsUser() + ")");
|
||||
}
|
||||
|
||||
return setTransactionCache("checkRmRead", nodeRef, AccessDecisionVoter.ACCESS_DENIED);
|
||||
}
|
||||
|
||||
if (permissionService.hasPermission(filePlan, RMPermissionModel.VIEW_RECORDS) == AccessStatus.DENIED)
|
||||
{
|
||||
// log capability details
|
||||
RMMethodSecurityInterceptor.reportCapabilityStatus(RMPermissionModel.VIEW_RECORDS, AccessDecisionVoter.ACCESS_DENIED);
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("\t\tUser does not have view records capability permission on node, access denied. (filePlan=" + filePlan.toString() + ", user=" + AuthenticationUtil.getRunAsUser() + ")");
|
||||
|
@@ -23,7 +23,9 @@ import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
|
||||
/**
|
||||
* @author Roy Wethearll
|
||||
* Read method security policy.
|
||||
*
|
||||
* @author Roy Wetherall
|
||||
* @since 2.1
|
||||
*/
|
||||
public class ReadPolicy extends AbstractBasePolicy
|
||||
@@ -35,7 +37,7 @@ public class ReadPolicy extends AbstractBasePolicy
|
||||
Class[] params,
|
||||
ConfigAttributeDefinition cad)
|
||||
{
|
||||
NodeRef testNodeRef = getTestNode(invocation, params, cad.getParameters().get(0), cad.isParent());
|
||||
NodeRef testNodeRef = getTestNode(invocation, params, cad.getParameters().get(0), cad.isParent());
|
||||
return capabilityService.getCapability(RMPermissionModel.VIEW_RECORDS).evaluate(testNodeRef);
|
||||
}
|
||||
}
|
@@ -728,13 +728,11 @@ public class RecordServiceImpl extends BaseBehaviourBean
|
||||
if (extendedPermissionService.hasPermission(nodeRef, PermissionService.WRITE) != AccessStatus.ALLOWED)
|
||||
{
|
||||
throw new AccessDeniedException("Can not create record from document, because the user " +
|
||||
AuthenticationUtil.getFullyAuthenticatedUser() +
|
||||
AuthenticationUtil.getRunAsUser() +
|
||||
" does not have Write permissions on the doucment " +
|
||||
nodeRef.toString());
|
||||
}
|
||||
|
||||
// Save the id of the currently logged in user
|
||||
final String userId = AuthenticationUtil.getRunAsUser();
|
||||
|
||||
// do the work of creating the record as the system user
|
||||
AuthenticationUtil.runAsSystem(new RunAsWork<Void>()
|
||||
@@ -775,10 +773,10 @@ public class RecordServiceImpl extends BaseBehaviourBean
|
||||
// save the information about the originating details
|
||||
Map<QName, Serializable> aspectProperties = new HashMap<QName, Serializable>(3);
|
||||
aspectProperties.put(PROP_RECORD_ORIGINATING_LOCATION, parentAssoc.getParentRef());
|
||||
aspectProperties.put(PROP_RECORD_ORIGINATING_USER_ID, userId);
|
||||
aspectProperties.put(PROP_RECORD_ORIGINATING_USER_ID, owner);
|
||||
aspectProperties.put(PROP_RECORD_ORIGINATING_CREATION_DATE, new Date());
|
||||
nodeService.addAspect(nodeRef, ASPECT_RECORD_ORIGINATING_DETAILS, aspectProperties);
|
||||
|
||||
|
||||
// make the document a record
|
||||
makeRecord(nodeRef);
|
||||
|
||||
@@ -1110,6 +1108,12 @@ public class RecordServiceImpl extends BaseBehaviourBean
|
||||
throw new AlfrescoRuntimeException("Unable to find the creator of document.");
|
||||
}
|
||||
ownableService.setOwner(nodeRef, documentOwner);
|
||||
|
||||
// clear the existing permissions
|
||||
permissionService.clearPermission(nodeRef, null);
|
||||
|
||||
// restore permission inheritance
|
||||
permissionService.setInheritParentPermissions(nodeRef, true);
|
||||
|
||||
// send an email to the record creator
|
||||
notificationHelper.recordRejectedEmailNotification(nodeRef, recordId, documentOwner);
|
||||
|
@@ -1,15 +1,18 @@
|
||||
|
||||
package org.alfresco.module.org_alfresco_module_rm.security;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import net.sf.acegisecurity.AccessDeniedException;
|
||||
import net.sf.acegisecurity.intercept.InterceptorStatusToken;
|
||||
import net.sf.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor;
|
||||
import net.sf.acegisecurity.vote.AccessDecisionVoter;
|
||||
|
||||
import org.alfresco.repo.security.permissions.impl.acegi.MethodSecurityInterceptor;
|
||||
import org.alfresco.service.cmr.security.AccessStatus;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.apache.commons.logging.Log;
|
||||
@@ -63,7 +66,7 @@ public class RMMethodSecurityInterceptor extends MethodSecurityInterceptor
|
||||
/**
|
||||
* Current capability report details.
|
||||
* <p>
|
||||
* Used to getnerate the capability error report.
|
||||
* Used to generate the capability error report.
|
||||
*/
|
||||
private static final ThreadLocal<Map<String, CapabilityReport>> CAPABILITIES = new ThreadLocal<Map<String, CapabilityReport>>()
|
||||
{
|
||||
@@ -74,6 +77,22 @@ public class RMMethodSecurityInterceptor extends MethodSecurityInterceptor
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Indicates whether this is an RM security check or not
|
||||
*/
|
||||
private static final ThreadLocal<Boolean> IS_RM_SECURITY_CHECK = new ThreadLocal<Boolean>()
|
||||
{
|
||||
protected Boolean initialValue() {return false;};
|
||||
};
|
||||
|
||||
/**
|
||||
* Messages to display in error report.
|
||||
*/
|
||||
private static final ThreadLocal<List<String>> MESSAGES = new ThreadLocal<List<String>>()
|
||||
{
|
||||
protected List<String> initialValue() {return new ArrayList<String>();};
|
||||
};
|
||||
|
||||
/**
|
||||
* Get capability report object from the thread local, creating one for
|
||||
* the given capability name if one does not already exist.
|
||||
@@ -95,6 +114,41 @@ public class RMMethodSecurityInterceptor extends MethodSecurityInterceptor
|
||||
return capability;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this is a RM security check or not
|
||||
*
|
||||
* @param newValue true if RM security check, false otherwise
|
||||
*/
|
||||
public static void isRMSecurityChecked(boolean newValue)
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
RMMethodSecurityInterceptor.IS_RM_SECURITY_CHECK.set(newValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a message to be displayed in the error report.
|
||||
*
|
||||
* @param message error message
|
||||
*/
|
||||
public static void addMessage(String message)
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
List<String> messages = RMMethodSecurityInterceptor.MESSAGES.get();
|
||||
messages.add(message);
|
||||
}
|
||||
}
|
||||
|
||||
public static void addMessage(String message, Object ... params)
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
addMessage(MessageFormat.format(message, params));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Report capability status.
|
||||
*
|
||||
@@ -186,20 +240,73 @@ public class RMMethodSecurityInterceptor extends MethodSecurityInterceptor
|
||||
{
|
||||
// clear the capability report information
|
||||
RMMethodSecurityInterceptor.CAPABILITIES.remove();
|
||||
RMMethodSecurityInterceptor.IS_RM_SECURITY_CHECK.remove();
|
||||
RMMethodSecurityInterceptor.MESSAGES.remove();
|
||||
|
||||
// before invocation (where method security check takes place)
|
||||
result = super.beforeInvocation(object);
|
||||
}
|
||||
catch (AccessDeniedException exception)
|
||||
{
|
||||
String failureReport = getFailureReport();
|
||||
if (failureReport == null)
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
throw exception;
|
||||
MethodInvocation mi = (MethodInvocation)object;
|
||||
|
||||
StringBuilder methodDetails = new StringBuilder("\n");
|
||||
if (RMMethodSecurityInterceptor.IS_RM_SECURITY_CHECK.get())
|
||||
{
|
||||
methodDetails.append("RM method security check was performed.\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
methodDetails.append("Standard DM method security check was performed.\n");
|
||||
}
|
||||
|
||||
boolean first = true;
|
||||
methodDetails.append("Failed on method: ").append(mi.getMethod().getName()).append("(");
|
||||
for (Object arg : mi.getArguments())
|
||||
{
|
||||
if (first)
|
||||
{
|
||||
first = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
methodDetails.append(", ");
|
||||
}
|
||||
|
||||
if (arg != null)
|
||||
{
|
||||
methodDetails.append(arg.toString());
|
||||
}
|
||||
else
|
||||
{
|
||||
methodDetails.append("null");
|
||||
}
|
||||
}
|
||||
methodDetails.append(")\n");
|
||||
|
||||
List<String> messages = RMMethodSecurityInterceptor.MESSAGES.get();
|
||||
for (String message : messages)
|
||||
{
|
||||
methodDetails.append(message).append("\n");
|
||||
}
|
||||
|
||||
String failureReport = getFailureReport();
|
||||
if (failureReport == null)
|
||||
{
|
||||
// rethrow with additional information
|
||||
throw new AccessDeniedException(exception.getMessage() + methodDetails, exception);
|
||||
}
|
||||
else
|
||||
{
|
||||
// rethrow with additional information
|
||||
throw new AccessDeniedException(exception.getMessage() + methodDetails + getFailureReport(), exception);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// rethrow with additional information
|
||||
throw new AccessDeniedException(exception.getMessage() + getFailureReport(), exception);
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
Reference in New Issue
Block a user