Retrieve LDAP server type - java

i want to know if there is a way to retrieve the connected ldap server type (openldap or active directory)
this below my connection method
public DirContext ldapUserConnect(String serverUrl, int serverPort, String serverType, String userDn,
String userPassword, boolean binaryAttribute) throws NamingException {
// connect to ldap server using ldap_server table data
if ((userDn != null && !userDn.isEmpty()) || (userPassword != null && !userPassword.isEmpty())) {
Map<String, String> environment = getLdapEnvironement(serverUrl, serverPort, serverType, binaryAttribute);
// connect with user account
environment.put(javax.naming.Context.SECURITY_PRINCIPAL, userDn);
environment.put(javax.naming.Context.SECURITY_CREDENTIALS, userPassword);
return new InitialDirContext((Hashtable<String, String>) environment);
} else {
return null;
}
}

The general approach is to examine the rootDSE entry; however, there is no guarantee that all LDAP server vendors support this method. Determine LDAP Server Vendor provides descriptions for several LDAP "flavors" and determining their vendor name/version.

Related

LDAP validate user uid without knowing his ou

I have to authenticate users into my system using LDAP, my problem is that i have many organizations and i dont have a list user-organization.
I have this code working forcing a knowing organization:
ldap.validate("uid=" + this.email + ",ou="+ThisIsTheOrImForcing+",ou=users,dc=myDC,dc=com,dc=br", this.password);
My validation method:
public boolean validate(String principal, String password){
// Set up the environment for creating the initial context
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
//env.put(Context.PROVIDER_URL, "ldap://IP:PORT/DC=opus");
//env.put(Context.PROVIDER_URL, "ldap://IP:PORT/dc=mydc,dc=com,dc=br");
env.put(Context.PROVIDER_URL, "ldap://IP:PORT/dc=mydc,dc=com,dc=br");
//Authenticate as S. User and password
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL,principal );
env.put(Context.SECURITY_CREDENTIALS, password);
try{
// Create the initial context
DirContext ctx = new InitialDirContext(env);
return true;
} catch( AuthenticationException ae ) {
return false;
} catch( NamingException ne ) {
return false;
}
}
I have the var ThisIsTheOrImForcing, how can i authenticate the user without knowing his organization, i have to search for it first, how?
Simple bind in LDAP requires you to provide the DN of the user, hence why you need to know the organization.
You have two ways to deal with it :
Make a search on your directory for the user which has the attribute uid = this.email , retrieve his DN, and authenticate with the found DN to validate the user
Use other way to authenticate, such as SASL mechanism : See this for more information. You will also have to look for your Directory configuration which mechanism is supported
Usually, uid is a unique identifier. A search operation is used to map the given uid to its distinguished name, so you don't have to worry about the exact place of an account in the directory.

LDAP is not delivering results

