/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2016 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.module.org_alfresco_module_rm.test.legacy.service;
import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.alfresco.model.ContentModel;
import org.alfresco.module.org_alfresco_module_rm.audit.RecordsManagementAuditEntry;
import org.alfresco.module.org_alfresco_module_rm.audit.RecordsManagementAuditQueryParameters;
import org.alfresco.module.org_alfresco_module_rm.audit.RecordsManagementAuditService;
import org.alfresco.module.org_alfresco_module_rm.audit.event.AuditEvent;
import org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel;
import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase;
import org.alfresco.repo.security.authentication.AuthenticationException;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.EqualsHelper;
import org.alfresco.util.Pair;
/**
* @see RecordsManagementAuditService
*
* @author Derek Hulley
* @author Roy Wetherall
*
* @since 3.2
*/
public class RecordsManagementAuditServiceImplTest extends BaseRMTestCase
implements RMPermissionModel
{
/** Test record */
private NodeRef record;
/** Test start time */
private Date testStartTime;
/**
* @see org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase#setUp()
*/
@Override
protected void setUp() throws Exception
{
super.setUp();
doTestInTransaction(new Test<Void>()
{
@Override
public Void run() throws Exception
{
// test start time recorded
testStartTime = new Date();
// Stop and clear the log
rmAuditService.stopAuditLog(filePlan);
rmAuditService.clearAuditLog(filePlan);
rmAuditService.startAuditLog(filePlan);
// check that audit service is started
assertTrue(rmAuditService.isAuditLogEnabled(filePlan));
return null;
}
});
}
/**
* @see org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase#isUserTest()
*/
@Override
protected boolean isUserTest()
{
return true;
}
/**
* @see org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase#setupTestDataImpl()
*/
@Override
protected void setupTestDataImpl()
{
super.setupTestDataImpl();
record = utils.createRecord(rmFolder, "AuditTest.txt");
}
/**
* @see org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase#setupTestUsersImpl(org.alfresco.service.cmr.repository.NodeRef)
*/
@Override
protected void setupTestUsersImpl(NodeRef filePlan)
{
super.setupTestUsersImpl(filePlan);
// Give all the users file permission objects
for (String user : testUsers)
{
filePlanPermissionService.setPermission(filePlan, user, FILING);
filePlanPermissionService.setPermission(rmContainer, user, FILING);
}
}
public void testGetAuditEvents()
{
doTestInTransaction(new Test<Void>()
{
@Override
public Void run() throws Exception
{
List<AuditEvent> events = rmAuditService.getAuditEvents();
System.out.println("Found audit events:");
for (AuditEvent event : events)
{
System.out.println(" - " + event.getName() + " (" + event.getLabel() + ")");
}
return null;
}
}, ADMIN_USER);
}
/**
* Test getAuditTrail method to check that deleted items always show in the audit.
*
* @see RM-2391 (last addressed isue)
*/
public void testGetAuditTrailForDeletedItem()
{
// We have only one entry for the event "audit.start":
List<RecordsManagementAuditEntry> entries = getAuditTrail(1, ADMIN_USER);
assertEquals(entries.get(0).getEvent(), "audit.start");
// Event "audit.view" was generated but will be visible on the next call to getAuditTrail().
// Make a change:
updateTitle(filePlan, ADMIN_USER); // event=Update RM Object
// Show the audit has been updated; at this point we have three entries for the three events up to now:
// "audit.start", "audit.view" and "Update RM Object";
entries = getAuditTrail(3, ADMIN_USER);
assertEquals(entries.get(2).getEvent(), "audit.start");
assertEquals(entries.get(1).getEvent(), "audit.view");
assertEquals(entries.get(0).getEvent(), "Update RM Object");
// New "audit.view" event was generated - will be visible on next getAuditTrail().
doTestInTransaction(new Test<Void>()
{
@Override
public Void run() throws Exception
{
nodeService.deleteNode(record);
List<RecordsManagementAuditEntry> entries = getAuditTrail(5, ADMIN_USER);
assertEquals(entries.get(4).getEvent(), "audit.start");
assertEquals(entries.get(3).getEvent(), "audit.view");
assertEquals(entries.get(2).getEvent(), "Update RM Object");
assertEquals(entries.get(1).getEvent(), "audit.view");
// Show the audit contains a reference to the deleted item:
assertEquals(entries.get(0).getEvent(), "Delete RM Object");
assertEquals(entries.get(0).getNodeRef(), record);
return null;
}
});
}
/**
* Test getAuditTrail method and parameter filters.
*/
public void testGetAuditTrail()
{
// show the audit is empty
getAuditTrail(1, ADMIN_USER);
// make a change
final String updatedProperty = updateTitle(filePlan, ADMIN_USER);
// show the audit has been updated
List<RecordsManagementAuditEntry> entries = getAuditTrail(3, ADMIN_USER);
final RecordsManagementAuditEntry entry = entries.get(0);
assertNotNull(entry);
// investigate the contents of the audit entry
doTestInTransaction(new Test<Void>()
{
@SuppressWarnings("unchecked")
@Override
public Void run() throws Exception
{
assertEquals(filePlan, entry.getNodeRef());
String id = (String)nodeService.getProperty(filePlan, PROP_IDENTIFIER);
assertEquals(id, entry.getIdentifier());
Map<QName, Serializable> after = entry.getAfterProperties();
Map<QName, Pair<Serializable, Serializable>> changed = entry.getChangedProperties();
assertTrue(after.containsKey(PROP_TITLE));
assertTrue(changed.containsKey(PROP_TITLE));
Serializable value = ((Map<Locale, Serializable>)after.get(PROP_TITLE)).get(Locale.ENGLISH);
assertEquals(updatedProperty, value);
value = ((Map<Locale, Serializable>)changed.get(PROP_TITLE).getSecond()).get(Locale.ENGLISH);
assertEquals(updatedProperty, value);
return null;
}
}, ADMIN_USER);
// add some more title updates
updateTitle(rmContainer, ADMIN_USER);
updateTitle(rmFolder, ADMIN_USER);
updateTitle(record, ADMIN_USER);
// show the audit has been updated
getAuditTrail(7, ADMIN_USER);
// snap shot date
Date snapShot = new Date();
// show the audit results can be limited
RecordsManagementAuditQueryParameters params = new RecordsManagementAuditQueryParameters();
params.setMaxEntries(2);
getAuditTrail(params, 2, ADMIN_USER);
// test filter by user
updateTitle(rmContainer, recordsManagerName);
updateTitle(rmFolder, recordsManagerName);
updateTitle(record, recordsManagerName);
params = new RecordsManagementAuditQueryParameters();
params.setUser(recordsManagerName);
getAuditTrail(params, 3, ADMIN_USER);
// test filter by date
params = new RecordsManagementAuditQueryParameters();
params.setDateFrom(snapShot);
getAuditTrail(params, 13, ADMIN_USER);
params = new RecordsManagementAuditQueryParameters();
params.setDateTo(snapShot);
getAuditTrail(params, 14, ADMIN_USER);
params.setDateFrom(testStartTime);
getAuditTrail(params, 15, ADMIN_USER);
// test filter by object
updateTitle(record, ADMIN_USER);
updateTitle(record, ADMIN_USER);
updateTitle(record, ADMIN_USER);
params = new RecordsManagementAuditQueryParameters();
params.setNodeRef(record);
getAuditTrail(params, 5, ADMIN_USER);
// test filter by event
params = new RecordsManagementAuditQueryParameters();
// params.setEvent("cutoff");
// getAuditTrail(params, 0, ADMIN_USER);
params.setEvent("Update RM Object");
getAuditTrail(params, 10, ADMIN_USER);
// test filter by property
// params = new RecordsManagementAuditQueryParameters();
//params.setProperty(PROP_ADDRESSEES);
//getAuditTrail(params, 0, ADMIN_USER);
// params.setProperty(PROP_TITLE);
// getAuditTrail(params, 10, ADMIN_USER);
}
/**
* Tests the following methods:
* - start()
* - stop()
* - clear()
* - isEnabled()
* - getDateLastStopped()
* - getDateLastStarted()
*
* @throws InterruptedException
*/
public void testAdminMethods() throws InterruptedException
{
// Stop the audit
rmAuditService.stopAuditLog(filePlan);
Thread.sleep(5000);
List<RecordsManagementAuditEntry> result1 = getAuditTrail(ADMIN_USER);
assertNotNull(result1);
// Update the fileplan
updateTitle(filePlan, ADMIN_USER);
Thread.sleep(5000);
// There should be no new audit entries
List<RecordsManagementAuditEntry> result2 = getAuditTrail(ADMIN_USER);
assertNotNull(result2);
assertEquals(
"Audit results should not have changed after auditing was disabled",
result1.size(), result2.size());
// repeat with a start
rmAuditService.startAuditLog(filePlan);
updateTitle(filePlan, ADMIN_USER);
Thread.sleep(5000);
List<RecordsManagementAuditEntry> result3 = getAuditTrail(ADMIN_USER);
assertNotNull(result3);
assertTrue(
"Expected more results after enabling audit",
result3.size() > result1.size());
Thread.sleep(5000);
// Stop and delete all entries
rmAuditService.stopAuditLog(filePlan);
rmAuditService.clearAuditLog(filePlan);
// There should be no entries
List<RecordsManagementAuditEntry> result4 = getAuditTrail(ADMIN_USER);
assertNotNull(result4);
assertEquals(
"Audit entries should have been cleared",
0, result4.size());
}
// TODO testAuditRMAction
// TODO testGetAuditTrailFile
// TODO testFileAuditTrailAsRecord
public void xtestAuditAuthentication()
{
rmAuditService.stopAuditLog(filePlan);
rmAuditService.clearAuditLog(filePlan);
rmAuditService.startAuditLog(filePlan);
//MutableAuthenticationService authenticationService = serviceRegistry.getAuthenticationService();
//PersonService personService = serviceRegistry.getPersonService();
try
{
personService.deletePerson("baboon");
authenticationService.deleteAuthentication("baboon");
}
catch (Throwable e)
{
// Not serious
}
// Failed login attempt ...
try
{
AuthenticationUtil.pushAuthentication();
authenticationService.authenticate("baboon", "lskdfj".toCharArray());
fail("Expected authentication failure");
}
catch (AuthenticationException e)
{
// Good
}
finally
{
AuthenticationUtil.popAuthentication();
}
rmAuditService.stopAuditLog(filePlan);
List<RecordsManagementAuditEntry> result1 = getAuditTrail(ADMIN_USER);
// Check that the username is reflected correctly in the results
assertFalse("No audit results were generated for the failed login.", result1.isEmpty());
boolean found = false;
for (RecordsManagementAuditEntry entry : result1)
{
String userName = entry.getUserName();
if (userName.equals("baboon"))
{
found = true;
break;
}
}
assertTrue("Expected to hit failed login attempt for user", found);
// Test successful authentication
try
{
personService.deletePerson("cdickons");
authenticationService.deleteAuthentication("cdickons");
}
catch (Throwable e)
{
// Not serious
}
authenticationService.createAuthentication("cdickons", getName().toCharArray());
Map<QName, Serializable> personProperties = new HashMap<QName, Serializable>();
personProperties.put(ContentModel.PROP_USERNAME, "cdickons");
personProperties.put(ContentModel.PROP_FIRSTNAME, "Charles");
personProperties.put(ContentModel.PROP_LASTNAME, "Dickons");
personService.createPerson(personProperties);
rmAuditService.clearAuditLog(filePlan);
rmAuditService.startAuditLog(filePlan);
try
{
AuthenticationUtil.pushAuthentication();
authenticationService.authenticate("cdickons", getName().toCharArray());
}
finally
{
AuthenticationUtil.popAuthentication();
}
rmAuditService.stopAuditLog(filePlan);
List<RecordsManagementAuditEntry> result2 = getAuditTrail(ADMIN_USER);
found = false;
for (RecordsManagementAuditEntry entry : result2)
{
String userName = entry.getUserName();
String fullName = entry.getFullName();
if (userName.equals("cdickons") && EqualsHelper.nullSafeEquals(fullName, "Charles Dickons"))
{
found = true;
break;
}
}
assertTrue("Expected to hit successful login attempt for Charles Dickons (cdickons)", found);
}
/** === Helper methods === */
private List<RecordsManagementAuditEntry> getAuditTrail(String asUser)
{
return getAuditTrail(-1, asUser);
}
private List<RecordsManagementAuditEntry> getAuditTrail(final int expectedCount, String asUser)
{
return getAuditTrail(new RecordsManagementAuditQueryParameters(), expectedCount, asUser);
}
private List<RecordsManagementAuditEntry> getAuditTrail(final RecordsManagementAuditQueryParameters params, final int expectedCount, final String asUser)
{
return doTestInTransaction(new Test<List<RecordsManagementAuditEntry>>()
{
@Override
public List<RecordsManagementAuditEntry> run() throws Exception
{
return rmAuditService.getAuditTrail(params);
}
@Override
public void test(List<RecordsManagementAuditEntry> result) throws Exception
{
assertNotNull(result);
if (expectedCount != -1)
{
assertEquals(expectedCount, result.size());
}
}
}, asUser);
}
private String updateTitle(final NodeRef nodeRef, final String asUser)
{
return doTestInTransaction(new Test<String>()
{
@Override
public String run() throws Exception
{
String updatedProperty = "Updated - " + System.currentTimeMillis();
nodeService.setProperty(nodeRef, ContentModel.PROP_TITLE, updatedProperty);
return updatedProperty;
}
}, asUser);
}
}