You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@knox.apache.org by km...@apache.org on 2014/01/27 18:00:39 UTC

[2/2] git commit: KNOX-23: Generate audit log of all gateway activity

KNOX-23: Generate audit log of all gateway activity


Project: http://git-wip-us.apache.org/repos/asf/incubator-knox/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-knox/commit/f69acbbc
Tree: http://git-wip-us.apache.org/repos/asf/incubator-knox/tree/f69acbbc
Diff: http://git-wip-us.apache.org/repos/asf/incubator-knox/diff/f69acbbc

Branch: refs/heads/master
Commit: f69acbbc4d02c12884101e7c8145efb77ef98f59
Parents: a9e7dea
Author: Kevin Minder <ke...@hortonworks.com>
Authored: Mon Jan 27 11:58:55 2014 -0500
Committer: Kevin Minder <ke...@hortonworks.com>
Committed: Mon Jan 27 11:58:55 2014 -0500

----------------------------------------------------------------------
 .../gateway/filter/AclsAuthorizationFilter.java |  13 +-
 .../filter/AclsAuthorizationResources.java      |  27 ++
 .../filter/ShiroSubjectIdentityAdapter.java     |  16 ++
 .../home/conf/gateway-log4j.properties          |   8 +
 .../apache/hadoop/gateway/GatewayFactory.java   |   2 +-
 .../apache/hadoop/gateway/GatewayFilter.java    |  70 ++++-
 .../apache/hadoop/gateway/GatewayResources.java |   4 +
 .../apache/hadoop/gateway/GatewayServer.java    |  12 +
 .../apache/hadoop/gateway/GatewayServlet.java   |  15 +
 .../gateway/dispatch/AppCookieManager.java      |  11 +-
 .../hadoop/gateway/dispatch/HdfsDispatch.java   |  12 +
 .../gateway/dispatch/HttpClientDispatch.java    |  13 +
 .../gateway/dispatch/UrlConnectionDispatch.java |  16 ++
 .../apache/hadoop/gateway/AuditLoggingTest.java | 226 +++++++++++++++
 .../hadoop/gateway/GatewayFilterTest.java       |   2 +-
 .../src/test/resources/log4j.properties         |   3 +
 .../gateway/filter/AbstractGatewayFilter.java   |   1 +
 .../AbstractIdentityAssertionFilter.java        |  18 ++
 .../gateway/i18n/GatewaySpiResources.java       |  28 ++
 .../apache/hadoop/test/log/CollectAppender.java |  51 ++++
 gateway-util-common/pom.xml                     |  12 +
 .../apache/hadoop/gateway/audit/api/Action.java |  33 +++
 .../hadoop/gateway/audit/api/ActionOutcome.java |  36 +++
 .../hadoop/gateway/audit/api/AuditContext.java  |  51 ++++
 .../hadoop/gateway/audit/api/AuditService.java  |  77 +++++
 .../gateway/audit/api/AuditServiceFactory.java  |  43 +++
 .../hadoop/gateway/audit/api/Auditor.java       |  86 ++++++
 .../gateway/audit/api/CorrelationContext.java   |  74 +++++
 .../gateway/audit/api/CorrelationService.java   |  99 +++++++
 .../audit/api/CorrelationServiceFactory.java    |  44 +++
 .../hadoop/gateway/audit/api/ResourceType.java  |  28 ++
 .../gateway/audit/log4j/appender/JdbmQueue.java | 171 +++++++++++
 .../appender/JdbmStoreAndForwardAppender.java   | 115 ++++++++
 .../audit/log4j/audit/AuditConstants.java       |  35 +++
 .../audit/log4j/audit/Log4jAuditContext.java    | 116 ++++++++
 .../audit/log4j/audit/Log4jAuditService.java    |  84 ++++++
 .../gateway/audit/log4j/audit/Log4jAuditor.java | 111 ++++++++
 .../correlation/Log4jCorrelationContext.java    | 105 +++++++
 .../correlation/Log4jCorrelationService.java    | 128 +++++++++
 .../gateway/audit/log4j/layout/AuditLayout.java |  78 ++++++
 .../hadoop/gateway/audit/AuditLayoutTest.java   | 141 ++++++++++
 .../hadoop/gateway/audit/AuditServiceTest.java  | 167 +++++++++++
 .../hadoop/gateway/audit/JdbmQueueTest.java     | 280 +++++++++++++++++++
 .../audit/StoreAndForwardAppenderTest.java      |  61 ++++
 .../src/test/resources/log4j.properties         |  25 ++
 pom.xml                                         |   6 +-
 46 files changed, 2742 insertions(+), 12 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/f69acbbc/gateway-provider-security-authz-acls/src/main/java/org/apache/hadoop/gateway/filter/AclsAuthorizationFilter.java
----------------------------------------------------------------------
diff --git a/gateway-provider-security-authz-acls/src/main/java/org/apache/hadoop/gateway/filter/AclsAuthorizationFilter.java b/gateway-provider-security-authz-acls/src/main/java/org/apache/hadoop/gateway/filter/AclsAuthorizationFilter.java
index 78ee5fa..6f31193 100644
--- a/gateway-provider-security-authz-acls/src/main/java/org/apache/hadoop/gateway/filter/AclsAuthorizationFilter.java
+++ b/gateway-provider-security-authz-acls/src/main/java/org/apache/hadoop/gateway/filter/AclsAuthorizationFilter.java
@@ -27,11 +27,18 @@ import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.apache.hadoop.gateway.audit.api.Action;
+import org.apache.hadoop.gateway.audit.api.ActionOutcome;
+import org.apache.hadoop.gateway.audit.api.AuditServiceFactory;
+import org.apache.hadoop.gateway.audit.api.Auditor;
+import org.apache.hadoop.gateway.audit.api.ResourceType;
+import org.apache.hadoop.gateway.audit.log4j.audit.AuditConstants;
 import org.apache.hadoop.gateway.i18n.messages.MessagesFactory;
 import org.apache.hadoop.gateway.security.GroupPrincipal;
 import org.apache.hadoop.gateway.security.ImpersonatedPrincipal;
 import org.apache.hadoop.gateway.security.PrimaryPrincipal;
 import org.apache.hadoop.gateway.util.IpAddressValidator;
+import org.apache.hadoop.gateway.util.urltemplate.Template;
 
 import java.io.IOException;
 import java.security.AccessController;
