I was trying to deploy Slide under Windows XP Pro SP1 using the
JNDIPrincipalStore to tap into our company's LDAP server with over 4,000
users.  The desired setup was to have Slide access our LDAP server so that:

        1. Users of our company's other systems could use Slide with the
same username and password.
        2. We wouldn't have to manually enter usernames and passwords into
Slide (and ask users for their passwords, which we don't know and shouldn't
need to know)

We also wanted to control who had access to specific files and directories
using Slide's roles and ACL capabilities.  Although the user information
would be coming from an LDAP server, we didn't want the role information
coming from the LDAP server.  We would set up the roles in Slide.  So the
user would authenticate themselves with the same username and password that
they normally used (which is stored in the LDAP server), and if they were a
valid user in the LDAP database, then Slide's roles would determine which
files under Slide's control that they had access to.

In the beginning, I wasn't that knowledgeable about LDAP and JNDI, but after
a lot of experimenting and exchanging several e-mails with James Mason (the
creator of Slide's JNDIPrincipalStore) about the problems I was encountering
trying to setup the JNDIPrincipalStore and Tomcat's JNDIRealm (which is
embedded in a JBoss 4.0.0 server), I found the solution.

First, I used JNDIRealm's bind mode to authenticate users to the Slide
server, since the JNDIPrincipalStore does not keep user passwords in its
store.  Our LDAP server encodes passwords using Unix's crypt algorithm.
Since JDK 1.4 does not come with a MessageDigest subclass for Unix crypt (it
only has MD5 and SHA), I couldn't use the digest attribute in the JNDIRealm
configuration.  Bind mode lets the LDAP server handle the password-encoding
and comparisons for authentication, as opposed to the JNDIRealm.  To
correctly setup the JNDIRealm using bind mode, you'll need to make these
changes to your configuration:

        1. Remove (or comment out) all references to the JAASRealm.  This
includes the JAASRealm configuration in your webapp's context.xml file (if
you're deploying under a recent version of Tomcat), and the security-domain
element that refers to the SlideLoginModule in JBoss' jboss-web.xml file.

        2. Make sure that you have a security-role element in your web.xml
file that corresponds to whatever role(s) that your users belong to on your
LDAP server.  This is so that JNDIRealm's bind mode can correctly
authenticate users using info on the LDAP server.  This also means that, in
the web.xml file, wherever Slide references roles (such as root, guest, and
user), you should have a reference for the role(s) that your users belong to
on the LDAP server.

        3. In your webapp's context.xml file, add this element under the
Context element:

                <Realm className="org.apache.catalina.realm.JNDIRealm"
                        debug="0"
                        contextFactory="com.sun.jndi.ldap.LdapCtxFactory"
                        connectionURL="xxxxxxxx"
                        userPattern="uid={0},ou=xxxxxx,o=xxxxxxx"
                        userRoleName="xxxx" />

           Where the connectionURL is the URL of your LDAP server, such as
