Merge MNT-17512_AuditPermissionChanges into release/V2.4.

This commit is contained in:
Tom Page
2017-02-23 17:00:35 +00:00
7 changed files with 298 additions and 94 deletions

View File

@@ -120,6 +120,7 @@
<property name="configuredFilePermissions"> <property name="configuredFilePermissions">
<value>${rm.haspermissionmap.write}</value> <value>${rm.haspermissionmap.write}</value>
</property> </property>
<property name="recordsManagementAuditService" ref="RecordsManagementAuditService" />
</bean> </bean>
<bean class="org.alfresco.util.BeanExtender"> <bean class="org.alfresco.util.BeanExtender">
<property name="beanName" value="permissionServiceImpl"/> <property name="beanName" value="permissionServiceImpl"/>

View File

@@ -14,4 +14,7 @@ rm.audit.audit-clear=Audit Clear
rm.audit.audit-view=Audit View rm.audit.audit-view=Audit View
rm.audit.trail-file-fail=Can't generate audit report. rm.audit.trail-file-fail=Can't generate audit report.
rm.audit.audit-report=Audit Report rm.audit.audit-report=Audit Report
rm.audit.set-permission=Set Permission
rm.audit.enable-inherit-permission=Enable Inherit Permissions
rm.audit.disable-inherit-permission=Disable Inherit Permissions
recordable-version-config=Auto-Declare Options recordable-version-config=Auto-Declare Options

View File

@@ -448,6 +448,7 @@
<property name="authorityService" ref="AuthorityService" /> <property name="authorityService" ref="AuthorityService" />
<property name="filePlanRoleService" ref="FilePlanRoleService" /> <property name="filePlanRoleService" ref="FilePlanRoleService" />
<property name="filePlanService" ref="FilePlanService" /> <property name="filePlanService" ref="FilePlanService" />
<property name="recordsManagementAuditService" ref="RecordsManagementAuditService" />
</bean> </bean>
<bean id="FilePlanPermissionService" class="org.springframework.aop.framework.ProxyFactoryBean"> <bean id="FilePlanPermissionService" class="org.springframework.aop.framework.ProxyFactoryBean">

View File

