From 57e57197c9f86515d2d6b69ee86e450bd357ecba Mon Sep 17 00:00:00 2001
From: "Endi S. Dewata" <edewata@redhat.com>
Date: Wed, 2 Oct 2013 12:39:13 -0400
Subject: [PATCH] Added audit resource.

A new REST service and clients have been added to manage the audit
configuration in all subsystems.

Ticket #652
---
 .../ca/CertificateAuthorityApplication.java        |   4 +
 .../src/com/netscape/certsrv/ca/CAClient.java      |   2 +
 .../src/com/netscape/certsrv/kra/KRAClient.java    |   2 +
 .../TKSClient.java => logging/AuditClient.java}    |  36 ++-
 .../com/netscape/certsrv/logging/AuditConfig.java  | 318 +++++++++++++++++++++
 .../netscape/certsrv/logging/AuditResource.java    |  46 +++
 .../src/com/netscape/certsrv/ocsp/OCSPClient.java  |   2 +
 .../src/com/netscape/certsrv/tks/TKSClient.java    |   2 +
 .../src/com/netscape/certsrv/tps/TPSClient.java    |   2 +
 .../certsrv/tps/config/ConfigCollection.java       |  65 -----
 .../src/com/netscape/cms/logging/LogFile.java      | 133 +++++----
 .../com/netscape/cmscore/logging/AuditService.java | 177 ++++++++++++
 .../src/com/netscape/cmstools/cli/CACLI.java       |   2 +
 .../src/com/netscape/cmstools/cli/KRACLI.java      |   2 +
 .../src/com/netscape/cmstools/cli/OCSPCLI.java     |   2 +
 .../src/com/netscape/cmstools/cli/TKSCLI.java      |   2 +
 .../src/com/netscape/cmstools/cli/TPSCLI.java      |   2 +
 .../com/netscape/cmstools/logging/AuditCLI.java    |  71 +++++
 .../netscape/cmstools/logging/AuditModifyCLI.java  | 115 ++++++++
 .../netscape/cmstools/logging/AuditShowCLI.java    |  86 ++++++
 .../kra/KeyRecoveryAuthorityApplication.java       |   4 +
 .../src/com/netscape/ocsp/OCSPApplication.java     |   4 +
 base/tks/src/com/netscape/tks/TKSApplication.java  |   4 +
 base/tps-tomcat/shared/conf/CS.cfg.in              |   4 +-
 .../org/dogtagpki/server/tps/TPSApplication.java   |   4 +
 25 files changed, 954 insertions(+), 137 deletions(-)
 copy base/common/src/com/netscape/certsrv/{tks/TKSClient.java => logging/AuditClient.java} (54%)
 create mode 100644 base/common/src/com/netscape/certsrv/logging/AuditConfig.java
 create mode 100644 base/common/src/com/netscape/certsrv/logging/AuditResource.java
 delete mode 100644 base/common/src/com/netscape/certsrv/tps/config/ConfigCollection.java
 create mode 100644 base/common/src/com/netscape/cmscore/logging/AuditService.java
 create mode 100644 base/java-tools/src/com/netscape/cmstools/logging/AuditCLI.java
 create mode 100644 base/java-tools/src/com/netscape/cmstools/logging/AuditModifyCLI.java
 create mode 100644 base/java-tools/src/com/netscape/cmstools/logging/AuditShowCLI.java

diff --git a/base/ca/src/com/netscape/ca/CertificateAuthorityApplication.java b/base/ca/src/com/netscape/ca/CertificateAuthorityApplication.java
index e12da7fb796196d8d410e8615b91a1b1ff8d7c3d..478376c65ed7a9d1295867c94958f32e5317af7a 100644
--- a/base/ca/src/com/netscape/ca/CertificateAuthorityApplication.java
+++ b/base/ca/src/com/netscape/ca/CertificateAuthorityApplication.java
@@ -24,6 +24,7 @@ import com.netscape.cms.servlet.csadmin.SecurityDomainService;
 import com.netscape.cms.servlet.csadmin.SystemConfigService;
 import com.netscape.cms.servlet.profile.ProfileService;
 import com.netscape.cms.servlet.request.CertRequestService;