ldap://ldap.company.com:389.  The userPattern attribute is the location on
your LDAP server where usernames can be found.  The username that users
enter during authentication is put in place of the {0} and the resulting
string is used to find the user on the LDAP server.  If your LDAP server
uses an attribute in the user's record to specify which roles that user
belongs to, use the userRoleName attribute to tell JNDIRealm which attribute
to look at for role information.  You can set the debug attribute to 99 to
see the steps that the JNDIRealm is taking to authenticate users to Slide.
See Tomcat's JNDIRealm documentation
(http://tomcat.apache.org/tomcat-5.0-doc/realm-howto.html#JNDIRealm) for a
more complete description of configuring JNDIRealm to work with your LDAP
setup.

        As for the JNDIPrincipalStore setup, I used the standard file-based
store for everything but the /users directory, which JNDIPrincipalStore
handled.  Here's how I setup the JNDIPrincipalStore:

        <store name="ldapusers">
                <nodestore
classname="org.apache.slide.store.txjndi.JNDIPrincipalStore">
                          <parameter
name="cache.refresh.checkrate">15</parameter>
                          <parameter
name="cache.refresh.rate">800</parameter>
                          <parameter
name="cache.refresh.threshold">120000</parameter>
                      <parameter
name="jndi.container">ou=xxxxx,o=xxxxx</parameter>  
                      <parameter name="jndi.attributes.rdn">uid</parameter>

                <parameter
name="jndi.search.filter">(objectClass=inetorgperson)</parameter>
                      <parameter
name="jndi.search.scope">ONELEVEL_SCOPE</parameter>
                      <parameter
name="jndi.search.attributes">uid</parameter>  
                      <parameter
name="java.naming.provider.url">xxxxx</parameter>
                          <parameter
name="java.naming.factory.initial">com.sun.jndi.ldap.LdapCtxFactory</paramet
er>
                </nodestore>
                                
                <sequencestore
classname="org.apache.slide.store.txfile.FileSequenceStore">
                        <parameter
name="rootpath">C:/jboss-4.0.0/server/slide-cms/data/usersequencestore/seque
nce</parameter>
                </sequencestore>  
                                
              <securitystore>  
                    <reference store="nodestore"/>
            </securitystore>  
            <lockstore>  
                        <reference store="nodestore"/>  
              </lockstore>  
                <revisiondescriptorsstore>  
                <reference store="nodestore"/>  
              </revisiondescriptorsstore>  
            <revisiondescriptorstore>  
                <reference store="nodestore"/>  
                </revisiondescriptorstore>  
            <contentstore>
                        <reference store="nodestore" />
              </contentstore>  
        </store>

        <scope match="/users" store="ldapusers" />

        The java.naming.provider.url parameter should be the same value that
you used for JNDIRealm's connectionURL attribute.  The jndi.container should
be the entry on your LDAP where users are stored.  See the documentation in
the JNDIPrincipalStore's javadoc
(http://www.jsourcery.com/output/apache/jakarta/slide/server/2.1/org/apache/
slide/store/txjndi/JNDIPrincipalStore.html) for configuration info for the
other parameters.

        You can assign Slide role information the same as always.  The users
will be added to the /users directory with Slide username being equal to the
LDAP username (the uid attribute in the above configuration).  So if there
was a user in the LDAP database whose uid is testuser, that user's directory
in Slide would be located at /users/testuser.

        Now, this alone should be enough to get you up and running using
JNDIRealm for authentication and JNDIPrincipalStore for retrieving usernames
for Slide.  But if you have a lot of users that are being stored in
JNDIPrincipalStore's EHCache and you're running this under Windows
(specifically XP Pro SP1), 
when you log into Slide, and try to look at the contents of the /users
director you may get an exception like this:

        12:24:54,711 INFO  [STDOUT] javax.naming.CommunicationException:
ldap.xxxxxx:389 [Root exception is java.net.BindException: Address already
in use: connect]

        If you don't get this exception but aren't able to view the contents
of the /users directory on a consistent basis, try setting Slide's debug
level higher by setting the logger-level attribute in the root <slide>
element in your Domain.xml file to 7 (which is the DEBUG log level) and
restarting Slide.  The logger-level values can be found at
http://www.jsourcery.com/output/apache/jakarta/slide/server/2.1/constant-val
ues.html#org.apache.slide.util.logger.Logger.DEBUG

        Initially, this exception message would make you think that
connections to the LDAP server aren't being closed or are trying to use a
port already in use by something else.  But it turns out that there weren't
unclosed connections to the LDAP server.  On Windows, when you close a
TCP/IP port, Windows waits for a certain amount of time after closing the
port before that port can be used again.  So in Java, when you call
javax.naming.Context.close(), the underlying connection to the LDAP server
is closed and the method returns.  The port that was used is closed
internally to Windows, but it waits a certain amount of time before
processes can re-use that port.  I had to edit the Windows registry to allow
more open ports (on Windows XP Pro the default maximum user ports is 5000,
which I changed to 65534) and reboot to get it to work.  Here are the links
on Microsoft's site that describe what to change in the registry:

        http://support.microsoft.com/kb/196271/EN-US/
        
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/randz/proto
col/tcp_time-wait_delay.asp

        I only changed the MaxUserPorts setting to get it to work, but you
may want to play with the TcpTimedWaitDelay setting if the idea of allowing
more ports to be opened by user processes bothers you.

        Hopefully, this information will save you a lot of time and
headaches.  A lot of my frustrations were due to my initial lack of
understanding LDAP and JNDI.  I definitely learned a lot more about these
subjects after figuring this problem out.  Thanks goes out to James Mason,
who was patient enough to help a complete stranger with configuration
problems and understanding LDAP and JNDI.  Also, thanks to Google for
searching the Slide mailing list and everywhere else to track down the
source of the errors I was encountering, (especially the underlying Windows
registry problem).

- Michael James

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

Reply via email to