restapi-cli-audit-onos-1.14-manual-cherry-pick

Change-Id: I9ef42dc94fda76334133c556548cb63f0bea7f50
diff --git a/cli/src/main/java/org/onosproject/cli/AbstractShellCommand.java b/cli/src/main/java/org/onosproject/cli/AbstractShellCommand.java
index 09e08fa..9b3c024 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 java.util.Set;
 import java.util.TreeSet;
@@ -38,6 +39,8 @@
  */
 public abstract class AbstractShellCommand extends AbstractAction implements CodecContext {
 
+    private final ObjectMapper mapper = new ObjectMapper();
+
     @Option(name = "-j", aliases = "--json", description = "Output JSON",
             required = false, multiValued = false)
     private boolean json = false;
@@ -152,6 +155,7 @@
     @Override
     protected Object doExecute() throws Exception {
         try {
+            auditCommand();
             execute();
         } catch (ServiceNotFoundException e) {
             error(e.getMessage());
@@ -160,8 +164,16 @@
     }
 
 
-
-    private final ObjectMapper mapper = new ObjectMapper();
+    // 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 = "{\"command\" : \"" + Thread.currentThread().getName().substring(5) + "\"}";
+            auditService.logUserAction(user, action);
+        }
+    }
 
     @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/BUCK b/core/net/BUCK
index 8a6bad8..5146650 100644
--- a/core/net/BUCK
+++ b/core/net/BUCK
@@ -5,7 +5,6 @@
     '//lib:KRYO',
     '//core/common:onos-core-common',
     '//incubator/api:onos-incubator-api',
-    '//utils/rest:onlab-rest',
     '//incubator/net:onos-incubator-net',
     '//incubator/store:onos-incubator-store',
     '//core/store/serializers:onos-core-serializers',
