ca.nrc.cadc.auth.AuthenticationUtil Maven / Gradle / Ivy
Show all versions of cadc-util Show documentation
/*
************************************************************************
******************* CANADIAN ASTRONOMY DATA CENTRE *******************
************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES **************
*
* (c) 2024. (c) 2024.
* 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;
import ca.nrc.cadc.date.DateUtil;
import ca.nrc.cadc.net.NetUtil;
import ca.nrc.cadc.util.InvalidConfigException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.cert.CertificateException;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.X509Certificate;
import java.text.DateFormat;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import javax.naming.InvalidNameException;
import javax.naming.ldap.LdapName;
import javax.naming.ldap.Rdn;
import javax.security.auth.Subject;
import javax.security.auth.x500.X500Principal;
import javax.servlet.http.HttpServletRequest;
import org.apache.log4j.Logger;
/**
* Security utility.
*
* @author adriand
* @version $Version$
*/
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";
// IVOA header to indicate successful authentication. Value is a principal name.
public static final String VO_AUTHENTICATED_HEADER = "x-vo-authenticated";
// IVOA header to set a Bearer token in a response
public static final String VO_TOKEN_BEARER = "x-vo-bearer";
public static final String CHALLENGE_TYPE_BEARER = "Bearer";
public static final String CHALLENGE_TYPE_BASIC = "Basic";
public static final String CHALLENGE_TYPE_IVOA_BEARER = "ivoa_bearer";
public static final String CHALLENGE_TYPE_IVOA_X509 = "ivoa_x509";
@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" };
private static Logger log = Logger.getLogger(AuthenticationUtil.class);
/**
* Load the available IdentityManager implementation. This utility method will
* check the ca.nrc.cadc.auth.IdentityManager
system property for a
* configured class name. If not configured or loading the configured implementation
* fails, the default is the ca.nrc.cadc.auth.NoOpIdentityManager
.
*
* @return an IdentityManager implementation
*/
public static IdentityManager getIdentityManager() {
String cname = System.getProperty(IdentityManager.class.getName());
IdentityManager ret = new NoOpIdentityManager();
if (cname != null) {
try {
Class c = Class.forName(cname);
Object o = c.getConstructor().newInstance();
ret = (IdentityManager) o;
} catch (ClassNotFoundException
| IllegalAccessException | IllegalArgumentException | InstantiationException
| NoSuchMethodException | SecurityException | InvocationTargetException ex) {
throw new InvalidConfigException("failed to load configured IdentityManager: " + cname, ex);
}
}
log.debug("loaded IdentityManager: " + ret.getClass().getName());
return ret;
}
// backwards compat
private static Authenticator getAuthenticator(IdentityManager im) {
String cname = System.getProperty(Authenticator.class.getName());
if (cname != null && im instanceof NoOpIdentityManager) {
try {
Class c = Class.forName(cname);
Object o = c.getConstructor().newInstance();
Authenticator ret = (Authenticator) o;
log.warn("DEPRECATED: using " + Authenticator.class.getName() + " = " + cname);
return ret;
} catch (ClassNotFoundException
| IllegalAccessException | IllegalArgumentException | InstantiationException
| NoSuchMethodException | SecurityException | InvocationTargetException ex) {
throw new InvalidConfigException("failed to load configured IdentityManager: " + cname, ex);
}
}
return null;
}
public static Subject augmentSubject(Subject s) {
IdentityManager auth = getIdentityManager();
// temporary backwards compat
//Authenticator alt = getAuthenticator(auth);
//if (alt != null) {
// return alt.augment(s);
//}
return auth.augment(s);
}
public static Subject validateSubject(Subject s) throws NotAuthenticatedException {
IdentityManager auth = getIdentityManager();
// temporary backwards compat
//Authenticator alt = getAuthenticator(auth);
//if (alt != null) {
// return alt.validate(s);
//}
return auth.validate(s);
}
public static Subject getAnonSubject() {
Subject ret = new Subject();
setAuthMethod(ret, AuthMethod.ANON);
return ret;
}
/**
* Get the AuthMethod used by the caller. This is normally only meaningful in
* server side applications to figure out how the caller authenticated.
*
* @param s
* @return
*/
public static AuthMethod getAuthMethod(Subject s) {
if (s == null) {
return null;
}
Set m = s.getPublicCredentials(AuthMethod.class);
if (m.isEmpty()) {
return null;
}
return m.iterator().next();
}
private static void setAuthMethod(Subject s, AuthMethod am) {
if (s == null || am == null) {
return;
}
s.getPublicCredentials().add(am);
}
/**
* Get an AuthMethod that can be used with credentials from the specified set.
*
* @param subject the subject with credentials
* @return
*/
public static AuthMethod getAuthMethodFromCredentials(Subject subject) {
if (subject == null || subject.getPublicCredentials().isEmpty()) {
return AuthMethod.ANON;
}
// web services using CDP and command-line applications with --cert option
Set cert = subject.getPublicCredentials(X509CertificateChain.class);
if (!cert.isEmpty()) {
return AuthMethod.CERT;
}
// command-line applications with --netrc option
Set pa = subject.getPublicCredentials(PasswordCredentials.class);
if (!pa.isEmpty()) {
return AuthMethod.PASSWORD;
}
// ui applications pass cookie(s) along
Set sso = subject.getPublicCredentials(SSOCookieCredential.class);
if (!sso.isEmpty()) {
return AuthMethod.COOKIE;
}
Set delToken = subject.getPublicCredentials(SignedToken.class);
if (!delToken.isEmpty()) {
return AuthMethod.TOKEN;
}
Set token = subject.getPublicCredentials(AuthorizationToken.class);
if (!token.isEmpty()) {
return AuthMethod.TOKEN;
}
return AuthMethod.ANON;
}
/**
* Create a Subject using the given PrincipalExtractor. An implementation of the
* PrincipalExtractor interface is used to extract the authentication
* information from the incoming request. An implementation for plain servlet
* environment is provided here and a Restlet implementation is currently
* included in the cadcUWS library.
*
* This method tries to detect the use of a proxy certificate and add the
* Principal representing the real identity of the user by comparing the subject
* and issuer fields of the certificate and using the issuer principal when the
* certificate is self-signed. If the user has connected anonymously, the
* returned Subject will have no principals and no credentials, but should be
* safe to use with Subject.doAs(...).
*
*
* This method will also try to load an implementation of the IdentityManager
* interface and use it to process the Subject before return. By default, it
* will try to load the ca.nrc.cadc.auth.NoOpIdentityManager
and
* simply ignore all authentication. Applications may override this default class
* name by setting the ca.nrc.cadc.auth.IdentityManager system property
* to the class name of their implementation.
*
*
* @param principalExtractor The PrincipalExtractor to provide Principals.
* @param augmentSubject Whether to augment the subject using Authenticator interface.
* @return A new Subject.
*/
public static Subject getSubject(PrincipalExtractor principalExtractor, boolean augmentSubject) {
if (principalExtractor == null) {
throw new IllegalArgumentException("principalExtractor cannot be null");
}
final Set principals = principalExtractor.getPrincipals();
final X509CertificateChain chain = principalExtractor.getCertificateChain();
AuthMethod am = null;
final Set