Using java to authenticate LDAP user throught Spring security - java

I'm having some troubles with Spring security and Adam Ldap.
Here you can see my post without useful answer.
I thought to use my authenticate java code to set spring environment. This is my java code:
#Override
public void isAuthenticated(String username, String password) throws LdapException{
if (databaseMatlabClientServices.getByUsersEnabled(username)== null)
throw new LdapException("User doesn't exist into DART database. Please contact the administrator!");
String dn="";;
//First query to retriev DN
Hashtable<String, Object> ldapEnv = new Hashtable<String, Object>();
ldapEnv.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
ldapEnv.put(Context.PROVIDER_URL, env.getRequiredProperty(PROPERTY_NAME_LDAP_URL));
//Without authentication ldapEnv.put(Context.SECURITY_AUTHENTICATION, "none");
//With authentication to access to LDAP server
ldapEnv.put(Context.SECURITY_AUTHENTICATION, "simple");
ldapEnv.put(Context.SECURITY_PRINCIPAL, env.getRequiredProperty(PROPERTY_NAME_LDAP_NAME));
ldapEnv.put(Context.SECURITY_CREDENTIALS, env.getRequiredProperty(PROPERTY_NAME_LDAP_PASSWORD));
String[] returnAttribute = {"dn"};
DirContext ctx = null;
NamingEnumeration<SearchResult> results = null;
try {
ctx = new InitialDirContext(ldapEnv);
SearchControls controls = new SearchControls();
controls.setReturningAttributes(returnAttribute);
controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
// without authentication on local server String filter = "uid=" + username ;
String filter = "CN=" + username ;
results = ctx.search(env.getRequiredProperty(PROPERTY_NAME_LDAP_USERSEARCHBASE), filter, controls);
if (results.hasMore())
dn = results.nextElement().getNameInNamespace();
else
throw new LdapException("Wrong username. Please retry!");
} catch (NamingException e) {
throw new LdapException(e);
} finally {
try{
if (results != null)
results.close();
if (ctx != null)
ctx.close();
}catch(Exception e){
throw new LdapException(e);
}
}
//Second query to try to access with obtained Dn and given password
Hashtable<String, Object> authEnv = new Hashtable<String, Object>();
authEnv.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
authEnv.put(Context.PROVIDER_URL, env.getRequiredProperty(PROPERTY_NAME_LDAP_URL));
authEnv.put(Context.SECURITY_AUTHENTICATION, "simple");
authEnv.put(Context.SECURITY_PRINCIPAL, dn);
authEnv.put(Context.SECURITY_CREDENTIALS, password);
DirContext ctx2 = null;
try {
ctx2 = new InitialDirContext(authEnv);
} catch (AuthenticationException authEx) {
throw new LdapException("Authentication error. Password was wrong");
} catch(Exception e){
throw new LdapException(e);
}finally {
try{
if (ctx2 != null)
ctx2.close();
}catch(Exception e){
throw new LdapException(e);
}
}
}
This code recognize if user and password exists on Ldap System.
In Spring I have
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception{
LdapContextSource contextSource = new LdapContextSource();
contextSource.setUrl("ldaps://vdap.floal:636/");
contextSource.setBase("DC=fg,DC=local");
contextSource.setReferral("follow");
contextSource.setUserDn("CN=A00XXX32,CN=Administration,CN=fdam,DC=fg,DC=local");
contextSource.setPassword(password);
contextSource.afterPropertiesSet();
LdapAuthenticationProviderConfigurer<AuthenticationManagerBuilder> ldapAuthenticationProviderConfigurer = auth.ldapAuthentication();
ldapAuthenticationProviderConfigurer
.contextSource(contextSource)
.userSearchBase("CN=fdam")
.userSearchFilter(env.getRequiredProperty("(CN={0})"))
.ldapAuthoritiesPopulator(myAuthPopulator);
}
and my authorities populator to give role information from database
#Service("myAuthPopulator")
public class MyAuthoritiesPopulator implements LdapAuthoritiesPopulator {
#Autowired
private UserServices userServices;
static final Logger LOG = LoggerFactory.getLogger(MyAuthoritiesPopulator.class);
#Transactional(readOnly=true)
#Override
public Collection<? extends GrantedAuthority> getGrantedAuthorities(DirContextOperations userData, String username) {
Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();
try{
com.domain.User user = userServices.getByUsersEnabled(username);
if (user==null){
LOG.error("Threw exception in MyAuthoritiesPopulator::getGrantedAuthorities : User doesn't exist into DART database" );
}
else{
//Use this if a user can have different roles
// for(Role role : user.getRole()) {
// authorities.add(new SimpleGrantedAuthority(role.getRole()));
// }
authorities.add(new SimpleGrantedAuthority(user.getRole().getRole()));
return authorities;
}
}catch(Exception e){
LOG.error("Threw exception in MyAuthoritiesPopulator::getGrantedAuthorities : " + ErrorExceptionBuilder.buildErrorResponse(e)); }
return authorities;
}
}
Is it possible to merge these two strategy to fix my problem with Ldap authentication, so use java code to set Spring? Thanks

