/** * The contents of this file are subject to the license and copyright * detailed in the LICENSE and NOTICE files at the root of the source * tree and available online at * * http://www.dspace.org/license/ */ package org.dspace.authenticate; import static org.dspace.eperson.service.EPersonService.MD_PHONE; import java.io.IOException; import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import javax.naming.NamingEnumeration; import javax.naming.NamingException; import javax.naming.directory.Attribute; import javax.naming.directory.Attributes; import javax.naming.directory.BasicAttribute; import javax.naming.directory.BasicAttributes; import javax.naming.directory.SearchControls; import javax.naming.directory.SearchResult; import javax.naming.ldap.InitialLdapContext; import javax.naming.ldap.LdapContext; import javax.naming.ldap.StartTlsRequest; import javax.naming.ldap.StartTlsResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.Logger; import org.dspace.authenticate.factory.AuthenticateServiceFactory; import org.dspace.authenticate.service.AuthenticationService; import org.dspace.authorize.AuthorizeException; import org.dspace.core.Context; import org.dspace.core.LogHelper; import org.dspace.eperson.EPerson; import org.dspace.eperson.Group; import org.dspace.eperson.factory.EPersonServiceFactory; import org.dspace.eperson.service.EPersonService; import org.dspace.eperson.service.GroupService; import org.dspace.services.ConfigurationService; import org.dspace.services.factory.DSpaceServicesFactory; /** * This combined LDAP authentication method supersedes both the 'LDAPAuthentication' * and the 'LDAPHierarchicalAuthentication' methods. It's capable of both: *
EPerson
. If an EPerson
is found it is
* set in the Context
that was passed.
*
* @param context
* DSpace context, will be modified (ePerson set) upon success.
*
* @param username
* Username (or email address) when method is explicit. Use null for
* implicit method.
*
* @param password
* Password for explicit auth, or null for implicit method.
*
* @param realm
* Realm is an extra parameter used by some authentication methods, leave null if
* not applicable.
*
* @param request
* The HTTP request that started this operation, or null if not applicable.
*
* @return One of:
* SUCCESS, BAD_CREDENTIALS, CERT_REQUIRED, NO_SUCH_USER, BAD_ARGS
* Meaning:
*
SUCCESS - authenticated OK.
*
BAD_CREDENTIALS - user exists, but credentials (e.g. passwd) don't match
*
CERT_REQUIRED - not allowed to login this way without X.509 cert.
*
NO_SUCH_USER - user not found using this method.
*
BAD_ARGS - user/pw not appropriate for this method
*/
@Override
public int authenticate(Context context,
String netid,
String password,
String realm,
HttpServletRequest request)
throws SQLException {
log.info(LogHelper.getHeader(context, "auth", "attempting trivial auth of user=" + netid));
// Skip out when no netid or password is given.
if (netid == null || password == null) {
return BAD_ARGS;
}
// Locate the eperson
EPerson eperson = null;
try {
eperson = ePersonService.findByNetid(context, netid.toLowerCase());
} catch (SQLException e) {
// ignore
}
SpeakerToLDAP ldap = new SpeakerToLDAP(log);
// Get the DN of the user
boolean anonymousSearch = configurationService.getBooleanProperty("authentication-ldap.search.anonymous");
String adminUser = configurationService.getProperty("authentication-ldap.search.user");
String adminPassword = configurationService.getProperty("authentication-ldap.search.password");
String objectContext = configurationService.getProperty("authentication-ldap.object_context");
String idField = configurationService.getProperty("authentication-ldap.id_field");
String dn = "";
// If adminUser is blank and anonymous search is not allowed, then we can't search so construct the DN
// instead of searching it
if ((StringUtils.isBlank(adminUser) || StringUtils.isBlank(adminPassword)) && !anonymousSearch) {
dn = idField + "=" + netid + "," + objectContext;
} else {
dn = ldap.getDNOfUser(adminUser, adminPassword, context, netid);
}
// Check a DN was found
if ((dn == null) || (dn.trim().equals(""))) {
log.info(LogHelper
.getHeader(context, "failed_login", "no DN found for user " + netid));
return BAD_CREDENTIALS;
}
// if they entered a netid that matches an eperson
if (eperson != null) {
// e-mail address corresponds to active account
if (eperson.getRequireCertificate()) {
return CERT_REQUIRED;
} else if (!eperson.canLogIn()) {
return BAD_ARGS;
}
if (ldap.ldapAuthenticate(dn, password, context)) {
context.setCurrentUser(eperson);
request.setAttribute(LDAP_AUTHENTICATED, true);
// assign user to groups based on ldap dn
assignGroups(dn, ldap.ldapGroup, context);
log.info(LogHelper
.getHeader(context, "authenticate", "type=ldap"));
return SUCCESS;
} else {
return BAD_CREDENTIALS;
}
} else {
// the user does not already exist so try and authenticate them
// with ldap and create an eperson for them
if (ldap.ldapAuthenticate(dn, password, context)) {
// Register the new user automatically
log.info(LogHelper.getHeader(context,
"autoregister", "netid=" + netid));
String email = ldap.ldapEmail;
// Check if we were able to determine an email address from LDAP
if (StringUtils.isEmpty(email)) {
// If no email, check if we have a "netid_email_domain". If so, append it to the netid to create
// email
if (configurationService.hasProperty("authentication-ldap.netid_email_domain")) {
email = netid + configurationService.getProperty("authentication-ldap.netid_email_domain");
} else {
// We don't have a valid email address. We'll default it to 'netid' but log a warning
log.warn(LogHelper.getHeader(context, "autoregister",
"Unable to locate email address for account '" + netid + "', so" +
" it has been set to '" + netid + "'. " +
"Please check the LDAP 'email_field' OR consider " +
"configuring 'netid_email_domain'."));
email = netid;
}
}
if (StringUtils.isNotEmpty(email)) {
try {
eperson = ePersonService.findByEmail(context, email);
if (eperson != null) {
log.info(LogHelper.getHeader(context,
"type=ldap-login", "type=ldap_but_already_email"));
context.turnOffAuthorisationSystem();
eperson.setNetid(netid.toLowerCase());
ePersonService.update(context, eperson);
context.dispatchEvents();
context.restoreAuthSystemState();
context.setCurrentUser(eperson);
request.setAttribute(LDAP_AUTHENTICATED, true);
// assign user to groups based on ldap dn
assignGroups(dn, ldap.ldapGroup, context);
return SUCCESS;
} else {
if (canSelfRegister(context, request, netid)) {
// TEMPORARILY turn off authorisation
try {
context.turnOffAuthorisationSystem();
eperson = ePersonService.create(context);
if (StringUtils.isNotEmpty(email)) {
eperson.setEmail(email);
}
if (StringUtils.isNotEmpty(ldap.ldapGivenName)) {
eperson.setFirstName(context, ldap.ldapGivenName);
}
if (StringUtils.isNotEmpty(ldap.ldapSurname)) {
eperson.setLastName(context, ldap.ldapSurname);
}
if (StringUtils.isNotEmpty(ldap.ldapPhone)) {
ePersonService.setMetadataSingleValue(context, eperson,
MD_PHONE, ldap.ldapPhone, null);
}
eperson.setNetid(netid.toLowerCase());
eperson.setCanLogIn(true);
authenticationService.initEPerson(context, request, eperson);
ePersonService.update(context, eperson);
context.dispatchEvents();
context.setCurrentUser(eperson);
request.setAttribute(LDAP_AUTHENTICATED, true);
// assign user to groups based on ldap dn
assignGroups(dn, ldap.ldapGroup, context);
} catch (AuthorizeException e) {
return NO_SUCH_USER;
} finally {
context.restoreAuthSystemState();
}
log.info(LogHelper.getHeader(context, "authenticate",
"type=ldap-login, created ePerson"));
return SUCCESS;
} else {
// No auto-registration for valid certs
log.info(LogHelper.getHeader(context,
"failed_login", "type=ldap_but_no_record"));
return NO_SUCH_USER;
}
}
} catch (AuthorizeException e) {
eperson = null;
} finally {
context.restoreAuthSystemState();
}
}
}
}
return BAD_ARGS;
}
/**
* Internal class to manage LDAP query and results, mainly
* because there are multiple values to return.
*/
private static class SpeakerToLDAP {
private Logger log = null;
protected String ldapEmail = null;
protected String ldapGivenName = null;
protected String ldapSurname = null;
protected String ldapPhone = null;
protected ArrayList