diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/audit/query.get.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/repository/audit/query.get.desc.xml index 7955d406b7..e15fe0d100 100644 --- a/config/alfresco/templates/webscripts/org/alfresco/repository/audit/query.get.desc.xml +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/audit/query.get.desc.xml @@ -1,7 +1,7 @@ Alfresco Audit Service Query Get audit events - /api/audit/query/{application}?{k1}={v1},{k2}={v2} + /api/audit/query/{application}?fromId={fromId}&toId={toId}&fromTime={fromTime}&toTime={toTime}&user={user}&forward={forward}&limit={limit}&verbose={verbose}&{k1}={v1}&{k2}={v2} admin required diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/audit/query.get.json.ftl b/config/alfresco/templates/webscripts/org/alfresco/repository/audit/query.get.json.ftl new file mode 100644 index 0000000000..1a5e4a7e8d --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/audit/query.get.json.ftl @@ -0,0 +1,25 @@ +{ + "entries": + [ + <#list entries as entry> + { + "id":${entry.id}, + "application":${entry.application}, + "user":<#if entry.user??>${entry.user}<#else>null, + "time":${entry.time?c}, + "values": + <#if entry.values??> + { + <#assign first=true> + <#list entry.values?keys as k> + <#if entry.values[k]??> + <#if !first>,<#else><#assign first=false>"${k}": + <#assign value = entry.values[k]>"${value}" + + + } + <#else>null + }<#if entry_has_next>, + + ] +} diff --git a/config/alfresco/web-scripts-application-context.xml b/config/alfresco/web-scripts-application-context.xml index b98321d7dd..9535dc03f0 100644 --- a/config/alfresco/web-scripts-application-context.xml +++ b/config/alfresco/web-scripts-application-context.xml @@ -872,6 +872,12 @@ parent="abstractAuditWebScript"> + + + + diff --git a/source/java/org/alfresco/repo/web/scripts/audit/AbstractAuditWebScript.java b/source/java/org/alfresco/repo/web/scripts/audit/AbstractAuditWebScript.java index af90c99def..912fbac976 100644 --- a/source/java/org/alfresco/repo/web/scripts/audit/AbstractAuditWebScript.java +++ b/source/java/org/alfresco/repo/web/scripts/audit/AbstractAuditWebScript.java @@ -40,6 +40,21 @@ public abstract class AbstractAuditWebScript extends DeclarativeWebScript public static final String PARAM_ENABLED = "enabled"; public static final String PARAM_FROM_TIME = "fromTime"; public static final String PARAM_TO_TIME = "toTime"; + public static final String PARAM_FROM_ID = "fromId"; + public static final String PARAM_TO_ID = "toId"; + public static final String PARAM_USER = "user"; + public static final String PARAM_FORWARD = "forward"; + public static final String PARAM_LIMIT = "limit"; + public static final String PARAM_VERBOSE = "verbose"; + + public static final Long DEFAULT_FROM_TIME = null; + public static final Long DEFAULT_TO_TIME = null; + public static final Long DEFAULT_FROM_ID = null; + public static final Long DEFAULT_TO_ID = null; + public static final String DEFAULT_USER = null; + public static final boolean DEFAULT_FORWARD = true; + public static final int DEFAULT_LIMIT = 100; + public static final boolean DEFAULT_VERBOSE = false; public static final String JSON_KEY_ENABLED = "enabled"; public static final String JSON_KEY_APPLICATIONS = "applications"; @@ -47,6 +62,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_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"; + /** * Logger that can be used by subclasses. */ @@ -77,7 +99,7 @@ public abstract class AbstractAuditWebScript extends DeclarativeWebScript * * @return Returns the application name or null if not present */ - protected final String getAppName(WebScriptRequest req) + protected final String getParamAppName(WebScriptRequest req) { Map templateVars = req.getServiceMatch().getTemplateVars(); String app = templateVars.get(PARAM_APPLICATION); @@ -95,7 +117,7 @@ public abstract class AbstractAuditWebScript extends DeclarativeWebScript * * @return Returns the path or null if not present */ - protected String getPath(WebScriptRequest req) + protected String getParamPath(WebScriptRequest req) { Map templateVars = req.getServiceMatch().getTemplateVars(); String paramPath = templateVars.get(PARAM_PATH); @@ -111,35 +133,99 @@ public abstract class AbstractAuditWebScript extends DeclarativeWebScript return paramPath; } - protected boolean getEnableDisable(WebScriptRequest req) + protected boolean getParamEnableDisable(WebScriptRequest req) { String enableStr = req.getParameter(PARAM_ENABLED); return Boolean.parseBoolean(enableStr); } - protected Long getFromTime(WebScriptRequest req) + protected Long getParamFromTime(WebScriptRequest req) { - String timeStr = req.getParameter(PARAM_FROM_TIME); + String paramStr = req.getParameter(PARAM_FROM_TIME); try { - return Long.parseLong(timeStr); + return Long.parseLong(paramStr); } catch (NumberFormatException e) { - return null; + return DEFAULT_TO_TIME; } } - protected Long getToTime(WebScriptRequest req) + protected Long getParamToTime(WebScriptRequest req) { - String timeStr = req.getParameter(PARAM_TO_TIME); + String paramStr = req.getParameter(PARAM_TO_TIME); try { - return Long.parseLong(timeStr); + return Long.parseLong(paramStr); } catch (NumberFormatException e) { - return null; + return DEFAULT_TO_TIME; } } + + protected Long getParamFromId(WebScriptRequest req) + { + String paramStr = req.getParameter(PARAM_FROM_ID); + try + { + return Long.parseLong(paramStr); + } + catch (NumberFormatException e) + { + return DEFAULT_FROM_ID; + } + } + + protected Long getParamToId(WebScriptRequest req) + { + String paramStr = req.getParameter(PARAM_TO_ID); + try + { + return Long.parseLong(paramStr); + } + catch (NumberFormatException e) + { + return DEFAULT_TO_ID; + } + } + + protected String getParamUser(WebScriptRequest req) + { + return req.getParameter(PARAM_USER); + } + + protected boolean getParamForward(WebScriptRequest req) + { + String paramStr = req.getParameter(PARAM_FORWARD); + if (paramStr == null) + { + return DEFAULT_FORWARD; + } + return Boolean.parseBoolean(paramStr); + } + + protected int getParamLimit(WebScriptRequest req) + { + String paramStr = req.getParameter(PARAM_LIMIT); + try + { + return Integer.parseInt(paramStr); + } + catch (NumberFormatException e) + { + return DEFAULT_LIMIT; + } + } + + protected boolean getParamVerbose(WebScriptRequest req) + { + String paramStr = req.getParameter(PARAM_VERBOSE); + if (paramStr == null) + { + return DEFAULT_VERBOSE; + } + return Boolean.parseBoolean(paramStr); + } } diff --git a/source/java/org/alfresco/repo/web/scripts/audit/AuditClearPost.java b/source/java/org/alfresco/repo/web/scripts/audit/AuditClearPost.java index 78d64fcda4..046582621f 100644 --- a/source/java/org/alfresco/repo/web/scripts/audit/AuditClearPost.java +++ b/source/java/org/alfresco/repo/web/scripts/audit/AuditClearPost.java @@ -38,7 +38,7 @@ public class AuditClearPost extends AbstractAuditWebScript { Map model = new HashMap(7); - String appName = getAppName(req); + String appName = getParamAppName(req); if (appName == null) { throw new WebScriptException(Status.STATUS_BAD_REQUEST, "audit.err.app.notProvided"); @@ -49,8 +49,8 @@ public class AuditClearPost extends AbstractAuditWebScript throw new WebScriptException(Status.STATUS_NOT_FOUND, "audit.err.app.notFound", appName); } // Get from/to times - Long fromTime = getFromTime(req); // might be null - Long toTime = getToTime(req); // might be null + Long fromTime = getParamFromTime(req); // might be null + Long toTime = getParamToTime(req); // might be null // Clear int cleared = auditService.clearAudit(appName, fromTime, toTime); diff --git a/source/java/org/alfresco/repo/web/scripts/audit/AuditControlGet.java b/source/java/org/alfresco/repo/web/scripts/audit/AuditControlGet.java index ca99404c94..83e82054c3 100644 --- a/source/java/org/alfresco/repo/web/scripts/audit/AuditControlGet.java +++ b/source/java/org/alfresco/repo/web/scripts/audit/AuditControlGet.java @@ -39,8 +39,8 @@ public class AuditControlGet extends AbstractAuditWebScript { Map model = new HashMap(7); - String appName = getAppName(req); - String path = getPath(req); + String appName = getParamAppName(req); + String path = getParamPath(req); boolean enabledGlobal = auditService.isAuditEnabled(); Map appsByName = auditService.getAuditApplications(); diff --git a/source/java/org/alfresco/repo/web/scripts/audit/AuditControlPost.java b/source/java/org/alfresco/repo/web/scripts/audit/AuditControlPost.java index 1d3b59db16..618db997e7 100644 --- a/source/java/org/alfresco/repo/web/scripts/audit/AuditControlPost.java +++ b/source/java/org/alfresco/repo/web/scripts/audit/AuditControlPost.java @@ -36,10 +36,10 @@ public class AuditControlPost extends AbstractAuditWebScript { Map model = new HashMap(7); - String appName = getAppName(req); - String path = getPath(req); + String appName = getParamAppName(req); + String path = getParamPath(req); - boolean enable = getEnableDisable(req); + boolean enable = getParamEnableDisable(req); if (appName == null) { diff --git a/source/java/org/alfresco/repo/web/scripts/audit/AuditQueryGet.java b/source/java/org/alfresco/repo/web/scripts/audit/AuditQueryGet.java new file mode 100644 index 0000000000..74646693ee --- /dev/null +++ b/source/java/org/alfresco/repo/web/scripts/audit/AuditQueryGet.java @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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 . + */ +package org.alfresco.repo.web.scripts.audit; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +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.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; + +/** + * @author Derek Hulley + * @since 3.4 + */ +public class AuditQueryGet extends AbstractAuditWebScript +{ + @Override + protected Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + final Map model = new HashMap(7); + + String appName = getParamAppName(req); + Long fromTime = getParamFromTime(req); + Long toTime = getParamToTime(req); + Long fromId = getParamFromId(req); + Long toId = getParamToId(req); + String user = getParamUser(req); + boolean forward = getParamForward(req); + int limit = getParamLimit(req); + final boolean verbose = getParamVerbose(req); + + if (appName == null) + { + Map appsByName = auditService.getAuditApplications(); + AuditApplication app = appsByName.get(appName); + if (app == null) + { + throw new WebScriptException(Status.STATUS_NOT_FOUND, "audit.err.app.notFound", appName); + } + } + + // Execute the query + AuditQueryParameters params = new AuditQueryParameters(); + params.setApplicationName(appName); + params.setFromTime(fromTime); + params.setToTime(toTime); + params.setFromId(fromId); + params.setToId(toId); + params.setUser(user); + params.setForward(forward); + + final List> entries = new ArrayList>(limit); + AuditQueryCallback callback = new AuditQueryCallback() + { + @Override + public boolean valuesRequired() + { + return verbose; + } + + @Override + public boolean handleAuditEntryError(Long entryId, String errorMsg, Throwable error) + { + return true; + } + + @Override + public boolean handleAuditEntry( + Long entryId, + String applicationName, + String user, + long time, + Map values) + { + Map entry = new HashMap(11); + entry.put(JSON_QUERY_KEY_ID, entryId); + entry.put(JSON_QUERY_KEY_APPLICATION, applicationName); + if (user != null) + { + entry.put(JSON_QUERY_KEY_USER, user); + } + entry.put(JSON_QUERY_KEY_TIME, new Long(time)); + if (values != null) + { + entry.put(JSON_QUERY_KEY_VALUES, values); + } + entries.add(entry); + + return true; + } + }; + + auditService.auditQuery(callback, params, limit); + + model.put(JSON_KEY_ENTRIES, entries); + + // Done + if (logger.isDebugEnabled()) + { + logger.debug("Result: \n\tRequest: " + req + "\n\tModel: " + model); + } + return model; + } +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/web/scripts/audit/AuditWebScriptTest.java b/source/java/org/alfresco/repo/web/scripts/audit/AuditWebScriptTest.java index f76212de55..605e174fdf 100644 --- a/source/java/org/alfresco/repo/web/scripts/audit/AuditWebScriptTest.java +++ b/source/java/org/alfresco/repo/web/scripts/audit/AuditWebScriptTest.java @@ -47,7 +47,8 @@ public class AuditWebScriptTest extends BaseWebScriptTest private AuditService auditService; private AuthenticationService authenticationService; private String admin; - private boolean globallyEnabled; + private boolean wasGloballyEnabled; + boolean wasRepoEnabled; @Override protected void setUp() throws Exception @@ -60,12 +61,17 @@ public class AuditWebScriptTest extends BaseWebScriptTest AuthenticationUtil.setFullyAuthenticatedUser(admin); - globallyEnabled = auditService.isAuditEnabled(); + wasGloballyEnabled = auditService.isAuditEnabled(); + wasRepoEnabled = auditService.isAuditEnabled(APP_REPO_NAME, APP_REPO_PATH); // Only enable if required - if (!globallyEnabled) + if (!wasGloballyEnabled) { auditService.setAuditEnabled(true); } + if (!wasRepoEnabled) + { + auditService.enableAudit(APP_REPO_NAME, APP_REPO_PATH); + } } @Override @@ -75,7 +81,7 @@ public class AuditWebScriptTest extends BaseWebScriptTest // Leave audit in correct state try { - if (!globallyEnabled) + if (!wasGloballyEnabled) { auditService.setAuditEnabled(false); } @@ -84,6 +90,21 @@ public class AuditWebScriptTest extends BaseWebScriptTest { throw new RuntimeException("Failed to set audit back to globally enabled/disabled state", e); } + try + { + if (wasRepoEnabled) + { + auditService.enableAudit(APP_REPO_NAME, APP_REPO_PATH); + } + else + { + auditService.disableAudit(APP_REPO_NAME, APP_REPO_PATH); + } + } + catch (Throwable e) + { + throw new RuntimeException("Failed to set repo audit back to enabled/disabled state", e); + } } public void testGetWithoutPermissions() throws Exception @@ -121,29 +142,21 @@ public class AuditWebScriptTest extends BaseWebScriptTest { boolean wasEnabled = auditService.isAuditEnabled(); - // We need to set this back after the test - try + if (wasEnabled) { - if (wasEnabled) - { - String url = "/api/audit/control?enable=false"; - TestWebScriptServer.PostRequest req = new TestWebScriptServer.PostRequest(url, "", MimetypeMap.MIMETYPE_JSON); - sendRequest(req, Status.STATUS_OK, admin); - } - else - { - String url = "/api/audit/control?enable=true"; - TestWebScriptServer.PostRequest req = new TestWebScriptServer.PostRequest(url, "", MimetypeMap.MIMETYPE_JSON); - sendRequest(req, Status.STATUS_OK, admin); - } - - // Check that it worked - testGetIsAuditEnabledGlobally(); + String url = "/api/audit/control?enable=false"; + TestWebScriptServer.PostRequest req = new TestWebScriptServer.PostRequest(url, "", MimetypeMap.MIMETYPE_JSON); + sendRequest(req, Status.STATUS_OK, admin); } - finally + else { - auditService.setAuditEnabled(wasEnabled); + String url = "/api/audit/control?enable=true"; + TestWebScriptServer.PostRequest req = new TestWebScriptServer.PostRequest(url, "", MimetypeMap.MIMETYPE_JSON); + sendRequest(req, Status.STATUS_OK, admin); } + + // Check that it worked + testGetIsAuditEnabledGlobally(); } private static final String APP_REPO_NAME = "AlfrescoRepository"; @@ -179,36 +192,47 @@ public class AuditWebScriptTest extends BaseWebScriptTest { boolean wasEnabled = auditService.isAuditEnabled(APP_REPO_NAME, APP_REPO_PATH); - // We need to set this back after the test - try + if (wasEnabled) { - if (wasEnabled) - { - String url = "/api/audit/control/" + APP_REPO_NAME + APP_REPO_PATH + "?enable=false"; - TestWebScriptServer.PostRequest req = new TestWebScriptServer.PostRequest(url, "", MimetypeMap.MIMETYPE_JSON); - sendRequest(req, Status.STATUS_OK, admin); - } - else - { - String url = "/api/audit/control/" + APP_REPO_NAME + APP_REPO_PATH + "?enable=true"; - TestWebScriptServer.PostRequest req = new TestWebScriptServer.PostRequest(url, "", MimetypeMap.MIMETYPE_JSON); - sendRequest(req, Status.STATUS_OK, admin); - } - - // Check that it worked - testGetIsAuditEnabledRepo(); + String url = "/api/audit/control/" + APP_REPO_NAME + APP_REPO_PATH + "?enable=false"; + TestWebScriptServer.PostRequest req = new TestWebScriptServer.PostRequest(url, "", MimetypeMap.MIMETYPE_JSON); + sendRequest(req, Status.STATUS_OK, admin); } - finally + else { - if (wasEnabled) - { - auditService.enableAudit(APP_REPO_NAME, APP_REPO_PATH); - } - else - { - auditService.disableAudit(APP_REPO_NAME, APP_REPO_PATH); - } + String url = "/api/audit/control/" + APP_REPO_NAME + APP_REPO_PATH + "?enable=true"; + TestWebScriptServer.PostRequest req = new TestWebScriptServer.PostRequest(url, "", MimetypeMap.MIMETYPE_JSON); + sendRequest(req, Status.STATUS_OK, admin); } + + // Check that it worked + testGetIsAuditEnabledRepo(); + } + + /** + * Perform a failed login attempt + */ + private void loginWithFailure() throws Exception + { + // Force a failed login + RunAsWork failureWork = new RunAsWork() + { + @Override + public Void doWork() throws Exception + { + try + { + authenticationService.authenticate("domino", "crud".toCharArray()); + fail("Failed to force authentication failure"); + } + catch (AuthenticationException e) + { + // Expected + } + return null; + } + }; + AuthenticationUtil.runAs(failureWork, AuthenticationUtil.getSystemUserName()); } public void testClearAuditRepo() throws Exception @@ -216,61 +240,40 @@ public class AuditWebScriptTest extends BaseWebScriptTest long now = System.currentTimeMillis(); long future = Long.MAX_VALUE; + loginWithFailure(); - boolean wasRepoEnabled = auditService.isAuditEnabled(APP_REPO_NAME, APP_REPO_PATH); - boolean wasEnabled = auditService.isAuditEnabled(); - // We need to set this back after the test - try - { - auditService.setAuditEnabled(true); - auditService.enableAudit(APP_REPO_NAME, APP_REPO_PATH); - - // Force a failed login - RunAsWork failureWork = new RunAsWork() - { - @Override - public Void doWork() throws Exception - { - try - { - authenticationService.authenticate("domino", "crud".toCharArray()); - fail("Failed to force authentication failure"); - } - catch (AuthenticationException e) - { - // Expected - } - return null; - } - }; - AuthenticationUtil.runAs(failureWork, AuthenticationUtil.getSystemUserName()); + // Delete audit entries that could not have happened + String url = "/api/audit/clear/" + APP_REPO_NAME + "?fromTime=" + future; + TestWebScriptServer.PostRequest req = new TestWebScriptServer.PostRequest(url, "", MimetypeMap.MIMETYPE_JSON); + Response response = sendRequest(req, Status.STATUS_OK, admin); + 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 audit entries that could not have happened - String url = "/api/audit/clear/" + APP_REPO_NAME + "?fromTime=" + future; - TestWebScriptServer.PostRequest req = new TestWebScriptServer.PostRequest(url, "", MimetypeMap.MIMETYPE_JSON); - Response response = sendRequest(req, Status.STATUS_OK, admin); - JSONObject json = new JSONObject(response.getContentAsString()); - int cleared = json.getInt(AbstractAuditWebScript.JSON_KEY_CLEARED); - assertEquals("Could not have cleared more than 0", 0, cleared); + 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); + } + + public void testQueryAuditRepo() throws Exception + { + long now = System.currentTimeMillis(); + long future = Long.MAX_VALUE; + + auditService.setAuditEnabled(true); + auditService.enableAudit(APP_REPO_NAME, APP_REPO_PATH); - 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); - } - finally - { - if (wasRepoEnabled) - { - auditService.enableAudit(APP_REPO_NAME, APP_REPO_PATH); - } - else - { - auditService.disableAudit(APP_REPO_NAME, APP_REPO_PATH); - } - auditService.setAuditEnabled(wasEnabled); - } + loginWithFailure(); + + // Delete 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()); + JSONArray jsonEntries = json.getJSONArray(AbstractAuditWebScript.JSON_KEY_ENTRIES); + assertTrue("Expected at least one entry", jsonEntries.length() > 0); } }