Related

Oracle LDAP Move User to a Different OU with Java

I'm looking for a way to change the OU of a user in my Oracle LDAP, using Java. So far I found only the DirContext.rename method, but that results in a corrupted LDAP entry. Below is the code I tried.
Hashtable<String, String> environment = new Hashtable<String, String>();
environment.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
environment.put(Context.SECURITY_AUTHENTICATION, "simple");
environment.put(Context.REFERRAL, "follow");
environment.put(Context.PROVIDER_URL, "ldap://localhost:10389");
environment.put(Context.SECURITY_PRINCIPAL, "uid=admin,ou=system");
environment.put(Context.SECURITY_CREDENTIALS, "admin");
DirContext ctx = null;
try {
ctx = new InitialDirContext(environment);
String oldCn = "uid=wso21,ou=wso2,ou=Users,dc=WSO2,dc=ORG";
String newCn = "uid=wso21,ou=vodafone,ou=Users,dc=WSO2,dc=ORG";
ctx.rename(oldCn, newCn);
} catch (NamingException e) {
e.printStackTrace();
} finally {
try {
ctx.close();
} catch (NamingException e) {
e.printStackTrace();
}
}
After this is executed, the moved entry is there but no attributes are present.
TIA!
Please modify try Block:
try {
ctx = new InitialDirContext(environment);
String oldDn = "uid=wso21,ou=wso2,ou=Users,dc=WSO2,dc=ORG";
String newDn = "uid=wso21,ou=vodafone,ou=Users,dc=WSO2,dc=ORG";
String newCn="newCN";
String newSn="newSN";
Attributes attrs = ctx.getAttributes(oldDn);
ctx.rename(oldDn,newDn);
attrs.remove("cn");
attrs.remove("sn");
//if needed remove more attributes
attrs.put("cn",newCn);
attrs.put("sn",newSn);
//if needed add more attributes
ctx.modifyAttributes(newDn, DirContext.ADD_ATTRIBUTE, attrs);
}

