Merge branch 'master' into feature/RM-7047_LinkToHeldItem

* master:
  fix test
  removed test as per RM-7061 resolution
  small fixes
  RM-6930 another review update, correct typo in existing message
  RM-6930 review updates
  RM-6930 review updates
  Code review comments (check node path for add to hold and remove from hold audit entries)
  Code review comments
  Delete hold using RM Actions API
  Changed the audited node
  replaced the todos
  Sonar: Format specifiers should be used instead of string concatenation. Sonar: Replace the type specification in this constructor call with the diamond operator ("<>")
  removed unused imports
  Refactored audit tests and holds api
  update method names
  RM-7033 Automate tests for Audit create, delete, add to, and remove from holds
  removed duplicate methods
  RM-6930 adding permissions check for active content in holds defore deletion
This commit is contained in:
Ramona Popa
2019-11-22 11:27:26 +02:00
18 changed files with 1318 additions and 196 deletions

View File

@@ -103,7 +103,7 @@ public abstract class BaseAPI
*/
protected List<String> getPropertyValues(JSONObject result, String propertyName)
{
ArrayList<String> results = new ArrayList<String>();
ArrayList<String> results = new ArrayList<>();
try
{
JSONArray items = result.getJSONArray("items");
@@ -541,7 +541,6 @@ public abstract class BaseAPI
AlfrescoHttpClient client = alfrescoHttpClientFactory.getObject();
T request = requestType.newInstance();
HttpResponse response = null;
JSONObject responseBody = null;
JSONObject returnValues = null;
@@ -555,7 +554,7 @@ public abstract class BaseAPI
}
LOGGER.info("Sending {} request to {}", requestType.getSimpleName(), requestUrl);
LOGGER.info("Request body: {}", requestParams);
response = client.execute(adminUser, adminPassword, request);
HttpResponse response = client.execute(adminUser, adminPassword, request);
LOGGER.info("Response: {}", response.getStatusLine());
try
@@ -587,13 +586,13 @@ public abstract class BaseAPI
case HttpStatus.SC_UNPROCESSABLE_ENTITY:
if (responseBody != null && responseBody.has(EXCEPTION_KEY))
{
LOGGER.error("Request failed: " + responseBody.getString(EXCEPTION_KEY));
LOGGER.error("Request failed: {}", responseBody.getString(EXCEPTION_KEY));
returnValues = responseBody;
}
break;
default:
LOGGER.error("Request returned unexpected HTTP status " + response.getStatusLine().getStatusCode());
LOGGER.error("Request returned unexpected HTTP status {}", response.getStatusLine().getStatusCode());
break;
}
}

View File

@@ -41,7 +41,11 @@ public enum AuditEvents
DELETE_USER_GROUP("Delete User Group", "Delete User Group"),
ADD_TO_USER_GROUP("Add To User Group", "Add To User Group"),
REMOVE_FROM_USER_GROUP("Remove From User Group", "Remove From User Group"),
LOGIN_UNSUCCESSFUL("Login.Failure", "Login Unsuccessful");
LOGIN_UNSUCCESSFUL("Login.Failure", "Login Unsuccessful"),
CREATE_HOLD("Create Hold", "Create Hold"),
DELETE_HOLD("Delete Hold", "Delete Hold"),
ADD_TO_HOLD("Add To Hold", "Add To Hold"),
REMOVE_FROM_HOLD("Remove From Hold", "Remove From Hold");
/** event audited */
public final String event;

View File

