incubator-jspwiki-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ajaqu...@apache.org
Subject svn commit: r796933 - in /incubator/jspwiki/trunk: ./ etc/ etc/ldap/ src/java/org/apache/wiki/ src/java/org/apache/wiki/auth/authorize/ src/java/org/apache/wiki/auth/login/ tests/java/org/apache/wiki/auth/authorize/ tests/java/org/apache/wiki/auth/login/
Date Thu, 23 Jul 2009 02:55:13 GMT
Author: ajaquith
Date: Thu Jul 23 02:55:12 2009
New Revision: 796933

URL: http://svn.apache.org/viewvc?rev=796933&view=rev
Log:
First LDAP support classes checked in. We support an LdapLoginModule and LdapAuthorizer. Both are configured via jspwiki.properties and should be considered experimental. Significant limitations at the moment include the assumption that role searches are done ONLY via anonymous bind. We support OpenLDAP, but not Active Directory (yet). The LdapUserDatabase implementation won't come for a while, either.

Added:
    incubator/jspwiki/trunk/etc/ldap/
    incubator/jspwiki/trunk/etc/ldap/Codec.class   (with props)
    incubator/jspwiki/trunk/etc/ldap/Codec.java
    incubator/jspwiki/trunk/etc/ldap/README
    incubator/jspwiki/trunk/etc/ldap/ldap.conf
    incubator/jspwiki/trunk/etc/ldap/slapd.conf
    incubator/jspwiki/trunk/etc/ldap/test.ldif
    incubator/jspwiki/trunk/src/java/org/apache/wiki/auth/authorize/LdapAuthorizer.java
    incubator/jspwiki/trunk/src/java/org/apache/wiki/auth/login/LdapLoginModule.java
    incubator/jspwiki/trunk/tests/java/org/apache/wiki/auth/authorize/LdapAuthorizerTest.java
    incubator/jspwiki/trunk/tests/java/org/apache/wiki/auth/login/LdapLoginModuleTest.java
Removed:
    incubator/jspwiki/trunk/src/java/org/apache/wiki/auth/login/LocaleCallback.java
Modified:
    incubator/jspwiki/trunk/ChangeLog
    incubator/jspwiki/trunk/etc/jspwiki.properties.tmpl
    incubator/jspwiki/trunk/src/java/org/apache/wiki/Release.java
    incubator/jspwiki/trunk/src/java/org/apache/wiki/auth/login/AbstractLoginModule.java
    incubator/jspwiki/trunk/src/java/org/apache/wiki/auth/login/UserDatabaseLoginModule.java
    incubator/jspwiki/trunk/src/java/org/apache/wiki/auth/login/WikiCallbackHandler.java
    incubator/jspwiki/trunk/tests/java/org/apache/wiki/auth/authorize/AllTests.java
    incubator/jspwiki/trunk/tests/java/org/apache/wiki/auth/login/AllTests.java

Modified: incubator/jspwiki/trunk/ChangeLog
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/ChangeLog?rev=796933&r1=796932&r2=796933&view=diff
==============================================================================
--- incubator/jspwiki/trunk/ChangeLog (original)
+++ incubator/jspwiki/trunk/ChangeLog Thu Jul 23 02:55:12 2009
@@ -1,3 +1,14 @@
+2009-07-23 Andrew Jaquith <ajaquith AT apache DOT org>
+
+        * 3.0.0-svn-134
+        
+        * First LDAP support classes checked in. We support an LdapLoginModule
+        and LdapAuthorizer. Both are configured via jspwiki.properties
+        and should be considered experimental. Significant limitations at the
+        moment include the assumption that role searches are done ONLY via
+        anonymous bind. We support OpenLDAP, but not Active Directory (yet).
+        The LdapUserDatabase implementation won't come for a while, either.
+
 2009-07-22 Janne Jalkanen <jalkanen@apache.org>
 
         * 3.0.0-svn-133

Modified: incubator/jspwiki/trunk/etc/jspwiki.properties.tmpl
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/etc/jspwiki.properties.tmpl?rev=796933&r1=796932&r2=796933&view=diff
==============================================================================
--- incubator/jspwiki/trunk/etc/jspwiki.properties.tmpl (original)
+++ incubator/jspwiki/trunk/etc/jspwiki.properties.tmpl Thu Jul 23 02:55:12 2009
@@ -460,6 +460,7 @@
 #  Supply the JAAS LoginModule class used for custom authentication here.
 #  The implementation MUST have a zero-argument constructor (as noted in the
 #  javax.security.auth.spi.LoginModule Javadocs).
+#jspwiki.loginModule.class = org.apache.wiki.auth.login.LdapLoginModule
 jspwiki.loginModule.class = org.apache.wiki.auth.login.UserDatabaseLoginModule
 
 #
@@ -467,9 +468,10 @@
 #  etc. should be replaced with the actual parameter names. The parameter names and
 # values will be loaded to a Map and passed to the LoginModule as the 'options' parameter
 # when its initialize() method is called. The default UserDatabaseLoginModule class does
-# not need any options.
-#jspwiki.loginModule.options.param1 = value1
-#jspwiki.loginModule.options.param2 = value2
+# not need any options. The example below is for LdapLoginModule.
+#jspwiki.loginModule.options.ldap.connectionURL = ldap://127.0.0.1:4890/
+#jspwiki.loginModule.options.ldap.userPattern = uid={0},ou=people,dc=jspwiki,dc=org
+#jspwiki.loginModule.options.ldap.ssl = false
 
 # 
 #  Cookie authentication & assertion

Added: incubator/jspwiki/trunk/etc/ldap/Codec.class
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/etc/ldap/Codec.class?rev=796933&view=auto
==============================================================================
Binary file - no diff available.

Propchange: incubator/jspwiki/trunk/etc/ldap/Codec.class
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: incubator/jspwiki/trunk/etc/ldap/Codec.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/etc/ldap/Codec.java?rev=796933&view=auto
==============================================================================
--- incubator/jspwiki/trunk/etc/ldap/Codec.java (added)
+++ incubator/jspwiki/trunk/etc/ldap/Codec.java Thu Jul 23 02:55:12 2009
@@ -0,0 +1,24 @@
+public class Codec {
+ 
+   public static final void main( String... args)
+   {
+       for ( String arg : args )
+       {
+           System.out.println( bitString( arg ) );
+       }
+   }
+ 
+   public static String bitString( String source )
+   {
+      StringBuffer s = new StringBuffer();
+      s.append( '\'' );
+      for ( byte b : source.getBytes() )
+      {
+          s.append( Integer.toBinaryString( b ) );
+      }
+      s.append( '\'' );
+      s.append( 'B' );
+      return s.toString();
+   }
+  
+}
\ No newline at end of file