I currently face the problem that an LDAP-Query is passed to an LDAP-Server and the LDAP-Server does not deliver results.
The query: (&(objectCategory=user)(mail=tester#oop-expert.de))
The given E-Mail is meant to not be found. So an empty result is expected.
In most environment configurations this query will pass perfectly and the LDAP returns an empty result immediately.
I broke down the problem to something that may relate to the network or host from where the query is sent. So the query will be fine if sent from one host/network and from another host/network the LDAP-Server will "starve" my LDAP-Client so the LDAP-Client closes the connection for a client-side timeout.
On the other hand: searching for an email that exists will always lead to an immediate result. Doesn't matter from which host/network.
The LDAP-Server is an Active Directory. There a several domain controllers providing an LDAP-Service, configured "round robin". Access per ip or dns does not make any difference on this subject.
The communication is secured via ssl. (ldaps)
In all situations the connection was established. So authentication and passing the query to the LDAP went fine.
Authorization should not be an issue either. I used the same LDAP user for all situations.
The LDAP client is always a JAVA implementation using InitialContext.
private InitialDirContext createDirContext(String principal, String credentials) throws NamingException {
if (credentials == null || credentials.isEmpty()) {
throw new LDAPLoginException();
}
return new InitialDirContext(createEnvironment(principal, credentials));
}
private Hashtable<String, String> createEnvironment(String principal, String credentials) {
Hashtable<String, String> env = new Hashtable<>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, this.ldapUrl);
// To get rid of the PartialResultException when using Active Directory
env.put(Context.REFERRAL, "follow");
// Needed for the Bind (User Authorized to Query the LDAP server)
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, principal);
env.put(Context.SECURITY_CREDENTIALS, credentials);
return env;
}
The code that builds and executes the query:
private LDAPUser getLDAPUserInfoByUniqueField(String attr, String value) {
DirContext serviceUserContext = null;
NamingEnumeration<SearchResult> results = null;
try {
String searchString = "(&(objectCategory=user)(" + attr + "=" + value + "))";
serviceUserContext = createDirContext(this.serviceUserPrincipal, serviceUserCredentials);
results = serviceUserContext.search("", searchString, createSearchControls()); // blocking...
return createLDAPUserInfo(results);
} catch (LDAPLoginException e) {
throw e;
} catch (Exception e) {
throw new LDAPFatalException(e);
} finally {
try {
if (results != null)
results.close();
} catch (NamingException e) {
}
try {
if (serviceUserContext != null)
serviceUserContext.close();
} catch (NamingException e) {
}
}
}
Utility methods:
private LDAPUser createLDAPUserInfo(NamingEnumeration<SearchResult> results) throws NamingException {
LDAPUser ldapUserInfo = null;
if (results.hasMore()) { // blocking here
SearchResult result = (SearchResult) results.next();
String sAMAccountName = extractsAMAccountName(result);
String distinguishName = extractDistinguishName(result);
String department = extractDepartment(result);
String email = extractEmail(result);
ldapUserInfo = new LDAPUser(sAMAccountName, distinguishName, department, email);
}
return ldapUserInfo;
}
private String extractsAMAccountName(SearchResult result) throws NamingException {
Attributes attrs = result.getAttributes();
Attribute attr = attrs.get("sAMAccountName");
return (String) attr.get();
}
The exception:
de.oopexpert.business.ldap.LDAPFatalException: javax.naming.PartialResultException [Root exception is javax.naming.CommunicationException: oopexpert.de:636 [Root exception is java.net.ConnectException: Connection timed out]]
at de.oopexpert.business.ldap.impl.LDAPImpl.getLDAPUserInfoByUniqueField(LDAPImpl.java:90)
at de.oopexpert.business.ldap.impl.LDAPImpl.getLDAPUserInfoByEmail(LDAPImpl.java:57)
Caused by: javax.naming.PartialResultException [Root exception is javax.naming.CommunicationException: oopexpert.de:636 [Root exception is java.net.ConnectException: Connection timed out]]
at com.sun.jndi.ldap.LdapNamingEnumeration.hasMoreImpl(LdapNamingEnumeration.java:242)
at com.sun.jndi.ldap.LdapNamingEnumeration.hasMore(LdapNamingEnumeration.java:189)
at de.oopexpert.business.ldap.impl.LDAPImpl.createLDAPUserInfo(LDAPImpl.java:139)
at de.oopexpert.business.ldap.impl.LDAPImpl.getLDAPUserInfoByUniqueField(LDAPImpl.java:84)
Any hints?
We struggled with four persons what is going on. We figured out, that it has something to do with "name resolution problems" on the server side when "following refferals". The customer specific environment configuration inherently prohibits us to prevent this behaviour. So we came up with a workaround.
As we are operating in an active directory domain we have global catalog servers. The important statement to this is from Microsoft "technet":
The global catalog is a distributed data repository that contains a searchable, partial representation of every object in every domain in a multidomain Active Directory Domain Services (AD DS) forest. The global catalog is stored on domain controllers that have been designated as global catalog servers and is distributed through multimaster replication. Searches that are directed to the global catalog are faster because they do not involve referrals to different domain controllers.
(from What Is the Global Catalog?)
The phrase "do not involve refferals" leads us to my JNDI environment configuration where I set the following:
env.put(Context.REFERRAL, "follow");
So as I did that, the LDAP-Server will ask other LDAP-Servers if the result of a query seems to be incomplete. This was confirmed by our administrators when I started the query and they debugged it on TCP-Level.
Somehow, I maybe do not really represent it correctly, the name of the other LDAP-Server could not be resolved which leads the client to starvation as the first LDAP-Server waits for resolution.
We tried to omit the client parameter "Context.Refferals=follow". Here we get immediate response. But the response wasn't as expected:
javax.naming.PartialResultException: Unprocessed Continuation Reference(s); remaining name ''
This came up because the first LDAP-Server has the opinion to return incomplete data.
Our administrators say that this can never be the case, because "every domain controller" is a "Global Catalog Server".
So my workaround is: I will deal with this exception and interprete it as "no result".

