I have put some code together for a client of mine and thought I would share
my experience with the Appfuse community, 
so here goes.

My client uses WSDL to get the fully qualified user 'CN' (LDAP speak).  
We then use LDAP to check that we can bind and finally use another WSDL to
check that the user is authenticated 
to do what they want to do.  An unusual set of interfaces I agree, but this
is how its done.

Note: you may not need to create local classes, my situation did not fit the
mould provided by the acegi implementation.
Note: I would not encourage verbatum copying of this work, it is very
unlikely that what I needed to do will be the same
for you, so this is simply to aid understanding of the building blocks.

1) It all starts with the Authentication manager,

    <bean id="authenticationManager"
class="org.acegisecurity.providers.ProviderManager">
           <property name="providers">
               <list>
                   <ref local="ldapProvider"/>
                   <!--ref local="daoAuthenticationProvider"/-->
                   <ref local="anonymousAuthenticationProvider"/>
               </list>
           </property>
       </bean>

Notice I have removed the normal daoAuthenticationProvider and replaced it
with an ldapProvider.

2) Create the ldapProvider,

     <bean id="clientBindAuthenticator"
class="myclient.service.impl.ClientBindAuthenticator">
         
        <property name="roleDao" ref="roleDaoPersistClient"/>
        <property name="userDetailsMapper" ref="ldapUserDetailsMapper"/>
      </bean>



    <bean id="clientAuthoritiesPopulator"
class="myclient.service.impl.ClientAuthoritiesPopulator">
        <property name="roleDaoWsdl" ref="roleDaoPersistClient"/>
   </bean>

    <bean id="ldapProvider"
class="org.acegisecurity.providers.ldap.LdapAuthenticationProvider">
           <constructor-arg ref="clientBindAuthenticator"/>
           <constructor-arg ref="clientAuthoritiesPopulator"/>
       </bean>

       <bean id="ldapUserDetailsMapper"
class="org.acegisecurity.userdetails.ldap.LdapUserDetailsMapper">
           <property name="rolePrefix" value=""/>
       </bean>

So the ldapProvider is still an LdapAuthenticationProvider but its arguments
are now locally implemented classes.

3) We also need to define the WSDL beans so they can be used in the
authentication an authorisation

      <bean id="getUserInfoConn1"
class="myclient.getuserinfov1.GetUserInfoV1ServiceStub">
         <constructor-arg
value="http://192.24.222.105:80/Client/getUserInfoV1"; />
      </bean>

        <bean id="getGroupMembersConn1"
class="myclient.getgroupmembersv1.GetGroupMembersV1ServiceStub">
         <constructor-arg
value="http://192.24.222.105:80/Client/getGroupMembersV1"; />
      </bean>

    <!-- UserDao: Hibernate implementation -->
    <bean id="roleDaoPersistClient"
class="myclient.dao.persist.RoleDaoWsdl">
        <property name="groupname" value="myGroup"/>
        <property name="adminUsername" value="user"/>
        <property name="adminPassword" value="password"/>
         <property name="getUserInfoConnectionPoolManager"
ref="getUserInfoConnectionPoolManager"/>
        <property name="getGroupMembersConnectionPoolManager"
ref="getGroupMembersConnectionPoolManager"/>
    </bean>

    <bean id="lookupDaoPersistClient"
class="myclient.dao.persist.LookupDaoWsdl">
            <property name="roleDao" ref="roleDaoPersistClient"/>
    </bean>

    <bean id="ldapInitialContext1"
class="org.acegisecurity.ldap.DefaultInitialDirContextFactory">
        <constructor-arg value="${ldap.url}/${ldap.base}"/>
        <property name="extraEnvVars">
            <map>
                <entry key="com.sun.jndi.ldap.connect.timeout"
value="150"></entry>
            </map>
        </property>
    </bean>

So our role class will now use wsdl calls to get information about the
clients CN and allowed roles.
I used Axis2 to create all of the Java code for the WSDL's and then simply
created beans around them.

The last bean defines the ldap initial context which we will use to make the
LDAP call.


4) Create the local controlling code.  The ClientBindAuthenticator is
called, here is my code

