diff --git a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/audit/AuditAddToHoldTests.java b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/audit/AuditAddToHoldTests.java
new file mode 100644
index 0000000000..954c074064
--- /dev/null
+++ b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/audit/AuditAddToHoldTests.java
@@ -0,0 +1,313 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2021 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail. Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco 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 Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+package org.alfresco.rest.rm.community.audit;
+
+import static java.util.Arrays.asList;
+
+import static org.alfresco.rest.rm.community.base.TestData.HOLD_DESCRIPTION;
+import static org.alfresco.rest.rm.community.base.TestData.HOLD_REASON;
+import static org.alfresco.rest.rm.community.model.audit.AuditEvents.ADD_TO_HOLD;
+import static org.alfresco.rest.rm.community.util.CommonTestUtils.generateTestPrefix;
+import static org.alfresco.rest.rm.community.utils.RMSiteUtil.FILE_PLAN_PATH;
+import static org.alfresco.utility.Utility.buildPath;
+import static org.alfresco.utility.Utility.removeLastSlash;
+import static org.alfresco.utility.data.RandomData.getRandomName;
+import static org.alfresco.utility.report.log.Step.STEP;
+import static org.apache.commons.httpclient.HttpStatus.SC_INTERNAL_SERVER_ERROR;
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import com.google.common.collect.ImmutableMap;
+
+import org.alfresco.dataprep.CMISUtil;
+import org.alfresco.rest.rm.community.base.BaseRMRestTest;
+import org.alfresco.rest.rm.community.model.audit.AuditEntry;
+import org.alfresco.rest.rm.community.model.record.Record;
+import org.alfresco.rest.rm.community.model.recordcategory.RecordCategory;
+import org.alfresco.rest.rm.community.model.recordcategory.RecordCategoryChild;
+import org.alfresco.rest.rm.community.model.user.UserPermissions;
+import org.alfresco.rest.rm.community.model.user.UserRoles;
+import org.alfresco.rest.v0.HoldsAPI;
+import org.alfresco.rest.v0.service.RMAuditService;
+import org.alfresco.rest.v0.service.RoleService;
+import org.alfresco.test.AlfrescoTest;
+import org.alfresco.utility.constants.UserRole;
+import org.alfresco.utility.model.FileModel;
+import org.alfresco.utility.model.SiteModel;
+import org.alfresco.utility.model.UserModel;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+/**
+ * This class contains the tests that check the add to hold event is audited
+ *
+ * @author Claudia Agache
+ * @since 3.3
+ */
+@AlfrescoTest (jira = "RM-6859")
+public class AuditAddToHoldTests extends BaseRMRestTest
+{
+ private final String PREFIX = generateTestPrefix(AuditAddToHoldTests.class);
+ private final String HOLD1 = PREFIX + "hold1";
+ private final String HOLD2 = PREFIX + "hold2";
+
+ @Autowired
+ private RMAuditService rmAuditService;
+ @Autowired
+ private HoldsAPI holdsAPI;
+ @Autowired
+ private RoleService roleService;
+
+ private UserModel rmAdmin, rmManagerNoReadOnHold, rmManagerNoReadOnNode;
+ private SiteModel privateSite;
+ private RecordCategory recordCategory;
+ private RecordCategoryChild recordFolder;
+ private List auditEntries;
+ private List holdsList = asList(HOLD1, HOLD2);
+ private List holdsListRef = new ArrayList<>();
+ private String hold1NodeRef;
+
+ @BeforeClass (alwaysRun = true)
+ public void preconditionForAuditAddToHoldTests() throws Exception
+ {
+ STEP("Create 2 holds.");
+ hold1NodeRef = holdsAPI.createHoldAndGetNodeRef(getAdminUser().getUsername(),
+ getAdminUser().getPassword(), HOLD1, HOLD_REASON, HOLD_DESCRIPTION);
+ String hold2NodeRef = holdsAPI.createHoldAndGetNodeRef(getAdminUser().getUsername(), getAdminUser().getPassword(), HOLD2, HOLD_REASON, HOLD_DESCRIPTION);
+ holdsListRef = asList(hold1NodeRef, hold2NodeRef);
+
+ STEP("Create a new record category with a record folder.");
+ recordCategory = createRootCategory(getRandomName("recordCategory"));
+ recordFolder = createRecordFolder(recordCategory.getId(), PREFIX + "recFolder");
+
+ STEP("Create an user with full rights to add content to a hold.");
+ rmAdmin = roleService.createUserWithRMRole(UserRoles.ROLE_RM_ADMIN.roleId);
+
+ STEP("Create a collaboration site.");
+ privateSite = dataSite.usingUser(rmAdmin).createPrivateRandomSite();
+
+ STEP("Create users without rights to add content to a hold.");
+ rmManagerNoReadOnHold = roleService.createUserWithSiteRoleRMRoleAndPermission(privateSite,
+ UserRole.SiteManager, recordCategory.getId(), UserRoles.ROLE_RM_MANAGER, UserPermissions.PERMISSION_FILING);
+ rmManagerNoReadOnNode = roleService.createUserWithRMRoleAndRMNodePermission(UserRoles.ROLE_RM_MANAGER.roleId,
+ hold1NodeRef, UserPermissions.PERMISSION_FILING);
+ }
+
+ /**
+ * Data provider with valid nodes that can be added to a hold
+ *
+ * @return the node id, the node name and the node path
+ * @throws Exception
+ */
+ @DataProvider (name = "validNodesForAddToHold")
+ public Object[][] getValidNodesForAddToHold() throws Exception
+ {
+ FileModel contentToBeAdded = dataContent.usingAdmin().usingSite(privateSite)
+ .createContent(CMISUtil.DocumentType.TEXT_PLAIN);
+ RecordCategoryChild recordFolderToBeAdded = createRecordFolder(recordCategory.getId(), PREFIX + "recFolderToBeAdded");
+ Record recordToBeAdded = createElectronicRecord(recordFolder.getId(), PREFIX + "record");
+ String recordFolderPath = removeLastSlash(buildPath(FILE_PLAN_PATH, recordCategory.getName(),
+ recordFolderToBeAdded.getName()));
+ String recordPath = removeLastSlash(buildPath(FILE_PLAN_PATH, recordCategory.getName(),
+ recordFolder.getName(), recordToBeAdded.getName()));
+ String contentPath = "/Company Home" + contentToBeAdded.getCmisLocation();
+
+ return new String[][]
+ {
+ // a record folder
+ { recordFolderToBeAdded.getId(), recordFolderToBeAdded.getName(), recordFolderPath },
+ // a record
+ { recordToBeAdded.getId(), recordToBeAdded.getName(), recordPath },
+ //an active content,
+ { contentToBeAdded.getNodeRefWithoutVersion(), contentToBeAdded.getName(), contentPath }
+ };
+ }
+
+ /**
+ * Given a document/record/record folder is added to a hold
+ * When I view the audit log
+ * Then an entry has been created in the audit log that contains the following:
+ * name of the hold
+ * name of the document/record/record folder added
+ * user who added the content
+ * date the content was added
+ * path of the node
+ */
+ @Test (dataProvider = "validNodesForAddToHold")
+ public void addToHoldEventIsAudited(String nodeId, String nodeName, String nodePath)
+ {
+ rmAuditService.clearAuditLog();
+
+ STEP("Add node to hold.");
+ holdsAPI.addItemToHold(rmAdmin.getUsername(), rmAdmin.getPassword(), nodeId, HOLD1);
+
+ STEP("Check the audit log contains the entry for the add to hold event.");
+ rmAuditService.checkAuditLogForEvent(getAdminUser(), ADD_TO_HOLD, rmAdmin, nodeName, nodePath,
+ asList(ImmutableMap.of("new", nodeName, "previous", "", "name", "Name"),
+ ImmutableMap.of("new", HOLD1, "previous", "", "name", "Hold Name")));
+ }
+
+ /**
+ * Given an unsuccessful add to hold action
+ * When I view the audit log
+ * Then the add to hold event isn't audited
+ */
+ @Test
+ public void unsuccessfulAddToHoldIsNotAudited() throws Exception
+ {
+ STEP("Create a new record");
+ Record recordToBeAdded = createElectronicRecord(recordFolder.getId(), PREFIX + "record");
+
+ rmAuditService.clearAuditLog();
+
+ STEP("Try to add the record to a hold by an user with no rights.");
+ holdsAPI.addItemsToHolds(rmManagerNoReadOnHold.getUsername(), rmManagerNoReadOnHold.getPassword(),
+ SC_INTERNAL_SERVER_ERROR, Collections.singletonList(recordToBeAdded.getId()),
+ Collections.singletonList(hold1NodeRef));
+
+ STEP("Check the audit log doesn't contain the entry for the unsuccessful add to hold.");
+ assertTrue("The list of events should not contain Add to Hold entry ",
+ rmAuditService.getAuditEntriesFilteredByEvent(getAdminUser(), ADD_TO_HOLD).isEmpty());
+ }
+
+ /**
+ * Given a not empty record folder is added to a hold
+ * When I view the audit log
+ * Then only an entry has been created in the audit log for the record folder added
+ */
+ @Test
+ public void addToHoldIsNotAuditedForRecordFolderChildren() throws Exception
+ {
+ STEP("Create a new record folder with a record inside");
+ RecordCategoryChild notEmptyRecFolder = createRecordFolder(recordCategory.getId(), PREFIX + "notEmptyRecFolder");
+ Record record = createElectronicRecord(notEmptyRecFolder.getId(), PREFIX + "record");
+
+ rmAuditService.clearAuditLog();
+
+ STEP("Add record folder to hold.");
+ holdsAPI.addItemToHold(rmAdmin.getUsername(), rmAdmin.getPassword(), notEmptyRecFolder.getId(), HOLD1);
+
+ auditEntries = rmAuditService.getAuditEntriesFilteredByEvent(getAdminUser(), ADD_TO_HOLD);
+
+ STEP("Check the audit log contains only an entry for add to hold.");
+ assertEquals("The list of events should contain only an entry", 1, auditEntries.size());
+ assertTrue("The list of events should not contain Add to Hold entry for the record",
+ auditEntries.stream().noneMatch(entry -> entry.getNodeName().equals(record.getName())));
+ }
+
+ /**
+ * Given a record is added to multiple holds
+ * When I view the audit log
+ * Then multiple entries have been created in the audit log for each add to hold event
+ */
+ @Test
+ public void addToHoldIsAuditedInBulkAddition() throws Exception
+ {
+ STEP("Create a new record");
+ Record recordToBeAdded = createElectronicRecord(recordFolder.getId(), PREFIX + "record");
+
+ rmAuditService.clearAuditLog();
+
+ STEP("Add record to multiple holds.");
+ holdsAPI.addItemsToHolds(rmAdmin.getUsername(), rmAdmin.getPassword(),
+ Collections.singletonList(recordToBeAdded.getId()), holdsList);
+
+ auditEntries = rmAuditService.getAuditEntriesFilteredByEvent(getAdminUser(), ADD_TO_HOLD);
+
+ STEP("Check the audit log contains entries for both additions.");
+ assertEquals("The list of events should contain Add to Hold entries for both holds", 2, auditEntries.size());
+ assertTrue("The hold name value for the first add to hold is not audited.",
+ auditEntries.stream().anyMatch(entry -> entry.getChangedValues().contains(
+ ImmutableMap.of("new", HOLD1, "previous", "", "name", "Hold Name"))));
+ assertTrue("The hold name value for the second add to hold is not audited.",
+ auditEntries.stream().anyMatch(entry -> entry.getChangedValues().contains(
+ ImmutableMap.of("new", HOLD2, "previous", "", "name", "Hold Name"))));
+ }
+
+ /**
+ * Given a document is added to a hold
+ * When I view the audit log as an user with no Read permissions over the document
+ * Then the add to hold entry isn't visible
+ */
+ @Test
+ public void addToHoldAuditEntryNotVisible()
+ {
+ STEP("Create a new file");
+ FileModel contentToBeAdded = dataContent.usingAdmin().usingSite(privateSite)
+ .createContent(CMISUtil.DocumentType.TEXT_PLAIN);
+ rmAuditService.clearAuditLog();
+
+ STEP("Add file to hold.");
+ holdsAPI.addItemToHold(rmAdmin.getUsername(), rmAdmin.getPassword(), contentToBeAdded.getNodeRefWithoutVersion(), HOLD1);
+
+ STEP("Check that an user with no Read permissions can't see the entry for the add to hold event.");
+ assertTrue("The list of events should not contain Add to Hold entry ",
+ rmAuditService.getAuditEntriesFilteredByEvent(rmManagerNoReadOnNode, ADD_TO_HOLD).isEmpty());
+ }
+
+ /**
+ * Given a document is added to a hold
+ * When I view the audit log as an user with no Read permissions over the hold
+ * Then the the hold name is replaced in the add to hold entry
+ */
+ @Test
+ public void addToHoldAuditEntryHoldNameNotVisible()
+ {
+ STEP("Create a new file");
+ FileModel contentToBeAdded = dataContent.usingAdmin().usingSite(privateSite)
+ .createContent(CMISUtil.DocumentType.TEXT_PLAIN);
+ rmAuditService.clearAuditLog();
+
+ STEP("Add file to hold.");
+ holdsAPI.addItemToHold(rmAdmin.getUsername(), rmAdmin.getPassword(), contentToBeAdded.getNodeRefWithoutVersion(), HOLD1);
+
+ auditEntries = rmAuditService.getAuditEntriesFilteredByEvent(rmManagerNoReadOnHold, ADD_TO_HOLD);
+
+ STEP("Check that an user with no Read permissions can't see the hold name in the add to hold event.");
+ String replacementHoldName = "You don't have permission to view this hold.";
+ assertEquals("The list of events should contain the Add to Hold entry", 1, auditEntries.size());
+ assertTrue("The hold name should not be visible in the Add to Hold entry ",
+ auditEntries.stream().anyMatch(entry -> entry.getChangedValues().contains(
+ ImmutableMap.of("new", replacementHoldName, "previous", "", "name", "Hold Name"))));
+ }
+
+ @AfterClass (alwaysRun = true)
+ public void cleanUpAuditAddToHoldTests()
+ {
+ holdsListRef.forEach(holdRef -> holdsAPI.deleteHold(getAdminUser(), holdRef));
+ dataSite.usingAdmin().deleteSite(privateSite);
+ asList(rmAdmin, rmManagerNoReadOnHold, rmManagerNoReadOnNode).forEach(user -> getDataUser().usingAdmin().deleteUser(user));
+ getRestAPIFactory().getRecordCategoryAPI().deleteRecordCategory(recordCategory.getId());
+ }
+}
diff --git a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/audit/AuditCreateHoldTests.java b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/audit/AuditCreateHoldTests.java
new file mode 100644
index 0000000000..01541c9832
--- /dev/null
+++ b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/audit/AuditCreateHoldTests.java
@@ -0,0 +1,189 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2021 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail. Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco 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 Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+package org.alfresco.rest.rm.community.audit;
+
+import static java.util.Arrays.asList;
+
+import static org.alfresco.rest.rm.community.base.TestData.HOLD_DESCRIPTION;
+import static org.alfresco.rest.rm.community.base.TestData.HOLD_REASON;
+import static org.alfresco.rest.rm.community.model.audit.AuditEvents.CREATE_HOLD;
+import static org.alfresco.rest.rm.community.util.CommonTestUtils.generateTestPrefix;
+import static org.alfresco.utility.report.log.Step.STEP;
+import static org.apache.commons.httpclient.HttpStatus.SC_INTERNAL_SERVER_ERROR;
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.google.common.collect.ImmutableMap;
+
+import org.alfresco.rest.rm.community.base.BaseRMRestTest;
+import org.alfresco.rest.rm.community.model.audit.AuditEntry;
+import org.alfresco.rest.rm.community.model.user.UserRoles;
+import org.alfresco.rest.v0.HoldsAPI;
+import org.alfresco.rest.v0.service.RMAuditService;
+import org.alfresco.rest.v0.service.RoleService;
+import org.alfresco.test.AlfrescoTest;
+import org.alfresco.utility.model.UserModel;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+/**
+ * This class contains the tests that check the create hold event is audited
+ *
+ * @author Claudia Agache
+ * @since 3.3
+ */
+@AlfrescoTest (jira = "RM-6859")
+public class AuditCreateHoldTests extends BaseRMRestTest
+{
+ private final String PREFIX = generateTestPrefix(AuditCreateHoldTests.class);
+ private final String HOLD1 = PREFIX + "createHold";
+ private final String HOLD2 = PREFIX + "createHold2";
+ private final String HOLD3 = PREFIX + "createHold3";
+ private List holdsListRef = new ArrayList<>();
+
+ @Autowired
+ private RMAuditService rmAuditService;
+ @Autowired
+ private HoldsAPI holdsAPI;
+ @Autowired
+ private RoleService roleService;
+
+ private UserModel rmAdmin, rmManager;
+
+ @BeforeClass (alwaysRun = true)
+ public void preconditionForAuditCreateHoldTests()
+ {
+ STEP("Create test users.");
+ rmAdmin = roleService.createUserWithRMRole(UserRoles.ROLE_RM_ADMIN.roleId);
+ rmManager = roleService.createUserWithRMRole(UserRoles.ROLE_RM_MANAGER.roleId);
+ }
+
+ /**
+ * Given a new hold is created
+ * When I view the audit log
+ * Then an entry has been created in the audit log which contains the following:
+ * name of the hold
+ * reason for hold
+ * user who created the hold
+ * date the creation occurred
+ */
+ @Test
+ public void createHoldEventIsAuditedForNewHold()
+ {
+ rmAuditService.clearAuditLog();
+
+ STEP("Create a new hold.");
+ String hold1NodeRef = holdsAPI.createHoldAndGetNodeRef(rmAdmin.getUsername(), rmAdmin.getPassword(), HOLD1,
+ HOLD_REASON, HOLD_DESCRIPTION);
+ holdsListRef.add(hold1NodeRef);
+ STEP("Check the audit log contains the entry for the created hold with the hold details.");
+ rmAuditService.checkAuditLogForEvent(getAdminUser(), CREATE_HOLD, rmAdmin, HOLD1,
+ asList(ImmutableMap.of("new", HOLD_REASON, "previous", "", "name", "Hold Reason"),
+ ImmutableMap.of("new", HOLD1, "previous", "", "name", "Hold Name")));
+ }
+
+ /**
+ * Given an unsuccessful create hold action
+ * When I view the audit log
+ * Then the create hold event isn't audited
+ */
+ @Test
+ public void createHoldEventIsNotAuditedForExistingHold()
+ {
+ STEP("Create a new hold.");
+ String hold2NodeRef = holdsAPI.createHoldAndGetNodeRef(rmAdmin.getUsername(), rmAdmin.getPassword(), HOLD2, HOLD_REASON, HOLD_DESCRIPTION);
+ holdsListRef.add(hold2NodeRef);
+ rmAuditService.clearAuditLog();
+
+ STEP("Try to create again the same hold and expect action to fail.");
+ holdsAPI.createHold(rmAdmin.getUsername(), rmAdmin.getPassword(), HOLD2, HOLD_REASON, HOLD_DESCRIPTION,
+ SC_INTERNAL_SERVER_ERROR);
+
+ STEP("Check the audit log doesn't contain the entry for the second create hold event.");
+ assertTrue("The list of events should not contain Create Hold entry ",
+ rmAuditService.getAuditEntriesFilteredByEvent(getAdminUser(), CREATE_HOLD).isEmpty());
+ }
+
+ /**
+ * Given a new hold is created and then deleted
+ * When I view the audit log
+ * Then the create hold entry still contains the initial details
+ */
+ @Test
+ public void createHoldAuditEntryIsNotLost()
+ {
+ final String holdName = PREFIX + "holdToBeDeleted";
+ rmAuditService.clearAuditLog();
+
+ STEP("Create a new hold.");
+ holdsAPI.createHold(rmAdmin.getUsername(), rmAdmin.getPassword(), holdName, HOLD_REASON, HOLD_DESCRIPTION);
+
+ STEP("Get the list of audit entries for the create hold event.");
+ List auditEntries = rmAuditService.getAuditEntriesFilteredByEvent(getAdminUser(), CREATE_HOLD);
+
+ STEP("Delete the created hold.");
+ holdsAPI.deleteHold(rmAdmin.getUsername(), rmAdmin.getPassword(), holdName);
+
+ STEP("Get again the list of audit entries for the create hold event.");
+ List auditEntriesAfterDelete = rmAuditService.getAuditEntriesFilteredByEvent(getAdminUser(), CREATE_HOLD);
+
+ STEP("Check that the audit entry for the created hold didn't change after hold deletion.");
+ assertEquals("The audit entry for Create Hold has been changed", auditEntries, auditEntriesAfterDelete);
+ }
+
+ /**
+ * Given a new hold is created
+ * When I view the audit log as an user with no Read permissions over the created hold
+ * Then the create hold entry isn't visible
+ */
+ @Test
+ public void createHoldAuditEntryNotVisible()
+ {
+ rmAuditService.clearAuditLog();
+
+ STEP("Create a new hold.");
+ String hold3NodeRef = holdsAPI.createHoldAndGetNodeRef(rmAdmin.getUsername(), rmAdmin.getPassword(), HOLD3,
+ HOLD_REASON, HOLD_DESCRIPTION);
+ holdsListRef.add(hold3NodeRef);
+
+ STEP("Check that an user with no Read permissions over the hold can't see the entry for the create hold event");
+ assertTrue("The list of events should not contain Create Hold entry ",
+ rmAuditService.getAuditEntriesFilteredByEvent(rmManager, CREATE_HOLD).isEmpty());
+ }
+
+ @AfterClass (alwaysRun = true)
+ public void cleanUpAuditCreateHoldTests()
+ {
+ holdsListRef.forEach(holdRef -> holdsAPI.deleteHold(getAdminUser(), holdRef));
+ asList(rmAdmin, rmManager).forEach(user -> getDataUser().usingAdmin().deleteUser(user));
+ }
+}
diff --git a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/audit/AuditDeleteHoldTests.java b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/audit/AuditDeleteHoldTests.java
new file mode 100644
index 0000000000..c5723bcd6e
--- /dev/null
+++ b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/audit/AuditDeleteHoldTests.java
@@ -0,0 +1,139 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2021 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail. Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco 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 Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+package org.alfresco.rest.rm.community.audit;
+
+import static java.util.Arrays.asList;
+
+import static org.alfresco.rest.rm.community.base.TestData.HOLD_DESCRIPTION;
+import static org.alfresco.rest.rm.community.base.TestData.HOLD_REASON;
+import static org.alfresco.rest.rm.community.model.audit.AuditEvents.DELETE_HOLD;
+import static org.alfresco.rest.rm.community.util.CommonTestUtils.generateTestPrefix;
+import static org.alfresco.utility.report.log.Step.STEP;
+import static org.apache.commons.httpclient.HttpStatus.SC_INTERNAL_SERVER_ERROR;
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.util.Collections;
+
+import com.google.common.collect.ImmutableMap;
+
+import org.alfresco.rest.rm.community.base.BaseRMRestTest;
+import org.alfresco.rest.rm.community.model.user.UserRoles;
+import org.alfresco.rest.v0.HoldsAPI;
+import org.alfresco.rest.v0.service.RMAuditService;
+import org.alfresco.rest.v0.service.RoleService;
+import org.alfresco.test.AlfrescoTest;
+import org.alfresco.utility.model.UserModel;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+/**
+ * This class contains the tests that check the delete hold event is audited
+ *
+ * @author Claudia Agache
+ * @since 3.3
+ */
+@AlfrescoTest (jira = "RM-6859")
+public class AuditDeleteHoldTests extends BaseRMRestTest
+{
+ private final String PREFIX = generateTestPrefix(AuditDeleteHoldTests.class);
+ private final String HOLD = PREFIX + "holdToBeDeleted";
+ private final String HOLD2 = PREFIX + "deleteHold";
+
+ @Autowired
+ private RMAuditService rmAuditService;
+ @Autowired
+ private HoldsAPI holdsAPI;
+ @Autowired
+ private RoleService roleService;
+
+ private UserModel rmAdmin, rmManager;
+ private String holdNodeRef;
+
+ @BeforeClass (alwaysRun = true)
+ public void preconditionForAuditDeleteHoldTests()
+ {
+ STEP("Create a new hold.");
+ holdNodeRef = holdsAPI.createHoldAndGetNodeRef(getAdminUser().getUsername(), getAdminUser().getPassword(), HOLD,
+ HOLD_REASON, HOLD_DESCRIPTION);
+
+ STEP("Create 2 users with different permissions for the created hold.");
+ rmAdmin = roleService.createUserWithRMRole(UserRoles.ROLE_RM_ADMIN.roleId);
+ rmManager = roleService.createUserWithRMRole(UserRoles.ROLE_RM_MANAGER.roleId);
+ }
+
+ /**
+ * Given a hold is deleted
+ * When I view the audit log
+ * Then an entry has been created in the audit log which contains the following:
+ * name of the hold
+ * user who deleted the hold
+ * date the delete occurred
+ */
+ @Test
+ public void deleteHoldEventIsAudited()
+ {
+ STEP("Create a new hold.");
+ String holdRef = holdsAPI.createHoldAndGetNodeRef(rmAdmin.getUsername(), rmAdmin.getPassword(), HOLD2,
+ HOLD_REASON, HOLD_DESCRIPTION);
+
+ rmAuditService.clearAuditLog();
+
+ STEP("Delete the created hold.");
+ holdsAPI.deleteHold(rmAdmin, holdRef);
+
+ STEP("Check the audit log contains the entry for the deleted hold with the hold details.");
+ rmAuditService.checkAuditLogForEvent(getAdminUser(), DELETE_HOLD, rmAdmin, HOLD2,
+ Collections.singletonList(ImmutableMap.of("new", "", "previous", HOLD2, "name", "Hold Name")));
+ }
+
+ /**
+ * Given an unsuccessful delete hold action
+ * When I view the audit log
+ * Then the delete hold event isn't audited
+ */
+ @Test
+ public void unsuccessfulDeleteHoldIsNotAudited()
+ {
+ rmAuditService.clearAuditLog();
+
+ STEP("Try to delete a hold by an user with no Read permissions over the hold.");
+ holdsAPI.deleteHold(rmManager.getUsername(), rmManager.getPassword(), holdNodeRef, SC_INTERNAL_SERVER_ERROR);
+
+ STEP("Check the audit log doesn't contain the entry for the unsuccessful delete hold.");
+ assertTrue("The list of events should not contain Delete Hold entry ",
+ rmAuditService.getAuditEntriesFilteredByEvent(getAdminUser(), DELETE_HOLD).isEmpty());
+ }
+
+ @AfterClass (alwaysRun = true)
+ public void cleanUpAuditDeleteHoldTests()
+ {
+ holdsAPI.deleteHold(getAdminUser(), holdNodeRef);
+ asList(rmAdmin, rmManager).forEach(user -> getDataUser().usingAdmin().deleteUser(user));
+ }
+}
diff --git a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/audit/AuditRemoveFromHoldTests.java b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/audit/AuditRemoveFromHoldTests.java
new file mode 100644
index 0000000000..75f7a9a433
--- /dev/null
+++ b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/audit/AuditRemoveFromHoldTests.java
@@ -0,0 +1,329 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2021 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail. Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco 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 Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+package org.alfresco.rest.rm.community.audit;
+
+import static java.util.Arrays.asList;
+
+import static org.alfresco.rest.rm.community.base.TestData.HOLD_DESCRIPTION;
+import static org.alfresco.rest.rm.community.base.TestData.HOLD_REASON;
+import static org.alfresco.rest.rm.community.model.audit.AuditEvents.REMOVE_FROM_HOLD;
+import static org.alfresco.rest.rm.community.util.CommonTestUtils.generateTestPrefix;
+import static org.alfresco.rest.rm.community.utils.RMSiteUtil.FILE_PLAN_PATH;
+import static org.alfresco.utility.Utility.buildPath;
+import static org.alfresco.utility.Utility.removeLastSlash;
+import static org.alfresco.utility.data.RandomData.getRandomName;
+import static org.alfresco.utility.report.log.Step.STEP;
+import static org.apache.commons.httpclient.HttpStatus.SC_INTERNAL_SERVER_ERROR;
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import com.google.common.collect.ImmutableMap;
+
+import org.alfresco.dataprep.CMISUtil;
+import org.alfresco.rest.rm.community.base.BaseRMRestTest;
+import org.alfresco.rest.rm.community.model.audit.AuditEntry;
+import org.alfresco.rest.rm.community.model.record.Record;
+import org.alfresco.rest.rm.community.model.recordcategory.RecordCategory;
+import org.alfresco.rest.rm.community.model.recordcategory.RecordCategoryChild;
+import org.alfresco.rest.rm.community.model.user.UserPermissions;
+import org.alfresco.rest.rm.community.model.user.UserRoles;
+import org.alfresco.rest.v0.HoldsAPI;
+import org.alfresco.rest.v0.service.RMAuditService;
+import org.alfresco.rest.v0.service.RoleService;
+import org.alfresco.test.AlfrescoTest;
+import org.alfresco.utility.constants.UserRole;
+import org.alfresco.utility.model.FileModel;
+import org.alfresco.utility.model.SiteModel;
+import org.alfresco.utility.model.UserModel;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+/**
+ * This class contains the tests that check the remove from hold event is audited
+ *
+ * @author Claudia Agache
+ * @since 3.3
+ */
+@AlfrescoTest (jira = "RM-6859")
+public class AuditRemoveFromHoldTests extends BaseRMRestTest
+{
+ private final String PREFIX = generateTestPrefix(AuditRemoveFromHoldTests.class);
+ private final String HOLD1 = PREFIX + "hold1";
+ private final String HOLD2 = PREFIX + "hold2";
+ private final String HOLD3 = PREFIX + "hold3";
+ private final String DELETED_HOLD = PREFIX + "deletedHold";
+
+ @Autowired
+ private RMAuditService rmAuditService;
+ @Autowired
+ private HoldsAPI holdsAPI;
+ @Autowired
+ private RoleService roleService;
+
+ private UserModel rmAdmin, rmManagerNoReadOnHold, rmManagerNoReadOnNode;
+ private SiteModel privateSite;
+ private RecordCategory recordCategory;
+ private RecordCategoryChild recordFolder, heldRecordFolder;
+ private Record heldRecord;
+ private List auditEntries;
+ private List holdsList = asList(HOLD1, HOLD2, HOLD3);
+ private List holdsListRef = new ArrayList<>();
+ private FileModel heldContent;
+ private String hold1NodeRef;
+
+ @BeforeClass (alwaysRun = true)
+ public void preconditionForAuditRemoveFromHoldTests() throws Exception
+ {
+ STEP("Create an user with full rights to remove content from a hold.");
+ rmAdmin = roleService.createUserWithRMRole(UserRoles.ROLE_RM_ADMIN.roleId);
+
+ STEP("Create a collaboration site.");
+ privateSite = dataSite.usingUser(rmAdmin).createPrivateRandomSite();
+
+ STEP("Create new holds.");
+ hold1NodeRef = holdsAPI.createHoldAndGetNodeRef(getAdminUser().getUsername(), getAdminUser().getPassword(),
+ HOLD1, HOLD_REASON, HOLD_DESCRIPTION);
+ String hold2NodeRef = holdsAPI.createHoldAndGetNodeRef(getAdminUser().getUsername(), getAdminUser().getPassword(), HOLD2, HOLD_REASON, HOLD_DESCRIPTION);
+ String hold3NodeRef = holdsAPI.createHoldAndGetNodeRef(getAdminUser().getUsername(), getAdminUser().getPassword(), HOLD3, HOLD_REASON, HOLD_DESCRIPTION);
+ String deleteNodeRef = holdsAPI.createHoldAndGetNodeRef(getAdminUser().getUsername(), getAdminUser().getPassword(), DELETED_HOLD, HOLD_REASON, HOLD_DESCRIPTION);
+ holdsListRef = asList(hold1NodeRef, hold2NodeRef, hold3NodeRef);
+
+ STEP("Create a new record category with a record folder.");
+ recordCategory = createRootCategory(getRandomName("recordCategory"));
+ recordFolder = createRecordFolder(recordCategory.getId(), getRandomName("recFolder"));
+
+ STEP("Create some held items");
+ heldContent = dataContent.usingAdmin().usingSite(privateSite)
+ .createContent(CMISUtil.DocumentType.TEXT_PLAIN);
+ heldRecordFolder = createRecordFolder(recordCategory.getId(), PREFIX + "heldRecFolder");
+ heldRecord = createElectronicRecord(recordFolder.getId(), PREFIX + "record");
+
+ holdsAPI.addItemsToHolds(getAdminUser().getUsername(), getAdminUser().getPassword(),
+ asList(heldContent.getNodeRefWithoutVersion(), heldRecordFolder.getId(), heldRecord.getId()),
+ holdsList);
+
+ STEP("Create users without rights to remove content from a hold.");
+ rmManagerNoReadOnHold = roleService.createUserWithSiteRoleRMRoleAndPermission(privateSite,
+ UserRole.SiteManager, recordCategory.getId(), UserRoles.ROLE_RM_MANAGER, UserPermissions.PERMISSION_FILING);
+ rmManagerNoReadOnNode = roleService.createUserWithRMRoleAndRMNodePermission(UserRoles.ROLE_RM_MANAGER.roleId,
+ hold1NodeRef, UserPermissions.PERMISSION_FILING);
+ }
+
+ /**
+ * Data provider with valid nodes that can be removed from a hold
+ *
+ * @return the node id, the node name and the node path
+ */
+ @DataProvider (name = "validNodesForRemoveFromHold")
+ public Object[][] getValidNodesForRemoveFromHold()
+ {
+ String recordFolderPath = removeLastSlash(buildPath(FILE_PLAN_PATH, recordCategory.getName(),
+ heldRecordFolder.getName()));
+ String recordPath = removeLastSlash(buildPath(FILE_PLAN_PATH, recordCategory.getName(),
+ recordFolder.getName(), heldRecord.getName()));
+ String contentPath = "/Company Home" + heldContent.getCmisLocation();
+
+ return new String[][]
+ {
+ // a record folder
+ { heldRecordFolder.getId(), heldRecordFolder.getName(), recordFolderPath },
+ // a record
+ { heldRecord.getId(), heldRecord.getName(), recordPath },
+ //an active content,
+ { heldContent.getNodeRefWithoutVersion(), heldContent.getName(), contentPath }
+ };
+ }
+
+ /**
+ * Given a document/record/record folder is removed from a hold
+ * When I view the audit log
+ * Then an entry has been created in the audit log that contains the following:
+ * name of the hold
+ * name of the document/record/record folder removed
+ * user who removed the content
+ * date the content was removed
+ * path of the node
+ */
+ @Test (dataProvider = "validNodesForRemoveFromHold")
+ public void removeFromHoldEventIsAudited(String nodeId, String nodeName, String nodePath)
+ {
+ rmAuditService.clearAuditLog();
+
+ STEP("Remove node from hold.");
+ holdsAPI.removeItemFromHold(rmAdmin.getUsername(), rmAdmin.getPassword(), nodeId, HOLD3);
+
+ STEP("Check the audit log contains the entry for the remove from hold event.");
+ rmAuditService.checkAuditLogForEvent(getAdminUser(), REMOVE_FROM_HOLD, rmAdmin, nodeName, nodePath,
+ asList(ImmutableMap.of("new", "", "previous", nodeName, "name", "Name"),
+ ImmutableMap.of("new", "", "previous", HOLD3, "name", "Hold Name")));
+ }
+
+ /**
+ * Given an unsuccessful remove from hold action
+ * When I view the audit log
+ * Then the remove from hold event isn't audited
+ */
+ @Test
+ public void unsuccessfulRemoveFromHoldIsNotAudited()
+ {
+ rmAuditService.clearAuditLog();
+
+ STEP("Try to remove the record from a hold by an user with no rights.");
+ holdsAPI.removeItemsFromHolds(rmManagerNoReadOnHold.getUsername(), rmManagerNoReadOnHold.getPassword(),
+ SC_INTERNAL_SERVER_ERROR, Collections.singletonList(heldRecord.getId()),
+ Collections.singletonList(hold1NodeRef));
+
+ STEP("Check the audit log doesn't contain the entry for the unsuccessful remove from hold.");
+ assertTrue("The list of events should not contain remove from hold entry ",
+ rmAuditService.getAuditEntriesFilteredByEvent(getAdminUser(), REMOVE_FROM_HOLD).isEmpty());
+ }
+
+ /**
+ * Given a not empty record folder is removed from a hold
+ * When I view the audit log
+ * Then only an entry has been created in the audit log for the record folder removed
+ */
+ @Test
+ public void removeFromHoldNotAuditedForRecordFolderChildren() throws Exception
+ {
+ STEP("Create a new record folder with a record inside");
+ RecordCategoryChild notEmptyRecFolder = createRecordFolder(recordCategory.getId(), PREFIX + "notEmptyRecFolder");
+ Record record = createElectronicRecord(notEmptyRecFolder.getId(), PREFIX + "record");
+
+ STEP("Add the record folder to a hold.");
+ holdsAPI.addItemToHold(rmAdmin.getUsername(), rmAdmin.getPassword(), notEmptyRecFolder.getId(), HOLD1);
+
+ rmAuditService.clearAuditLog();
+
+ STEP("Remove record folder from hold.");
+ holdsAPI.removeItemFromHold(rmAdmin.getUsername(), rmAdmin.getPassword(), notEmptyRecFolder.getId(), HOLD1);
+
+ STEP("Get the list of audit entries for the remove from hold event.");
+ auditEntries = rmAuditService.getAuditEntriesFilteredByEvent(getAdminUser(), REMOVE_FROM_HOLD);
+
+ STEP("Check the audit log contains only an entry for remove from hold.");
+ assertEquals("The list of events should contain only an entry", 1, auditEntries.size());
+ assertTrue("The list of events should not contain Remove from Hold entry for the record",
+ auditEntries.stream().noneMatch(entry -> entry.getNodeName().equals(record.getName())));
+ }
+
+ /**
+ * Given a record folder is removed from multiple holds
+ * When I view the audit log
+ * Then multiple entries have been created in the audit log for each remove from hold event
+ */
+ @Test
+ public void removeFromHoldIsAuditedInBulkRemoval()
+ {
+ rmAuditService.clearAuditLog();
+
+ STEP("Remove record folder from multiple holds.");
+ holdsAPI.removeItemsFromHolds(rmAdmin.getUsername(), rmAdmin.getPassword(),
+ Collections.singletonList(heldRecordFolder.getId()), asList(HOLD1, HOLD2));
+
+ STEP("Get the list of audit entries for the remove from hold event.");
+ auditEntries = rmAuditService.getAuditEntriesFilteredByEvent(getAdminUser(), REMOVE_FROM_HOLD);
+
+ STEP("Check the audit log contains entries for both removal.");
+ assertEquals("The list of events should contain remove from Hold entries for both holds", 2,
+ auditEntries.size());
+ assertTrue("The hold name value for the first remove from hold is not audited.",
+ auditEntries.stream().anyMatch(entry -> entry.getChangedValues().contains(
+ ImmutableMap.of("new", "", "previous", HOLD1, "name", "Hold Name"))));
+ assertTrue("The hold name value for the second remove from hold is not audited.",
+ auditEntries.stream().anyMatch(entry -> entry.getChangedValues().contains(
+ ImmutableMap.of("new", "", "previous", HOLD2, "name", "Hold Name"))));
+ }
+
+ /**
+ * Given a document/record/record folder is removed from a hold
+ * When I view the audit log as an user with no Read permissions over the node
+ * Then the remove from hold entry isn't visible
+ */
+ @Test
+ public void removeFromHoldAuditEntryNotVisible()
+ {
+ STEP("Add content to a hold.");
+ FileModel heldFile = dataContent.usingAdmin().usingSite(privateSite)
+ .createContent(CMISUtil.DocumentType.TEXT_PLAIN);
+ holdsAPI.addItemToHold(rmAdmin.getUsername(), rmAdmin.getPassword(), heldFile.getNodeRefWithoutVersion(), HOLD1);
+
+ rmAuditService.clearAuditLog();
+
+ STEP("Remove held content from the hold.");
+ holdsAPI.removeItemFromHold(rmAdmin.getUsername(), rmAdmin.getPassword(), heldFile.getNodeRefWithoutVersion(), HOLD1);
+
+ STEP("Check that an user with no Read permissions can't see the entry for the remove from hold event.");
+ assertTrue("The list of events should not contain Remove from Hold entry ",
+ rmAuditService.getAuditEntriesFilteredByEvent(rmManagerNoReadOnNode, REMOVE_FROM_HOLD).isEmpty());
+ }
+
+ /**
+ * Given a document/record/record folder is removed from a hold
+ * When I view the audit log as an user with no Read permissions over the hold
+ * Then the the hold name is replaced in the remove from hold entry
+ */
+ @Test
+ public void removeFromHoldAuditEntryHoldNameNotVisible()
+ {
+ STEP("Add content to a hold.");
+ FileModel heldFile = dataContent.usingAdmin().usingSite(privateSite)
+ .createContent(CMISUtil.DocumentType.TEXT_PLAIN);
+ holdsAPI.addItemToHold(rmAdmin.getUsername(), rmAdmin.getPassword(), heldFile.getNodeRefWithoutVersion(), HOLD1);
+
+ rmAuditService.clearAuditLog();
+
+ STEP("Remove held content from the hold.");
+ holdsAPI.removeItemFromHold(rmAdmin.getUsername(), rmAdmin.getPassword(), heldFile.getNodeRefWithoutVersion(), HOLD1);
+
+ auditEntries = rmAuditService.getAuditEntriesFilteredByEvent(rmManagerNoReadOnHold, REMOVE_FROM_HOLD);
+
+ STEP("Check that an user with no Read permissions can't see the hold name in the remove from hold event.");
+ String replacementHoldName = "You don't have permission to view this hold.";
+ assertEquals("The list of events should contain the Remove from Hold entry", 1, auditEntries.size());
+ assertTrue("The hold name should not be visible in the Remove from Hold entry ",
+ auditEntries.stream().anyMatch(entry -> entry.getChangedValues().contains(
+ ImmutableMap.of("new", "", "previous", replacementHoldName, "name", "Hold Name"))));
+ }
+
+ @AfterClass (alwaysRun = true)
+ public void cleanUpAuditRemoveFromHoldTests()
+ {
+ holdsListRef.forEach(holdRef -> holdsAPI.deleteHold(getAdminUser(), holdRef));
+ dataSite.usingAdmin().deleteSite(privateSite);
+ asList(rmAdmin, rmManagerNoReadOnHold, rmManagerNoReadOnNode).forEach(user -> getDataUser().usingAdmin().deleteUser(user));
+ getRestAPIFactory().getRecordCategoryAPI().deleteRecordCategory(recordCategory.getId());
+ }
+}
diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-audit-context.xml b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-audit-context.xml
index d8033cc8a1..3596e79165 100644
--- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-audit-context.xml
+++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-audit-context.xml
@@ -130,4 +130,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/audit/event/AddToHoldAuditEvent.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/audit/event/AddToHoldAuditEvent.java
new file mode 100644
index 0000000000..34fb578d5c
--- /dev/null
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/audit/event/AddToHoldAuditEvent.java
@@ -0,0 +1,85 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2021 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail. Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco 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 Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+
+package org.alfresco.module.org_alfresco_module_rm.audit.event;
+
+import static org.alfresco.repo.policy.Behaviour.NotificationFrequency.EVERY_EVENT;
+
+import java.io.Serializable;
+import java.util.Map;
+
+import org.alfresco.model.ContentModel;
+import org.alfresco.module.org_alfresco_module_rm.hold.HoldServicePolicies;
+import org.alfresco.repo.policy.annotation.Behaviour;
+import org.alfresco.repo.policy.annotation.BehaviourBean;
+import org.alfresco.repo.policy.annotation.BehaviourKind;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.cmr.repository.NodeService;
+import org.alfresco.service.namespace.QName;
+
+/**
+ * Add to hold audit event.
+ *
+ * @author Sara Aspery
+ * @since 3.3
+ */
+@BehaviourBean
+public class AddToHoldAuditEvent extends AuditEvent implements HoldServicePolicies.OnAddToHoldPolicy
+{
+ /**
+ * Node Service
+ */
+ private NodeService nodeService;
+
+ /**
+ * Sets the node service
+ *
+ * @param nodeService nodeService to set
+ */
+ public void setNodeService(NodeService nodeService)
+ {
+ this.nodeService = nodeService;
+ }
+
+ /**
+ * @see org.alfresco.module.org_alfresco_module_rm.hold.HoldServicePolicies.OnAddToHoldPolicy#onAddToHold(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.repository.NodeRef)
+ */
+ @Override
+ @Behaviour
+ (
+ kind = BehaviourKind.CLASS,
+ type = "rma:hold",
+ notificationFrequency = EVERY_EVENT
+ )
+ public void onAddToHold(NodeRef holdNodeRef, NodeRef contentNodeRef)
+ {
+ Map auditProperties = HoldUtils.makePropertiesMap(holdNodeRef, nodeService);
+ auditProperties.put(ContentModel.PROP_NAME, nodeService.getProperty(contentNodeRef, ContentModel.PROP_NAME));
+
+ recordsManagementAuditService.auditEvent(contentNodeRef, getName(), null, auditProperties, true, false);
+ }
+}
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/audit/event/CreateHoldAuditEvent.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/audit/event/CreateHoldAuditEvent.java
new file mode 100644
index 0000000000..85bf2e38b8
--- /dev/null
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/audit/event/CreateHoldAuditEvent.java
@@ -0,0 +1,88 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2021 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail. Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco 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 Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+
+package org.alfresco.module.org_alfresco_module_rm.audit.event;
+
+import java.io.Serializable;
+import java.util.Map;
+
+import org.alfresco.repo.node.NodeServicePolicies;
+import org.alfresco.repo.policy.Behaviour.NotificationFrequency;
+import org.alfresco.repo.policy.annotation.Behaviour;
+import org.alfresco.repo.policy.annotation.BehaviourBean;
+import org.alfresco.repo.policy.annotation.BehaviourKind;
+import org.alfresco.service.cmr.repository.ChildAssociationRef;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.cmr.repository.NodeService;
+import org.alfresco.service.namespace.QName;
+
+/**
+ * Create hold audit event.
+ * This listens to the NodeServicePolicies.OnCreateNodePolicy in order to cover the create hold action from Share
+ * since that does not call the createHold from HoldService
+ *
+ * @author Sara Aspery
+ * @since 3.3
+ */
+@BehaviourBean
+public class CreateHoldAuditEvent extends AuditEvent implements NodeServicePolicies.OnCreateNodePolicy
+{
+ /**
+ * Node Service
+ */
+ private NodeService nodeService;
+
+ /**
+ * Sets the node service
+ *
+ * @param nodeService nodeService to set
+ */
+ public void setNodeService(NodeService nodeService)
+ {
+ this.nodeService = nodeService;
+ }
+
+ /**
+ * @see org.alfresco.repo.node.NodeServicePolicies.OnCreateNodePolicy#onCreateNode(org.alfresco.service.cmr.repository.ChildAssociationRef)
+ */
+ @Override
+ @Behaviour
+ (
+ kind = BehaviourKind.CLASS,
+ type = "rma:hold",
+ notificationFrequency = NotificationFrequency.TRANSACTION_COMMIT
+ )
+ public void onCreateNode(ChildAssociationRef childAssociationRef)
+ {
+ NodeRef holdNodeRef = childAssociationRef.getChildRef();
+
+ Map auditProperties = HoldUtils.makePropertiesMap(holdNodeRef, nodeService);
+ auditProperties.put(PROP_HOLD_REASON, nodeService.getProperty(holdNodeRef, PROP_HOLD_REASON));
+
+ recordsManagementAuditService.auditEvent(holdNodeRef, getName(), null, auditProperties);
+ }
+}
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/audit/event/DeleteHoldAuditEvent.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/audit/event/DeleteHoldAuditEvent.java
new file mode 100644
index 0000000000..6699ff629c
--- /dev/null
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/audit/event/DeleteHoldAuditEvent.java
@@ -0,0 +1,82 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2021 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail. Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco 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 Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+
+package org.alfresco.module.org_alfresco_module_rm.audit.event;
+
+import static org.alfresco.repo.policy.Behaviour.NotificationFrequency.EVERY_EVENT;
+
+import java.io.Serializable;
+import java.util.Map;
+
+import org.alfresco.repo.node.NodeServicePolicies;
+import org.alfresco.repo.policy.annotation.Behaviour;
+import org.alfresco.repo.policy.annotation.BehaviourBean;
+import org.alfresco.repo.policy.annotation.BehaviourKind;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.cmr.repository.NodeService;
+import org.alfresco.service.namespace.QName;
+
+/**
+ * Delete hold audit event.
+ * This listens to the NodeServicePolicies.BeforeDeleteNodePolicy in order to cover the delete hold using nodes service
+ *
+ * @author Sara Aspery
+ * @since 3.3
+ */
+@BehaviourBean
+public class DeleteHoldAuditEvent extends AuditEvent implements NodeServicePolicies.BeforeDeleteNodePolicy
+{
+ /**
+ * Node Service
+ */
+ private NodeService nodeService;
+
+ /**
+ * Sets the node service
+ *
+ * @param nodeService nodeService to set
+ */
+ public void setNodeService(NodeService nodeService)
+ {
+ this.nodeService = nodeService;
+ }
+
+ /**
+ * @see org.alfresco.repo.node.NodeServicePolicies.BeforeDeleteNodePolicy#beforeDeleteNode(org.alfresco.service.cmr.repository.NodeRef)
+ */
+ @Override
+ @Behaviour (
+ kind = BehaviourKind.CLASS,
+ type = "rma:hold",
+ notificationFrequency = EVERY_EVENT
+ )
+ public void beforeDeleteNode(NodeRef holdNodeRef)
+ {
+ Map auditProperties = HoldUtils.makePropertiesMap(holdNodeRef, nodeService);
+ recordsManagementAuditService.auditEvent(holdNodeRef, getName(), auditProperties, null, true, false);
+ }
+}
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/audit/event/HoldUtils.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/audit/event/HoldUtils.java
new file mode 100644
index 0000000000..23b225ef8c
--- /dev/null
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/audit/event/HoldUtils.java
@@ -0,0 +1,69 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2021 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail. Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco 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 Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+
+package org.alfresco.module.org_alfresco_module_rm.audit.event;
+
+import org.alfresco.model.ContentModel;
+import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.cmr.repository.NodeService;
+import org.alfresco.service.namespace.QName;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Utility class for creating audit events about holds.
+ *
+ * @author Sara Aspery
+ * @since 3.3
+ */
+class HoldUtils
+{
+ /** A QName to display for the hold name. */
+ public static final QName HOLD_NAME = QName.createQName(RecordsManagementModel.RM_URI, "Hold Name");
+ /** A QName to display for the hold node ref. */
+ public static final QName HOLD_NODEREF = QName.createQName(RecordsManagementModel.RM_URI, "Hold NodeRef");
+
+ /**
+ * Create a properties map containing the hold name and node ref for the given hold.
+ *
+ * @param nodeRef The nodeRef of the hold.
+ * @param nodeService The node service.
+ * @return A map containing the name and noderef of the hold.
+ */
+ static Map makePropertiesMap(NodeRef nodeRef, NodeService nodeService)
+ {
+ Map auditProperties = new HashMap<>();
+
+ auditProperties.put(HOLD_NAME, nodeService.getProperty(nodeRef, ContentModel.PROP_NAME));
+ auditProperties.put(HOLD_NODEREF, nodeRef);
+
+ return auditProperties;
+ }
+}
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/audit/event/RemoveFromHoldAuditEvent.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/audit/event/RemoveFromHoldAuditEvent.java
new file mode 100644
index 0000000000..d9e2111fa3
--- /dev/null
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/audit/event/RemoveFromHoldAuditEvent.java
@@ -0,0 +1,84 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2021 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail. Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco 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 Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+
+package org.alfresco.module.org_alfresco_module_rm.audit.event;
+
+import java.io.Serializable;
+import java.util.Map;
+
+import org.alfresco.model.ContentModel;
+import org.alfresco.module.org_alfresco_module_rm.hold.HoldServicePolicies;
+import org.alfresco.repo.policy.Behaviour.NotificationFrequency;
+import org.alfresco.repo.policy.annotation.Behaviour;
+import org.alfresco.repo.policy.annotation.BehaviourBean;
+import org.alfresco.repo.policy.annotation.BehaviourKind;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.cmr.repository.NodeService;
+import org.alfresco.service.namespace.QName;
+
+/**
+ * Delete from hold audit event.
+ *
+ * @author Chris Shields
+ * @since 3.3
+ */
+@BehaviourBean
+public class RemoveFromHoldAuditEvent extends AuditEvent implements HoldServicePolicies.OnRemoveFromHoldPolicy
+{
+ /**
+ * Node Service
+ */
+ private NodeService nodeService;
+
+ /**
+ * Sets the node service
+ *
+ * @param nodeService nodeService to set
+ */
+ public void setNodeService(NodeService nodeService)
+ {
+ this.nodeService = nodeService;
+ }
+
+ /**
+ * @see org.alfresco.module.org_alfresco_module_rm.hold.HoldServicePolicies.OnRemoveFromHoldPolicy#onRemoveFromHold(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.repository.NodeRef)
+ */
+ @Override
+ @Behaviour
+ (
+ kind = BehaviourKind.CLASS,
+ type = "rma:hold",
+ notificationFrequency = NotificationFrequency.EVERY_EVENT
+ )
+ public void onRemoveFromHold(NodeRef holdNodeRef, NodeRef contentNodeRef)
+ {
+ Map auditProperties = HoldUtils.makePropertiesMap(holdNodeRef, nodeService);
+ auditProperties.put(ContentModel.PROP_NAME, nodeService.getProperty(contentNodeRef, ContentModel.PROP_NAME));
+
+ recordsManagementAuditService.auditEvent(contentNodeRef, getName(), auditProperties, null, true);
+ }
+}
diff --git a/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/audit/event/AddToHoldAuditEventUnitTest.java b/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/audit/event/AddToHoldAuditEventUnitTest.java
new file mode 100644
index 0000000000..11159f2d1c
--- /dev/null
+++ b/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/audit/event/AddToHoldAuditEventUnitTest.java
@@ -0,0 +1,92 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2021 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail. Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco 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 Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+
+package org.alfresco.module.org_alfresco_module_rm.audit.event;
+
+import org.alfresco.module.org_alfresco_module_rm.test.util.BaseUnitTest;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.cmr.repository.NodeService;
+import org.alfresco.util.GUID;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+
+import java.util.Map;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.isNull;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+/**
+ * Unit tests for {@link AddToHoldAuditEvent}.
+ *
+ * @author Sara Aspery
+ * @since 3.3
+ */
+public class AddToHoldAuditEventUnitTest extends BaseUnitTest
+{
+ @InjectMocks
+ private AddToHoldAuditEvent addToHoldAuditEvent;
+
+ @Mock
+ private NodeService mockedNodeService;
+
+ private NodeRef holdNodeRef;
+ private NodeRef contentNodeRef;
+
+ /** Set up the mocks. */
+ @Before
+ public void setUp()
+ {
+ initMocks(this);
+
+ holdNodeRef = generateNodeRef();
+ String holdName = "Hold " + GUID.generate();
+
+ contentNodeRef = generateNodeRef();
+ String contentName = "Content " + GUID.generate();
+
+ when(mockedNodeService.getProperty(holdNodeRef, PROP_NAME)).thenReturn(holdName);
+ when(mockedNodeService.getProperty(contentNodeRef, PROP_NAME)).thenReturn(contentName);
+ }
+
+ /**
+ * Check that the add to hold event calls an audit event.
+ *
+ */
+ @Test
+ public void testAddToHoldCausesAuditEvent()
+ {
+ addToHoldAuditEvent.onAddToHold(holdNodeRef, contentNodeRef);
+ verify(mockedRecordsManagementAuditService, times(1)).auditEvent(eq(contentNodeRef), any(String.class), isNull(Map.class), any(Map.class), eq(true), eq(false));
+ }
+}
diff --git a/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/audit/event/CreateHoldAuditEventUnitTest.java b/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/audit/event/CreateHoldAuditEventUnitTest.java
new file mode 100644
index 0000000000..f41c321fd0
--- /dev/null
+++ b/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/audit/event/CreateHoldAuditEventUnitTest.java
@@ -0,0 +1,93 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2021 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail. Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco 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 Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+
+package org.alfresco.module.org_alfresco_module_rm.audit.event;
+
+import org.alfresco.module.org_alfresco_module_rm.test.util.BaseUnitTest;
+import org.alfresco.service.cmr.repository.ChildAssociationRef;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.cmr.repository.NodeService;
+import org.alfresco.util.GUID;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+
+import java.util.Map;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.isNull;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+/**
+ * Unit tests for {@link CreateHoldAuditEvent}.
+ *
+ * @author Sara Aspery
+ * @since 3.3
+ */
+public class CreateHoldAuditEventUnitTest extends BaseUnitTest
+{
+ @InjectMocks
+ private CreateHoldAuditEvent createHoldAuditEvent;
+
+ @Mock
+ private NodeService mockedNodeService;
+
+ private NodeRef holdNodeRef;
+ private ChildAssociationRef childAssociationRef;
+
+ /** Set up the mocks. */
+ @Before
+ public void setUp()
+ {
+ initMocks(this);
+
+ holdNodeRef = generateNodeRef();
+ String holdName = "Hold " + GUID.generate();
+ String holdReason = "Reason " + GUID.generate();
+ childAssociationRef = generateChildAssociationRef(null, holdNodeRef);
+
+ when(childAssociationRef.getChildRef()).thenReturn(holdNodeRef);
+ when(mockedNodeService.getProperty(holdNodeRef, PROP_NAME)).thenReturn(holdName);
+ when(mockedNodeService.getProperty(holdNodeRef, PROP_HOLD_REASON)).thenReturn(holdReason);
+ }
+
+ /**
+ * Check that the create hold event calls an audit event.
+ *
+ */
+ @Test
+ public void testCreateHoldCausesAuditEvent()
+ {
+ createHoldAuditEvent.onCreateNode(childAssociationRef);
+ verify(mockedRecordsManagementAuditService, times(1)).auditEvent(eq(holdNodeRef), any(String.class), isNull(Map.class), any(Map.class));
+ }
+}
diff --git a/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/audit/event/DeleteHoldAuditEventUnitTest.java b/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/audit/event/DeleteHoldAuditEventUnitTest.java
new file mode 100644
index 0000000000..8ef99fe9b6
--- /dev/null
+++ b/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/audit/event/DeleteHoldAuditEventUnitTest.java
@@ -0,0 +1,89 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2021 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail. Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco 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 Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+
+ package org.alfresco.module.org_alfresco_module_rm.audit.event;
+
+import org.alfresco.module.org_alfresco_module_rm.test.util.BaseUnitTest;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.cmr.repository.NodeService;
+import org.alfresco.util.GUID;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Matchers;
+import org.mockito.Mock;
+
+import java.util.Map;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.isNull;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+/**
+ * Unit tests for {@link DeleteHoldAuditEvent}.
+ *
+ * @author Sara Aspery
+ * @since 3.3
+ */
+public class DeleteHoldAuditEventUnitTest extends BaseUnitTest
+{
+ @InjectMocks
+ private DeleteHoldAuditEvent deleteHoldAuditEvent;
+
+ @Mock
+ private NodeService mockedNodeService;
+
+ private NodeRef holdNodeRef;
+
+ /** Set up the mocks. */
+ @Before
+ public void setUp()
+ {
+ initMocks(this);
+
+ holdNodeRef = generateNodeRef();
+ String holdName = "Hold " + GUID.generate();
+
+ when(mockedNodeService.getProperty(holdNodeRef, PROP_NAME)).thenReturn(holdName);
+ }
+
+ /**
+ * Check that the delete hold event calls an audit event.
+ *
+ */
+ @Test
+ public void testDeleteHoldCausesAuditEvent()
+ {
+ deleteHoldAuditEvent.beforeDeleteNode(holdNodeRef);
+ verify(mockedRecordsManagementAuditService, times(1))
+ .auditEvent(eq(holdNodeRef), any(String.class), any(Map.class), isNull(Map.class), Matchers.eq(true), Matchers.eq(false));
+ }
+}
diff --git a/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/audit/event/RemoveFromHoldAuditEventUnitTest.java b/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/audit/event/RemoveFromHoldAuditEventUnitTest.java
new file mode 100644
index 0000000000..2ed91b0e96
--- /dev/null
+++ b/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/audit/event/RemoveFromHoldAuditEventUnitTest.java
@@ -0,0 +1,95 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2021 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail. Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco 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 Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+
+package org.alfresco.module.org_alfresco_module_rm.audit.event;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.isNull;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import java.util.Map;
+
+import org.alfresco.module.org_alfresco_module_rm.test.util.BaseUnitTest;
+import org.alfresco.service.cmr.repository.ChildAssociationRef;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.cmr.repository.NodeService;
+import org.alfresco.util.GUID;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+
+/**
+ * Unit tests for {@link RemoveFromHoldAuditEvent}.
+ *
+ * @author Chris Shields
+ * @since 3.3
+ */
+public class RemoveFromHoldAuditEventUnitTest extends BaseUnitTest
+{
+ @InjectMocks
+ private RemoveFromHoldAuditEvent removeFromHoldAuditEvent;
+
+ @Mock
+ private NodeService mockedNodeService;
+
+ private NodeRef holdNodeRef;
+ private NodeRef contentNodeRef;
+
+ /**
+ * Set up the mocks.
+ */
+ @Before
+ public void setUp()
+ {
+ initMocks(this);
+
+ holdNodeRef = generateNodeRef();
+ String holdName = "Hold " + GUID.generate();
+
+ contentNodeRef = generateNodeRef();
+ String contentName = "Content " + GUID.generate();
+
+ when(mockedNodeService.getProperty(holdNodeRef, PROP_NAME)).thenReturn(holdName);
+ when(mockedNodeService.getProperty(contentNodeRef, PROP_NAME)).thenReturn(contentName);
+ }
+
+ /**
+ * Check that the remove from hold event calls an audit event.
+ */
+ @Test
+ public void testRemoveFromHoldCausesAuditEvent()
+ {
+ removeFromHoldAuditEvent.onRemoveFromHold(holdNodeRef, contentNodeRef);
+ verify(mockedRecordsManagementAuditService, times(1)).auditEvent(eq(contentNodeRef), any(String.class), any(Map.class), isNull(Map.class), eq(true));
+ }
+
+}