@@ -28,7 +28,6 @@ package org.alfresco.rest.v0;
import static org.alfresco.rest.core.v0.APIUtils.convertHTTPResponseToJSON;
import static org.apache.http.HttpStatus.SC_OK;
import static org.testng.AssertJUnit.assertNotNull;
import java.text.MessageFormat;
import java.util.Collections;
@@ -39,8 +38,7 @@ import org.alfresco.rest.core.v0.APIUtils;
import org.alfresco.rest.core.v0.BaseAPI;
import org.alfresco.rest.rm.community.model.hold.HoldEntry;
import org.alfresco.rest.rm.community.util.PojoUtility;
import org.alfresco.utility.Utility;
import org.apache.chemistry.opencmis.client.api.CmisObject;
import org.alfresco.utility.model.UserModel;
import org.apache.http.HttpResponse;
import org.apache.http.ParseException;
import org.json.JSONArray;
@@ -81,18 +79,27 @@ public class HoldsAPI extends BaseAPI
* @param holdName the hold name
* @param reason hold reason
* @param description hold description
* @return The HTTP response (or null if no POST call was needed).
* @return The HTTP response.
*/
public HttpResponse createHold(String user, String password,
String holdName, String reason, String description)
public HttpResponse createHold(String user, String password, String holdName, String reason, String description)
{
return createHold(user, password, holdName, reason, description, SC_OK);
}
/**
* Util method to create a hold
*
* @param user the user creating the hold
* @param password the user's password
* @param holdName the hold name
* @param reason hold reason
* @param description hold description
* @param expectedStatusCode The expected return status code.
* @return The HTTP response or throws AssertionError if the returned status code is not as expected.
*/
public HttpResponse createHold(String user, String password, String holdName, String reason, String description,
int expectedStatusCode)
{
// if the hold already exists don't try to create it again
final String fullHoldPath = Utility.buildPath(getFilePlanPath(), HOLDS_CONTAINER) + holdName;
final CmisObject hold = getObjectByPath(user, password, fullHoldPath);
if (hold != null)
{
return null;
}
// retrieve the Holds container nodeRef
final String parentNodeRef = getItemNodeRef(user, password, "/" + HOLDS_CONTAINER);
@@ -102,11 +109,7 @@ public class HoldsAPI extends BaseAPI
requestParams.put("prop_cm_description", description);
requestParams.put("prop_rma_holdReason", reason);
// Make the POST request and throw an assertion error if it fails.
final HttpResponse httpResponse = doPostJsonRequest(user, password, SC_OK, requestParams, CREATE_HOLDS_API);
assertNotNull("Expected object to have been created at " + fullHoldPath,
getObjectByPath(user, password, fullHoldPath));
return httpResponse;
return doPostJsonRequest(user, password, expectedStatusCode, requestParams, CREATE_HOLDS_API);
}
/**
@@ -142,7 +145,36 @@ public class HoldsAPI extends BaseAPI
}
/**
* Deletes hold
* Deletes hold using RM Actions API and expect action to be successful
*
* @param user the user who does the request
* @param holdNodeRef the hold node ref
* @return The HTTP Response or throws AssertionError if the request is not successful.
*/
public HttpResponse deleteHold(UserModel user, String holdNodeRef)
{
return deleteHold(user.getUsername(), user.getPassword(), holdNodeRef, SC_OK);
}
/**
* Deletes hold using RM Actions API and expect a specific status code
*
* @param username user's username
* @param password its password
* @param holdNodeRef the hold node ref
* @return The HTTP Response or throws AssertionError if the returned status code is not as expected.
*/
public HttpResponse deleteHold(String username, String password, String holdNodeRef, int expectedStatusCode)
{
JSONObject requestParams = new JSONObject();
requestParams.put("name", "deleteHold");
requestParams.put("nodeRef", getNodeRefSpacesStore() + holdNodeRef);
return doPostJsonRequest(username, password, expectedStatusCode, requestParams, RM_ACTIONS_API);
}
/**
* Deletes hold using cmis
*
* @param username user's username
* @param password its password
@@ -179,7 +211,10 @@ public class HoldsAPI extends BaseAPI
*/
public HttpResponse addItemsToHolds(String user, String password, List<String> itemNodeRefs, List<String> holdNames)
{
return addItemsToHolds(user, password, SC_OK, itemNodeRefs, holdNames);
final List<String> holdNodeRefs = holdNames.stream()
.map(hold -> getItemNodeRef(user, password, String.format("/%s/%s", HOLDS_CONTAINER, hold)))
.collect(Collectors.toList());
return addItemsToHolds(user, password, SC_OK, itemNodeRefs, holdNodeRefs);
}
/**
@@ -188,13 +223,13 @@ public class HoldsAPI extends BaseAPI
* @param user the user who adds the items to the holds
* @param password the user's password
* @param itemNodeRefs the list of items nodeRefs to be added to holds
* @param holdNames the list of holds
* @param holdNodeRefs the list of holds
* @return The HTTP response
*/
public HttpResponse addItemsToHolds(String user, String password, int expectedStatus, List<String> itemNodeRefs,
List<String> holdNames)
List<String> holdNodeRefs)
{
final JSONObject requestParams = addOrRemoveToFromHoldJsonObject(user, password, itemNodeRefs, holdNames);
final JSONObject requestParams = addOrRemoveToFromHoldJsonObject(itemNodeRefs, holdNodeRefs);
return doPostJsonRequest(user, password, expectedStatus, requestParams, RM_HOLDS_API);
}
@@ -204,35 +239,30 @@ public class HoldsAPI extends BaseAPI
* @param user the user who adds the item to the hold
* @param password the user's password
* @param itemNodeRef the nodeRef of the item to be added to hold
* @param holdName the hold name
* @param holdNodeRef the hold node ref
* @return The error message
*/
public String addToHoldAndGetMessage(String user, String password, int expectedStatus, String itemNodeRef, String
holdName)
holdNodeRef)
{
final HttpResponse httpResponse = addItemsToHolds(user, password, expectedStatus, Collections.singletonList(itemNodeRef),
Collections.singletonList(holdName));
Collections.singletonList(holdNodeRef));
return APIUtils.extractErrorMessageFromHttpResponse(httpResponse);
}
/**
* Util method to create the request body used when adding items to holds or when removing items from holds
*
* @param user user to create the request body for add/remove an item to/from hold
* @param password the user's password
* @param items list of items node refs to be added to holds
* @param holdNames list of hold names for add/remove items
* @param items list of items node refs to be added to holds
* @param holdNodeRefs list of hold node refs for add/remove items
* @return JSONObject fo
*/
private JSONObject addOrRemoveToFromHoldJsonObject(String user, String password, List<String> items, List<String> holdNames)
private JSONObject addOrRemoveToFromHoldJsonObject(List<String> items, List<String> holdNodeRefs)
{
final JSONArray nodeRefs = new JSONArray();
items.forEach(itemNodeRef -> nodeRefs.put(getNodeRefSpacesStore() + itemNodeRef));
final List<String> holdNodeRefs = holdNames.stream().map(hold ->
getNodeRefSpacesStore() + getItemNodeRef(user, password, String.format("/%s/%s", HOLDS_CONTAINER, hold)))
.collect(Collectors.toList());
final JSONArray holds = new JSONArray();
holdNodeRefs.forEach(holds::put);
holdNodeRefs.forEach(holdNodeRef -> holds.put(getNodeRefSpacesStore() + holdNodeRef));
final JSONObject requestParams = new JSONObject();
requestParams.put("nodeRefs", nodeRefs);
requestParams.put("holds", holds);
@@ -264,7 +294,10 @@ public class HoldsAPI extends BaseAPI
*/
public HttpResponse removeItemsFromHolds(String user, String password, List<String> itemNodeRefs, List<String> holdNames)
{
return removeItemsFromHolds(user, password, SC_OK, itemNodeRefs, holdNames);
final List<String> holdNodeRefs = holdNames.stream()
.map(hold -> getItemNodeRef(user, password, String.format("/%s/%s", HOLDS_CONTAINER, hold)))
.collect(Collectors.toList());
return removeItemsFromHolds(user, password, SC_OK, itemNodeRefs, holdNodeRefs);
}
/**
@@ -274,13 +307,13 @@ public class HoldsAPI extends BaseAPI
* @param password the user's password
* @param expectedStatus https status code expected
* @param itemNodeRefs the list of items nodeRefs to be removed from hold
* @param holdNames the list of hold names
* @param holdNodeRefs the list of hold node refs
* @return The HTTP response
*/
public HttpResponse removeItemsFromHolds(String user, String password, int expectedStatus, List<String> itemNodeRefs,
List<String> holdNames)
List<String> holdNodeRefs)
{
final JSONObject requestParams = addOrRemoveToFromHoldJsonObject(user, password, itemNodeRefs, holdNames);
final JSONObject requestParams = addOrRemoveToFromHoldJsonObject(itemNodeRefs, holdNodeRefs);
return doPutJsonRequest(user, password, expectedStatus, requestParams, RM_HOLDS_API);
}
@@ -290,14 +323,14 @@ public class HoldsAPI extends BaseAPI
* @param user the user who removes the item from hold
* @param password the user's password
* @param itemNodeRef the nodeRef of the item to be removed from hold
* @param holdName the hold name
* @param holdNodeRef the hold node ref
* @return The error message
*/
public String removeFromHoldAndGetMessage(String user, String password, int expectedStatus, String itemNodeRef, String
holdName)
holdNodeRef)
{
final HttpResponse httpResponse = removeItemsFromHolds(user, password, expectedStatus, Collections.singletonList(itemNodeRef),
Collections.singletonList(holdName));
Collections.singletonList(holdNodeRef));
return APIUtils.extractErrorMessageFromHttpResponse(httpResponse);
}