@@ -41,6 +48,8 @@ import java.util.Collections;
 
 public class AclsAuthorizationFilter implements Filter {
   private static AclsAuthorizationMessages log = MessagesFactory.get( AclsAuthorizationMessages.class );
+  private static Auditor auditor = AuditServiceFactory.getAuditService().getAuditor( AuditConstants.DEFAULT_AUDITOR_NAME,
+          AuditConstants.KNOX_SERVICE_NAME, AuditConstants.KNOX_COMPONENT_NAME );
 
   private String resourceRole = null;
   private ArrayList<String> users;
@@ -126,11 +135,13 @@ public class AclsAuthorizationFilter implements Filter {
       FilterChain chain) throws IOException, ServletException {
     boolean accessGranted = enforceAclAuthorizationPolicy(request, response, chain);
     log.accessGranted(accessGranted);
-
+    String sourceUrl = (String)request.getAttribute( AbstractGatewayFilter.SOURCE_REQUEST_CONTEXT_URL_ATTRIBUTE_NAME );
     if (accessGranted) {
+      auditor.audit( Action.AUTHORIZATION, sourceUrl, ResourceType.URI, ActionOutcome.SUCCESS );
       chain.doFilter(request, response);
     }
     else {
+      auditor.audit( Action.AUTHORIZATION, sourceUrl, ResourceType.URI, ActionOutcome.FAILURE );
       sendUnauthorized((HttpServletResponse) response);
     }
   }

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/f69acbbc/gateway-provider-security-authz-acls/src/main/java/org/apache/hadoop/gateway/filter/AclsAuthorizationResources.java
----------------------------------------------------------------------
diff --git a/gateway-provider-security-authz-acls/src/main/java/org/apache/hadoop/gateway/filter/AclsAuthorizationResources.java b/gateway-provider-security-authz-acls/src/main/java/org/apache/hadoop/gateway/filter/AclsAuthorizationResources.java
new file mode 100644
index 0000000..370336b
--- /dev/null
+++ b/gateway-provider-security-authz-acls/src/main/java/org/apache/hadoop/gateway/filter/AclsAuthorizationResources.java
@@ -0,0 +1,27 @@
+/**
+ * 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.
+ */
+package org.apache.hadoop.gateway.filter;
+
+import org.apache.hadoop.gateway.i18n.resources.Resource;
+import org.apache.hadoop.gateway.i18n.resources.Resources;
+
+@Resources
+public interface AclsAuthorizationResources {
+  @Resource( text = "Response status: {0}" )
+  String responseStatus( int status );
+}

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/f69acbbc/gateway-provider-security-shiro/src/main/java/org/apache/hadoop/gateway/filter/ShiroSubjectIdentityAdapter.java
----------------------------------------------------------------------
diff --git a/gateway-provider-security-shiro/src/main/java/org/apache/hadoop/gateway/filter/ShiroSubjectIdentityAdapter.java b/gateway-provider-security-shiro/src/main/java/org/apache/hadoop/gateway/filter/ShiroSubjectIdentityAdapter.java
index 7812056..352e4c7 100644
--- a/gateway-provider-security-shiro/src/main/java/org/apache/hadoop/gateway/filter/ShiroSubjectIdentityAdapter.java
+++ b/gateway-provider-security-shiro/src/main/java/org/apache/hadoop/gateway/filter/ShiroSubjectIdentityAdapter.java
@@ -31,6 +31,13 @@ import javax.servlet.ServletException;
 import javax.servlet.ServletRequest;
 import javax.servlet.ServletResponse;
 
+import org.apache.hadoop.gateway.audit.api.Action;
+import org.apache.hadoop.gateway.audit.api.ActionOutcome;
+import org.apache.hadoop.gateway.audit.api.AuditService;
+import org.apache.hadoop.gateway.audit.api.AuditServiceFactory;
+import org.apache.hadoop.gateway.audit.api.Auditor;
+import org.apache.hadoop.gateway.audit.api.ResourceType;
+import org.apache.hadoop.gateway.audit.log4j.audit.AuditConstants;
 import org.apache.hadoop.gateway.security.GroupPrincipal;
 import org.apache.hadoop.gateway.security.PrimaryPrincipal;
 import org.apache.shiro.SecurityUtils;
@@ -39,6 +46,11 @@ import org.apache.shiro.subject.Subject;
 public class ShiroSubjectIdentityAdapter implements Filter {
   
   private static final String SUBJECT_USER_GROUPS = "subject.userGroups";
+  private static AuditService auditService = AuditServiceFactory.getAuditService();
+  private static Auditor auditor = auditService.getAuditor(
+      AuditConstants.DEFAULT_AUDITOR_NAME, AuditConstants.KNOX_SERVICE_NAME,
+      AuditConstants.KNOX_COMPONENT_NAME );
+  
 
   @Override
   public void init( FilterConfig filterConfig ) throws ServletException {
@@ -88,6 +100,9 @@ public class ShiroSubjectIdentityAdapter implements Filter {
       Set<Principal> principals = new HashSet<Principal>();
       Principal p = new PrimaryPrincipal(principal);
       principals.add(p);
+      auditService.createContext().setUsername( principal );
+      String sourceUri = (String)request.getAttribute( AbstractGatewayFilter.SOURCE_REQUEST_CONTEXT_URL_ATTRIBUTE_NAME );
+      auditor.audit( Action.AUTHENTICATION , sourceUri, ResourceType.URI, ActionOutcome.SUCCESS );
       
       // map ldap groups saved in session to Java Subject GroupPrincipal(s)
       if (SecurityUtils.getSubject().getSession().getAttribute(SUBJECT_USER_GROUPS) != null) {
@@ -96,6 +111,7 @@ public class ShiroSubjectIdentityAdapter implements Filter {
           Principal gp = new GroupPrincipal(userRole);
           principals.add(gp);
         }
+        auditor.audit( Action.AUTHENTICATION , sourceUri, ResourceType.URI, ActionOutcome.SUCCESS, "Groups: " + userRoles );
       }
       
       // TODO: add groups through extended JndiLdapRealm implementation once Jira KNOX-4 is resolved

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/f69acbbc/gateway-release/home/conf/gateway-log4j.properties
----------------------------------------------------------------------
diff --git a/gateway-release/home/conf/gateway-log4j.properties b/gateway-release/home/conf/gateway-log4j.properties
index 5844f2c..3ad09a9 100644
--- a/gateway-release/home/conf/gateway-log4j.properties
+++ b/gateway-release/home/conf/gateway-log4j.properties
@@ -16,6 +16,7 @@
 
 app.log.dir=${launcher.dir}/../logs
 app.log.file=${launcher.name}.log
+app.audit.file=${launcher.name}-audit.log
 
 log4j.rootLogger=ERROR, drfa
 
@@ -39,3 +40,10 @@ log4j.appender.drfa.DatePattern=.yyyy-MM-dd
 log4j.appender.drfa.layout=org.apache.log4j.PatternLayout
 log4j.appender.drfa.layout.ConversionPattern=%d{ISO8601} %-5p %c{2} (%F:%M(%L)) - %m%n
 
+log4j.logger.audit=INFO, auditfile
+log4j.appender.auditfile=org.apache.log4j.DailyRollingFileAppender
+log4j.appender.auditfile.File=${app.log.dir}/${app.audit.file}
+log4j.appender.auditfile.Append = true
+log4j.appender.auditfile.DatePattern = '.'yyyy-MM-dd
+log4j.appender.auditfile.layout = org.apache.hadoop.gateway.audit.log4j.layout.AuditLayout
+

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/f69acbbc/gateway-server/src/main/java/org/apache/hadoop/gateway/GatewayFactory.java
----------------------------------------------------------------------
diff --git a/gateway-server/src/main/java/org/apache/hadoop/gateway/GatewayFactory.java b/gateway-server/src/main/java/org/apache/hadoop/gateway/GatewayFactory.java
index 197b274..8e978a2 100644
--- a/gateway-server/src/main/java/org/apache/hadoop/gateway/GatewayFactory.java
+++ b/gateway-server/src/main/java/org/apache/hadoop/gateway/GatewayFactory.java
@@ -74,7 +74,7 @@ public class GatewayFactory {
     if( name == null ) {
       name = filter.role();
     }
-    gateway.addFilter( filter.up().pattern(), name, filter.impl(), createParams( filter ) );
+    gateway.addFilter( filter.up().pattern(), name, filter.impl(), createParams( filter ), filter.up().role() );
   }
 
   private static Map<String, String> createParams( FilterDescriptor filter ) {

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/f69acbbc/gateway-server/src/main/java/org/apache/hadoop/gateway/GatewayFilter.java
----------------------------------------------------------------------
diff --git a/gateway-server/src/main/java/org/apache/hadoop/gateway/GatewayFilter.java b/gateway-server/src/main/java/org/apache/hadoop/gateway/GatewayFilter.java
index 87dd8f2..7b9281a 100644
--- a/gateway-server/src/main/java/org/apache/hadoop/gateway/GatewayFilter.java
+++ b/gateway-server/src/main/java/org/apache/hadoop/gateway/GatewayFilter.java
@@ -17,8 +17,19 @@
  */
 package org.apache.hadoop.gateway;
 
+import org.apache.hadoop.gateway.audit.api.Action;
+import org.apache.hadoop.gateway.audit.api.ActionOutcome;
+import org.apache.hadoop.gateway.audit.api.AuditContext;
+import org.apache.hadoop.gateway.audit.api.AuditService;
+import org.apache.hadoop.gateway.audit.api.AuditServiceFactory;
+import org.apache.hadoop.gateway.audit.api.Auditor;
+import org.apache.hadoop.gateway.audit.api.CorrelationContext;
+import org.apache.hadoop.gateway.audit.api.CorrelationServiceFactory;
+import org.apache.hadoop.gateway.audit.api.ResourceType;
+import org.apache.hadoop.gateway.audit.log4j.audit.AuditConstants;
 import org.apache.hadoop.gateway.filter.AbstractGatewayFilter;
 import org.apache.hadoop.gateway.i18n.messages.MessagesFactory;
+import org.apache.hadoop.gateway.i18n.resources.ResourcesFactory;
 import org.apache.hadoop.gateway.util.urltemplate.Matcher;
 import org.apache.hadoop.gateway.util.urltemplate.Parser;
 import org.apache.hadoop.gateway.util.urltemplate.Template;
@@ -32,6 +43,7 @@ import javax.servlet.ServletRequest;
 import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
+
 import java.io.IOException;
 import java.net.URISyntaxException;
 import java.util.ArrayList;
@@ -41,6 +53,7 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.UUID;
 
 /**
  *
@@ -53,6 +66,11 @@ public class GatewayFilter implements Filter {
   };
   
   private static final GatewayMessages LOG = MessagesFactory.get( GatewayMessages.class );
+  private static final GatewayResources RES = ResourcesFactory.get( GatewayResources.class );
+  private static AuditService auditService = AuditServiceFactory.getAuditService();
+  private static Auditor auditor = auditService.getAuditor(
+      AuditConstants.DEFAULT_AUDITOR_NAME, AuditConstants.KNOX_SERVICE_NAME,
+      AuditConstants.KNOX_COMPONENT_NAME );
 
   private Set<Holder> holders;
   private Matcher<Chain> chains;
@@ -88,34 +106,50 @@ public class GatewayFilter implements Filter {
     } catch( URISyntaxException e ) {
       throw new ServletException( e );
     }
-
+    String pathWithContext = httpRequest.getContextPath() + path;
     LOG.receivedRequest( httpRequest.getMethod(), pathTemplate );
 
     servletRequest.setAttribute( AbstractGatewayFilter.SOURCE_REQUEST_URL_ATTRIBUTE_NAME, pathTemplate );
+    servletRequest.setAttribute( AbstractGatewayFilter.SOURCE_REQUEST_CONTEXT_URL_ATTRIBUTE_NAME, pathWithContext );
 
     Matcher<Chain>.Match match = chains.match( pathTemplate );
+    
+    assignCorrelationRequestId();
+    // Populate Audit/correlation parameters
+    AuditContext auditContext = auditService.createContext();
+    auditContext.setTargetServiceName( match == null ? null : match.getValue().getResourceRole() );
+    auditContext.setRemoteIp( servletRequest.getRemoteAddr() );
+    auditContext.setRemoteHostname( servletRequest.getRemoteHost() );
+    auditor.audit( Action.ACCESS, pathWithContext, ResourceType.URI, ActionOutcome.UNAVAILABLE );
+    
     if( match != null ) {
       Chain chain = match.getValue();
       try {
         chain.doFilter( servletRequest, servletResponse );
       } catch( IOException e ) {
         LOG.failedToExecuteFilter( e );
+        auditor.audit( Action.ACCESS, pathWithContext, ResourceType.URI, ActionOutcome.FAILURE );
         throw e;
       } catch( ServletException e ) {
         LOG.failedToExecuteFilter( e );
+        auditor.audit( Action.ACCESS, pathWithContext, ResourceType.URI, ActionOutcome.FAILURE );
         throw e;
       } catch( RuntimeException e ) {
         LOG.failedToExecuteFilter( e );
+        auditor.audit( Action.ACCESS, pathWithContext, ResourceType.URI, ActionOutcome.FAILURE );
         throw e;
       } catch( ThreadDeath e ) {
         LOG.failedToExecuteFilter( e );
+        auditor.audit( Action.ACCESS, pathWithContext, ResourceType.URI, ActionOutcome.FAILURE );
         throw e;
       } catch( Throwable e ) {
         LOG.failedToExecuteFilter( e );
+        auditor.audit( Action.ACCESS, pathWithContext, ResourceType.URI, ActionOutcome.FAILURE );
         throw new ServletException( e );
       }
     } else {
       httpResponse.setStatus( HttpServletResponse.SC_NOT_FOUND );
+      auditor.audit( Action.ACCESS, pathWithContext, ResourceType.URI, ActionOutcome.SUCCESS, RES.responseStatus( HttpServletResponse.SC_NOT_FOUND ) );
     }
     //KAM[ Don't do this or the Jetty default servlet will overwrite any response setup by the filter.
     // filterChain.doFilter( servletRequest, servletResponse );
@@ -134,13 +168,14 @@ public class GatewayFilter implements Filter {
     Chain chain = chains.get( holder.template );
     if( chain == null ) {
       chain = new Chain();
+      chain.setResourceRole( holder.getResourceRole() );
       chains.add( holder.template, chain );
     }
     chain.chain.add( holder );
   }
 
-  public void addFilter( String path, String name, Filter filter, Map<String,String> params ) throws URISyntaxException {
-    Holder holder = new Holder( path, name, filter, params );
+  public void addFilter( String path, String name, Filter filter, Map<String,String> params, String resourceRole ) throws URISyntaxException {
+    Holder holder = new Holder( path, name, filter, params, resourceRole );
     addHolder( holder );
   }
 
@@ -149,14 +184,20 @@ public class GatewayFilter implements Filter {
 //    addHolder( holder );
 //  }
 
-  public void addFilter( String path, String name, String clazz, Map<String,String> params ) throws URISyntaxException {
-    Holder holder = new Holder( path, name, clazz, params );
+  public void addFilter( String path, String name, String clazz, Map<String,String> params, String resourceRole ) throws URISyntaxException {
+    Holder holder = new Holder( path, name, clazz, params, resourceRole );
     addHolder( holder );
   }
+  
+  private void assignCorrelationRequestId() {
+    CorrelationContext correlationContext = CorrelationServiceFactory.getCorrelationService().createContext();
+    correlationContext.setRequestId( UUID.randomUUID().toString() );
+  }
 
   private class Chain implements FilterChain {
 
     private List<Holder> chain;
+    private String resourceRole; 
 
     private Chain() {
       this.chain = new ArrayList<Holder>();
@@ -180,6 +221,14 @@ public class GatewayFilter implements Filter {
       }
     }
 
+    private String getResourceRole() {
+      return resourceRole;
+    }
+
+    private void setResourceRole( String resourceRole ) {
+      this.resourceRole = resourceRole;
+    }
+
   }
 
   private class Holder implements Filter, FilterConfig {
@@ -190,8 +239,9 @@ public class GatewayFilter implements Filter {
     private Filter instance;
     private Class<? extends Filter> clazz;
     private String type;
+    private String resourceRole;
 
-    private Holder( String path, String name, Filter filter, Map<String,String> params ) throws URISyntaxException {
+    private Holder( String path, String name, Filter filter, Map<String,String> params, String resourceRole ) throws URISyntaxException {
 //      this.path = path;
       this.template = Parser.parse( path );
       this.name = name;
@@ -199,6 +249,7 @@ public class GatewayFilter implements Filter {
       this.instance = filter;
       this.clazz = filter.getClass();
       this.type = clazz.getCanonicalName();
+      this.resourceRole = resourceRole;
     }
 
 //    private Holder( String path, String name, Class<WarDirFilter> clazz, Map<String,String> params ) throws URISyntaxException {
@@ -211,7 +262,7 @@ public class GatewayFilter implements Filter {
 //      this.type = clazz.getCanonicalName();
 //    }
 
-    private Holder( String path, String name, String clazz, Map<String,String> params ) throws URISyntaxException {
+    private Holder( String path, String name, String clazz, Map<String,String> params, String resourceRole ) throws URISyntaxException {
 //      this.path = path;
       this.template = Parser.parse( path );
       this.name = name;
@@ -219,6 +270,7 @@ public class GatewayFilter implements Filter {
       this.instance = null;
       this.clazz = null;
       this.type = clazz;
+      this.resourceRole = resourceRole;
     }
 
     @Override
@@ -293,6 +345,10 @@ public class GatewayFilter implements Filter {
       }
       return instance;
     }
+    
+    private String getResourceRole() {
+      return resourceRole;
+    }
 
   }
 

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/f69acbbc/gateway-server/src/main/java/org/apache/hadoop/gateway/GatewayResources.java
----------------------------------------------------------------------
diff --git a/gateway-server/src/main/java/org/apache/hadoop/gateway/GatewayResources.java b/gateway-server/src/main/java/org/apache/hadoop/gateway/GatewayResources.java
index 44e6267..ce4b6c7 100644
--- a/gateway-server/src/main/java/org/apache/hadoop/gateway/GatewayResources.java
+++ b/gateway-server/src/main/java/org/apache/hadoop/gateway/GatewayResources.java
@@ -67,4 +67,8 @@ public interface GatewayResources {
 
   @Resource( text="Failed to create keystore directory: {0}" )
   String failedToCreateKeyStoreDirectory( String name );
+
+  @Resource( text="Response status: {0}" )
+  String responseStatus( int status );
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/f69acbbc/gateway-server/src/main/java/org/apache/hadoop/gateway/GatewayServer.java
----------------------------------------------------------------------
diff --git a/gateway-server/src/main/java/org/apache/hadoop/gateway/GatewayServer.java b/gateway-server/src/main/java/org/apache/hadoop/gateway/GatewayServer.java
index 794a50f..fee50dd 100644
--- a/gateway-server/src/main/java/org/apache/hadoop/gateway/GatewayServer.java
+++ b/gateway-server/src/main/java/org/apache/hadoop/gateway/GatewayServer.java
@@ -21,6 +21,12 @@ import org.apache.commons.cli.CommandLine;
 import org.apache.commons.cli.ParseException;
 import org.apache.commons.io.FileUtils;
 import org.apache.commons.io.IOUtils;
+import org.apache.hadoop.gateway.audit.api.Action;
+import org.apache.hadoop.gateway.audit.api.ActionOutcome;
+import org.apache.hadoop.gateway.audit.api.AuditServiceFactory;
+import org.apache.hadoop.gateway.audit.api.Auditor;
+import org.apache.hadoop.gateway.audit.api.ResourceType;
+import org.apache.hadoop.gateway.audit.log4j.audit.AuditConstants;
 import org.apache.hadoop.gateway.config.GatewayConfig;
 import org.apache.hadoop.gateway.config.impl.GatewayConfigImpl;
 import org.apache.hadoop.gateway.deploy.DeploymentFactory;
@@ -63,6 +69,8 @@ import java.util.regex.Pattern;
 public class GatewayServer {
   private static GatewayResources res = ResourcesFactory.get( GatewayResources.class );
   private static GatewayMessages log = MessagesFactory.get( GatewayMessages.class );
+  private static Auditor auditor = AuditServiceFactory.getAuditService().getAuditor( AuditConstants.DEFAULT_AUDITOR_NAME,
+          AuditConstants.KNOX_SERVICE_NAME, AuditConstants.KNOX_COMPONENT_NAME );
   private static GatewayServer server;
   private static GatewayServices services;
   
@@ -350,6 +358,7 @@ public class GatewayServer {
             File[] files = topoDir.listFiles( new WarDirFilter( topology.getName() + "\\.war\\.[0-9A-Fa-f]+" ) );
             if( files != null ) {
               for( File file : files ) {
+                auditor.audit( Action.UNDEPLOY, topology.getName(), ResourceType.TOPOLOGY, ActionOutcome.UNAVAILABLE );
                 log.deletingDeployment( file.getAbsolutePath() );
                 internalUndeploy( topology );
                 FileUtils.deleteQuietly( file );
@@ -358,6 +367,7 @@ public class GatewayServer {
           } else {
             try {
               if( !warDir.exists() ) {
+                auditor.audit( Action.DEPLOY, topology.getName(), ResourceType.TOPOLOGY, ActionOutcome.UNAVAILABLE );
                 log.deployingTopology( topology.getName(), warDir.getAbsolutePath() );
                 internalUndeploy( topology ); // KNOX-152
                 WebArchive war = null;
@@ -370,11 +380,13 @@ public class GatewayServer {
                 internalDeploy( topology, warDir );
                 //log.deployedTopology( topology.getName());
               } else {
+                auditor.audit( Action.REDEPLOY, topology.getName(), ResourceType.TOPOLOGY, ActionOutcome.UNAVAILABLE );
                 log.redeployingTopology( topology.getName(), warDir.getAbsolutePath() );
                 internalDeploy( topology, warDir );
                 //log.redeployedTopology( topology.getName() );
               }
             } catch( Throwable e ) {
+              auditor.audit( Action.DEPLOY, topology.getName(), ResourceType.TOPOLOGY, ActionOutcome.FAILURE );
               log.failedToDeployTopology( topology.getName(), e );
             }
           }

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/f69acbbc/gateway-server/src/main/java/org/apache/hadoop/gateway/GatewayServlet.java
----------------------------------------------------------------------
diff --git a/gateway-server/src/main/java/org/apache/hadoop/gateway/GatewayServlet.java b/gateway-server/src/main/java/org/apache/hadoop/gateway/GatewayServlet.java
index 0764663..b25ec17 100644
--- a/gateway-server/src/main/java/org/apache/hadoop/gateway/GatewayServlet.java
+++ b/gateway-server/src/main/java/org/apache/hadoop/gateway/GatewayServlet.java
@@ -17,8 +17,15 @@
  */
 package org.apache.hadoop.gateway;
 
+import org.apache.hadoop.gateway.audit.api.Action;
+import org.apache.hadoop.gateway.audit.api.ActionOutcome;
+import org.apache.hadoop.gateway.audit.api.AuditServiceFactory;
+import org.apache.hadoop.gateway.audit.api.Auditor;
+import org.apache.hadoop.gateway.audit.api.ResourceType;
+import org.apache.hadoop.gateway.audit.log4j.audit.AuditConstants;
 import org.apache.hadoop.gateway.descriptor.GatewayDescriptor;
 import org.apache.hadoop.gateway.descriptor.GatewayDescriptorFactory;
+import org.apache.hadoop.gateway.filter.AbstractGatewayFilter;
 import org.apache.hadoop.gateway.i18n.messages.MessagesFactory;
 import org.apache.hadoop.gateway.i18n.resources.ResourcesFactory;
 import org.apache.hadoop.gateway.services.GatewayServices;
@@ -32,6 +39,7 @@ import javax.servlet.ServletException;
 import javax.servlet.ServletRequest;
 import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpServletResponse;
+
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
@@ -45,6 +53,10 @@ public class GatewayServlet implements Servlet {
 
   private static final GatewayResources res = ResourcesFactory.get( GatewayResources.class );
   private static final GatewayMessages LOG = MessagesFactory.get( GatewayMessages.class );
+  
+  private static Auditor auditor = AuditServiceFactory.getAuditService()
+      .getAuditor( AuditConstants.DEFAULT_AUDITOR_NAME,
+          AuditConstants.KNOX_SERVICE_NAME, AuditConstants.KNOX_COMPONENT_NAME );
 
   private FilterConfigAdapter filterConfig;
   private volatile GatewayFilter filter;
@@ -113,6 +125,9 @@ public class GatewayServlet implements Servlet {
     } else {
       ((HttpServletResponse)servletResponse).setStatus( HttpServletResponse.SC_SERVICE_UNAVAILABLE );
     }
+    String requestUri = (String)servletRequest.getAttribute( AbstractGatewayFilter.SOURCE_REQUEST_CONTEXT_URL_ATTRIBUTE_NAME );
+    int status = ((HttpServletResponse)servletResponse).getStatus();
+    auditor.audit( Action.ACCESS, requestUri, ResourceType.URI, ActionOutcome.SUCCESS, res.responseStatus( status ) );
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/f69acbbc/gateway-server/src/main/java/org/apache/hadoop/gateway/dispatch/AppCookieManager.java
----------------------------------------------------------------------
diff --git a/gateway-server/src/main/java/org/apache/hadoop/gateway/dispatch/AppCookieManager.java b/gateway-server/src/main/java/org/apache/hadoop/gateway/dispatch/AppCookieManager.java
index 50c120e..a316f3c 100644
--- a/gateway-server/src/main/java/org/apache/hadoop/gateway/dispatch/AppCookieManager.java
+++ b/gateway-server/src/main/java/org/apache/hadoop/gateway/dispatch/AppCookieManager.java
@@ -22,6 +22,12 @@ import java.net.URI;
 import java.security.Principal;
 
 import org.apache.hadoop.gateway.GatewayMessages;
+import org.apache.hadoop.gateway.audit.api.Action;
+import org.apache.hadoop.gateway.audit.api.ActionOutcome;
+import org.apache.hadoop.gateway.audit.api.AuditServiceFactory;
+import org.apache.hadoop.gateway.audit.api.Auditor;
+import org.apache.hadoop.gateway.audit.api.ResourceType;
+import org.apache.hadoop.gateway.audit.log4j.audit.AuditConstants;
 import org.apache.hadoop.gateway.i18n.messages.MessagesFactory;
 import org.apache.http.Header;
 import org.apache.http.HeaderElement;
@@ -52,7 +58,8 @@ public class AppCookieManager {
   private static final String SET_COOKIE = "Set-Cookie";
 
   private static GatewayMessages LOG = MessagesFactory.get(GatewayMessages.class);
-
+  private static Auditor auditor = AuditServiceFactory.getAuditService().getAuditor( AuditConstants.DEFAULT_AUDITOR_NAME,
+          AuditConstants.KNOX_SERVICE_NAME, AuditConstants.KNOX_COMPONENT_NAME );
   private static final EmptyJaasCredentials EMPTY_JAAS_CREDENTIALS = new EmptyJaasCredentials();
 
   String appCookie;
@@ -115,6 +122,7 @@ public class AppCookieManager {
       hadoopAuthCookie = getHadoopAuthCookieValue(headers);
       if (hadoopAuthCookie == null) {
         LOG.failedSPNegoAuthn(uri.toString());
+        auditor.audit( Action.AUTHENTICATION, uri.toString(), ResourceType.URI, ActionOutcome.FAILURE );
         throw new IOException(
             "SPNego authn failed, can not get hadoop.auth cookie");
       }
@@ -128,6 +136,7 @@ public class AppCookieManager {
 
     }
     LOG.successfulSPNegoAuthn(uri.toString());
+    auditor.audit( Action.AUTHENTICATION, uri.toString(), ResourceType.URI, ActionOutcome.SUCCESS);
     hadoopAuthCookie = HADOOP_AUTH_EQ + quote(hadoopAuthCookie);
     setAppCookie(hadoopAuthCookie);
     return appCookie;

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/f69acbbc/gateway-server/src/main/java/org/apache/hadoop/gateway/dispatch/HdfsDispatch.java
----------------------------------------------------------------------
diff --git a/gateway-server/src/main/java/org/apache/hadoop/gateway/dispatch/HdfsDispatch.java b/gateway-server/src/main/java/org/apache/hadoop/gateway/dispatch/HdfsDispatch.java
index e8116ea..4deeffb 100644
--- a/gateway-server/src/main/java/org/apache/hadoop/gateway/dispatch/HdfsDispatch.java
+++ b/gateway-server/src/main/java/org/apache/hadoop/gateway/dispatch/HdfsDispatch.java
@@ -17,6 +17,12 @@
  */
 package org.apache.hadoop.gateway.dispatch;
 
+import org.apache.hadoop.gateway.audit.api.Action;
+import org.apache.hadoop.gateway.audit.api.ActionOutcome;
+import org.apache.hadoop.gateway.audit.api.AuditServiceFactory;
+import org.apache.hadoop.gateway.audit.api.Auditor;
+import org.apache.hadoop.gateway.audit.api.ResourceType;
+import org.apache.hadoop.gateway.audit.log4j.audit.AuditConstants;
 import org.apache.http.Header;
 import org.apache.http.HttpEntity;
 import org.apache.http.HttpResponse;
@@ -29,12 +35,16 @@ import org.eclipse.jetty.http.HttpStatus;
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
+
 import java.io.IOException;
 import java.net.URI;
 import java.net.URISyntaxException;
 
 public class HdfsDispatch extends HttpClientDispatch {
 
+  private static Auditor auditor = AuditServiceFactory.getAuditService().getAuditor( AuditConstants.DEFAULT_AUDITOR_NAME,
+          AuditConstants.KNOX_SERVICE_NAME, AuditConstants.KNOX_COMPONENT_NAME );
+
   public HdfsDispatch() throws ServletException {
     super();
   }
@@ -44,6 +54,8 @@ public class HdfsDispatch extends HttpClientDispatch {
       throws IOException, URISyntaxException {
     HttpEntity entity = createRequestEntity( request );
     URI requestUri = getDispatchUrl( request );
+    
+    auditor.audit( Action.DISPATCH, request.getRequestURI(), ResourceType.URI, ActionOutcome.UNAVAILABLE );
     if( "CREATE".equals( request.getParameter( "op" ) ) ) {
       HttpPut clientRequest = new HttpPut( requestUri );
       HttpClient client = new DefaultHttpClient();

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/f69acbbc/gateway-server/src/main/java/org/apache/hadoop/gateway/dispatch/HttpClientDispatch.java
----------------------------------------------------------------------
diff --git a/gateway-server/src/main/java/org/apache/hadoop/gateway/dispatch/HttpClientDispatch.java b/gateway-server/src/main/java/org/apache/hadoop/gateway/dispatch/HttpClientDispatch.java
index 546c1d0..0e0ffe8 100644
--- a/gateway-server/src/main/java/org/apache/hadoop/gateway/dispatch/HttpClientDispatch.java
+++ b/gateway-server/src/main/java/org/apache/hadoop/gateway/dispatch/HttpClientDispatch.java
@@ -29,6 +29,12 @@ import javax.servlet.http.HttpServletResponse;
 
 import org.apache.hadoop.gateway.GatewayMessages;
 import org.apache.hadoop.gateway.GatewayResources;
+import org.apache.hadoop.gateway.audit.api.Action;
+import org.apache.hadoop.gateway.audit.api.ActionOutcome;
+import org.apache.hadoop.gateway.audit.api.AuditServiceFactory;
+import org.apache.hadoop.gateway.audit.api.Auditor;
+import org.apache.hadoop.gateway.audit.api.ResourceType;
+import org.apache.hadoop.gateway.audit.log4j.audit.AuditConstants;
 import org.apache.hadoop.gateway.config.GatewayConfig;
 import org.apache.hadoop.gateway.i18n.messages.MessagesFactory;
 import org.apache.hadoop.gateway.i18n.resources.ResourcesFactory;
@@ -66,6 +72,8 @@ public class HttpClientDispatch extends AbstractGatewayDispatch {
 
   private static GatewayMessages LOG = MessagesFactory.get( GatewayMessages.class );
   private static GatewayResources RES = ResourcesFactory.get( GatewayResources.class );
+  private static Auditor auditor = AuditServiceFactory.getAuditService().getAuditor( AuditConstants.DEFAULT_AUDITOR_NAME,
+          AuditConstants.KNOX_SERVICE_NAME, AuditConstants.KNOX_COMPONENT_NAME );
   private static final int DEFAULT_REPLAY_BUFFER_SIZE =  4 * 1024; // 4K
 
   private AppCookieManager appCookieManager = new AppCookieManager();
@@ -108,6 +116,7 @@ public class HttpClientDispatch extends AbstractGatewayDispatch {
     } catch (IOException e) {
       // we do not want to expose back end host. port end points to clients, see JIRA KNOX-58
       LOG.dispatchServiceConnectionException( outboundRequest.getURI(), e );
+      auditor.audit( Action.DISPATCH, outboundRequest.getURI().toString(), ResourceType.URI, ActionOutcome.FAILURE );
       throw new IOException( RES.dispatchConnectionError() );
     } finally {
       if (inboundResponse != null) {
@@ -122,7 +131,11 @@ public class HttpClientDispatch extends AbstractGatewayDispatch {
             LOG.dispatchResponseCreatedStatusCode( statusCode, location.getValue() );
           }
         }
+        auditor.audit( Action.DISPATCH, outboundRequest.getURI().toString(), ResourceType.URI, ActionOutcome.SUCCESS, RES.responseStatus( statusCode ) );
+      } else {
+        auditor.audit( Action.DISPATCH, outboundRequest.getURI().toString(), ResourceType.URI, ActionOutcome.UNAVAILABLE );
       }
+      
     }
 
     // Copy the client respond header to the server respond.

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/f69acbbc/gateway-server/src/main/java/org/apache/hadoop/gateway/dispatch/UrlConnectionDispatch.java
----------------------------------------------------------------------
diff --git a/gateway-server/src/main/java/org/apache/hadoop/gateway/dispatch/UrlConnectionDispatch.java b/gateway-server/src/main/java/org/apache/hadoop/gateway/dispatch/UrlConnectionDispatch.java
index 8e2145b..5b02221 100644
--- a/gateway-server/src/main/java/org/apache/hadoop/gateway/dispatch/UrlConnectionDispatch.java
+++ b/gateway-server/src/main/java/org/apache/hadoop/gateway/dispatch/UrlConnectionDispatch.java
@@ -19,7 +19,15 @@ package org.apache.hadoop.gateway.dispatch;
 
 import org.apache.commons.io.IOUtils;
 import org.apache.hadoop.gateway.GatewayMessages;
+import org.apache.hadoop.gateway.GatewayResources;
+import org.apache.hadoop.gateway.audit.api.Action;
+import org.apache.hadoop.gateway.audit.api.ActionOutcome;
+import org.apache.hadoop.gateway.audit.api.AuditServiceFactory;
+import org.apache.hadoop.gateway.audit.api.Auditor;
+import org.apache.hadoop.gateway.audit.api.ResourceType;
+import org.apache.hadoop.gateway.audit.log4j.audit.AuditConstants;
 import org.apache.hadoop.gateway.i18n.messages.MessagesFactory;
+import org.apache.hadoop.gateway.i18n.resources.ResourcesFactory;
 import org.apache.hadoop.gateway.util.urltemplate.Parser;
 import org.apache.hadoop.gateway.util.urltemplate.Resolver;
 import org.apache.hadoop.gateway.util.urltemplate.Rewriter;
@@ -30,6 +38,7 @@ import org.apache.hadoop.security.authentication.client.KerberosAuthenticator;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
+
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
@@ -47,6 +56,9 @@ import java.util.Enumeration;
 public class UrlConnectionDispatch extends AbstractGatewayDispatch {
 
   private static final GatewayMessages LOG = MessagesFactory.get( GatewayMessages.class );
+  private static final GatewayResources RES = ResourcesFactory.get( GatewayResources.class );
+  private static Auditor auditor = AuditServiceFactory.getAuditService().getAuditor( AuditConstants.DEFAULT_AUDITOR_NAME,
+          AuditConstants.KNOX_SERVICE_NAME, AuditConstants.KNOX_COMPONENT_NAME );
 
   @Override
   public void doGet( URI url, HttpServletRequest request, HttpServletResponse response ) throws IOException, URISyntaxException {
@@ -95,6 +107,7 @@ public class UrlConnectionDispatch extends AbstractGatewayDispatch {
       //System.out.println( "Resolved query: " + clientUrl );
       AuthenticatedURL.Token token = new AuthenticatedURL.Token();
       KerberosAuthenticator authenticator = new KerberosAuthenticator();
+      auditor.audit( Action.DISPATCH, urlStr, ResourceType.URI, ActionOutcome.UNAVAILABLE );
       HttpURLConnection conn = new AuthenticatedURL( authenticator ).openConnection( clientUrl, token );
       //System.out.println( "STATUS=" + conn.getResponseCode() );
       InputStream input = conn.getInputStream();
@@ -107,12 +120,15 @@ public class UrlConnectionDispatch extends AbstractGatewayDispatch {
           input.close();
         }
       }
+      auditor.audit( Action.DISPATCH, urlStr, ResourceType.URI, ActionOutcome.SUCCESS );
     } catch( AuthenticationException e ) {
       response.sendError( HttpServletResponse.SC_UNAUTHORIZED );
       LOG.failedToEstablishConnectionToUrl( urlStr, e );
+      auditor.audit( Action.DISPATCH, urlStr, ResourceType.URI, ActionOutcome.FAILURE, RES.responseStatus( HttpServletResponse.SC_UNAUTHORIZED ) );
     } catch( FileNotFoundException e ) {
       response.sendError( HttpServletResponse.SC_NOT_FOUND );
       LOG.failedToEstablishConnectionToUrl( urlStr, e );
+      auditor.audit( Action.DISPATCH, urlStr, ResourceType.URI, ActionOutcome.FAILURE, RES.responseStatus( HttpServletResponse.SC_NOT_FOUND ) );
     }
 
   }

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/f69acbbc/gateway-server/src/test/java/org/apache/hadoop/gateway/AuditLoggingTest.java
----------------------------------------------------------------------
diff --git a/gateway-server/src/test/java/org/apache/hadoop/gateway/AuditLoggingTest.java b/gateway-server/src/test/java/org/apache/hadoop/gateway/AuditLoggingTest.java
new file mode 100644
index 0000000..82a2da0
--- /dev/null
+++ b/gateway-server/src/test/java/org/apache/hadoop/gateway/AuditLoggingTest.java
@@ -0,0 +1,226 @@
+/**
+ * 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.
+ */
+package org.apache.hadoop.gateway;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.IsNull.notNullValue;
+import static org.hamcrest.core.IsNull.nullValue;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.hadoop.gateway.audit.api.Action;
+import org.apache.hadoop.gateway.audit.api.ActionOutcome;
+import org.apache.hadoop.gateway.audit.api.AuditContext;
+import org.apache.hadoop.gateway.audit.api.CorrelationContext;
+import org.apache.hadoop.gateway.audit.api.ResourceType;
+import org.apache.hadoop.gateway.audit.log4j.audit.AuditConstants;
+import org.apache.hadoop.gateway.audit.log4j.audit.Log4jAuditService;
+import org.apache.hadoop.gateway.audit.log4j.correlation.Log4jCorrelationService;
+import org.apache.hadoop.gateway.dispatch.HttpClientDispatch;
+import org.apache.hadoop.gateway.i18n.resources.ResourcesFactory;
+import org.apache.hadoop.test.log.CollectAppender;
+import org.apache.log4j.spi.LoggingEvent;
+import org.easymock.EasyMock;
+import org.junit.Before;
+import org.junit.Test;
+
+public class AuditLoggingTest {
+  private static final String PATH = "path";
+  private static final String CONTEXT_PATH = "contextPath/";
+  private static final String ADDRESS = "address";
+  private static final String HOST = "host";
+
+  private static final GatewayResources RES = ResourcesFactory.get( GatewayResources.class );
+
+  @Before
+  public void loggingSetup() {
+    CollectAppender.queue.clear();
+  }
+
+  @Test
+  /**
+   * Empty filter chain. Two events with same correlation ID are expected:
+   * 
+   * action=access request_type=uri outcome=unavailable
+   * action=access request_type=uri outcome=success message=Response status: 404
+   */
+  public void testNoFiltersAudit() throws ServletException, IOException {
+    FilterConfig config = EasyMock.createNiceMock( FilterConfig.class );
+    EasyMock.replay( config );
+
+    HttpServletRequest request = EasyMock.createNiceMock( HttpServletRequest.class );
+    EasyMock.expect( request.getPathInfo() ).andReturn( PATH ).anyTimes();
+    EasyMock.expect( request.getContextPath() ).andReturn( CONTEXT_PATH ).anyTimes();
+    EasyMock.expect( request.getRemoteAddr() ).andReturn( ADDRESS ).anyTimes();
+    EasyMock.expect( request.getRemoteHost() ).andReturn( HOST ).anyTimes();
+
+    EasyMock.replay( request );
+
+    HttpServletResponse response = EasyMock.createNiceMock( HttpServletResponse.class );
+    EasyMock.replay( response );
+
+    FilterChain chain = EasyMock.createNiceMock( FilterChain.class );
+    EasyMock.replay( chain );
+
+    GatewayFilter gateway = new GatewayFilter();
+    gateway.init( config );
+    gateway.doFilter( request, response, chain );
+    gateway.destroy();
+
+    assertThat( CollectAppender.queue.size(), is( 2 ) );
+    Iterator<LoggingEvent> iterator = CollectAppender.queue.iterator();
+    LoggingEvent accessEvent = iterator.next();
+    verifyAuditEvent( accessEvent, CONTEXT_PATH + PATH, ResourceType.URI, Action.ACCESS, ActionOutcome.UNAVAILABLE, null, null );
+
+    LoggingEvent responseEvent = iterator.next();
+    verifyAuditEvent( responseEvent, CONTEXT_PATH + PATH, ResourceType.URI, Action.ACCESS, ActionOutcome.SUCCESS, null,
+        RES.responseStatus( HttpServletResponse.SC_NOT_FOUND ) );
+    assertThat( getRequestId( accessEvent ), is( getRequestId( responseEvent ) ) );
+  }
+
+  @Test
+  /**
+   * One NoOp filter in chain. Single audit event with same with specified request URI is expected:
+   * 
+   * action=access request_type=uri outcome=unavailable
+   */
+  public void testNoopFilter() throws ServletException, IOException,
+      URISyntaxException {
+
+    FilterConfig config = EasyMock.createNiceMock( FilterConfig.class );
+    EasyMock.replay( config );
+
+    HttpServletRequest request = EasyMock.createNiceMock( HttpServletRequest.class );
+    EasyMock.expect( request.getPathInfo() ).andReturn( PATH ).anyTimes();
+    EasyMock.expect( request.getContextPath() ).andReturn( CONTEXT_PATH ).anyTimes();
+    EasyMock.expect( request.getRemoteAddr() ).andReturn( ADDRESS ).anyTimes();
+    EasyMock.expect( request.getRemoteHost() ).andReturn( HOST ).anyTimes();
+    EasyMock.replay( request );
+
+    HttpServletResponse response = EasyMock.createNiceMock( HttpServletResponse.class );
+    EasyMock.replay( response );
+
+    FilterChain chain = EasyMock.createNiceMock( FilterChain.class );
+    EasyMock.replay( chain );
+
+    Filter filter = EasyMock.createNiceMock( Filter.class );
+    EasyMock.replay( filter );
+
+    GatewayFilter gateway = new GatewayFilter();
+    gateway.addFilter( "path", "filter", filter, null, null );
+    gateway.init( config );
+    gateway.doFilter( request, response, chain );
+    gateway.destroy();
+
+    assertThat( CollectAppender.queue.size(), is( 1 ) );
+    Iterator<LoggingEvent> iterator = CollectAppender.queue.iterator();
+    LoggingEvent accessEvent = iterator.next();
+    verifyAuditEvent( accessEvent, CONTEXT_PATH + PATH, ResourceType.URI,
+        Action.ACCESS, ActionOutcome.UNAVAILABLE, null, null );
+
+  }
+
+  @Test
+  /**
+   * Dispatching outbound request. Remote host is unreachable. Two log events is expected:
+   * 
+   * action=dispatch request_type=uri outcome=FAILED
+   * action=dispatch request_type=uri outcome=unavailable
+   */
+  public void testHttpClientOutboundException() throws IOException,
+      URISyntaxException {
+    String uri = "http://outbound-host:port/path";
+
+    HttpServletRequest inboundRequest = EasyMock.createNiceMock( HttpServletRequest.class );
+    EasyMock.expect( inboundRequest.getHeaderNames() ).andReturn( Collections.enumeration( new ArrayList<String>() ) ).anyTimes();
+    EasyMock.replay( inboundRequest );
+
+    HttpServletResponse outboundResponse = EasyMock.createNiceMock( HttpServletResponse.class );
+    EasyMock.replay( outboundResponse );
+
+    HttpClientDispatch dispatch = new HttpClientDispatch();
+    try {
+      dispatch.doGet( new URI( uri ), inboundRequest, outboundResponse );
+      fail( "Expected exception while accessing to unreachable host" );
+    } catch ( IOException e ) {
+      Iterator<LoggingEvent> iterator = CollectAppender.queue.iterator();
+      LoggingEvent failureEvent = iterator.next();
+      verifyValue( (String) failureEvent.getMDC( AuditConstants.MDC_RESOURCE_NAME_KEY ), uri );
+      verifyValue( (String) failureEvent.getMDC( AuditConstants.MDC_RESOURCE_TYPE_KEY ), ResourceType.URI );
+      verifyValue( (String) failureEvent.getMDC( AuditConstants.MDC_ACTION_KEY ), Action.DISPATCH );
+      verifyValue( (String) failureEvent.getMDC( AuditConstants.MDC_OUTCOME_KEY ), ActionOutcome.FAILURE );
+
+      LoggingEvent unavailableEvent = iterator.next();
+      verifyValue( (String) unavailableEvent.getMDC( AuditConstants.MDC_RESOURCE_NAME_KEY ), uri );
+      verifyValue( (String) unavailableEvent.getMDC( AuditConstants.MDC_RESOURCE_TYPE_KEY ), ResourceType.URI );
+      verifyValue( (String) unavailableEvent.getMDC( AuditConstants.MDC_ACTION_KEY ), Action.DISPATCH );
+      verifyValue( (String) unavailableEvent.getMDC( AuditConstants.MDC_OUTCOME_KEY ), ActionOutcome.UNAVAILABLE );
+    }
+  }
+
+  private void verifyAuditEvent( LoggingEvent event, String resourceName,
+      String resourceType, String action, String outcome, String targetService,
+      String message ) {
+    event.getMDCCopy();
+    CorrelationContext cc = (CorrelationContext) event.getMDC( Log4jCorrelationService.MDC_CORRELATION_CONTEXT_KEY );
+    assertThat( cc, notNullValue() );
+    assertThat( cc.getRequestId(), is( notNullValue() ) );
+    AuditContext ac = (AuditContext) event.getMDC( Log4jAuditService.MDC_AUDIT_CONTEXT_KEY );
+    assertThat( ac, notNullValue() );
+    assertThat( ac.getRemoteIp(), is( ADDRESS ) );
+    assertThat( ac.getRemoteHostname(), is( HOST ) );
+    assertThat( (String) event.getMDC( AuditConstants.MDC_SERVICE_KEY ), is( AuditConstants.KNOX_SERVICE_NAME ) );
+    assertThat( (String) event.getMDC( AuditConstants.MDC_COMPONENT_KEY ), is( AuditConstants.KNOX_COMPONENT_NAME ) );
+    assertThat( (String) event.getLoggerName(), is( AuditConstants.DEFAULT_AUDITOR_NAME ) );
+    verifyValue( (String) event.getMDC( AuditConstants.MDC_RESOURCE_NAME_KEY ), resourceName );
+    verifyValue( (String) event.getMDC( AuditConstants.MDC_RESOURCE_TYPE_KEY ), resourceType );
+    verifyValue( (String) event.getMDC( AuditConstants.MDC_ACTION_KEY ), action );
+    verifyValue( (String) event.getMDC( AuditConstants.MDC_OUTCOME_KEY ), outcome );
+    verifyValue( ac.getTargetServiceName(), targetService );
+    verifyValue( event.getRenderedMessage(), message );
+  }
+
+  private void verifyValue( String actual, String expected ) {
+    if( expected == null ) {
+      assertThat( actual, nullValue() );
+    } else {
+      assertThat( actual, is( expected ) );
+    }
+  }
+
+  private String getRequestId( LoggingEvent event ) {
+    CorrelationContext cc = (CorrelationContext) event
+        .getMDC( Log4jCorrelationService.MDC_CORRELATION_CONTEXT_KEY );
+    return cc == null ? null : cc.getRequestId();
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/f69acbbc/gateway-server/src/test/java/org/apache/hadoop/gateway/GatewayFilterTest.java
----------------------------------------------------------------------
diff --git a/gateway-server/src/test/java/org/apache/hadoop/gateway/GatewayFilterTest.java b/gateway-server/src/test/java/org/apache/hadoop/gateway/GatewayFilterTest.java
index bd9ca31..0ce0886 100644
--- a/gateway-server/src/test/java/org/apache/hadoop/gateway/GatewayFilterTest.java
+++ b/gateway-server/src/test/java/org/apache/hadoop/gateway/GatewayFilterTest.java
@@ -77,7 +77,7 @@ public class GatewayFilterTest {
     EasyMock.replay( filter );
 
     GatewayFilter gateway = new GatewayFilter();
-    gateway.addFilter( "path", "filter", filter, null );
+    gateway.addFilter( "path", "filter", filter, null, null );
     gateway.init( config );
     gateway.doFilter( request, response, chain );
     gateway.destroy();

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/f69acbbc/gateway-server/src/test/resources/log4j.properties
----------------------------------------------------------------------
diff --git a/gateway-server/src/test/resources/log4j.properties b/gateway-server/src/test/resources/log4j.properties
index c64be2f..7474633 100644
--- a/gateway-server/src/test/resources/log4j.properties
+++ b/gateway-server/src/test/resources/log4j.properties
@@ -23,6 +23,9 @@ log4j.appender.stdout=org.apache.log4j.ConsoleAppender
 log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
 log4j.appender.stdout.layout.ConversionPattern=%5p [%c] %m%n
 
+log4j.logger.audit = INFO, collectappender
+log4j.appender.collectappender = org.apache.hadoop.test.log.CollectAppender
+
 #log4j.logger.org.apache.hadoop.gateway=DEBUG
 #log4j.logger.org.eclipse.jetty=DEBUG
 #log4j.logger.org.apache.shiro=DEBUG

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/f69acbbc/gateway-spi/src/main/java/org/apache/hadoop/gateway/filter/AbstractGatewayFilter.java
----------------------------------------------------------------------
diff --git a/gateway-spi/src/main/java/org/apache/hadoop/gateway/filter/AbstractGatewayFilter.java b/gateway-spi/src/main/java/org/apache/hadoop/gateway/filter/AbstractGatewayFilter.java
index 83dfb6f..6ce985d 100644
--- a/gateway-spi/src/main/java/org/apache/hadoop/gateway/filter/AbstractGatewayFilter.java
+++ b/gateway-spi/src/main/java/org/apache/hadoop/gateway/filter/AbstractGatewayFilter.java
@@ -38,6 +38,7 @@ public abstract class AbstractGatewayFilter implements Filter {
 
   public static final String SOURCE_REQUEST_URL_ATTRIBUTE_NAME = "sourceRequestUrl";
   public static final String TARGET_REQUEST_URL_ATTRIBUTE_NAME = "targetRequestUrl";
+  public static final String SOURCE_REQUEST_CONTEXT_URL_ATTRIBUTE_NAME = "sourceRequestContextUrl";
 //  public static final String RESPONSE_STREAMER_ATTRIBUTE_NAME = "responseStreamer";
   private static final GatewaySpiMessages LOG = MessagesFactory.get( GatewaySpiMessages.class );
 

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/f69acbbc/gateway-spi/src/main/java/org/apache/hadoop/gateway/filter/security/AbstractIdentityAssertionFilter.java
----------------------------------------------------------------------
diff --git a/gateway-spi/src/main/java/org/apache/hadoop/gateway/filter/security/AbstractIdentityAssertionFilter.java b/gateway-spi/src/main/java/org/apache/hadoop/gateway/filter/security/AbstractIdentityAssertionFilter.java
index fdd1e06..6d54246 100644
--- a/gateway-spi/src/main/java/org/apache/hadoop/gateway/filter/security/AbstractIdentityAssertionFilter.java
+++ b/gateway-spi/src/main/java/org/apache/hadoop/gateway/filter/security/AbstractIdentityAssertionFilter.java
@@ -17,8 +17,17 @@
  */
 package org.apache.hadoop.gateway.filter.security;
 
+import org.apache.hadoop.gateway.audit.api.Action;
+import org.apache.hadoop.gateway.audit.api.ActionOutcome;
+import org.apache.hadoop.gateway.audit.api.AuditService;
+import org.apache.hadoop.gateway.audit.api.AuditServiceFactory;
+import org.apache.hadoop.gateway.audit.api.Auditor;
+import org.apache.hadoop.gateway.audit.api.ResourceType;
+import org.apache.hadoop.gateway.audit.log4j.audit.AuditConstants;
 import org.apache.hadoop.gateway.i18n.GatewaySpiMessages;
+import org.apache.hadoop.gateway.i18n.GatewaySpiResources;
 import org.apache.hadoop.gateway.i18n.messages.MessagesFactory;
+import org.apache.hadoop.gateway.i18n.resources.ResourcesFactory;
 import org.apache.hadoop.gateway.security.GroupPrincipal;
 import org.apache.hadoop.gateway.security.ImpersonatedPrincipal;
 import org.apache.hadoop.gateway.security.PrimaryPrincipal;
@@ -42,11 +51,17 @@ import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.ServletException;
 
+import java.util.Arrays;
 import java.util.Set;
 
 public abstract class AbstractIdentityAssertionFilter extends AbstractIdentityAssertionBase implements Filter {
 
   private static final GatewaySpiMessages LOG = MessagesFactory.get( GatewaySpiMessages.class );
+  private static final GatewaySpiResources RES = ResourcesFactory.get( GatewaySpiResources.class );
+  private static AuditService auditService = AuditServiceFactory.getAuditService();
+  private static Auditor auditor = auditService.getAuditor(
+      AuditConstants.DEFAULT_AUDITOR_NAME, AuditConstants.KNOX_SERVICE_NAME,
+      AuditConstants.KNOX_COMPONENT_NAME );
   protected PrincipalMapper mapper = new SimplePrincipalMapper();
 
   public AbstractIdentityAssertionFilter() {
@@ -103,6 +118,8 @@ public abstract class AbstractIdentityAssertionFilter extends AbstractIdentityAs
     if (primaryPrincipal != null) {
       if (!primaryPrincipal.getName().equals(mappedPrincipalName)) {
         impersonationNeeded = true;
+        auditService.getContext().setProxyUsername( mappedPrincipalName );
+        auditor.audit( Action.IDENTITY_MAPPING, primaryPrincipal.getName(), ResourceType.PRINCIPAL, ActionOutcome.SUCCESS );
       }
     }
     else {
@@ -172,6 +189,7 @@ public abstract class AbstractIdentityAssertionFilter extends AbstractIdentityAs
   private void addMappedGroupsToSubject(String mappedPrincipalName, Subject subject) {
     String[] groups = mapper.mapGroupPrincipal(mappedPrincipalName);
     if (groups != null) {
+      auditor.audit( Action.IDENTITY_MAPPING, mappedPrincipalName, ResourceType.PRINCIPAL, ActionOutcome.SUCCESS, RES.groupsList( Arrays.toString( groups ) ) );
       for (int i = 0; i < groups.length; i++) {
         subject.getPrincipals().add(new GroupPrincipal(groups[i]));
       }

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/f69acbbc/gateway-spi/src/main/java/org/apache/hadoop/gateway/i18n/GatewaySpiResources.java
----------------------------------------------------------------------
diff --git a/gateway-spi/src/main/java/org/apache/hadoop/gateway/i18n/GatewaySpiResources.java b/gateway-spi/src/main/java/org/apache/hadoop/gateway/i18n/GatewaySpiResources.java
new file mode 100644
index 0000000..b48cfb5
--- /dev/null
+++ b/gateway-spi/src/main/java/org/apache/hadoop/gateway/i18n/GatewaySpiResources.java
@@ -0,0 +1,28 @@
+/**
+ * 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.
+ */
+package org.apache.hadoop.gateway.i18n;
+
+import org.apache.hadoop.gateway.i18n.resources.Resource;
+import org.apache.hadoop.gateway.i18n.resources.Resources;
+
+@Resources
+public interface GatewaySpiResources {
+  @Resource(text = "Groups: {0}")
+  String groupsList( String groups );
+  
+}

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/f69acbbc/gateway-test-utils/src/main/java/org/apache/hadoop/test/log/CollectAppender.java
----------------------------------------------------------------------
diff --git a/gateway-test-utils/src/main/java/org/apache/hadoop/test/log/CollectAppender.java b/gateway-test-utils/src/main/java/org/apache/hadoop/test/log/CollectAppender.java
new file mode 100644
index 0000000..d14ab7b
--- /dev/null
+++ b/gateway-test-utils/src/main/java/org/apache/hadoop/test/log/CollectAppender.java
@@ -0,0 +1,51 @@
+/**
+ * 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.
+ */
+package org.apache.hadoop.test.log;
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+import org.apache.log4j.AppenderSkeleton;
+import org.apache.log4j.spi.LoggingEvent;
+
+public class CollectAppender extends AppenderSkeleton {
+
+  public CollectAppender() {
+    super();
+  }
+
+  public static BlockingQueue<LoggingEvent> queue = new LinkedBlockingQueue<LoggingEvent>();
+  public static boolean closed = false;
+
+  @Override
+  protected void append( LoggingEvent event ) {
+    event.getProperties();
+    queue.add( event );
+  }
+
+  @Override
+  public void close() {
+    closed = true;
+  }
+
+  @Override
+  public boolean requiresLayout() {
+    return false;
+  }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/f69acbbc/gateway-util-common/pom.xml
----------------------------------------------------------------------
diff --git a/gateway-util-common/pom.xml b/gateway-util-common/pom.xml
index f8c9e54..e8743b4 100644
--- a/gateway-util-common/pom.xml
+++ b/gateway-util-common/pom.xml
@@ -66,6 +66,10 @@
             <groupId>${gateway-group}</groupId>
             <artifactId>gateway-i18n</artifactId>
         </dependency>
+		<dependency>
+            <groupId>org.apache.directory.server</groupId>
+            <artifactId>apacheds-jdbm</artifactId>
+        </dependency>
         <dependency>
             <groupId>org.hamcrest</groupId>
             <artifactId>hamcrest-core</artifactId>
@@ -89,6 +93,14 @@
             <artifactId>easymock</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+            <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-log4j12</artifactId>
+        </dependency>
 
     </dependencies>
 

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/f69acbbc/gateway-util-common/src/main/java/org/apache/hadoop/gateway/audit/api/Action.java
----------------------------------------------------------------------
diff --git a/gateway-util-common/src/main/java/org/apache/hadoop/gateway/audit/api/Action.java b/gateway-util-common/src/main/java/org/apache/hadoop/gateway/audit/api/Action.java
new file mode 100644
index 0000000..b60b367
--- /dev/null
+++ b/gateway-util-common/src/main/java/org/apache/hadoop/gateway/audit/api/Action.java
@@ -0,0 +1,33 @@
+/**
+ * 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.
+ */
+package org.apache.hadoop.gateway.audit.api;
+
+public abstract class Action {
+  private Action() {
+  }
+
+  public static final String AUTHENTICATION = "authentication";
+  public static final String AUTHORIZATION = "authorization";
+  public static final String REDEPLOY = "redeploy";
+  public static final String DEPLOY = "deploy";
+  public static final String UNDEPLOY = "undeploy";
+  public static final String IDENTITY_MAPPING = "identity-mapping";
+  public static final String DISPATCH = "dispatch";
+  public static final String ACCESS = "access";
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/f69acbbc/gateway-util-common/src/main/java/org/apache/hadoop/gateway/audit/api/ActionOutcome.java
----------------------------------------------------------------------
diff --git a/gateway-util-common/src/main/java/org/apache/hadoop/gateway/audit/api/ActionOutcome.java b/gateway-util-common/src/main/java/org/apache/hadoop/gateway/audit/api/ActionOutcome.java
new file mode 100644
index 0000000..0c864ba
--- /dev/null
+++ b/gateway-util-common/src/main/java/org/apache/hadoop/gateway/audit/api/ActionOutcome.java
@@ -0,0 +1,36 @@
+/**
+ * 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.
+ */
+package org.apache.hadoop.gateway.audit.api;
+
+/**
+ * Contains a list of possible action outcomes. It may be used to keep audit
+ * records consistent across services and components. For example, to avoid the
+ * following: "Success", "success", "SUCCESS", "Succeed" Action outcomes doesn't
+ * restricted to this list and any constants from component's source code may be
+ * used.
+ * 
+ */
+public abstract class ActionOutcome {
+  private ActionOutcome() {
+  }
+
+  public static final String SUCCESS = "success";
+  public static final String FAILURE = "failure";
+  public static final String UNAVAILABLE = "unavailable";
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/f69acbbc/gateway-util-common/src/main/java/org/apache/hadoop/gateway/audit/api/AuditContext.java
----------------------------------------------------------------------
diff --git a/gateway-util-common/src/main/java/org/apache/hadoop/gateway/audit/api/AuditContext.java b/gateway-util-common/src/main/java/org/apache/hadoop/gateway/audit/api/AuditContext.java
new file mode 100644
index 0000000..be2414b
--- /dev/null
+++ b/gateway-util-common/src/main/java/org/apache/hadoop/gateway/audit/api/AuditContext.java
@@ -0,0 +1,51 @@
+/**
+ * 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.
+ */
+package org.apache.hadoop.gateway.audit.api;
+
+/**
+ * Holds the context of the current request for auditing purposes.
+ */
+public interface AuditContext {
+
+  String getUsername();
+
+  void setUsername( String username );
+
+  String getProxyUsername();
+
+  void setProxyUsername( String proxyUsername );
+
+  String getSystemUsername();
+
+  void setSystemUsername( String systemUsername );
+   
+  String getTargetServiceName();
+  
+  void setTargetServiceName( String targetServiceName );
+
+  String getRemoteIp();
+
+  void setRemoteIp( String remoteIp );
+
+  String getRemoteHostname();
+
+  void setRemoteHostname( String remoteHostname );
+
+  void destroy();
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/f69acbbc/gateway-util-common/src/main/java/org/apache/hadoop/gateway/audit/api/AuditService.java
----------------------------------------------------------------------
diff --git a/gateway-util-common/src/main/java/org/apache/hadoop/gateway/audit/api/AuditService.java b/gateway-util-common/src/main/java/org/apache/hadoop/gateway/audit/api/AuditService.java
new file mode 100644
index 0000000..efc77a1
--- /dev/null
+++ b/gateway-util-common/src/main/java/org/apache/hadoop/gateway/audit/api/AuditService.java
@@ -0,0 +1,77 @@
+/**
+ * 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.
+ */
+package org.apache.hadoop.gateway.audit.api;
+
+import java.util.concurrent.Callable;
+
+/**
+ * Manipulates the audit context associated with the current thread.
+ */
+public interface AuditService {
+
+  /**
+   * Creates and attaches an "empty" audit context.
+   *
+   * @return A new, empty, attached audit context.  Will never be null.
+   */
+  AuditContext createContext();
+
+  /**
+   * Retrieves the current attached audit context, if any.
+   *
+   * @return The current attached audit context if any.  May be null.
+   */
+  AuditContext getContext();
+
+  /**
+   * Attaches the provided audit context to the current thread.
+   * Providing a null value will have no effect and the currently attached context if any will remain.
+   *
+   * @param context The audit context to attach to the current thread.  May be null.
+   */
+  void attachContext( AuditContext context );
+
+  /**
+   * Detaches the current audit context, if any, from the current thread.
+   * This will typically be used when an audit context needs to be propagated between threads.
+   *
+   * @return The now detached audit context, if any.  May be null.
+   */
+  AuditContext detachContext();
+
+  /**
+   * Retrieves an auditor configured with the the provided component and service names.  Will never be null.
+   *
+   * @param auditorName The name of auditor. Can be used to separate audit events to different destinations. For example security audit, operations audit, etc
+   * @param componentName The name of component that will be placed used in every audit event generated by {@link org.apache.hadoop.gateway.audit.api.Auditor Auditor} instance
+   * @param serviceName The name of service that will be placed used in every audit event generated by {@link org.apache.hadoop.gateway.audit.api.Auditor Auditor} instance
+   */
+  Auditor getAuditor( String auditorName, String componentName, String serviceName );
+
+  /**
+   * Executes the callable within the provided audit context.
+   * The provided context is attached and detached around the invocation of the callable.
+   *
+   * @param context The context to establish around the invocation of the callable.  May not be null.
+   * @param callable The callable to invoke after establishing the correlation context.  May not be null.
+   * @return The result of the callable's call method.
+   * @throws Exception Thrown if thrown by the callable's call method.
+   */
+  <T> T execute( AuditContext context, Callable<T> callable ) throws Exception;
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/f69acbbc/gateway-util-common/src/main/java/org/apache/hadoop/gateway/audit/api/AuditServiceFactory.java
----------------------------------------------------------------------
diff --git a/gateway-util-common/src/main/java/org/apache/hadoop/gateway/audit/api/AuditServiceFactory.java b/gateway-util-common/src/main/java/org/apache/hadoop/gateway/audit/api/AuditServiceFactory.java
new file mode 100644
index 0000000..ed0222b
--- /dev/null
+++ b/gateway-util-common/src/main/java/org/apache/hadoop/gateway/audit/api/AuditServiceFactory.java
@@ -0,0 +1,43 @@
+/**
+ * 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.
+ */
+package org.apache.hadoop.gateway.audit.api;
+
+import org.apache.hadoop.gateway.audit.log4j.audit.Log4jAuditService;
+
+public abstract class AuditServiceFactory {
+
+  // The global audit service instance.
+  private static AuditService auditService = null;
+
+  // To prevent instantiation.
+  private AuditServiceFactory() {
+  }
+
+  /**
+   * Provides access to the default audit service implementation.
+   * @return The default audit service implementation.  Will not be null.
+   */
+  public static AuditService getAuditService() {
+    // Race condition acceptable and will only result in multiple service instantiations.
+    if( auditService == null ) {
+      auditService = new Log4jAuditService();
+    }
+    return auditService;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/f69acbbc/gateway-util-common/src/main/java/org/apache/hadoop/gateway/audit/api/Auditor.java
----------------------------------------------------------------------
diff --git a/gateway-util-common/src/main/java/org/apache/hadoop/gateway/audit/api/Auditor.java b/gateway-util-common/src/main/java/org/apache/hadoop/gateway/audit/api/Auditor.java
new file mode 100644
index 0000000..80d2dd3
--- /dev/null
+++ b/gateway-util-common/src/main/java/org/apache/hadoop/gateway/audit/api/Auditor.java
@@ -0,0 +1,86 @@
+/**
+ * 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.
+ */
+package org.apache.hadoop.gateway.audit.api;
+
+/**
+ * Used to record audit events.
+ */
+public interface Auditor {
+
+  /**
+   * Records a single audit event.
+   *
+   * @param correlationContext The explicit correlation context to use when recording this audit event.  May not be null.
+   * @param auditContext The explicit audit context to use when recording this audit event.  May not be null.
+   * @param action The action being recorded for this audit event.  May not be null.
+   * @param resourceName The resource identifier to record for this audit event.  May not be null.
+   * @param resourceType The resource type to record for this audit event.  May not be null.
+   * @param outcome The outcome to record for this audit event.  Typically the result of a authorization check.  May not be null.
+   * @param message An arbitrary message to record with the audit event.  May be null.
+   */
+  void audit( CorrelationContext correlationContext, AuditContext auditContext, String action,
+              String resourceName, String resourceType, String outcome, String message );
+
+  /**
+   * Records a single audit event using context information associated with the current thread.
+   *
+   * @param action The action being recorded for this audit event.  May not be null.
+   * @param resourceName The resource identifier to record for this audit event.  May not be null.
+   * @param resourceType The resource type to record for this audit event.  May not be null.
+   * @param outcome The outcome to record for this audit event.  Typically the result of a authorization check.  May not be null.
+   * @param message An arbitrary message to record with the audit event.  May be null.
+   */
+  void audit( String action, String resourceName, String resourceType, String outcome, String message );
+  
+  /**
+   * Records a single audit event using context information associated with the current thread.
+   *
+   * @param action The action being recorded for this audit event.  May not be null.
+   * @param resourceName The resource identifier to record for this audit event.  May not be null.
+   * @param resourceType The resource type to record for this audit event.  May not be null.
+   * @param outcome The outcome to record for this audit event.  Typically the result of a authorization check.  May not be null.
+   */
+  void audit( String action, String resourceName, String resourceType, String outcome );
+
+
+  /**
+   * The service name established when the Auditor was acquired.
+   * Every event logged by auditor instance will contain data about service that generated event.
+   * 
+   * @return The service name established when the Auditor was acquired.
+   */
+  String getServiceName();
+
+  /**
+   * The component name established when the Auditor was acquired.
+   * 
+   * @return The component name established when the Auditor was acquired.
+   */
+  String getComponentName();
+
+  /**
+   * The auditor name established when the Auditor was acquired.
+   * As an example, authentication/authorization operations may be logged to separate security log.
+   * Or actions on some resources shouldn't be logged into central storage.
+   * Auditor name provide an ability to logically group audit events, configure theirs filtration and  persistence
+   * 
+   * @return The auditor name established when the Auditor was acquired.
+   */
+  String getAuditorName();
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/f69acbbc/gateway-util-common/src/main/java/org/apache/hadoop/gateway/audit/api/CorrelationContext.java
----------------------------------------------------------------------
diff --git a/gateway-util-common/src/main/java/org/apache/hadoop/gateway/audit/api/CorrelationContext.java b/gateway-util-common/src/main/java/org/apache/hadoop/gateway/audit/api/CorrelationContext.java
new file mode 100644
index 0000000..29b6706
--- /dev/null
+++ b/gateway-util-common/src/main/java/org/apache/hadoop/gateway/audit/api/CorrelationContext.java
@@ -0,0 +1,74 @@
+/**
+ * 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.
+ */
+package org.apache.hadoop.gateway.audit.api;
+
+public interface CorrelationContext {
+
+  /**
+   * A unique value representing the current, active request.
+   *
+   * @return A unique value representing the current, active request.
+   */
+  String getRequestId();
+
+  /**
+   * A unique value representing the current, active request.
+   * If the current request id value is different from the current parent request id value then
+   * the current request id value is moved to the parent request id before it is replaced by the provided request id.
+   * If the root request id is not set it will be set with the first non-null value of either the parent request id or the passed request id.
+   *
+   * @param requestId A unique value representing the current, active request.
+   */
+  void setRequestId( String requestId );
+
+  /**
+   * The parent request ID if this is a sub-request.
+   *
+   * @return The parent request ID.
+   */
+  String getParentRequestId();
+
+  /**
+   * Sets the parent request ID if this is a sub-request.
+   *
+   * @param parentRequestId The parent request ID.
+   */
+  void setParentRequestId( String parentRequestId );
+
+  /**
+   * The root request ID if this is a sub-request.
+   *
+   * @return The root request ID.
+   */
+  String getRootRequestId();
+
+  /**
+   * Sets the root request ID if this is a sub-request.
+   *
+   * @param rootRequestId The root request ID.
+   */
+  void setRootRequestId( String rootRequestId );
+
+  /**
+   * Would be used to indicate that the context can be cleaned and reused.  
+   * This is only important if the service would like to maintain a pool of available "empty" context 
+   * that can be reused to limit memory allocation and garbage collection.
+   */
+  void destroy();
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/f69acbbc/gateway-util-common/src/main/java/org/apache/hadoop/gateway/audit/api/CorrelationService.java
----------------------------------------------------------------------
diff --git a/gateway-util-common/src/main/java/org/apache/hadoop/gateway/audit/api/CorrelationService.java b/gateway-util-common/src/main/java/org/apache/hadoop/gateway/audit/api/CorrelationService.java
new file mode 100644
index 0000000..896298d
--- /dev/null
+++ b/gateway-util-common/src/main/java/org/apache/hadoop/gateway/audit/api/CorrelationService.java
@@ -0,0 +1,99 @@
+/**
+ * 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.
+ */
+package org.apache.hadoop.gateway.audit.api;
+
+import java.util.concurrent.Callable;
+
+/**
+ * Manipulates the correlations context associated with the current thread.
+ */
+public interface CorrelationService {
+
+  /**
+   * The recommended protocol header name used to transmit the correlation context over the network.
+   */
+  static String PROTOCOL_HEADER = "X-Correlation-Context";
+
+  /**
+   * Creates a new correlation context.  The context is attached and empty.
+   *
+   * @return A new correlation context.
+   */
+  CorrelationContext createContext();
+
+  /**
+   * Returns the current attached correlation context if any.
+   *
+   * @return The current attached correlation context.  May be null.
+   */
+  CorrelationContext getContext();
+
+  /**
+   * Sets the current attached correlation context.
+   * Will overwrite any existing attached context if any.
+   * Null contexts are ignored and any existing attached context will remain.
+   *
+   * @param context The correlation context to attach.
+   */
+  void attachContext( CorrelationContext context );
+
+  /**
+   * Detaches the existing attached context if any.
+   * This will typically be done so that the context can be persisted or attached to a different thread.
+   *
+   * @return The now detached correlation context.  May be null.
+   */
+  CorrelationContext detachContext();
+  
+  /**
+   * Executes the callable within the provided correlation context.
+   * The provided context is attached and detached around the invocation of the callable.
+   * @param context The correlation context to establish around the invocation of the callable.  May not be null.
+   * @param callable The callable to invoke after establishing the correlation context.  May not be null.
+   * @return The result of the callable's call method.
+   * @throws Exception Thrown if thrown by the callable's call method.
+   */
+  <T> T execute( CorrelationContext context, Callable<T> callable ) throws Exception;
+  
+  /**
+   * Attaches the externalized correlation context
+   * @param externalizedContext The externalized correlation context
+   * @return An attached instance of correlation context that was restored form externalized context
+   */
+  CorrelationContext attachExternalizedContext( byte[] externalizedContext );
+  
+  /**
+   * Detaches the existing attached correlation context and returns it in externalized form.
+   * @return The detached externalized context
+   */
+  byte[] detachExternalizedContext();
+  
+  /**
+   * Restores correlation context from externalized form.
+   * @param externalizedContext The externalized correlation context. May not be null.
+   * @return the correlation context that is not attached yet
+   */
+  CorrelationContext readExternalizedContext( byte[] externalizedContext );
+  
+  /**
+   * Returns externalized correlation context without detaching it from execution scope.
+   * @return The externalized correlation context
+   */
+  byte[] getExternalizedContext();
+
+}