From 852f035c8ed72de44a8212c088806051aa36002f Mon Sep 17 00:00:00 2001
From: "Endi S. Dewata" <edewata@redhat.com>
Date: Thu, 10 Apr 2014 17:55:02 -0400
Subject: [PATCH] Added enable/disable functionality for TPS audit.

A new method has been added to TPS audit REST service to enable or
disable audit logging. The CLI and UI have been modified to provide
a way to acces this functionality. Also, new ACL entries have been
added for audit.

Ticket #955
---
 .../com/netscape/certsrv/logging/AuditClient.java  |  5 ++
 .../com/netscape/certsrv/logging/AuditConfig.java  | 22 +++---
 .../netscape/certsrv/logging/AuditResource.java    | 13 ++++
 .../com/netscape/cmstools/logging/AuditCLI.java    |  2 +-
 .../netscape/cmstools/logging/AuditModifyCLI.java  | 46 ++++++++-----
 .../org/dogtagpki/server/rest/AuditService.java    | 39 +++++++++--
 base/tps-tomcat/shared/conf/acl.ldif               |  1 +
 base/tps-tomcat/shared/conf/acl.properties         |  2 +
 base/tps-tomcat/shared/conf/auth-method.properties |  1 +
 base/tps-tomcat/shared/webapps/tps/WEB-INF/web.xml | 13 ++++
 base/tps-tomcat/shared/webapps/tps/js/audit.js     | 80 +++++++++++++++++++++-
 base/tps-tomcat/shared/webapps/tps/ui/audit.html   |  5 +-
 12 files changed, 189 insertions(+), 40 deletions(-)

diff --git a/base/common/src/com/netscape/certsrv/logging/AuditClient.java b/base/common/src/com/netscape/certsrv/logging/AuditClient.java
index 95d2c7fb47fddc8d91562091b868349c4134189c..018850c574c1646a014260375df0559757e85e27 100644
--- a/base/common/src/com/netscape/certsrv/logging/AuditClient.java
+++ b/base/common/src/com/netscape/certsrv/logging/AuditClient.java
@@ -49,4 +49,9 @@ public class AuditClient extends Client {
         Response response = resource.updateAuditConfig(auditConfig);
         return client.getEntity(response, AuditConfig.class);
     }
+
+    public AuditConfig changeAuditStatus(String action) {
+        Response response = resource.changeAuditStatus(action);
+        return client.getEntity(response, AuditConfig.class);
+    }
 }
diff --git a/base/common/src/com/netscape/certsrv/logging/AuditConfig.java b/base/common/src/com/netscape/certsrv/logging/AuditConfig.java
index d52dd97c63d11c00dc60bbe2d3fefdd0e1cb6f96..2d1e20eb9295f8438793826b2df67b437a3eaddd 100644
--- a/base/common/src/com/netscape/certsrv/logging/AuditConfig.java
+++ b/base/common/src/com/netscape/certsrv/logging/AuditConfig.java
@@ -59,7 +59,7 @@ public class AuditConfig {
         }
     }
 
-    Boolean enabled;
+    String status;
     Boolean signed;
     Integer interval;
     Integer bufferSize;
