Java LDAP Authentication using username and password - authentication

I have a working code snippet by which i can authenticate a user by dn and password. My requirement is that the user will be entering his username(sAMAccountName) and I want to authenticate using sAMAccountName and password. How can I modify this code to achieve it?
String userName = "John P R-Asst General Manager";
String passWord = "asdfgh123";
String base ="OU=SOU,DC=example,DC=com";
String dn = "cn=" + userName + "," + base;
String ldapURL = "ldap://mdsdc3.example.com:389";
authEnv.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
authEnv.put(Context.PROVIDER_URL, ldapURL);
authEnv.put(Context.SECURITY_AUTHENTICATION, "simple");
authEnv.put(Context.SECURITY_PRINCIPAL, dn);
authEnv.put(Context.SECURITY_CREDENTIALS, password);
try {
DirContext authContext = new InitialDirContext(authEnv);
return true;
} catch (NamingException namEx) {
return false;
}

I hope this helps many of you.
You don't need to all user hierarchy with CN, DN, etc.
You can login just passing domain\user and password.
I've my code working as it is bellow:
try
{
// Set up the environment for creating the initial context
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_server:389");
//
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, "domain\\user"); //we have 2 \\ because it's a escape char
env.put(Context.SECURITY_CREDENTIALS, "test");
// Create the initial context
DirContext ctx = new InitialDirContext(env);
boolean result = ctx != null;
if(ctx != null)
ctx.close();
return result;
}
catch (Exception e)
{
return false;
}

Can you try to complete Context.PROVIDER_URL like this :
String ldapURL = "ldap://mdsdc3.example.com:389/DC=example,DC=com";
I Think it would be better to use GSSAPI, perhaps you can have a look here and here

Related

I can't reset Active Directory user password by the admin using C# .NET Core 6

I'm Trying to reset active directory user password by .NET core web API but always return below exception, even if I put very complex password
System.DirectoryServices.AccountManagement.PasswordException:
'The password does not meet the password policy requirements.
Check the minimum password length, password complexity
and password history requirements. (0x800708C5)'
I tried both ways (DirectoryEntry and the new one) but I get the same exception.
Here is my code, but I think
public bool ResetPassword(string oldPassword, string newPassword, string userNameI)
{
/* // set up domain context
PrincipalContext context = new PrincipalContext(ContextType.Domain, LDAP_PATH, userName, password);
if (context != null)
{
// find the user you want to delete
UserPrincipal user = UserPrincipal.FindByIdentity(context, IdentityType.SamAccountName, userNameI);
if (user != null)
{
user.Enabled = true;
user.ChangePassword(oldPassword,newPassword);
user.ExpirePasswordNow();
user.Save();
return true;
}
}*/
/*
var entry = new DirectoryEntry
{
Path = "LDAP://MyIP",
Username = userName,
Password = password
};
using (var searcher = new DirectorySearcher(entry))
{
searcher.Filter = "(SAMAccountName=" + userNameI + ")";
var result = searcher.FindOne();
var user = result.GetDirectoryEntry();
user.Invoke("ChangePassword", new object[] { oldPassword.Trim(), newPassword.Trim() });
user.CommitChanges();
return true;
}
*/
using (PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "LDAPIP", userName, password))
{
using (UserPrincipal user = UserPrincipal.FindByIdentity(ctx, IdentityType.SamAccountName, userNameI))
{
if (user != null)
{
user.ChangePassword(oldPassword, newPassword);
user.Save();
return true;
}
else
{
throw new Exception(string.Format("Username not found: {0}", userNameI));
}
}
return false;
}
}
The below code is working fine, the Old password is not needed in resetting password and account owner user name is needed :
public bool ResetPassword(string newPassword, string accountOwneruserName /*user name for the user that you want to change his password*/)
{
using (PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "17x.xx.xx.x" /*Active Directory server Ip*/, adminUserName, adminPassword ))
{
using (UserPrincipal user = UserPrincipal.FindByIdentity(ctx, IdentityType.SamAccountName, accountOwneruserName))
{
if (user != null)
{
user.SetPassword(newPassword.Trim());
user.Save();
return true;
}
else
{
throw new Exception(string.Format("Username not found: {0}", accountOwneruserName));
}
}
return false;
}
}

In 389 DS if LDAP admin changes password then it replaces last password from history