[B cannot be cast to java.lang.String in LDAP Search

When i am trying to search password of user from ldap server this
below error displayed
In this code its doesn't return user Password in String. It throws
java.lang.ClassCastException: [B cannot be cast to java.lang.String
Code:
public class selectEntry {
DirContext ldapContext = null;
public selectEntry() {
try {
Hashtable<String, String> environment = new Hashtable<String, String>();
environment.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
environment.put(Context.PROVIDER_URL, url);
environment.put(Context.SECURITY_AUTHENTICATION, conntype);
environment.put(Context.SECURITY_PRINCIPAL, AdminDn);
environment.put(Context.SECURITY_CREDENTIALS, password);
ldapContext = new InitialDirContext(environment);
System.out.println("Bind successful");
} catch (Exception exception) {
exception.printStackTrace();
}
}
public void getEntry() {
try {
SearchControls searcCon = new SearchControls();
searcCon.setSearchScope(SearchControls.SUBTREE_SCOPE);
NamingEnumeration results
= ldapContext.search("uid=aruhat.aruhat,ou=openzki,dc=aruhat,dc=co,dc=in", "(uid=aruhat.aruhat)", searcCon);
if (results != null) {
while (results.hasMore()) {
SearchResult res = (SearchResult) results.next();
Attributes atbs = res.getAttributes();
Attribute atb = atbs.get("userPassword");
String name = (String) atb.get();
System.out.println("Name is :=> " + name);
}
} else {
System.out.println("fail");
}
} catch (Exception e) {
System.out.println("Exception Type:=> "+e);
System.out.println("Exception Message:=> "+e.getMessage());
e.printStackTrace();
}
}
public static void main(String[] args) {
new selectEntry().getEntry();
}
}
LDAP passwords are stored as hashes, not strings. The attribute value is returned as a byte[], as the exception says.
However you don't have any good reason for obtaining the hashed password attribute in the first place. It won't do you any good. Review your requirement. You should be binding as the user using the old password to test whether it's valid, not trying to read the password, which you won't get.
Retreiving binary attributes requires the ;binary suffix, e.g., userCertificate;binary. Then you have the Attribute object. Invoke
byte[] bytes = (byte[]) attr.get()
and you are done. Don't work with toString() or (String) cast.

Java LDAP returning always a single value instead of a list

I want to query my ldap to give me all users where sn contains a specific value (maier). However I always get a single result.
public LdapContext getLdapContext(){
LdapContext ctx = null;
try{
Hashtable<String, String> env = new Hashtable<String, String>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://ldap.url:389");
ctx = new InitialLdapContext(env, null);
System.out.println("Connection Successful.");
}catch(NamingException nex){
System.out.println("LDAP Connection: FAILED");
nex.printStackTrace();
}
return ctx;
}
private User getUserBasicAttributes(String username, LdapContext ctx) {
User user=null;
try {
SearchControls constraints = new SearchControls();
constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
String[] attrIDs = { "distinguishedName",
"sn",
"givenname",
"mail",
"telephonenumber"};
constraints.setReturningAttributes(attrIDs);
constraints.setCountLimit(200);
NamingEnumeration answer = ctx.search("DC=myDc,DC=com", "sn=*maier*", constraints);
if (answer.hasMore()) {
Attributes attrs = ((SearchResult) answer.next()).getAttributes();
System.out.println("distinguishedName "+ attrs.get("distinguishedName"));
System.out.println("givenname "+ attrs.get("givenname"));
System.out.println("sn "+ attrs.get("sn"));
System.out.println("mail "+ attrs.get("mail"));
System.out.println("telephonenumber "+ attrs.get("telephonenumber"));
}else{
throw new Exception("Invalid User");
}
} catch (Exception ex) {
ex.printStackTrace();
}
return user;
}
Did I do anything wrong?
You're not looping, so of course you're only getting one result. Change if (answer.hasMore()) to while (answer.hasMore()).

Async LDAP authentication with play framework

I am building a web application with the Play Framework. To authenticate the users of my application I use LDAP. I am new to the play framework and async programming. I wanted the LDAP authentication to be async and do not block my app. So I created a Promise and put the LDAP authentication code in it. My code is working but I am not sure if it is Async or not. How can I verify that it is really Async. Here is my code
public class ActiveDirectoryServices {
public static final String ldapURL = Play.application().configuration().getString("ActiveDirectory.url");
public static final String domainName = Play.application().configuration().getString("ActoveDirectory.DomainName");
public static final int timeout = Play.application().configuration().getInt("ActoveDirectory.timeout");
private static final String account = "account";
private static final String pass = "password";
public static Promise<Boolean> authenticate(String username, String password) throws AuthenticationException, CommunicationException, NamingException{
Hashtable<String, String> env = new Hashtable<String,String>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put("com.sun.jndi.ldap.connect.timeout", ""+(timeout*1000));
env.put(Context.PROVIDER_URL, ldapURL);
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, username+domainName);
env.put(Context.SECURITY_CREDENTIALS, password);
DirContext authContext = null;
authContext = new InitialDirContext(env);
return Promise.pure(Boolean.TRUE);
}
}
Then in a controller I use the above code as following:
try {
Promise<Boolean> promiseActiveDirectoryCheck = ActiveDirectoryServices.authenticate(userName, password);
return promiseActiveDirectoryCheck.flatMap(response -> {
if(response){
return Promise.pure(ok("access granted"));
}
});
}catch (AuthenticationException exp) {
return Promise.pure(ok("access denied"));
}catch (CommunicationException exp) {
return Promise.pure(ok("The active directory server is not reachable"));
}catch (NamingException exp) {
return Promise.pure(ok("active directory domain name does not exist"));
}

Java AD Authentication across Trusted Domains

I am trying to implement Active Directory authentication in Java which will be ran from a Linux machine. Our AD set-up will consist of multiple servers that share trust relationships with one another so for our test environment we have two domain controllers:
test1.ad1.foo.com who trusts test2.ad2.bar.com.
Using the code below I can successfully authenticate a user from test1 but not on test2:
public class ADDetailsProvider implements ResultSetProvider {
private String domain;
private String user;
private String password;
public ADDetailsProvider(String user, String password) {
//extract domain name
if (user.contains("\\")) {
this.user = user.substring((user.lastIndexOf("\\") + 1), user.length());
this.domain = user.substring(0, user.lastIndexOf("\\"));
} else {
this.user = user;
this.domain = "";
}
this.password = password;
}
/* Test from the command line */
public static void main (String[] argv) throws SQLException {
ResultSetProvider res = processADLogin(argv[0], argv[1]);
ResultSet results = null;
res.assignRowValues(results, 0);
System.out.println(argv[0] + " " + argv[1]);
}
public boolean assignRowValues(ResultSet results, int currentRow)
throws SQLException
{
// Only want a single row
if (currentRow >= 1) return false;
try {
ADAuthenticator adAuth = new ADAuthenticator();
LdapContext ldapCtx = adAuth.authenticate(this.domain, this.user, this.password);
NamingEnumeration userDetails = adAuth.getUserDetails(ldapCtx, this.user);
// Fill the result set (throws SQLException).
while (userDetails.hasMoreElements()) {
Attribute attr = (Attribute)userDetails.next();
results.updateString(attr.getID(), attr.get().toString());
}
results.updateInt("authenticated", 1);
return true;
} catch (FileNotFoundException fnf) {
Logger.getAnonymousLogger().log(Level.WARNING,
"Caught File Not Found Exception trying to read cris_authentication.properties");
results.updateInt("authenticated", 0);
return false;
} catch (IOException ioe) {
Logger.getAnonymousLogger().log(Level.WARNING,
"Caught IO Excpetion processing login");
results.updateInt("authenticated", 0);
return false;
} catch (AuthenticationException aex) {
Logger.getAnonymousLogger().log(Level.WARNING,
"Caught Authentication Exception attempting to bind to LDAP for [{0}]",
this.user);
results.updateInt("authenticated", 0);
return true;
} catch (NamingException ne) {
Logger.getAnonymousLogger().log(Level.WARNING,
"Caught Naming Exception performing user search or LDAP bind for [{0}]",
this.user);
results.updateInt("authenticated", 0);
return true;
}
}
public void close() {
// nothing needed here
}
/**
* This method is called via a Postgres function binding to access the
* functionality provided by this class.
*/
public static ResultSetProvider processADLogin(String user, String password) {
return new ADDetailsProvider(user, password);
}
}
public class ADAuthenticator {
public ADAuthenticator()
throws FileNotFoundException, IOException {
Properties props = new Properties();
InputStream inStream = this.getClass().getClassLoader().
getResourceAsStream("com/bar/foo/ad/authentication.properties");
props.load(inStream);
this.domain = props.getProperty("ldap.domain");
inStream.close();
}
public LdapContext authenticate(String domain, String user, String pass)
throws AuthenticationException, NamingException, IOException {
Hashtable env = new Hashtable();
this.domain = domain;
env.put(Context.INITIAL_CONTEXT_FACTORY, com.sun.jndi.ldap.LdapCtxFactory);
env.put(Context.PROVIDER_URL, "ldap://" + test1.ad1.foo.com + ":" + 3268);
env.put(Context.SECURITY_AUTHENTICATION, simple);
env.put(Context.REFERRAL, follow);
env.put(Context.SECURITY_PRINCIPAL, (domain + "\\" + user));
env.put(Context.SECURITY_CREDENTIALS, pass);
// Bind using specified username and password
LdapContext ldapCtx = new InitialLdapContext(env, null);
return ldapCtx;
}
public NamingEnumeration getUserDetails(LdapContext ldapCtx, String user)
throws NamingException {
// List of attributes to return from LDAP query
String returnAttributes[] = {"ou", "sAMAccountName", "givenName", "sn", "memberOf"};
//Create the search controls
SearchControls searchCtls = new SearchControls();
searchCtls.setReturningAttributes(returnAttributes);
//Specify the search scope
searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
// Specify the user to search against
String searchFilter = "(&(objectClass=*)(sAMAccountName=" + user + "))";
//Perform the search
NamingEnumeration answer = ldapCtx.search("dc=dev4,dc=dbt,dc=ukhealth,dc=local", searchFilter, searchCtls);
// Only care about the first tuple
Attributes userAttributes = ((SearchResult)answer.next()).getAttributes();
if (userAttributes.size() <= 0) throw new NamingException();
return (NamingEnumeration) userAttributes.getAll();
}
From what I understand of the trust relationship, if trust1 receives a login attempt for a user in trust2, then it should forward the login attempt on to it and it works this out from the user's domain name.
Is this correct or am I missing something or is this not possible using the method above?
--EDIT--
The stack trace from the LDAP bind is
{java.naming.provider.url=ldap://test1.ad1.foo.com:3268, java.naming.factory.initial=com.sun.jndi.ldap.LdapCtxFactory, java.naming.security.authentication=simple, java.naming.referral=follow}
30-Oct-2012 13:16:02
ADDetailsProvider assignRowValues
WARNING: Caught Authentication Exception attempting to bind to LDAP for [trusttest]
Auth error is [LDAP: error code 49 - 80090308: LdapErr: DSID-0C0903A9, comment: AcceptSecurityContext error, data 52e, v1db0]
As far as I know, you should set Context.REFERRAL to true.
Is this what you meant in your code?
In addition, when I switched to GSSAPI/Kerberos,
I defined trust relationships between the kerberos realms and it worked for me.

Categories