public class ClientBindAuthenticator implements LdapAuthenticator,
InitializingBean, MessageSourceAware {

    private RoleDao roleDao = null;
    private ConnectionPoolManager mConnectionPoolManager = null;

    public ConnectionPoolManager getConnectionPoolManager() {
        return mConnectionPoolManager;
    }

    public void setConnectionPoolManager(ConnectionPoolManager
pConnectionPoolManager) {
        mConnectionPoolManager = pConnectionPoolManager;
    }

    public RoleDao getRoleDao() {
        return roleDao;
    }

    public void setRoleDao(RoleDao roleDao) {
        this.roleDao = roleDao;
    }

    public ClientBindAuthenticator(){

     }

    public String[] getUserAttributes(){
        return null;
    }

    private LdapEntryMapper mUserDetailsMapper;

    public LdapEntryMapper getUserDetailsMapper(){
        return mUserDetailsMapper;
    }

    public void setUserDetailsMapper(LdapEntryMapper pUserDetailsMapper){
        this.mUserDetailsMapper = pUserDetailsMapper;
    }


    public LdapUserDetails authenticate(String pUsername, String pPassword)
{
        LdapUserDetails ldapUserDetails = null;
        try {
            QueryResponse qr = getRoleDao().getUserQueryResponse(pUsername);
            if ( ( qr != null ) && ( "found".equals(qr.getResult()) ) &&
canBindWithDn(qr.getDn(), pPassword) ){
                LdapUserDetailsImpl.Essence ldapUserDetailsImplEssence = new
LdapUserDetailsImpl.Essence();
               
ldapUserDetailsImplEssence.setAccountNonExpired(!qr.isAccountExpired());
               
ldapUserDetailsImplEssence.setAccountNonLocked(!qr.isAccountDissabled());
               
ldapUserDetailsImplEssence.setCredentialsNonExpired(!qr.isAccountExpired());
                ldapUserDetailsImplEssence.setDn(qr.getDn());
               
ldapUserDetailsImplEssence.setEnabled(!qr.isAccountDissabled());
                ldapUserDetailsImplEssence.setPassword(pPassword);
                ldapUserDetailsImplEssence.setUsername(pUsername);
                ldapUserDetails =
ldapUserDetailsImplEssence.createUserDetails();
            }
        } catch ( Throwable e ){
            e.printStackTrace();
            throw new BadCredentialsException("Could not deturmine the
validity of your credencials");
        }


        if ( ldapUserDetails == null ){
            throw new BadCredentialsException("The Credentials you have
entered are not valid for use of the optimise system");
        }

        return ldapUserDetails;  //To change body of implemented methods use
File | Settings | File Templates.
    }

    public boolean canBindWithDn(String userDn, String password) {


        this.getConnectionPoolManager().resetPoolParticipantsIterator();
        DefaultInitialDirContextFactory poolParticipant = null;
        boolean succeeded = false;
        boolean badCreds = false;
        Date now = new Date();
        while ( ( ! succeeded ) && ( ! badCreds ) &&
                ( poolParticipant =
(DefaultInitialDirContextFactory)this.getConnectionPoolManager().getNextPoolParticipant()
) != null ){
                try {

                    poolParticipant.setManagerDn(userDn);
                    poolParticipant.setManagerPassword(password);
                    // this call blows up if the user is no good
                    System.out.println("before bind call");
                    DirContext dirContext =
poolParticipant.newInitialDirContext();
                    System.out.println("after bind call, took : " + (new
Date().getTime() - now.getTime()) + "ms");
                    dirContext.close();
                   
this.getConnectionPoolManager().setPoolParticipantIsUp(poolParticipant,
true);
                    succeeded = true;
                } catch ( BadCredentialsException bad ){
                   
this.getConnectionPoolManager().setPoolParticipantIsUp(poolParticipant,
true);
                    badCreds = true;
                } catch (Throwable e) {
                    // This will be thrown if an invalid user name is used
and the method may
                    // be called multiple times to try different names, so
we trap the exception
                    // unless a subclass wishes to implement more
specialized behaviour.
                   
this.getConnectionPoolManager().setPoolParticipantIsUp(poolParticipant,
false);
                    System.out.println("Threw error in bind : " + e);
                    System.out.println("after failed bind call, took : " +
(new Date().getTime() - now.getTime()) + "ms");
                }
            }
            return succeeded;
       }


    public void afterPropertiesSet() throws Exception {
        //To change body of implemented methods use File | Settings | File
Templates.
    }

    public void setMessageSource(MessageSource messageSource) {
        //To change body of implemented methods use File | Settings | File
Templates.
    }
}

So in the main call to authenticate a call is made to the role dao to use
the wsdl to get the users CN.
A method called canBindWithDn is then called with this CN and password.
I found the best way to understand all the security stuff was to actually
find the code and look at it, when
you do you will probably be suprised how gracefully simple it is.
(Try to ignore the added complexity of the connection pool manager I had to
add)
The LDAP call gets a DefaultInitialDirConnectionFactory (defined as a bean),
sets the DN and password and attempts to bind.
If for any reason it cannot(account dissabled etc) the bind will fail.  
Thats all I need to know from this call, are they authenticated, yes!
Finally, if the bind was a success the ldapUserDetailsImpl.Essence is filled
in to pass back to the client.
That information will be stored in the session for reference(again look at
the code and the magic will become mere
trickery)

5) The Role WSDL work, have a look at the code,

public class RoleDaoWsdl implements RoleDao  {

    private ConnectionPoolManager mGetUserInfoConnectionPoolManager;
    private ConnectionPoolManager mGetGroupMembersConnectionPoolManager;
    private String mAdminUsername;
    private String mAdminPassword;

    public ConnectionPoolManager getGetUserInfoConnectionPoolManager() {
        return mGetUserInfoConnectionPoolManager;
    }

    public void setGetUserInfoConnectionPoolManager(ConnectionPoolManager
pGetUserInfoConnectionPoolManager) {
        mGetUserInfoConnectionPoolManager =
pGetUserInfoConnectionPoolManager;
    }

    public ConnectionPoolManager getGetGroupMembersConnectionPoolManager() {
        return mGetGroupMembersConnectionPoolManager;
    }

    public void
setGetGroupMembersConnectionPoolManager(ConnectionPoolManager
pGetGroupMembersConnectionPoolManager) {
        mGetGroupMembersConnectionPoolManager =
pGetGroupMembersConnectionPoolManager;
    }

    public String getAdminUsername() {
        return mAdminUsername;
    }

    public void setAdminUsername(String pAdminUsername) {
        this.mAdminUsername = pAdminUsername;
    }

    public String getAdminPassword() {
        return mAdminPassword;
    }

    public void setAdminPassword(String pAdminPassword) {
        this.mAdminPassword = pAdminPassword;
    }

    private String mGroupname = null;

    public String getGroupname() {
        return mGroupname;
    }

    public void setGroupname(String pGroupname) {
        this.mGroupname = pGroupname;
    }

    public myclient.getuserinfov1.internal.response.QueryResponse
getUserQueryResponse(String pUsername) throws UsernameNotFoundException {
        myclient.getuserinfov1.internal.response.QueryResponse qr = null;
        try {
            System.out.println("Going to call getUserInfo with username : "
+ pUsername);
            GetUserInfo getUserInfo = new GetUserInfo();
            getUserInfo.setSvc_acct(getAdminUsername());
            getUserInfo.setSvc_pwd(getAdminPassword());
            getUserInfo.setUserid(pUsername);
           
this.getGetUserInfoConnectionPoolManager().resetPoolParticipantsIterator();
            boolean success = false;
            Date start = new Date();
            GetUserInfoV1ServiceStub pooledUserInfoService = null;
            while ( ( ! success ) &&
                    ( ( pooledUserInfoService =
(GetUserInfoV1ServiceStub)this.getGetUserInfoConnectionPoolManager().getNextPoolParticipant()
) != null ) ){
                try {
                    GetUserInfoCallbackHandler callbackHandler = new
GetUserInfoCallbackHandler(pooledUserInfoService,
this.getGetUserInfoConnectionPoolManager().getTimeout());
                    qr =
callbackHandler.makeBlockingCallToGetResults(getUserInfo);
                    System.out.println("Completed in : " + (new
Date().getTime() - start.getTime()) + "ms" );
                   
this.getGetUserInfoConnectionPoolManager().setPoolParticipantIsUp(pooledUserInfoService,
true);
                    // got a result regardless of good or bad creds
                    success = true;
                    boolean badCreds = "found".equals(qr.getResult());
                    System.out.println("Finished call to getUserInfo : " + (
badCreds ? "Successfully" : "Bad Credencials"));
                     String[] attr = qr.getAttrWithName("memberOf");
                     if ( ( attr != null ) && ( attr.length > 0 ) ){
                         for (int i=0; i<attr.length;i++){
                             System.out.println("group : " + attr[i]);
                         }
                     } else {
                         System.out.println("group attr not returned");
                     }
                } catch ( Throwable ee ){
                    System.out.println("Failed in : " + (new
Date().getTime() - start.getTime()) + "ms" );
                   
this.getGetUserInfoConnectionPoolManager().setPoolParticipantIsUp(pooledUserInfoService,
false);
                    continue;
                } finally {
                    pooledUserInfoService.cleanup();
                }

            }
        } catch ( Throwable e ){
            throw new UsernameNotFoundException("Failed to get user
QueryResponse",e);
        }
        return qr;
    }

    // This will currently only pass back one role ever as it is just
looking for one groupname
    private Set doRolesByUsernameCall(String pUsername) throws
UsernameNotFoundException {
        Set retSet = new HashSet();
        try {
            GetGroupMembers groupMembers = new GetGroupMembers();
            groupMembers.setSvc_acct(getAdminUsername());
            groupMembers.setSvc_pwd(getAdminPassword());
            groupMembers.setGroup(getGroupname());
            System.out.println("Groupname looking for is : " +
getGroupname());
           
this.getGetGroupMembersConnectionPoolManager().resetPoolParticipantsIterator();
            boolean success = false;
            QueryResponse qr = null;
            Date start = new Date();
            GetGroupMembersV1ServiceStub pooledGroupMembersService = null;
            while ( ( ! success ) &&
                    ( ( pooledGroupMembersService =
(GetGroupMembersV1ServiceStub)this.getGetGroupMembersConnectionPoolManager().getNextPoolParticipant()
) != null ) ){
                try {
                    GetGroupMembersCallbackHandler callbackHandler = new
GetGroupMembersCallbackHandler(pooledGroupMembersService,
this.getGetGroupMembersConnectionPoolManager().getTimeout());
                   
pooledGroupMembersService.startgetGroupMembersV1Operation(groupMembers,callbackHandler);
                    qr =
callbackHandler.makeBlockingCallToGetResults(groupMembers);
                    System.out.println("Completed in : " + (new
Date().getTime() - start.getTime()) + "ms" );

                   
this.getGetGroupMembersConnectionPoolManager().setPoolParticipantIsUp(pooledGroupMembersService,
true);
                    Set validUsers = getUsersInGroup(qr);
                    System.out.println("Got a return set with size " +
(validUsers != null ? validUsers.size() + "" : "empty set"));
                    if ( ( validUsers != null ) && ( validUsers.size() > 0 )
){
                        Iterator validUsersIter = validUsers.iterator();
                        while (validUsersIter.hasNext() ){
                            String validUser =
(String)validUsersIter.next();
                            System.out.println("group role : " + validUser
);
                            if ( validUser.equalsIgnoreCase(pUsername) ){
                                System.out.println("Got the match,
allocating role");
                                retSet.add(TestModelHelper.getTestRole());
                                break;
                            }
                        }
                } else {
                    System.out.println("group role attr not returned");
                }
                    success = true;
                } catch ( Throwable ee ){
                    System.out.println("Failed in : " + (new
Date().getTime() - start.getTime()) + "ms" );
                   
this.getGetGroupMembersConnectionPoolManager().setPoolParticipantIsUp(pooledGroupMembersService,
false);
                    continue;
                } finally {
                    pooledGroupMembersService.cleanup();
                }


            }

        } catch ( Throwable e ){
            e.printStackTrace();
            throw new UsernameNotFoundException("Failed call to
getGroupMembers",e);
        }
        return retSet;
    }

    private Set getUsersInGroup(QueryResponse qr) {
        Set retSet = new HashSet();
        if ( ( qr != null ) &&
             ( qr.getMembers() != null ) &&
             ( qr.getMembers().getValue() != null ) ){
            Members_type2 members = qr.getMembers();
            Value_type1[] values = members.getValue();
            for (int i=0; i<values.length;i++){
                Value_type1 value = values[i];
                retSet.add(value.getUserPrincipalName());
            }
        }
        return retSet;
    }

    public Set getRolesByUsername(String pUsername) throws
UsernameNotFoundException {
        return this.doRolesByUsernameCall(pUsername);
    }

    public List getRoles(Role role) {
        //return getHibernateTemplate().find("from Role");
        return TestModelHelper.getRoles();
    }

    public Role getRole(Long roleId) {
        //return (Role) getHibernateTemplate().get(Role.class, roleId);
        throw new RuntimeException("Method not supported");
    }

    public Role getRoleByName(String rolename) {
        /**List roles = getHibernateTemplate().find("from Role where
name=?", rolename);
        if (roles.isEmpty()) {
            return null;
        } else {
            return (Role) roles.get(0);
        }**/
        throw new RuntimeException("Method not supported");
    }

    public void saveRole(Role role) {
        throw new RuntimeException("Method not supported");

        //getHibernateTemplate().saveOrUpdate(role);
    }

    public void removeRole(String rolename) {
        //Object role = getRoleByName(rolename);
        //getHibernateTemplate().delete(role);
    }

    public List getObjects(Class clazz) {
        return getRoles(null);
    }

    public Object getObject(Class clazz, Serializable id) {
        throw new RuntimeException("Method not supported");

    }

    public void saveObject(Object o) {
        //To change body of implemented methods use File | Settings | File
Templates.
    }

    public void removeObject(Class clazz, Serializable id) {
        throw new RuntimeException("Method not supported");
        //To change body of implemented methods use File | Settings | File
Templates.
    }


}

As can be seen from this over complex file, is that the role information is
got from calling the two WSDL beans.
One gets the user information and the other gets the users in that group(not
very efficient, but it works).

Thats it!

I think it makes sence.  Well to someone who has been wandering around this
code for several days it does anyway.
I was not going completely for elegance I was going for delivery, as is the
way of things; but it worked for me.

Feel free to comment, but remember I am not the great and powerful Matt
Raible.  Long live Appfuse :)

Thanks

Nigel


-- 
View this message in context: 
http://www.nabble.com/Security-with-LDAP-and-WSDL-tf3887233s2369.html#a11018942
Sent from the AppFuse - User mailing list archive at Nabble.com.

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to