Under 389 DS we have setup as password policy to save last 5 password in history, so that those can not be used.
We have java application under which password is changed as LDAP administrator. But when password is changed instead of adding new password in password history, it replaces old password with new password. This enables user to change password to the password used in past.
Eg.
User sets password to abc, then history is {abc}.
User changes password to efg, then history is { abc, efg }
Administrator changes password to xyz, then history is expected to be {xyz, abc, efg} but it is {xyz, efg}.
private static void changePasswordAsAdmin(String userDn, String sNewPassword) throws Exception {
System.out.println("Setting Password:" + sNewPassword);
LDAPConnection connection = new LDAPConnection(new MySSLSocketFactory(), "Ldap host", 636, "adminCN", "adminPwd" );
final List mods = new ArrayList();
final Modification item = new Modification(ModificationType.REPLACE, "userPassword", sNewPassword);
mods.add(item);
final ModifyRequest request = new ModifyRequest(userDn, mods);
try {
LDAPResult result = connection.modify(request);
System.out.println(result.getResultString());
}
catch(Exception e) {
System.out.println(e.getMessage());
throw e;
}
}
private static void changePasswordAsUser(String userDn, String oldPassword, String newPassword) throws Exception {
System.out.println("Setting Password, old password: " + oldPassword + ", new password: " + newPassword);
LDAPConnection connection = new LDAPConnection(new MySSLSocketFactory(), "ldapHost", 636, "userDn", "oldPassword");
final List mods = new ArrayList();
final Modification item = new Modification(ModificationType.REPLACE, "userPassword", newPassword);
mods.add(item);
final ModifyRequest request = new ModifyRequest(userDn, mods);
try {
LDAPResult result = connection.modify(request);
System.out.println(result.getResultString());
}
catch(Exception e) {
System.out.println(e.getMessage());
}
}
This is a known bug which should be fixed according to : https://pagure.io/389-ds-base/issue/48813
Try to update you version of 389DS

How to fetch list of all distinguished names (DNs) from LDAP server using JNDI?

I wish to fetch the list of all distinguised names (DNs) from LDAP server using JNDI. I am able to fetch the base DN using following code:
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://" + ldapServer + ":" + ldapPort);
env.put(Context.REFERRAL, "follow");
if(sslEnabled) {
env.put("java.naming.ldap.factory.socket", TrustAllSSLSocketFactory.class.getName());
}
// Create the LDAP context
LdapContext context = new InitialLdapContext(env, null);
String base = "";
String filter = "(objectclass=*)";
SearchControls controls = new SearchControls();
controls.setSearchScope(SearchControls.OBJECT_SCOPE);
// Search the directory for retrieving namingContexts attribute
// which contains all the base DNs values
NamingEnumeration<SearchResult> results = context.search(base, filter, controls);
List<String> namingContextsList = new ArrayList<String>();
// Process attributes
if(results.hasMore()) {
Attributes attrs = results.next().getAttributes();
if (attrs != null) {
Attribute namingContexts = attrs.get("namingContexts");
NamingEnumeration enumeration = namingContexts.getAll();
while(enumeration.hasMore()) {
namingContextsList.add((String) enumeration.next());
}
}
}
System.out.println(namingContextsList);
Could you please help in fetching all the possible DNs in similar manner or other?
Just change OBJECT_SCOPE to SUBTREE_SCOPE.
This is all documented, you know.
Following code works for me: (Note that you need to provide credentials to perform this operation and the attribute name is "distinguishedName")
String ldapServer = "192.168.0.11";
String ldapPort = "389";
String principal = "CN=user";
String password = "password";
Hashtable<String,String> environment = new Hashtable<String,String>();
environment.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
environment.put(Context.PROVIDER_URL, "ldap://" + ldapServer + ":" + ldapPort);
environment.put(Context.SECURITY_AUTHENTICATION, "simple");
environment.put(Context.SECURITY_PRINCIPAL, principal);
environment.put(Context.SECURITY_CREDENTIALS, password);
environment.put(Context.REFERRAL, "follow");
environment.put("com.sun.jndi.ldap.connect.pool", "true");
// Create the LDAP context
LdapContext context = new InitialLdapContext(environment, null);
String baseDN = "DC=domain,DC=com" // Put your base DN here
String filter = "(objectclass=*)";
SearchControls controls = new SearchControls();
controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
//controls.setSearchScope(SearchControls.ONELEVEL_SCOPE); // Use this for first level DNs only
NamingEnumeration<SearchResult> results = context.search(baseDN, filter, controls);
List<String> searchDNsList = new ArrayList<String>();
try {
// Process attributes
while(results.hasMore()) {
Attributes attrs = results.next().getAttributes();
if (attrs != null) {
Attribute distinguisedNames = attrs.get("distinguishedName");
if(distinguisedNames != null) {
NamingEnumeration enumeration = distinguisedNames.getAll();
while(enumeration.hasMore()) {
String searchDN = (String) enumeration.next();
searchDNsList.add(searchDN);
}
}
}
}
} catch(Exception ex) {
ex.printStackTrace();
}
System.out.println(searchDNsList);

