mirror of
https://github.com/DSpace/DSpace.git
synced 2025-10-07 01:54:22 +00:00
[ 2102617 ] Sands Fish - X509Authentication fails to assign appropriate special groups
http://sourceforge.net/tracker/index.php?func=detail&aid=2102617&group_id=19984&atid=319984 git-svn-id: http://scm.dspace.org/svn/repo/branches/dspace-1_5_x@3114 9c30dcfa-912a-0410-8fc2-9e0234be79fd
This commit is contained in:
@@ -66,39 +66,53 @@ import org.dspace.core.LogManager;
|
||||
import org.dspace.eperson.EPerson;
|
||||
|
||||
/**
|
||||
* Implicit authentication method that gets credentials from the X.509
|
||||
* client certificate supplied by the HTTPS client when connecting to
|
||||
* this server. The email address in that certificate is taken as the
|
||||
* authenticated user name with no further checking, so be sure
|
||||
* your HTTP server (e.g. Tomcat) is configured correctly to accept only
|
||||
* client certificates it can validate.
|
||||
* Implicit authentication method that gets credentials from the X.509 client
|
||||
* certificate supplied by the HTTPS client when connecting to this server. The
|
||||
* email address in that certificate is taken as the authenticated user name
|
||||
* with no further checking, so be sure your HTTP server (e.g. Tomcat) is
|
||||
* configured correctly to accept only client certificates it can validate.
|
||||
* <p>
|
||||
* See the <code>AuthenticationMethod</code> interface for more details.
|
||||
* <p>
|
||||
* <b>Configuration:</b><pre>
|
||||
* authentication.x509.keystore.path = <em>path to Java keystore file</em>
|
||||
* authentication.x509.keystore.password = <em>password to access the keystore</em>
|
||||
* authentication.x509.ca.cert = <em>path to certificate file for CA whose client certs to accept.</em>
|
||||
* authentication.x509.autoregister = <em>"true" if E-Person is created automatically for unknown new users.</em>
|
||||
* <b>Configuration:</b>
|
||||
*
|
||||
* <pre>
|
||||
* authentication.x509.keystore.path =
|
||||
* <em>
|
||||
* path to Java keystore file
|
||||
* </em>
|
||||
* authentication.x509.keystore.password =
|
||||
* <em>
|
||||
* password to access the keystore
|
||||
* </em>
|
||||
* authentication.x509.ca.cert =
|
||||
* <em>
|
||||
* path to certificate file for CA whose client certs to accept.
|
||||
* </em>
|
||||
* authentication.x509.autoregister =
|
||||
* <em>
|
||||
* "true" if E-Person is created automatically for unknown new users.
|
||||
* </em>
|
||||
* </pre>
|
||||
*
|
||||
* Only one of the "<code>keystore.path</code>" or "<code>ca.cert</code>"
|
||||
* options is required. If you supply a keystore, then all of the "trusted"
|
||||
* certificates in the keystore represent CAs whose client certificates will
|
||||
* be accepted. The <code>ca.cert</code> option only allows a single CA to be named.
|
||||
* options is required. If you supply a keystore, then all of the "trusted"
|
||||
* certificates in the keystore represent CAs whose client certificates will be
|
||||
* accepted. The <code>ca.cert</code> option only allows a single CA to be
|
||||
* named.
|
||||
* <p>
|
||||
* You can configure <em>both</em> a keystore and a CA cert, and both will
|
||||
* be used.
|
||||
* You can configure <em>both</em> a keystore and a CA cert, and both will be
|
||||
* used.
|
||||
* <p>
|
||||
* The <code>autoregister</code> configuration parameter determines what
|
||||
* the <code>canSelfRegister()</code> method returns. It also allows an
|
||||
* EPerson record to be created automatically when the presented
|
||||
* certificate is acceptable but there is no corresponding EPerson.
|
||||
*
|
||||
* The <code>autoregister</code> configuration parameter determines what the
|
||||
* <code>canSelfRegister()</code> method returns. It also allows an EPerson
|
||||
* record to be created automatically when the presented certificate is
|
||||
* acceptable but there is no corresponding EPerson.
|
||||
*
|
||||
* @author Larry Stone
|
||||
* @version $Revision$
|
||||
*/
|
||||
public class X509Authentication
|
||||
implements AuthenticationMethod
|
||||
public class X509Authentication implements AuthenticationMethod
|
||||
{
|
||||
|
||||
/** log4j category */
|
||||
@@ -111,27 +125,31 @@ public class X509Authentication
|
||||
private static KeyStore caCertKeyStore = null;
|
||||
|
||||
private static String loginPageTitle = null;
|
||||
|
||||
|
||||
private static String loginPageURL = null;
|
||||
|
||||
|
||||
/**
|
||||
* Initialization:
|
||||
* Set caPublicKey and/or keystore. This loads the information
|
||||
* needed to check if a client cert presented is valid and acceptable.
|
||||
* Initialization: Set caPublicKey and/or keystore. This loads the
|
||||
* information needed to check if a client cert presented is valid and
|
||||
* acceptable.
|
||||
*/
|
||||
static
|
||||
{
|
||||
/*
|
||||
* allow identification of alternative entry points
|
||||
* for certificate authentication when
|
||||
* selected by the user rather than implicitly.
|
||||
* allow identification of alternative entry points for certificate
|
||||
* authentication when selected by the user rather than implicitly.
|
||||
*/
|
||||
loginPageTitle = ConfigurationManager.getProperty("authentication.x509.chooser.title.key");
|
||||
loginPageURL = ConfigurationManager.getProperty("authentication.x509.chooser.uri");
|
||||
|
||||
String keystorePath = ConfigurationManager.getProperty("authentication.x509.keystore.path");
|
||||
String keystorePassword = ConfigurationManager.getProperty("authentication.x509.keystore.password");
|
||||
String caCertPath = ConfigurationManager.getProperty("authentication.x509.ca.cert");
|
||||
loginPageTitle = ConfigurationManager
|
||||
.getProperty("authentication.x509.chooser.title.key");
|
||||
loginPageURL = ConfigurationManager
|
||||
.getProperty("authentication.x509.chooser.uri");
|
||||
|
||||
String keystorePath = ConfigurationManager
|
||||
.getProperty("authentication.x509.keystore.path");
|
||||
String keystorePassword = ConfigurationManager
|
||||
.getProperty("authentication.x509.keystore.password");
|
||||
String caCertPath = ConfigurationManager
|
||||
.getProperty("authentication.x509.ca.cert");
|
||||
|
||||
// backward-compatible kludge
|
||||
if (caCertPath == null)
|
||||
@@ -143,7 +161,8 @@ public class X509Authentication
|
||||
FileInputStream fis = null;
|
||||
if (keystorePassword == null)
|
||||
keystorePassword = "";
|
||||
try {
|
||||
try
|
||||
{
|
||||
KeyStore ks = KeyStore.getInstance("JKS");
|
||||
fis = new FileInputStream(keystorePath);
|
||||
ks.load(fis, keystorePassword.toCharArray());
|
||||
@@ -151,18 +170,26 @@ public class X509Authentication
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
log.error("X509Authentication: Failed to load CA keystore, file="+
|
||||
keystorePath+", error="+e.toString());
|
||||
log
|
||||
.error("X509Authentication: Failed to load CA keystore, file="
|
||||
+ keystorePath + ", error=" + e.toString());
|
||||
}
|
||||
catch (GeneralSecurityException e)
|
||||
{
|
||||
log.error("X509Authentication: Failed to extract CA keystore, file="+
|
||||
keystorePath+", error="+e.toString());
|
||||
log
|
||||
.error("X509Authentication: Failed to extract CA keystore, file="
|
||||
+ keystorePath + ", error=" + e.toString());
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (fis != null)
|
||||
try { fis.close(); } catch (IOException ioe) { }
|
||||
try
|
||||
{
|
||||
fis.close();
|
||||
}
|
||||
catch (IOException ioe)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,38 +203,51 @@ public class X509Authentication
|
||||
fis = new FileInputStream(caCertPath);
|
||||
is = new BufferedInputStream(fis);
|
||||
X509Certificate cert = (X509Certificate) CertificateFactory
|
||||
.getInstance("X.509").generateCertificate(is);
|
||||
.getInstance("X.509").generateCertificate(is);
|
||||
if (cert != null)
|
||||
caPublicKey = cert.getPublicKey();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
log.error("X509Authentication: Failed to load CA cert, file="+
|
||||
caCertPath+", error="+e.toString());
|
||||
log.error("X509Authentication: Failed to load CA cert, file="
|
||||
+ caCertPath + ", error=" + e.toString());
|
||||
}
|
||||
catch (CertificateException e)
|
||||
{
|
||||
log.error("X509Authentication: Failed to extract CA cert, file="+
|
||||
caCertPath+", error="+e.toString());
|
||||
log
|
||||
.error("X509Authentication: Failed to extract CA cert, file="
|
||||
+ caCertPath + ", error=" + e.toString());
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (is != null)
|
||||
try { is.close(); } catch (IOException ioe) { }
|
||||
try
|
||||
{
|
||||
is.close();
|
||||
}
|
||||
catch (IOException ioe)
|
||||
{
|
||||
}
|
||||
|
||||
if (fis != null)
|
||||
try { fis.close(); } catch (IOException ioe) { }
|
||||
try
|
||||
{
|
||||
fis.close();
|
||||
}
|
||||
catch (IOException ioe)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the email address from <code>certificate</code>, or null
|
||||
* if an email address cannot be found in the certificate.
|
||||
* Return the email address from <code>certificate</code>, or null if an
|
||||
* email address cannot be found in the certificate.
|
||||
* <p>
|
||||
* Note that the certificate parsing has only been tested with certificates
|
||||
* granted by the MIT Certification Authority, and may not work elsewhere.
|
||||
*
|
||||
*
|
||||
* @param certificate -
|
||||
* An X509 certificate object
|
||||
* @return - The email address found in certificate, or null if an email
|
||||
@@ -249,7 +289,7 @@ public class X509Authentication
|
||||
/**
|
||||
* Verify CERTIFICATE against KEY. Return true if and only if CERTIFICATE is
|
||||
* valid and can be verified against KEY.
|
||||
*
|
||||
*
|
||||
* @param certificate -
|
||||
* An X509 certificate object
|
||||
* @param key -
|
||||
@@ -257,8 +297,7 @@ public class X509Authentication
|
||||
* @return - True if CERTIFICATE is valid and can be verified against KEY,
|
||||
* false otherwise.
|
||||
*/
|
||||
private static boolean isValid(Context context,
|
||||
X509Certificate certificate)
|
||||
private static boolean isValid(Context context, X509Certificate certificate)
|
||||
{
|
||||
if (certificate == null)
|
||||
return false;
|
||||
@@ -271,7 +310,8 @@ public class X509Authentication
|
||||
catch (CertificateException e)
|
||||
{
|
||||
log.info(LogManager.getHeader(context, "authentication",
|
||||
"X.509 Certificate is EXPIRED or PREMATURE: "+e.toString()));
|
||||
"X.509 Certificate is EXPIRED or PREMATURE: "
|
||||
+ e.toString()));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -286,7 +326,8 @@ public class X509Authentication
|
||||
catch (GeneralSecurityException e)
|
||||
{
|
||||
log.info(LogManager.getHeader(context, "authentication",
|
||||
"X.509 Certificate FAILED SIGNATURE check: "+e.toString()));
|
||||
"X.509 Certificate FAILED SIGNATURE check: "
|
||||
+ e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -299,11 +340,12 @@ public class X509Authentication
|
||||
|
||||
while (ke.hasMoreElements())
|
||||
{
|
||||
String alias = (String)ke.nextElement();
|
||||
String alias = (String) ke.nextElement();
|
||||
if (caCertKeyStore.isCertificateEntry(alias))
|
||||
{
|
||||
Certificate ca = caCertKeyStore.getCertificate(alias);
|
||||
try {
|
||||
try
|
||||
{
|
||||
certificate.verify(ca.getPublicKey());
|
||||
return true;
|
||||
}
|
||||
@@ -312,13 +354,16 @@ public class X509Authentication
|
||||
}
|
||||
}
|
||||
}
|
||||
log.info(LogManager.getHeader(context, "authentication",
|
||||
"Keystore method FAILED SIGNATURE check on client cert."));
|
||||
log
|
||||
.info(LogManager
|
||||
.getHeader(context, "authentication",
|
||||
"Keystore method FAILED SIGNATURE check on client cert."));
|
||||
}
|
||||
catch (GeneralSecurityException e)
|
||||
{
|
||||
log.info(LogManager.getHeader(context, "authentication",
|
||||
"X.509 Certificate FAILED SIGNATURE check: "+e.toString()));
|
||||
"X.509 Certificate FAILED SIGNATURE check: "
|
||||
+ e.toString()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -326,26 +371,23 @@ public class X509Authentication
|
||||
}
|
||||
|
||||
/**
|
||||
* Predicate, can new user automatically create EPerson.
|
||||
* Checks configuration value. You'll probably want this to
|
||||
* be true to take advantage of a Web certificate infrastructure
|
||||
* with many more users than are already known by DSpace.
|
||||
* Predicate, can new user automatically create EPerson. Checks
|
||||
* configuration value. You'll probably want this to be true to take
|
||||
* advantage of a Web certificate infrastructure with many more users than
|
||||
* are already known by DSpace.
|
||||
*/
|
||||
public boolean canSelfRegister(Context context,
|
||||
HttpServletRequest request,
|
||||
String username)
|
||||
throws SQLException
|
||||
public boolean canSelfRegister(Context context, HttpServletRequest request,
|
||||
String username) throws SQLException
|
||||
{
|
||||
return ConfigurationManager
|
||||
.getBooleanProperty("authentication.x509.autoregister");
|
||||
.getBooleanProperty("authentication.x509.autoregister");
|
||||
}
|
||||
|
||||
/**
|
||||
* Nothing extra to initialize.
|
||||
* Nothing extra to initialize.
|
||||
*/
|
||||
public void initEPerson(Context context, HttpServletRequest request,
|
||||
EPerson eperson)
|
||||
throws SQLException
|
||||
EPerson eperson) throws SQLException
|
||||
{
|
||||
}
|
||||
|
||||
@@ -353,9 +395,7 @@ public class X509Authentication
|
||||
* We don't use EPerson password so there is no reason to change it.
|
||||
*/
|
||||
public boolean allowSetPassword(Context context,
|
||||
HttpServletRequest request,
|
||||
String username)
|
||||
throws SQLException
|
||||
HttpServletRequest request, String username) throws SQLException
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -377,37 +417,33 @@ public class X509Authentication
|
||||
}
|
||||
|
||||
/**
|
||||
* X509 certificate authentication. The client certificate
|
||||
* is obtained from the <code>ServletRequest</code> object.
|
||||
* X509 certificate authentication. The client certificate is obtained from
|
||||
* the <code>ServletRequest</code> object.
|
||||
* <ul>
|
||||
* <li>If the certificate is valid, and corresponds to an existing EPerson,
|
||||
* and the user is allowed to login, return success.</li>
|
||||
* <li>If the user is matched but is not allowed to login, it fails.</li>
|
||||
* <li>If the certificate is valid, but there is no corresponding EPerson,
|
||||
* the <code>"authentication.x509.autoregister"</code>
|
||||
* configuration parameter is checked (via <code>canSelfRegister()</code>)
|
||||
* the <code>"authentication.x509.autoregister"</code> configuration
|
||||
* parameter is checked (via <code>canSelfRegister()</code>)
|
||||
* <ul>
|
||||
* <li>If it's true, a new EPerson record is created for the certificate, and
|
||||
* the result is success.</li>
|
||||
* <li>If it's true, a new EPerson record is created for the certificate,
|
||||
* and the result is success.</li>
|
||||
* <li>If it's false, return that the user was unknown.</li>
|
||||
* </ul>
|
||||
* </li>
|
||||
* </ul>
|
||||
*
|
||||
*
|
||||
* @return One of: SUCCESS, BAD_CREDENTIALS, NO_SUCH_USER, BAD_ARGS
|
||||
*/
|
||||
public int authenticate(Context context,
|
||||
String username,
|
||||
String password,
|
||||
String realm,
|
||||
HttpServletRequest request)
|
||||
throws SQLException
|
||||
public int authenticate(Context context, String username, String password,
|
||||
String realm, HttpServletRequest request) throws SQLException
|
||||
{
|
||||
// Obtain the certificate from the request, if any
|
||||
X509Certificate[] certs = null;
|
||||
if (request != null)
|
||||
certs = (X509Certificate[]) request
|
||||
.getAttribute("javax.servlet.request.X509Certificate");
|
||||
.getAttribute("javax.servlet.request.X509Certificate");
|
||||
|
||||
if ((certs == null) || (certs.length == 0))
|
||||
return BAD_ARGS;
|
||||
@@ -418,8 +454,10 @@ public class X509Authentication
|
||||
{
|
||||
if (!isValid(context, certs[0]))
|
||||
{
|
||||
log.warn(LogManager.getHeader(context, "authenticate",
|
||||
"type=x509certificate, status=BAD_CREDENTIALS (not valid)"));
|
||||
log
|
||||
.warn(LogManager
|
||||
.getHeader(context, "authenticate",
|
||||
"type=x509certificate, status=BAD_CREDENTIALS (not valid)"));
|
||||
return BAD_CREDENTIALS;
|
||||
}
|
||||
|
||||
@@ -431,19 +469,20 @@ public class X509Authentication
|
||||
if (eperson == null)
|
||||
{
|
||||
// Cert is valid, but no record.
|
||||
if (email != null && canSelfRegister(context, request, null))
|
||||
if (email != null
|
||||
&& canSelfRegister(context, request, null))
|
||||
{
|
||||
// Register the new user automatically
|
||||
log.info(LogManager.getHeader(context,
|
||||
"autoregister", "from=x.509, email=" + email));
|
||||
log.info(LogManager.getHeader(context, "autoregister",
|
||||
"from=x.509, email=" + email));
|
||||
|
||||
// TEMPORARILY turn off authorisation
|
||||
context.setIgnoreAuthorization(true);
|
||||
eperson = EPerson.create(context);
|
||||
eperson.setEmail(email);
|
||||
eperson.setCanLogIn(true);
|
||||
AuthenticationManager.initEPerson(context,
|
||||
request, eperson);
|
||||
AuthenticationManager.initEPerson(context, request,
|
||||
eperson);
|
||||
eperson.update();
|
||||
context.commit();
|
||||
context.setIgnoreAuthorization(false);
|
||||
@@ -453,8 +492,10 @@ public class X509Authentication
|
||||
else
|
||||
{
|
||||
// No auto-registration for valid certs
|
||||
log.warn(LogManager.getHeader(context, "authenticate",
|
||||
"type=cert_but_no_record, cannot auto-register"));
|
||||
log
|
||||
.warn(LogManager
|
||||
.getHeader(context, "authenticate",
|
||||
"type=cert_but_no_record, cannot auto-register"));
|
||||
return NO_SUCH_USER;
|
||||
}
|
||||
}
|
||||
@@ -463,7 +504,8 @@ public class X509Authentication
|
||||
else if (!eperson.canLogIn())
|
||||
{
|
||||
log.warn(LogManager.getHeader(context, "authenticate",
|
||||
"type=x509certificate, email="+email+", canLogIn=false, rejecting."));
|
||||
"type=x509certificate, email=" + email
|
||||
+ ", canLogIn=false, rejecting."));
|
||||
return BAD_ARGS;
|
||||
}
|
||||
|
||||
@@ -487,32 +529,32 @@ public class X509Authentication
|
||||
|
||||
/**
|
||||
* Returns URL of password-login servlet.
|
||||
*
|
||||
*
|
||||
* @param context
|
||||
* DSpace context, will be modified (EPerson set) upon success.
|
||||
*
|
||||
* DSpace context, will be modified (EPerson set) upon success.
|
||||
*
|
||||
* @param request
|
||||
* The HTTP request that started this operation, or null if not applicable.
|
||||
*
|
||||
* The HTTP request that started this operation, or null if not
|
||||
* applicable.
|
||||
*
|
||||
* @param response
|
||||
* The HTTP response from the servlet method.
|
||||
*
|
||||
* The HTTP response from the servlet method.
|
||||
*
|
||||
* @return fully-qualified URL
|
||||
*/
|
||||
public String loginPageURL(Context context,
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response)
|
||||
public String loginPageURL(Context context, HttpServletRequest request,
|
||||
HttpServletResponse response)
|
||||
{
|
||||
return loginPageURL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns message key for title of the "login" page, to use
|
||||
* in a menu showing the choice of multiple login methods.
|
||||
*
|
||||
* Returns message key for title of the "login" page, to use in a menu
|
||||
* showing the choice of multiple login methods.
|
||||
*
|
||||
* @param context
|
||||
* DSpace context, will be modified (EPerson set) upon success.
|
||||
*
|
||||
* DSpace context, will be modified (EPerson set) upon success.
|
||||
*
|
||||
* @return Message key to look up in i18n message catalog.
|
||||
*/
|
||||
public String loginPageTitle(Context context)
|
||||
|
Reference in New Issue
Block a user