@@ -68,13 +68,13 @@ public class AuditConfig {
 
     Link link;
 
-    @XmlElement(name="Enabled")
-    public Boolean getEnabled() {
-        return enabled;
+    @XmlElement(name="Status")
+    public String getStatus() {
+        return status;
     }
 
-    public void setEnabled(Boolean enabled) {
-        this.enabled = enabled;
+    public void setStatus(String status) {
+        this.status = status;
     }
 
     @XmlElement(name="Signed")
@@ -164,7 +164,7 @@ public class AuditConfig {
         final int prime = 31;
         int result = 1;
         result = prime * result + ((bufferSize == null) ? 0 : bufferSize.hashCode());
-        result = prime * result + ((enabled == null) ? 0 : enabled.hashCode());
+        result = prime * result + ((status == null) ? 0 : status.hashCode());
         result = prime * result + ((eventConfigs == null) ? 0 : eventConfigs.hashCode());
         result = prime * result + ((interval == null) ? 0 : interval.hashCode());
         result = prime * result + ((link == null) ? 0 : link.hashCode());
@@ -186,10 +186,10 @@ public class AuditConfig {
                 return false;
         } else if (!bufferSize.equals(other.bufferSize))
             return false;
-        if (enabled == null) {
-            if (other.enabled != null)
+        if (status == null) {
+            if (other.status != null)
                 return false;
-        } else if (!enabled.equals(other.enabled))
+        } else if (!status.equals(other.status))
             return false;
         if (eventConfigs == null) {
             if (other.eventConfigs != null)
@@ -237,7 +237,7 @@ public class AuditConfig {
     public static void main(String args[]) throws Exception {
 
         AuditConfig before = new AuditConfig();
-        before.setEnabled(true);
+        before.setStatus("Enabled");
         before.setSigned(false);
         before.setInterval(10);
         before.setBufferSize(512);
diff --git a/base/common/src/com/netscape/certsrv/logging/AuditResource.java b/base/common/src/com/netscape/certsrv/logging/AuditResource.java
index 6053c6b8fc40132f3e6281e8eb143eced332a139..9b14986b116593a7d05a9767e7b1880add080315 100644
--- a/base/common/src/com/netscape/certsrv/logging/AuditResource.java
+++ b/base/common/src/com/netscape/certsrv/logging/AuditResource.java
@@ -18,11 +18,15 @@
 package com.netscape.certsrv.logging;
 
 import javax.ws.rs.GET;
+import javax.ws.rs.POST;
 import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Response;
 
 import org.jboss.resteasy.annotations.ClientResponseType;
 
+import com.netscape.certsrv.acls.ACLMapping;
+import com.netscape.certsrv.authentication.AuthMethodMapping;
 import com.netscape.certsrv.base.PATCH;
 
 
@@ -30,6 +34,8 @@ import com.netscape.certsrv.base.PATCH;
  * @author Endi S. Dewata
  */
 @Path("audit")
+@AuthMethodMapping("audit")
+@ACLMapping("audit.read")
 public interface AuditResource {
 
     @GET
@@ -38,5 +44,12 @@ public interface AuditResource {
 
     @PATCH
     @ClientResponseType(entityType=AuditConfig.class)
+    @ACLMapping("audit.modify")
     public Response updateAuditConfig(AuditConfig configData);
+
+    @POST
+    @ClientResponseType(entityType=AuditConfig.class)
+    @ACLMapping("audit.modify")
+    public Response changeAuditStatus(
+            @QueryParam("action") String action);
 }
diff --git a/base/java-tools/src/com/netscape/cmstools/logging/AuditCLI.java b/base/java-tools/src/com/netscape/cmstools/logging/AuditCLI.java
index 311a833279e2d3ef08a774a7fdfdaeff4f7b39da..11e530066026043992e07ceb0dbfad5cd452933e 100644
--- a/base/java-tools/src/com/netscape/cmstools/logging/AuditCLI.java
+++ b/base/java-tools/src/com/netscape/cmstools/logging/AuditCLI.java
@@ -51,7 +51,7 @@ public class AuditCLI extends CLI {
 
     public static void printAuditConfig(AuditConfig auditConfig) throws IOException {
 
-        if (auditConfig.getEnabled() != null) System.out.println("  Enabled: " + auditConfig.getEnabled());
+        if (auditConfig.getStatus() != null) System.out.println("  Status: " + auditConfig.getStatus());
         if (auditConfig.getSigned() != null) System.out.println("  Signed: " + auditConfig.getSigned());
         if (auditConfig.getInterval() != null) System.out.println("  Interval (seconds): " + auditConfig.getInterval());
         if (auditConfig.getBufferSize() != null) System.out.println("  Buffer size (bytes): " + auditConfig.getBufferSize());
diff --git a/base/java-tools/src/com/netscape/cmstools/logging/AuditModifyCLI.java b/base/java-tools/src/com/netscape/cmstools/logging/AuditModifyCLI.java
index 9906c20ee6738a775d4dd97c23e54e842c42e696..d37b9768dddb2be21825bbf417f4058b6ec32175 100644
--- a/base/java-tools/src/com/netscape/cmstools/logging/AuditModifyCLI.java
+++ b/base/java-tools/src/com/netscape/cmstools/logging/AuditModifyCLI.java
@@ -49,9 +49,12 @@ public class AuditModifyCLI extends CLI {
 
     public void execute(String[] args) throws Exception {
 
-        Option option = new Option(null, "input", true, "Input file containing audit configuration.");
+        Option option = new Option(null, "action", true, "Action: update (default), enable, disable.");
+        option.setArgName("action");
+        options.addOption(option);
+
+        option = new Option(null, "input", true, "Input file containing audit configuration.");
         option.setArgName("file");
-        option.setRequired(true);
         options.addOption(option);
 
         option = new Option(null, "output", true, "Output file to store audit configuration.");
@@ -76,32 +79,39 @@ public class AuditModifyCLI extends CLI {
             System.exit(1);
         }
 
+        String action = cmd.getOptionValue("action", "update");
         String input = cmd.getOptionValue("input");
         String output = cmd.getOptionValue("output");
 
-        if (input == null) {
-            System.err.println("Error: Input file is required.");
-            printHelp();
-            System.exit(1);
-        }
-
         AuditConfig auditConfig;
 
-        try (BufferedReader in = new BufferedReader(new FileReader(input));
-            StringWriter sw = new StringWriter();
-            PrintWriter out = new PrintWriter(sw, true)) {
+        if (action.equals("update")) {
 
-            String line;
-            while ((line = in.readLine()) != null) {
-                out.println(line);
+            if (input == null) {
+                System.err.println("Error: Input file is required.");
+                printHelp();
+                System.exit(1);
             }
 
-            auditConfig = AuditConfig.valueOf(sw.toString());
+            try (BufferedReader in = new BufferedReader(new FileReader(input));
+                StringWriter sw = new StringWriter();
+                PrintWriter out = new PrintWriter(sw, true)) {
+
+                String line;
+                while ((line = in.readLine()) != null) {
+                    out.println(line);
+                }
+
+                auditConfig = AuditConfig.valueOf(sw.toString());
+            }
+
+            auditConfig = auditCLI.auditClient.updateAuditConfig(auditConfig);
+
+        } else { // other actions
+            auditConfig = auditCLI.auditClient.changeAuditStatus(action);
         }
 
-        auditConfig = auditCLI.auditClient.updateAuditConfig(auditConfig);
-
-        MainCLI.printMessage("Updated audit configuration");
+        MainCLI.printMessage("Modified audit configuration");
 
         if (output == null) {
             AuditCLI.printAuditConfig(auditConfig);
diff --git a/base/server/cms/src/org/dogtagpki/server/rest/AuditService.java b/base/server/cms/src/org/dogtagpki/server/rest/AuditService.java
index bd5625844be8e80fd5d56631a39e863c7ff29edb..c9900717067329e95f8b3be46f42eb8e95600a79 100644
--- a/base/server/cms/src/org/dogtagpki/server/rest/AuditService.java
+++ b/base/server/cms/src/org/dogtagpki/server/rest/AuditService.java
@@ -72,7 +72,7 @@ public class AuditService extends PKIService implements AuditResource {
         IConfigStore cs = CMS.getConfigStore();
 
         AuditConfig auditConfig = new AuditConfig();
-        auditConfig.setEnabled(cs.getBoolean("log.instance.SignedAudit.enable", false));
+        auditConfig.setStatus(cs.getBoolean("log.instance.SignedAudit.enable", false) ? "Enabled" : "Disabled");
         auditConfig.setSigned(cs.getBoolean("log.instance.SignedAudit.logSigning", false));
         auditConfig.setInterval(cs.getInteger("log.instance.SignedAudit.flushInterval", 5));
         auditConfig.setBufferSize(cs.getInteger("log.instance.SignedAudit.bufferSize", 512));
@@ -132,10 +132,6 @@ public class AuditService extends PKIService implements AuditResource {
 
             IConfigStore cs = CMS.getConfigStore();
 
-            if (auditConfig.getEnabled() != null) {
-                cs.putBoolean("log.instance.SignedAudit.enable", auditConfig.getEnabled());
-            }
-
             if (auditConfig.getSigned() != null) {
                 cs.putBoolean("log.instance.SignedAudit.logSigning", auditConfig.getSigned());
             }
@@ -215,4 +211,37 @@ public class AuditService extends PKIService implements AuditResource {
             throw new PKIException(e.getMessage());
         }
     }
+
+    @Override
+    public Response changeAuditStatus(String action) {
+
+        CMS.debug("AuditService.changeAuditStatus()");
+
+        try {
+            IConfigStore cs = CMS.getConfigStore();
+
+            if ("enable".equals(action)) {
+                cs.putBoolean("log.instance.SignedAudit.enable", true);
+
+            } else if ("disable".equals(action)) {
+                cs.putBoolean("log.instance.SignedAudit.enable", false);
+
+            } else {
+                throw new BadRequestException("Invalid action " + action);
+            }
+
+            cs.commit(true);
+
+            AuditConfig auditConfig = createAuditConfig();
+
+            return createOKResponse(auditConfig);
+
+        } catch (PKIException e) {
+            throw e;
+
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new PKIException(e.getMessage());
+        }
+    }
 }
diff --git a/base/tps-tomcat/shared/conf/acl.ldif b/base/tps-tomcat/shared/conf/acl.ldif
index 63cd990b2b1776d9abe6ea31e89492e0600421ef..00992c2cc4e5d5ab21bc361670dc6ecdebf0b13d 100644
--- a/base/tps-tomcat/shared/conf/acl.ldif
+++ b/base/tps-tomcat/shared/conf/acl.ldif
@@ -22,6 +22,7 @@ resourceACLS: certServer.admin.request.enrollment:submit,read,execute:allow (sub
 resourceACLS: certServer.clone.configuration:read,modify:allow (modify,read) group="Enterprise CA Administrators" || group="Enterprise KRA Administrators" || group="Enterprise OCSP Administrators" || group="Enterprise TPS Administrators":Only Enterprise Administrators are allowed to clone the configuration.
 resourceACLS: certServer.tps.account:login,logout:allow (login,logout) user="anybody":Anybody can login and logout
 resourceACLS: certServer.tps.authenticators:read,add,modify,approve,remove:allow (read,add,modify,approve,remove) group="TUS Administrators":Only admins can access authenticators.
+resourceACLS: certServer.tps.audit:read,modify:allow (read,modify) group="TUS Administrators":Only admins can access configuration.
 resourceACLS: certServer.tps.config:read,modify:allow (read,modify) group="TUS Administrators":Only admins can access configuration.
 resourceACLS: certServer.tps.connections:read,add,modify,approve,remove:allow (read,add,modify,approve,remove) group="TUS Administrators":Only admins can access connections.
 resourceACLS: certServer.tps.groups:execute:allow (execute) group="TUS Administrators":Admins may execute group operations
diff --git a/base/tps-tomcat/shared/conf/acl.properties b/base/tps-tomcat/shared/conf/acl.properties
index aa49ba0b6ffa756ec2f9664b4b9765ebc604eb5a..ba955724bcec6889dadac4e9da652eef71725acd 100644
--- a/base/tps-tomcat/shared/conf/acl.properties
+++ b/base/tps-tomcat/shared/conf/acl.properties
@@ -8,6 +8,8 @@
 
 account.login = certServer.tps.account,login
 account.logout = certServer.tps.account,logout
+audit.read = certServer.tps.audit,read
+audit.modify = certServer.tps.audit,modify
 authenticators.read = certServer.tps.authenticators,read
 authenticators.add = certServer.tps.authenticators,add
 authenticators.modify = certServer.tps.authenticators,modify
diff --git a/base/tps-tomcat/shared/conf/auth-method.properties b/base/tps-tomcat/shared/conf/auth-method.properties
index af894ba052e5270c5417b181671555db4ea075ee..8b160e7ac477a1d245123ac26347d79e3c4d3a26 100644
--- a/base/tps-tomcat/shared/conf/auth-method.properties
+++ b/base/tps-tomcat/shared/conf/auth-method.properties
@@ -8,6 +8,7 @@
 
 default = *
 account = certUserDBAuthMgr,passwdUserDBAuthMgr
+audit = certUserDBAuthMgr
 authenticators = certUserDBAuthMgr
 certs = certUserDBAuthMgr
 certrequests = certUserDBAuthMgr
diff --git a/base/tps-tomcat/shared/webapps/tps/WEB-INF/web.xml b/base/tps-tomcat/shared/webapps/tps/WEB-INF/web.xml
index 17aaf511ae56b2935e0144fa374855872846343a..58861351467cd1b959cb6aa8a1b1c845ebb3d89b 100644
--- a/base/tps-tomcat/shared/webapps/tps/WEB-INF/web.xml
+++ b/base/tps-tomcat/shared/webapps/tps/WEB-INF/web.xml
@@ -197,6 +197,19 @@
 
     <security-constraint>
         <web-resource-collection>
+            <web-resource-name>Audit</web-resource-name>
+            <url-pattern>/rest/audit/*</url-pattern>
+        </web-resource-collection>
+        <auth-constraint>
+            <role-name>*</role-name>
+        </auth-constraint>
+        <user-data-constraint>
+            <transport-guarantee>CONFIDENTIAL</transport-guarantee>
+        </user-data-constraint>
+    </security-constraint>
+
+    <security-constraint>
+        <web-resource-collection>
             <web-resource-name>Authenticators</web-resource-name>
             <url-pattern>/rest/authenticators/*</url-pattern>
         </web-resource-collection>
diff --git a/base/tps-tomcat/shared/webapps/tps/js/audit.js b/base/tps-tomcat/shared/webapps/tps/js/audit.js
index b1ba21923982a46452cffdaf62ef3ef5fe1056c5..b5de110bac712390c77d04c88cfa74e11286c870 100644
--- a/base/tps-tomcat/shared/webapps/tps/js/audit.js
+++ b/base/tps-tomcat/shared/webapps/tps/js/audit.js
@@ -26,7 +26,7 @@ var AuditModel = Model.extend({
     parseResponse: function(response) {
         return {
             id: "audit",
-            enabled: response.Enabled,
+            status: response.Status,
             signed: response.Signed,
             interval: response.Interval,
             bufferSize: response.BufferSize,
@@ -35,7 +35,7 @@ var AuditModel = Model.extend({
     },
     createRequest: function(entry) {
         return {
-            Enabled: entry.enabled,
+            Status: entry.status,
             Signed: entry.signed,
             Interval: entry.interval,
             BufferSize: entry.bufferSize,
@@ -43,6 +43,32 @@ var AuditModel = Model.extend({
                 Event: entry.events
             }
         };
+    },
+    enable: function(options) {
+        var self = this;
+        $.ajax({
+            type: "POST",
+            url: self.url() + "?action=enable",
+            dataType: "json"
+        }).done(function(data, textStatus, jqXHR) {
+            self.set(self.parseResponse(data));
+            if (options.success) options.success.call(self, data, textStatus, jqXHR);
+        }).fail(function(jqXHR, textStatus, errorThrown) {
+            if (options.error) options.error.call(self, jqXHR, textStatus, errorThrown);
+        });
+    },
+    disable: function(options) {
+        var self = this;
+        $.ajax({
+            type: "POST",
+            url: self.url() + "?action=disable",
+            dataType: "json"
+        }).done(function(data, textStatus, jqXHR) {
+            self.set(self.parseResponse(data));
+            if (options.success) options.success.call(self, data, textStatus, jqXHR);
+        }).fail(function(jqXHR, textStatus, errorThrown) {
+            if (options.error) options.error.call(self, jqXHR, textStatus, errorThrown);
+        });
     }
 });
 
@@ -74,7 +100,7 @@ var AuditPage = EntryPage.extend({
     initialize: function(options) {
         var self = this;
         options.model = new AuditModel();
-        options.editable = ["enabled", "signed", "interval", "bufferSize"];
+        options.editable = ["signed", "interval", "bufferSize"];
         AuditPage.__super__.initialize.call(self, options);
     },
     setup: function() {
@@ -82,6 +108,45 @@ var AuditPage = EntryPage.extend({
 
         AuditPage.__super__.setup.call(self);
 
+        self.enableLink = $("a[name='enable']", self.menu);
+        self.disableLink = $("a[name='disable']", self.menu);
+
+        self.enableLink.click(function(e) {
+            var message = "Are you sure you want to enable this entry?";
+            if (!confirm(message)) return;
+            self.model.enable({
+                success: function(data, textStatus, jqXHR) {
+                    self.entry = _.clone(self.model.attributes);
+                    self.render();
+                },
+                error: function(jqXHR, textStatus, errorThrown) {
+                    new ErrorDialog({
+                        el: $("#error-dialog"),
+                        title: "HTTP Error " + jqXHR.responseJSON.Code,
+                        content: jqXHR.responseJSON.Message
+                    }).open();
+                }
+            });
+        });
+
+        self.disableLink.click(function(e) {
+            var message = "Are you sure you want to disable this entry?";
+            if (!confirm(message)) return;
+            self.model.disable({
+                success: function(data, textStatus, jqXHR) {
+                    self.entry = _.clone(self.model.attributes);
+                    self.render();
+                },
+                error: function(jqXHR, textStatus, errorThrown) {
+                    new ErrorDialog({
+                        el: $("#error-dialog"),
+                        title: "HTTP Error " + jqXHR.responseJSON.Code,
+                        content: jqXHR.responseJSON.Message
+                    }).open();
+                }
+            });
+        });
+
         var eventDialog = self.$("#event-dialog");
 
         var eventEditDialog = new Dialog({
@@ -114,6 +179,15 @@ var AuditPage = EntryPage.extend({
 
         AuditPage.__super__.renderContent.call(self);
 
+        var status = self.entry.status;
+        if (status == "Disabled") {
+            self.enableLink.show();
+            self.disableLink.hide();
+        } else if (status == "Enabled") {
+            self.enableLink.hide();
+            self.disableLink.show();
+        }
+
         if (self.mode == "edit") {
             self.eventsTable.mode = "edit";
 
diff --git a/base/tps-tomcat/shared/webapps/tps/ui/audit.html b/base/tps-tomcat/shared/webapps/tps/ui/audit.html
index a229c711b0100c8d54fe77b18e5fa875948992ea..55b4c5c9b3c82745fbbed13fcc5cacb4deeaf82b 100644
--- a/base/tps-tomcat/shared/webapps/tps/ui/audit.html
+++ b/base/tps-tomcat/shared/webapps/tps/ui/audit.html
@@ -23,6 +23,7 @@
 
 <span class="pki-menu" style="display: none;">
 <a name="edit" href="#">Edit</a><br>
+<a name="enable" href="#">Enable</a><a name="disable" href="#" style="display: none;">Disable</a><br>
 </span>
 
 <span class="pki-buttons" style="display: none;">
@@ -36,8 +37,8 @@
 
 <div name="audit" class="pki-fields">
 <fieldset>
-    <label>Enabled</label>
-    <input name="enabled" readonly="readonly">
+    <label>Status</label>
+    <input name="status" readonly="readonly">
     <br>
     <label>Signed</label>
     <input name="signed" readonly="readonly">
-- 
1.8.4.2