SpringLdap 2 over ldaps

I've followed an interesting webinar about springLdap and I'm planning to migrate my current Ldap interface to SpringLdap. However I haven't seen any easy way to connect to an LDAPS server. In my current implementation I had to do something like:
String nextToken = stCc.nextToken();
Properties envP = initializeEnv(nextToken, userPassword);
try
{
LdapContext ctx = new InitialLdapContext(envP, null);
//System.out.println(nextToken + " successfully validation");
return ctx;
}
and
private Properties initializeEnv(String userName, String userPassword) throws IOException
{
Properties envP = new Properties();
envP.setProperty(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
envP.put(Context.PROVIDER_URL, (String) properties.get("ldap.server.url"));
if (userName != null)
envP.setProperty(Context.SECURITY_PRINCIPAL, userName);
if (userPassword != null)
envP.setProperty(Context.SECURITY_CREDENTIALS, userPassword);
envP.setProperty(Context.SECURITY_AUTHENTICATION, "simple");
envP.setProperty("java.naming.security.protocol", "ssl");
envP.setProperty("com.sun.jndi.ldap.connect.pool", "true");
envP.put("java.naming.ldap.factory.socket", "org.mycompany.ldap.CustSSLSocketFactory");
return envP;
}
and more:
public EmblSSLSocketFactory()
{
try
{
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, new TrustManager[]
{
new DummyTrustmanager()
}, new SecureRandom());
socketFactory = ctx.getSocketFactory();
}
catch (Exception ex)
{
ex.printStackTrace(System.err); /* handle exception */
}
}
Which is the equivalent (and possibly easier) way to do such authentication over TLS with SPRING-LDAP 2?
Thanks

apache directory server authentication....tell me where i went wrong.....the code which i used.........is listed below

import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
public class AdvancedBindDemo2 {
public static void main(String[] args) throws NamingException {
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://localhost:10389");
env.put(Context.SECURITY_AUTHENTICATION, "simple");
String uid = "user1";//supplying userid manually
String password = "secret";
DirContext ctx = null;
try {
// Step 1: Bind anonymously
ctx = new InitialDirContext(env);
// Step 2: Search the directory
String base = "ou=users,ou=system";//base
String filter = "(objectClass=*)";
SearchControls ctls = new SearchControls();
ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
ctls.setReturningAttributes(new String[0]);
ctls.setReturningObjFlag(true);
NamingEnumeration enm = ctx.search(base, filter, new String[] { uid }, ctls);
String dn = null;
if (enm.hasMore()) {
SearchResult result = (SearchResult) enm.next();
dn = result.getNameInNamespace();
System.out.println("dn: "+dn);
}
if (dn == null || enm.hasMore()) {
// uid not found or not unique
throw new NamingException("Authentication failed");
}
// Step 3: Bind with found DN and given password
ctx.addToEnvironment(Context.SECURITY_PRINCIPAL, dn);
ctx.addToEnvironment(Context.SECURITY_CREDENTIALS, password);
// Perform a lookup in order to force a bind operation with JNDI
ctx.lookup(dn);
System.out.println("Authentication successful");
} catch (NamingException e) {
System.out.println(e.getMessage());
} finally {
ctx.close();
}
}
}
The above program will authenticate a user as well as it will show the dn for the user. And here I am using Apache ds in eclipse. From the above program I am trying to get the dn: for user1 who's base is ou=users,ou=system but I am not getting the dn: for user1.
Assuming that user1's DN is "uid=user1,ou=users,ou=system" then your filter should be changed to "(uid=user1)".
Note also that the attribute you are requesting is wrong it should be - new String[] { "uid" }