We have a relational database with user information and hashed passwords that I'd like to serve up using LDAP for single sign-on purposes, and MyVD seems to be able to do everything we need. My first step was to get searches to work with no authentication whatsoever, and I've got the LDAP records coming in just fine for the users and their attributes, so I'm going back now and creating my own custom insert for the authentication mechanism which will basically hash the bind request's password and try to match it up to the hashed password in our database.
There is very little documentation as far as how to implement the particular details of the insert's operations (there's a fairly good overview about creating an insert in general), so I've been attempting to go off of existing source in the MyVD project as a basis for my code. Right now I continuously get the following Exception, which I've traced back through the project code, and still cannot figure out why it is occurring:
LDAPException: Could not bind to any services (49) Invalid Credentials
LDAPException: Server Message: [email protected],o=RBD,c=us
at net.sourceforge.myvd.router.Router.bind(Router.java:164)
at net.sourceforge.myvd.chain.BindInterceptorChain.nextBind(BindInterceptorChain.java:52)
at net.sourceforge.myvd.inserts.routing.MasterReplicaRouter.bind(MasterReplicaRouter.java:109)
at net.sourceforge.myvd.chain.BindInterceptorChain.nextBind(BindInterceptorChain.java:49)
at net.sourceforge.myvd.inserts.DumpTransaction.bind(DumpTransaction.java:142)
at net.sourceforge.myvd.chain.BindInterceptorChain.nextBind(BindInterceptorChain.java:49)
at net.sourceforge.myvd.protocol.ldap.BindHandler.messageReceived(BindHandler.java:153)
at net.sourceforge.myvd.protocol.ldap.LDAPOperation.messageReceived(LDAPOperation.java:84)
at org.apache.mina.handler.demux.DemuxingIoHandler.messageReceived(DemuxingIoHandler.java:141)
at net.sourceforge.myvd.protocol.ldap.LdapProtocolProvider$LdapProtocolHandler.messageReceived(LdapProtocolProvider.java:402)
at org.apache.mina.common.support.AbstractIoFilterChain$TailFilter.messageReceived(AbstractIoFilterChain.java:570)
at org.apache.mina.common.support.AbstractIoFilterChain.callNextMessageReceived(AbstractIoFilterChain.java:299)
at org.apache.mina.common.support.AbstractIoFilterChain.access$1100(AbstractIoFilterChain.java:53)
at org.apache.mina.common.support.AbstractIoFilterChain$EntryImpl$1.messageReceived(AbstractIoFilterChain.java:648)
at org.apache.mina.filter.codec.support.SimpleProtocolDecoderOutput.flush(SimpleProtocolDecoderOutput.java:58)
at org.apache.mina.filter.codec.ProtocolCodecFilter.messageReceived(ProtocolCodecFilter.java:180)
at org.apache.mina.common.support.AbstractIoFilterChain.callNextMessageReceived(AbstractIoFilterChain.java:299)
at org.apache.mina.common.support.AbstractIoFilterChain.access$1100(AbstractIoFilterChain.java:53)
at org.apache.mina.common.support.AbstractIoFilterChain$EntryImpl$1.messageReceived(AbstractIoFilterChain.java:648)
at org.apache.mina.filter.executor.ExecutorFilter.processEvent(ExecutorFilter.java:220)
at org.apache.mina.filter.executor.ExecutorFilter$ProcessEventsRunnable.run(ExecutorFilter.java:264)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at org.apache.mina.util.NamePreservingRunnable.run(NamePreservingRunnable.java:51)
at java.lang.Thread.run(Thread.java:662)
As you'll see with the insert class below, I'm just checking that the password is 123
, and I can see from the log files that that's what's being sent to the class:
[2013-10-08 16:21:24,198][MyVD-2] DEBUG DBAuthenticator - USER: [email protected]
[2013-10-08 16:21:24,198][MyVD-2] DEBUG DBAuthenticator - PASSWORD: 123
So I'm at a loss, which is why I'm now posting this question ;) Here's my custom insert class that implements the Insert
interface:
package com.foobar.myvd;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Properties;
import net.sourceforge.myvd.chain.AddInterceptorChain;
import net.sourceforge.myvd.chain.BindInterceptorChain;
import net.sourceforge.myvd.chain.CompareInterceptorChain;
import net.sourceforge.myvd.chain.DeleteInterceptorChain;
import net.sourceforge.myvd.chain.ExetendedOperationInterceptorChain;
import net.sourceforge.myvd.chain.ModifyInterceptorChain;
import net.sourceforge.myvd.chain.PostSearchCompleteInterceptorChain;
import net.sourceforge.myvd.chain.PostSearchEntryInterceptorChain;
import net.sourceforge.myvd.chain.RenameInterceptorChain;
import net.sourceforge.myvd.chain.SearchInterceptorChain;
import net.sourceforge.myvd.core.NameSpace;
import net.sourceforge.myvd.inserts.Insert;
import net.sourceforge.myvd.types.Attribute;
import net.sourceforge.myvd.types.Bool;
import net.sourceforge.myvd.types.DistinguishedName;
import net.sourceforge.myvd.types.Entry;
import net.sourceforge.myvd.types.ExtendedOperation;
import net.sourceforge.myvd.types.Filter;
import net.sourceforge.myvd.types.Int;
import net.sourceforge.myvd.types.Password;
import net.sourceforge.myvd.types.Results;
import org.apache.log4j.Logger;
import com.novell.ldap.LDAPConstraints;
import com.novell.ldap.LDAPException;
import com.novell.ldap.LDAPModification;
import com.novell.ldap.LDAPSearchConstraints;
import com.novell.ldap.util.RDN;
public class DBAuthenticator implements Insert {
Logger logger = Logger.getLogger(DBAuthenticator.class);
String name;
@Override
public void add(AddInterceptorChain arg0, Entry arg1, LDAPConstraints arg2)
throws LDAPException {
arg0.nextAdd(arg1, arg2);
}
@Override
public void bind(BindInterceptorChain chain, DistinguishedName dn,
Password password, LDAPConstraints constraints)
throws LDAPException {
String user = ((RDN) dn.getDN().getRDNs().get(0)).getValue();
String passwordString = null;
try {
passwordString = new String(password.getValue(), "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
logger.debug("USER: " + user);
logger.debug("PASSWORD: " + passwordString);
if (!passwordString.equals("123"))
throw new LDAPException("Manual Invalid Credentials",
LDAPException.INVALID_CREDENTIALS,
"Manual Invalid Credentials");
logger.debug("NO THROW");
chain.nextBind(dn, password, constraints);
logger.debug("nextBind CALLED.");
}
@Override
public void compare(CompareInterceptorChain arg0, DistinguishedName arg1,
Attribute arg2, LDAPConstraints arg3) throws LDAPException {
arg0.nextCompare(arg1, arg2, arg3);
}
@Override
public void configure(String name, Properties arg1, NameSpace arg2)
throws LDAPException {
this.name = name;
}
@Override
public void delete(DeleteInterceptorChain arg0, DistinguishedName arg1,
LDAPConstraints arg2) throws LDAPException {
arg0.nextDelete(arg1, arg2);
}
@Override
public void extendedOperation(ExetendedOperationInterceptorChain arg0,
ExtendedOperation arg1, LDAPConstraints arg2) throws LDAPException {
arg0.nextExtendedOperations(arg1, arg2);
}
@Override
public String getName() {
return this.getClass().getSimpleName();
}
@Override
public void modify(ModifyInterceptorChain arg0, DistinguishedName arg1,
ArrayList<LDAPModification> arg2, LDAPConstraints arg3)
throws LDAPException {
arg0.nextModify(arg1, arg2, arg3);
}
@Override
public void postSearchComplete(PostSearchCompleteInterceptorChain arg0,
DistinguishedName arg1, Int arg2, Filter arg3,
ArrayList<Attribute> arg4, Bool arg5, LDAPSearchConstraints arg6)
throws LDAPException {
}
@Override
public void postSearchEntry(PostSearchEntryInterceptorChain arg0,
Entry arg1, DistinguishedName arg2, Int arg3, Filter arg4,
ArrayList<Attribute> arg5, Bool arg6, LDAPSearchConstraints arg7)
throws LDAPException {
arg0.nextPostSearchEntry(arg1, arg2, arg3, arg4, arg5, arg6, arg7);
}
@Override
public void rename(RenameInterceptorChain arg0, DistinguishedName arg1,
DistinguishedName arg2, Bool arg3, LDAPConstraints arg4)
throws LDAPException {
arg0.nextRename(arg1, arg2, arg3, arg4);
}
@Override
public void rename(RenameInterceptorChain arg0, DistinguishedName arg1,
DistinguishedName arg2, DistinguishedName arg3, Bool arg4,
LDAPConstraints arg5) throws LDAPException {
arg0.nextRename(arg1, arg2, arg3, arg4, arg5);
}
@Override
public void search(SearchInterceptorChain arg0, DistinguishedName arg1,
Int arg2, Filter arg3, ArrayList<Attribute> arg4, Bool arg5,
Results arg6, LDAPSearchConstraints arg7) throws LDAPException {
arg0.nextSearch(arg1, arg2, arg3, arg4, arg5, arg6, arg7);
}
@Override
public void shutdown() {
}
}
And here is my current myvd.conf file that configures the server:
#Listen on port 389
server.listener.port=10389
#Listen on 636 using SSL
#server.secure.listener.port=636
#server.secure.keystore=/var/keystores/myvd.ks
#server.secure.keypass=secret
#Cnfigure global chains
server.globalChain=LogAllTransactions,router
server.globalChain.LogAllTransactions.className=net.sourceforge.myvd.inserts.DumpTransaction
server.globalChain.LogAllTransactions.config.logLevel=info
server.globalChain.LogAllTransactions.config.label=Global
server.globalChain.router.className=net.sourceforge.myvd.inserts.routing.MasterReplicaRouter
server.globalChain.router.config.specifyToInclude=false
server.globalChain.router.config.readOnly=Rbdconnect
#Configure namespaces
server.nameSpaces=Root,Readusers
#Define RootDSE
server.Root.chain=RootDSE
server.Root.nameSpace=
server.Root.weight=0
server.Root.RootDSE.className=net.sourceforge.myvd.inserts.RootDSE
server.Root.RootDSE.config.namingContexts=o=RBD,c=us
#Define rbdconnect namespace
server.Readusers.chain=dbauth,db
server.Readusers.nameSpace=o=RBD,c=us
server.Readusers.weight=100
server.Readusers.db.className=net.sourceforge.myvd.inserts.jdbc.JdbcInsert
server.Readusers.db.config.driver=org.postgresql.Driver
server.Readusers.db.config.url=jdbc:postgresql://localhost:5432/foo
server.Readusers.db.config.user=bar
server.Readusers.db.config.pass=bat
server.Readusers.db.config.rdn=uid
server.Readusers.db.config.mapping=uid=username,ou=ou,cn=employee_name,givenName=firstname,sn=lastname,mail=email,active=active
server.Readusers.db.config.objectClass=inetOrgPerson
server.Readusers.db.config.sql=SELECT ad_user.isactive AS active, username, ad_user.name AS employee_name, firstname, lastname, email, CASE WHEN ad_role.name IS NULL THEN 'Unknown' ELSE ad_role.name END AS ou FROM ad_user LEFT OUTER JOIN ad_role ON ad_user.default_ad_role_id = ad_role.ad_role_id
server.Readusers.dbauth.className=com.retailingwireless.myvd.DBAuthenticator
I would love someone to enlighten me as to what I'm missing because I've been staring at this for hours now, and (in my usual fashion) it's probably something simple and right under my nose. Thanks for taking the time!