Logging into multiple gmail accounts to read messages in inbox in java using imaps

I'm beginner,
I'm writing a program which logins into my g-mail accounts and checks the messages in the Inbox.
To do this I'm using imaps ("imaps.gmail.com").
But what my problem is while logging into all the accounts, checking the messages in it one by one.
Program Reporting an error/kind of:
javax.mail.AuthenticationFailedException: [ALERT] Please log in via
your web browser: some url (Failure) imaps: xxxx#gmail.com for the
second and other accounts...
From this What I understand is I'm not closing sessions, store properly. So,Program is unable to check in the respective second and rest mail accounts..
Someone Help (With clarification)..
And the architecture of my code:
public class InboxRead {
public static boolean release = false;
private Session session = null;
public void checkInbox(String username, String password) {
// logging into account using imaps..
Properties props = System.getProperties();
props.setProperty("mail.store.protocol", "imaps");
session = Session.getDefaultInstance(props, null);
store = session.getStore("imaps");
store.connect("imap.gmail.com", username, password);
System.out.println(store);
// reading inbox... closing all folders here
store.close();
session = null;
release = true;
}
public static void main(String[] args) {
for(;;) { // gives no of accounts with credentials..
do {
String username = "xxx#gmail.com";
String password = "xxxx";
InboxRead.checkInbox(username, password);
} while(release);
}
}
}
...
Its a Google gateway thrown error. Google maintain high security in the aspect of profile access or even mail access but even that can be controlled by the user using below link Google secure control
go to that link with your google account and select "turn on" and then try to execute your program.

When to disconnect bosh connection establish from app server to use prebinding in strophe?

