From cd5e6c2d2c39db486095c5c006ace3530df6a994 Mon Sep 17 00:00:00 2001
From: "Endi S. Dewata" <edewata@redhat.com>
Date: Tue, 21 Apr 2015 16:29:45 -0400
Subject: [PATCH] Fixed authentication data in audit log.

The REST methods may be executed by different threads even though
they are invoked in the same session. A new interceptor has been
added to all subsystems to make sure the SessionContext is created
properly for each thread. This will fix the authentication data in
the audit log. The SessionContext has also been improved to use
ThreadLocal instead of a global Hashtable.

https://fedorahosted.org/pki/ticket/1054
---
 .../dogtagpki/server/ca/rest/CAApplication.java    |   4 +-
 .../com/netscape/certsrv/base/SessionContext.java  |  42 ++-------
 .../dogtagpki/server/kra/rest/KRAApplication.java  |   4 +-
 .../server/ocsp/rest/OCSPApplication.java          |   4 +-
 .../server/rest/SessionContextInterceptor.java     | 100 +++++++++++++++++++++
 .../dogtagpki/server/tks/rest/TKSApplication.java  |   4 +-
 .../dogtagpki/server/tps/rest/TPSApplication.java  |   4 +-
 7 files changed, 124 insertions(+), 38 deletions(-)
 create mode 100644 base/server/cms/src/org/dogtagpki/server/rest/SessionContextInterceptor.java

diff --git a/base/ca/src/org/dogtagpki/server/ca/rest/CAApplication.java b/base/ca/src/org/dogtagpki/server/ca/rest/CAApplication.java
index d73b794da2f5f9703d2b3c40f815bcb417e6c7b6..39dfb91b25f5face507754a83de38fe6b708b11c 100644
--- a/base/ca/src/org/dogtagpki/server/ca/rest/CAApplication.java
+++ b/base/ca/src/org/dogtagpki/server/ca/rest/CAApplication.java
@@ -8,10 +8,11 @@ import javax.ws.rs.core.Application;
 import org.dogtagpki.server.rest.ACLInterceptor;
 import org.dogtagpki.server.rest.AccountService;
 import org.dogtagpki.server.rest.AuditService;
+import org.dogtagpki.server.rest.SessionContextInterceptor;
 import org.dogtagpki.server.rest.AuthMethodInterceptor;
 import org.dogtagpki.server.rest.GroupService;
-import org.dogtagpki.server.rest.PKIExceptionMapper;
 import org.dogtagpki.server.rest.MessageFormatInterceptor;
+import org.dogtagpki.server.rest.PKIExceptionMapper;
 import org.dogtagpki.server.rest.SecurityDomainService;
 import org.dogtagpki.server.rest.SelfTestService;
 import org.dogtagpki.server.rest.SystemCertService;