+import com.netscape.cmscore.logging.AuditService;
 import com.netscape.cmscore.selftests.SelfTestService;
 
 public class CertificateAuthorityApplication extends Application {
@@ -35,6 +36,9 @@ public class CertificateAuthorityApplication extends Application {
         // account
         classes.add(AccountService.class);
 
+        // audit
+        classes.add(AuditService.class);
+
         // installer
         classes.add(SystemConfigService.class);
 
diff --git a/base/common/src/com/netscape/certsrv/ca/CAClient.java b/base/common/src/com/netscape/certsrv/ca/CAClient.java
index e0bc6c9b3159f030d169933cf5bf5e6ecd2a4142..3790c89de9e7ffdba783491378362d88a3a95f4b 100644
--- a/base/common/src/com/netscape/certsrv/ca/CAClient.java
+++ b/base/common/src/com/netscape/certsrv/ca/CAClient.java
@@ -33,6 +33,7 @@ import com.netscape.certsrv.client.PKIClient;
 import com.netscape.certsrv.client.SubsystemClient;
 import com.netscape.certsrv.dbs.certdb.CertId;
 import com.netscape.certsrv.group.GroupClient;
+import com.netscape.certsrv.logging.AuditClient;
 import com.netscape.certsrv.profile.ProfileData;
 import com.netscape.certsrv.profile.ProfileDataInfos;
 import com.netscape.certsrv.profile.ProfileResource;
@@ -53,6 +54,7 @@ public class CAClient extends SubsystemClient {
 
     public void init() throws URISyntaxException {
 
+        addClient(new AuditClient(client, name));
         addClient(new GroupClient(client, name));
         addClient(new SelfTestClient(client, name));
         addClient(new UserClient(client, name));
diff --git a/base/common/src/com/netscape/certsrv/kra/KRAClient.java b/base/common/src/com/netscape/certsrv/kra/KRAClient.java
index 28991caf6249f445151caa7b90bfc2684a9d84f5..658aba50bf03d93aab2c6bbe7783a4d04b79350e 100644
--- a/base/common/src/com/netscape/certsrv/kra/KRAClient.java
+++ b/base/common/src/com/netscape/certsrv/kra/KRAClient.java
@@ -20,6 +20,7 @@ import com.netscape.certsrv.key.KeyRequestInfo;
 import com.netscape.certsrv.key.KeyRequestInfos;
 import com.netscape.certsrv.key.KeyRequestResource;
 import com.netscape.certsrv.key.KeyResource;
+import com.netscape.certsrv.logging.AuditClient;
 import com.netscape.certsrv.request.RequestId;
 import com.netscape.certsrv.selftests.SelfTestClient;
 import com.netscape.certsrv.system.SystemCertResource;
@@ -39,6 +40,7 @@ public class KRAClient extends SubsystemClient {
 
     public void init() throws URISyntaxException {
 
+        addClient(new AuditClient(client, name));
         addClient(new GroupClient(client, name));
         addClient(new SelfTestClient(client, name));
         addClient(new UserClient(client, name));
diff --git a/base/common/src/com/netscape/certsrv/tks/TKSClient.java b/base/common/src/com/netscape/certsrv/logging/AuditClient.java
similarity index 54%
copy from base/common/src/com/netscape/certsrv/tks/TKSClient.java
copy to base/common/src/com/netscape/certsrv/logging/AuditClient.java
index a972241ce6521e9cd92138f2f99088704d7f3d0a..5892a20cb860ddc0d03f001d1ffc1e6c03c9324f 100644
--- a/base/common/src/com/netscape/certsrv/tks/TKSClient.java
+++ b/base/common/src/com/netscape/certsrv/logging/AuditClient.java
@@ -15,28 +15,38 @@
 //(C) 2013 Red Hat, Inc.
 //All rights reserved.
 //--- END COPYRIGHT BLOCK ---
-package com.netscape.certsrv.tks;
+package com.netscape.certsrv.logging;
 
 import java.net.URISyntaxException;
 
+import org.jboss.resteasy.client.ClientResponse;
+
+import com.netscape.certsrv.client.Client;
 import com.netscape.certsrv.client.PKIClient;
-import com.netscape.certsrv.client.SubsystemClient;
-import com.netscape.certsrv.group.GroupClient;
-import com.netscape.certsrv.selftests.SelfTestClient;
-import com.netscape.certsrv.system.TPSConnectorClient;
-import com.netscape.certsrv.user.UserClient;
 
-public class TKSClient extends SubsystemClient {
+/**
+ * @author Endi S. Dewata
+ */
+public class AuditClient extends Client {
 
-    public TKSClient(PKIClient client) throws URISyntaxException {
-        super(client, "tks");
+    public AuditResource resource;
+
+    public AuditClient(PKIClient client, String subsystem) throws URISyntaxException {
+        super(client, subsystem, "audit");
         init();
     }
 
     public void init() throws URISyntaxException {
-        addClient(new GroupClient(client, name));
-        addClient(new SelfTestClient(client, name));
-        addClient(new TPSConnectorClient(client, name));
-        addClient(new UserClient(client, name));
+        resource = createProxy(AuditResource.class);
+    }
+
+    public AuditConfig getAuditConfig() {
+        return resource.getAuditConfig();
+    }
+
+    public AuditConfig updateAuditConfig(AuditConfig auditConfig) {
+        @SuppressWarnings("unchecked")
+        ClientResponse<AuditConfig> response = (ClientResponse<AuditConfig>)resource.updateAuditConfig(auditConfig);
+        return client.getEntity(response);
     }
 }
diff --git a/base/common/src/com/netscape/certsrv/logging/AuditConfig.java b/base/common/src/com/netscape/certsrv/logging/AuditConfig.java
new file mode 100644
index 0000000000000000000000000000000000000000..6abe7af2a8ef3053996af9b3086c8034b43ecdb7
--- /dev/null
+++ b/base/common/src/com/netscape/certsrv/logging/AuditConfig.java
@@ -0,0 +1,318 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2013 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+
+package com.netscape.certsrv.logging;
+
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.Marshaller;
+import javax.xml.bind.Unmarshaller;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlValue;
+import javax.xml.bind.annotation.adapters.XmlAdapter;
+import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
+
+import org.jboss.resteasy.plugins.providers.atom.Link;
+
+/**
+ * @author Endi S. Dewata
+ */
+@XmlRootElement(name="Audit")
+public class AuditConfig {
+
+    public static Marshaller marshaller;
+    public static Unmarshaller unmarshaller;
+
+    static {
+        try {
+            marshaller = JAXBContext.newInstance(AuditConfig.class).createMarshaller();
+            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
+            unmarshaller = JAXBContext.newInstance(AuditConfig.class).createUnmarshaller();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    Boolean enabled;
+    Boolean signed;
+    Integer interval;
+    Integer bufferSize;
+    Collection<String> mandatoryEvents = new TreeSet<String>();
+    Map<String, Boolean> optionalEvents = new TreeMap<String, Boolean>();
+
+    Link link;
+
+    @XmlElement(name="Enabled")
+    public Boolean getEnabled() {
+        return enabled;
+    }
+
+    public void setEnabled(Boolean enabled) {
+        this.enabled = enabled;
+    }
+
+    @XmlElement(name="Signed")
+    public Boolean getSigned() {
+        return signed;
+    }
+
+    public void setSigned(Boolean signed) {
+        this.signed = signed;
+    }
+
+    @XmlElement(name="Interval")
+    public Integer getInterval() {
+        return interval;
+    }
+
+    public void setInterval(Integer interval) {
+        this.interval = interval;
+    }
+
+    @XmlElement(name="BufferSize")
+    public Integer getBufferSize() {
+        return bufferSize;
+    }
+
+    public void setBufferSize(Integer bufferSize) {
+        this.bufferSize = bufferSize;
+    }
+
+    @XmlElement(name="MandatoryEvents")
+    @XmlJavaTypeAdapter(MandatoryEventsAdapter.class)
+    public Collection<String> getMandatoryEvents() {
+        return mandatoryEvents;
+    }
+
+    public void setMandatoryEvents(Collection<String> mandatoryEvents) {
+        this.mandatoryEvents.clear();
+        this.mandatoryEvents.addAll(mandatoryEvents);
+    }
+
+    public void addMandatoryEvent(String event) {
+        mandatoryEvents.add(event);
+    }
+
+    public void removeMandatoryEvent(String event) {
+        mandatoryEvents.remove(event);
+    }
+
+    @XmlElement(name="OptionalEvents")
+    @XmlJavaTypeAdapter(OptionalEventsAdapter.class)
+    public Map<String, Boolean> getOptionalEvents() {
+        return optionalEvents;
+    }
+
+    public void setOptionalEvents(Map<String, Boolean> optionalEvents) {
+        this.optionalEvents.clear();
+        this.optionalEvents.putAll(optionalEvents);
+    }
+
+    public Collection<String> getOptionalEventNames() {
+        return optionalEvents.keySet();
+    }
+
+    public Boolean getOptionalEvent(String name) {
+        return optionalEvents.get(name);
+    }
+
+    public void setOptionalEvent(String name, Boolean value) {
+        optionalEvents.put(name, value);
+    }
+
+    public Boolean removeOptionalEvent(String name) {
+        return optionalEvents.remove(name);
+    }
+
+    public static class MandatoryEventsAdapter extends XmlAdapter<EventList, Collection<String>> {
+
+        public EventList marshal(Collection<String> input) {
+            EventList output = new EventList();
+            for (String name : input) {
+                Event event = new Event();
+                event.name = name;
+                output.entries.add(event);
+            }
+            return output;
+        }
+
+        public Collection<String> unmarshal(EventList input) {
+            Collection<String> output = new TreeSet<String>();
+            for (Event event : input.entries) {
+                output.add(event.name);
+            }
+            return output;
+        }
+    }
+
+    public static class OptionalEventsAdapter extends XmlAdapter<EventList, Map<String, Boolean>> {
+
+        public EventList marshal(Map<String, Boolean> map) {
+            EventList list = new EventList();
+            for (Map.Entry<String, Boolean> entry : map.entrySet()) {
+                Event event = new Event();
+                event.name = entry.getKey();
+                event.value = entry.getValue();
+                list.entries.add(event);
+            }
+            return list;
+        }
+
+        public Map<String, Boolean> unmarshal(EventList list) {
+            Map<String, Boolean> map = new LinkedHashMap<String, Boolean>();
+            for (Event event : list.entries) {
+                map.put(event.name, event.value);
+            }
+            return map;
+        }
+    }
+
+    public static class EventList {
+        @XmlElement(name="Event")
+        public List<Event> entries = new ArrayList<Event>();
+    }
+
+    public static class Event {
+
+        @XmlAttribute
+        public String name;
+
+        @XmlValue
+        public Boolean value;
+    }
+
+    @XmlElement(name="Link")
+    public Link getLink() {
+        return link;
+    }
+
+    public void setLink(Link link) {
+        this.link = link;
+    }
+
+    @Override
+    public int hashCode() {
+        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 + ((optionalEvents == null) ? 0 : optionalEvents.hashCode());
+        result = prime * result + ((interval == null) ? 0 : interval.hashCode());
+        result = prime * result + ((link == null) ? 0 : link.hashCode());
+        result = prime * result + ((mandatoryEvents == null) ? 0 : mandatoryEvents.hashCode());
+        result = prime * result + ((signed == null) ? 0 : signed.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        AuditConfig other = (AuditConfig) obj;
+        if (bufferSize == null) {
+            if (other.bufferSize != null)
+                return false;
+        } else if (!bufferSize.equals(other.bufferSize))
+            return false;
+        if (enabled == null) {
+            if (other.enabled != null)
+                return false;
+        } else if (!enabled.equals(other.enabled))
+            return false;
+        if (optionalEvents == null) {
+            if (other.optionalEvents != null)
+                return false;
+        } else if (!optionalEvents.equals(other.optionalEvents))
+            return false;
+        if (interval == null) {
+            if (other.interval != null)
+                return false;
+        } else if (!interval.equals(other.interval))
+            return false;
+        if (link == null) {
+            if (other.link != null)
+                return false;
+        } else if (!link.equals(other.link))
+            return false;
+        if (mandatoryEvents == null) {
+            if (other.mandatoryEvents != null)
+                return false;
+        } else if (!mandatoryEvents.equals(other.mandatoryEvents))
+            return false;
+        if (signed == null) {
+            if (other.signed != null)
+                return false;
+        } else if (!signed.equals(other.signed))
+            return false;
+        return true;
+    }
+
+    public String toString() {
+        try {
+            StringWriter sw = new StringWriter();
+            marshaller.marshal(this, sw);
+            return sw.toString();
+
+        } catch (Exception e) {
+            e.printStackTrace();
+            return super.toString();
+        }
+    }
+
+    public static AuditConfig valueOf(String string) throws Exception {
+        try {
+            return (AuditConfig)unmarshaller.unmarshal(new StringReader(string));
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    public static void main(String args[]) throws Exception {
+
+        AuditConfig before = new AuditConfig();
+        before.setEnabled(true);
+        before.setSigned(false);
+        before.setInterval(10);
+        before.setBufferSize(512);
+        before.addMandatoryEvent("event1");
+        before.addMandatoryEvent("event2");
+        before.setOptionalEvent("event3", true);
+        before.setOptionalEvent("event4", false);
+
+        String string = before.toString();
+        System.out.println(string);
+
+        AuditConfig after = AuditConfig.valueOf(string);
+        System.out.println(before.equals(after));
+    }
+}
diff --git a/base/common/src/com/netscape/certsrv/logging/AuditResource.java b/base/common/src/com/netscape/certsrv/logging/AuditResource.java
new file mode 100644
index 0000000000000000000000000000000000000000..4b705a5f9dfc9e071cbd45a47f1b9b53e7bac001
--- /dev/null
+++ b/base/common/src/com/netscape/certsrv/logging/AuditResource.java
@@ -0,0 +1,46 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2013 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+package com.netscape.certsrv.logging;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.jboss.resteasy.annotations.ClientResponseType;
+
+
+/**
+ * @author Endi S. Dewata
+ */
+@Path("audit")
+public interface AuditResource {
+
+    @GET
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    public AuditConfig getAuditConfig();
+
+    @PUT
+    @ClientResponseType(entityType=AuditConfig.class)
+    @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    public Response updateAuditConfig(AuditConfig configData);
+}
diff --git a/base/common/src/com/netscape/certsrv/ocsp/OCSPClient.java b/base/common/src/com/netscape/certsrv/ocsp/OCSPClient.java
index ab85bd2000da6d7c588eee5d8576b0c4032a2061..6163d48186356ce4d59764ba0560322fbc4f9117 100644
--- a/base/common/src/com/netscape/certsrv/ocsp/OCSPClient.java
+++ b/base/common/src/com/netscape/certsrv/ocsp/OCSPClient.java
@@ -22,6 +22,7 @@ import java.net.URISyntaxException;
 import com.netscape.certsrv.client.PKIClient;
 import com.netscape.certsrv.client.SubsystemClient;
 import com.netscape.certsrv.group.GroupClient;
+import com.netscape.certsrv.logging.AuditClient;
 import com.netscape.certsrv.selftests.SelfTestClient;
 import com.netscape.certsrv.user.UserClient;
 
@@ -33,6 +34,7 @@ public class OCSPClient extends SubsystemClient {
     }
 
     public void init() throws URISyntaxException {
+        addClient(new AuditClient(client, name));
         addClient(new GroupClient(client, name));
         addClient(new SelfTestClient(client, name));
         addClient(new UserClient(client, name));
diff --git a/base/common/src/com/netscape/certsrv/tks/TKSClient.java b/base/common/src/com/netscape/certsrv/tks/TKSClient.java
index a972241ce6521e9cd92138f2f99088704d7f3d0a..5adde7461fb678dd2d25a890cad5778cd5a810fe 100644
--- a/base/common/src/com/netscape/certsrv/tks/TKSClient.java
+++ b/base/common/src/com/netscape/certsrv/tks/TKSClient.java
@@ -22,6 +22,7 @@ import java.net.URISyntaxException;
 import com.netscape.certsrv.client.PKIClient;
 import com.netscape.certsrv.client.SubsystemClient;
 import com.netscape.certsrv.group.GroupClient;
+import com.netscape.certsrv.logging.AuditClient;
 import com.netscape.certsrv.selftests.SelfTestClient;
 import com.netscape.certsrv.system.TPSConnectorClient;
 import com.netscape.certsrv.user.UserClient;
@@ -34,6 +35,7 @@ public class TKSClient extends SubsystemClient {
     }
 
     public void init() throws URISyntaxException {
+        addClient(new AuditClient(client, name));
         addClient(new GroupClient(client, name));
         addClient(new SelfTestClient(client, name));
         addClient(new TPSConnectorClient(client, name));
diff --git a/base/common/src/com/netscape/certsrv/tps/TPSClient.java b/base/common/src/com/netscape/certsrv/tps/TPSClient.java
index eeccdb0183e7f89f219c136e4fab62ca7afc7e2a..40820d84ded6ec308c7b19540e46e64052ab8aa7 100644
--- a/base/common/src/com/netscape/certsrv/tps/TPSClient.java
+++ b/base/common/src/com/netscape/certsrv/tps/TPSClient.java
@@ -23,6 +23,7 @@ import com.netscape.certsrv.client.PKIClient;
 import com.netscape.certsrv.client.SubsystemClient;
 import com.netscape.certsrv.group.GroupClient;
 import com.netscape.certsrv.logging.ActivityClient;
+import com.netscape.certsrv.logging.AuditClient;
 import com.netscape.certsrv.selftests.SelfTestClient;
 import com.netscape.certsrv.tps.authenticator.AuthenticatorClient;
 import com.netscape.certsrv.tps.cert.TPSCertClient;
@@ -44,6 +45,7 @@ public class TPSClient extends SubsystemClient {
 
     public void init() throws URISyntaxException {
         addClient(new ActivityClient(client, name));
+        addClient(new AuditClient(client, name));
         addClient(new AuthenticatorClient(client, name));
         addClient(new TPSCertClient(client, name));
         addClient(new ConfigClient(client, name));
diff --git a/base/common/src/com/netscape/certsrv/tps/config/ConfigCollection.java b/base/common/src/com/netscape/certsrv/tps/config/ConfigCollection.java
deleted file mode 100644
index a36f61fd174ffe07562f3e5010c3418860ebe054..0000000000000000000000000000000000000000
--- a/base/common/src/com/netscape/certsrv/tps/config/ConfigCollection.java
+++ /dev/null
@@ -1,65 +0,0 @@
-// --- BEGIN COPYRIGHT BLOCK ---
-// This program is free software; you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation; version 2 of the License.
-//
-// This program 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 General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License along
-// with this program; if not, write to the Free Software Foundation, Inc.,
-// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-//
-// (C) 2013 Red Hat, Inc.
-// All rights reserved.
-// --- END COPYRIGHT BLOCK ---
-
-package com.netscape.certsrv.tps.config;
-
-import java.util.ArrayList;
-import java.util.Collection;
-
-import javax.xml.bind.annotation.XmlElement;
-import javax.xml.bind.annotation.XmlElementRef;
-import javax.xml.bind.annotation.XmlRootElement;
-
-import org.jboss.resteasy.plugins.providers.atom.Link;
-
-
-/**
- * @author Endi S. Dewata
- */
-@XmlRootElement(name="Configurations")
-public class ConfigCollection {
-
-    Collection<ConfigData> configs = new ArrayList<ConfigData>();
-    Collection<Link> links = new ArrayList<Link>();
-
-    @XmlElementRef
-    public Collection<ConfigData> getConfigs() {
-        return configs;
-    }
-
-    public void setConfigs(Collection<ConfigData> configs) {
-        this.configs = configs;
-    }
-
-    public void addConfig(ConfigData configData) {
-        configs.add(configData);
-    }
-
-    @XmlElement(name="Link")
-    public Collection<Link> getLinks() {
-        return links;
-    }
-
-    public void setLink(Collection<Link> links) {
-        this.links = links;
-    }
-
-    public void addLink(Link link) {
-        links.add(link);
-    }
-}
diff --git a/base/common/src/com/netscape/cms/logging/LogFile.java b/base/common/src/com/netscape/cms/logging/LogFile.java
index 2c3510c74fe5a90989569d8d083ed44dace624fa..c465a4e53e25fede60327f0c3fc6bf2ce6ec2b59 100644
--- a/base/common/src/com/netscape/cms/logging/LogFile.java
+++ b/base/common/src/com/netscape/cms/logging/LogFile.java
@@ -47,13 +47,15 @@ import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.util.Date;
 import java.util.Hashtable;
+import java.util.LinkedHashSet;
 import java.util.Locale;
 import java.util.Properties;
-import java.util.StringTokenizer;
+import java.util.Set;
 import java.util.Vector;
 
 import javax.servlet.ServletException;
 
+import org.apache.commons.lang.StringUtils;
 import org.mozilla.jss.CryptoManager;
 import org.mozilla.jss.crypto.CryptoToken;
 import org.mozilla.jss.crypto.ObjectNotFoundException;
@@ -91,7 +93,9 @@ public class LogFile implements ILogEventListener, IExtendedPluginInfo {
     public static final String PROP_SIGNED_AUDIT_LOG_SIGNING = "logSigning";
     public static final String PROP_SIGNED_AUDIT_CERT_NICKNAME =
                               "signedAuditCertNickname";
-    public static final String PROP_SIGNED_AUDIT_EVENTS = "events";
+    public static final String PROP_SIGNED_AUDIT_SELECTED_EVENTS = "events";
+    public static final String PROP_SIGNED_AUDIT_UNSELECTED_EVENTS = "unselected.events";
+    public static final String PROP_SIGNED_AUDIT_MANDATORY_EVENTS = "mandatory.events";
     public static final String PROP_LEVEL = "level";
     static final String PROP_FILE_NAME = "fileName";
     static final String PROP_LAST_HASH_FILE_NAME = "lastHashFileName";
@@ -184,10 +188,19 @@ public class LogFile implements ILogEventListener, IExtendedPluginInfo {
     private Thread mFlushThread = null;
 
     /**
+     * The mandatory log event types
+     */
+    protected Set<String> mandatoryEvents = new LinkedHashSet<String>();
+
+    /**
      * The selected log event types
      */
-    protected String mSelectedEventsList = null;
-    protected Vector<String> mSelectedEvents = null;
+    protected Set<String> selectedEvents = new LinkedHashSet<String>();
+
+    /**
+     * The unselected log event types
+     */
+    protected Set<String> unselectedEvents = new LinkedHashSet<String>();
 
     /**
      * The eventType that this log is triggered
@@ -272,14 +285,23 @@ public class LogFile implements ILogEventListener, IExtendedPluginInfo {
             }
         }
 
-        // selective logging
-        mSelectedEventsList = null;
-        try {
-            mSelectedEventsList = config.getString(PROP_SIGNED_AUDIT_EVENTS);
-        } catch (EBaseException e) {
-            // when not specified, ALL are selected by default
+        // mandatory events
+        String mandatoryEventsList = config.getString(PROP_SIGNED_AUDIT_MANDATORY_EVENTS, "");
+        for (String event : StringUtils.split(mandatoryEventsList, ", ")) {
+            mandatoryEvents.add(event);
+        }
+
+        // selected events
+        String selectedEventsList = config.getString(PROP_SIGNED_AUDIT_SELECTED_EVENTS, "");
+        for (String event : StringUtils.split(selectedEventsList, ", ")) {
+            selectedEvents.add(event);
+        }
+
+        // unselected events
+        String unselectedEventsList = config.getString(PROP_SIGNED_AUDIT_UNSELECTED_EVENTS, "");
+        for (String event : StringUtils.split(unselectedEventsList, ", ")) {
+            unselectedEvents.add(event);
         }
-        mSelectedEvents = string2Vector(mSelectedEventsList);
 
         try {
             init(config);
@@ -289,33 +311,13 @@ public class LogFile implements ILogEventListener, IExtendedPluginInfo {
     }
 
     /**
-     * turns a comma-separated String into a Vector
-     */
-    protected Vector<String> string2Vector(String theString) {
-        Vector<String> theVector = new Vector<String>();
-        if (theString == null) {
-            return theVector;
-        }
-
-        StringTokenizer tokens = new StringTokenizer(theString,
-                                                    ",");
-        while (tokens.hasMoreTokens()) {
-            String eventId = tokens.nextToken().trim();
-
-            theVector.addElement(eventId);
-            CMS.debug("LogFile: log event type selected: " + eventId);
-        }
-        return theVector;
-    }
-
-    /**
      * add the event to the selected events list
      *
      * @param event to be selected
      */
     public void selectEvent(String event) {
-        if (!mSelectedEvents.contains(event))
-            mSelectedEvents.addElement(event);
+        selectedEvents.add(event);
+        unselectedEvents.remove(event);
     }
 
     /**
@@ -324,8 +326,8 @@ public class LogFile implements ILogEventListener, IExtendedPluginInfo {
      * @param event to be de-selected
      */
     public void deselectEvent(String event) {
-        if (mSelectedEvents.contains(event))
-            mSelectedEvents.removeElement(event);
+        selectedEvents.remove(event);
+        unselectedEvents.add(event);
     }
 
     /**
@@ -334,9 +336,14 @@ public class LogFile implements ILogEventListener, IExtendedPluginInfo {
      * @param events comma-separated event list
      */
     public void replaceEvents(String events) {
-        Vector<String> v = string2Vector(events);
-        mSelectedEvents.removeAllElements();
-        mSelectedEvents = v;
+        // unselect all events
+        unselectedEvents.addAll(selectedEvents);
+        selectedEvents.clear();
+
+        // select specified events
+        for (String event : StringUtils.split(events, ", ")) {
+            selectEvent(event);
+        }
     }
 
     public static String base64Encode(byte[] bytes) throws IOException {
@@ -1066,22 +1073,29 @@ public class LogFile implements ILogEventListener, IExtendedPluginInfo {
             }
         }
 
-        // Is the event type selected?
         // If no selection specified in configuration, then all are selected
+        if (selectedEvents.isEmpty()) {
+            String entry = logEvt2String(ev);
+            log(entry);
+            return;
+        }
+
         // If no type specified in propertity file, then treated as selected
-        if (mSelectedEvents.size() > 0) {
-            String type = ev.getEventType();
-            if (type != null) {
-                if (!mSelectedEvents.contains(type)) {
-                    CMS.debug("LogFile: event type not selected: " + type);
-                    return;
-                }
-            }
+        String type = ev.getEventType();
+        if (type == null) {
+            String entry = logEvt2String(ev);
+            log(entry);
+            return;
         }
 
-        String entry = logEvt2String(ev);
+        // Is the event type mandatory or selected?
+        if (mandatoryEvents.contains(type) || selectedEvents.contains(type)) {
+            String entry = logEvt2String(ev);
+            log(entry);
+            return;
+        }
 
-        log(entry);
+        CMS.debug("LogFile: event type not selected: " + type);
     }
 
     public String logEvt2String(ILogEvent ev) {
@@ -1378,7 +1392,9 @@ public class LogFile implements ILogEventListener, IExtendedPluginInfo {
         //if( mType.equals( ILogger.PROP_SIGNED_AUDIT ) ) {
         v.addElement(PROP_SIGNED_AUDIT_LOG_SIGNING + "=");
         v.addElement(PROP_SIGNED_AUDIT_CERT_NICKNAME + "=");
-        v.addElement(PROP_SIGNED_AUDIT_EVENTS + "=");
+        v.addElement(PROP_SIGNED_AUDIT_MANDATORY_EVENTS + "=");
+        v.addElement(PROP_SIGNED_AUDIT_SELECTED_EVENTS + "=");
+        v.addElement(PROP_SIGNED_AUDIT_UNSELECTED_EVENTS + "=");
         //}
 
         return v;
@@ -1431,12 +1447,9 @@ public class LogFile implements ILogEventListener, IExtendedPluginInfo {
                                 + mSAuditCertNickName);
                 }
 
-                if (mSelectedEventsList == null) {
-                    v.addElement(PROP_SIGNED_AUDIT_EVENTS + "=");
-                } else {
-                    v.addElement(PROP_SIGNED_AUDIT_EVENTS + "="
-                                + mSelectedEventsList);
-                }
+                v.addElement(PROP_SIGNED_AUDIT_MANDATORY_EVENTS + "=" + StringUtils.join(mandatoryEvents, ","));
+                v.addElement(PROP_SIGNED_AUDIT_SELECTED_EVENTS + "=" + StringUtils.join(selectedEvents, ","));
+                v.addElement(PROP_SIGNED_AUDIT_UNSELECTED_EVENTS + "=" + StringUtils.join(unselectedEvents, ","));
             }
         } catch (Exception e) {
         }
@@ -1469,8 +1482,12 @@ public class LogFile implements ILogEventListener, IExtendedPluginInfo {
                             ";boolean;Enable audit logs to be signed",
                     PROP_SIGNED_AUDIT_CERT_NICKNAME +
                             ";string;The nickname of the certificate to be used to sign audit logs",
-                    PROP_SIGNED_AUDIT_EVENTS +
-                            ";string;A comma-separated list of strings used to specify particular signed audit log events",
+                    PROP_SIGNED_AUDIT_MANDATORY_EVENTS +
+                            ";string;A comma-separated list of strings used to specify mandatory signed audit log events",
+                    PROP_SIGNED_AUDIT_SELECTED_EVENTS +
+                            ";string;A comma-separated list of strings used to specify selected signed audit log events",
+                    PROP_SIGNED_AUDIT_UNSELECTED_EVENTS +
+                            ";string;A comma-separated list of strings used to specify unselected signed audit log events",
             };
 
             return params;
diff --git a/base/common/src/com/netscape/cmscore/logging/AuditService.java b/base/common/src/com/netscape/cmscore/logging/AuditService.java
new file mode 100644
index 0000000000000000000000000000000000000000..89ac6263ebb4def6a240b432e5e6eeabfa71c4fa
--- /dev/null
+++ b/base/common/src/com/netscape/cmscore/logging/AuditService.java
@@ -0,0 +1,177 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2013 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+
+package com.netscape.cmscore.logging;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.util.Collection;
+import java.util.LinkedHashSet;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Request;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+
+import org.apache.commons.lang.StringUtils;
+import org.jboss.resteasy.plugins.providers.atom.Link;
+
+import com.netscape.certsrv.apps.CMS;
+import com.netscape.certsrv.base.EBaseException;
+import com.netscape.certsrv.base.IConfigStore;
+import com.netscape.certsrv.base.PKIException;
+import com.netscape.certsrv.logging.AuditConfig;
+import com.netscape.certsrv.logging.AuditResource;
+import com.netscape.cms.servlet.base.PKIService;
+
+/**
+ * @author Endi S. Dewata
+ */
+public class AuditService extends PKIService implements AuditResource {
+
+    @Context
+    private UriInfo uriInfo;
+
+    @Context
+    private HttpHeaders headers;
+
+    @Context
+    private Request request;
+
+    @Context
+    private HttpServletRequest servletRequest;
+
+    public final static int DEFAULT_SIZE = 20;
+
+    public AuditService() {
+        CMS.debug("AuditService.<init>()");
+    }
+
+    public AuditConfig createAuditConfig() throws UnsupportedEncodingException, EBaseException {
+
+        IConfigStore cs = CMS.getConfigStore();
+
+        AuditConfig auditConfig = new AuditConfig();
+        auditConfig.setEnabled(cs.getBoolean("log.instance.SignedAudit.enable", false));
+        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));
+
+        // unselected events
+        for (String event : StringUtils.split(cs.getString("log.instance.SignedAudit.unselected.events", ""), ", ")) {
+            auditConfig.setOptionalEvent(event.trim(), false);
+        }
+
+        // in case of duplicates, selected events override unselected events
+        for (String event : StringUtils.split(cs.getString("log.instance.SignedAudit.events", ""), ", ")) {
+            auditConfig.setOptionalEvent(event.trim(), true);
+        }
+
+        // mandatory events
+        for (String event : StringUtils.split(cs.getString("log.instance.SignedAudit.mandatory.events", ""), ", ")) {
+            auditConfig.addMandatoryEvent(event.trim());
+        }
+
+        URI uri = uriInfo.getBaseUriBuilder().path(AuditResource.class).build();
+        auditConfig.setLink(new Link("self", uri));
+
+        return auditConfig;
+    }
+
+    @Override
+    public AuditConfig getAuditConfig() {
+
+        CMS.debug("AuditService.getAuditConfig()");
+
+        try {
+            return createAuditConfig();
+
+        } catch (PKIException e) {
+            throw e;
+
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new PKIException(e.getMessage());
+        }
+    }
+
+    @Override
+    public Response updateAuditConfig(AuditConfig auditConfig) {
+
+        CMS.debug("AuditService.updateAuditConfig()");
+
+        try {
+            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());
+            }
+
+            if (auditConfig.getInterval() != null) {
+                cs.putInteger("log.instance.SignedAudit.flushInterval", auditConfig.getInterval());
+            }
+
+            if (auditConfig.getBufferSize() != null) {
+                cs.putInteger("log.instance.SignedAudit.bufferSize", auditConfig.getBufferSize());
+            }
+
+            // update events selection
+            Collection<String> selected = new LinkedHashSet<String>();
+            Collection<String> unselected = new LinkedHashSet<String>();
+
+            for (String name : auditConfig.getOptionalEvents().keySet()) {
+                Boolean value = auditConfig.getOptionalEvent(name);
+
+                if (value) {
+                    selected.add(name);
+
+                } else {
+                    unselected.add(name);
+                }
+            }
+
+            cs.putString("log.instance.SignedAudit.events", StringUtils.join(selected, ","));
+            cs.putString("log.instance.SignedAudit.unselected.events", StringUtils.join(unselected, ","));
+
+            // mandatory events cannot be updated
+
+            cs.commit(true);
+
+            auditConfig = createAuditConfig();
+
+            return Response
+                    .ok(auditConfig)
+                    .type(MediaType.APPLICATION_XML)
+                    .build();
+
+        } catch (PKIException e) {
+            throw e;
+
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new PKIException(e.getMessage());
+        }
+    }
+}
diff --git a/base/java-tools/src/com/netscape/cmstools/cli/CACLI.java b/base/java-tools/src/com/netscape/cmstools/cli/CACLI.java
index fdef872fbf534c00b8a20afd6dab729505e41201..0ce1afe8d7f0f76f29a2dd3a563ff99d0145bfa7 100644
--- a/base/java-tools/src/com/netscape/cmstools/cli/CACLI.java
+++ b/base/java-tools/src/com/netscape/cmstools/cli/CACLI.java
@@ -21,6 +21,7 @@ package com.netscape.cmstools.cli;
 import com.netscape.certsrv.ca.CAClient;
 import com.netscape.certsrv.client.Client;
 import com.netscape.cmstools.group.GroupCLI;
+import com.netscape.cmstools.logging.AuditCLI;
 import com.netscape.cmstools.selftests.SelfTestCLI;
 import com.netscape.cmstools.user.UserCLI;
 
@@ -34,6 +35,7 @@ public class CACLI extends SubsystemCLI {
     public CACLI(CLI parent) {
         super("ca", "CA management commands", parent);
 
+        addModule(new AuditCLI(this));
         addModule(new GroupCLI(this));
         addModule(new SelfTestCLI(this));
         addModule(new UserCLI(this));
diff --git a/base/java-tools/src/com/netscape/cmstools/cli/KRACLI.java b/base/java-tools/src/com/netscape/cmstools/cli/KRACLI.java
index 3929b6f8de61552df5d8ed1b9ebb9817bad511c6..7bed774737eb1ab69d1550205f8156697fc9f2b2 100644
--- a/base/java-tools/src/com/netscape/cmstools/cli/KRACLI.java
+++ b/base/java-tools/src/com/netscape/cmstools/cli/KRACLI.java
@@ -21,6 +21,7 @@ package com.netscape.cmstools.cli;
 import com.netscape.certsrv.client.Client;
 import com.netscape.certsrv.kra.KRAClient;
 import com.netscape.cmstools.group.GroupCLI;
+import com.netscape.cmstools.logging.AuditCLI;
 import com.netscape.cmstools.selftests.SelfTestCLI;
 import com.netscape.cmstools.user.UserCLI;
 
@@ -34,6 +35,7 @@ public class KRACLI extends SubsystemCLI {
     public KRACLI(CLI parent) {
         super("kra", "KRA management commands", parent);
 
+        addModule(new AuditCLI(this));
         addModule(new GroupCLI(this));
         addModule(new SelfTestCLI(this));
         addModule(new UserCLI(this));
diff --git a/base/java-tools/src/com/netscape/cmstools/cli/OCSPCLI.java b/base/java-tools/src/com/netscape/cmstools/cli/OCSPCLI.java
index 3768a48e745e4632d22a755751e137ec40ec6299..1eea5aad5ce12cb36647f3ef63d93a1e76db503f 100644
--- a/base/java-tools/src/com/netscape/cmstools/cli/OCSPCLI.java
+++ b/base/java-tools/src/com/netscape/cmstools/cli/OCSPCLI.java
@@ -21,6 +21,7 @@ package com.netscape.cmstools.cli;
 import com.netscape.certsrv.client.Client;
 import com.netscape.certsrv.ocsp.OCSPClient;
 import com.netscape.cmstools.group.GroupCLI;
+import com.netscape.cmstools.logging.AuditCLI;
 import com.netscape.cmstools.selftests.SelfTestCLI;
 import com.netscape.cmstools.user.UserCLI;
 
@@ -34,6 +35,7 @@ public class OCSPCLI extends SubsystemCLI {
     public OCSPCLI(CLI parent) {
         super("ocsp", "OCSP management commands", parent);
 
+        addModule(new AuditCLI(this));
         addModule(new GroupCLI(this));
         addModule(new SelfTestCLI(this));
         addModule(new UserCLI(this));
diff --git a/base/java-tools/src/com/netscape/cmstools/cli/TKSCLI.java b/base/java-tools/src/com/netscape/cmstools/cli/TKSCLI.java
index dd4f179c262036b286d3f44526f9a4a8fac2211e..cfc5d6ab512997b1e5a078c2a35a3c04737d872b 100644
--- a/base/java-tools/src/com/netscape/cmstools/cli/TKSCLI.java
+++ b/base/java-tools/src/com/netscape/cmstools/cli/TKSCLI.java
@@ -21,6 +21,7 @@ package com.netscape.cmstools.cli;
 import com.netscape.certsrv.client.Client;
 import com.netscape.certsrv.tks.TKSClient;
 import com.netscape.cmstools.group.GroupCLI;
+import com.netscape.cmstools.logging.AuditCLI;
 import com.netscape.cmstools.selftests.SelfTestCLI;
 import com.netscape.cmstools.system.TPSConnectorCLI;
 import com.netscape.cmstools.user.UserCLI;
@@ -35,6 +36,7 @@ public class TKSCLI extends SubsystemCLI {
     public TKSCLI(CLI parent) {
         super("tks", "TKS management commands", parent);
 
+        addModule(new AuditCLI(this));
         addModule(new GroupCLI(this));
         addModule(new SelfTestCLI(this));
         addModule(new TPSConnectorCLI(this));
diff --git a/base/java-tools/src/com/netscape/cmstools/cli/TPSCLI.java b/base/java-tools/src/com/netscape/cmstools/cli/TPSCLI.java
index a3fe27eb831579add73fcb781f1ecc5b361e2b73..b6df2995a923f5b3704eb315f3d19e1839d40af7 100644
--- a/base/java-tools/src/com/netscape/cmstools/cli/TPSCLI.java
+++ b/base/java-tools/src/com/netscape/cmstools/cli/TPSCLI.java
@@ -22,6 +22,7 @@ import com.netscape.certsrv.client.Client;
 import com.netscape.certsrv.tps.TPSClient;
 import com.netscape.cmstools.group.GroupCLI;
 import com.netscape.cmstools.logging.ActivityCLI;
+import com.netscape.cmstools.logging.AuditCLI;
 import com.netscape.cmstools.selftests.SelfTestCLI;
 import com.netscape.cmstools.tps.authenticator.AuthenticatorCLI;
 import com.netscape.cmstools.tps.cert.TPSCertCLI;
@@ -42,6 +43,7 @@ public class TPSCLI extends SubsystemCLI {
         super("tps", "TPS management commands", mainCLI);
 
         addModule(new ActivityCLI(this));
+        addModule(new AuditCLI(this));
         addModule(new AuthenticatorCLI(this));
         addModule(new TPSCertCLI(this));
         addModule(new ConfigCLI(this));
diff --git a/base/java-tools/src/com/netscape/cmstools/logging/AuditCLI.java b/base/java-tools/src/com/netscape/cmstools/logging/AuditCLI.java
new file mode 100644
index 0000000000000000000000000000000000000000..42e735ecac744db6454fbf93398a9124ae595277
--- /dev/null
+++ b/base/java-tools/src/com/netscape/cmstools/logging/AuditCLI.java
@@ -0,0 +1,71 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2013 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+
+package com.netscape.cmstools.logging;
+
+import java.io.IOException;
+import java.util.Map;
+
+import org.jboss.resteasy.plugins.providers.atom.Link;
+
+import com.netscape.certsrv.logging.AuditClient;
+import com.netscape.certsrv.logging.AuditConfig;
+import com.netscape.cmstools.cli.CLI;
+
+/**
+ * @author Endi S. Dewata
+ */
+public class AuditCLI extends CLI {
+
+    public AuditClient auditClient;
+
+    public AuditCLI(CLI parent) {
+        super("audit", "Audit management commands", parent);
+
+        addModule(new AuditModifyCLI(this));
+        addModule(new AuditShowCLI(this));
+    }
+
+    public void execute(String[] args) throws Exception {
+
+        client = parent.getClient();
+        auditClient = (AuditClient)parent.getClient("audit");
+
+        super.execute(args);
+    }
+
+    public static void printAuditConfig(AuditConfig auditConfig) throws IOException {
+
+        if (auditConfig.getEnabled() != null) System.out.println("  Enabled: " + auditConfig.getEnabled());
+        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());
+
+        System.out.println("  Events:");
+        Map<String, Boolean> events = auditConfig.getOptionalEvents();
+        for (String name : events.keySet()) {
+            Boolean value = events.get(name);
+            System.out.println("    " + name + ": " + value);
+        }
+
+        Link link = auditConfig.getLink();
+        if (verbose && link != null) {
+            System.out.println("  Link: " + link.getHref());
+        }
+    }
+}
diff --git a/base/java-tools/src/com/netscape/cmstools/logging/AuditModifyCLI.java b/base/java-tools/src/com/netscape/cmstools/logging/AuditModifyCLI.java
new file mode 100644
index 0000000000000000000000000000000000000000..9906c20ee6738a775d4dd97c23e54e842c42e696
--- /dev/null
+++ b/base/java-tools/src/com/netscape/cmstools/logging/AuditModifyCLI.java
@@ -0,0 +1,115 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2013 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+
+package com.netscape.cmstools.logging;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.Option;
+
+import com.netscape.certsrv.logging.AuditConfig;
+import com.netscape.cmstools.cli.CLI;
+import com.netscape.cmstools.cli.MainCLI;
+
+/**
+ * @author Endi S. Dewata
+ */
+public class AuditModifyCLI extends CLI {
+
+    public AuditCLI auditCLI;
+
+    public AuditModifyCLI(AuditCLI auditCLI) {
+        super("mod", "Modify audit configuration", auditCLI);
+        this.auditCLI = auditCLI;
+    }
+
+    public void printHelp() {
+        formatter.printHelp(getFullName() + " [OPTIONS...]", options);
+    }
+
+    public void execute(String[] args) throws Exception {
+
+        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.");
+        option.setArgName("file");
+        options.addOption(option);
+
+        CommandLine cmd = null;
+
+        try {
+            cmd = parser.parse(options, args);
+
+        } catch (Exception e) {
+            System.err.println("Error: " + e.getMessage());
+            printHelp();
+            System.exit(1);
+        }
+
+        String[] cmdArgs = cmd.getArgs();
+
+        if (cmdArgs.length != 0) {
+            printHelp();
+            System.exit(1);
+        }
+
+        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)) {
+
+            String line;
+            while ((line = in.readLine()) != null) {
+                out.println(line);
+            }
+
+            auditConfig = AuditConfig.valueOf(sw.toString());
+        }
+
+        auditConfig = auditCLI.auditClient.updateAuditConfig(auditConfig);
+
+        MainCLI.printMessage("Updated audit configuration");
+
+        if (output == null) {
+            AuditCLI.printAuditConfig(auditConfig);
+
+        } else {
+            try (PrintWriter out = new PrintWriter(new FileWriter(output))) {
+                out.println(auditConfig);
+            }
+        }
+    }
+}
diff --git a/base/java-tools/src/com/netscape/cmstools/logging/AuditShowCLI.java b/base/java-tools/src/com/netscape/cmstools/logging/AuditShowCLI.java
new file mode 100644
index 0000000000000000000000000000000000000000..9968d722d82caf793dfb3da936b0c372a18e8cd6
--- /dev/null
+++ b/base/java-tools/src/com/netscape/cmstools/logging/AuditShowCLI.java
@@ -0,0 +1,86 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2013 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+
+package com.netscape.cmstools.logging;
+
+import java.io.FileWriter;
+import java.io.PrintWriter;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.Option;
+
+import com.netscape.certsrv.logging.AuditConfig;
+import com.netscape.cmstools.cli.CLI;
+import com.netscape.cmstools.cli.MainCLI;
+
+/**
+ * @author Endi S. Dewata
+ */
+public class AuditShowCLI extends CLI {
+
+    public AuditCLI auditCLI;
+
+    public AuditShowCLI(AuditCLI auditCLI) {
+        super("show", "Show audit configuration", auditCLI);
+        this.auditCLI = auditCLI;
+    }
+
+    public void printHelp() {
+        formatter.printHelp(getFullName(), options);
+    }
+
+    public void execute(String[] args) throws Exception {
+
+        Option option = new Option(null, "output", true, "Output file to store audit configuration.");
+        option.setArgName("file");
+        options.addOption(option);
+
+        CommandLine cmd = null;
+
+        try {
+            cmd = parser.parse(options, args);
+
+        } catch (Exception e) {
+            System.err.println("Error: " + e.getMessage());
+            printHelp();
+            System.exit(1);
+        }
+
+        String[] cmdArgs = cmd.getArgs();
+
+        if (cmdArgs.length != 0) {
+            printHelp();
+            System.exit(1);
+        }
+
+        String output = cmd.getOptionValue("output");
+
+        AuditConfig auditConfig = auditCLI.auditClient.getAuditConfig();
+
+        if (output == null) {
+            MainCLI.printMessage("Audit configuration");
+            AuditCLI.printAuditConfig(auditConfig);
+
+        } else {
+            try (PrintWriter out = new PrintWriter(new FileWriter(output))) {
+                out.println(auditConfig);
+            }
+            MainCLI.printMessage("Stored audit configuration into " + output);
+        }
+    }
+}
diff --git a/base/kra/src/com/netscape/kra/KeyRecoveryAuthorityApplication.java b/base/kra/src/com/netscape/kra/KeyRecoveryAuthorityApplication.java
index b72fa423bda9528bf75a0c05b6033015c96c82ed..04b4989ef4f5cd97447fee310d5e8a24ce72e5c3 100644
--- a/base/kra/src/com/netscape/kra/KeyRecoveryAuthorityApplication.java
+++ b/base/kra/src/com/netscape/kra/KeyRecoveryAuthorityApplication.java
@@ -18,6 +18,7 @@ import com.netscape.cms.servlet.admin.UserService;
 import com.netscape.cms.servlet.csadmin.SystemConfigService;
 import com.netscape.cms.servlet.key.KeyService;
 import com.netscape.cms.servlet.request.KeyRequestService;
+import com.netscape.cmscore.logging.AuditService;
 import com.netscape.cmscore.selftests.SelfTestService;
 
 public class KeyRecoveryAuthorityApplication extends Application {
@@ -30,6 +31,9 @@ public class KeyRecoveryAuthorityApplication extends Application {
         // account
         classes.add(AccountService.class);
 
+        // audit
+        classes.add(AuditService.class);
+
         // installer
         classes.add(SystemConfigService.class);
 
diff --git a/base/ocsp/src/com/netscape/ocsp/OCSPApplication.java b/base/ocsp/src/com/netscape/ocsp/OCSPApplication.java
index 951a29b60fb33979dc85a85b40b16a49d8c993be..39c17cede189635f19a480e16cf06066c22288cc 100644
--- a/base/ocsp/src/com/netscape/ocsp/OCSPApplication.java
+++ b/base/ocsp/src/com/netscape/ocsp/OCSPApplication.java
@@ -16,6 +16,7 @@ import com.netscape.cms.servlet.admin.UserCertService;
 import com.netscape.cms.servlet.admin.UserMembershipService;
 import com.netscape.cms.servlet.admin.UserService;
 import com.netscape.cms.servlet.csadmin.SystemConfigService;
+import com.netscape.cmscore.logging.AuditService;
 import com.netscape.cmscore.selftests.SelfTestService;
 
 public class OCSPApplication extends Application {
@@ -28,6 +29,9 @@ public class OCSPApplication extends Application {
         // account
         classes.add(AccountService.class);
 
+        // audit
+        classes.add(AuditService.class);
+
         // installer
         classes.add(SystemConfigService.class);
 
diff --git a/base/tks/src/com/netscape/tks/TKSApplication.java b/base/tks/src/com/netscape/tks/TKSApplication.java
index 27701e8ec1b6c5018af9951f51bdcf39b41de5f9..ff9d66ed265bda273cb7cafa6a90684980493e1d 100644
--- a/base/tks/src/com/netscape/tks/TKSApplication.java
+++ b/base/tks/src/com/netscape/tks/TKSApplication.java
@@ -17,6 +17,7 @@ import com.netscape.cms.servlet.admin.UserMembershipService;
 import com.netscape.cms.servlet.admin.UserService;
 import com.netscape.cms.servlet.csadmin.SystemConfigService;
 import com.netscape.cms.servlet.tks.TPSConnectorService;
+import com.netscape.cmscore.logging.AuditService;
 import com.netscape.cmscore.selftests.SelfTestService;
 
 public class TKSApplication extends Application {
@@ -29,6 +30,9 @@ public class TKSApplication extends Application {
         // account
         classes.add(AccountService.class);
 
+        // audit
+        classes.add(AuditService.class);
+
         // installer
         classes.add(SystemConfigService.class);
 
diff --git a/base/tps-tomcat/shared/conf/CS.cfg.in b/base/tps-tomcat/shared/conf/CS.cfg.in
index 02ab97415ab99acb2d910b196df6601350667c78..0458bc5cc39e253de83bd6e5b01510ff4dd559c0 100644
--- a/base/tps-tomcat/shared/conf/CS.cfg.in
+++ b/base/tps-tomcat/shared/conf/CS.cfg.in
@@ -275,7 +275,9 @@ log.instance.SignedAudit._005=## AUDIT_LOG_STARTUP,AUDIT_LOG_SHUTDOWN,ROLE_ASSUM
 log.instance.SignedAudit._006=##
 log.instance.SignedAudit.bufferSize=512
 log.instance.SignedAudit.enable=true
-log.instance.SignedAudit.events=AUDIT_LOG_STARTUP,AUDIT_LOG_SHUTDOWN,ROLE_ASSUME,CONFIG_CERT_POLICY,CONFIG_CERT_PROFILE,CONFIG_CRL_PROFILE,CONFIG_OCSP_PROFILE,CONFIG_AUTH,CONFIG_ROLE,CONFIG_ACL,CONFIG_SIGNED_AUDIT,CONFIG_ENCRYPTION,CONFIG_TRUSTED_PUBLIC_KEY,CONFIG_DRM,SELFTESTS_EXECUTION,AUDIT_LOG_DELETE,LOG_PATH_CHANGE,LOG_EXPIRATION_CHANGE,PRIVATE_KEY_ARCHIVE_REQUEST,PRIVATE_KEY_ARCHIVE_REQUEST_PROCESSED,PRIVATE_KEY_EXPORT_REQUEST_PROCESSED_SUCCESS,PRIVATE_KEY_EXPORT_REQUEST_PROCESSED_FAILURE,KEY_RECOVERY_REQUEST,KEY_RECOVERY_REQUEST_ASYNC,KEY_RECOVERY_AGENT_LOGIN,KEY_RECOVERY_REQUEST_PROCESSED,KEY_RECOVERY_REQUEST_PROCESSED_ASYNC,KEY_GEN_ASYMMETRIC,NON_PROFILE_CERT_REQUEST,PROFILE_CERT_REQUEST,CERT_REQUEST_PROCESSED,CERT_STATUS_CHANGE_REQUEST,CERT_STATUS_CHANGE_REQUEST_PROCESSED,AUTHZ_SUCCESS,AUTHZ_FAIL,INTER_BOUNDARY,AUTH_FAIL,AUTH_SUCCESS,CERT_PROFILE_APPROVAL,PROOF_OF_POSSESSION,CRL_RETRIEVAL,CRL_VALIDATION,CMC_SIGNED_REQUEST_SIG_VERIFY,SERVER_SIDE_KEYGEN_REQUEST_PROCESSED_FAILURE,SERVER_SIDE_KEYGEN_REQUEST_PROCESSED_SUCCESS,SERVER_SIDE_KEYGEN_REQUEST,COMPUTE_SESSION_KEY_REQUEST,COMPUTE_SESSION_KEY_REQUEST_PROCESSED_SUCCESS, COMPUTE_SESSION_KEY_REQUEST_PROCESSED_FAILURE,DIVERSIFY_KEY_REQUEST,DIVERSIFY_KEY_REQUEST_PROCESSED_SUCCESS, DIVERSIFY_KEY_REQUEST_PROCESSED_FAILURE,ENCRYPT_DATA_REQUEST,ENCRYPT_DATA_REQUEST_PROCESSED_SUCCESS,ENCRYPT_DATA_REQUEST_PROCESSED_FAILURE,OCSP_ADD_CA_REQUEST,OCSP_ADD_CA_REQUEST_PROCESSED,OCSP_REMOVE_CA_REQUEST,OCSP_REMOVE_CA_REQUEST_PROCESSED_SUCCESS,OCSP_REMOVE_CA_REQUEST_PROCESSED_FAILURE,COMPUTE_RANDOM_DATA_REQUEST,COMPUTE_RANDOM_DATA_REQUEST_PROCESSED_SUCCESS,COMPUTE_RANDOM_DATA_REQUEST_PROCESSED_FAILURE,CIMC_CERT_VERIFICATION,LOGGING_SIGNED_AUDIT_SIGNING,ENROLLMENT,PIN_RESET,FORMAT,CONFIG,CONFIG_TOKEN,CONFIG_PROFILE,APPLET_UPGRADE,KEY_CHANGEOVER,RENEWAL,CIMC_CERT_VERIFICATION
+log.instance.SignedAudit.events=AUTHZ_SUCCESS,AUTHZ_FAIL,AUTH_FAIL,AUTH_SUCCESS,ROLE_ASSUME,ENROLLMENT,PIN_RESET,FORMAT,CONFIG,CONFIG_ROLE,CONFIG_TOKEN,CONFIG_PROFILE,CONFIG_AUDIT,APPLET_UPGRADE,KEY_CHANGEOVER,RENEWAL,CIMC_CERT_VERIFICATION
+log.instance.SignedAudit.unselected.events=
+log.instance.SignedAudit.mandatory.events=AUDIT_LOG_STARTUP,AUDIT_LOG_SHUTDOWN,LOGGING_SIGNED_AUDIT_SIGNING
 log.instance.SignedAudit.expirationTime=0
 log.instance.SignedAudit.fileName=[PKI_INSTANCE_PATH]/logs/[PKI_SUBSYSTEM_TYPE]/signedAudit/tps_cert-tps_audit
 log.instance.SignedAudit.flushInterval=5
diff --git a/base/tps-tomcat/src/org/dogtagpki/server/tps/TPSApplication.java b/base/tps-tomcat/src/org/dogtagpki/server/tps/TPSApplication.java
index 0387298cee18d89414644c1437258d84c097a21e..bdce5831f67fd44c8167291a5fe14e28eebc09a1 100644
--- a/base/tps-tomcat/src/org/dogtagpki/server/tps/TPSApplication.java
+++ b/base/tps-tomcat/src/org/dogtagpki/server/tps/TPSApplication.java
@@ -41,6 +41,7 @@ import com.netscape.cms.servlet.admin.UserCertService;
 import com.netscape.cms.servlet.admin.UserMembershipService;
 import com.netscape.cms.servlet.admin.UserService;
 import com.netscape.cms.servlet.csadmin.SystemConfigService;
+import com.netscape.cmscore.logging.AuditService;
 import com.netscape.cmscore.selftests.SelfTestService;
 
 /**
@@ -56,6 +57,9 @@ public class TPSApplication extends Application {
         // account
         classes.add(AccountService.class);
 
+        // audit
+        classes.add(AuditService.class);
+
         // installer
         classes.add(SystemConfigService.class);
 
-- 
1.8.1.4

