Refactoring audit subsystem to clean-up and eliminate back-dependency from core to CLI; still needs additional work.

Change-Id: I93c04c94f27b7b89c582b359eebe125458a573a7
diff --git a/cli/src/main/java/org/onosproject/cli/AbstractShellCommand.java b/cli/src/main/java/org/onosproject/cli/AbstractShellCommand.java
index 494d7d8..cbcd8d4 100644
--- a/cli/src/main/java/org/onosproject/cli/AbstractShellCommand.java
+++ b/cli/src/main/java/org/onosproject/cli/AbstractShellCommand.java
@@ -29,6 +29,7 @@
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.security.AuditService;
 import org.slf4j.Logger;
 
 import static org.slf4j.LoggerFactory.getLogger;
@@ -43,63 +44,12 @@
 
     protected static final Logger log = getLogger(AbstractShellCommand.class);
 
+    private final ObjectMapper mapper = new ObjectMapper();
+
     @Option(name = "-j", aliases = "--json", description = "Output JSON",
             required = false, multiValued = false)
     private boolean json = false;
 
-    private static String auditFile = "all";
-    private static boolean auditEnabled = false;
-
-    /**
-     * To check if CLI Audit is enabled.
-     *
-     * @return true if the CLI Audit is enabled.
-     */
-    private static boolean isEnabled() {
-        return auditEnabled;
-    }
-
-    /**
-     * To enable CLI Audit.
-     */
-    public static void enableAudit() {
-        auditEnabled = true;
-    }
-
-    /**
-     * To disable CLI Audit.
-     */
-    public static void disableAudit() {
-        auditEnabled = false;
-    }
-
-    /**
-     * To set audit file type which CLI Audit logs must be saved.
-     *
-     * @param auditFile file that CLI Audit logs must be saved.
-     */
-    public static void setAuditFile(String auditFile) {
-        AbstractShellCommand.auditFile = auditFile;
-    }
-
-    /**
-     * To save audit logs into the log file.
-     *
-     * @param msg audit message.
-     */
-    private static void saveAuditLog(String msg) {
-        if (isEnabled()) {
-            if (auditFile.equals("all")) {
-                log.info(msg);
-                log.info("AuditLog : " + msg);
-            } else if (auditFile.equals("karaf")) {
-                log.info(msg);
-            } else if (auditFile.equals("audit")) {
-                log.info("AuditLog : " + msg);
-            }
-        }
-    }
-
     /**
      * Returns the reference to the implementation of the specified service.
      *
@@ -109,7 +59,6 @@
      * @throws org.onlab.osgi.ServiceNotFoundException if service is unavailable
      */
     public static <T> T get(Class<T> serviceClass) {
-        saveAuditLog("Audit ");
         return DefaultServiceDirectory.getService(serviceClass);
     }
 
@@ -204,8 +153,9 @@
     }
 
     @Override
