LDAP user password authentication using JNDI - java

public static void main(String[] args)
{
String INITCTX = "com.sun.jndi.ldap.LdapCtxFactory";
String MY_HOST = "ldap://Localhost:1389";
String MGR_DN = "cn=John,ou=Users,o=IT,dc=QuizPortal";
String MGR_PW = "password";
//Identify service provider to use
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, INITCTX);
env.put(Context.PROVIDER_URL, MY_HOST);
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, MGR_DN);
env.put(Context.SECURITY_CREDENTIALS, MGR_PW);
try
{
// Create the initial directory context
InitialDirContext initialContext = new InitialDirContext(env);
System.out.println("Context Sucessfully Initialized");
}
catch(Exception e)
{
System.err.println(e);
}
}
I would like to ask when I set the MGR_DN = "cn=John,ou=Users,o=IT,dc=QuizPortal" to MGR_DN = "uid=103,ou=Users,o=IT,dc=QuizPortal". Basically changing from cn to uid, I would encounter an error
javax.naming.AuthenticationException: [LDAP: error code 49 - Invalid Credentials]
I am authenticated when is specified as cn=John but not uid=103. Am I not allowed to specify by uid?

If you don't know the exact DN in advance, you should do a search in the LDAP directory first. This can be done more or less like this (make sure you catch the relevant exceptions):
Properties env = new Properties();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, ldapServerUrl);
env.put(Context.SECURITY_AUTHENTICATION, "none");
SearchControls searchCtrls = new SearchControls();
searchCtrls.setReturningAttributes(new String[] {});
searchCtrls.setSearchScope(SearchControls.SUBTREE_SCOPE);
String filter = "(&(cn=" + identifier + "))";
DirContext ctx = null;
ctx = new InitialDirContext(env);
NamingEnumeration<SearchResult> answer = ctx.search(
ldapBaseDN, filter, searchCtrls);
String fullDN = null;
if (answer.hasMore()) {
fullDN = answer.next().getNameInNamespace();
ctx.close();
ctx = null;
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, fullDN);
env.put(Context.SECURITY_CREDENTIALS, password);
ctx = new InitialDirContext(env);
return true;
}
// Exception otherwise ...
Here, the search filter is "(&(cn=" + identifier + "))" (so, for example (&(cn=John))), but you could use the uid instead. Uniqueness of the results depends on the configuration of the LDAP server. The base DN also depends on the way it's set up (it could be ou=Users,o=IT,dc=QuizPortal in your example).

You have to specify the DN or distinguished name. That's the name the user is bound as in the directory. You can't just select any chain of attributes. If your users are bound via the 'cn' attribute then only the 'cn' attribute is part of the DN.

It looks like a server configuration issue. Here's a similar problem including a solution. Basically you'll have to specify whether to use uid or cn for authentication in ldap-authentication.properties.

Related

LDAP error code 49 AcceptSecurityContext error data 52e v2580 even with the correct credentials

I'm adding LDAP authentication to the spring-boot application. All set accordingly and I'm getting "LDAP error code 49 AcceptSecurityContext error data 52e v2580" error even after providing correct credentials.
I'm using import javax.naming.Context; and have mentioned the code below.
String url = ldap_url;
String domain = ldap_domain;
String uname = request.getUsername();
String pwd = request.getPassword();
boolean authentication = false;
boolean error = true;
String msg;
String ldapSearchBase = "OU=TEST_OU, DC=DC2, DC=DC1";
// create env for 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, url);
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, "CN=" + uname + "#" + domain + "," + ldapSearchBase);
env.put(Context.SECURITY_CREDENTIALS, pwd);
NamingEnumeration results = null;
try {
LdapContext ctx = new InitialLdapContext(env, null);
authentication = true;
error = false;
} catch (NamingException e) {
logger.error("LDAP error for :{NamingException}" + e);
return ResponseEntity.ok(new ApiResponse(true, e.getMessage()));
} finally {
if (!error) {
msg = "Login success!!!";
} else {
msg = "Authentication failed!";
}
}
logger.info("exitinig...");
if (authentication) {
return ResponseEntity.ok(new ApiResponse(false, msg));
} else {
return ResponseEntity.ok(new ApiResponse(true, msg));
}
Error is catching as NamingException.
An error response with LDAP error code 49 ... data 52e "Returns when username is valid but password/credential is invalid."
There could be infrastructure issues such as when the domain controller computer account may not be synchronized with the Key Distribution Center (KDC). However you would probably have a lot more other issues when this condition exists.
This can occur when your account requires a SmartCard to sign in.
There is a setting in Active Directory called "Smart card is required for interactive logon".
You might get this error in Active directory (Windows popular implementation of LDAP) if you specify the username but not the windows domain.
This is what you should do:
USERNAME: <YOUR_WINDOWS_DOMAIN>\<YOUR_USERNAME>
PASSWORD: <YOUR_PASSWORD>