Added: incubator/jspwiki/trunk/etc/ldap/README
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/etc/ldap/README?rev=796933&view=auto
==============================================================================
--- incubator/jspwiki/trunk/etc/ldap/README (added)
+++ incubator/jspwiki/trunk/etc/ldap/README Thu Jul 23 02:55:12 2009
@@ -0,0 +1,38 @@
+This document describes how to set up OpenLDAP with a sample dataset to support
+the LdapAuthorizerTest and LdapLoginModuleTest unit tests. It assumes that OpenLDAP
+is installed on your system and that config files are stored in /etc/openldap.
+OpenLDAP runs on most Unix-like systems. Mac OS X 10.5 Leopard has OpenLDAP installed
+out of the box, and this is the setup this document was written to work with.
+Your mileage may vary.
+
+First-time setup
+1) copy ldap.conf to /etc/openldap
+
+2) copy slapd.conf to /etc/openldap
+
+3) Test that conf file is ok
+/usr/libexec/slapd -d -1 -f /etc/openldap/slapd.conf -Tt -u
+
+4) Start slapd; bind to localhost and run as user 'arj'
+/usr/libexec/slapd -h "ldap://127.0.0.1:4890/" -d -1 -u arj
+
+5) Add user file (connection with password "test")
+ldapadd -x -h 127.0.0.1 -p 4890 -D "cn=Manager,dc=jspwiki,dc=org" -w test -f test.ldif
+
+6) Verify users were added
+ldapsearch -x -h 127.0.0.1 -p 4890 -b 'dc=jspwiki,dc=org' | grep dn
+ldapsearch -x -h 127.0.0.1 -p 4890 -b 'ou=people,dc=jspwiki,dc=org'
+ldapsearch -x -h 127.0.0.1 -p 4890 -b 'ou=roles,dc=jspwiki,dc=org'
+ldapsearch -x -h 127.0.0.1 -p 4890 -b 'dc=jspwiki,dc=org' '(objectClass=groupOfUniqueNames)'
+ldapsearch -x -h 127.0.0.1 -p 4890 -b 'dc=jspwiki,dc=org' '(uniqueMember=uid\3Dadmin,ou\3Dpeople,dc\3Djspwiki,dc\3Dorg)'
+ldapsearch -x -h 127.0.0.1 -p 4890 -b 'dc=jspwiki,dc=org' '(&(objectClass=groupOfUniqueNames)(uniqueMember=uid\3Dadmin,ou\3Dpeople,dc\3Djspwiki,dc\3Dorg))'
+
+Default search is "sub": the current DN + all children, recursively
+-LLL       suppress comments
+-s uid     sort by uid
+| grep dn  handy pipe to list just the dns
+
+Tips
+http://www.grennan.com/ldap-HOWTO.html
+
+The Codec class is designed to create ASN.1 BitStrings. It's a crap implementation but is fine for our purposes here.

Added: incubator/jspwiki/trunk/etc/ldap/ldap.conf
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/etc/ldap/ldap.conf?rev=796933&view=auto
==============================================================================
--- incubator/jspwiki/trunk/etc/ldap/ldap.conf (added)
+++ incubator/jspwiki/trunk/etc/ldap/ldap.conf Thu Jul 23 02:55:12 2009
@@ -0,0 +1,2 @@
+HOST 127.0.0.1
+BASE dc=jspwiki,dc=org

Added: incubator/jspwiki/trunk/etc/ldap/slapd.conf
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/etc/ldap/slapd.conf?rev=796933&view=auto
==============================================================================
--- incubator/jspwiki/trunk/etc/ldap/slapd.conf (added)
+++ incubator/jspwiki/trunk/etc/ldap/slapd.conf Thu Jul 23 02:55:12 2009
@@ -0,0 +1,14 @@
+include   /private/etc/openldap/schema/core.schema
+include   /private/etc/openldap/schema/cosine.schema
+include   /private/etc/openldap/schema/inetorgperson.schema
+include   /private/etc/openldap/schema/nis.schema
+pidfile   /Users/arj/workspace/ldap/run/slapd.pid
+argsfile  /Users/arj/workspace/ldap/run/slapd.args
+database  bdb
+suffix    "dc=jspwiki,dc=org"
+rootdn    "cn=Manager,dc=jspwiki,dc=org"
+# Password for next line is 'test'
+rootpw    {SSHA}RJMzbtilrHxvREvaJP7eTBZzSN1Za73l
+directory	/Users/arj/workspace/ldap/data
+index     objectClass,uid,uidNumber,gidNumber             eq
+index     cn,mail,surname,givenname                       eq,subinitial
\ No newline at end of file

Added: incubator/jspwiki/trunk/etc/ldap/test.ldif
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/etc/ldap/test.ldif?rev=796933&view=auto
==============================================================================
--- incubator/jspwiki/trunk/etc/ldap/test.ldif (added)
+++ incubator/jspwiki/trunk/etc/ldap/test.ldif Thu Jul 23 02:55:12 2009
@@ -0,0 +1,113 @@
+# Define top-level entry
+dn: dc=jspwiki,dc=org
+objectClass: organization
+objectClass: dcObject
+objectClass: top
+dc: jspwiki
+o: jspwiki
+
+# Define an entry to contain people
+# searches for users are based on this entry
+dn: ou=people,dc=jspwiki,dc=org
+objectClass: organizationalUnit
+ou: people
+
+# User Janne Jalkanen
+dn: uid=janne,ou=people,dc=jspwiki,dc=org 
+objectClass: inetOrgPerson
+x500UniqueIdentifier: '101101110111110111110011111001111000110011111001111001110111110111110100111001111001110000110110110001110000110001110100'B
+uid: janne
+sn: Jalkanen
+cn: Janne Jalkanen
+mail: janne@ecyrd.com
+userPassword: {SSHA}1WFv9OV11pD5IySgVH3sFa2VlCyYjbLrcVT/qw==
+
+# User user
+dn: uid=user,ou=people,dc=jspwiki,dc=org 
+objectClass: inetOrgPerson
+x500UniqueIdentifier: '11001111000101100001100011110001110101111000110110101101110001110101110000111001101011011101001100011110010110001110110111100011001011001101100100101101110110110001110100110001110011011011011000101110001101111100110110011110100'B
+uid: user
+sn: User
+cn: Test User
+mail: user@example.com
+userPassword: {SSHA}EW9VbybtKrg2qSM2VTdewF4h6rGc1+2tp8zjfw==
+
+# User Administrator
+dn: uid=admin,ou=people,dc=jspwiki,dc=org 
+objectClass: inetOrgPerson
+x500UniqueIdentifier: '11000101101111100110110011011010011001001100001110011010110111001011001011110001100110101101110100111001110001110110101101110000111011111000011001110110111000111000101100110110010111001011101001101111110011101101100011110010110101'B
+uid: admin
+sn: Administrator
+cn: Administrator
+mail: admin@locahost
+userPassword: {SSHA}6YNKYMwXICUf5pMvYUZumgbFCxZMT2njtUQtJw==
+
+# User Alice
+dn: uid=Alice,ou=people,dc=jspwiki,dc=org 
+objectClass: inetOrgPerson
+x500UniqueIdentifier: '11100111100011001011001101100001101011110001100101011011100011110001111000111001001011011101001100001110001110001101101110000111010011010111000110110111011011100111000111000111100011110001101011110011100101101001100010111000'B
+uid: Alice
+sn: Alice
+cn: Alice
+mail: alice@example.com
+userPassword: {SSHA}3V4zI5W6mT+x5NIHKI2KFQIYBdnAYKNOE9Aj+Q==
+
+# User Bob
+dn: uid=Bob,ou=people,dc=jspwiki,dc=org 
+objectClass: inetOrgPerson
+x500UniqueIdentifier: '11100111001101100101111001110001011010111000011100001011011100101110100110010011011110110111010011001101100100111000101101111000110010011001101101001011011101111100110110010110010011000011011011011011000111101001110011100100110001'B
+uid: Bob
+sn: Bob
+cn: Bob
+mail: bob@example.com
+userPassword: {SSHA}NP3aAmiwK0gHywTe4qbY6klKDqnZ+F9ym9YiLg==
+
+# User Charlie
+dn: uid=Charlie,ou=people,dc=jspwiki,dc=org 
+objectClass: inetOrgPerson
+x500UniqueIdentifier: '1100101101001100110110000110010011011111011111010110110111000011100011010011000110110111010011001101101011110011011011100010111001110110110100101101110011110011110000110101110000111000101110001100011110001011001101100110110010'B
+uid: Charlie
+sn: Charlie
+cn: Charlie
+mail: charlie@example.com
+userPassword: {SSHA}wn81B14F9axtTVYsipQKC2OWQHlc6EcpMSe58Q==
+
+# User Fred Flintstone
+dn: uid=Fred,ou=people,dc=jspwiki,dc=org 
+objectClass: inetOrgPerson
+x500UniqueIdentifier: '1100101100011110011011001011100010110111110101110101101101111001110110110011110101101101110100111001110001111000101011011100010111001110010110011101101110111110010110010011000111100101101001100011110001110001111000011101111100001'B
+uid: Fred
+sn: Flintstone
+cn: Fred Flintstone
+mail: fred@example.com
+userPassword: {SSHA}iDeE9dysPUE28SWd6yeIqiIj9sIVyiMM7VnMKQ==
+
+# User Biff
+dn: uid=Biff,ou=people,dc=jspwiki,dc=org 
+objectClass: inetOrgPerson
+x500UniqueIdentifier: '110000111001011000011000011001111010111001011101101011011100101100001110010111001001011011101001100010110110110100101101110001011001011101101100101011011100101110001100001110101110001111000110100110000111000111100110110001110001'B
+uid: Biff
+sn: Biff
+cn: biff
+mail: biff@example.com
+userPassword: {SSHA}xKAIienaZZHhKTGCNv5Li6lzeemaSs6ZYXTHFQ==
+
+
+# Define an entry to contain LDAP roles.
+# Searches for roles by LdapAuthorizer
+dn: ou=roles,dc=jspwiki,dc=org
+objectClass: organizationalUnit
+ou: roles
+
+# Define the "Admin" role
+dn: cn=Admin,ou=roles,dc=jspwiki,dc=org
+objectClass: groupOfUniqueNames
+cn: Admin
+uniqueMember: uid=admin,ou=people,dc=jspwiki,dc=org
+
+# Define the "Role1" role
+dn: cn=role1,ou=roles,dc=jspwiki,dc=org
+objectClass: groupOfUniqueNames
+cn: Role1
+uniqueMember: uid=Fred,ou=people,dc=jspwiki,dc=org
+uniqueMember: uid=Biff,ou=people,dc=jspwiki,dc=org
\ No newline at end of file