-    public Object execute() throws Exception {
+    public final Object execute() throws Exception {
         try {
+            auditCommand();
             doExecute();
         } catch (ServiceNotFoundException e) {
             error(e.getMessage());
@@ -213,15 +163,23 @@
         return null;
     }
 
-    protected void doExecute() throws Exception {
-        try {
-            execute();
-        } catch (ServiceNotFoundException e) {
-            error(e.getMessage());
+    // Handles auditing
+    private void auditCommand() {
+        AuditService auditService = get(AuditService.class);
+        if (auditService != null && auditService.isAuditing()) {
+            // FIXME: Compose and log audit message here; this is a hack
+            String user = "foo"; // FIXME
+            String action = Thread.currentThread().getName().substring(5); // FIXME
+            auditService.logUserAction(user, action);
         }
     }
 
-    private final ObjectMapper mapper = new ObjectMapper();
+    /**
+     * Body of the shell command.
+     *
+     * @throws Exception thrown when problem is encountered
+     */
+    protected abstract void doExecute() throws Exception;
 
     @Override
     public ObjectMapper mapper() {
diff --git a/core/api/src/main/java/org/onosproject/security/AuditService.java b/core/api/src/main/java/org/onosproject/security/AuditService.java
new file mode 100644
index 0000000..f98eda4
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/security/AuditService.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.security;
+
+/**
+ * Service for enabling audit logging.
+ */
+public interface AuditService {
+
+    /**
+     * Returns true if auditing is enabled.
+     *
+     * @return true if audit enabled; false otherwise
+     */
+    boolean isAuditing();
+
+    /**
+     * Logs the specified user action.
+     *
+     * @param user   user that initiated the action
+     * @param action action being logged
+     */
+    void logUserAction(String user, String action);
+
+}
diff --git a/core/net/BUILD b/core/net/BUILD
index 50e0a3f..8047609 100644
--- a/core/net/BUILD
+++ b/core/net/BUILD
@@ -1,9 +1,7 @@
 COMPILE_DEPS = CORE_DEPS + JACKSON + METRICS + KRYO + [
     "//core/common:onos-core-common",
-    "//utils/rest:onlab-rest",
     "//core/store/serializers:onos-core-serializers",
     "//core/store/primitives:onos-core-primitives",
-    "//cli:onos-cli",
     "@org_osgi_service_cm//jar",
 ]
 
diff --git a/core/net/src/main/java/org/onosproject/audit/impl/AuditManager.java b/core/net/src/main/java/org/onosproject/audit/impl/AuditManager.java
new file mode 100644
index 0000000..a871f3c
--- /dev/null
+++ b/core/net/src/main/java/org/onosproject/audit/impl/AuditManager.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2016-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.audit.impl;
+
+import org.onosproject.cfg.ComponentConfigService;
+import org.onosproject.security.AuditService;
+import org.osgi.service.component.ComponentContext;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Modified;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Dictionary;
+
+import static org.onlab.util.Tools.get;
+import static org.onosproject.net.OsgiPropertyConstants.AUDIT_LOGGER;
+import static org.onosproject.net.OsgiPropertyConstants.AUDIT_LOGGER_DEFAULT;
+import static org.onosproject.net.OsgiPropertyConstants.AUDIT_ENABLED;
+import static org.onosproject.net.OsgiPropertyConstants.AUDIT_ENABLED_DEFAULT;
+
+/**
+ * Component to manage audit logging.
+ */
+@Component(
+        immediate = true,
+        service = { AuditService.class },
+        property = {
+                AUDIT_ENABLED + ":Boolean=" + AUDIT_ENABLED_DEFAULT,
+                AUDIT_LOGGER + "=" + AUDIT_LOGGER_DEFAULT,
+        })
+public class AuditManager implements AuditService {
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    private Logger auditLog = log;
+
+    /** Specifies whether or not audit logging is enabled. */
+    private boolean auditEnabled = AUDIT_ENABLED_DEFAULT;
+
+    /** Name of the audit logger. */
+    private String auditFile = AUDIT_LOGGER_DEFAULT;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    protected ComponentConfigService cfgService;
+
+    @Activate
+    protected void activate(ComponentContext ctx) {
+        cfgService.registerProperties(getClass());
+        modified(ctx);
+        log.info("Started");
+    }
+
+    @Deactivate
+    protected void deactivate(ComponentContext ctx) {
+        log.info("Stopped");
+    }
+
+    @Modified
+    protected void modified(ComponentContext ctx) {
+        Dictionary<?, ?> properties = ctx.getProperties();
+        if (properties != null) {
+            auditEnabled = Boolean.parseBoolean(get(properties, AUDIT_ENABLED));
+            auditFile = get(properties, AUDIT_LOGGER);
+            auditLog = LoggerFactory.getLogger(auditFile);
+            log.info("Reconfigured; auditEnabled={}; auditFile={}", auditEnabled, auditFile);
+        }
+    }
+
+    @Override
+    public boolean isAuditing() {
+        return auditEnabled;
+    }
+
+    @Override
+    public void logUserAction(String user, String action) {
+        if (auditEnabled) {
+            auditLog.info("user={}; action={}", user, action);
+        }
+    }
+
+}
diff --git a/core/net/src/main/java/org/onosproject/net/audit/impl/package-info.java b/core/net/src/main/java/org/onosproject/audit/impl/package-info.java
similarity index 94%
rename from core/net/src/main/java/org/onosproject/net/audit/impl/package-info.java
rename to core/net/src/main/java/org/onosproject/audit/impl/package-info.java
index 2845808..370c9de 100644
--- a/core/net/src/main/java/org/onosproject/net/audit/impl/package-info.java
+++ b/core/net/src/main/java/org/onosproject/audit/impl/package-info.java
@@ -17,4 +17,4 @@
 /**
  * Implementation of Audit Configuration.
  */
-package org.onosproject.net.audit.impl;
\ No newline at end of file
+package org.onosproject.audit.impl;
\ No newline at end of file
diff --git a/core/net/src/main/java/org/onosproject/net/OsgiPropertyConstants.java b/core/net/src/main/java/org/onosproject/net/OsgiPropertyConstants.java
index db5c5e3..3d6dec4 100644
--- a/core/net/src/main/java/org/onosproject/net/OsgiPropertyConstants.java
+++ b/core/net/src/main/java/org/onosproject/net/OsgiPropertyConstants.java
@@ -125,10 +125,10 @@
     public static final String DTP_MAX_BATCH_MS = "maxBatchMs";
     public static final int DTP_MAX_BATCH_MS_DEFAULT = 50;
 
-    public static final String AUDIT_STATUS_DESC = "auditEnabled";
-    public static final boolean AUDIT_STATUS_DEFAULT = false;
+    public static final String AUDIT_ENABLED = "auditEnabled";
+    public static final boolean AUDIT_ENABLED_DEFAULT = false;
 
-    public static final String AUDIT_FILE_TYPE_DESC = "auditFile";
-    public static final String AUDIT_FILE_TYPE_DEFAULT = "all";
+    public static final String AUDIT_LOGGER = "auditLogger";
+    public static final String AUDIT_LOGGER_DEFAULT = "securityAudit";
 
 }
diff --git a/core/net/src/main/java/org/onosproject/net/audit/impl/AuditManager.java b/core/net/src/main/java/org/onosproject/net/audit/impl/AuditManager.java
deleted file mode 100644
index 31b6a18..0000000
--- a/core/net/src/main/java/org/onosproject/net/audit/impl/AuditManager.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Foundation
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.onosproject.net.audit.impl;
-
-import org.onlab.rest.AuditFilter;
-
-import org.onosproject.cfg.ComponentConfigService;
-import org.onosproject.cli.AbstractShellCommand;
-import org.osgi.service.component.ComponentContext;
-import org.osgi.service.component.annotations.Component;
-import org.osgi.service.component.annotations.Modified;
-import org.osgi.service.component.annotations.Activate;
-import org.osgi.service.component.annotations.Reference;
-import org.osgi.service.component.annotations.ReferenceCardinality;
-
-import java.util.Dictionary;
-
-import static org.onlab.util.Tools.get;
-import static org.onosproject.net.OsgiPropertyConstants.AUDIT_FILE_TYPE_DESC;
-import static org.onosproject.net.OsgiPropertyConstants.AUDIT_FILE_TYPE_DEFAULT;
-import static org.onosproject.net.OsgiPropertyConstants.AUDIT_STATUS_DESC;
-import static org.onosproject.net.OsgiPropertyConstants.AUDIT_STATUS_DEFAULT;
-
-
-/**
- * Component to manage REST API Audit.
- */
-@Component(
-        immediate = true,
-        property = {
-                AUDIT_FILE_TYPE_DESC + "=" + AUDIT_FILE_TYPE_DEFAULT,
-                AUDIT_STATUS_DESC + ":Boolean=" + AUDIT_STATUS_DEFAULT
-        })
-public class AuditManager {
-
-    public String auditFile = AUDIT_FILE_TYPE_DEFAULT;
-    public boolean auditEnabled = AUDIT_STATUS_DEFAULT;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY)
-    protected ComponentConfigService cfgService;
-
-    @Activate
-    public void activate(ComponentContext context) {
-        cfgService.registerProperties(getClass());
-        setAuditStatus(auditFile, auditEnabled);
-    }
-
-    @Modified
-    protected void modifyFileType(ComponentContext context) {
-        Dictionary<?, ?> properties = context.getProperties();
-        if (properties == null) {
-            return;
-        }
-        auditFile = get(properties, AUDIT_FILE_TYPE_DESC);
-        String enableAuditStr = get(properties, AUDIT_STATUS_DESC);
-
-        auditEnabled = Boolean.parseBoolean(enableAuditStr);
-        setAuditStatus(auditFile, auditEnabled);
-    }
-
-    /**
-     * To enable Audit and set file type for REST API and  CLI as  per the changes in configuration properties.
-     *
-     * @param auditFile    file which audit logs are saved.
-     * @param auditEnabled status of REST API Audit and CLI Audit.
-     */
-    public void setAuditStatus(String auditFile, boolean auditEnabled) {
-        if (auditEnabled) {
-            AuditFilter.enableAudit();
-            AbstractShellCommand.enableAudit();
-        } else {
-            AuditFilter.disableAudit();
-            AbstractShellCommand.disableAudit();
-        }
-        AuditFilter.setAuditFile(auditFile);
-        AbstractShellCommand.setAuditFile(auditFile);
-    }
-}
diff --git a/utils/rest/src/main/java/org/onlab/rest/AbstractWebApplication.java b/utils/rest/src/main/java/org/onlab/rest/AbstractWebApplication.java
index f6c2739..41839c7 100644
--- a/utils/rest/src/main/java/org/onlab/rest/AbstractWebApplication.java
+++ b/utils/rest/src/main/java/org/onlab/rest/AbstractWebApplication.java
@@ -54,7 +54,6 @@
                     WebApplicationExceptionMapper.class,
                     IllegalArgumentExceptionMapper.class,
                     IllegalStateExceptionMapper.class,
-                    AuditFilter.class,
                     JsonBodyWriter.class);
         builder.add(classes);
         return builder.build();
diff --git a/utils/rest/src/main/java/org/onlab/rest/AuditFilter.java b/utils/rest/src/main/java/org/onlab/rest/AuditFilter.java
deleted file mode 100644
index 0f098ac..0000000
--- a/utils/rest/src/main/java/org/onlab/rest/AuditFilter.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Foundation
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.onlab.rest;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-import org.apache.commons.io.IOUtils;
-import org.slf4j.Logger;
-
-import javax.ws.rs.container.ContainerRequestContext;
-import javax.ws.rs.container.ContainerRequestFilter;
-import javax.ws.rs.container.ContainerResponseContext;
-import javax.ws.rs.container.ContainerResponseFilter;
-import java.io.IOException;
-
-import static org.onlab.util.Tools.readTreeFromStream;
-import static org.slf4j.LoggerFactory.getLogger;
-
-/**
- * FIlter for logging all REST Api http requests and details of request and response.
- */
-
-public class AuditFilter implements ContainerRequestFilter, ContainerResponseFilter {
-
-    private static Logger log = getLogger(AuditFilter.class);
-    private ObjectMapper mapper = new ObjectMapper();
-    private final String separator = "  |  ";
-
-    private static boolean disableForTests = false;
-    private static String auditFile = "all";
-    private static boolean auditEnabled = false;
-
-    @Override
-    public void filter(ContainerRequestContext requestContext) throws IOException {
-        if (disableForTests) {
-            return;
-        }
-        if (isEnabled()) {
-            String requestBody = (requestContext.hasEntity() ?
-                    (readTreeFromStream(mapper, requestContext.getEntityStream()).toString()) : "");
-            requestContext.setProperty("requestBody", requestBody);
-            requestContext.setProperty("auditLog", "Path: " + requestContext.getUriInfo().getPath() + separator
-                    + "Method: " + requestContext.getMethod() + separator
-                    + (requestContext.getMethod().equals("PUT") ?
-                    ("Path_Parameters: " + requestContext.getUriInfo().getPathParameters().toString() + separator
-                            + "Query_Parameters: " + requestContext.getUriInfo().getQueryParameters().toString()
-                            + separator + "Request_Body: " + requestBody) : ""));
-            requestContext.setEntityStream(IOUtils.toInputStream(requestBody));
-        }
-    }
-
-    @Override
-    public void filter(ContainerRequestContext containerRequestContext,
-                       ContainerResponseContext containerResponseContext) throws IOException {
-        if (disableForTests) {
-            return;
-        }
-        if (isEnabled()) {
-            containerRequestContext.setProperty("auditLog", containerRequestContext.getProperty("auditLog") + separator
-                    + "Status: " + containerResponseContext.getStatusInfo().toString());
-            saveAuditLog(containerRequestContext.getProperty("auditLog").toString());
-        }
-    }
-
-    /**
-     * To disable unit testing for this class.
-     */
-    public static void disableForTests() {
-        disableForTests = true;
-    }
-
-    /**
-     * To save audit logs into the log file.
-     *
-     * @param msg audit message.
-     */
-    private void saveAuditLog(String msg) {
-        if (isEnabled()) {
-            if (auditFile.equals("all")) {
-                log.info(msg);
-                log.info("AuditLog : " + msg);
-            } else if (auditFile.equals("karaf")) {
-                log.info(msg);
-            } else if (auditFile.equals("audit")) {
-                log.info("AuditLog : " + msg);
-            }
-        }
-    }
-
-    /**
-     * To check if REST API Audit is enabled.
-     *
-     * @return true if the REST API Audit is enabled.
-     */
-    private static boolean isEnabled() {
-        return auditEnabled;
-    }
-
-    /**
-     * To enable REST API Audit.
-     */
-    public static void enableAudit() {
-        auditEnabled = true;
-    }
-
-    /**
-     * To disable REST API Audit.
-     */
-    public static void disableAudit() {
-        auditEnabled = false;
-    }
-
-    /**
-     * To set audit file type which REST API Audit logs must be saved.
-     *
-     * @param auditFile file that REST API Audit logs are saved.
-     */
-    public static void setAuditFile(String auditFile) {
-        AuditFilter.auditFile = auditFile;
-    }
-
-
-}
diff --git a/web/api/src/main/java/org/onosproject/rest/resources/AuditFilter.java b/web/api/src/main/java/org/onosproject/rest/resources/AuditFilter.java
new file mode 100644
index 0000000..38471ab
--- /dev/null
+++ b/web/api/src/main/java/org/onosproject/rest/resources/AuditFilter.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.rest.resources;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.commons.io.IOUtils;
+import org.onlab.osgi.DefaultServiceDirectory;
+import org.onlab.osgi.ServiceDirectory;
+import org.onosproject.security.AuditService;
+
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.ContainerResponseContext;
+import javax.ws.rs.container.ContainerResponseFilter;
+import java.io.IOException;
+
+import static org.onlab.util.Tools.readTreeFromStream;
+
+/**
+ * HTTP Filter for auditing REST API requests.
+ */
+public class AuditFilter implements ContainerRequestFilter, ContainerResponseFilter {
+
+    private ObjectMapper mapper = new ObjectMapper();
+    private final String separator = "  |  ";
+
+    private static boolean disableForTests = false;
+    private static ServiceDirectory services = new DefaultServiceDirectory();
+
+    /**
+     * Disables functionality for unit tests.
+     */
+    public static void disableForTests() {
+        disableForTests = true;
+    }
+
+    @Override
+    public void filter(ContainerRequestContext requestContext) throws IOException {
+        if (auditService() != null) {
+            String requestBody = (requestContext.hasEntity() ?
+                    (readTreeFromStream(mapper, requestContext.getEntityStream()).toString()) : "");
+            requestContext.setProperty("requestBody", requestBody);
+            // FIXME: audit message should be better structured
+            requestContext.setProperty("auditMessage", "Path: " + requestContext.getUriInfo().getPath() + separator
+                    + "Method: " + requestContext.getMethod() + separator
+                    + (requestContext.getMethod().equals("PUT") ?
+                    // FIXME: is there really a need to differentiate based on method?
+                    ("Path_Parameters: " + requestContext.getUriInfo().getPathParameters().toString() + separator
+                            + "Query_Parameters: " + requestContext.getUriInfo().getQueryParameters().toString()
+                            + separator + "Request_Body: " + requestBody) : ""));
+            requestContext.setEntityStream(IOUtils.toInputStream(requestBody));
+        }
+    }
+
+    @Override
+    public void filter(ContainerRequestContext containerRequestContext,
+                       ContainerResponseContext containerResponseContext) throws IOException {
+        AuditService auditService = auditService();
+        if (auditService != null) {
+            containerRequestContext.setProperty("auditMessage", containerRequestContext.getProperty("auditMessage")
+                    + separator + "Status: " + containerResponseContext.getStatusInfo().toString());
+            // FIXME: Audit record should indicate who did it, not just what was done and when
+            String user = containerRequestContext.getSecurityContext().getUserPrincipal().getName();
+            String action = containerRequestContext.getProperty("auditMessage").toString();
+            auditService.logUserAction(user, action);
+        }
+    }
+
+    private AuditService auditService() {
+        AuditService auditService = disableForTests ? null : services.get(AuditService.class);
+        return auditService != null && auditService.isAuditing() ? auditService : null;
+    }
+}
diff --git a/web/api/src/main/java/org/onosproject/rest/resources/CoreWebApplication.java b/web/api/src/main/java/org/onosproject/rest/resources/CoreWebApplication.java
index 37242a0..a6227bc 100644
--- a/web/api/src/main/java/org/onosproject/rest/resources/CoreWebApplication.java
+++ b/web/api/src/main/java/org/onosproject/rest/resources/CoreWebApplication.java
@@ -53,7 +53,8 @@
                 DiagnosticsWebResource.class,
                 UiPreferencesWebResource.class,
                 SystemInfoWebResource.class,
-                PacketProcessorsWebResource.class
+                PacketProcessorsWebResource.class,
+                AuditFilter.class
         );
     }
 }
diff --git a/web/api/src/test/java/org/onosproject/rest/resources/ResourceTest.java b/web/api/src/test/java/org/onosproject/rest/resources/ResourceTest.java
index 9ccf53f..54203fd 100644
--- a/web/api/src/test/java/org/onosproject/rest/resources/ResourceTest.java
+++ b/web/api/src/test/java/org/onosproject/rest/resources/ResourceTest.java
@@ -24,7 +24,6 @@
 import org.onlab.junit.TestUtils;
 import org.onlab.osgi.ServiceDirectory;
 import org.onlab.rest.AuthorizationFilter;
-import org.onlab.rest.AuditFilter;
 import org.onlab.rest.BaseResource;
 
 /**