@@ -89,6 +90,7 @@ public class CAApplication extends Application {
         classes.add(PKIExceptionMapper.class);
 
         // interceptors
+        singletons.add(new SessionContextInterceptor());
         singletons.add(new AuthMethodInterceptor());
         singletons.add(new ACLInterceptor());
         singletons.add(new MessageFormatInterceptor());
diff --git a/base/common/src/com/netscape/certsrv/base/SessionContext.java b/base/common/src/com/netscape/certsrv/base/SessionContext.java
index 79b75508f33e6dec17ee2df8a703bd62683c9c65..81debaee8c759360f7430c921caa17573610f4c8 100644
--- a/base/common/src/com/netscape/certsrv/base/SessionContext.java
+++ b/base/common/src/com/netscape/certsrv/base/SessionContext.java
@@ -82,26 +82,12 @@ public class SessionContext extends Hashtable<Object, Object> {
      */
     public static final String IPADDRESS = "ipAddress";
 
-    private static Hashtable<Thread, SessionContext> mContexts = new Hashtable<Thread, SessionContext>();
+    private static ThreadLocal<SessionContext> instance = new ThreadLocal<SessionContext>();
 
     /**
      * Constructs a session context.
      */
     public SessionContext() {
-        super();
-    }
-
-    /**
-     * Creates a new context and associates it with
-     * the current thread. If the current thread is
-     * also associated with a old context, the old
-     * context will be replaced.
-     */
-    private static SessionContext createContext() {
-        SessionContext sc = new SessionContext();
-
-        setContext(sc);
-        return sc;
     }
 
     /**
@@ -114,7 +100,7 @@ public class SessionContext extends Hashtable<Object, Object> {
      * @param sc session context
      */
     public static void setContext(SessionContext sc) {
-        mContexts.put(Thread.currentThread(), sc);
+        instance.set(sc);
     }
 
     /**
@@ -125,12 +111,12 @@ public class SessionContext extends Hashtable<Object, Object> {
      * @return sesssion context
      */
     public static SessionContext getContext() {
-        SessionContext sc = mContexts.get(Thread.currentThread());
-
-        if (sc == null) {
-            sc = createContext();
+        SessionContext context = instance.get();
+        if (context == null) {
+            context = new SessionContext();
+            instance.set(context);
         }
-        return sc;
+        return context;
     }
 
     /**
@@ -141,23 +127,13 @@ public class SessionContext extends Hashtable<Object, Object> {
      * @return sesssion context
      */
     public static SessionContext getExistingContext() {
-        SessionContext sc = mContexts.get(Thread.currentThread());
-
-        if (sc == null) {
-            return null;
-        }
-
-        return sc;
+        return instance.get();
     }
 
     /**
      * Releases the current session context.
      */
     public static void releaseContext() {
-        SessionContext sc = mContexts.get(Thread.currentThread());
-
-        if (sc != null) {
-            mContexts.remove(Thread.currentThread());
-        }
+        instance.set(null);
     }
 }
diff --git a/base/kra/src/org/dogtagpki/server/kra/rest/KRAApplication.java b/base/kra/src/org/dogtagpki/server/kra/rest/KRAApplication.java
index 815763cdd82ed2b427c585e52d344ecc02602d24..f63ea9b4fc178092ad36ac59c65aa32a9dc8c59c 100644
--- a/base/kra/src/org/dogtagpki/server/kra/rest/KRAApplication.java
+++ b/base/kra/src/org/dogtagpki/server/kra/rest/KRAApplication.java
@@ -8,10 +8,11 @@ import javax.ws.rs.core.Application;
 import org.dogtagpki.server.rest.ACLInterceptor;
 import org.dogtagpki.server.rest.AccountService;
 import org.dogtagpki.server.rest.AuditService;
+import org.dogtagpki.server.rest.SessionContextInterceptor;
 import org.dogtagpki.server.rest.AuthMethodInterceptor;
 import org.dogtagpki.server.rest.GroupService;
-import org.dogtagpki.server.rest.PKIExceptionMapper;
 import org.dogtagpki.server.rest.MessageFormatInterceptor;
+import org.dogtagpki.server.rest.PKIExceptionMapper;
 import org.dogtagpki.server.rest.SecurityDomainService;
 import org.dogtagpki.server.rest.SelfTestService;
 import org.dogtagpki.server.rest.SystemCertService;
@@ -67,6 +68,7 @@ public class KRAApplication extends Application {
         classes.add(PKIExceptionMapper.class);
 
         // interceptors
+        singletons.add(new SessionContextInterceptor());
         singletons.add(new AuthMethodInterceptor());
         singletons.add(new ACLInterceptor());
         singletons.add(new MessageFormatInterceptor());
diff --git a/base/ocsp/src/org/dogtagpki/server/ocsp/rest/OCSPApplication.java b/base/ocsp/src/org/dogtagpki/server/ocsp/rest/OCSPApplication.java
index 1950edf29d06e6b881d901fbcc9141d76592940d..c01a73407a997b74c6bdc96ff47e2bde4e558bf7 100644
--- a/base/ocsp/src/org/dogtagpki/server/ocsp/rest/OCSPApplication.java
+++ b/base/ocsp/src/org/dogtagpki/server/ocsp/rest/OCSPApplication.java
@@ -8,10 +8,11 @@ import javax.ws.rs.core.Application;
 import org.dogtagpki.server.rest.ACLInterceptor;
 import org.dogtagpki.server.rest.AccountService;
 import org.dogtagpki.server.rest.AuditService;
+import org.dogtagpki.server.rest.SessionContextInterceptor;
 import org.dogtagpki.server.rest.AuthMethodInterceptor;
 import org.dogtagpki.server.rest.GroupService;
-import org.dogtagpki.server.rest.PKIExceptionMapper;
 import org.dogtagpki.server.rest.MessageFormatInterceptor;
+import org.dogtagpki.server.rest.PKIExceptionMapper;
 import org.dogtagpki.server.rest.SecurityDomainService;
 import org.dogtagpki.server.rest.SelfTestService;
 import org.dogtagpki.server.rest.SystemCertService;
@@ -63,6 +64,7 @@ public class OCSPApplication extends Application {
         classes.add(PKIExceptionMapper.class);
 
         // interceptors
+        singletons.add(new SessionContextInterceptor());
         singletons.add(new AuthMethodInterceptor());
         singletons.add(new ACLInterceptor());
         singletons.add(new MessageFormatInterceptor());
diff --git a/base/server/cms/src/org/dogtagpki/server/rest/SessionContextInterceptor.java b/base/server/cms/src/org/dogtagpki/server/rest/SessionContextInterceptor.java
new file mode 100644
index 0000000000000000000000000000000000000000..bae25b66018b3cbebdc235b4328bbde89836e49b
--- /dev/null
+++ b/base/server/cms/src/org/dogtagpki/server/rest/SessionContextInterceptor.java
@@ -0,0 +1,100 @@
+//--- 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) 2012 Red Hat, Inc.
+//All rights reserved.
+//--- END COPYRIGHT BLOCK ---
+package org.dogtagpki.server.rest;
+
+import java.io.IOException;
+import java.security.Principal;
+import java.util.Locale;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.SecurityContext;
+import javax.ws.rs.ext.Provider;
+
+import com.netscape.certsrv.apps.CMS;
+import com.netscape.certsrv.authentication.IAuthToken;
+import com.netscape.certsrv.base.ForbiddenException;
+import com.netscape.certsrv.base.SessionContext;
+import com.netscape.cms.realm.PKIPrincipal;
+import com.netscape.cms.servlet.base.UserInfo;
+
+/**
+ * @author Endi S. Dewata
+ */
+@Provider
+public class SessionContextInterceptor implements ContainerRequestFilter {
+
+    @Context
+    HttpServletRequest servletRequest;
+
+    @Context
+    SecurityContext securityContext;
+
+    public Locale getLocale(HttpServletRequest req) {
+        String lang = req.getHeader("accept-language");
+
+        if (lang == null)
+            return Locale.getDefault();
+
+        return new Locale(UserInfo.getUserLanguage(lang), UserInfo.getUserCountry(lang));
+    }
+
+    @Override
+    public void filter(ContainerRequestContext requestContext) throws IOException {
+
+        Principal principal = securityContext.getUserPrincipal();
+
+        // If unauthenticated, ignore.
+        if (principal == null) {
+            CMS.debug("SessionContextInterceptor: Not authenticated.");
+            SessionContext.releaseContext();
+            return;
+        }
+
+        CMS.debug("SessionContextInterceptor: principal: " + principal.getName());
+
+        // If unrecognized principal, reject request.
+        if (!(principal instanceof PKIPrincipal)) {
+            CMS.debug("SessionContextInterceptor: Invalid user principal.");
+            throw new ForbiddenException("Invalid user principal.");
+        }
+
+        PKIPrincipal pkiPrincipal = (PKIPrincipal) principal;
+        IAuthToken authToken = pkiPrincipal.getAuthToken();
+
+        // If missing auth token, reject request.
+        if (authToken == null) {
+            CMS.debug("SessionContextInterceptor: No authorization token present.");
+            throw new ForbiddenException("No authorization token present.");
+        }
+
+        SessionContext context = SessionContext.getContext();
+
+        String ip = servletRequest.getRemoteAddr();
+        context.put(SessionContext.IPADDRESS, ip);
+
+        Locale locale = getLocale(servletRequest);
+        context.put(SessionContext.LOCALE, locale);
+
+        context.put(SessionContext.AUTH_TOKEN, authToken);
+        context.put(SessionContext.USER_ID, pkiPrincipal.getName());
+        context.put(SessionContext.USER, pkiPrincipal.getUser());
+    }
+}
diff --git a/base/tks/src/org/dogtagpki/server/tks/rest/TKSApplication.java b/base/tks/src/org/dogtagpki/server/tks/rest/TKSApplication.java
index c0fdc6734fe2d27b5122d8c81f8d0ae9cf4aa3c8..030132e933a28d45737641cf341697b105a980b6 100644
--- a/base/tks/src/org/dogtagpki/server/tks/rest/TKSApplication.java
+++ b/base/tks/src/org/dogtagpki/server/tks/rest/TKSApplication.java
@@ -8,10 +8,11 @@ import javax.ws.rs.core.Application;
 import org.dogtagpki.server.rest.ACLInterceptor;
 import org.dogtagpki.server.rest.AccountService;
 import org.dogtagpki.server.rest.AuditService;
+import org.dogtagpki.server.rest.SessionContextInterceptor;
 import org.dogtagpki.server.rest.AuthMethodInterceptor;
 import org.dogtagpki.server.rest.GroupService;
-import org.dogtagpki.server.rest.PKIExceptionMapper;
 import org.dogtagpki.server.rest.MessageFormatInterceptor;
+import org.dogtagpki.server.rest.PKIExceptionMapper;
 import org.dogtagpki.server.rest.SelfTestService;
 import org.dogtagpki.server.rest.SystemCertService;
 import org.dogtagpki.server.rest.UserService;
@@ -49,6 +50,7 @@ public class TKSApplication extends Application {
         classes.add(PKIExceptionMapper.class);
 
         // interceptors
+        singletons.add(new SessionContextInterceptor());
         singletons.add(new AuthMethodInterceptor());
         singletons.add(new ACLInterceptor());
         singletons.add(new MessageFormatInterceptor());
diff --git a/base/tps/src/org/dogtagpki/server/tps/rest/TPSApplication.java b/base/tps/src/org/dogtagpki/server/tps/rest/TPSApplication.java
index 70c8afd02e6d4cea38adbe9f86a21804ce61db8d..7ed870fb0bf412bd715e640d028b012f5af514dc 100644
--- a/base/tps/src/org/dogtagpki/server/tps/rest/TPSApplication.java
+++ b/base/tps/src/org/dogtagpki/server/tps/rest/TPSApplication.java
@@ -25,10 +25,11 @@ import javax.ws.rs.core.Application;
 import org.dogtagpki.server.rest.ACLInterceptor;
 import org.dogtagpki.server.rest.AccountService;
 import org.dogtagpki.server.rest.AuditService;
+import org.dogtagpki.server.rest.SessionContextInterceptor;
 import org.dogtagpki.server.rest.AuthMethodInterceptor;
 import org.dogtagpki.server.rest.GroupService;
-import org.dogtagpki.server.rest.PKIExceptionMapper;
 import org.dogtagpki.server.rest.MessageFormatInterceptor;
+import org.dogtagpki.server.rest.PKIExceptionMapper;
 import org.dogtagpki.server.rest.SelfTestService;
 import org.dogtagpki.server.rest.SystemCertService;
 import org.dogtagpki.server.rest.UserService;
@@ -89,6 +90,7 @@ public class TPSApplication extends Application {
         classes.add(PKIExceptionMapper.class);
 
         // interceptors
+        singletons.add(new SessionContextInterceptor());
         singletons.add(new AuthMethodInterceptor());
         singletons.add(new ACLInterceptor());
         singletons.add(new MessageFormatInterceptor());
-- 
1.9.3