View File

@@ -0,0 +1,130 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2019 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 <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rest.v0.service;
import static org.alfresco.utility.report.log.Step.STEP;
import static org.testng.AssertJUnit.assertTrue;
import java.time.Instant;
import java.util.List;
import org.alfresco.rest.rm.community.model.audit.AuditEntry;
import org.alfresco.rest.rm.community.model.audit.AuditEvents;
import org.alfresco.rest.v0.RMAuditAPI;
import org.alfresco.utility.data.DataUser;
import org.alfresco.utility.model.UserModel;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* Produces processed results from RM Audit REST API calls
*
* @author Claudia Agache
* @since 3.3
*/
@Service
public class RMAuditService
{
@Autowired
private RMAuditAPI rmAuditAPI;
@Autowired
private DataUser dataUser;
/**
* Clear the list of audit entries as admin user.
*/
public void clearAuditLog()
{
STEP("Clean audit logs.");
rmAuditAPI.clearAuditLog(dataUser.getAdminUser().getUsername(), dataUser.getAdminUser().getPassword());
}
/**
* Returns a list of rm audit entries filtered by given event
*
* @param user the user who requests the list of rm audit entries
* @param auditEvent the event
* @return the list of audit entries matching the event
*/
public List<AuditEntry> getAuditEntriesFilteredByEvent(UserModel user, AuditEvents auditEvent)
{
STEP("Get the list of audit entries for the " + auditEvent.eventDisplayName + " event.");
return rmAuditAPI.getRMAuditLog(user.getUsername(), user.getPassword(), 100, auditEvent.event);
}
/**
* Checks the rm audit log contains the entry for the given event.
*
* @param user the user who checks the audit log
* @param auditEvent the audited event
* @param auditUser the user who did the audited event
* @param nodeName the audited node name if exists or empty string
* @param changedValues the values changed by event if exist or empty list
*/
public void checkAuditLogForEvent(UserModel user, AuditEvents auditEvent, UserModel auditUser,
String nodeName, List<Object> changedValues)
{
final Instant eventTimestamp = Instant.now();
List<AuditEntry> auditEntries = getAuditEntriesFilteredByEvent(user, auditEvent);
assertTrue("The list of events is not filtered by " + auditEvent.event,
auditEntries.stream().allMatch(auditEntry -> auditEntry.getEvent().equals(auditEvent.eventDisplayName)));
assertTrue("The event details are not audited",
auditEntries.stream().anyMatch(auditEntry -> auditEntry.getNodeName().equals(nodeName) &&
auditEntry.getUserName().equals(auditUser.getUsername()) &&
CollectionUtils.isEqualCollection(auditEntry.getChangedValues(), changedValues) &&
!auditEntry.getTimestamp().isEmpty() &&
Instant.parse(auditEntry.getTimestamp()).compareTo(eventTimestamp) <= 0));
}
/**
* Checks the rm audit log contains the entry for the given event.
*
* @param user the user who checks the audit log
* @param auditEvent the audited event
* @param auditUser the user who did the audited event
* @param nodeName the audited node name if exists or empty string
* @param nodePath the path of the audited node if exists or empty string
* @param changedValues the values changed by event if exist or empty list
*/
public void checkAuditLogForEvent(UserModel user, AuditEvents auditEvent, UserModel auditUser,
String nodeName, String nodePath, List<Object> changedValues)
{
final Instant eventTimestamp = Instant.now();
List<AuditEntry> auditEntries = getAuditEntriesFilteredByEvent(user, auditEvent);
assertTrue("The list of events is not filtered by " + auditEvent.event,
auditEntries.stream().allMatch(auditEntry -> auditEntry.getEvent().equals(auditEvent.eventDisplayName)));
assertTrue("The event details are not audited",
auditEntries.stream().anyMatch(auditEntry -> auditEntry.getNodeName().equals(nodeName) &&
auditEntry.getUserName().equals(auditUser.getUsername()) &&
auditEntry.getPath().equals(nodePath) &&
CollectionUtils.isEqualCollection(auditEntry.getChangedValues(), changedValues) &&
!auditEntry.getTimestamp().isEmpty() &&
Instant.parse(auditEntry.getTimestamp()).compareTo(eventTimestamp) <= 0));
}
}

View File

@@ -0,0 +1,299 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2019 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 <http://www.gnu.org/licenses/>.
* #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.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.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<AuditEntry> auditEntries;
private List<String> holdsList = asList(HOLD1, HOLD2);
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);
holdsAPI.createHold(getAdminUser().getUsername(), getAdminUser().getPassword(), HOLD2, HOLD_REASON, HOLD_DESCRIPTION);
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
{
String documentLibrary = "/documentLibrary";
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(documentLibrary, recordCategory.getName(),
recordFolderToBeAdded.getName()));
String recordPath = removeLastSlash(buildPath(documentLibrary, 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 }
};
}
/**
* Data provider with invalid users that can not add content to a hold
*
* @return the userModel
*/
@DataProvider (name = "invalidUsersForAddToHold")
public Object[][] getInvalidUsersForAddToHold()
{
return new UserModel[][]
{
{ rmManagerNoReadOnHold },
{ rmManagerNoReadOnNode }
};
}
/**
* 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 hold or the document
* Then the add to hold entry isn't visible
*/
@Test (dataProvider = "invalidUsersForAddToHold")
public void addToHoldAuditEntryNotVisible(UserModel user)
{
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(user, ADD_TO_HOLD).isEmpty());
}
@AfterClass (alwaysRun = true)
public void cleanUpAuditAddToHoldTests()
{
holdsList.forEach(hold -> holdsAPI.deleteHold(getAdminUser().getUsername(), getAdminUser().getPassword(), hold));
dataSite.usingAdmin().deleteSite(privateSite);
asList(rmAdmin, rmManagerNoReadOnHold, rmManagerNoReadOnNode).forEach(user -> getDataUser().usingAdmin().deleteUser(user));
getRestAPIFactory().getRecordCategoryAPI().deleteRecordCategory(recordCategory.getId());
}
}

View File

