From df1a7676c443e67328add77fea6b764ef5d211fa Mon Sep 17 00:00:00 2001 From: Fraser Tweedale Date: Tue, 22 Dec 2015 18:49:08 +1100 Subject: [PATCH] Add external authentication support --- .classpath | 2 + base/ca/tomcat8/conf/Catalina/localhost/ca.xml | 2 + .../certsrv/authentication/ExternalAuthToken.java | 139 +++++++++++++++++++++ .../org/dogtagpki/server/rest/ACLInterceptor.java | 16 +-- .../org/dogtagpki/server/rest/AccountService.java | 5 +- .../server/rest/AuthMethodInterceptor.java | 29 +++-- .../server/rest/SessionContextInterceptor.java | 30 ++--- base/server/tomcat/src/CMakeLists.txt | 8 ++ .../cms/tomcat/ExternalAuthenticationValve.java | 75 +++++++++++ 9 files changed, 264 insertions(+), 42 deletions(-) create mode 100644 base/common/src/com/netscape/certsrv/authentication/ExternalAuthToken.java create mode 100644 base/server/tomcat/src/com/netscape/cms/tomcat/ExternalAuthenticationValve.java diff --git a/.classpath b/.classpath index 9fd5144bf32f3a4af6b6992f7b5027ef55d9f2df..2cac0dedce4376283783067312981998fbfad4a9 100644 --- a/.classpath +++ b/.classpath @@ -54,7 +54,9 @@ + + diff --git a/base/ca/tomcat8/conf/Catalina/localhost/ca.xml b/base/ca/tomcat8/conf/Catalina/localhost/ca.xml index 46f270817a58282b950b75a15bb3bd052f178f0c..0268bc17e055b98198a9a44275319e77217c87fd 100644 --- a/base/ca/tomcat8/conf/Catalina/localhost/ca.xml +++ b/base/ca/tomcat8/conf/Catalina/localhost/ca.xml @@ -27,6 +27,8 @@ + + getElements() { + return null; + } + + public Object get(String k) { + return null; + } + + public boolean set(String k, String v) { + return false; + } + + public String getInString(String k) { + if (k != null + && (k.equals(IAuthToken.USER_ID) || k.equals(IAuthToken.UID))) + return principal.getName(); + else + return null; + } + + public boolean set(String k, byte[] v) { + return false; + } + + public byte[] getInByteArray(String k) { + return null; + } + + public boolean set(String k, Integer v) { + return false; + } + + public Integer getInInteger(String k) { + return null; + } + + public boolean set(String k, BigInteger[] v) { + return false; + } + + public BigInteger[] getInBigIntegerArray(String k) { + return null; + } + + public boolean set(String k, Date v) { + return false; + } + + public Date getInDate(String k) { + return null; + } + + public boolean set(String k, String[] v) { + return false; + } + + public String[] getInStringArray(String k) { + if (k != null && k.equals(IAuthToken.GROUPS)) + return principal.getRoles(); + else + return null; + } + + public boolean set(String k, X509CertImpl v) { + return false; + } + + public X509CertImpl getInCert(String k) { + return null; + } + + public boolean set(String k, CertificateExtensions v) { + return false; + } + + public CertificateExtensions getInCertExts(String k) { + return null; + } + + public boolean set(String k, Certificates v) { + return false; + } + + public Certificates getInCertificates(String k) { + return null; + } + + public boolean set(String k, byte[][] v) { + return false; + } + + public byte[][] getInByteArrayArray(String k) { + return null; + } +} diff --git a/base/server/cms/src/org/dogtagpki/server/rest/ACLInterceptor.java b/base/server/cms/src/org/dogtagpki/server/rest/ACLInterceptor.java index 49001168130831bbb002711120891195b5d54ba5..3a76a5c793f4c8ba346c3979cee86fe7a75a7d7a 100644 --- a/base/server/cms/src/org/dogtagpki/server/rest/ACLInterceptor.java +++ b/base/server/cms/src/org/dogtagpki/server/rest/ACLInterceptor.java @@ -31,11 +31,13 @@ import javax.ws.rs.core.Context; import javax.ws.rs.core.SecurityContext; import javax.ws.rs.ext.Provider; +import org.apache.catalina.realm.GenericPrincipal; import org.jboss.resteasy.core.ResourceMethodInvoker; import org.jboss.resteasy.spi.Failure; import com.netscape.certsrv.acls.ACLMapping; import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.authentication.ExternalAuthToken; import com.netscape.certsrv.authentication.IAuthToken; import com.netscape.certsrv.authorization.AuthzToken; import com.netscape.certsrv.authorization.EAuthzAccessDenied; @@ -140,18 +142,12 @@ public class ACLInterceptor implements ContainerRequestFilter { if (principal != null) CMS.debug("ACLInterceptor: principal: " + principal.getName()); - // If unrecognized principal, reject request. - if (principal != null && !(principal instanceof PKIPrincipal)) { - CMS.debug("ACLInterceptor: Invalid user principal."); - // audit comment: no Principal, no one to blame here - throw new ForbiddenException("Invalid user principal."); - } - - PKIPrincipal pkiPrincipal = null; IAuthToken authToken = null; if (principal != null) { - pkiPrincipal = (PKIPrincipal) principal; - authToken = pkiPrincipal.getAuthToken(); + if (principal instanceof PKIPrincipal) + authToken = ((PKIPrincipal) principal).getAuthToken(); + else if (principal instanceof GenericPrincipal) + authToken = new ExternalAuthToken((GenericPrincipal) principal); } // If missing auth token, reject request. diff --git a/base/server/cms/src/org/dogtagpki/server/rest/AccountService.java b/base/server/cms/src/org/dogtagpki/server/rest/AccountService.java index 4e8e6e6f89fc065e2ad0c5fa616165de929142db..827e99e076585d0732bfde8ae795d6ae63648d5f 100644 --- a/base/server/cms/src/org/dogtagpki/server/rest/AccountService.java +++ b/base/server/cms/src/org/dogtagpki/server/rest/AccountService.java @@ -29,6 +29,7 @@ import javax.ws.rs.core.Request; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; +import org.apache.catalina.realm.GenericPrincipal; import org.apache.commons.lang.StringUtils; import com.netscape.certsrv.account.AccountInfo; @@ -75,8 +76,10 @@ public class AccountService extends PKIService implements AccountResource { String email = user.getEmail(); if (!StringUtils.isEmpty(email)) response.setEmail(email); + } - String[] roles = pkiPrincipal.getRoles(); + if (principal instanceof GenericPrincipal) { + String[] roles = ((GenericPrincipal) principal).getRoles(); response.setRoles(Arrays.asList(roles)); } diff --git a/base/server/cms/src/org/dogtagpki/server/rest/AuthMethodInterceptor.java b/base/server/cms/src/org/dogtagpki/server/rest/AuthMethodInterceptor.java index ac0b2518cdc42528b7c0e94153f2b02777c26785..2a81813fad282ee943ea9eaae43291eb1397c686 100644 --- a/base/server/cms/src/org/dogtagpki/server/rest/AuthMethodInterceptor.java +++ b/base/server/cms/src/org/dogtagpki/server/rest/AuthMethodInterceptor.java @@ -139,22 +139,20 @@ public class AuthMethodInterceptor implements ContainerRequestFilter { throw new ForbiddenException("Anonymous access not allowed."); } - // If unrecognized principal, reject request. - if (!(principal instanceof PKIPrincipal)) { - CMS.debug("AuthMethodInterceptor: unknown principal"); - throw new ForbiddenException("Unknown user principal"); - } + String authManager = "external"; + if (principal instanceof PKIPrincipal) { + PKIPrincipal pkiPrincipal = (PKIPrincipal) principal; + IAuthToken authToken = pkiPrincipal.getAuthToken(); - PKIPrincipal pkiPrincipal = (PKIPrincipal) principal; - IAuthToken authToken = pkiPrincipal.getAuthToken(); + // If missing auth token, reject request. + if (authToken == null) { + CMS.debug("AuthMethodInterceptor: missing authentication token"); + throw new ForbiddenException("Missing authentication token."); + } - // If missing auth token, reject request. - if (authToken == null) { - CMS.debug("AuthMethodInterceptor: missing authentication token"); - throw new ForbiddenException("Missing authentication token."); + authManager = (String) authToken.get(AuthToken.TOKEN_AUTHMGR_INST_NAME); } - String authManager = (String) authToken.get(AuthToken.TOKEN_AUTHMGR_INST_NAME); CMS.debug("AuthMethodInterceptor: authentication manager: " + authManager); if (authManager == null) { @@ -162,7 +160,12 @@ public class AuthMethodInterceptor implements ContainerRequestFilter { throw new ForbiddenException("Missing authentication manager."); } - if (authMethods.isEmpty() || authMethods.contains(authManager) || authMethods.contains("*")) { + if ( + authMethods.isEmpty() + || authManager.equals("external") + || authMethods.contains(authManager) + || authMethods.contains("*") + ) { CMS.debug("AuthMethodInterceptor: access granted"); return; } diff --git a/base/server/cms/src/org/dogtagpki/server/rest/SessionContextInterceptor.java b/base/server/cms/src/org/dogtagpki/server/rest/SessionContextInterceptor.java index b6461abfdee36ea4eeba4d07da815482b02712ba..e8def14d9d77f046ef1dd52dd5ceacf0041ed543 100644 --- a/base/server/cms/src/org/dogtagpki/server/rest/SessionContextInterceptor.java +++ b/base/server/cms/src/org/dogtagpki/server/rest/SessionContextInterceptor.java @@ -29,9 +29,11 @@ import javax.ws.rs.core.Context; import javax.ws.rs.core.SecurityContext; import javax.ws.rs.ext.Provider; +import org.apache.catalina.realm.GenericPrincipal; import org.jboss.resteasy.core.ResourceMethodInvoker; import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.authentication.ExternalAuthToken; import com.netscape.certsrv.authentication.IAuthToken; import com.netscape.certsrv.base.ForbiddenException; import com.netscape.certsrv.base.SessionContext; @@ -80,21 +82,6 @@ public class SessionContextInterceptor implements ContainerRequestFilter { 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(); @@ -103,8 +90,15 @@ public class SessionContextInterceptor implements ContainerRequestFilter { 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()); + context.put(SessionContext.USER_ID, principal.getName()); + + if (principal instanceof PKIPrincipal) { + PKIPrincipal pkiPrincipal = (PKIPrincipal) principal; + context.put(SessionContext.AUTH_TOKEN, pkiPrincipal.getAuthToken()); + context.put(SessionContext.USER, pkiPrincipal.getUser()); + } else if (principal instanceof GenericPrincipal) { + context.put(SessionContext.AUTH_TOKEN, + new ExternalAuthToken((GenericPrincipal) principal)); + } } } diff --git a/base/server/tomcat/src/CMakeLists.txt b/base/server/tomcat/src/CMakeLists.txt index 669cc8883043062119afdb5b55db28828d09e92f..2e3658f4b12cdbc4515b0532c3c20b5c0194e84e 100644 --- a/base/server/tomcat/src/CMakeLists.txt +++ b/base/server/tomcat/src/CMakeLists.txt @@ -133,6 +133,13 @@ find_file(NUXWDOG_JAR /usr/share/java ) +find_file(TOMCAT_COYOTE_JAR + NAMES + tomcat-coyote.jar + PATHS + /usr/share/java/tomcat +) + # build pki-tomcat javac(pki-tomcat-classes SOURCES @@ -140,6 +147,7 @@ javac(pki-tomcat-classes CLASSPATH ${SERVLET_JAR} ${TOMCAT_CATALINA_JAR} ${TOMCAT_UTIL_SCAN_JAR} ${NUXWDOG_JAR} ${APACHE_COMMONS_LANG_JAR} ${TOMCATJSS_JAR} + ${TOMCAT_COYOTE_JAR} OUTPUT_DIR ${CMAKE_BINARY_DIR}/../../tomcat ) diff --git a/base/server/tomcat/src/com/netscape/cms/tomcat/ExternalAuthenticationValve.java b/base/server/tomcat/src/com/netscape/cms/tomcat/ExternalAuthenticationValve.java new file mode 100644 index 0000000000000000000000000000000000000000..92cec5d301803be63dfb305b12aaca7985f6fc83 --- /dev/null +++ b/base/server/tomcat/src/com/netscape/cms/tomcat/ExternalAuthenticationValve.java @@ -0,0 +1,75 @@ +// --- 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) 2015 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- + +package com.netscape.cms.tomcat; + +import java.io.IOException; +import java.security.Principal; +import java.util.ArrayList; +import javax.servlet.ServletException; + +import org.apache.catalina.connector.Request; +import org.apache.catalina.connector.Response; +import org.apache.catalina.realm.GenericPrincipal; +import org.apache.catalina.valves.ValveBase; + +public class ExternalAuthenticationValve extends ValveBase { + + public void invoke(Request req, Response resp) + throws IOException, ServletException { + System.out.println("ExternalAuthenticationValve; authType: " + + req.getAuthType()); + System.out.println("ExternalAuthenticationValve; principal: " + + req.getUserPrincipal()); + System.out.println(req.getCoyoteRequest().getAttributes().toString()); + + org.apache.coyote.Request coyoteReq = req.getCoyoteRequest(); + Principal oldPrincipal = req.getUserPrincipal(); + + if (oldPrincipal != null) { + Integer numGroups = 0; + String numGroupsStr = (String) + coyoteReq.getAttribute("REMOTE_USER_GROUP_N"); + if (numGroupsStr != null) { + try { + numGroups = new Integer(numGroupsStr); + } catch (NumberFormatException e) { + System.out.println("ExternalAuthenticationValve: invalid REMOTE_USER_GROUP_N value: " + e); + } + } + + ArrayList groups = new ArrayList<>(); + for (int i = 1; i <= numGroups; i++) { + String k = "REMOTE_USER_GROUP_" + i; + String s = (String) coyoteReq.getAttribute(k); + if (s != null && !s.isEmpty()) + groups.add(s); + else + System.out.println("ExternalAuthenticationValve: missing or empty attribute: " + k); + } + + GenericPrincipal newPrincipal = + new GenericPrincipal(oldPrincipal.getName(), null, groups); + + System.out.println("ExternalAuthenticationValve: setting new principal: " + newPrincipal); + req.setUserPrincipal(newPrincipal); + } + + getNext().invoke(req, resp); + } +} -- 2.5.0