ALF-4106 (ALF-4103): AuditService REST API; Query

- Added path- and value-based queries
 - Added limits
 - .desc.xml documentation
 - Further unit tests


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@22187 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Derek Hulley
2010-09-02 15:13:42 +00:00
parent 6ac734a083
commit 10d3c01b3e
6 changed files with 278 additions and 28 deletions

View File

@@ -38,6 +38,8 @@ public abstract class AbstractAuditWebScript extends DeclarativeWebScript
public static final String PARAM_APPLICATION = "application";
public static final String PARAM_PATH="path";
public static final String PARAM_ENABLED = "enabled";
public static final String PARAM_VALUE = "value";
public static final String PARAM_VALUE_TYPE = "valueType";
public static final String PARAM_FROM_TIME = "fromTime";
public static final String PARAM_TO_TIME = "toTime";
public static final String PARAM_FROM_ID = "fromId";
@@ -62,12 +64,13 @@ public abstract class AbstractAuditWebScript extends DeclarativeWebScript
public static final String JSON_KEY_PATH = "path";
public static final String JSON_KEY_CLEARED = "cleared";
public static final String JSON_KEY_ENTRY_COUNT = "count";
public static final String JSON_KEY_ENTRIES = "entries";
public static final String JSON_QUERY_KEY_ID = "id";
public static final String JSON_QUERY_KEY_APPLICATION = "application";
public static final String JSON_QUERY_KEY_USER = "user";
public static final String JSON_QUERY_KEY_TIME = "time";
public static final String JSON_QUERY_KEY_VALUES = "values";
public static final String JSON_KEY_ENTRY_ID = "id";
public static final String JSON_KEY_ENTRY_APPLICATION = "application";
public static final String JSON_KEY_ENTRY_USER = "user";
public static final String JSON_KEY_ENTRY_TIME = "time";
public static final String JSON_KEY_ENTRY_VALUES = "values";
/**
* Logger that can be used by subclasses.
@@ -139,6 +142,19 @@ public abstract class AbstractAuditWebScript extends DeclarativeWebScript
return Boolean.parseBoolean(enableStr);
}
protected String getParamValue(WebScriptRequest req)
{
return req.getParameter(PARAM_VALUE);
}
protected String getParamValueType(WebScriptRequest req)
{
return req.getParameter(PARAM_VALUE_TYPE);
}
/**
* @see #DEFAULT_FROM_TIME
*/
protected Long getParamFromTime(WebScriptRequest req)
{
String paramStr = req.getParameter(PARAM_FROM_TIME);
@@ -152,6 +168,9 @@ public abstract class AbstractAuditWebScript extends DeclarativeWebScript
}
}
/**
* @see #DEFAULT_TO_TIME
*/
protected Long getParamToTime(WebScriptRequest req)
{
String paramStr = req.getParameter(PARAM_TO_TIME);
@@ -165,6 +184,9 @@ public abstract class AbstractAuditWebScript extends DeclarativeWebScript
}
}
/**
* @see #DEFAULT_FROM_ID
*/
protected Long getParamFromId(WebScriptRequest req)
{
String paramStr = req.getParameter(PARAM_FROM_ID);
@@ -178,6 +200,9 @@ public abstract class AbstractAuditWebScript extends DeclarativeWebScript
}
}
/**
* @see #DEFAULT_TO_ID
*/
protected Long getParamToId(WebScriptRequest req)
{
String paramStr = req.getParameter(PARAM_TO_ID);
@@ -191,11 +216,17 @@ public abstract class AbstractAuditWebScript extends DeclarativeWebScript
}
}
/**
* @see #DEFAULT_USER
*/
protected String getParamUser(WebScriptRequest req)
{
return req.getParameter(PARAM_USER);
}
/**
* @see #DEFAULT_FORWARD
*/
protected boolean getParamForward(WebScriptRequest req)
{
String paramStr = req.getParameter(PARAM_FORWARD);
@@ -206,6 +237,9 @@ public abstract class AbstractAuditWebScript extends DeclarativeWebScript
return Boolean.parseBoolean(paramStr);
}
/**
* @see #DEFAULT_LIMIT
*/
protected int getParamLimit(WebScriptRequest req)
{
String paramStr = req.getParameter(PARAM_LIMIT);
@@ -219,6 +253,9 @@ public abstract class AbstractAuditWebScript extends DeclarativeWebScript
}
}
/**
* @see #DEFAULT_VERBOSE
*/
protected boolean getParamVerbose(WebScriptRequest req)
{
String paramStr = req.getParameter(PARAM_VERBOSE);

View File

@@ -20,6 +20,7 @@ package org.alfresco.repo.web.scripts.audit;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -27,6 +28,7 @@ import java.util.Map;
import org.alfresco.service.cmr.audit.AuditQueryParameters;
import org.alfresco.service.cmr.audit.AuditService.AuditApplication;
import org.alfresco.service.cmr.audit.AuditService.AuditQueryCallback;
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
import org.springframework.extensions.webscripts.Cache;
import org.springframework.extensions.webscripts.Status;
import org.springframework.extensions.webscripts.WebScriptException;
@@ -44,6 +46,10 @@ public class AuditQueryGet extends AbstractAuditWebScript
final Map<String, Object> model = new HashMap<String, Object>(7);
String appName = getParamAppName(req);
String path = getParamPath(req);
Serializable value = getParamValue(req);
String valueType = getParamValueType(req);
Long fromTime = getParamFromTime(req);
Long toTime = getParamToTime(req);
Long fromId = getParamFromId(req);
@@ -63,6 +69,25 @@ public class AuditQueryGet extends AbstractAuditWebScript
}
}
// Transform the value to the correct type
if (value != null && valueType != null)
{
try
{
@SuppressWarnings("unchecked")
Class<? extends Serializable> clazz = (Class<? extends Serializable>) Class.forName(valueType);
value = DefaultTypeConverter.INSTANCE.convert(clazz, value);
}
catch (ClassNotFoundException e)
{
throw new WebScriptException(Status.STATUS_BAD_REQUEST, "audit.err.value.classNotFound", valueType);
}
catch (Throwable e)
{
throw new WebScriptException(Status.STATUS_BAD_REQUEST, "audit.err.value.convertFailed", value, valueType);
}
}
// Execute the query
AuditQueryParameters params = new AuditQueryParameters();
params.setApplicationName(appName);
@@ -72,6 +97,10 @@ public class AuditQueryGet extends AbstractAuditWebScript
params.setToId(toId);
params.setUser(user);
params.setForward(forward);
if (path != null || value != null)
{
params.addSearchKey(path, value);
}
final List<Map<String, Object>> entries = new ArrayList<Map<String,Object>>(limit);
AuditQueryCallback callback = new AuditQueryCallback()
@@ -97,16 +126,16 @@ public class AuditQueryGet extends AbstractAuditWebScript
Map<String, Serializable> values)
{
Map<String, Object> entry = new HashMap<String, Object>(11);
entry.put(JSON_QUERY_KEY_ID, entryId);
entry.put(JSON_QUERY_KEY_APPLICATION, applicationName);
entry.put(JSON_KEY_ENTRY_ID, entryId);
entry.put(JSON_KEY_ENTRY_APPLICATION, applicationName);
if (user != null)
{
entry.put(JSON_QUERY_KEY_USER, user);
entry.put(JSON_KEY_ENTRY_USER, user);
}
entry.put(JSON_QUERY_KEY_TIME, new Long(time));
entry.put(JSON_KEY_ENTRY_TIME, new Date(time));
if (values != null)
{
entry.put(JSON_QUERY_KEY_VALUES, values);
entry.put(JSON_KEY_ENTRY_VALUES, values);
}
entries.add(entry);
@@ -116,6 +145,7 @@ public class AuditQueryGet extends AbstractAuditWebScript
auditService.auditQuery(callback, params, limit);
model.put(JSON_KEY_ENTRY_COUNT, entries.size());
model.put(JSON_KEY_ENTRIES, entries);
// Done

View File

@@ -18,6 +18,7 @@
*/
package org.alfresco.repo.web.scripts.audit;
import java.util.Date;
import java.util.Map;
import org.alfresco.repo.content.MimetypeMap;
@@ -31,6 +32,7 @@ import org.alfresco.service.cmr.security.AuthenticationService;
import org.json.JSONArray;
import org.json.JSONObject;
import org.springframework.context.ApplicationContext;
import org.springframework.extensions.surf.util.ISO8601DateFormat;
import org.springframework.extensions.webscripts.Status;
import org.springframework.extensions.webscripts.TestWebScriptServer;
import org.springframework.extensions.webscripts.TestWebScriptServer.Response;
@@ -212,7 +214,7 @@ public class AuditWebScriptTest extends BaseWebScriptTest
/**
* Perform a failed login attempt
*/
private void loginWithFailure() throws Exception
private void loginWithFailure(final String username) throws Exception
{
// Force a failed login
RunAsWork<Void> failureWork = new RunAsWork<Void>()
@@ -222,7 +224,7 @@ public class AuditWebScriptTest extends BaseWebScriptTest
{
try
{
authenticationService.authenticate("domino", "crud".toCharArray());
authenticationService.authenticate(username, "crud".toCharArray());
fail("Failed to force authentication failure");
}
catch (AuthenticationException e)
@@ -240,7 +242,7 @@ public class AuditWebScriptTest extends BaseWebScriptTest
long now = System.currentTimeMillis();
long future = Long.MAX_VALUE;
loginWithFailure();
loginWithFailure(getName());
// Delete audit entries that could not have happened
String url = "/api/audit/clear/" + APP_REPO_NAME + "?fromTime=" + future;
@@ -249,15 +251,24 @@ public class AuditWebScriptTest extends BaseWebScriptTest
JSONObject json = new JSONObject(response.getContentAsString());
int cleared = json.getInt(AbstractAuditWebScript.JSON_KEY_CLEARED);
assertEquals("Could not have cleared more than 0", 0, cleared);
// Delete the entry (at least)
url = "/api/audit/clear/" + APP_REPO_NAME + "?fromTime=" + now + "&toTime=" + future;
req = new TestWebScriptServer.PostRequest(url, "", MimetypeMap.MIMETYPE_JSON);
response = sendRequest(req, Status.STATUS_OK, admin);
json = new JSONObject(response.getContentAsString());
cleared = json.getInt(AbstractAuditWebScript.JSON_KEY_CLEARED);
assertTrue("Should have cleared at least 1 entry", cleared > 0);
// Delete all entries
url = "/api/audit/clear/" + APP_REPO_NAME;;
req = new TestWebScriptServer.PostRequest(url, "", MimetypeMap.MIMETYPE_JSON);
response = sendRequest(req, Status.STATUS_OK, admin);
json = new JSONObject(response.getContentAsString());
cleared = json.getInt(AbstractAuditWebScript.JSON_KEY_CLEARED);
}
@SuppressWarnings("unused")
public void testQueryAuditRepo() throws Exception
{
long now = System.currentTimeMillis();
@@ -266,14 +277,98 @@ public class AuditWebScriptTest extends BaseWebScriptTest
auditService.setAuditEnabled(true);
auditService.enableAudit(APP_REPO_NAME, APP_REPO_PATH);
loginWithFailure();
loginWithFailure(getName());
// Delete audit entries that could not have happened
// Query for audit entries that could not have happened
String url = "/api/audit/query/" + APP_REPO_NAME + "?fromTime=" + now + "&verbose=true";
TestWebScriptServer.GetRequest req = new TestWebScriptServer.GetRequest(url);
Response response = sendRequest(req, Status.STATUS_OK, admin);
JSONObject json = new JSONObject(response.getContentAsString());
Long entryCount = json.getLong(AbstractAuditWebScript.JSON_KEY_ENTRY_COUNT);
JSONArray jsonEntries = json.getJSONArray(AbstractAuditWebScript.JSON_KEY_ENTRIES);
assertTrue("Expected at least one entry", jsonEntries.length() > 0);
assertEquals("Entry count and physical count don't match", new Long(jsonEntries.length()), entryCount);
JSONObject jsonEntry = jsonEntries.getJSONObject(0);
Long entryId = jsonEntry.getLong(AbstractAuditWebScript.JSON_KEY_ENTRY_ID);
assertNotNull("No entry ID", entryId);
String entryTimeStr = jsonEntry.getString(AbstractAuditWebScript.JSON_KEY_ENTRY_TIME);
assertNotNull("No entry time String", entryTimeStr);
Date entryTime = ISO8601DateFormat.parse((String)entryTimeStr); // Check conversion
JSONObject jsonValues = jsonEntry.getJSONObject(AbstractAuditWebScript.JSON_KEY_ENTRY_VALUES);
String entryUsername = jsonValues.getString("/repository/login/error/user");
assertEquals("Didn't find the login-failure-user", getName(), entryUsername);
// Query using well-known ID
Long fromEntryId = entryId; // Search is inclusive on the 'from' side
Long toEntryId = entryId.longValue() + 1L; // Search is exclusive on the 'to' side
url = "/api/audit/query/" + APP_REPO_NAME + "?fromId=" + fromEntryId + "&toId=" + toEntryId;
req = new TestWebScriptServer.GetRequest(url);
response = sendRequest(req, Status.STATUS_OK, admin);
json = new JSONObject(response.getContentAsString());
jsonEntries = json.getJSONArray(AbstractAuditWebScript.JSON_KEY_ENTRIES);
assertEquals("Incorrect number of search results", 1, jsonEntries.length());
// Query using a non-existent entry path
url = "/api/audit/query/" + APP_REPO_NAME + "/repository/login/error/userXXX" + "?verbose=true";
req = new TestWebScriptServer.GetRequest(url);
response = sendRequest(req, Status.STATUS_OK, admin);
json = new JSONObject(response.getContentAsString());
jsonEntries = json.getJSONArray(AbstractAuditWebScript.JSON_KEY_ENTRIES);
assertTrue("Should not have found anything", jsonEntries.length() == 0);
// Query using a good entry path
url = "/api/audit/query/" + APP_REPO_NAME + "/repository/login/error/user" + "?verbose=true";
req = new TestWebScriptServer.GetRequest(url);
response = sendRequest(req, Status.STATUS_OK, admin);
json = new JSONObject(response.getContentAsString());
jsonEntries = json.getJSONArray(AbstractAuditWebScript.JSON_KEY_ENTRIES);
assertTrue("Should have found entries", jsonEntries.length() > 0);
// Now login with failure using a GUID and ensure that we can find it
String missingUser = new Long(System.currentTimeMillis()).toString();
// Query for event that has not happened
url = "/api/audit/query/" + APP_REPO_NAME + "/repository/login/error/user" + "?value=" + missingUser;
req = new TestWebScriptServer.GetRequest(url);
response = sendRequest(req, Status.STATUS_OK, admin);
json = new JSONObject(response.getContentAsString());
jsonEntries = json.getJSONArray(AbstractAuditWebScript.JSON_KEY_ENTRIES);
assertEquals("Incorrect number of search results", 0, jsonEntries.length());
loginWithFailure(missingUser);
// Query for event that has happened once
url = "/api/audit/query/" + APP_REPO_NAME + "/repository/login/error/user" + "?value=" + missingUser;
req = new TestWebScriptServer.GetRequest(url);
response = sendRequest(req, Status.STATUS_OK, admin);
json = new JSONObject(response.getContentAsString());
jsonEntries = json.getJSONArray(AbstractAuditWebScript.JSON_KEY_ENTRIES);
assertEquals("Incorrect number of search results", 1, jsonEntries.length());
// Query for event, but casting the value to the incorrect type
url = "/api/audit/query/" + APP_REPO_NAME + "/repository/login/error/user" + "?value=" + missingUser + "&valueType=java.lang.Long";
req = new TestWebScriptServer.GetRequest(url);
response = sendRequest(req, Status.STATUS_OK, admin);
json = new JSONObject(response.getContentAsString());
jsonEntries = json.getJSONArray(AbstractAuditWebScript.JSON_KEY_ENTRIES);
assertEquals("Incorrect number of search results", 0, jsonEntries.length());
// Test what happens when the target data needs encoding
String oddUser = "%$£\\\"\'";
loginWithFailure(oddUser);
// Query for the event limiting to one by count and descending (i.e. get last)
url = "/api/audit/query/" + APP_REPO_NAME + "?forward=false&limit=1&verbose=true";
req = new TestWebScriptServer.GetRequest(url);
response = sendRequest(req, Status.STATUS_OK, admin);
json = new JSONObject(response.getContentAsString());
jsonEntries = json.getJSONArray(AbstractAuditWebScript.JSON_KEY_ENTRIES);
assertEquals("Incorrect number of search results", 1, jsonEntries.length());
jsonEntry = jsonEntries.getJSONObject(0);
entryId = jsonEntry.getLong(AbstractAuditWebScript.JSON_KEY_ENTRY_ID);
assertNotNull("No entry ID", entryId);
jsonValues = jsonEntry.getJSONObject(AbstractAuditWebScript.JSON_KEY_ENTRY_VALUES);
entryUsername = jsonValues.getString("/repository/login/error/user");
assertEquals("Didn't find the login-failure-user", oddUser, entryUsername);
}
}