@@ -0,0 +1,185 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2019 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 <http://www.gnu.org/licenses/>.
* #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.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";
@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.");
holdsAPI.createHold(rmAdmin.getUsername(), rmAdmin.getPassword(), HOLD1, HOLD_REASON, HOLD_DESCRIPTION);
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.");
holdsAPI.createHold(rmAdmin.getUsername(), rmAdmin.getPassword(), HOLD2, HOLD_REASON, HOLD_DESCRIPTION);
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<AuditEntry> 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<AuditEntry> 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.");
holdsAPI.createHold(rmAdmin.getUsername(), rmAdmin.getPassword(), HOLD3, HOLD_REASON, HOLD_DESCRIPTION);
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()
{
asList(HOLD1, HOLD2, HOLD3).forEach(hold ->
holdsAPI.deleteHold(getAdminUser().getUsername(), getAdminUser().getPassword(), hold));
asList(rmAdmin, rmManager).forEach(user -> getDataUser().usingAdmin().deleteUser(user));
}
}

View File

@@ -0,0 +1,139 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2019 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 <http://www.gnu.org/licenses/>.
* #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().getUsername(), getAdminUser().getPassword(), HOLD);
asList(rmAdmin, rmManager).forEach(user -> getDataUser().usingAdmin().deleteUser(user));
}
}

View File