how to create a new user jazn-data.xml programmatically?

I'm using JDeveloper 11g.
I want to create a new user programmatically in 'jazn-data.xml'. Is this possible and how can I do it?
public void createWeblogicUser() {
try {
Hashtable<String, String> env = new Hashtable<String, String>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");
env.put(Context.SECURITY_PRINCIPAL, "weblogic");
env.put(Context.SECURITY_CREDENTIALS, "welcome1");
env.put(Context.PROVIDER_URL, "t3://127.0.0.1:7101");
InitialContext ctx = new InitialContext(env);
MBeanServer wls = (MBeanServer) ctx.lookup("java:comp/env/jmx/runtime");
wls.invoke(new ObjectName(" Security:Name=myrealmDefaultAuthenticator")
, "createUser"
, new Object[] {"wls_user5555", "password123","User created programmatically."}
, new String[] {"java.lang.String", "java.lang.String","java.lang.String"}
);
ctx.close();
}
catch(Exception ex) {
ex.printStackTrace();
}
}
Yes, is possible.
Technically speaking, you do not create users in jazn-data.xml, this is just a file used at development time. At runtime, you create users in weblogic.
Please find below a link:
Creating WebLogic users programmatically from a standalone Java client

How to load a properties file in a java servlet to set a ldap connection variable ?

In my java web app I have the following DAO constructor.
public DAO() throws IOException {
env = new Hashtable<String, String>();
env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://localhost:1389");
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, "cn=Directory Manager");
env.put(Context.SECURITY_CREDENTIALS, "secret");
}
I would like to set the env variable loading the following config.properties file:
INITIAL_CONTEXT_FACTORY=com.sun.jndi.ldap.LdapCtxFactory
PROVIDER_URL=ldap://localhost:1389
SECURITY_AUTHENTICATION=simple
SECURITY_PRINCIPAL=cn=Directory Manager
SECURITY_CREDENTIALS=secret
I load the file with the following code:
Properties props = new Properties();
InputStream in = this.getClass().getResourceAsStream("config.properties");
props.load(in);
in.close();
Now, I tried to set the env variable but it doesn't work:
env = new Hashtable<String, String>();
env.put(Context.INITIAL_CONTEXT_FACTORY, System.getProperty("INITIAL_CONTEXT_FACTORY"));
env.put(Context.PROVIDER_URL, System.getProperty("PROVIDER_URL"));
env.put(Context.SECURITY_AUTHENTICATION, System.getProperty("SECURITY_AUTHENTICATION"));
env.put(Context.SECURITY_PRINCIPAL, System.getProperty("SECURITY_PRINCIPAL"));
env.put(Context.SECURITY_CREDENTIALS, System.getProperty("SECURITY_CREDENTIALS"));
You are loading the properties from file into the props variable, so you have to use this props variable instead of system properties. I.e.
env.put(Context.INITIAL_CONTEXT_FACTORY, props.getProperty("INITIAL_CONTEXT_FACTORY"));
// etc.
You're doing a System.getProperty(). You should do:
props.getProperty()
Maybe create a configuration class that loads and maintains your Property object
ok. thank you.
The following code works:
public DAO() throws IOException {
Properties props = new Properties();
InputStream in = getClass().getClassLoader().getResourceAsStream("config.properties");
props.load(in);
env = new Hashtable<String, String>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, props.getProperty("PROVIDER_URL"));
env.put(Context.SECURITY_AUTHENTICATION, props.getProperty("SECURITY_AUTHENTICATION"));
env.put(Context.SECURITY_PRINCIPAL, props.getProperty("SECURITY_PRINCIPAL"));
env.put(Context.SECURITY_CREDENTIALS, props.getProperty("SECURITY_CREDENTIALS"));
}

How to connect with Active Directory without a full CN/DN from Java code