diff --git a/core/net/BUILD b/core/net/BUILD
index 3c17dd0..111170d 100644
--- a/core/net/BUILD
+++ b/core/net/BUILD
@@ -1,7 +1,6 @@
 COMPILE_DEPS = CORE_DEPS + JACKSON + METRICS + KRYO + [
     "//core/common:onos-core-common",
     "//incubator/api:onos-incubator-api",
-    "//utils/rest:onlab-rest",
     "//incubator/net:onos-incubator-net",
     "//incubator/store:onos-incubator-store",
     "//core/store/serializers:onos-core-serializers",
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..2a8e483
--- /dev/null
+++ b/core/net/src/main/java/org/onosproject/audit/impl/AuditManager.java
@@ -0,0 +1,102 @@
+/*
+ * 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.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Modified;
+import org.onosproject.cfg.ComponentConfigService;
+import org.onosproject.security.AuditService;
+import org.osgi.service.component.ComponentContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Dictionary;
+
+import static org.onlab.util.Tools.get;
+
+/**
+ * Component to manage audit logging.
+ */
+@Component(immediate = true)
+@Service
+public class AuditManager implements AuditService {
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    private Logger auditLog = log;
+
+    private static final String AUDIT_ENABLED = "auditEnabled";
+    private static final boolean AUDIT_ENABLED_DEFAULT = false;
+
+    private static final String AUDIT_LOGGER = "auditLogger";
+    private static final String AUDIT_LOGGER_DEFAULT = "securityAudit";
+
+
+    @Property(name = AUDIT_ENABLED, boolValue = AUDIT_ENABLED_DEFAULT,
+            label = "Specifies whether or not audit logging is enabled.")
+    private boolean auditEnabled = AUDIT_ENABLED_DEFAULT;
+
+    @Property(name = AUDIT_LOGGER, value = AUDIT_LOGGER_DEFAULT,
+            label = "Name of the audit logger.")
+    private String auditLogger = AUDIT_LOGGER_DEFAULT;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    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));
+            auditLogger = get(properties, AUDIT_LOGGER);
+            auditLog = LoggerFactory.getLogger(auditLogger);
+            log.info("Reconfigured; auditEnabled={}; auditLogger={}", auditEnabled, auditLogger);
+        }
+    }
+
+    @Override
+    public boolean isAuditing() {
+        return auditEnabled;
+    }
+
+    @Override
+    public void logUserAction(String user, String action) {
+        if (auditEnabled) {
+            action = action.concat(" | " + auditLogger);
+            auditLog.info("user={}; action={}", user, action);
+        }
+    }
+
+}
diff --git a/core/net/src/main/java/org/onosproject/audit/impl/package-info.java b/core/net/src/main/java/org/onosproject/audit/impl/package-info.java
new file mode 100644
index 0000000..370c9de
--- /dev/null
+++ b/core/net/src/main/java/org/onosproject/audit/impl/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015-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.
+ */
+
+/**
+ * Implementation of Audit Configuration.
+ */
+package org.onosproject.audit.impl;
\ No newline at end of file
diff --git a/tools/package/etc/org.ops4j.pax.logging.cfg b/tools/package/etc/org.ops4j.pax.logging.cfg
new file mode 100644
index 0000000..732c9d6
--- /dev/null
+++ b/tools/package/etc/org.ops4j.pax.logging.cfg
@@ -0,0 +1,66 @@
+#################################################################################
+#
+#    Licensed to the Apache Software Foundation (ASF) under one or more
+#    contributor license agreements.  See the NOTICE file distributed with
+#    this work for additional information regarding copyright ownership.
+#    The ASF licenses this file to You 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.
+#
+################################################################################
+
+# Root logger
+log4j.rootLogger=INFO, out, osgi:*
+log4j.logger.securityAudit=INFO, Audit
+log4j.throwableRenderer=org.apache.log4j.OsgiThrowableRenderer
+
+# CONSOLE appender not used by default
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ISO8601} | %-5.5p | %-16.16t | %-32.32c{1} | %X{bundle.id} - %X{bundle.name} - %X{bundle.version} | %m%n
+
+# File appender
+log4j.appender.out=org.apache.log4j.RollingFileAppender
+log4j.appender.out.layout=org.apache.log4j.PatternLayout
+log4j.appender.out.layout.ConversionPattern=%d{ISO8601} | %-5.5p | %-16.16t | %-32.32c{1} | %X{bundle.id} | %m%n
+log4j.appender.out.file=${karaf.data}/log/karaf.log
+log4j.appender.out.append=true
+log4j.appender.out.filter.a=org.apache.log4j.varia.StringMatchFilter
+log4j.appender.out.filter.a.StringToMatch=securityAudit
+log4j.appender.out.filter.a.AcceptOnMatch=false
+log4j.appender.out.maxFileSize=100MB
+log4j.appender.out.maxBackupIndex=10
+
+# audit log File appender
+log4j.appender.Audit=org.apache.log4j.RollingFileAppender
+log4j.appender.Audit.layout=org.apache.log4j.PatternLayout
+log4j.appender.Audit.layout.ConversionPattern=%d{ISO8601} | %-5.5p | %-16.16t | %-32.32c{1} | %X{bundle.id} | %m%n
+log4j.appender.Audit.file=${karaf.data}/log/audit.log
+log4j.appender.Audit.append=true
+log4j.appender.Audit.maxFileSize=100MB
+log4j.appender.Audit.maxBackupIndex=10
+
+# Sift appender
+log4j.appender.sift=org.apache.log4j.sift.MDCSiftingAppender
+log4j.appender.sift.key=bundle.name
+log4j.appender.sift.default=karaf
+log4j.appender.sift.appender=org.apache.log4j.FileAppender
+log4j.appender.sift.appender.layout=org.apache.log4j.PatternLayout
+log4j.appender.sift.appender.layout.ConversionPattern=%d{ISO8601} | %-5.5p | %-16.16t | %-32.32c{1} | %m%n
+log4j.appender.sift.appender.file=${karaf.data}/log/$\\{bundle.name\\}.log
+log4j.appender.sift.appender.append=true
+
+log4j.logger.org.onosproject.segmentrouting = DEBUG
+log4j.logger.org.onosproject.flowdump = INFO
+log4j.logger.org.onosproject.routing.fpm = DEBUG
+log4j.logger.org.onosproject.routeservice.impl = DEBUG
+log4j.logger.org.onosproject.routeservice.store = DEBUG
+log4j.logger.org.onosproject.net.flowobjective.impl = DEBUG
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..81c4f42
--- /dev/null
+++ b/web/api/src/main/java/org/onosproject/rest/resources/AuditFilter.java
@@ -0,0 +1,91 @@
+/*
+ * 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 final String logCompSeperator = "\" : \"";
+
+    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" + logCompSeperator
+                    + requestContext.getUriInfo().getPath() + separator + "Method"
+                    + logCompSeperator + requestContext.getMethod() + separator
+                    + (requestContext.getMethod().equals("PUT") ?
+                    // FIXME: is there really a need to differentiate based on method?
+                    ("Path_Parameters" + logCompSeperator + requestContext.getUriInfo().getPathParameters().toString()
+                            + separator + "Query_Parameters" + logCompSeperator
+                            + requestContext.getUriInfo().getQueryParameters().toString()
+                            + separator + "Request_Body" + logCompSeperator + 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" + logCompSeperator + 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 67b6c96..36801c5 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
@@ -56,7 +56,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 9f809f3..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
@@ -51,6 +51,7 @@
     private void configureProperties() {
         set(TestProperties.CONTAINER_PORT, 0);
         AuthorizationFilter.disableForTests();
+        AuditFilter.disableForTests();
     }
 
     /**