I'm trying to change the mail and password attributes of Active Directory using java. To do this, it was generated a security certificate and imported into the certificate store of the jvm (carcerts). The connection is made using ssl successfully however when trying to change any field, eg mail,I get the following error message:
javax.naming.OperationNotSupportedException: [LDAP: error code 53 - 00002035: LdapErr: DSID-0C090B3E, comment: Operation not allowed through GC port, data 0, v1db1
source code:
// change mail
private void salvarEmailESenhaNoAd() throws IOException, NamingException {
Properties mudaSenhaProperties = new PropertiesFactory().propertiesByJndi("mudaSenha");
ADUtil adUtil = null;
try
{
adUtil = new ADUtil(mudaSenhaProperties);
adUtil.changeEmail("CN=018061671627,OU=SDS,OU=CS,OU=STI,OU=DG,OU=PRES,OU=TRERN,DC=tre-rn,DC=jus,DC=br", dadosUsuario.getEmail());
}
finally
{
if (adUtil != null)
{
adUtil.close();
}
}
}
public class ADUtil {
private LdapContext ldapContext = null;
public ADUtil(Properties properties) throws NamingException
{
ldapContext = createUserContext(properties);
}
private LdapContext createUserContext(Properties properties) throws NamingException {
String javaNamingFactoryInitial = properties.getProperty("java.naming.factory.initial");
String javaNamingProviderUrl = properties.getProperty("java.naming.provider.url");
String javaNamingSecurityAuthentication = properties.getProperty("java.naming.security.authentication");
String javaNamingSecurityPrincipal = properties.getProperty("java.naming.security.principal");
String javaNamingSecurityCredentials = properties.getProperty("java.naming.security.credentials");
String javaNamingSecurityProtocol = properties.getProperty("java.naming.security.protocol");
String javaNamingReferral = properties.getProperty("java.naming.referral");
String javaNetSslTrustStore = properties.getProperty("javax.net.ssl.trustStore");
String javaNetSslTrustStorePassword = properties.getProperty("javax.net.ssl.trustStorePassword");
Hashtable<String, String> env = new Hashtable<String, String>();
System.setProperty("javax.net.ssl.trustStore", javaNetSslTrustStore);
System.setProperty("javax.net.ssl.trustStorePassword", javaNetSslTrustStorePassword);
env.put(Context.INITIAL_CONTEXT_FACTORY, javaNamingFactoryInitial);
env.put(Context.SECURITY_AUTHENTICATION, javaNamingSecurityAuthentication);
env.put(Context.SECURITY_PRINCIPAL, javaNamingSecurityPrincipal);
env.put(Context.SECURITY_CREDENTIALS, javaNamingSecurityCredentials);
env.put(Context.SECURITY_PROTOCOL, javaNamingSecurityProtocol); // para poder modificar password y grupos del usuario.
env.put(Context.PROVIDER_URL, javaNamingProviderUrl);
env.put(Context.REFERRAL, javaNamingReferral);
return new InitialLdapContext(env, null);
}
public void changePassword(String userCN, String newPassword) throws NamingException, UnsupportedEncodingException, IOException {
modifyAdAttribute(userCN, "unicodePwd", converteString(newPassword));
System.out.println("Password changed for " + userCN);
}
public void changeEmail(String userDN, String newEmail) throws NamingException, UnsupportedEncodingException, IOException {
modifyAdAttribute(userDN, "mail", converteString(newEmail));
System.out.println("Email changed for " + newEmail);
}
private void modifyAdAttribute(String userCN, String attribute, Object value) throws NamingException{
ModificationItem[] modificationItem = new ModificationItem[1];
modificationItem[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE,
new BasicAttribute(attribute, value));
ldapContext.modifyAttributes(userCN, modificationItem);
}
private static byte[] converteString(String password) throws UnsupportedEncodingException{
String newQuotedPassword = "\"" + password + "\"";
return newQuotedPassword.getBytes("UTF-16LE");
}
public void close() throws NamingException
{
if (ldapContext != null)
{
ldapContext.close();
}
}
Reading the attributes is usually done with the code below:
public class TesteLdap {
/**
* #param args the command line arguments
*/
private static SearchControls getSimpleSearchControls() {
SearchControls searchControls = new SearchControls();
searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
searchControls.setTimeLimit(30000);
//String[] attrIDs = {"objectGUID"};
//searchControls.setReturningAttributes(attrIDs);
return searchControls;
}
public static void main(String[] args) throws NamingException {
try {
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://rndc10.tre-rn.jus.br:3269");
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, "user");
env.put(Context.SECURITY_CREDENTIALS, "password");
env.put(Context.SECURITY_PROTOCOL, "ssl");
env.put(Context.REFERRAL, "ignore");
String filter = "(&(objectClass=user)(CN=018061671627))";
LdapContext ctx = new InitialLdapContext(env, null);
ctx.setRequestControls(null);
NamingEnumeration<?> namingEnum = ctx.search("OU=DG,OU=PRES,OU=TRERN,DC=tre-rn,DC=jus,DC=br", filter, getSimpleSearchControls());
while (namingEnum.hasMore()) {
SearchResult result = (SearchResult) namingEnum.next();
Attributes attrs = result.getAttributes();
System.out.println(attrs.get("cn"));
System.out.println(attrs.get("displayname"));
System.out.println(attrs.get("mail"));
System.out.println(attrs.get("distinguishedName"));
}
namingEnum.close();
} catch (Exception e) {
e.printStackTrace();
My environment: Active Directory on Windows 2008R2 Standart, Open JDK 7 running on unbutu 12.10.
I would be grateful if someone help me because I've tried everything but I can not make it work.
The error seems to be self-explanatory. You should perform this operation over LDAPS using port 636.
Have you tried to use
env.put(Context.PROVIDER_URL, "ldaps://rndc10.tre-rn.jus.br:636");
(Note that ldaps:// is not mandatory if you specify ssl separately).
Do you have RODCs (Read only DCs) in your environment? And if so, is rndc10.tre-rn.jus.br a RODC?
Perhaps you should be pointing to a DC that is writeable?
Have you tried updating this string:
env.put(Context.PROVIDER_URL, "ldap://rndc10.tre-rn.jus.br:3269");
to:
env.put(Context.PROVIDER_URL, "ldaps://rndc10.tre-rn.jus.br:3269");
^
Related
I'm trying to do an LDAP authentication using Java but this always return a null result and usrNamespace. Also was able to confirm that the username and password passed are correct.
With the username I use cn, i tried changing (uid=" + username + ") to cn but still gives me the same result
If anyone can help me that would be greatly appreciated. Thank you!
public class LdapAuthenticationAdapter implements AuthenticationAdapter {
#Override
public boolean authenticate(String username, String password) throws Exception {
Properties prop = new Properties();
//set the property value
SECURITY_AUTHENTICATION = prop.getProperty("eq.SECURITY_AUTHENTICATION");
SECURITY_PRINCIPAL = prop.getProperty("eq.SECURITY_PRINCIPAL");
SECURITY_CREDENTIALS = prop.getProperty("eq.SECURITY_CREDENTIALS");
PROVIDER_URL = prop.getProperty("eq.PROVIDER_URL");
// Get admin user, password(encrypted), host, port and other LDAP parameters
// from equationConfiguration.properties
Hashtable<String, Object> env = new Hashtable<String, Object>();
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, "uid=admin,ou=system");
env.put(Context.SECURITY_CREDENTIALS, "secret");
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://localhost:10389/dc=main,dc=com");
// env.put("java.naming.ldap.attributes.binary", "objectSID"); // validate this line if applicable
InitialDirContext context = new InitialDirContext(env);
SearchControls ctrls = new SearchControls();
ctrls.setReturningAttributes(new String[] { "givenName", "sn","memberOf" });
ctrls.setSearchScope(SearchControls.SUBTREE_SCOPE);
NamingEnumeration<javax.naming.directory.SearchResult> answers = null;
SearchResult result = null;
String usrNamespace = null;
try {
answers = context.search("ou=bankfusionusers", "(uid=" + username + ")", ctrls); // Get directory context
result = answers.nextElement();
usrNamespace = result.getNameInNamespace();
Properties props = new Properties();
props.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
props.put(Context.PROVIDER_URL, "ldap://localhost:10389/dc=main,dc=com");
props.put(Context.SECURITY_PRINCIPAL, usrNamespace);
props.put(Context.SECURITY_CREDENTIALS, password);
System.err.println("Entry 1");
context = new InitialDirContext(props);
}catch(NullPointerException e){
System.err.println("Unsuccessful authenticated bind " + e + "\n");
return false;
}
return true;
}//end method
}
I changed your code a little bit, and it works on mine.
public static void main(String[] args) throws NamingException {
Properties initialProperties = new Properties();
initialProperties.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
initialProperties.put(Context.PROVIDER_URL, "ldap://192.168.0.179:389");
initialProperties.put(Context.SECURITY_PRINCIPAL, "cn=Directory Manager");
initialProperties.put(Context.SECURITY_CREDENTIALS, "dirmanager");
initialProperties.put(Context.SECURITY_AUTHENTICATION, "simple");
InitialDirContext context = new InitialDirContext(initialProperties);
SearchControls ctrls = new SearchControls();
ctrls.setReturningAttributes(new String[] { "cn", "sn","givenname" });
ctrls.setSearchScope(SearchControls.SUBTREE_SCOPE);
NamingEnumeration<javax.naming.directory.SearchResult> answers = null;
SearchResult result = null;
String usrNamespace = null;
try {
String username = "user.997"; // I added this, I removed some of your code as well
answers = context.search("dc=example,dc=com", "(uid=" + username + ")", ctrls); // Get directory context
result = answers.nextElement();
usrNamespace = result.getNameInNamespace();
System.out.println("result variable shows : " + result);
System.out.println("usrNamespace variable shows: " + usrNamespace);
}catch(NullPointerException e){
System.err.println("Unsuccessful authenticated bind " + e + "\n");
}
}
}
In my console, I see
I'm newer coding ldap using spring-boot (ldapTemplate). I want to get the groups that a user belongs, get the list of membreOf attributes, I tried this:
#Override
public Person getUserInfo(String uid, String orgnisationUnit) throws InvalidNameException {
Name dn = bindDn(uid, orgnisationUnit);
return (Person ) ldapTemplate.lookup(dn, new LdapMapper());
}
This is myLdapMapper:
public class LdapMapper implements ContextMapper<Object> {
#Override
public Object mapFromContext(Object ctx) {
DirContextAdapter context = (DirContextAdapter) ctx;
Person p = new Person();
p.setFirstName(context.getStringAttribute("cn"));
p.setMailAddress(context.getStringAttribute("uid"));
p.setRoles(context.getObjectAttributes("memberOf")); // roles was declared like: private Object[] roles
return p;
}
}
Would you have any propositions ?
import java.io.*;
import java.text.*;
import java.util.*;
import javax.naming.*;
import javax.naming.directory.*;
import javax.naming.ldap.InitialLdapContext;
import javax.xml.bind.*;
public class LdapConnection {
public void getUserDetail(String user_name, String passwd) throws NamingException {
DirContext ctx = null;
String username = user_name;
try {
ctx = context(user_name, passwd);
SearchControls searchCtls = new SearchControls();
String returnedAtts[] = {"sn", "mail", "cn", "givenName",
"telephoneNumber", "manager","memberOf"};
searchCtls.setReturningAttributes(returnedAtts);
searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
String searchFilter = "(&(objectClass=user)(mail=*))";
String searchBase = "OU=India,DC=<Domain Component>";
NamingEnumeration<?> answer = ctx.search(searchBase, searchFilter,
searchCtls);
while (answer.hasMoreElements()) {
SearchResult sr = (SearchResult) answer.next();
Attributes attrs = sr.getAttributes();
if (attrs != null) {
try {
String cn = attrs.get("cn").get().toString();
String mail_id = attrs.get("mail").get().toString();
NamingEnumeration<?> memberOf = attrs.get("memberOf").getAll();
while (answer.hasMoreElements()) {
String member =(String)memberOf.next();
System.out.println("memberOf : " + member);
}
} catch (NullPointerException e) {
System.out.println(e.getMessage());
}
}
}
} catch (NamingException e) {
System.out.println(e.getMessage());
} finally {
if(!ctx.equals(null))
ctx.close(); }
}
/**
* This method will return Directory Context to the Called method,Used to
* bind with LDAP
*/
public DirContext context(String user, String passwd)
throws NamingException {
Hashtable<String, String> env = new Hashtable<String, String>();
String adminName = "CN=" + user
+ ",OU=User,OU=India,DC=<Domain Component>";
String adminPassword = passwd;
String ldapURL = <ldapserver url with port>;
env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, ldapURL);
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, adminName);
env.put(Context.SECURITY_CREDENTIALS, adminPassword);
DirContext ctx = new InitialLdapContext(env, null);
return ctx; }
public static void main(String[] args) throws NamingException {
LdapConnection ldap = new LdapConnection();
ldap.getUserDetail("username","password");
}
}`
I hope this code will solve you problem.
Is their any way to fetch duplicates from AD using java ? I see we can do it in power shell by grouping all usernames and then checking count >1.
https://gallery.technet.microsoft.com/scriptcenter/Find-Active-Directory-c8789b42
Please help :).
you should get all objects of a special type(such as user, group , ...) and their attributes. then check duplicate attributes of all objects. for do this, you can insert each attributes in a hasp map as a key, and insert all value of attribute per each object and check is duplicated or not ?
use JAVA JNDI to access AD server as follow:
/**
* retrieve all attributes of a named object.
*
*/
class GetAllAttrs {
static void printAttrs(Attributes attrs) {
if (attrs == null) {
System.out.println("No attributes");
} else {
/* Print each attribute */
try {
for (NamingEnumeration ae = attrs.getAll(); ae.hasMore();) {
Attribute attr = (Attribute) ae.next();
System.out.println("attribute: " + attr.getID());
/* print each value */
for (NamingEnumeration e = attr.getAll(); e.hasMore(); System.out
.println("value: " + e.next()))
;
}
} catch (NamingException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
// Set up the environment for creating the initial context
Hashtable<String, Object> env = new Hashtable<String, Object>(11);
env
.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://localhost:389/o=JNDITutorial");
try {
// Create the initial context
DirContext ctx = new InitialDirContext(env);
// Get all the attributes of named object
Attributes answer = ctx.getAttributes("cn=Ted Geisel, ou=People");
// Print the answer
printAttrs(answer);
// Close the context when we're done
ctx.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
also you can use search filter to limit your outputs:
public class LdapSearch {
public static void main(String[] args) throws Exception {
Hashtable env = new Hashtable();
String sp = "com.sun.jndi.ldap.LdapCtxFactory";
env.put(Context.INITIAL_CONTEXT_FACTORY, sp);
String ldapUrl = "ldap://localhost:389/dc=yourName, dc=com";
env.put(Context.PROVIDER_URL, ldapUrl);
DirContext dctx = new InitialDirContext(env);
String base = "ou=People";
SearchControls sc = new SearchControls();
String[] attributeFilter = { "cn", "mail" };
sc.setReturningAttributes(attributeFilter);
sc.setSearchScope(SearchControls.SUBTREE_SCOPE);
String filter = "(&(sn=W*)(l=Criteria*))";
NamingEnumeration results = dctx.search(base, filter, sc);
while (results.hasMore()) {
SearchResult sr = (SearchResult) results.next();
Attributes attrs = sr.getAttributes();
Attribute attr = attrs.get("cn");
System.out.print(attr.get() + ": ");
attr = attrs.get("mail");
System.out.println(attr.get());
}
dctx.close();
}
}
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"));
}
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.