@@ -26,20 +26,20 @@
*/
package org.alfresco.rest.rm.community.audit;
import static java.util.Arrays.asList;
import static org.alfresco.rest.rm.community.model.audit.AuditEvents.ADD_TO_USER_GROUP;
import static org.alfresco.rest.rm.community.model.audit.AuditEvents.CREATE_USER_GROUP;
import static org.alfresco.rest.rm.community.model.audit.AuditEvents.DELETE_USER_GROUP;
import static org.alfresco.rest.rm.community.model.audit.AuditEvents.REMOVE_FROM_USER_GROUP;
import static org.alfresco.utility.report.log.Step.STEP;
import static org.testng.AssertJUnit.assertTrue;
import java.util.List;
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.audit.AuditEntry;
import org.alfresco.rest.v0.RMAuditAPI;
import org.alfresco.rest.v0.service.RMAuditService;
import org.alfresco.test.AlfrescoTest;
import org.alfresco.utility.model.GroupModel;
import org.alfresco.utility.model.UserModel;
@@ -57,16 +57,14 @@ import org.testng.annotations.Test;
public class AuditGroupEventsTests extends BaseRMRestTest
{
@Autowired
private RMAuditAPI rmAuditAPI;
private RMAuditService rmAuditService;
private GroupModel testGroup;
private UserModel testUser;
@BeforeClass (alwaysRun = true)
public void cleanAuditLogs()
{
//clean audit logs
rmAuditAPI.clearAuditLog(getAdminUser().getUsername(), getAdminUser().getPassword());
rmAuditService.clearAuditLog();
}
/**
@@ -79,17 +77,10 @@ public class AuditGroupEventsTests extends BaseRMRestTest
{
testGroup = dataGroup.createRandomGroup();
STEP("Get the list of audit entries for the create group event.");
List<AuditEntry> auditEntries = rmAuditAPI.getRMAuditLog(getAdminUser().getUsername(),
getAdminUser().getPassword(), 100, CREATE_USER_GROUP.event);
STEP("Check the audit log contains only the entries for the created group.");
assertTrue("The list of events is not filtered by " + CREATE_USER_GROUP.event,
auditEntries.stream().allMatch(auditEntry -> auditEntry.getEvent().equals(CREATE_USER_GROUP.eventDisplayName)));
assertTrue("The group name for the new group created is not audited.",
auditEntries.stream().filter(auditEntry -> auditEntry.getEvent().equals(CREATE_USER_GROUP.eventDisplayName))
.anyMatch(auditEntry -> auditEntry.getNodeName().equals(testGroup.getGroupIdentifier())));
STEP("Check the audit log contains the entry for the created group.");
rmAuditService.checkAuditLogForEvent(getAdminUser(), CREATE_USER_GROUP, getAdminUser(), testGroup.getGroupIdentifier(),
Collections.singletonList(ImmutableMap.of("new", testGroup.getGroupIdentifier(), "previous", "",
"name", "authorityDisplayName")));
}
/**
@@ -104,19 +95,10 @@ public class AuditGroupEventsTests extends BaseRMRestTest
testUser = getDataUser().createRandomTestUser();
dataGroup.usingUser(testUser).addUserToGroup(testGroup);
STEP("Get the list of audit entries for the add user to group event.");
List<AuditEntry> auditEntries = rmAuditAPI.getRMAuditLog(getAdminUser().getUsername(),
getAdminUser().getPassword(), 100, ADD_TO_USER_GROUP.event);
STEP("Check the audit log contains only the entries for the add user to group event.");
assertTrue("The list of events is not filtered by " + ADD_TO_USER_GROUP.event,
auditEntries.stream().allMatch(auditEntry -> auditEntry.getEvent().equals(ADD_TO_USER_GROUP.eventDisplayName)));
assertTrue("The username and destination group are not audited.",
auditEntries.stream().filter(auditEntry -> auditEntry.getEvent().equals(ADD_TO_USER_GROUP.eventDisplayName))
.anyMatch(auditEntry -> auditEntry.getNodeName().equals(testGroup.getGroupIdentifier())
&& auditEntry.getChangedValues().contains(ImmutableMap.of("new", testUser.getUsername(), "previous", "", "name", "User Name"))
&& auditEntry.getChangedValues().contains(ImmutableMap.of("new", testGroup.getGroupIdentifier(), "previous", "", "name", "Parent Group"))));
STEP("Check the audit log contains the entry for the add user to group event.");
rmAuditService.checkAuditLogForEvent(getAdminUser(), ADD_TO_USER_GROUP, getAdminUser(), testGroup.getGroupIdentifier(),
asList(ImmutableMap.of("new", testUser.getUsername(), "previous", "", "name", "User Name"),
ImmutableMap.of("new", testGroup.getGroupIdentifier(), "previous", "", "name", "Parent Group")));
}
/**
@@ -132,19 +114,10 @@ public class AuditGroupEventsTests extends BaseRMRestTest
dataGroup.usingUser(testUser).addUserToGroup(testGroup);
dataGroup.removeUserFromGroup(testGroup, testUser);
STEP("Get the list of audit entries for the add user to group event.");
List<AuditEntry> auditEntries = rmAuditAPI.getRMAuditLog(getAdminUser().getUsername(),
getAdminUser().getPassword(), 100, REMOVE_FROM_USER_GROUP.event);
STEP("Check the audit log contains only the entries for the remove user from group event.");
assertTrue("The list of events is not filtered by " + REMOVE_FROM_USER_GROUP.event,
auditEntries.stream().allMatch(auditEntry -> auditEntry.getEvent().equals(REMOVE_FROM_USER_GROUP.eventDisplayName)));
assertTrue("The username and previous parent group are not audited.",
auditEntries.stream().filter(auditEntry -> auditEntry.getEvent().equals(REMOVE_FROM_USER_GROUP.eventDisplayName))
.anyMatch(auditEntry -> auditEntry.getNodeName().equals(testGroup.getGroupIdentifier())
&& auditEntry.getChangedValues().contains(ImmutableMap.of("new", "", "previous", testUser.getUsername(), "name", "User Name"))
&& auditEntry.getChangedValues().contains(ImmutableMap.of("new", "","previous", testGroup.getGroupIdentifier(), "name", "Parent Group"))));
STEP("Check the audit log contains the entry for the remove user from group event.");
rmAuditService.checkAuditLogForEvent(getAdminUser(), REMOVE_FROM_USER_GROUP, getAdminUser(), testGroup.getGroupIdentifier(),
asList(ImmutableMap.of("new", "", "previous", testUser.getUsername(), "name", "User Name"),
ImmutableMap.of("new", "","previous", testGroup.getGroupIdentifier(), "name", "Parent Group")));
}
/**
@@ -158,17 +131,9 @@ public class AuditGroupEventsTests extends BaseRMRestTest
testGroup = dataGroup.createRandomGroup();
dataGroup.deleteGroup(testGroup);
STEP("Get the list of audit entries for the delete group event.");
List<AuditEntry> auditEntries = rmAuditAPI.getRMAuditLog(getAdminUser().getUsername(),
getAdminUser().getPassword(), 100, DELETE_USER_GROUP.event);
STEP("Check the audit log contains only the entries for the created group.");
assertTrue("The list of events is not filtered by " + DELETE_USER_GROUP.event,
auditEntries.stream().allMatch(auditEntry -> auditEntry.getEvent().equals(DELETE_USER_GROUP.eventDisplayName)));
assertTrue("The group name for the deleted group is not audited.",
auditEntries.stream().filter(auditEntry -> auditEntry.getEvent().equals(DELETE_USER_GROUP.eventDisplayName))
.anyMatch(auditEntry -> auditEntry.getNodeName().equals(testGroup.getGroupIdentifier())
&& auditEntry.getChangedValues().contains(ImmutableMap.of("new", "", "previous", testGroup.getGroupIdentifier(), "name", "authorityDisplayName"))));
STEP("Check the audit log contains the entry for the delete group event.");
rmAuditService.checkAuditLogForEvent(getAdminUser(), DELETE_USER_GROUP, getAdminUser(), testGroup.getGroupIdentifier(),
Collections.singletonList(ImmutableMap.of("new", "", "previous", testGroup.getGroupIdentifier(),
"name", "authorityDisplayName")));
}
}

View File

@@ -34,11 +34,10 @@ import java.util.List;
import org.alfresco.rest.rm.community.base.BaseRMRestTest;
import org.alfresco.rest.rm.community.model.audit.AuditEntry;
import org.alfresco.rest.v0.RMAuditAPI;
import org.alfresco.rest.v0.service.RMAuditService;
import org.alfresco.test.AlfrescoTest;
import org.alfresco.utility.model.UserModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
/**
@@ -51,14 +50,7 @@ import org.testng.annotations.Test;
public class AuditLoginEventsTests extends BaseRMRestTest
{
@Autowired
private RMAuditAPI rmAuditAPI;
@BeforeClass (alwaysRun = true)
public void cleanAuditLogs()
{
//clean audit logs
rmAuditAPI.clearAuditLog(getAdminUser().getUsername(), getAdminUser().getPassword());
}
private RMAuditService rmAuditService;
/**
* Given I have tried to login using invalid credentials
@@ -68,12 +60,13 @@ public class AuditLoginEventsTests extends BaseRMRestTest
@Test
public void filterByLoginUnsuccessful() throws Exception
{
rmAuditService.clearAuditLog();
restClient.authenticateUser(new UserModel(getAdminUser().getUsername(), "InvalidPassword"));
restClient.withCoreAPI().getSites();
STEP("Get the list of audit entries for the login unsuccessful event.");
List<AuditEntry> auditEntries = rmAuditAPI.getRMAuditLog(getAdminUser().getUsername(),
getAdminUser().getPassword(), 100, LOGIN_UNSUCCESSFUL.event);
List<AuditEntry> auditEntries = rmAuditService.getAuditEntriesFilteredByEvent(getAdminUser(),
LOGIN_UNSUCCESSFUL);
STEP("Check the audit log contains only the entries for the login unsuccessful event.");
assertTrue("The list of events is not filtered by " + LOGIN_UNSUCCESSFUL.event,

View File

@@ -0,0 +1,312 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2019 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 <http://www.gnu.org/licenses/>.
* #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.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.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<AuditEntry> auditEntries;
private List<String> holdsList = asList(HOLD1, HOLD2, HOLD3);
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);
holdsAPI.createHold(getAdminUser().getUsername(), getAdminUser().getPassword(), HOLD2, HOLD_REASON, HOLD_DESCRIPTION);
holdsAPI.createHold(getAdminUser().getUsername(), getAdminUser().getPassword(), HOLD3, HOLD_REASON, HOLD_DESCRIPTION);
holdsAPI.createHold(getAdminUser().getUsername(), getAdminUser().getPassword(), DELETED_HOLD, HOLD_REASON, HOLD_DESCRIPTION);
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 documentLibrary = "/documentLibrary";
String recordFolderPath = removeLastSlash(buildPath(documentLibrary, recordCategory.getName(),
heldRecordFolder.getName()));
String recordPath = removeLastSlash(buildPath(documentLibrary, 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 }
};
}
/**
* Data provider with invalid users that can not remove content from a hold
*
* @return the userModel
*/
@DataProvider (name = "invalidUsersForRemoveFromHold")
public Object[][] getInvalidUsersForRemoveFromHold()
{
return new UserModel[][]
{
{ rmManagerNoReadOnHold },
{ rmManagerNoReadOnNode }
};
}
/**
* 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 hold or the node
* Then the remove from hold entry isn't visible
*/
@Test (dataProvider = "invalidUsersForRemoveFromHold")
public void removeFromHoldAuditEntryNotVisible(UserModel user)
{
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(user, REMOVE_FROM_HOLD).isEmpty());
}
@AfterClass (alwaysRun = true)
public void cleanUpAuditRemoveFromHoldTests()
{
holdsList.forEach(hold -> holdsAPI.deleteHold(getAdminUser().getUsername(), getAdminUser().getPassword(), hold));
dataSite.usingAdmin().deleteSite(privateSite);
asList(rmAdmin, rmManagerNoReadOnHold, rmManagerNoReadOnNode).forEach(user -> getDataUser().usingAdmin().deleteUser(user));
getRestAPIFactory().getRecordCategoryAPI().deleteRecordCategory(recordCategory.getId());
}
}

