diff --git a/cadc-auth-restlet/README.md b/cadc-auth-restlet/README.md
deleted file mode 100644
index 62c19d8c..00000000
--- a/cadc-auth-restlet/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-
-auth-restlet is obsolete and not published to Maven Central
-
diff --git a/cadc-auth-restlet/build.gradle b/cadc-auth-restlet/build.gradle
deleted file mode 100644
index 06a93c5a..00000000
--- a/cadc-auth-restlet/build.gradle
+++ /dev/null
@@ -1,27 +0,0 @@
-plugins {
- id 'java'
- id 'maven'
- id 'maven-publish'
- id 'com.jfrog.bintray' version '1.8.4'
-}
-
-repositories {
- jcenter()
- mavenLocal()
-}
-
-sourceCompatibility = 1.7
-
-group = 'org.opencadc'
-
-version = '1.2.2'
-
-dependencies {
- compile 'log4j:log4j:1.2.17'
- compile 'org.restlet.jse:org.restlet:2.0.2'
-
- compile 'org.opencadc:cadc-util:[1.2.13,1.3)'
-
- testCompile 'junit:junit:4.13'
- testCompile 'org.easymock:easymock:3.6'
-}
diff --git a/cadc-auth-restlet/src/main/java/ca/nrc/cadc/auth/restlet/RestletPrincipalExtractor.java b/cadc-auth-restlet/src/main/java/ca/nrc/cadc/auth/restlet/RestletPrincipalExtractor.java
deleted file mode 100644
index 5fe2ca06..00000000
--- a/cadc-auth-restlet/src/main/java/ca/nrc/cadc/auth/restlet/RestletPrincipalExtractor.java
+++ /dev/null
@@ -1,265 +0,0 @@
-/*
-************************************************************************
-******************* CANADIAN ASTRONOMY DATA CENTRE *******************
-************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES **************
-*
-* (c) 2018. (c) 2018.
-* Government of Canada Gouvernement du Canada
-* National Research Council Conseil national de recherches
-* Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6
-* All rights reserved Tous droits réservés
-*
-* NRC disclaims any warranties, Le CNRC dénie toute garantie
-* expressed, implied, or énoncée, implicite ou légale,
-* statutory, of any kind with de quelque nature que ce
-* respect to the software, soit, concernant le logiciel,
-* including without limitation y compris sans restriction
-* any warranty of merchantability toute garantie de valeur
-* or fitness for a particular marchande ou de pertinence
-* purpose. NRC shall not be pour un usage particulier.
-* liable in any event for any Le CNRC ne pourra en aucun cas
-* damages, whether direct or être tenu responsable de tout
-* indirect, special or general, dommage, direct ou indirect,
-* consequential or incidental, particulier ou général,
-* arising from the use of the accessoire ou fortuit, résultant
-* software. Neither the name de l'utilisation du logiciel. Ni
-* of the National Research le nom du Conseil National de
-* Council of Canada nor the Recherches du Canada ni les noms
-* names of its contributors may de ses participants ne peuvent
-* be used to endorse or promote être utilisés pour approuver ou
-* products derived from this promouvoir les produits dérivés
-* software without specific prior de ce logiciel sans autorisation
-* written permission. préalable et particulière
-* par écrit.
-*
-* This file is part of the Ce fichier fait partie du projet
-* OpenCADC project. OpenCADC.
-*
-* OpenCADC is free software: OpenCADC est un logiciel libre ;
-* you can redistribute it and/or vous pouvez le redistribuer ou le
-* modify it under the terms of modifier suivant les termes de
-* the GNU Affero General Public la “GNU Affero General Public
-* License as published by the License” telle que publiée
-* Free Software Foundation, par la Free Software Foundation
-* either version 3 of the : soit la version 3 de cette
-* License, or (at your option) licence, soit (à votre gré)
-* any later version. toute version ultérieure.
-*
-* OpenCADC is distributed in the OpenCADC est distribué
-* hope that it will be useful, dans l’espoir qu’il vous
-* but WITHOUT ANY WARRANTY; sera utile, mais SANS AUCUNE
-* without even the implied GARANTIE : sans même la garantie
-* warranty of MERCHANTABILITY implicite de COMMERCIALISABILITÉ
-* or FITNESS FOR A PARTICULAR ni d’ADÉQUATION À UN OBJECTIF
-* PURPOSE. See the GNU Affero PARTICULIER. Consultez la Licence
-* General Public License for Générale Publique GNU Affero
-* more details. pour plus de détails.
-*
-* You should have received Vous devriez avoir reçu une
-* a copy of the GNU Affero copie de la Licence Générale
-* General Public License along Publique GNU Affero avec
-* with OpenCADC. If not, see OpenCADC ; si ce n’est
-* . pas le cas, consultez :
-* .
-*
-* $Revision: 5 $
-*
-************************************************************************
- */
-
-package ca.nrc.cadc.auth.restlet;
-
-import ca.nrc.cadc.auth.AuthenticationUtil;
-import ca.nrc.cadc.auth.DelegationToken;
-import ca.nrc.cadc.auth.HttpPrincipal;
-import ca.nrc.cadc.auth.InvalidDelegationTokenException;
-import ca.nrc.cadc.auth.NotAuthenticatedException;
-import ca.nrc.cadc.auth.PrincipalExtractor;
-import ca.nrc.cadc.auth.SSLUtil;
-import ca.nrc.cadc.auth.SSOCookieCredential;
-import ca.nrc.cadc.auth.SSOCookieManager;
-import ca.nrc.cadc.auth.X509CertificateChain;
-import ca.nrc.cadc.net.NetUtil;
-import ca.nrc.cadc.util.ArrayUtil;
-import ca.nrc.cadc.util.StringUtil;
-import java.io.IOException;
-import java.security.AccessControlException;
-import java.security.Principal;
-import java.security.cert.X509Certificate;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import org.apache.log4j.Logger;
-import org.restlet.Request;
-import org.restlet.data.Cookie;
-import org.restlet.data.Form;
-import org.restlet.util.Series;
-
-/**
- * Principal Extractor implementation using a Restlet Request.
- */
-public class RestletPrincipalExtractor implements PrincipalExtractor {
-
- private static final Logger log = Logger.getLogger(RestletPrincipalExtractor.class);
-
- private final Request request;
- private X509CertificateChain chain;
- private DelegationToken token;
-
- private List cookieCredentialList;
- private Set cookiePrincipals = new HashSet<>(); // identities extracted from cookie
- private Set principals = new HashSet<>();
-
- /**
- * Hidden no-arg constructor for testing.
- */
- RestletPrincipalExtractor() {
- this.request = null;
- }
-
- /**
- * Create this extractor from the given Restlet Request.
- *
- * @param req The Restlet Request.
- */
- public RestletPrincipalExtractor(final Request req) {
- this.request = req;
- }
-
- private void init() {
-
- if (chain == null) {
- final Collection requestCertificates
- = (Collection) getRequest().getAttributes().get(
- "org.restlet.https.clientCertificates");
- if ((requestCertificates != null) && (!requestCertificates.isEmpty())) {
- this.chain = new X509CertificateChain(requestCertificates);
- principals.add(this.chain.getPrincipal());
- }
- }
-
- log.debug("Value of CERT_HEADER_ENABLE sys prop: " + System.getProperty(CERT_HEADER_ENABLE));
- if (chain == null && "true".equals(System.getProperty(CERT_HEADER_ENABLE))) {
- Form allHeaders = (Form) getRequest().getAttributes().get("org.restlet.http.headers");
- String certString = allHeaders.getFirstValue(CERT_HEADER_FIELD, true);
- log.debug(CERT_HEADER_FIELD + ":\n" + certString + "\n");
- if (certString != null && certString.length() > 0) {
- try {
- byte[] certBytes = SSLUtil.getCertificates(certString.getBytes());
- chain = new X509CertificateChain(SSLUtil.readCertificateChain(certBytes), null);
- principals.add(chain.getPrincipal());
- } catch (Exception e) {
- log.error("Failed to read certificate", e);
- throw new AccessControlException("Failed to read certificate: " + e.getMessage());
- }
- }
- }
-
- if (token == null) {
- Form headers = (Form) getRequest().getAttributes().get("org.restlet.http.headers");
- String tokenValue = headers.getFirstValue(AuthenticationUtil.AUTH_HEADER, true);
- if (StringUtil.hasText(tokenValue)) {
- try {
- this.token = DelegationToken.parse(tokenValue, request.getResourceRef().getPath());
- } catch (InvalidDelegationTokenException ex) {
- log.debug("invalid DelegationToken: " + tokenValue, ex);
- throw new NotAuthenticatedException("invalid delegation token. " + ex.getMessage());
- } catch (RuntimeException ex) {
- log.debug("invalid DelegationToken: " + tokenValue, ex);
- throw new NotAuthenticatedException("invalid delegation token. " + ex.getMessage());
- } finally {
- }
- }
- }
-
- // add HttpPrincipal
- final String httpUser = getAuthenticatedUsername();
- if (StringUtil.hasText(httpUser)) // user from HTTP AUTH
- {
- principals.add(new HttpPrincipal(httpUser));
- } else if (token != null) // user from token
- {
- principals.add(token.getUser());
- }
-
- Series cookies = getRequest().getCookies();
- log.debug("cookie count: " + cookies.size());
- log.debug("principal count: " + principals.size());
- log.debug(principals);
- if (cookies == null || (cookies.size() == 0)) {
- return;
- }
-
- for (Cookie ssoCookie : cookies) {
- log.debug(ssoCookie.toString());
-
- if (SSOCookieManager.DEFAULT_SSO_COOKIE_NAME.equals(
- ssoCookie.getName())
- && StringUtil.hasText(ssoCookie.getValue())) {
- SSOCookieManager ssoCookieManager = new SSOCookieManager();
- try {
- DelegationToken cookieToken = ssoCookieManager.parse(
- ssoCookie.getValue());
-
- cookiePrincipals = cookieToken.getIdentityPrincipals();
- principals.addAll(cookiePrincipals);
-
- cookieCredentialList = ssoCookieManager.getSSOCookieCredentials(ssoCookie.getValue(),
- NetUtil.getDomainName(getRequest().getResourceRef().toUrl()));
- } catch (InvalidDelegationTokenException | IOException e) {
- log.debug("Cannot use SSO Cookie. Reason: " + e.getMessage());
- throw new NotAuthenticatedException("invalid cookie. " + e.getMessage());
- }
- }
- }
- }
-
- public X509CertificateChain getCertificateChain() {
- init();
- return chain;
- }
-
- public Set getPrincipals() {
- init();
- return principals;
- }
-
- public DelegationToken getDelegationToken() {
- init();
- return token;
- }
-
- /**
- * Obtain the Username submitted with the Request.
- *
- * @return String username, or null if none found.
- */
- protected String getAuthenticatedUsername() {
- final String username;
-
- if (!getRequest().getClientInfo().getPrincipals().isEmpty()) {
- // Put in to support Safari not injecting a Challenge Response.
- // Grab the first principal's name as the username.
- // update: this is *always* right and works with realms; the previous
- // call to getRequest().getChallengeResponse().getIdentifier() would
- // return whatever username the caller provided in a non-authenticating call
- username = getRequest().getClientInfo().getPrincipals().get(0).getName();
- log.debug("username: " + username);
- } else {
- username = null;
- }
-
- return username;
- }
-
- public Request getRequest() {
- return request;
- }
-
- @Override
- public List getSSOCookieCredentials() {
- return cookieCredentialList;
- }
-}
diff --git a/cadc-auth-restlet/src/test/java/ca/nrc/cadc/auth/restlet/RestletPrincipalExtractorTest.java b/cadc-auth-restlet/src/test/java/ca/nrc/cadc/auth/restlet/RestletPrincipalExtractorTest.java
deleted file mode 100644
index a7ae978f..00000000
--- a/cadc-auth-restlet/src/test/java/ca/nrc/cadc/auth/restlet/RestletPrincipalExtractorTest.java
+++ /dev/null
@@ -1,342 +0,0 @@
-/*
- ************************************************************************
- **** C A N A D I A N A S T R O N O M Y D A T A C E N T R E *****
- *
- * (c) 2012. (c) 2012.
- * National Research Council Conseil national de recherches
- * Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6
- * All rights reserved Tous droits reserves
- *
- * NRC disclaims any warranties Le CNRC denie toute garantie
- * expressed, implied, or statu- enoncee, implicite ou legale,
- * tory, of any kind with respect de quelque nature que se soit,
- * to the software, including concernant le logiciel, y com-
- * without limitation any war- pris sans restriction toute
- * ranty of merchantability or garantie de valeur marchande
- * fitness for a particular pur- ou de pertinence pour un usage
- * pose. NRC shall not be liable particulier. Le CNRC ne
- * in any event for any damages, pourra en aucun cas etre tenu
- * whether direct or indirect, responsable de tout dommage,
- * special or general, consequen- direct ou indirect, particul-
- * tial or incidental, arising ier ou general, accessoire ou
- * from the use of the software. fortuit, resultant de l'utili-
- * sation du logiciel.
- *
- *
- * @author jenkinsd
- * 4/20/12 - 12:49 PM
- *
- *
- *
- **** C A N A D I A N A S T R O N O M Y D A T A C E N T R E *****
- ************************************************************************
- */
-
-package ca.nrc.cadc.auth.restlet;
-
-import ca.nrc.cadc.auth.AuthenticationUtil;
-import ca.nrc.cadc.auth.DelegationToken;
-import ca.nrc.cadc.auth.NotAuthenticatedException;
-import java.security.Principal;
-import java.security.cert.X509Certificate;
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.Collection;
-import java.util.Date;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import javax.security.auth.x500.X500Principal;
-import org.apache.log4j.Logger;
-import static org.easymock.EasyMock.*;
-import static org.junit.Assert.*;
-import org.junit.Assert;
-import org.junit.Test;
-import org.restlet.Request;
-import org.restlet.data.ClientInfo;
-import org.restlet.data.Cookie;
-import org.restlet.data.Form;
-import org.restlet.engine.util.CookieSeries;
-import org.restlet.util.Series;
-
-public class RestletPrincipalExtractorTest {
-
- private static final Logger log = Logger.getLogger(RestletPrincipalExtractorTest.class);
-
- private RestletPrincipalExtractor testSubject;
- private final Request mockRequest = createMock(Request.class);
-
- @Test
- public void testCaseInsensitiveDelegationToken() throws Exception {
- // create an invalid token and test that RestletPrincipalExtractor finds it even when case of
- // attribute is changed
- setTestSubject(new RestletPrincipalExtractor() {
- @Override
- public Request getRequest() {
- return getMockRequest();
- }
-
- @Override
- protected String getAuthenticatedUsername() {
- return null;
- }
- });
-
- final ConcurrentMap attributes
- = new ConcurrentHashMap();
- Form form = new Form(AuthenticationUtil.AUTH_HEADER + "=foo");
- attributes.put("org.restlet.http.headers", form);
- expect(getMockRequest().getAttributes()).andReturn(attributes).atLeastOnce();
-
- replay(getMockRequest());
- try {
- DelegationToken dt = getTestSubject().getDelegationToken();
- assertTrue(false);
- } catch (NotAuthenticatedException e)
- {
- assertEquals("Unexpected exception", "invalid delegation token. null", e.getMessage());
- }
-
- // repeat test with lowercase attributes
- reset(getMockRequest());
- attributes.clear();
- form = new Form(AuthenticationUtil.AUTH_HEADER.toLowerCase() + "=foo");
- attributes.put("org.restlet.http.headers", form);
- expect(getMockRequest().getAttributes()).andReturn(attributes).atLeastOnce();
-
- replay(getMockRequest());
- try {
- DelegationToken dt = getTestSubject().getDelegationToken();
- Assert.fail("reason: expected exception due to invalid token");
- } catch (NotAuthenticatedException e)
- {
- assertEquals("expected exception", "invalid delegation token. null", e.getMessage());
- }
- }
-
- @Test
- public void testGetDelegationToken() throws Exception {
- setTestSubject(new RestletPrincipalExtractor() {
- @Override
- public Request getRequest() {
- return getMockRequest();
- }
-
- @Override
- protected String getAuthenticatedUsername() {
- return null;
- }
- });
-
- final Series requestCookies = new CookieSeries();
- expect(getMockRequest().getCookies()).andReturn(requestCookies).atLeastOnce();
-
- final ConcurrentMap attributes
- = new ConcurrentHashMap();
- Form mockHeaders = createMock(Form.class);
- attributes.put("org.restlet.http.headers", mockHeaders);
- expect(mockHeaders.getFirstValue(AuthenticationUtil.AUTH_HEADER, true)).andReturn(null).atLeastOnce();
- expect(getMockRequest().getAttributes()).andReturn(attributes).atLeastOnce();
-
- replay(mockHeaders);
- replay(getMockRequest());
-
- DelegationToken dt = getTestSubject().getDelegationToken();
- System.out.println(dt);
- assertNull("Should have no token", dt);
-
- Set ps = getTestSubject().getPrincipals();
- assertTrue("Should have no principals.", ps.isEmpty());
-
- verify(getMockRequest());
- }
-
- @Test
- public void addCookiePrincipal() throws Exception {
- final Series requestCookies = new CookieSeries();
-
- setTestSubject(new RestletPrincipalExtractor() {
- @Override
- public Request getRequest() {
- return getMockRequest();
- }
- });
-
- final ClientInfo clientInfo = new ClientInfo();
- expect(getMockRequest().getClientInfo()).andReturn(clientInfo).atLeastOnce();
-
- expect(getMockRequest().getCookies()).andReturn(requestCookies).atLeastOnce();
-
- final ConcurrentMap attributes
- = new ConcurrentHashMap();
- Form mockHeaders = createMock(Form.class);
- attributes.put("org.restlet.http.headers", mockHeaders);
- expect(mockHeaders.getFirstValue(AuthenticationUtil.AUTH_HEADER, true)).andReturn(null).atLeastOnce();
- expect(getMockRequest().getAttributes()).andReturn(attributes).atLeastOnce();
-
- replay(mockHeaders);
- replay(getMockRequest());
-
- Set ps = getTestSubject().getPrincipals();
-
- assertTrue("Should have no principals.", ps.isEmpty());
-
- verify(getMockRequest());
-
- //
- // TEST 2 -- can't test this without full setup of keys to generate and validate
- /*
- reset(mockHeaders);
- reset(getMockRequest());
-
- String sessionID = new SSOCookieManager().generate(new HttpPrincipal("foo"));
- requestCookies.add("CADC_SSO", sessionID);
-
- expect(getMockRequest().getClientInfo()).andReturn(clientInfo).atLeastOnce();
-
- expect(getMockRequest().getCookies()).andReturn(requestCookies).atLeastOnce();
-
- expect(mockHeaders.getFirstValue(AuthenticationUtil.AUTH_HEADER)).andReturn(null).atLeastOnce();
-
- expect(getMockRequest().getAttributes()).andReturn(attributes).atLeastOnce();
-
- replay(mockHeaders);
- replay(getMockRequest());
-
- ps = getTestSubject().getPrincipals();
-
- assertEquals("Should have one principal.", 1, ps.size());
- CookiePrincipal cp = (CookiePrincipal) ps.iterator().next();
- assertEquals(sessionID, cp.getSessionId());
-
-
- verify(getMockRequest());
- */
- }
-
- @Test
- public void addHTTPPrincipal() throws Exception {
-
- setTestSubject(new RestletPrincipalExtractor() {
- @Override
- public Request getRequest() {
- return getMockRequest();
- }
-
- @Override
- protected String getAuthenticatedUsername() {
- return null;
- }
- });
-
- final Series requestCookies = new CookieSeries();
- expect(getMockRequest().getCookies()).andReturn(requestCookies).atLeastOnce();
-
- final ConcurrentMap attributes
- = new ConcurrentHashMap();
- Form mockHeaders = createMock(Form.class);
- attributes.put("org.restlet.http.headers", mockHeaders);
- expect(mockHeaders.getFirstValue(AuthenticationUtil.AUTH_HEADER, true)).andReturn(null).atLeastOnce();
- expect(getMockRequest().getAttributes()).andReturn(attributes).atLeastOnce();
-
- replay(mockHeaders);
- replay(getMockRequest());
-
- Set ps = getTestSubject().getPrincipals();
-
- assertTrue("Should have no principals.", ps.isEmpty());
-
- verify(getMockRequest());
- }
-
- @Test
- public void addX500Principal() throws Exception {
- setTestSubject(new RestletPrincipalExtractor() {
- @Override
- public Request getRequest() {
- return getMockRequest();
- }
- });
-
- final ClientInfo clientInfo = new ClientInfo();
- expect(getMockRequest().getClientInfo()).andReturn(clientInfo).atLeastOnce();
-
- final ConcurrentMap attributes
- = new ConcurrentHashMap();
- Form mockHeaders = createMock(Form.class);
- attributes.put("org.restlet.http.headers", mockHeaders);
- expect(mockHeaders.getFirstValue(AuthenticationUtil.AUTH_HEADER, true)).andReturn(null).atLeastOnce();
- expect(getMockRequest().getAttributes()).andReturn(attributes).atLeastOnce();
-
- final Series requestCookies = new CookieSeries();
- expect(getMockRequest().getCookies()).andReturn(requestCookies).atLeastOnce();
-
- replay(mockHeaders);
- replay(getMockRequest());
-
- Set ps = getTestSubject().getPrincipals();
-
- assertTrue("Should have no principals.", ps.isEmpty());
-
- verify(getMockRequest());
-
- //
- // TEST 2
- reset(getMockRequest());
-
- expect(getMockRequest().getClientInfo()).andReturn(clientInfo).atLeastOnce();
-
- expect(getMockRequest().getCookies()).andReturn(requestCookies).atLeastOnce();
-
- mockHeaders = createMock(Form.class);
- attributes.put("org.restlet.http.headers", mockHeaders);
- expect(mockHeaders.getFirstValue(AuthenticationUtil.AUTH_HEADER, true)).andReturn(null).atLeastOnce();
-
- final Calendar notAfterCal = Calendar.getInstance();
- notAfterCal.set(1977, Calendar.NOVEMBER, 25, 3, 21, 0);
- notAfterCal.set(Calendar.MILLISECOND, 0);
-
- final X500Principal subjectX500Principal
- = new X500Principal("CN=CN1,O=O1");
- final X500Principal issuerX500Principal
- = new X500Principal("CN=CN2,O=O2");
- final Date notAfterDate = notAfterCal.getTime();
- final X509Certificate mockCertificate
- = createMock(X509Certificate.class);
-
- final Collection certificates1
- = new ArrayList();
-
- certificates1.add(mockCertificate);
-
- attributes.put("org.restlet.https.clientCertificates", certificates1);
- expect(getMockRequest().getAttributes()).andReturn(attributes).atLeastOnce();
-
- expect(mockCertificate.getNotAfter()).andReturn(notAfterDate).once();
- expect(mockCertificate.getSubjectX500Principal()).
- andReturn(subjectX500Principal).once();
- expect(mockCertificate.getIssuerX500Principal()).andReturn(
- issuerX500Principal).once();
-
- replay(mockHeaders);
- replay(getMockRequest(), mockCertificate);
-
- ps = getTestSubject().getPrincipals();
-
- assertEquals("Should have one HTTP principal.", 1, ps.size());
-
- verify(getMockRequest(), mockCertificate);
- }
-
- protected RestletPrincipalExtractor getTestSubject() {
- return testSubject;
- }
-
- protected void setTestSubject(final RestletPrincipalExtractor testSubject) {
- this.testSubject = testSubject;
- }
-
- public Request getMockRequest() {
- return mockRequest;
- }
-}
diff --git a/cadc-util/build.gradle b/cadc-util/build.gradle
index fab5eef4..67b15a62 100644
--- a/cadc-util/build.gradle
+++ b/cadc-util/build.gradle
@@ -15,7 +15,7 @@ sourceCompatibility = 1.8
group = 'org.opencadc'
-version = '1.12.7'
+version = '1.12.8'
description = 'OpenCADC core utility library'
def git_url = 'https://github.com/opencadc/core'
diff --git a/cadc-util/src/main/java/ca/nrc/cadc/auth/AuthenticationUtil.java b/cadc-util/src/main/java/ca/nrc/cadc/auth/AuthenticationUtil.java
index dcb95188..c6e0876d 100644
--- a/cadc-util/src/main/java/ca/nrc/cadc/auth/AuthenticationUtil.java
+++ b/cadc-util/src/main/java/ca/nrc/cadc/auth/AuthenticationUtil.java
@@ -107,12 +107,9 @@
*/
public class AuthenticationUtil {
- @Deprecated // Should be using standard Authorization header
- public static final String AUTH_HEADER = "X-CADC-DelegationToken";
-
// HTTP/1.1 Authorization header as defined by RFC 7235
public static final String AUTHORIZATION_HEADER = "Authorization";
-
+
// HTTP/1.1 WWW-Authenticate header
public static final String AUTHENTICATE_HEADER = "WWW-Authenticate";
@@ -127,8 +124,6 @@ public class AuthenticationUtil {
public static final String CHALLENGE_TYPE_IVOA_BEARER = "ivoa_bearer";
public static final String CHALLENGE_TYPE_IVOA_X509 = "ivoa_x509";
public static final String CHALLENGE_TYPE_IVOA_COOKIE = "ivoa_cookie";
- @Deprecated
- public static final String TOKEN_TYPE_CADC = AUTH_HEADER;
// Mandatory support list of RDN descriptors according to RFC 4512.
private static final String[] ORDERED_RDN_KEYS = new String[] { "DC", "CN", "OU", "O", "STREET", "L", "ST", "C", "UID" };
@@ -776,6 +771,9 @@ public static String getPrincipalType(Principal userID) {
if (userID instanceof X500Principal) {
return IdentityType.X500.getValue().toLowerCase();
}
+ if (userID instanceof OpenIdPrincipal) {
+ return IdentityType.OPENID.getValue().toLowerCase();
+ }
if (userID instanceof HttpPrincipal) {
return IdentityType.USERNAME.getValue().toLowerCase();
}
diff --git a/cadc-util/src/main/java/ca/nrc/cadc/auth/ServletPrincipalExtractor.java b/cadc-util/src/main/java/ca/nrc/cadc/auth/ServletPrincipalExtractor.java
index 35c1f6b6..f0747a0a 100644
--- a/cadc-util/src/main/java/ca/nrc/cadc/auth/ServletPrincipalExtractor.java
+++ b/cadc-util/src/main/java/ca/nrc/cadc/auth/ServletPrincipalExtractor.java
@@ -136,13 +136,6 @@ public ServletPrincipalExtractor(final HttpServletRequest req) {
}
}
- // custom header (deprecated)
- String cadcTokenHeader = request.getHeader(AuthenticationUtil.AUTH_HEADER);
- if (cadcTokenHeader != null) {
- AuthorizationTokenPrincipal principal = new AuthorizationTokenPrincipal(AuthenticationUtil.AUTH_HEADER, cadcTokenHeader);
- principals.add(principal);
- }
-
// authorization header
Enumeration authTokens = request.getHeaders(AuthenticationUtil.AUTHORIZATION_HEADER);
while (authTokens.hasMoreElements()) {
diff --git a/cadc-util/src/main/java/ca/nrc/cadc/auth/SignedToken.java b/cadc-util/src/main/java/ca/nrc/cadc/auth/SignedToken.java
index ad484743..d8181f37 100644
--- a/cadc-util/src/main/java/ca/nrc/cadc/auth/SignedToken.java
+++ b/cadc-util/src/main/java/ca/nrc/cadc/auth/SignedToken.java
@@ -248,6 +248,21 @@ private static StringBuilder getContent(SignedToken token) {
return sb;
}
+ /**
+ * Checks whether a token is a signed token or not.
+ * @param text Token to check
+ * @return true if the token is a CADC access token, false otherwise
+ */
+ public static boolean isSignedToken(String text) {
+ try {
+ String val = Base64.decodeString(text);
+ return val.contains(VALUE_DELIM);
+ } catch (IllegalArgumentException ignore) {
+ // not a base64 encoded string
+ return false;
+ }
+ }
+
/**
* Builds a DelegationToken from a text string
*
diff --git a/cadc-util/src/main/java/ca/nrc/cadc/auth/TokenValidator.java b/cadc-util/src/main/java/ca/nrc/cadc/auth/TokenValidator.java
index f04e53b5..37da422c 100644
--- a/cadc-util/src/main/java/ca/nrc/cadc/auth/TokenValidator.java
+++ b/cadc-util/src/main/java/ca/nrc/cadc/auth/TokenValidator.java
@@ -96,7 +96,7 @@ public class TokenValidator {
* @throws AccessControlException
*/
public static Subject validateTokens(Subject subject) throws NotAuthenticatedException {
-
+
// cookies
Set cookiePrincipals = subject.getPrincipals(CookiePrincipal.class);
log.debug("validateTokens: found " + cookiePrincipals.size() + " cookie principals");
@@ -116,7 +116,7 @@ public static Subject validateTokens(Subject subject) throws NotAuthenticatedExc
}
}
}
-
+
// tokens
Set tokenPrincipals = subject.getPrincipals(AuthorizationTokenPrincipal.class);
log.debug("validateTokens: found " + tokenPrincipals.size() + " token principals");
@@ -125,41 +125,32 @@ public static Subject validateTokens(Subject subject) throws NotAuthenticatedExc
log.debug("header value: " + p.getHeaderValue());
String credentials = null;
String challengeType = null;
- if (AuthenticationUtil.TOKEN_TYPE_CADC.equals(p.getHeaderKey())) {
- challengeType = AuthenticationUtil.TOKEN_TYPE_CADC;
- credentials = p.getHeaderValue().trim();
- } else if (AuthenticationUtil.AUTHORIZATION_HEADER.equals(p.getHeaderKey())) {
+ if (AuthenticationUtil.AUTHORIZATION_HEADER.equals(p.getHeaderKey())) {
// parse the token into challenge type and credentials.
int spaceIndex = p.getHeaderValue().indexOf(" ");
if (spaceIndex == -1) {
throw new NotAuthenticatedException(challengeType, AuthError.INVALID_REQUEST,
- "missing authorization challenge");
+ "missing authorization challenge");
}
challengeType = p.getHeaderValue().substring(0, spaceIndex).trim();
if (AuthenticationUtil.CHALLENGE_TYPE_BEARER.equalsIgnoreCase(challengeType)) {
credentials = p.getHeaderValue().substring(spaceIndex + 1).trim();
- }
- // else: ignore
- // throw new NotAuthenticatedException(challengeType, AuthError.INVALID_REQUEST,
- // "unsupported challenge type: " + challengeType);
- //}
- }
- if (challengeType != null && credentials != null) {
- log.debug("challenge type: " + challengeType);
- log.debug("credentials: " + credentials);
+ try {
+ if (SignedToken.isSignedToken(credentials)) {
+ SignedToken validatedToken = SignedToken.parse(credentials);
+ subject.getPrincipals().addAll(validatedToken.getIdentityPrincipals());
- try {
- SignedToken validatedToken = SignedToken.parse(credentials);
- subject.getPrincipals().addAll(validatedToken.getIdentityPrincipals());
-
- AuthorizationToken authToken = new AuthorizationToken(
- challengeType, credentials, validatedToken.getDomains(), validatedToken.getScope());
+ AuthorizationToken authToken = new AuthorizationToken(
+ challengeType, credentials, validatedToken.getDomains(), validatedToken.getScope());
- log.debug("Adding token credential to subject, removing token principal");
- subject.getPublicCredentials().add(authToken);
- subject.getPrincipals().remove(p);
- } catch (Exception ex) {
- throw new NotAuthenticatedException(challengeType, AuthError.INVALID_TOKEN, ex.getMessage(), ex);
+ log.debug("Adding token credential to subject, removing token principal");
+ subject.getPublicCredentials().add(authToken);
+ subject.getPrincipals().remove(p);
+ } // else: other kind of bearer token: leave AuthorizationTokenPrincipal for more processing
+ } catch (Exception ex) {
+ throw new NotAuthenticatedException(
+ challengeType, AuthError.INVALID_TOKEN, ex.getMessage(), ex);
+ }
}
}
// ignore other challenge types
diff --git a/cadc-util/src/test/java/ca/nrc/cadc/auth/AuthenticationUtilTest.java b/cadc-util/src/test/java/ca/nrc/cadc/auth/AuthenticationUtilTest.java
index e40f4073..814fcc51 100755
--- a/cadc-util/src/test/java/ca/nrc/cadc/auth/AuthenticationUtilTest.java
+++ b/cadc-util/src/test/java/ca/nrc/cadc/auth/AuthenticationUtilTest.java
@@ -80,24 +80,19 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-import ca.nrc.cadc.util.FileUtil;
import ca.nrc.cadc.util.Log4jInit;
-import ca.nrc.cadc.util.RsaSignatureGenerator;
-import java.io.File;
import java.net.InetAddress;
import java.net.PasswordAuthentication;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.PrivilegedAction;
import java.security.cert.X509Certificate;
-import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
-import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -472,7 +467,6 @@ public void testGetSubjectFromHttpServletRequest_Anon()
expect(mockRequest.getRemoteUser()).andReturn(null).atLeastOnce();
expect(mockRequest.getCookies()).andReturn(null).atLeastOnce();
- expect(mockRequest.getHeader(AuthenticationUtil.AUTH_HEADER)).andReturn(null).atLeastOnce();
expect(mockRequest.getHeaders("Authorization")).andReturn(Collections.emptyEnumeration());
expect(mockRequest.getAttribute(
"javax.servlet.request.X509Certificate")).andReturn(null).atLeastOnce();
@@ -513,7 +507,6 @@ public void testGetSubjectFromHttpServletRequest_XClientCertificateEnabled()
expect(mockRequest.getRemoteUser()).andReturn(null).atLeastOnce();
expect(mockRequest.getCookies()).andReturn(null).atLeastOnce();
- expect(mockRequest.getHeader(AuthenticationUtil.AUTH_HEADER)).andReturn(null).atLeastOnce();
expect(mockRequest.getHeaders("Authorization")).andReturn(Collections.emptyEnumeration());
expect(mockRequest.getAttribute(
"javax.servlet.request.X509Certificate")).andReturn(null).atLeastOnce();
@@ -553,7 +546,6 @@ public void testGetSubjectFromHttpServletRequest_HttpPrincipal()
expect(mockRequest.getRemoteUser()).andReturn("foo").atLeastOnce();
expect(mockRequest.getCookies()).andReturn(null).atLeastOnce();
- expect(mockRequest.getHeader(AuthenticationUtil.AUTH_HEADER)).andReturn(null).atLeastOnce();
expect(mockRequest.getHeaders("Authorization")).andReturn(Collections.emptyEnumeration());
expect(mockRequest.getAttribute(
"javax.servlet.request.X509Certificate")).andReturn(null).atLeastOnce();
@@ -669,7 +661,6 @@ public void testGetSubjectFromHttpServletRequest_X500Principal()
expect(mockRequest.getRemoteUser()).andReturn(null).atLeastOnce();
expect(mockRequest.getCookies()).andReturn(null).atLeastOnce();
- expect(mockRequest.getHeader(AuthenticationUtil.AUTH_HEADER)).andReturn(null).atLeastOnce();
expect(mockRequest.getHeader("Authorization")).andReturn(null);
expect(mockRequest.getAttribute(
"javax.servlet.request.X509Certificate")).andReturn(ca).atLeastOnce();
@@ -755,7 +746,6 @@ public void testGetSubjectFromHttpServletRequest_CookiePrincipal() throws Except
expect(mockRequest.getRemoteUser()).andReturn(null).atLeastOnce();
expect(mockRequest.getCookies()).andReturn(cookies).atLeastOnce();
- expect(mockRequest.getHeader(AuthenticationUtil.AUTH_HEADER)).andReturn(null).atLeastOnce();
expect(mockRequest.getHeader("Authorization")).andReturn(null);
expect(mockRequest.getAttribute(
"javax.servlet.request.X509Certificate")).andReturn(null).atLeastOnce();
diff --git a/cadc-util/src/test/java/ca/nrc/cadc/auth/ServletPrincipalExtractorTest.java b/cadc-util/src/test/java/ca/nrc/cadc/auth/ServletPrincipalExtractorTest.java
index 0852a3e6..e1e5fef7 100644
--- a/cadc-util/src/test/java/ca/nrc/cadc/auth/ServletPrincipalExtractorTest.java
+++ b/cadc-util/src/test/java/ca/nrc/cadc/auth/ServletPrincipalExtractorTest.java
@@ -42,7 +42,6 @@
import ca.nrc.cadc.util.Log4jInit;
import ca.nrc.cadc.util.PropertiesReader;
-import ca.nrc.cadc.util.RSASignatureGeneratorValidatorTest;
import ca.nrc.cadc.util.RsaSignatureGenerator;
import java.io.File;
@@ -54,9 +53,7 @@
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
-import org.easymock.EasyMock;
import org.junit.After;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
@@ -106,7 +103,6 @@ public void testCookie() throws Exception
expect(request.getAttribute(
ServletPrincipalExtractor.CERT_REQUEST_ATTRIBUTE)).andReturn(null);
- expect(request.getHeader(AuthenticationUtil.AUTH_HEADER)).andReturn(null);
expect(request.getHeaders("Authorization")).andReturn(Collections.emptyEnumeration());
expect(request.getCookies()).andReturn(cookies);
expect(request.getRemoteUser()).andReturn(null).times(2);
diff --git a/cadc-util/src/test/java/ca/nrc/cadc/auth/TokenValidatorTest.java b/cadc-util/src/test/java/ca/nrc/cadc/auth/TokenValidatorTest.java
index 45e62ac2..80003a0a 100644
--- a/cadc-util/src/test/java/ca/nrc/cadc/auth/TokenValidatorTest.java
+++ b/cadc-util/src/test/java/ca/nrc/cadc/auth/TokenValidatorTest.java
@@ -127,28 +127,16 @@ public void testValidateTokens() {
subject = TokenValidator.validateTokens(subject);
Assert.assertEquals("cookie credential", 1, subject.getPublicCredentials(SSOCookieCredential.class).size());
Assert.assertEquals("cookie principal", 0, subject.getPrincipals(AuthorizationTokenPrincipal.class).size());
-
- // test cadc deprecated tokens
- subject = new Subject();
- token = new SignedToken(new HttpPrincipal("user"), null, expiry, domains);
- value = SignedToken.format(token);
- AuthorizationTokenPrincipal authPrincipal = new AuthorizationTokenPrincipal(AuthenticationUtil.TOKEN_TYPE_CADC, value);
- subject.getPrincipals().add(authPrincipal);
- subject = TokenValidator.validateTokens(subject);
- Assert.assertEquals("token credential", 1, subject.getPublicCredentials(AuthorizationToken.class).size());
- AuthorizationToken authToken = subject.getPublicCredentials(AuthorizationToken.class).iterator().next();
- Assert.assertEquals("cadc token type", AuthenticationUtil.TOKEN_TYPE_CADC, authToken.getType());
- Assert.assertEquals("cadc token value", value, authToken.getCredentials());
-
+
// bearer tokens
subject = new Subject();
token = new SignedToken(new HttpPrincipal("user"), URI.create("the:scope"), expiry, domains);
value = SignedToken.format(token);
- authPrincipal = new AuthorizationTokenPrincipal(AuthenticationUtil.AUTHORIZATION_HEADER, "Bearer " + value);
+ AuthorizationTokenPrincipal authPrincipal = new AuthorizationTokenPrincipal(AuthenticationUtil.AUTHORIZATION_HEADER, "Bearer " + value);
subject.getPrincipals().add(authPrincipal);
subject = TokenValidator.validateTokens(subject);
Assert.assertEquals("token credential", 1, subject.getPublicCredentials(AuthorizationToken.class).size());
- authToken = subject.getPublicCredentials(AuthorizationToken.class).iterator().next();
+ AuthorizationToken authToken = subject.getPublicCredentials(AuthorizationToken.class).iterator().next();
Assert.assertEquals("bearer token type", AuthenticationUtil.CHALLENGE_TYPE_BEARER, authToken.getType());
Assert.assertEquals("bearer token value", value, authToken.getCredentials());
Assert.assertEquals("bearer token scope", "the:scope", authToken.getScope().toString());
@@ -165,10 +153,14 @@ public void testValidateTokens() {
} catch (NotAuthenticatedException ex) {
Assert.assertTrue("exception message", ex.getMessage().contains("unsupported challenge type: ivoa"));
}
-
- // invalid bearer token
+
+
+ // invalid bearer token (expired
+ Date expired = new Date(new Date().getTime() - (48 * 3600 * 1000));
subject = new Subject();
- authPrincipal = new AuthorizationTokenPrincipal(AuthenticationUtil.AUTHORIZATION_HEADER, "Bearer tampered");
+ token = new SignedToken(new HttpPrincipal("user"), URI.create("the:scope"), expired, domains);
+ value = SignedToken.format(token);
+ authPrincipal = new AuthorizationTokenPrincipal(AuthenticationUtil.AUTHORIZATION_HEADER, "Bearer " + value);
subject.getPrincipals().add(authPrincipal);
try {
subject = TokenValidator.validateTokens(subject);
@@ -178,6 +170,13 @@ public void testValidateTokens() {
Assert.assertEquals("invalid_token", e.getAuthError().getValue());
}
+ // invalid bearer CADC access token - ignored
+ subject = new Subject();
+ authPrincipal = new AuthorizationTokenPrincipal(AuthenticationUtil.AUTHORIZATION_HEADER, "Bearer tampered");
+ subject.getPrincipals().add(authPrincipal);
+ subject = TokenValidator.validateTokens(subject);
+ Assert.assertTrue("invalid bearer token ignored", subject.getPrincipals().contains(authPrincipal));
+
// unsupported challenge type
subject = new Subject();
token = new SignedToken(new HttpPrincipal("user"), null, expiry, domains);