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

@@ -2,4 +2,6 @@
audit.err.app.notProvided=Application name not supplied. audit.err.app.notProvided=Application name not supplied.
audit.err.app.notFound=Application not found: {0} audit.err.app.notFound=Application not found: {0}
audit.err.path.notProvided=No path was supplied after the application name. audit.err.path.notProvided=No path was supplied after the application name.
audit.err.action.invalid=Parameter 'action' must be either 'enable' or 'disable' audit.err.action.invalid=Parameter 'action' must be either 'enable' or 'disable'
audit.err.value.classNotFound='valueType' not recognised: {0}
audit.err.value.convertFailed=Unable to convert ''{0}'' to type ''{1}''

View File

@@ -1,7 +1,46 @@
<webscript> <webscript>
<shortname>Alfresco Audit Service Query</shortname> <shortname>Alfresco Audit Service Query</shortname>
<description>Get audit events</description> <description>
<url>/api/audit/query/{application}?fromId={fromId}&amp;toId={toId}&amp;fromTime={fromTime}&amp;toTime={toTime}&amp;user={user}&amp;forward={forward}&amp;limit={limit}&amp;verbose={verbose}&amp;{k1}={v1}&amp;{k2}={v2}</url> <![CDATA[
Query to retrieve audit events.
JSON returned:
{
"count":2,
"entries":
[
{
"id":49,
"application":AlfrescoRepository,
"user":testAuditAuthenticationService,
"time":"2010-09-01T14:02:08.454+01:00",
"values":
{
"\/repository\/login\/error\/user":"testAuditAuthenticationService"
}
},
{
"id":51,
"application":AlfrescoRepository,
"user":null,
"time":"2010-09-01T15:26:07.571+01:00",
"values":
{
"\/repository\/login\/error\/user":"banana"
}
}
]
}
To get the last entry ID:
/api/audit/query/{application}?forward=false&limit=1&verbose=false
Note: It is not possible to get a total result count without executing the query.
]]>
</description>
<url>/api/audit/query/{application}?fromId={fromId}&amp;toId={toId}&amp;fromTime={fromTime}&amp;toTime={toTime}&amp;user={user}&amp;forward={forward}&amp;limit={limit}&amp;verbose={verbose}</url>
<url>/api/audit/query/{application}/{path}?value={value}&amp;valueType={valueType}&amp;fromId={fromId}&amp;toId={toId}&amp;fromTime={fromTime}&amp;toTime={toTime}&amp;user={user}&amp;forward={forward}&amp;limit={limit}&amp;verbose={verbose}</url>
<format default="json" /> <format default="json" />
<authentication>admin</authentication> <authentication>admin</authentication>
<transaction allow="readonly">required</transaction> <transaction allow="readonly">required</transaction>
@@ -9,15 +48,60 @@
<args> <args>
<arg> <arg>
<name>application</name> <name>application</name>
<description>Name of the audit application (mandatory parameter)</description> <description>
<![CDATA[
Name of the audit application (mandatory).
e.g. /api/audit/query/AlfrescoRepository : AlfrescoRepository
]]>
</description>
</arg> </arg>
<arg> <arg>
<name>k1</name> <name>path</name>
<description>First key to query for. If no value is provided, then the present of the key is enough.</description> <description>
<![CDATA[
Full value path to search against.
e.g. /api/audit/query/AlfrescoRepository/repository/login/error/user : /repository/login/error/user
]]>
</description>
</arg> </arg>
<arg> <arg>
<name>v1</name> <name>value</name>
<description>First value to query for. If this is no provided, then the presence of the key is enough.</description> <description>
<![CDATA[
Optional value to search for. If no 'valueType' is specified, then the value will be treated as a String.
]]>
</description>
</arg>
<arg>
<name>valueType</name>
<description>
<![CDATA[
Optional class name to convert the 'value' parameter.
e.g. valueType=java.lang.Long
]]>
</description>
</arg>
<arg>
<name>limit</name>
<description>
<![CDATA[
Optionally limit the number of entries retrieved.
e.g. limit=100
The default, when unspecified, is 100. Using extreme values will result in memory issues
during the FreeMarker template conversion. It is possible to use the lower bound ID-based
queries ('fromId'), in conjunction with the 'limit' parameter, to page through results.
]]>
</description>
</arg>
<arg>
<name>verbose</name>
<description>
<![CDATA[
Determine if entry 'values' should be returned.
e.g. verbose=true : pull back all entry values
e.g. verbose=false : ignore all entry values
]]>
</description>
</arg> </arg>
</args> </args>