View File

@@ -29,18 +29,17 @@ package org.alfresco.rest.rm.community.audit;
import static org.alfresco.rest.rm.community.model.audit.AuditEvents.CREATE_PERSON;
import static org.alfresco.rest.rm.community.util.CommonTestUtils.generateTestPrefix;
import static org.alfresco.utility.report.log.Step.STEP;
import static org.testng.AssertJUnit.assertTrue;
import java.util.List;
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.audit.AuditEntry;
import org.alfresco.rest.v0.RMAuditAPI;
import org.alfresco.rest.v0.service.RMAuditService;
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;
/**
@@ -52,10 +51,9 @@ import org.testng.annotations.Test;
public class AuditUserEventsTests extends BaseRMRestTest
{
private final String PREFIX = generateTestPrefix(AuditUserEventsTests.class);
private UserModel createUser;
@Autowired
private RMAuditAPI rmAuditAPI;
private RMAuditService rmAuditService;
/**
* Given I have created a new user
@@ -66,30 +64,16 @@ public class AuditUserEventsTests extends BaseRMRestTest
*/
@Test
@AlfrescoTest(jira = "RM-6223")
public void createUserEventIsAudited() throws Exception
public void createUserEventIsAudited()
{
rmAuditService.clearAuditLog();
STEP("Create a new user.");
String userName = "auditCreateUser" + PREFIX;
createUser = getDataUser().createUser(userName);
STEP("Get the list of audit entries for the create person event.");
List<AuditEntry> auditEntries = rmAuditAPI.getRMAuditLog(getAdminUser().getUsername(),
getAdminUser().getPassword(), 100, CREATE_PERSON.event);
STEP("Check the audit log contains only the entries for the created user.");
assertTrue("The list of events is not filtered by " + CREATE_PERSON.event,
auditEntries.stream().allMatch(auditEntry -> auditEntry.getEvent().equals(CREATE_PERSON.eventDisplayName)));
assertTrue("The username value for the user created is not audited.",
auditEntries.stream().filter(auditEntry -> auditEntry.getEvent().equals(CREATE_PERSON.eventDisplayName))
.allMatch(auditEntry -> auditEntry.getNodeName().equals(userName)));
}
@BeforeClass (alwaysRun = true)
public void cleanAuditLogs()
{
//clean audit logs
rmAuditAPI.clearAuditLog(getAdminUser().getUsername(), getAdminUser().getPassword());
STEP("Check the audit log contains the entry for the created user event.");
rmAuditService.checkAuditLogForEvent(getAdminUser(), CREATE_PERSON, getAdminUser(), userName,
Collections.singletonList(ImmutableMap.of("new", userName, "previous", "", "name", "User Name")));
}
@AfterClass (alwaysRun = true)

View File