Modified: incubator/jspwiki/trunk/src/java/org/apache/wiki/Release.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/java/org/apache/wiki/Release.java?rev=796933&r1=796932&r2=796933&view=diff
==============================================================================
--- incubator/jspwiki/trunk/src/java/org/apache/wiki/Release.java (original)
+++ incubator/jspwiki/trunk/src/java/org/apache/wiki/Release.java Thu Jul 23 02:55:12 2009
@@ -77,7 +77,7 @@
      *  <p>
      *  If the build identifier is empty, it is not added.
      */
-    public static final String     BUILD         = "133";
+    public static final String     BUILD         = "134";
 
     /**
      *  This is the generic version string you should use

Added: incubator/jspwiki/trunk/src/java/org/apache/wiki/auth/authorize/LdapAuthorizer.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/java/org/apache/wiki/auth/authorize/LdapAuthorizer.java?rev=796933&view=auto
==============================================================================
--- incubator/jspwiki/trunk/src/java/org/apache/wiki/auth/authorize/LdapAuthorizer.java (added)
+++ incubator/jspwiki/trunk/src/java/org/apache/wiki/auth/authorize/LdapAuthorizer.java Thu Jul 23 02:55:12 2009
@@ -0,0 +1,233 @@
+package org.apache.wiki.auth.authorize;
+
+import java.security.Principal;
+import java.util.*;
+
+import javax.naming.Context;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.InitialLdapContext;
+
+import org.apache.wiki.WikiEngine;
+import org.apache.wiki.WikiSession;
+import org.apache.wiki.auth.Authorizer;
+import org.apache.wiki.auth.WikiSecurityException;
+import org.apache.wiki.log.Logger;
+import org.apache.wiki.log.LoggerFactory;
+import org.apache.wiki.util.TextUtil;
+
+/**
+ * Authorizer whose Roles are supplied by LDAP groups.
+ */
+public class LdapAuthorizer implements Authorizer
+{
+    private Hashtable<String, String> m_env;
+
+    private String m_roleBase = null;
+
+    private String m_userPattern = null;
+
+    /**
+     * {@inheritDoc}
+     */
+    public Principal findRole( String role )
+    {
+        try
+        {
+            DirContext ctx = new InitialLdapContext( m_env, null );
+            SearchControls searchControls = new SearchControls();
+            searchControls.setReturningAttributes( new String[0] );
+            NamingEnumeration<SearchResult> roles = ctx.search( m_roleBase, "(cn=" + role + ")", searchControls );
+            if( roles.hasMore() )
+            {
+                return new Role( role );
+            }
+        }
+        catch( NamingException e )
+        {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Principal[] getRoles()
+    {
+        Set<Role> foundRoles = new HashSet<Role>();
+        try
+        {
+            DirContext ctx = new InitialLdapContext( m_env, null );
+            SearchControls searchControls = new SearchControls();
+            searchControls.setReturningAttributes( new String[] { "cn" } );
+            NamingEnumeration<SearchResult> roles = ctx.search( m_roleBase, "(objectClass=groupOfUniqueNames)", searchControls );
+            while ( roles.hasMore() )
+            {
+                SearchResult foundRole = roles.next();
+                String roleName = (String) foundRole.getAttributes().get( "cn" ).get( 0 );
+                foundRoles.add( new Role( roleName ) );
+            }
+        }
+        catch( NamingException e )
+        {
+            e.printStackTrace();
+        }
+        return foundRoles.toArray( new Role[foundRoles.size()] );
+    }
+
+    private static final Logger log = LoggerFactory.getLogger( LdapAuthorizer.class );
+
+    /**
+     * Property that supplies the connection URL for the LDAP server, e.g.
+     * <code>ldap://127.0.0.1:4890/</code>. This property is also used by
+     * {@link org.apache.wiki.auth.login.LdapLoginModule}.
+     */
+    protected static final String PROPERTY_CONNECTION_URL = "jspwiki.loginModule.options.ldap.connectionURL";
+
+    /**
+     * Property that indicates whether to use SSL for connecting to the LDAP
+     * server. This property is also used by
+     * {@link org.apache.wiki.auth.login.LdapLoginModule}.
+     */
+    protected static final String PROPERTY_SSL = "jspwiki.loginModule.options.ldap.ssl";
+
+    /**
+     * Property that supplies the DN pattern for finding users, e.g.
+     * <code>uid={0},ou=people,dc=jspwiki,dc=org</code> This property is also
+     * used by {@link org.apache.wiki.auth.login.LdapLoginModule}.
+     */
+    protected static final String PROPERTY_USER_PATTERN = "jspwiki.loginModule.options.ldap.userPattern";
+
+    /**
+     * Property that supplies the DN pattern for finding roles, e.g.
+     * <code>ou=roles,dc=jspwiki,dc=org</code>
+     */
+    protected static final String PROPERTY_ROLE_BASE = "jspwiki.ldap.roleBase";
+
+    /**
+     * {@inheritDoc}
+     */
+    public void initialize( WikiEngine engine, Properties props ) throws WikiSecurityException
+    {
+        Hashtable<String, String> env = new Hashtable<String, String>();
+        env.put( Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory" );
+        env.put( Context.SECURITY_AUTHENTICATION, "none" );
+
+        // LDAP server to search
+        String option = (String) props.get( PROPERTY_CONNECTION_URL );
+        if( option != null && option.trim().length() > 0 )
+        {
+            env.put( Context.PROVIDER_URL, option.trim() );
+        }
+
+        // Use SSL?
+        option = (String) props.get( PROPERTY_SSL );
+        boolean ssl = TextUtil.isPositive( option );
+        env.put( Context.SECURITY_PROTOCOL, ssl ? "ssl" : "none" );
+        m_env = env;
+
+        if( log.isDebugEnabled() )
+        {
+            log.debug( "Built JNDI environment for LDAP search.", m_env );
+        }
+
+        // DN pattern for finding users
+        option = (String) props.get( PROPERTY_USER_PATTERN );
+        if( option != null && option.trim().length() > 0 )
+        {
+            m_userPattern = option.trim();
+        }
+        else
+        {
+            throw new WikiSecurityException( PROPERTY_USER_PATTERN + " not supplied." );
+        }
+
+        // DN pattern for finding roles
+        option = (String) props.get( PROPERTY_ROLE_BASE );
+        if( option != null && option.trim().length() > 0 )
+        {
+            m_roleBase = option.trim();
+        }
+        else
+        {
+            throw new WikiSecurityException( PROPERTY_ROLE_BASE + " not supplied." );
+        }
+        
+        // Do a quick connection test, and fail-fast if needed
+        try
+        {
+            new InitialLdapContext( m_env, null );
+        }
+        catch( NamingException e )
+        {
+            throw new WikiSecurityException( "Could not start LdapAuthorizer! Cause: " + e.getMessage(), e );
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * This implementation returns <code>true</code> when at least one of the
+     * user login Principals contained in the WikiSession's Subject belongs to
+     * an LDAP group contained in the role-base DN. The user DN is constructed
+     * from the user Principal, where the <code>uid</code> of the user's DN is
+     * the Principal name, and the rest of the DN is determined by
+     * {@link #PROPERTY_USER_PATTERN}.
+     * </p>
+     * <p>
+     * To make an accurate search, Principals that are of type {@link Role},
+     * {@link org.apache.wiki.auth.GroupPrincipal} are excluded from
+     * consideration. So are {@link org.apache.wiki.auth.WikiPrincipal} whose
+     * type are {@link org.apache.wiki.auth.WikiPrincipal#FULL_NAME} or
+     * {@link org.apache.wiki.auth.WikiPrincipal#WIKI_NAME}.
+     * </p>
+     * <p>
+     * For example, consider an LDAP user base of
+     * <code>ou=people,dc=jspwiki,dc=org</code>, and a WikiSession whose subject
+     * contains three user principals, the two built-in roles <code>ALL</code>
+     * and <code>AUTHENTICATED</code>, and a group principal
+     * <code>MyGroup</code>:
+     * </p>
+     * <blockquote><code>WikiPrincipal.LOGIN_NAME "biggie.smalls"<br/>
+     * WikiPrincipal.FULL_NAME "Biggie Smalls"<br/>
+     * WikiPrincipal.WIKI_NAME "BiggieSmalls"<br/>
+     * Role.ALL
+     * Role.AUTHENTICATED
+     * GroupPrincipal "MyGroup"</code></blockquote>
+     * <p>
+     * In this case, only WikiPrincipal.LOGIN_NAME "biggie.smalls" would be
+     * examined. An LDAP search would be constructed that searched the LDAP role
+     * base for an object whose <code>objectClass</code> was of type
+     * <code>groupOfUniqueNames</code> and whose
+     * <code>uniqueMember<code> attribute contained the value
+     * <code>uid=biggie.smalls,ou=people,dc=jspwiki,dc=org</code>.
+     * </p>
+     */
+    public boolean isUserInRole( WikiSession session, Principal role )
+    {
+        // Build DN
+        String uid = session.getLoginPrincipal().getName();
+        String dn = m_userPattern.replace( "{0}", uid ).trim();
+        dn = dn.replace( "=", "\\3D" );
+
+        try
+        {
+            DirContext ctx = new InitialLdapContext( m_env, null );
+            SearchControls searchControls = new SearchControls();
+            searchControls.setReturningAttributes( new String[0] );
+            String filter = "(&(objectClass=groupOfUniqueNames)(cn=" + role.getName() + ")(uniqueMember=" + dn + "))";
+            NamingEnumeration<SearchResult> roles = ctx.search( m_roleBase, filter, searchControls );
+            return roles.hasMore();
+        }
+        catch( NamingException e )
+        {
+            e.printStackTrace();
+        }
+        return false;
+    }
+
+}

Modified: incubator/jspwiki/trunk/src/java/org/apache/wiki/auth/login/AbstractLoginModule.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/java/org/apache/wiki/auth/login/AbstractLoginModule.java?rev=796933&r1=796932&r2=796933&view=diff
==============================================================================
--- incubator/jspwiki/trunk/src/java/org/apache/wiki/auth/login/AbstractLoginModule.java (original)
+++ incubator/jspwiki/trunk/src/java/org/apache/wiki/auth/login/AbstractLoginModule.java Thu Jul 23 02:55:12 2009
@@ -20,13 +20,18 @@
  */
 package org.apache.wiki.auth.login;
 
+import java.io.IOException;
 import java.security.Principal;
 import java.util.Collection;
 import java.util.HashSet;
+import java.util.Locale;
 import java.util.Map;
 
 import javax.security.auth.Subject;
+import javax.security.auth.callback.Callback;
 import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.LanguageCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
 import javax.security.auth.login.LoginException;
 import javax.security.auth.spi.LoginModule;
 
@@ -38,7 +43,7 @@
 
 /**
  * Abstract JAAS {@link javax.security.auth.spi.LoginModule}that implements
- * base functionality. The methods {@link #login()} and {@link #commit()} must
+ * base functionality. The method {@link #login()} must
  * be implemented by subclasses. The default implementations of
  * {@link #initialize(Subject, CallbackHandler, Map, Map)}, {@link #abort()} and
  * {@link #logout()} should be sufficient for most purposes.
@@ -51,6 +56,12 @@
     private static final Logger   log = LoggerFactory.getLogger( AbstractLoginModule.class );
 
     protected CallbackHandler m_handler;
+    
+    /**
+     * Stores the Locale, as determined by {@link #initialize(Subject, CallbackHandler, Map, Map)}.
+     * If not set, defaults to {@link Locale#getDefault()}.
+     */
+    protected Locale m_locale = null;
 
     protected Map<String,?>             m_options;
 
@@ -174,8 +185,14 @@
 
     /**
      * Initializes the LoginModule with a given <code>Subject</code>,
-     * callback handler, options and shared state. In particular, the member
-     * variable <code>m_principals</code> is initialized as a blank Set.
+     * callback handler, options and shared state. The member
+     * variable <code>m_principals</code> is initialized as an empty Set.
+     * In addition, if the CallbackHander supports the
+     * {@link javax.security.auth.callback.LanguageCallback} callback, the Locale
+     * that callback returns will be assigned to protected variable <code>m_locale</code>.
+     * The value of <code>m_locale</code> can then be used by subclasses to provide
+     * localized error messages if needed. If the CallbackHandler does not support
+     * LanguageCallback, the JRE's default Locale will be used.
      * @see javax.security.auth.spi.LoginModule#initialize(javax.security.auth.Subject,
      *      javax.security.auth.callback.CallbackHandler, java.util.Map,
      *      java.util.Map)
@@ -205,6 +222,22 @@
         }
         // Stash the previous WikiPrincipals; we will flush these if login succeeds
         m_previousWikiPrincipals.addAll( subject.getPrincipals( WikiPrincipal.class ) );
+        
+        // Try to figure out the user's Locale, using the JDK LanguageCallback first
+        Callback[] callbacks = new Callback[] { new LanguageCallback() };
+        try
+        {
+            callbackHandler.handle( callbacks );
+            m_locale = ((LanguageCallback)callbacks[0]).getLocale();
+        }
+        catch( IOException e ) { }
+        catch( UnsupportedCallbackException e ) { }
+        
+        // If still not set, use the JRE default
+        if ( m_locale == null )
+        {
+            m_locale = Locale.getDefault();
+        }
     }
 
     /**

Added: incubator/jspwiki/trunk/src/java/org/apache/wiki/auth/login/LdapLoginModule.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/java/org/apache/wiki/auth/login/LdapLoginModule.java?rev=796933&view=auto
==============================================================================
--- incubator/jspwiki/trunk/src/java/org/apache/wiki/auth/login/LdapLoginModule.java (added)
+++ incubator/jspwiki/trunk/src/java/org/apache/wiki/auth/login/LdapLoginModule.java Thu Jul 23 02:55:12 2009
@@ -0,0 +1,294 @@
+/*
+    JSPWiki - a JSP-based WikiWiki clone.
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.    
+ */
+package org.apache.wiki.auth.login;
+
+import java.io.IOException;
+import java.security.Principal;
+import java.util.*;
+
+import javax.naming.*;
+import javax.naming.directory.*;
+import javax.naming.ldap.InitialLdapContext;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.auth.login.FailedLoginException;
+import javax.security.auth.login.LoginException;
+
+import org.apache.wiki.auth.WikiPrincipal;
+import org.apache.wiki.auth.authorize.Role;
+import org.apache.wiki.i18n.InternationalizationManager;
+import org.apache.wiki.log.Logger;
+import org.apache.wiki.log.LoggerFactory;
+import org.apache.wiki.util.TextUtil;
+
+/**
+ * <p>
+ * LoginModule that authenticates users against an LDAP server with the user's
+ * supplied credentials. The authentication is performed by binding to the LDAP
+ * server using the user's credentials, with or without SSL. To use this
+ * LoginModule, callers must initialize it with a JAAS options Map that supplies
+ * the following key/value pairs:
+ * </p>
+ * <ul>
+ * <li>{@link #OPTIONS_CONNECTION_URL} - the connection string for the LDAP
+ * server, for example <code>ldap://ldap.jspwiki.org:389/</code></li>
+ * <li>{@link #OPTIONS_USER_PATTERN} - the pattern used for forming the
+ * distinguished name for the user. The user ID supplied during the login will
+ * be substituted into the <code>{0}</code> token in this pattern, if it
+ * contains one. For example, if the user pattern is
+ * <code>uid={0},ou=people,dc=jspwiki,dc=org</code> and the user name supplied
+ * during login is <code>fflintstone</code>, the DN that will be used for
+ * authentication will be
+ * <code>uid=fflintstone,ou=people,dc=jspwiki,dc=org</code></li>
+ * <li>{@link #OPTIONS_SSL} - Optional parameter that specifies whether to use
+ * SSL when connecting to the LDAP server. Values like <code>true</code> or
+ * <code>on</code> indicate that SSL should be used. If this parameter is not
+ * supplied, SSL will not be used.</li>
+ * </ul>
+ * <p>
+ * If this LoginModule is used with a system-wide JAAS configuration (with or
+ * without JSPWiki), the JAAS options can be specified as described above in the
+ * JAAS configuration file. However, if this LoginModule is used with JSPWiki
+ * custom authentication, the JAAS options map can be set in
+ * <code>jspwiki.properties</code>. In this case, each option key must be
+ * prefixed by <code>jspwiki.loginModule.options.</code> to indicate that the
+ * values are, in fact, JAAS configuration items. Thus, the option
+ * {@link #OPTIONS_CONNECTION_URL} would be configured in
+ * <code>jspwiki.properties</code> using the key/value pair
+ * <code>jspwiki.loginModule.options.{@value #OPTIONS_CONNECTION_URL}
+ * = ldap://ldap.jspwiki.org:389/</code>.
+ * </p>
+ * 
+ * @author Andrew Jaquith
+ */
+public class LdapLoginModule extends AbstractLoginModule
+{
+
+    private static final Logger log = LoggerFactory.getLogger( LdapLoginModule.class );
+
+    private static final InternationalizationManager I18N = new InternationalizationManager( null );
+
+    private static final Principal[] NO_PRINCIPALS = new Principal[0];
+
+    /**
+     * JAAS option that supplies the connection URL for the LDAP server, e.g.
+     * ldap://127.0.0.1:4890/
+     */
+    protected static final String OPTIONS_CONNECTION_URL = "ldap.connectionURL";
+
+    /**
+     * JAAS option that supplies the DN pattern for finding users, e.g.
+     * uid={0},ou=people,dc=jspwiki,dc=org
+     */
+    protected static final String OPTIONS_USER_PATTERN = "ldap.userPattern";
+
+    /**
+     * JAAS option that indicates whether to use SSL for connecting to the LDAP
+     * server.
+     */
+    protected static final String OPTIONS_SSL = "ldap.ssl";
+
+    public boolean login() throws LoginException
+    {
+        // Retrieve the essential callbacks: username and password
+        String username;
+        String password;
+        NameCallback ncb = new NameCallback( "User name" );
+        PasswordCallback pcb = new PasswordCallback( "Password", false );
+        Callback[] callbacks = new Callback[] { ncb, pcb };
+        try
+        {
+            m_handler.handle( callbacks );
+            username = ncb.getName();
+            password = new String( pcb.getPassword() );
+        }
+        catch( UnsupportedCallbackException e )
+        {
+            String message = "Unable to handle callback; disallowing login.";
+            log.error( message, e );
+            throw new LoginException( message );
+        }
+        catch( IOException e )
+        {
+            String message = "IO exception; disallowing login.";
+            log.error( message, e );
+            throw new LoginException( message );
+        }
+
+        // Create LDAP context and log in!
+        try
+        {
+            // Log in
+            Hashtable<String, String> env = buildJndiEnvironment( username, password );
+            String dn = env.get( Context.SECURITY_PRINCIPAL );
+            DirContext ctx = new InitialLdapContext( env, null );
+
+            // If login succeeds, commit the login principal
+            m_principals.add( new WikiPrincipal( username, WikiPrincipal.LOGIN_NAME ) );
+            if( log.isDebugEnabled() )
+            {
+                log.debug( "Logged in user " + username + " with LDAP DN " + dn );
+            }
+
+            // Also look up the full name (and make the wiki name out of it)
+            Principal[] principals = extractNamePrincipals( ctx, dn );
+            for( Principal principal : principals )
+            {
+                m_principals.add( principal );
+            }
+            return true;
+        }
+        catch( AuthenticationException e )
+        {
+            String message = I18N.get( InternationalizationManager.CORE_BUNDLE, m_locale, "login.error.password" );
+            throw new FailedLoginException( message );
+        }
+        catch( NamingException e )
+        {
+            String message = "Naming exception; disallowing login.";
+            log.error( message, e );
+            throw new LoginException( message );
+        }
+    }
+
+    /**
+     * Builds a JNDI environment hashtable for authenticating to the LDAP
+     * server. The hashtable is built using information extracted from the
+     * options map supplied to the LoginModule via
+     * {@link #initialize(javax.security.auth.Subject, javax.security.auth.callback.CallbackHandler, Map, Map)}
+     * . The options map supplies the LDAP connection URL and SSL handling flag;
+     * the username and password parameters supply the LDAP credentials.
+     * 
+     * @param username the user's distinguished name (DN), used for
+     *            authentication
+     * @param password the password
+     * @return the constructed hash table
+     */
+    private Hashtable<String, String> buildJndiEnvironment( String username, String password )
+    {
+        Hashtable<String, String> env = new Hashtable<String, String>();
+        env.put( Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory" );
+        env.put( Context.SECURITY_AUTHENTICATION, "simple" );
+        env.put( Context.SECURITY_CREDENTIALS, password );
+
+        // LDAP server to search
+        String option = (String) m_options.get( OPTIONS_CONNECTION_URL );
+        if( option != null && option.trim().length() > 0 )
+        {
+            env.put( Context.PROVIDER_URL, option.trim() );
+        }
+
+        // DN pattern for finding users
+        option = (String) m_options.get( OPTIONS_USER_PATTERN );
+        if( option != null && option.trim().length() > 0 )
+        {
+            option = option.replace( "{0}", username ).trim();
+            env.put( Context.SECURITY_PRINCIPAL, option );
+        }
+
+        // Use SSL?
+        option = (String) m_options.get( OPTIONS_SSL );
+        boolean ssl = TextUtil.isPositive( option );
+        env.put( Context.SECURITY_PROTOCOL, ssl ? "ssl" : "none" );
+
+        if( log.isDebugEnabled() )
+        {
+            log.debug( "Built JNDI environment for LDAP login.", env );
+        }
+
+        return env;
+    }
+
+    /**
+     * Looks up the user at a supplied DN and returns an array of WikiPrincipals
+     * whose values are equal to the node's common name (cn) attribute, and a
+     * trimmed version without spaces. If no user is found this method will
+     * return a zero-length array.
+     * 
+     * @param ctx the previously initialized LDAP context
+     * @param dn the distinguished name
+     * @return the principal
+     * @throws NamingException if anything goes wrong for any reason
+     */
+    protected Principal[] extractNamePrincipals( DirContext ctx, String dn ) throws NamingException
+    {
+        // Look up the user in the current LDAP context
+        Attributes attributes = ctx.getAttributes( dn, new String[] { "cn" } );
+        Attribute attribute = attributes.get( "cn" );
+        if( attribute != null && attribute.size() > 0 )
+        {
+            String fullName = attribute.get( 0 ).toString();
+            Principal[] principals = new Principal[2];
+            
+            // FIXME: This should be sanitized better.
+            String wikiName = fullName.indexOf( ' ' ) == -1 ? fullName : fullName.replace( " ", "" );
+            
+            principals[0] = new WikiPrincipal( fullName, WikiPrincipal.FULL_NAME );
+            principals[1] = new WikiPrincipal( wikiName, WikiPrincipal.WIKI_NAME );
+            return principals;
+        }
+        return NO_PRINCIPALS;
+    }
+
+    protected Map<Role, Set<Principal>> getRoles( String ldapUrl )
+    {
+        String roleSearch = "ou=roles,dc=jspwiki,dc=org";
+        Map<Role, Set<Principal>> roleMap = new HashMap<Role, Set<Principal>>();
+        try
+        {
+            InitialContext iCtx = new InitialContext();
+            DirContext ctx = (DirContext) iCtx.lookup( ldapUrl );
+            SearchControls controls = new SearchControls();
+            NamingEnumeration<SearchResult> ne = ctx.search( roleSearch, "(objectClass=groupOfUniqueNames)", controls );
+            while ( ne.hasMore() )
+            {
+                SearchResult result = ne.next();
+                Attributes attributes = result.getAttributes();
+
+                // Get role name
+                String role = attributes.get( "cn" ).get().toString();
+
+                // Build role membership
+                Attribute attribute = attributes.get( "uniqueMember" );
+                Set<Principal> members = new HashSet<Principal>();
+                if( attribute != null )
+                {
+                    for( int i = 0; i < attribute.size(); i++ )
+                    {
+                        Principal[] principals = extractNamePrincipals( ctx, attribute.get( i ).toString() );
+                        for( Principal principal : principals )
+                        {
+                            members.add( principal );
+                        }
+                    }
+                }
+                roleMap.put( new Role( role ), members );
+            }
+        }
+        catch( NamingException e )
+        {
+            e.printStackTrace();
+        }
+        return roleMap;
+    }
+}

Modified: incubator/jspwiki/trunk/src/java/org/apache/wiki/auth/login/UserDatabaseLoginModule.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/java/org/apache/wiki/auth/login/UserDatabaseLoginModule.java?rev=796933&r1=796932&r2=796933&view=diff
==============================================================================
--- incubator/jspwiki/trunk/src/java/org/apache/wiki/auth/login/UserDatabaseLoginModule.java (original)
+++ incubator/jspwiki/trunk/src/java/org/apache/wiki/auth/login/UserDatabaseLoginModule.java Thu Jul 23 02:55:12 2009
@@ -21,7 +21,6 @@
 package org.apache.wiki.auth.login;
 
 import java.io.IOException;
-import java.util.Locale;
 
 import javax.security.auth.callback.Callback;
 import javax.security.auth.callback.NameCallback;
@@ -30,7 +29,6 @@
 import javax.security.auth.login.FailedLoginException;
 import javax.security.auth.login.LoginException;
 
-import org.apache.wiki.WikiEngine;
 import org.apache.wiki.auth.NoSuchPrincipalException;
 import org.apache.wiki.auth.WikiPrincipal;
 import org.apache.wiki.auth.user.UserDatabase;
@@ -51,13 +49,8 @@
  * username</li>
  * <li>{@link javax.security.auth.callback.PasswordCallback}- supplies the
  * password</li>
- * <li>{@link org.apache.wiki.auth.login.LocaleCallback}- supplies the
- * HTTP request, from which the user's {@link java.util.Locale} is obtained
- * (used for constructing localized error messages)</li>
- * <li>{@link org.apache.wiki.auth.login.WikiEngineCallback}- supplies the
- * {@link org.apache.wiki.WikiEngine}, from which the
- * {@link org.apache.wiki.i18n.InternationalizationManager} is obtained
- * (used for constructing localized error messages)</li>
+ * <li>{@link org.apache.wiki.auth.login.UserDatabaseCallback}- supplies the
+ * {@link org.apache.wiki.auth.user.UserDatabase}, which authenticates the user</li>
  * </ol>
  * <p>
  * After authentication, a Principals based on the login name will be created
@@ -69,6 +62,8 @@
 public class UserDatabaseLoginModule extends AbstractLoginModule
 {
 
+    private static final InternationalizationManager I18N = new InternationalizationManager( null );
+    
     private static final Logger log = LoggerFactory.getLogger( UserDatabaseLoginModule.class );
 
     /**
@@ -87,18 +82,14 @@
     {
         NameCallback ncb = new NameCallback( "User name" );
         PasswordCallback pcb = new PasswordCallback( "Password", false );
-        LocaleCallback lcb = new LocaleCallback();
-        WikiEngineCallback wcb = new WikiEngineCallback();
-        Callback[] callbacks = new Callback[] { ncb, pcb, lcb, wcb };
+        UserDatabaseCallback ucb = new UserDatabaseCallback();
+        Callback[] callbacks = new Callback[] { ncb, pcb, ucb };
         try
         {
             m_handler.handle( callbacks );
             String username = ncb.getName();
             String password = new String( pcb.getPassword() );
-            Locale locale = lcb.getLocale();
-            WikiEngine engine = wcb.getEngine();
-            UserDatabase db = engine.getUserManager().getUserDatabase();
-            InternationalizationManager i18n = engine.getInternationalizationManager();
+            UserDatabase db = ucb.getUserDatabase();
 
             // Look up the user and compare the password hash
             if ( db == null )
@@ -112,7 +103,7 @@
             }
             catch( NoSuchPrincipalException e )
             {
-                throw new FailedLoginException( i18n.get( InternationalizationManager.CORE_BUNDLE, locale, "login.error.password" ) );
+                throw new FailedLoginException( I18N.get( InternationalizationManager.CORE_BUNDLE, m_locale, "login.error.password" ) );
             }
             String storedPassword = profile.getPassword();
             if ( storedPassword != null && db.validatePassword( username, password ) )
@@ -127,7 +118,7 @@
 
                 return true;
             }
-            throw new FailedLoginException( i18n.get( InternationalizationManager.CORE_BUNDLE, locale, "login.error.password" ) );
+            throw new FailedLoginException( I18N.get( InternationalizationManager.CORE_BUNDLE, m_locale, "login.error.password" ) );
         }
         catch( IOException e )
         {

Modified: incubator/jspwiki/trunk/src/java/org/apache/wiki/auth/login/WikiCallbackHandler.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/java/org/apache/wiki/auth/login/WikiCallbackHandler.java?rev=796933&r1=796932&r2=796933&view=diff
==============================================================================
--- incubator/jspwiki/trunk/src/java/org/apache/wiki/auth/login/WikiCallbackHandler.java (original)
+++ incubator/jspwiki/trunk/src/java/org/apache/wiki/auth/login/WikiCallbackHandler.java Thu Jul 23 02:55:12 2009
@@ -85,11 +85,11 @@
             {
                 ( (HttpRequestCallback) callback ).setRequest( m_request );
             }
-            else if( callback instanceof WikiEngineCallback )
+            else if( callback instanceof WikiEngineCallback && m_engine != null )
             {
                 ( (WikiEngineCallback) callback ).setEngine( m_engine );
             }
-            else if ( callback instanceof UserDatabaseCallback )
+            else if ( callback instanceof UserDatabaseCallback && m_engine != null )
             {
                 ( (UserDatabaseCallback) callback ).setUserDatabase( m_engine.getUserManager().getUserDatabase() );
             }
@@ -101,9 +101,9 @@
             {
                 ( (PasswordCallback) callback ).setPassword( m_password.toCharArray() );
             }
-            else if ( callback instanceof LocaleCallback )
+            else if ( callback instanceof LanguageCallback )
             {
-                ( (LocaleCallback) callback ).setLocale( m_request != null ? m_request.getLocale() : Locale.getDefault() );
+                ( (LanguageCallback) callback ).setLocale( m_request != null ? m_request.getLocale() : Locale.getDefault() );
             }
             else if( callbacks[i] instanceof TextOutputCallback )
             {

Modified: incubator/jspwiki/trunk/tests/java/org/apache/wiki/auth/authorize/AllTests.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/tests/java/org/apache/wiki/auth/authorize/AllTests.java?rev=796933&r1=796932&r2=796933&view=diff
==============================================================================
--- incubator/jspwiki/trunk/tests/java/org/apache/wiki/auth/authorize/AllTests.java (original)
+++ incubator/jspwiki/trunk/tests/java/org/apache/wiki/auth/authorize/AllTests.java Thu Jul 23 02:55:12 2009
@@ -38,9 +38,10 @@
     {
         TestSuite suite = new TestSuite( "Authorizer, group and group database tests" );
         suite.addTestSuite( GroupTest.class );
-        suite.addTestSuite( WebContainerAuthorizerTest.class );
         suite.addTestSuite( JDBCGroupDatabaseTest.class );
         suite.addTestSuite( XMLGroupDatabaseTest.class );
+        suite.addTestSuite( LdapAuthorizerTest.class );
+        suite.addTestSuite( WebContainerAuthorizerTest.class );
         return suite;
     }
 }
\ No newline at end of file

Added: incubator/jspwiki/trunk/tests/java/org/apache/wiki/auth/authorize/LdapAuthorizerTest.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/tests/java/org/apache/wiki/auth/authorize/LdapAuthorizerTest.java?rev=796933&view=auto
==============================================================================
--- incubator/jspwiki/trunk/tests/java/org/apache/wiki/auth/authorize/LdapAuthorizerTest.java (added)
+++ incubator/jspwiki/trunk/tests/java/org/apache/wiki/auth/authorize/LdapAuthorizerTest.java Thu Jul 23 02:55:12 2009
@@ -0,0 +1,112 @@
+/*
+    JSPWiki - a JSP-based WikiWiki clone.
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.    
+ */
+package org.apache.wiki.auth.authorize;
+
+import java.security.Principal;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+import junit.framework.TestCase;
+
+import org.apache.wiki.TestEngine;
+import org.apache.wiki.WikiSession;
+import org.apache.wiki.auth.AuthorizationManager;
+import org.apache.wiki.auth.Authorizer;
+
+/**
+ * @author Andrew Jaquith
+ */
+public class LdapAuthorizerTest extends TestCase
+{
+    private Map<String, String> m_options;
+
+    protected void setUp()
+    {
+        m_options = new HashMap<String, String>();
+    }
+
+    /**
+     * @see junit.framework.TestCase#setUp()
+     */
+    protected TestEngine createEngine( Map<String, String> config ) throws Exception
+    {
+        Properties props = new Properties();
+        props.load( TestEngine.findTestProperties() );
+        props.putAll( config );
+        props.put( AuthorizationManager.PROP_AUTHORIZER, LdapAuthorizer.class.getCanonicalName() );
+        TestEngine engine = new TestEngine( props );
+        return engine;
+    }
+
+    public void testGetRoles() throws Exception
+    {
+        m_options.put( LdapAuthorizer.PROPERTY_CONNECTION_URL, "ldap://127.0.0.1:4890" );
+        m_options.put( LdapAuthorizer.PROPERTY_ROLE_BASE, "ou=roles,dc=jspwiki,dc=org" );
+        m_options.put( LdapAuthorizer.PROPERTY_USER_PATTERN, "uid={0},ou=people,dc=jspwiki,dc=org" );
+        Authorizer authorizer = createEngine( m_options ).getAuthorizationManager().getAuthorizer();
+
+        // LDAP should return just 2 roles, Admin and Role1
+        Principal[] roles = authorizer.getRoles();
+        assertEquals( 2, roles.length );
+        Role admin = new Role( "Admin" );
+        Role role1 = new Role( "Role1" );
+        assertTrue( roles[0].equals( admin ) || roles[1].equals( admin ) );
+        assertTrue( roles[0].equals( role1 ) || roles[1].equals( role1 ) );
+    }
+
+    public void testFindRole() throws Exception
+    {
+        m_options.put( LdapAuthorizer.PROPERTY_CONNECTION_URL, "ldap://127.0.0.1:4890" );
+        m_options.put( LdapAuthorizer.PROPERTY_ROLE_BASE, "ou=roles,dc=jspwiki,dc=org" );
+        m_options.put( LdapAuthorizer.PROPERTY_USER_PATTERN, "uid={0},ou=people,dc=jspwiki,dc=org" );
+        Authorizer authorizer = createEngine( m_options ).getAuthorizationManager().getAuthorizer();
+        
+        // We should be able to find roles Admin and Role1
+        assertEquals( new Role("Admin"), authorizer.findRole( "Admin" ) );
+        assertEquals( new Role("Role1"), authorizer.findRole( "Role1" ) );
+        
+        // We should not be able to find role Authenticated
+        assertNull( null, authorizer.findRole( "Authenticated" ) );
+    }
+
+    public void testIsUserInRole() throws Exception
+    {
+        m_options.put( LdapAuthorizer.PROPERTY_CONNECTION_URL, "ldap://127.0.0.1:4890" );
+        m_options.put( LdapAuthorizer.PROPERTY_ROLE_BASE, "ou=roles,dc=jspwiki,dc=org" );
+        m_options.put( LdapAuthorizer.PROPERTY_USER_PATTERN, "uid={0},ou=people,dc=jspwiki,dc=org" );
+        TestEngine engine = createEngine( m_options );
+        Authorizer authorizer = engine.getAuthorizationManager().getAuthorizer();
+        Role admin = new Role( "Admin" );
+        Role role1 = new Role( "Role1" );
+        
+        // Janne does not belong to any roles
+        WikiSession session = engine.janneSession();
+        assertFalse( authorizer.isUserInRole( session, admin ) );
+        assertFalse( authorizer.isUserInRole( session, role1 ) );
+        
+        // The Admin belongs to just the Admin role
+        session = engine.adminSession();
+        assertTrue( authorizer.isUserInRole( session, admin ) );
+        assertFalse( authorizer.isUserInRole( session, role1 ) );
+    }
+
+}

Modified: incubator/jspwiki/trunk/tests/java/org/apache/wiki/auth/login/AllTests.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/tests/java/org/apache/wiki/auth/login/AllTests.java?rev=796933&r1=796932&r2=796933&view=diff
==============================================================================
--- incubator/jspwiki/trunk/tests/java/org/apache/wiki/auth/login/AllTests.java (original)
+++ incubator/jspwiki/trunk/tests/java/org/apache/wiki/auth/login/AllTests.java Thu Jul 23 02:55:12 2009
@@ -39,6 +39,7 @@
         TestSuite suite = new TestSuite( "Login module tests" );
         suite.addTestSuite( AnonymousLoginModuleTest.class );
         suite.addTestSuite( CookieAssertionLoginModuleTest.class );
+        suite.addTestSuite( LdapLoginModuleTest.class );
         suite.addTestSuite( UserDatabaseLoginModuleTest.class );
         suite.addTestSuite( WebContainerLoginModuleTest.class );
         return suite;

Added: incubator/jspwiki/trunk/tests/java/org/apache/wiki/auth/login/LdapLoginModuleTest.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/tests/java/org/apache/wiki/auth/login/LdapLoginModuleTest.java?rev=796933&view=auto
==============================================================================
--- incubator/jspwiki/trunk/tests/java/org/apache/wiki/auth/login/LdapLoginModuleTest.java (added)
+++ incubator/jspwiki/trunk/tests/java/org/apache/wiki/auth/login/LdapLoginModuleTest.java Thu Jul 23 02:55:12 2009
@@ -0,0 +1,118 @@
+/*
+    JSPWiki - a JSP-based WikiWiki clone.
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.    
+ */
+package org.apache.wiki.auth.login;
+
+import java.security.Principal;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import javax.security.auth.Subject;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.login.LoginException;
+import javax.security.auth.spi.LoginModule;
+
+import junit.framework.TestCase;
+
+import org.apache.wiki.auth.WikiPrincipal;
+import org.apache.wiki.auth.authorize.Role;
+
+/**
+ * @author Andrew R. Jaquith
+ */
+public class LdapLoginModuleTest extends TestCase
+{
+    static final Map<String,String> m_options;
+    
+    static {
+        m_options = new HashMap<String, String>();
+        m_options.put( LdapLoginModule.OPTIONS_CONNECTION_URL, "ldap://127.0.0.1:4890" );
+        m_options.put( LdapLoginModule.OPTIONS_USER_PATTERN, "uid={0},ou=people,dc=jspwiki,dc=org" );
+    }
+
+    public final void testLoginNonExistentUser() throws Exception
+    {
+        // Log in with a user that isn't in the database
+        Subject subject = new Subject();
+        CallbackHandler handler = new WikiCallbackHandler( null, null, "NonExistentUser", "password" );
+        LoginModule module = new LdapLoginModule();
+        module.initialize( subject, handler, new HashMap<String, Object>(), m_options );
+        try
+        {
+            module.login();
+        }
+        catch ( LoginException e )
+        {
+            // Could not log in; this is what we expected
+        }
+        
+        // Verify no principals injected into Subject
+        Set<Principal> principals = subject.getPrincipals();
+        assertEquals( 0, principals.size() );
+    }
+
+    public final void testLogin() throws Exception
+    {
+        // Login with a user that IS in the database
+        Subject subject = new Subject();
+        CallbackHandler handler = new WikiCallbackHandler( null, null, "janne", "myP@5sw0rd" );
+        LoginModule module = new LdapLoginModule();
+        module.initialize( subject, handler, new HashMap<String, Object>(), m_options );
+        module.login();
+        module.commit();
+        
+        // Successful login will inject the usual LoginPrincipal
+        Set<Principal> principals = subject.getPrincipals();
+        assertEquals( 3, principals.size() );
+        assertTrue( principals.contains( new WikiPrincipal( "janne", WikiPrincipal.LOGIN_NAME ) ) );
+        
+        // PLUS, in this case only, principals for Wiki Name and Full Name
+        assertTrue( principals.contains( new WikiPrincipal( "Janne Jalkanen", WikiPrincipal.FULL_NAME ) ) );
+        assertTrue( principals.contains( new WikiPrincipal( "JanneJalkanen", WikiPrincipal.WIKI_NAME ) ) );
+        
+        // AuthenticationManager, NOT the LoginModule, adds the Role principals
+        assertFalse( principals.contains( Role.AUTHENTICATED ) );
+        assertFalse( principals.contains( Role.ALL ) );
+    }
+
+    public final void testLogout() throws Exception
+    {
+        Subject subject = new Subject();
+
+        // Log in with a valid user
+        CallbackHandler handler = new WikiCallbackHandler( null, null, "user", "password" );
+        LoginModule module = new LdapLoginModule();
+        module.initialize( subject, handler, new HashMap<String, Object>(), m_options );
+        module.login();
+        module.commit();
+        Set<Principal> principals = subject.getPrincipals();
+        assertEquals( 3, principals.size() );
+        assertTrue( principals.contains( new WikiPrincipal( "user", WikiPrincipal.LOGIN_NAME ) ) );
+        assertTrue( principals.contains( new WikiPrincipal( "Test User", WikiPrincipal.FULL_NAME ) ) );
+        assertTrue( principals.contains( new WikiPrincipal( "TestUser", WikiPrincipal.WIKI_NAME ) ) );
+        assertFalse( principals.contains( Role.AUTHENTICATED ) );
+        assertFalse( principals.contains( Role.ALL ) );
+        
+        // Log out and verify no principals still in subject
+        module.logout();
+        assertEquals( 0, principals.size() );
+    }
+}



Mime
View raw message