View File

@@ -1,20 +1,21 @@
<#escape x as jsonUtils.encodeJSONString(x)>
{ {
"count":${count?c},
"entries": "entries":
[ [
<#list entries as entry> <#list entries as entry>
{ {
"id":${entry.id}, "id":${entry.id?c},
"application":${entry.application}, "application":${entry.application},
"user":<#if entry.user??>${entry.user}<#else>null</#if>, "user":<#if entry.user??>${entry.user}<#else>null</#if>,
"time":${entry.time?c}, "time":"${xmldate(entry.time)}",
"values": "values":
<#if entry.values??> <#if entry.values??>
{ {
<#assign first=true> <#assign first=true>
<#list entry.values?keys as k> <#list entry.values?keys as k>
<#if entry.values[k]??> <#if entry.values[k]??>
<#if !first>,<#else><#assign first=false></#if>"${k}": <#if !first>,<#else><#assign first=false></#if>"${k}":<#assign value = entry.values[k]>"${value}"
<#assign value = entry.values[k]>"${value}"
</#if> </#if>
</#list> </#list>
} }
@@ -23,3 +24,4 @@
</#list> </#list>
] ]
} }
</#escape>

View File

@@ -38,6 +38,8 @@ public abstract class AbstractAuditWebScript extends DeclarativeWebScript
public static final String PARAM_APPLICATION = "application"; public static final String PARAM_APPLICATION = "application";
public static final String PARAM_PATH="path"; public static final String PARAM_PATH="path";
public static final String PARAM_ENABLED = "enabled"; 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_FROM_TIME = "fromTime";
public static final String PARAM_TO_TIME = "toTime"; public static final String PARAM_TO_TIME = "toTime";
public static final String PARAM_FROM_ID = "fromId"; 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_PATH = "path";
public static final String JSON_KEY_CLEARED = "cleared"; 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_KEY_ENTRIES = "entries";
public static final String JSON_QUERY_KEY_ID = "id"; public static final String JSON_KEY_ENTRY_ID = "id";
public static final String JSON_QUERY_KEY_APPLICATION = "application"; public static final String JSON_KEY_ENTRY_APPLICATION = "application";
public static final String JSON_QUERY_KEY_USER = "user"; public static final String JSON_KEY_ENTRY_USER = "user";
public static final String JSON_QUERY_KEY_TIME = "time"; public static final String JSON_KEY_ENTRY_TIME = "time";
public static final String JSON_QUERY_KEY_VALUES = "values"; public static final String JSON_KEY_ENTRY_VALUES = "values";
/** /**
* Logger that can be used by subclasses. * Logger that can be used by subclasses.
@@ -139,6 +142,19 @@ public abstract class AbstractAuditWebScript extends DeclarativeWebScript
return Boolean.parseBoolean(enableStr); 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) protected Long getParamFromTime(WebScriptRequest req)
{ {
String paramStr = req.getParameter(PARAM_FROM_TIME); 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) protected Long getParamToTime(WebScriptRequest req)
{ {
String paramStr = req.getParameter(PARAM_TO_TIME); 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) protected Long getParamFromId(WebScriptRequest req)
{ {
String paramStr = req.getParameter(PARAM_FROM_ID); 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) protected Long getParamToId(WebScriptRequest req)
{ {
String paramStr = req.getParameter(PARAM_TO_ID); 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) protected String getParamUser(WebScriptRequest req)
{ {
return req.getParameter(PARAM_USER); return req.getParameter(PARAM_USER);
} }
/**
* @see #DEFAULT_FORWARD
*/
protected boolean getParamForward(WebScriptRequest req) protected boolean getParamForward(WebScriptRequest req)
{ {
String paramStr = req.getParameter(PARAM_FORWARD); String paramStr = req.getParameter(PARAM_FORWARD);
@@ -206,6 +237,9 @@ public abstract class AbstractAuditWebScript extends DeclarativeWebScript
return Boolean.parseBoolean(paramStr); return Boolean.parseBoolean(paramStr);
} }
/**
* @see #DEFAULT_LIMIT
*/
protected int getParamLimit(WebScriptRequest req) protected int getParamLimit(WebScriptRequest req)
{ {
String paramStr = req.getParameter(PARAM_LIMIT); String paramStr = req.getParameter(PARAM_LIMIT);
@@ -219,6 +253,9 @@ public abstract class AbstractAuditWebScript extends DeclarativeWebScript
} }
} }
/**
* @see #DEFAULT_VERBOSE
*/
protected boolean getParamVerbose(WebScriptRequest req) protected boolean getParamVerbose(WebScriptRequest req)
{ {
String paramStr = req.getParameter(PARAM_VERBOSE); 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.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; 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.AuditQueryParameters;
import org.alfresco.service.cmr.audit.AuditService.AuditApplication; import org.alfresco.service.cmr.audit.AuditService.AuditApplication;
import org.alfresco.service.cmr.audit.AuditService.AuditQueryCallback; 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.Cache;
import org.springframework.extensions.webscripts.Status; import org.springframework.extensions.webscripts.Status;
import org.springframework.extensions.webscripts.WebScriptException; 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); final Map<String, Object> model = new HashMap<String, Object>(7);
String appName = getParamAppName(req); String appName = getParamAppName(req);
String path = getParamPath(req);
Serializable value = getParamValue(req);
String valueType = getParamValueType(req);
Long fromTime = getParamFromTime(req); Long fromTime = getParamFromTime(req);
Long toTime = getParamToTime(req); Long toTime = getParamToTime(req);
Long fromId = getParamFromId(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 // Execute the query
AuditQueryParameters params = new AuditQueryParameters(); AuditQueryParameters params = new AuditQueryParameters();
params.setApplicationName(appName); params.setApplicationName(appName);
@@ -72,6 +97,10 @@ public class AuditQueryGet extends AbstractAuditWebScript
params.setToId(toId); params.setToId(toId);
params.setUser(user); params.setUser(user);
params.setForward(forward); params.setForward(forward);
if (path != null || value != null)
{
params.addSearchKey(path, value);
}
final List<Map<String, Object>> entries = new ArrayList<Map<String,Object>>(limit); final List<Map<String, Object>> entries = new ArrayList<Map<String,Object>>(limit);
AuditQueryCallback callback = new AuditQueryCallback() AuditQueryCallback callback = new AuditQueryCallback()
@@ -97,16 +126,16 @@ public class AuditQueryGet extends AbstractAuditWebScript
Map<String, Serializable> values) Map<String, Serializable> values)
{ {
Map<String, Object> entry = new HashMap<String, Object>(11); Map<String, Object> entry = new HashMap<String, Object>(11);
entry.put(JSON_QUERY_KEY_ID, entryId); entry.put(JSON_KEY_ENTRY_ID, entryId);
entry.put(JSON_QUERY_KEY_APPLICATION, applicationName); entry.put(JSON_KEY_ENTRY_APPLICATION, applicationName);
if (user != null) 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) if (values != null)
{ {
entry.put(JSON_QUERY_KEY_VALUES, values); entry.put(JSON_KEY_ENTRY_VALUES, values);
} }
entries.add(entry); entries.add(entry);
@@ -116,6 +145,7 @@ public class AuditQueryGet extends AbstractAuditWebScript
auditService.auditQuery(callback, params, limit); auditService.auditQuery(callback, params, limit);
model.put(JSON_KEY_ENTRY_COUNT, entries.size());
model.put(JSON_KEY_ENTRIES, entries); model.put(JSON_KEY_ENTRIES, entries);
// Done // Done

View File

@@ -18,6 +18,7 @@
*/ */
package org.alfresco.repo.web.scripts.audit; package org.alfresco.repo.web.scripts.audit;
import java.util.Date;
import java.util.Map; import java.util.Map;
import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.content.MimetypeMap;
@@ -31,6 +32,7 @@ import org.alfresco.service.cmr.security.AuthenticationService;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONObject; import org.json.JSONObject;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.extensions.surf.util.ISO8601DateFormat;
import org.springframework.extensions.webscripts.Status; import org.springframework.extensions.webscripts.Status;
import org.springframework.extensions.webscripts.TestWebScriptServer; import org.springframework.extensions.webscripts.TestWebScriptServer;
import org.springframework.extensions.webscripts.TestWebScriptServer.Response; import org.springframework.extensions.webscripts.TestWebScriptServer.Response;
@@ -212,7 +214,7 @@ public class AuditWebScriptTest extends BaseWebScriptTest
/** /**
* Perform a failed login attempt * Perform a failed login attempt
*/ */
private void loginWithFailure() throws Exception private void loginWithFailure(final String username) throws Exception
{ {
// Force a failed login // Force a failed login
RunAsWork<Void> failureWork = new RunAsWork<Void>() RunAsWork<Void> failureWork = new RunAsWork<Void>()
@@ -222,7 +224,7 @@ public class AuditWebScriptTest extends BaseWebScriptTest
{ {
try try
{ {
authenticationService.authenticate("domino", "crud".toCharArray()); authenticationService.authenticate(username, "crud".toCharArray());
fail("Failed to force authentication failure"); fail("Failed to force authentication failure");
} }
catch (AuthenticationException e) catch (AuthenticationException e)
@@ -240,7 +242,7 @@ public class AuditWebScriptTest extends BaseWebScriptTest
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
long future = Long.MAX_VALUE; long future = Long.MAX_VALUE;
loginWithFailure(); loginWithFailure(getName());
// Delete audit entries that could not have happened // Delete audit entries that could not have happened
String url = "/api/audit/clear/" + APP_REPO_NAME + "?fromTime=" + future; 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()); JSONObject json = new JSONObject(response.getContentAsString());
int cleared = json.getInt(AbstractAuditWebScript.JSON_KEY_CLEARED); int cleared = json.getInt(AbstractAuditWebScript.JSON_KEY_CLEARED);
assertEquals("Could not have cleared more than 0", 0, 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; url = "/api/audit/clear/" + APP_REPO_NAME + "?fromTime=" + now + "&toTime=" + future;
req = new TestWebScriptServer.PostRequest(url, "", MimetypeMap.MIMETYPE_JSON); req = new TestWebScriptServer.PostRequest(url, "", MimetypeMap.MIMETYPE_JSON);
response = sendRequest(req, Status.STATUS_OK, admin); response = sendRequest(req, Status.STATUS_OK, admin);
json = new JSONObject(response.getContentAsString()); json = new JSONObject(response.getContentAsString());
cleared = json.getInt(AbstractAuditWebScript.JSON_KEY_CLEARED); cleared = json.getInt(AbstractAuditWebScript.JSON_KEY_CLEARED);
assertTrue("Should have cleared at least 1 entry", cleared > 0); 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 public void testQueryAuditRepo() throws Exception
{ {
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
@@ -266,14 +277,98 @@ public class AuditWebScriptTest extends BaseWebScriptTest
auditService.setAuditEnabled(true); auditService.setAuditEnabled(true);
auditService.enableAudit(APP_REPO_NAME, APP_REPO_PATH); 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"; String url = "/api/audit/query/" + APP_REPO_NAME + "?fromTime=" + now + "&verbose=true";
TestWebScriptServer.GetRequest req = new TestWebScriptServer.GetRequest(url); TestWebScriptServer.GetRequest req = new TestWebScriptServer.GetRequest(url);
Response response = sendRequest(req, Status.STATUS_OK, admin); Response response = sendRequest(req, Status.STATUS_OK, admin);
JSONObject json = new JSONObject(response.getContentAsString()); JSONObject json = new JSONObject(response.getContentAsString());
Long entryCount = json.getLong(AbstractAuditWebScript.JSON_KEY_ENTRY_COUNT);
JSONArray jsonEntries = json.getJSONArray(AbstractAuditWebScript.JSON_KEY_ENTRIES); JSONArray jsonEntries = json.getJSONArray(AbstractAuditWebScript.JSON_KEY_ENTRIES);
assertTrue("Expected at least one entry", jsonEntries.length() > 0); 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);
} }
} }