@@ -615,37 +615,6 @@ public class BaseRMRestTest extends RestTest
recordCategoryAPI.deleteRecordCategory(recordCategoryId);
}
/**
* Helper method to create a test user with rm role
*
* @param userRole the rm role
* @return the created user model
*/
protected UserModel createUserWithRMRole(String userRole)
{
UserModel rmUser = getDataUser().createRandomTestUser();
getRestAPIFactory().getRMUserAPI().assignRoleToUser(rmUser.getUsername(), userRole);
assertStatusCode(OK);
return rmUser;
}
/**
* Helper method to create a test user with rm role and permissions over the record category
*
* @param userRole the rm role
* @param userPermission the permissions over the record category
* @param recordCategory the category on which user has permissions
* @return the created user model
*/
protected UserModel createUserWithRMRoleAndCategoryPermission(String userRole, RecordCategory recordCategory,
UserPermissions userPermission)
{
UserModel rmUser = createUserWithRMRole(userRole);
getRestAPIFactory().getRMUserAPI().addUserPermission(recordCategory.getId(), rmUser, userPermission);
assertStatusCode(OK);
return rmUser;
}
/**
* Returns search results for the given search term
*

View File

@@ -26,6 +26,8 @@
*/
package org.alfresco.rest.rm.community.files;
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.fileplancomponents.FilePlanComponentAlias.FILE_PLAN_ALIAS;
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.TRANSFERS_ALIAS;
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.UNFILED_RECORDS_CONTAINER_ALIAS;
@@ -87,7 +89,7 @@ public class DeclareAndFileDocumentAsRecordTests extends BaseRMRestTest
private final static String INVALID_DESTINATION_PATH_EXC = "Unable to execute create-record action, because the destination path is invalid.";
private final static String DESTINATION_PATH_NOT_RECORD_FOLDER_EXC = "Unable to execute create-record action, because the destination path is not a record folder.";
private final static String CLOSED_RECORD_FOLDER_EXC = "You can't add new items to a closed record folder.";
private final static String HOLD_NAME = "holdName";
private final static String HOLD_NAME = getRandomName("holdName");
private final static String RECORD_FOLDER_NAME_WITH_SPACE = "Folder With Spaces In Name";
private UserModel userFillingPermission, userReadOnlyPermission;
@@ -411,8 +413,7 @@ public class DeclareAndFileDocumentAsRecordTests extends BaseRMRestTest
public void declareAndFileToHeldRecordFolderUsingFilesAPI() throws Exception
{
RecordCategoryChild heldRecordFolder = createFolder(recordCategory.getId(), getRandomName("heldRecordFolder"));
holdsAPI.createHold(getAdminUser().getUsername(), getAdminUser().getPassword(), HOLD_NAME, "hold reason",
"hold description");
holdsAPI.createHold(getAdminUser().getUsername(), getAdminUser().getPassword(), HOLD_NAME, HOLD_REASON, HOLD_DESCRIPTION);
holdsAPI.addItemToHold(getAdminUser().getUsername(), getAdminUser().getPassword(), heldRecordFolder.getId(),
HOLD_NAME);

View File

@@ -108,7 +108,7 @@ public class AddToHoldsTests extends BaseRMRestTest
private ContentActions contentActions;
@BeforeClass (alwaysRun = true)
public void preconditionForAddContentToHold() throws Exception
public void preconditionForAddContentToHold()
{
STEP("Create a hold.");
holdNodeRef = holdsAPI.createHoldAndGetNodeRef(getAdminUser().getUsername(), getAdminUser().getUsername(),
@@ -174,7 +174,7 @@ public class AddToHoldsTests extends BaseRMRestTest
* Valid nodes to be added to hold
*/
@DataProvider (name = "validNodesForAddToHold")
public Object[][] getValidNodesForAddToHold() throws Exception
public Object[][] getValidNodesForAddToHold()
{
//create electronic and nonElectronic record in record folder
RecordCategoryChild recordFolder = createCategoryFolderInFilePlan();
@@ -287,7 +287,7 @@ public class AddToHoldsTests extends BaseRMRestTest
users.add(userModel);
STEP("Add the node to the hold with user without permission.");
String response = holdsAPI.addToHoldAndGetMessage(userModel.getUsername(), userModel.getPassword(),
SC_INTERNAL_SERVER_ERROR, nodeToBeAddedToHold, HOLD);
SC_INTERNAL_SERVER_ERROR, nodeToBeAddedToHold, holdNodeRef);
assertTrue(response.contains(ACCESS_DENIED_ERROR_MESSAGE));
STEP("Check the node is not frozen.");
@@ -345,7 +345,7 @@ public class AddToHoldsTests extends BaseRMRestTest
{
STEP("Add the node to the hold ");
String responseErrorMessage = holdsAPI.addToHoldAndGetMessage(getAdminUser().getUsername(),
getAdminUser().getPassword(), responseCode, itemNodeRef, HOLD);
getAdminUser().getPassword(), responseCode, itemNodeRef, holdNodeRef);
assertTrue(responseErrorMessage.contains(errorMessage),
"Actual error message " + responseErrorMessage + " expected " + errorMessage);
@@ -354,7 +354,7 @@ public class AddToHoldsTests extends BaseRMRestTest
}
@AfterClass (alwaysRun = true)
public void cleanUpAddContentToHold() throws Exception
public void cleanUpAddContentToHold()
{
holdsAPI.deleteHold(getAdminUser().getUsername(), getAdminUser().getPassword(), HOLD);
dataSite.usingAdmin().deleteSite(testSite);

View File

@@ -95,12 +95,12 @@ public class RemoveFromHoldsTests extends BaseRMRestTest
private RoleService roleService;
@BeforeClass (alwaysRun = true)
public void preconditionForRemoveContentFromHold() throws Exception
public void preconditionForRemoveContentFromHold()
{
STEP("Create two holds.");
holdNodeRefOne = holdsAPI.createHoldAndGetNodeRef(getAdminUser().getUsername(), getAdminUser().getUsername(),
HOLD_ONE, HOLD_REASON, HOLD_DESCRIPTION);
String holdNodeRefTwo = holdsAPI.createHoldAndGetNodeRef(getAdminUser().getUsername(), getAdminUser()
holdsAPI.createHoldAndGetNodeRef(getAdminUser().getUsername(), getAdminUser()
.getUsername(), HOLD_TWO, HOLD_REASON, HOLD_DESCRIPTION);
STEP("Create test files.");
@@ -265,7 +265,7 @@ public class RemoveFromHoldsTests extends BaseRMRestTest
STEP("Remove node from hold with user without right permission or capability");
String responseNoHoldPermission = holdsAPI.removeFromHoldAndGetMessage(userModel.getUsername(),
userModel.getPassword(), SC_INTERNAL_SERVER_ERROR, nodeIdToBeRemoved, HOLD_ONE);
userModel.getPassword(), SC_INTERNAL_SERVER_ERROR, nodeIdToBeRemoved, holdNodeRefOne);
assertTrue(responseNoHoldPermission.contains(ACCESS_DENIED_ERROR_MESSAGE));
STEP("Check node is frozen.");

View File

@@ -5,4 +5,6 @@ rm.hold.add-to-hold-locked-node=Locked content can't be added to a hold.
rm.hold.delete-frozen-node=Frozen content can't be deleted.
rm.hold.delete-node-frozen-children=Can't delete folder because it contains frozen content.
rm.hold.move-frozen-node=Frozen content can't be moved.
rm.hold.update-frozen-node=Frozen content can't be updated.
rm.hold.update-frozen-node=Frozen content can't be updated.
rm.hold.generic-permission-error=Can't delete hold, because you don't have the correct permissions for all the items within the hold.
rm.hold.detailed-permission-error=Can't delete hold, because filing permissions for the following items are needed:

View File

@@ -101,6 +101,8 @@ public class HoldServiceImpl extends ServiceBaseImpl
/** I18N */
private static final String MSG_ERR_ACCESS_DENIED = "permissions.err_access_denied";
private static final String MSG_ERR_HOLD_PERMISSION_GENERIC_ERROR = "rm.hold.generic-permission-error";
private static final String MSG_ERR_HOLD_PERMISSION_DETAILED_ERROR = "rm.hold.detailed-permission-error";
/** File Plan Service */
private FilePlanService filePlanService;
@@ -526,7 +528,7 @@ public class HoldServiceImpl extends ServiceBaseImpl
if (!isHold(hold))
{
throw new AlfrescoRuntimeException("Can't delete hold, becuase passed node is not a hold. (hold=" + hold.toString() + ")");
throw new AlfrescoRuntimeException("Can't delete hold, because passed node is not a hold. (hold=" + hold.toString() + ")");
}
List<NodeRef> held = AuthenticationUtil.runAsSystem(new RunAsWork<List<NodeRef>>()
@@ -543,14 +545,25 @@ public class HoldServiceImpl extends ServiceBaseImpl
{
try
{
if (permissionService.hasPermission(nodeRef, RMPermissionModel.FILING) == AccessStatus.DENIED)
String permission;
if (recordService.isRecord(nodeRef) || recordFolderService.isRecordFolder(nodeRef))
{
permission = RMPermissionModel.FILING;
}
else
{
permission = PermissionService.READ;
}
if (permissionService.hasPermission(nodeRef, permission) == AccessStatus.DENIED)
{
heldNames.add((String) nodeService.getProperty(nodeRef, ContentModel.PROP_NAME));
}
}
catch (AccessDeniedException ade)
{
throw new AlfrescoRuntimeException("Can't delete hold, because you don't have filling permissions on all the items held within the hold.", ade);
throw new AccessDeniedException(I18NUtil.getMessage(MSG_ERR_HOLD_PERMISSION_GENERIC_ERROR), ade);
}
}
@@ -564,7 +577,7 @@ public class HoldServiceImpl extends ServiceBaseImpl
sb.append(name);
sb.append("'");
}
throw new AlfrescoRuntimeException("Can't delete hold, because filing permissions for the following items are needed: " + sb.toString());
throw new AccessDeniedException(I18NUtil.getMessage(MSG_ERR_HOLD_PERMISSION_DETAILED_ERROR) + sb.toString());
}
invokeBeforeDeleteHold(hold);

View File

@@ -96,6 +96,7 @@ public class HoldServiceImplUnitTest extends BaseUnitTest
@Mock
private CapabilityService mockedCapabilityService;
@Spy @InjectMocks HoldServiceImpl holdService;
@Before
@@ -221,9 +222,7 @@ public class HoldServiceImplUnitTest extends BaseUnitTest
when(mockedNodeService.createNode(eq(holdContainer), eq(ContentModel.ASSOC_CONTAINS), any(QName.class) , eq(TYPE_HOLD), any(Map.class)))
.thenReturn(new ChildAssociationRef(ContentModel.ASSOC_CONTAINS, holdContainer, generateQName(), hold));
// mocks for policies
doNothing().when(holdService).invokeBeforeCreateHold(any(), anyString(), anyString());
doNothing().when(holdService).invokeOnCreateHold(any());
mockPoliciesForCreateHold();
// create hold
NodeRef newHold = holdService.createHold(filePlan, HOLD_NAME, HOLD_REASON, HOLD_DESCRIPTION);
@@ -311,9 +310,7 @@ public class HoldServiceImplUnitTest extends BaseUnitTest
@Test
public void deleteHold()
{
// mocks for policies
doNothing().when(holdService).invokeBeforeDeleteHold(any());
doNothing().when(holdService).invokeOnDeleteHold(any(), any());
mockPoliciesForDeleteHold();
// delete hold
holdService.deleteHold(hold);
@@ -579,6 +576,103 @@ public class HoldServiceImplUnitTest extends BaseUnitTest
holdService.removeFromHolds(holds, activeContent);
}
/**
* test delete hold throws exception for failed read permission check for content
*/
@Test (expected = AccessDeniedException.class)
public void testDeleteHoldThrowsExceptionForActiveContentWithoutReadPermission()
{
NodeRef heldContent = generateNodeRef(TYPE_CONTENT);
List<ChildAssociationRef> holds = createListOfHoldAssociations(heldContent);
when(mockedNodeService.getChildAssocs(hold, ASSOC_FROZEN_CONTENT, RegexQNamePattern.MATCH_ALL)).thenReturn(holds);
when(mockedPermissionService.hasPermission(heldContent, PermissionService.READ)).thenReturn(AccessStatus.DENIED);
when(mockedNodeService.getProperty(heldContent, ContentModel.PROP_NAME)).thenReturn("foo");
holdService.deleteHold(hold);
}
/**
* test delete hold throws exception for failed read permission check for records
*/
@Test (expected = AccessDeniedException.class)
public void testDeleteHoldThrowsExceptionForARecordWithoutReadPermission()
{
NodeRef heldContent = generateNodeRef();
List<ChildAssociationRef> holds = createListOfHoldAssociations(heldContent);
when(mockedNodeService.getChildAssocs(hold, ASSOC_FROZEN_CONTENT, RegexQNamePattern.MATCH_ALL)).thenReturn(holds);
when(mockedRecordService.isRecord(heldContent)).thenThrow(new AccessDeniedException(""));
holdService.deleteHold(hold);
}
/**
* test delete hold throws exception for failed file permission check for records
*/
@Test (expected = AccessDeniedException.class)
public void testDeleteHoldThrowsExceptionForARecordWithoutFilePermission()
{
NodeRef heldContent = generateNodeRef();
List<ChildAssociationRef> holds = createListOfHoldAssociations(heldContent);
when(mockedNodeService.getChildAssocs(hold, ASSOC_FROZEN_CONTENT, RegexQNamePattern.MATCH_ALL)).thenReturn(holds);
when(mockedRecordService.isRecord(heldContent)).thenReturn(true);
when(mockedPermissionService.hasPermission(heldContent, RMPermissionModel.FILING)).thenReturn(AccessStatus.DENIED);
when(mockedNodeService.getProperty(heldContent, ContentModel.PROP_NAME)).thenReturn("foo");
holdService.deleteHold(hold);
}
/**
* Test hold deleted for active content with read permission
*/
@Test
public void testDeleteHoldChecksReadPermissionForActiveContent()
{
NodeRef heldContent = generateNodeRef(TYPE_CONTENT);
List<ChildAssociationRef> holds = createListOfHoldAssociations(heldContent);
mockPoliciesForDeleteHold();
when(mockedNodeService.getChildAssocs(hold, ASSOC_FROZEN_CONTENT, RegexQNamePattern.MATCH_ALL)).thenReturn(holds);
when(mockedRecordService.isRecord(heldContent)).thenReturn(false);
when(mockedRecordFolderService.isRecordFolder(heldContent)).thenReturn(false);
when(mockedPermissionService.hasPermission(heldContent, PermissionService.READ)).thenReturn(AccessStatus.ALLOWED);
when(mockedNodeService.getProperty(heldContent, ContentModel.PROP_NAME)).thenReturn("foo");
holdService.deleteHold(hold);
verify(mockedNodeService, times(1)).deleteNode(hold);
}
/**
* Helper method to create hold and associations with given content
*/
private List<ChildAssociationRef> createListOfHoldAssociations(NodeRef heldContent)
{
List<ChildAssociationRef> holds = new ArrayList<>(2);
holds.add(new ChildAssociationRef(ASSOC_FROZEN_CONTENT, hold, ASSOC_FROZEN_CONTENT, heldContent, true, 1));
return holds;
}
/**
* mocks policies for create hold
*/
private void mockPoliciesForCreateHold()
{
doNothing().when(holdService).invokeBeforeCreateHold(any(), anyString(), anyString());
doNothing().when(holdService).invokeOnCreateHold(any());
}
/**
* mocks policies for delete hold
*/
private void mockPoliciesForDeleteHold()
{
doNothing().when(holdService).invokeBeforeDeleteHold(any());
doNothing().when(holdService).invokeOnDeleteHold(any(), any());
}
/**
* mocks policies for add to hold
*/