I have implemented method:
public static LdapContext buildContext(String username, String password) {
LdapContext context = null;
Hashtable<String, String> env = new Hashtable<String, String>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, username);
env.put(Context.SECURITY_CREDENTIALS, password);
env.put(Context.PROVIDER_URL, DOMAIN_URL);
try {
context = new InitialLdapContext(env, null);
} catch (NamingException e) {
}
return context;
}
I do not know the full CN/DN string. I only pass the name of a user (f.e. Tom) and password.
I have no info about groups etc..
Thank you in advance!
Search for the entry using what information you have. The search result will contain the number of entries that matched the search and the DN of each entry that matched, therefore the search should be as restrictive as possible in order to return just the one entry for which you're looking. Then use that DN to BIND to the server,
What Terry said.
We have an example of performing Basic JNDI Search with Administration Account

Cannot connect with Active Directory to reset password

I am totally new to this field and not much experienced in java too. I was assigned this task and I could connect in simple mode as admin and retrieve info, but couldnot reset password. I found in many sites that I have to use ssl for that, but couldnt successfully do that as I get a
"simple bind failed"
error I post my code below of what I did and also the codes I commented out(which I tried earlier). Please please please help. I couldnt solve the problem from any source I received. I used a certificate copied from server into my keystore. Is this the right way to use that?? If i remove the ssl part
env.put(Context.SECURITY_PROTOCOL,"ssl");
I get a handshake exception
Problem with TLS: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path validation failed: java.security.cert.CertPathValidatorException: signature check failed
public class ActiveDirectory {
private DirContext ctx;
public boolean connect(String username,String password){
Hashtable<String, String> env = new Hashtable<String, String>();
// Properties env=new Properties();
env.put(Context.SECURITY_PROTOCOL,"ssl");
env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.PROVIDER_URL, "ldap://192.168.1.199:389");
env.put(Context.REFERRAL, "follow");
// The value of Context.SECURITY_PRINCIPAL must be the logon username
// with the domain name
env.put(Context.SECURITY_PRINCIPAL, username+"#xxxx.net");
// The value of the Context.SECURITY_CREDENTIALS should be the user's
// password
env.put(Context.SECURITY_CREDENTIALS, password);
try {
// Authenticate the logon user
ctx = new InitialLdapContext(env,null);
return true;
}catch(NamingException e){
System.out.println("Error in connecting : " + e.getMessage());
return false;
}
}
public boolean changePasswordAdmin(String userName,String newPassword){
try {
//set password is a ldap modfy operation
//Secure the session with TLS
StartTlsResponse tls = (StartTlsResponse)((LdapContext) ctx).extendedOperation(new StartTlsRequest());
tls.negotiate();
//set password is a ldap modfy operation
ModificationItem[] mods = new ModificationItem[1];
//Replace the "unicdodePwd" attribute with a new value
//Password must be both Unicode and a quoted string
String newQuotedPassword = "\"" + newPassword + "\"";
byte[] newUnicodePassword = newQuotedPassword.getBytes("UTF-16LE");
mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, new BasicAttribute("unicodePwd", newUnicodePassword));
// Perform the update
ctx.modifyAttributes(userName, mods);
System.out.println("Reset Password for: " + userName);
tls.close();
ctx.close();
return true;
}
catch (NamingException e) {
System.out.println("Problem resetting password: " + e);
}
catch (UnsupportedEncodingException e) {
System.out.println("Problem encoding password: " + e);
}
catch (IOException e) {
System.out.println("Problem with TLS: " + e);
}
return false;
}
public static void main(String args[]) throws NamingException {
Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
// the keystore that holds trusted root certificates
System.setProperty("javax.net.ssl.trustStore", "C:\\keystore.jks");
System.setProperty("javax.net.ssl.trustStorePassword", "****");
System.setProperty("javax.net.ssl.keyStore", "C:\\keystore.jks");
System.setProperty("javax.net.ssl.keyStorePassword", "****");
ActiveDirectory d= new ActiveDirectory();
d.connect("Administrator", "Group&Team2");
System.out.println(d.fetchData("MG"));
System.out.println(d.changePasswordAdmin("CN=Manager MG. Manager,OU=Manager,DC=xxxxx,DC=net", "Abcd#10"));
}
}
Your active directory doesn't have a valid certificate.
This is probably the case because the root certificate wasn't imported in Java.
Here is a small tutorial how to import certificates into java.

Categories