This question is Extension of my previous question on this SO question "How to connect XMPP bosh server using java smack library?"
I am using Java as server side language. I have successfully implement xmpp BOSH connection using smach-jbosh thanks to #Deuteu for helping me to achieve this, so far I have modify jbosh's BOSHClient.java file and added two getter method for extracting RID and SID.
Now I have RID and SID on my app server (I am using Apache Tomcat). I need to pass this credential to Strophe (web client) so that it can attach to connection.
Here I have some doubt.
When to disconnect bosh Connection establish from the app server? before passing sid, rid and jid to strophe or after passing sid, rid and jid to strophe?
As per my observation during implementation for the same, I have observed that once bosh connection from the app server has been disconnected, session is expired and SID and RID is no longer useful!!!
I have implemented this logic (Establishing bosh connection and Extracting sid and rid) on a Servlet, here once response has been send from Servlet, Thread will get expired and end BOSH connection will get terminated, so I am not able perform `Attach()` on strophe as session is expired.
Can somebody help me with that problem?
I believe #fpsColton's answer is correct - I'm just added extra info for clarity. As requested on linked thread here is the code changes I made on this - note: I only added the parts where I've labelled "DH"
In BOSHConnection:
// DH: function to preserve current api
public void login(String username, String password, String resource)
throws XMPPException {
login(username, password, resource, false);
}
// DH: Most of this is existing login function, but added prebind parameter
// to allow leaving function after all required pre-bind steps done and before
// presence stanza gets sent (sent from attach in XMPP client)
public void login(String username, String password, String resource, boolean preBind)
throws XMPPException {
if (!isConnected()) {
throw new IllegalStateException("Not connected to server.");
}
if (authenticated) {
throw new IllegalStateException("Already logged in to server.");
}
// Do partial version of nameprep on the username.
username = username.toLowerCase().trim();
String response;
if (config.isSASLAuthenticationEnabled()
&& saslAuthentication.hasNonAnonymousAuthentication()) {
// Authenticate using SASL
if (password != null) {
response = saslAuthentication.authenticate(username, password, resource);
} else {
response = saslAuthentication.authenticate(username, resource, config.getCallbackHandler());
}
} else {
// Authenticate using Non-SASL
response = new NonSASLAuthentication(this).authenticate(username, password, resource);
}
// Indicate that we're now authenticated.
authenticated = true;
anonymous = false;
// DH: Prebind only requires connect and authenticate
if (preBind) {
return;
}
// Set the user.
if (response != null) {
this.user = response;
// Update the serviceName with the one returned by the server
config.setServiceName(StringUtils.parseServer(response));
} else {
this.user = username + "#" + getServiceName();
if (resource != null) {
this.user += "/" + resource;
}
}
// Create the roster if it is not a reconnection.
if (this.roster == null) {
this.roster = new Roster(this);
}
if (config.isRosterLoadedAtLogin()) {
this.roster.reload();
}
// Set presence to online.
if (config.isSendPresence()) {
sendPacket(new Presence(Presence.Type.available));
}
// Stores the autentication for future reconnection
config.setLoginInfo(username, password, resource);
// If debugging is enabled, change the the debug window title to include
// the
// name we are now logged-in as.l
if (config.isDebuggerEnabled() && debugger != null) {
debugger.userHasLogged(user);
}
}
and
// DH
#Override
public void disconnect() {
client.close();
}
then my Client-side (Web Server) wrapper class - for connecting from within JSP is:
Note: This is proving code rather than production - so there's some stuff in here you may not want.
public class SmackBoshConnector {
private String sessionID = null;
private String authID = null;
private Long requestID = 0L;
private String packetID = null;
private boolean connected = false;
public boolean connect(String userName, String password, String host, int port, final String xmppService) {
boolean success = false;
try {
Enumeration<SaslClientFactory> saslFacts = Sasl.getSaslClientFactories();
if (!saslFacts.hasMoreElements()) {
System.out.println("Sasl Provider not pre-loaded");
int added = Security.addProvider(new com.sun.security.sasl.Provider());
if (added == -1) {
System.out.println("Sasl Provider could not be loaded");
System.exit(added);
}
else {
System.out.println("Sasl Provider added");
}
}
BOSHConfiguration config = new BOSHConfiguration(false, host, port, "/http-bind/", xmppService);
BOSHConnection connection = new BOSHConnection(config);
PacketListener sndListener = new PacketListener() {
#Override
public void processPacket(Packet packet) {
SmackBoshConnector.this.packetID = packet.getPacketID();
System.out.println("Send PacketId["+packetID+"] to["+packet.toXML()+"]");
}
};
PacketListener rcvListener = new PacketListener() {
#Override
public void processPacket(Packet packet) {
SmackBoshConnector.this.packetID = packet.getPacketID();
System.out.println("Rcvd PacketId["+packetID+"] to["+packet.toXML()+"]");
}
};
PacketFilter packetFilter = new PacketFilter() {
#Override
public boolean accept(Packet packet) {
return true;
}
};
connection.addPacketSendingListener(sndListener, packetFilter);
connection.addPacketListener(rcvListener, packetFilter);
connection.connect();
// login with pre-bind only
connection.login(userName, password, "", true);
authID = connection.getConnectionID();
BOSHClient client = connection.getClient();
sessionID = client.getSid();
requestID = client.getRid();
System.out.println("Connected ["+authID+"] sid["+sessionID+"] rid["+requestID+"]");
success = true;
connected = true;
try {
Thread.yield();
Thread.sleep(500);
}
catch (InterruptedException e) {
// Ignore
}
finally {
connection.disconnect();
}
} catch (XMPPException ex) {
Logger.getLogger(SmackBoshConnector.class.getName()).log(Level.SEVERE, null, ex);
}
return success;
}
public boolean isConnected() {
return connected;
}
public String getSessionID() {
return sessionID;
}
public String getAuthID() {
return authID;
}
public String getRequestIDAsString() {
return Long.toString(requestID);
}
public String getNextRequestIDAsString() {
return Long.toString(requestID+1);
}
public static void main(String[] args) {
SmackBoshConnector bc = new SmackBoshConnector();
bc.connect("dazed", "i3ji44mj7k2qt14djct0t5o709", "192.168.2.15", 5280, "my.xmppservice.com");
}
}
I confess that I'm don't fully remember why I put the Thread.yield and Thread.sleep(1/2 sec) in here - I think - as you can see with added PacketListener - the lower level functions return after sending data and before getting a response back from the server - and if you disconnect before the server has sent it's response then it (also) causes it to clean up the session and things won't work. However it may be that, as #fpsColton says, this dicsonnect() isn't actually required.
Edit: I now remember a bit more about whay I included sleep() and yield(). I noticed that Smack library includes sleep() in several places, including XMPPConnection.shutdown() as per source. Plus in terms of yield() I had problems in my environment (Java in Oracle Database - probably untypical) when it wasn't included - as per Smack Forum Thread.
Good luck.
After you have created a BOSH session with smack and have extracted the SID+RID values, you need to pass them to Strophe's attach() and from here on out you need to let strophe deal with this connection. Once Strophe has attached, you do not want your server to be doing anything to the connection at all.
If your server side code sends any messages at all to the connection manager after strophe has attached, it's likely that it will send a invalid RID which will cause your session to terminate.
Again, once the session has been established and is usable by strophe, do not attempt to continue using it from the server side. After your server side bosh client completes authentication and you've passed the SID+RID to the page, just destroy the server side connection object, don't attempt to disconnect or anything as this will end your session.
The thing you need to remember is, unlike traditional XMPP connections over TCP, BOSH clients do NOT maintain a persistent connection to the server (this is why we use BOSH in web applications). So there is nothing to disconnect. The persistent connection is actually between the XMPP server and the BOSH connection manager, it's not something you need to deal with. So when you call disconnect from your server side BOSH client, you're telling the connection manager to end the session and close it's connection to the XMPP server, which completely defeats the purpose of creating the session in the first place.

How do I enable/disable an LDAP user?

I'm using the following java code to authenticate an LDAP user. This will succeed if the object exists with the specified username and password. However, I don't see any properties on the user which would allow me to activate/deactivate the user. Specifically, I just want to set a property on the user which would make authentication fail without actually deleting the entire object.
Can this be done directly or do I need to write separate code which loads the user and checks that a field is marked active or inactive?
public static boolean authenticate(String url, String securityAuthentication, String securityPrincipal, String usernameKey, String username, String password) {
try {
Hashtable env = new Hashtable();
env.put(InitialContext.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
env.put(InitialContext.PROVIDER_URL, url);
env.put(InitialContext.SECURITY_AUTHENTICATION, securityAuthentication);
env.put(InitialContext.SECURITY_PRINCIPAL, usernameKey +"="+ username + securityPrincipal);
env.put(InitialContext.SECURITY_CREDENTIALS, password );
// Create the initial context
DirContext ctx = new InitialDirContext(env);
return true;
} catch (NamingException e) {
return false;
}
}
EDIT: I'm told that in we have one that's active directory and another which is 389 Directory Server.

Categories