@@ -39,18 +39,18 @@ import org.alfresco.service.namespace.QName;
/** /**
* Records management audit service. * Records management audit service.
* *
* @author Gavin Cornwell * @author Gavin Cornwell
*/ */
public interface RecordsManagementAuditService extends RecordsManagementAuditServiceDeprecated public interface RecordsManagementAuditService extends RecordsManagementAuditServiceDeprecated
{ {
public enum ReportFormat { HTML, JSON } public enum ReportFormat { HTML, JSON }
/** /**
* Retrieves a list of audit events. * Retrieves a list of audit events.
* *
* @return List of audit events * @return List of audit events
*/ */
List<AuditEvent> getAuditEvents(); List<AuditEvent> getAuditEvents();
@@ -60,49 +60,49 @@ public interface RecordsManagementAuditService extends RecordsManagementAuditSer
* <p> * <p>
* Creates an instance of a simple audit event and registers it with * Creates an instance of a simple audit event and registers it with
* the service. * the service.
* *
* @param name name of audit event * @param name name of audit event
* @param label display label of audit event * @param label display label of audit event
*/ */
void registerAuditEvent(String name, String label); void registerAuditEvent(String name, String label);
/** /**
* Register audit event. * Register audit event.
* *
* @param auditEvent audit event * @param auditEvent audit event
*/ */
void registerAuditEvent(AuditEvent auditEvent); void registerAuditEvent(AuditEvent auditEvent);
/** /**
* Audits an event, assumes no properties where modified and that the event should not be audited * Audits an event, assumes no properties where modified and that the event should not be audited
* immediately. * immediately.
* *
* @param nodeRef node reference * @param nodeRef node reference
* @param eventName event name * @param eventName event name
*/ */
void auditEvent(NodeRef nodeRef, void auditEvent(NodeRef nodeRef,
String eventName); String eventName);
/** /**
* Audits an event, assumes that the event should not be audited immediately and not be removed if no property is changed. * Audits an event, assumes that the event should not be audited immediately and not be removed if no property is changed.
* *
* @param nodeRef node reference * @param nodeRef node reference
* @param eventName event name * @param eventName event name
* @param before property values before event * @param before property values before event (this must be modifiable and may be changed by the method).
* @param after property values after event * @param after property values after event (this must be modifiable and may be changed by the method).
*/ */
void auditEvent(NodeRef nodeRef, void auditEvent(NodeRef nodeRef,
String eventName, String eventName,
Map<QName, Serializable> before, Map<QName, Serializable> before,
Map<QName, Serializable> after); Map<QName, Serializable> after);
/** /**
* Audit event, assumes not to be removed if no property is changed. * Audit event, assumes not to be removed if no property is changed.
* *
* @param nodeRef node reference * @param nodeRef node reference
* @param eventName event name * @param eventName event name
* @param before property values before event * @param before property values before event (this must be modifiable and may be changed by the method).
* @param after property values after event * @param after property values after event (this must be modifiable and may be changed by the method).
* @param immediate true if event is to be audited immediately, false otherwise * @param immediate true if event is to be audited immediately, false otherwise
*/ */
void auditEvent(NodeRef nodeRef, void auditEvent(NodeRef nodeRef,
@@ -110,14 +110,14 @@ public interface RecordsManagementAuditService extends RecordsManagementAuditSer
Map<QName, Serializable> before, Map<QName, Serializable> before,
Map<QName, Serializable> after, Map<QName, Serializable> after,
boolean immediate); boolean immediate);
/** /**
* Audit event. * Audit event.
* *
* @param nodeRef node reference * @param nodeRef node reference
* @param eventName event name * @param eventName event name
* @param before property values before event * @param before property values before event (this must be modifiable and may be changed by the method).
* @param after property values after event * @param after property values after event (this must be modifiable and may be changed by the method).
* @param immediate true if event is to be audited immediately, false otherwise * @param immediate true if event is to be audited immediately, false otherwise
* @param removeIfNoPropertyChanged true if event is not audited when there are no properties changed, false otherwise * @param removeIfNoPropertyChanged true if event is not audited when there are no properties changed, false otherwise
*/ */
@@ -127,52 +127,67 @@ public interface RecordsManagementAuditService extends RecordsManagementAuditSer
Map<QName, Serializable> after, Map<QName, Serializable> after,
boolean immediate, boolean immediate,
boolean removeIfNoPropertyChanged); boolean removeIfNoPropertyChanged);
/**
* Supply incremental changes as part of an event. This will either create a new event or update the existing details to put any supplied properties into the map.
*
* @param nodeRef node reference
* @param eventName event name
* @param before additional property values before event (this must be modifiable and may be changed by the method).
* @param after additional property values after event (this must be modifiable and may be changed by the method).
* @param removeIfNoPropertyChanged true if event is not audited when there are no properties changed, false otherwise
*/
void auditOrUpdateEvent(NodeRef nodeRef,
String eventName,
Map<QName, Serializable> before,
Map<QName, Serializable> after,
boolean removeIfNoPropertyChanged);
/** /**
* Determines whether the RM audit log is currently enabled. * Determines whether the RM audit log is currently enabled.
* *
* @param filePlan file plan * @param filePlan file plan
* @return true if RM auditing is active false otherwise * @return true if RM auditing is active false otherwise
*/ */
boolean isAuditLogEnabled(NodeRef filePlan); boolean isAuditLogEnabled(NodeRef filePlan);
/** /**
* Start RM auditing. * Start RM auditing.
* *
* @param filePlan file plan * @param filePlan file plan
*/ */
void startAuditLog(NodeRef filePlan); void startAuditLog(NodeRef filePlan);
/** /**
* Stop RM auditing. * Stop RM auditing.
* *
* @param filePlan file plan * @param filePlan file plan
*/ */
void stopAuditLog(NodeRef filePlan); void stopAuditLog(NodeRef filePlan);
/** /**
* Clears the RM audit. * Clears the RM audit.
* *
* @param filePlan file plan * @param filePlan file plan
*/ */
void clearAuditLog(NodeRef filePlan); void clearAuditLog(NodeRef filePlan);
/** /**
* Returns the date the RM audit was last started. * Returns the date the RM audit was last started.
* *
* @param filePlan file plan * @param filePlan file plan
* @return Date the audit was last started * @return Date the audit was last started
*/ */
Date getDateAuditLogLastStarted(NodeRef filePlan); Date getDateAuditLogLastStarted(NodeRef filePlan);
/** /**
* Returns the date the RM audit was last stopped. * Returns the date the RM audit was last stopped.
* *
* @return Date the audit was last stopped * @return Date the audit was last stopped
*/ */
Date getDateAuditLogLastStopped(NodeRef filePlan); Date getDateAuditLogLastStopped(NodeRef filePlan);
/** /**
* Retrieves a list of audit log entries using the provided parameters * Retrieves a list of audit log entries using the provided parameters
* represented by the RecordsManagementAuditQueryParameters instance. * represented by the RecordsManagementAuditQueryParameters instance.
@@ -181,13 +196,13 @@ public interface RecordsManagementAuditService extends RecordsManagementAuditSer
* object will result in ALL audit log entries for the RM system being * object will result in ALL audit log entries for the RM system being
* returned. Setting the various parameters effectively filters the full * returned. Setting the various parameters effectively filters the full
* audit trail. * audit trail.
* *
* @param params Parameters to use to retrieve audit trail (never <tt>null</tt>) * @param params Parameters to use to retrieve audit trail (never <tt>null</tt>)
* @param format The format the report should be produced in * @param format The format the report should be produced in
* @return File containing JSON representation of audit trail * @return File containing JSON representation of audit trail
*/ */
File getAuditTrailFile(RecordsManagementAuditQueryParameters params, ReportFormat format); File getAuditTrailFile(RecordsManagementAuditQueryParameters params, ReportFormat format);
/** /**
* Retrieves a list of audit log entries using the provided parameters * Retrieves a list of audit log entries using the provided parameters
* represented by the RecordsManagementAuditQueryParameters instance. * represented by the RecordsManagementAuditQueryParameters instance.
@@ -196,23 +211,23 @@ public interface RecordsManagementAuditService extends RecordsManagementAuditSer
* object will result in ALL audit log entries for the RM system being * object will result in ALL audit log entries for the RM system being
* returned. Setting the various parameters effectively filters the full * returned. Setting the various parameters effectively filters the full
* audit trail. * audit trail.
* *
* @param params Parameters to use to retrieve audit trail (never <tt>null</tt>) * @param params Parameters to use to retrieve audit trail (never <tt>null</tt>)
* @return All entries for the audit trail * @return All entries for the audit trail
*/ */
List<RecordsManagementAuditEntry> getAuditTrail(RecordsManagementAuditQueryParameters params); List<RecordsManagementAuditEntry> getAuditTrail(RecordsManagementAuditQueryParameters params);
/** /**
* Retrieves a list of audit log entries using the provided parameters * Retrieves a list of audit log entries using the provided parameters
* represented by the RecordsManagementAuditQueryParameters instance and * represented by the RecordsManagementAuditQueryParameters instance and
* then files the resulting log as an undeclared record in the record folder * then files the resulting log as an undeclared record in the record folder
* represented by the given NodeRef. * represented by the given NodeRef.
* <p> * <p>
* The parameters are all optional so an empty RecordsManagementAuditQueryParameters * The parameters are all optional so an empty RecordsManagementAuditQueryParameters
* object will result in ALL audit log entries for the RM system being * object will result in ALL audit log entries for the RM system being
* returned. Setting the various parameters effectively filters the full * returned. Setting the various parameters effectively filters the full
* audit trail. * audit trail.
* *
* @param params Parameters to use to retrieve audit trail (never <tt>null</tt>) * @param params Parameters to use to retrieve audit trail (never <tt>null</tt>)
* @param destination NodeRef representing a record folder in which to file the audit log * @param destination NodeRef representing a record folder in which to file the audit log
* @param format The format the report should be produced in * @param format The format the report should be produced in

View File

@@ -110,7 +110,7 @@ public class RecordsManagementAuditServiceImpl extends AbstractLifecycleBean
private static Log logger = LogFactory.getLog(RecordsManagementAuditServiceImpl.class); private static Log logger = LogFactory.getLog(RecordsManagementAuditServiceImpl.class);
private static final String ACCESS_AUDIT_CAPABILITY = "AccessAudit"; private static final String ACCESS_AUDIT_CAPABILITY = "AccessAudit";
private static final String KEY_RM_AUDIT_NODE_RECORDS = "RMAUditNodeRecords"; private static final String KEY_RM_AUDIT_NODE_RECORDS = "RMAUditNodeRecords";
protected static final String RM_AUDIT_EVENT_LOGIN_SUCCESS = "Login.Success"; protected static final String RM_AUDIT_EVENT_LOGIN_SUCCESS = "Login.Success";
@@ -284,7 +284,7 @@ public class RecordsManagementAuditServiceImpl extends AbstractLifecycleBean
{ {
this.namespaceService = namespaceService; this.namespaceService = namespaceService;
} }
/** /**
* @param capabilityService capability service * @param capabilityService capability service
*/ */
@@ -292,9 +292,7 @@ public class RecordsManagementAuditServiceImpl extends AbstractLifecycleBean
{ {
this.capabilityService = capabilityService; this.capabilityService = capabilityService;
} }
/** /**
* @param ignoredAuditProperties * @param ignoredAuditProperties
@@ -514,40 +512,89 @@ public class RecordsManagementAuditServiceImpl extends AbstractLifecycleBean
} }
else else
{ {
Set<RMAuditNode> auditDetails = TransactionalResourceHelper.getSet(KEY_RM_AUDIT_NODE_RECORDS);
AlfrescoTransactionSupport.bindListener(txnListener);
// RM-936: Eliminate multiple audit maps from being generated when events with the same name are required to be fired multiple times in the same transaction. // RM-936: Eliminate multiple audit maps from being generated when events with the same name are required to be fired multiple times in the same transaction.
// Check if auditDetails already contains an auditedNode with the same combination of nodeRef and eventName. // Check if auditDetails already contains an auditedNode with the same combination of nodeRef and eventName.
boolean auditNodeAlreadyExists = false; RMAuditNode existingEventNode = findExistingEventNode(nodeRef, eventName);
for (RMAuditNode existingRMAuditNode : auditDetails) if (existingEventNode != null)
{ {
if (existingRMAuditNode.getNodeRef().equals(nodeRef) && existingRMAuditNode.getEventName().equals(eventName)) // If there exists such an auditNode, update its 'after' properties with the latest set of properties and leave its 'before' properties unchanged so that it
{ // retains the original set of properties. The first 'before' and last 'after' will be diff'ed when comes to building the auditMap later when the transaction
// If there exists such an auditNode, update its 'after' properties with the latest set of properties and leave its 'before' properties unchanged so that it // commits.
// retains the original set of properties. The first 'before' and last 'after' will be diff'ed when comes to building the auditMap later when the transaction existingEventNode.setNodePropertiesAfter(after);
// commits.
existingRMAuditNode.setNodePropertiesAfter(after);
auditNodeAlreadyExists = true;
break;
}
} }
else
if (!auditNodeAlreadyExists)
{ {
// Create a new auditNode if it doesn't already exist createAuditEventInTransaction(nodeRef, eventName, before, after, removeIfNoPropertyChanged);
RMAuditNode auditedNode = new RMAuditNode();
auditedNode.setNodeRef(nodeRef);
auditedNode.setEventName(eventName);
auditedNode.setNodePropertiesBefore(before);
auditedNode.setNodePropertiesAfter(after);
auditedNode.setRemoveIfNoPropertyChanged(removeIfNoPropertyChanged);
auditDetails.add(auditedNode);
} }
} }
} }
/** {@inheritDoc} */
@Override
public void auditOrUpdateEvent(NodeRef nodeRef, String eventName, Map<QName, Serializable> before,
Map<QName, Serializable> after, boolean removeIfNoPropertyChanged)
{
RMAuditNode existingEventNode = findExistingEventNode(nodeRef, eventName);
if (existingEventNode != null)
{
// Update the existing event to include all the new properties.
existingEventNode.getNodePropertiesBefore().putAll(before);
existingEventNode.getNodePropertiesAfter().putAll(after);
}
else
{
createAuditEventInTransaction(nodeRef, eventName, before, after, removeIfNoPropertyChanged);
}
}
/**
* Create a new audit event for this transaction.
*
* @param nodeRef The node the audit message is about.
* @param eventName The event.
* @param before The before property map to use.
* @param after The after property map to use.
* @param removeIfNoPropertyChanged Whether to remove the event if no properties have changed.
*/
private void createAuditEventInTransaction(NodeRef nodeRef, String eventName, Map<QName, Serializable> before,
Map<QName, Serializable> after, boolean removeIfNoPropertyChanged)
{
// Create a new auditNode.
RMAuditNode auditedNode = new RMAuditNode();
auditedNode.setNodeRef(nodeRef);
auditedNode.setEventName(eventName);
auditedNode.setNodePropertiesBefore(before);
auditedNode.setNodePropertiesAfter(after);
auditedNode.setRemoveIfNoPropertyChanged(removeIfNoPropertyChanged);
// Add it to the transaction.
Set<RMAuditNode> auditDetails = TransactionalResourceHelper.getSet(KEY_RM_AUDIT_NODE_RECORDS);
auditDetails.add(auditedNode);
}
/**
* Find an audit node if it already exists for the transaction.
*
* @param nodeRef The node the event is against.
* @param eventName The name of the event.
* @param auditDetails The complete set of events for the transaction.
* @return The pre-existing event node, or null if none exists.
*/
private RMAuditNode findExistingEventNode(NodeRef nodeRef, String eventName)
{
AlfrescoTransactionSupport.bindListener(txnListener);
Set<RMAuditNode> auditDetails = TransactionalResourceHelper.getSet(KEY_RM_AUDIT_NODE_RECORDS);
RMAuditNode existingEventNode = null;
for (RMAuditNode existingRMAuditNode : auditDetails)
{
if (existingRMAuditNode.getNodeRef().equals(nodeRef) && existingRMAuditNode.getEventName().equals(eventName))
{
existingEventNode = existingRMAuditNode;
}
}
return existingEventNode;
}
/** /**
* Helper method to build audit map * Helper method to build audit map
* *
@@ -929,7 +976,7 @@ public class RecordsManagementAuditServiceImpl extends AbstractLifecycleBean
// Skip it // Skip it
return true; return true;
} }
if(nodeRef != null && nodeService.exists(nodeRef) && if(nodeRef != null && nodeService.exists(nodeRef) &&
!AccessStatus.ALLOWED.equals( !AccessStatus.ALLOWED.equals(
capabilityService.getCapabilityAccessState(nodeRef, ACCESS_AUDIT_CAPABILITY))) capabilityService.getCapabilityAccessState(nodeRef, ACCESS_AUDIT_CAPABILITY)))

View File

@@ -27,6 +27,9 @@
package org.alfresco.module.org_alfresco_module_rm.security; package org.alfresco.module.org_alfresco_module_rm.security;
import static java.util.Collections.singletonMap;
import static org.alfresco.module.org_alfresco_module_rm.security.ExtendedReaderDynamicAuthority.EXTENDED_READER;
import static org.alfresco.module.org_alfresco_module_rm.security.ExtendedWriterDynamicAuthority.EXTENDED_WRITER;
import static org.alfresco.repo.policy.Behaviour.NotificationFrequency.TRANSACTION_COMMIT; import static org.alfresco.repo.policy.Behaviour.NotificationFrequency.TRANSACTION_COMMIT;
import static org.alfresco.repo.policy.annotation.BehaviourKind.CLASS; import static org.alfresco.repo.policy.annotation.BehaviourKind.CLASS;
import static org.alfresco.repo.security.authentication.AuthenticationUtil.getSystemUserName; import static org.alfresco.repo.security.authentication.AuthenticationUtil.getSystemUserName;
@@ -34,10 +37,15 @@ import static org.alfresco.service.cmr.security.OwnableService.NO_OWNER;
import static org.alfresco.util.ParameterCheck.mandatory; import static org.alfresco.util.ParameterCheck.mandatory;
import static org.apache.commons.lang.BooleanUtils.isTrue; import static org.apache.commons.lang.BooleanUtils.isTrue;
import java.io.Serializable;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map;
import java.util.Set; import java.util.Set;
import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.module.org_alfresco_module_rm.audit.RecordsManagementAuditService;
import org.alfresco.module.org_alfresco_module_rm.audit.event.AuditEvent;
import org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel; import org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel;
import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService; import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService;
import org.alfresco.module.org_alfresco_module_rm.role.FilePlanRoleService; import org.alfresco.module.org_alfresco_module_rm.role.FilePlanRoleService;
@@ -48,6 +56,7 @@ import org.alfresco.repo.policy.PolicyComponent;
import org.alfresco.repo.policy.annotation.Behaviour; import org.alfresco.repo.policy.annotation.Behaviour;
import org.alfresco.repo.policy.annotation.BehaviourBean; import org.alfresco.repo.policy.annotation.BehaviourBean;
import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.security.AccessPermission; import org.alfresco.service.cmr.security.AccessPermission;
@@ -73,6 +82,12 @@ public class FilePlanPermissionServiceImpl extends ServiceBaseImpl
RMPermissionModel, RMPermissionModel,
NodeServicePolicies.OnMoveNodePolicy NodeServicePolicies.OnMoveNodePolicy
{ {
/** An audit key for the set permission event. */
private static final String AUDIT_SET_PERMISSION = "set-permission";
/** An namespace to use when constructing QNames to use for auditing changes to permissions. */
private static final String AUDIT_NAMESPACE = "audit://permissions/";
/** Permission service */ /** Permission service */
private PermissionService permissionService; private PermissionService permissionService;
@@ -91,6 +106,9 @@ public class FilePlanPermissionServiceImpl extends ServiceBaseImpl
/** File plan service */ /** File plan service */
private FilePlanService filePlanService; private FilePlanService filePlanService;
/** The RM audit service. */
private RecordsManagementAuditService recordsManagementAuditService;
/** Logger */ /** Logger */
private static final Log LOGGER = LogFactory.getLog(FilePlanPermissionServiceImpl.class); private static final Log LOGGER = LogFactory.getLog(FilePlanPermissionServiceImpl.class);
@@ -111,6 +129,16 @@ public class FilePlanPermissionServiceImpl extends ServiceBaseImpl
NodeServicePolicies.OnMoveNodePolicy.QNAME, NodeServicePolicies.OnMoveNodePolicy.QNAME,
TYPE_RECORD_CATEGORY, TYPE_RECORD_CATEGORY,
new JavaBehaviour(this, "onMoveNode", TRANSACTION_COMMIT)); new JavaBehaviour(this, "onMoveNode", TRANSACTION_COMMIT));
AuthenticationUtil.runAsSystem(new RunAsWork<Void>()
{
@Override
public Void doWork() throws Exception
{
recordsManagementAuditService.registerAuditEvent(new AuditEvent(AUDIT_SET_PERMISSION, "rm.audit.set-permission"));
return null;
}
});
} }
/** /**
@@ -227,6 +255,16 @@ public class FilePlanPermissionServiceImpl extends ServiceBaseImpl
this.filePlanService = filePlanService; this.filePlanService = filePlanService;
} }
/**
* Set the RM audit service.
*
* @param recordsManagementAuditService The RM audit service.
*/
public void setRecordsManagementAuditService(RecordsManagementAuditService recordsManagementAuditService)
{
this.recordsManagementAuditService = recordsManagementAuditService;
}
/** /**
* @see org.alfresco.module.org_alfresco_module_rm.security.FilePlanPermissionService#setupRecordCategoryPermissions(org.alfresco.service.cmr.repository.NodeRef) * @see org.alfresco.module.org_alfresco_module_rm.security.FilePlanPermissionService#setupRecordCategoryPermissions(org.alfresco.service.cmr.repository.NodeRef)
*/ */
@@ -340,6 +378,7 @@ public class FilePlanPermissionServiceImpl extends ServiceBaseImpl
final boolean hasUserPermission = authenticationUtil.runAs(new AuthenticationUtil.RunAsWork<Boolean>() final boolean hasUserPermission = authenticationUtil.runAs(new AuthenticationUtil.RunAsWork<Boolean>()
{ {
@Override
public Boolean doWork() public Boolean doWork()
{ {
return getPermissionService().hasPermission(nodeRef, RMPermissionModel.FILING) == AccessStatus.ALLOWED; return getPermissionService().hasPermission(nodeRef, RMPermissionModel.FILING) == AccessStatus.ALLOWED;
@@ -350,6 +389,7 @@ public class FilePlanPermissionServiceImpl extends ServiceBaseImpl
{ {
authenticationUtil.runAsSystem(new AuthenticationUtil.RunAsWork<Void>() authenticationUtil.runAsSystem(new AuthenticationUtil.RunAsWork<Void>()
{ {
@Override
public Void doWork() public Void doWork()
{ {
getPermissionService().setPermission(nodeRef, user, RMPermissionModel.FILING, true); getPermissionService().setPermission(nodeRef, user, RMPermissionModel.FILING, true);
@@ -365,6 +405,7 @@ public class FilePlanPermissionServiceImpl extends ServiceBaseImpl
* @param parent parent node reference * @param parent parent node reference
* @param nodeRef child node reference * @param nodeRef child node reference
*/ */
@Override
public void setupPermissions(final NodeRef parent, final NodeRef nodeRef) public void setupPermissions(final NodeRef parent, final NodeRef nodeRef)
{ {
mandatory("parent", parent); mandatory("parent", parent);
@@ -374,6 +415,7 @@ public class FilePlanPermissionServiceImpl extends ServiceBaseImpl
{ {
authenticationUtil.runAsSystem(new AuthenticationUtil.RunAsWork<Object>() authenticationUtil.runAsSystem(new AuthenticationUtil.RunAsWork<Object>()
{ {
@Override
public Object doWork() public Object doWork()
{ {
// set inheritance // set inheritance
@@ -480,6 +522,7 @@ public class FilePlanPermissionServiceImpl extends ServiceBaseImpl
authenticationUtil.runAsSystem(new AuthenticationUtil.RunAsWork<Object>() authenticationUtil.runAsSystem(new AuthenticationUtil.RunAsWork<Object>()
{ {
@Override
public Object doWork() public Object doWork()
{ {
if (nodeService.exists(record) && nodeService.hasAspect(record, aspectTypeQName)) if (nodeService.exists(record) && nodeService.hasAspect(record, aspectTypeQName))
@@ -506,6 +549,7 @@ public class FilePlanPermissionServiceImpl extends ServiceBaseImpl
authenticationUtil.runAs(new AuthenticationUtil.RunAsWork<Void>() authenticationUtil.runAs(new AuthenticationUtil.RunAsWork<Void>()
{ {
@Override
public Void doWork() public Void doWork()
{ {
NodeRef record = sourceAssocRef.getChildRef(); NodeRef record = sourceAssocRef.getChildRef();
@@ -547,6 +591,7 @@ public class FilePlanPermissionServiceImpl extends ServiceBaseImpl
/** /**
* @see org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService#setPermission(org.alfresco.service.cmr.repository.NodeRef, java.lang.String, java.lang.String, boolean) * @see org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService#setPermission(org.alfresco.service.cmr.repository.NodeRef, java.lang.String, java.lang.String, boolean)
*/ */
@Override
public void setPermission(final NodeRef nodeRef, final String authority, final String permission) public void setPermission(final NodeRef nodeRef, final String authority, final String permission)
{ {
ParameterCheck.mandatory("nodeRef", nodeRef); ParameterCheck.mandatory("nodeRef", nodeRef);
@@ -555,12 +600,18 @@ public class FilePlanPermissionServiceImpl extends ServiceBaseImpl
authenticationUtil.runAsSystem(new AuthenticationUtil.RunAsWork<Object>() authenticationUtil.runAsSystem(new AuthenticationUtil.RunAsWork<Object>()
{ {
@Override
public Void doWork() public Void doWork()
{ {
if (canPerformPermissionAction(nodeRef)) if (canPerformPermissionAction(nodeRef))
{ {
QName auditProperty = constructAuditEventName(authority, permission);
Map<QName, Serializable> oldPermission = getCurrentPermissionForAuthority(nodeRef, authority, permission, auditProperty);
// Set the permission on the node // Set the permission on the node
getPermissionService().setPermission(nodeRef, authority, permission, true); getPermissionService().setPermission(nodeRef, authority, permission, true);
// Add an entry in the audit log.
recordsManagementAuditService.auditOrUpdateEvent(nodeRef, AUDIT_SET_PERMISSION, oldPermission,
new HashMap<>(singletonMap(auditProperty, (Serializable) true)), true);
} }
else else
{ {
@@ -575,9 +626,31 @@ public class FilePlanPermissionServiceImpl extends ServiceBaseImpl
}); });
} }
/**
* Get the current permission on a node for an authority.
*
* @param nodeRef The node.
* @param authority The authority.
* @param auditProperty The QName used as the key in the returned map.
* @return A map from the audit property to true or false depending on whether the user currently has permission.
*/
private Map<QName, Serializable> getCurrentPermissionForAuthority(NodeRef nodeRef, String authority, String permission, QName auditProperty)
{
Set<AccessPermission> allSetPermissions = getPermissionService().getAllSetPermissions(nodeRef);
for (AccessPermission setPermission : allSetPermissions)
{
if (setPermission.getAuthority().equals(authority) && setPermission.getPermission().equals(permission))
{
return new HashMap<>(singletonMap(auditProperty, (Serializable) true));
}
}
return new HashMap<>(singletonMap(auditProperty, (Serializable) false));
}
/** /**
* @see org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService#deletePermission(org.alfresco.service.cmr.repository.NodeRef, java.lang.String, java.lang.String) * @see org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService#deletePermission(org.alfresco.service.cmr.repository.NodeRef, java.lang.String, java.lang.String)
*/ */
@Override
public void deletePermission(final NodeRef nodeRef, final String authority, final String permission) public void deletePermission(final NodeRef nodeRef, final String authority, final String permission)
{ {
ParameterCheck.mandatory("nodeRef", nodeRef); ParameterCheck.mandatory("nodeRef", nodeRef);
@@ -586,12 +659,18 @@ public class FilePlanPermissionServiceImpl extends ServiceBaseImpl
authenticationUtil.runAsSystem(new AuthenticationUtil.RunAsWork<Object>() authenticationUtil.runAsSystem(new AuthenticationUtil.RunAsWork<Object>()
{ {
@Override
public Void doWork() public Void doWork()
{ {
if (canPerformPermissionAction(nodeRef)) if (canPerformPermissionAction(nodeRef))
{ {
QName auditProperty = constructAuditEventName(authority, permission);
Map<QName, Serializable> oldPermission = getCurrentPermissionForAuthority(nodeRef, authority, permission, auditProperty);
// Delete permission on this node // Delete permission on this node
getPermissionService().deletePermission(nodeRef, authority, permission); getPermissionService().deletePermission(nodeRef, authority, permission);
// Add an entry in the audit log.
recordsManagementAuditService.auditOrUpdateEvent(nodeRef, AUDIT_SET_PERMISSION, oldPermission,
new HashMap<>(singletonMap(auditProperty, (Serializable) false)), true);
} }
else else
{ {
@@ -606,6 +685,19 @@ public class FilePlanPermissionServiceImpl extends ServiceBaseImpl
}); });
} }
/**
* Construct a QName so that the authority and permission are visible in the log.
*
* @param authority The authority whose permission is being changed.
* @param permission The name of the permission being changed.
* @return A QName such that the local name will make sense to the end user.
*/
private QName constructAuditEventName(String authority, String permission)
{
QName auditProperty = QName.createQName(AUDIT_NAMESPACE, permission + " " + authority);
return auditProperty;
}
private boolean canPerformPermissionAction(NodeRef nodeRef) private boolean canPerformPermissionAction(NodeRef nodeRef)
{ {
return isFilePlanContainer(nodeRef) || isRecordFolder(nodeRef) || isRecord(nodeRef) || isTransfer(nodeRef) || isHold(nodeRef); return isFilePlanContainer(nodeRef) || isRecordFolder(nodeRef) || isRecord(nodeRef) || isTransfer(nodeRef) || isHold(nodeRef);

View File

@@ -36,12 +36,15 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import org.alfresco.module.org_alfresco_module_rm.audit.RecordsManagementAuditService;
import org.alfresco.module.org_alfresco_module_rm.audit.event.AuditEvent;
import org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel; import org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel;
import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService; 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.model.RecordsManagementModel;
import org.alfresco.module.org_alfresco_module_rm.role.FilePlanRoleService; import org.alfresco.module.org_alfresco_module_rm.role.FilePlanRoleService;
import org.alfresco.repo.cache.SimpleCache; import org.alfresco.repo.cache.SimpleCache;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.security.permissions.AccessControlEntry; import org.alfresco.repo.security.permissions.AccessControlEntry;
import org.alfresco.repo.security.permissions.AccessControlList; import org.alfresco.repo.security.permissions.AccessControlList;
import org.alfresco.repo.security.permissions.processor.PermissionPostProcessor; import org.alfresco.repo.security.permissions.processor.PermissionPostProcessor;
@@ -69,7 +72,12 @@ import org.springframework.context.ApplicationEvent;
public class ExtendedPermissionServiceImpl extends PermissionServiceImpl public class ExtendedPermissionServiceImpl extends PermissionServiceImpl
implements ExtendedPermissionService implements ExtendedPermissionService
{ {
/** Writers simple cache */ /** An audit key for the enable permission inheritance event. */
private static final String AUDIT_ENABLE_INHERIT_PERMISSION = "enable-inherit-permission";
/** An audit key for the disable permission inheritance event. */
private static final String AUDIT_DISABLE_INHERIT_PERMISSION = "disable-inherit-permission";
/** Writers simple cache */
protected SimpleCache<Serializable, Set<String>> writersCache; protected SimpleCache<Serializable, Set<String>> writersCache;
/** /**
@@ -88,10 +96,30 @@ public class ExtendedPermissionServiceImpl extends PermissionServiceImpl
/** File plan service */ /** File plan service */
private FilePlanService filePlanService; private FilePlanService filePlanService;
/** Permission processor registry */ /** Permission processor registry */
private PermissionProcessorRegistry permissionProcessorRegistry; private PermissionProcessorRegistry permissionProcessorRegistry;
/** The RM audit service. */
private RecordsManagementAuditService recordsManagementAuditService;
/** {@inheritDoc} Register the audit events. */
@Override
public void init()
{
super.init();
AuthenticationUtil.runAsSystem(new RunAsWork<Void>()
{
@Override
public Void doWork() throws Exception
{
recordsManagementAuditService.registerAuditEvent(new AuditEvent(AUDIT_ENABLE_INHERIT_PERMISSION, "rm.audit.enable-inherit-permission"));
recordsManagementAuditService.registerAuditEvent(new AuditEvent(AUDIT_DISABLE_INHERIT_PERMISSION, "rm.audit.disable-inherit-permission"));
return null;
}
});
}
/** /**
* Gets the file plan service * Gets the file plan service
* *
@@ -111,17 +139,27 @@ public class ExtendedPermissionServiceImpl extends PermissionServiceImpl
{ {
this.filePlanService = filePlanService; this.filePlanService = filePlanService;
} }
/** /**
* Sets the permission processor registry * Sets the permission processor registry
* *
* @param permissionProcessorRegistry the permissions processor registry * @param permissionProcessorRegistry the permissions processor registry
*/ */
public void setPermissionProcessorRegistry(PermissionProcessorRegistry permissionProcessorRegistry) public void setPermissionProcessorRegistry(PermissionProcessorRegistry permissionProcessorRegistry)
{ {
this.permissionProcessorRegistry = permissionProcessorRegistry; this.permissionProcessorRegistry = permissionProcessorRegistry;
} }
/**
* Set the RM audit service.
*
* @param recordsManagementAuditService The RM audit service.
*/
public void setRecordsManagementAuditService(RecordsManagementAuditService recordsManagementAuditService)
{
this.recordsManagementAuditService = recordsManagementAuditService;
}
/** /**
* @see org.alfresco.repo.security.permissions.impl.PermissionServiceImpl#setAnyDenyDenies(boolean) * @see org.alfresco.repo.security.permissions.impl.PermissionServiceImpl#setAnyDenyDenies(boolean)
*/ */
@@ -186,40 +224,40 @@ public class ExtendedPermissionServiceImpl extends PermissionServiceImpl
public AccessStatus hasPermission(NodeRef nodeRef, String perm) public AccessStatus hasPermission(NodeRef nodeRef, String perm)
{ {
AccessStatus result = AccessStatus.UNDETERMINED; AccessStatus result = AccessStatus.UNDETERMINED;
// permission pre-processors // permission pre-processors
List<PermissionPreProcessor> preProcessors = permissionProcessorRegistry.getPermissionPreProcessors(); List<PermissionPreProcessor> preProcessors = permissionProcessorRegistry.getPermissionPreProcessors();
for (PermissionPreProcessor preProcessor : preProcessors) for (PermissionPreProcessor preProcessor : preProcessors)
{ {
// pre process permission // pre process permission
result = preProcessor.process(nodeRef, perm); result = preProcessor.process(nodeRef, perm);
// veto if denied // veto if denied
if (AccessStatus.DENIED.equals(result)) if (AccessStatus.DENIED.equals(result))
{ {
return result; return result;
} }
} }
// evaluate permission // evaluate permission
result = hasPermissionImpl(nodeRef, perm); result = hasPermissionImpl(nodeRef, perm);
// permission post-processors // permission post-processors
List<PermissionPostProcessor> postProcessors = permissionProcessorRegistry.getPermissionPostProcessors(); List<PermissionPostProcessor> postProcessors = permissionProcessorRegistry.getPermissionPostProcessors();
for (PermissionPostProcessor postProcessor : postProcessors) for (PermissionPostProcessor postProcessor : postProcessors)
{ {
// post process permission // post process permission
result = postProcessor.process(result, nodeRef, perm, this.configuredReadPermissions, this.configuredFilePermissions); result = postProcessor.process(result, nodeRef, perm, this.configuredReadPermissions, this.configuredFilePermissions);
} }
return result; return result;
} }
/** /**
* Implementation of hasPermission method call. * Implementation of hasPermission method call.
* <p> * <p>
* Separation also convenient for unit testing. * Separation also convenient for unit testing.
* *
* @param nodeRef node reference * @param nodeRef node reference
* @param perm permission * @param perm permission
* @return {@link AccessStatus} access status result * @return {@link AccessStatus} access status result
@@ -315,6 +353,7 @@ public class ExtendedPermissionServiceImpl extends PermissionServiceImpl
* @param aclId * @param aclId
* @return * @return
*/ */
@Override
public Set<String> getReadersDenied(Long aclId) public Set<String> getReadersDenied(Long aclId)
{ {
AccessControlList acl = aclDaoComponent.getAccessControlList(aclId); AccessControlList acl = aclDaoComponent.getAccessControlList(aclId);
@@ -354,6 +393,7 @@ public class ExtendedPermissionServiceImpl extends PermissionServiceImpl
/** /**
* @see org.alfresco.repo.security.permissions.impl.ExtendedPermissionService#getWriters(java.lang.Long) * @see org.alfresco.repo.security.permissions.impl.ExtendedPermissionService#getWriters(java.lang.Long)
*/ */
@Override
public Set<String> getWriters(Long aclId) public Set<String> getWriters(Long aclId)
{ {
AccessControlList acl = aclDaoComponent.getAccessControlList(aclId); AccessControlList acl = aclDaoComponent.getAccessControlList(aclId);
@@ -401,7 +441,12 @@ public class ExtendedPermissionServiceImpl extends PermissionServiceImpl
{ {
setPermission(nodeRef, adminRole, RMPermissionModel.FILING, true); setPermission(nodeRef, adminRole, RMPermissionModel.FILING, true);
} }
super.setInheritParentPermissions(nodeRef, inheritParentPermissions); if (inheritParentPermissions != super.getInheritParentPermissions(nodeRef))
{
super.setInheritParentPermissions(nodeRef, inheritParentPermissions);
String auditEvent = (inheritParentPermissions ? AUDIT_ENABLE_INHERIT_PERMISSION : AUDIT_DISABLE_INHERIT_PERMISSION);
recordsManagementAuditService.auditEvent(nodeRef, auditEvent);
}
} }
/** /**