Improving LDAP filters by escaping characters
diff --git a/redback-authentication/redback-authentication-providers/redback-authentication-ldap/src/main/java/org/apache/archiva/redback/authentication/ldap/LdapBindAuthenticator.java b/redback-authentication/redback-authentication-providers/redback-authentication-ldap/src/main/java/org/apache/archiva/redback/authentication/ldap/LdapBindAuthenticator.java
index 9c33ffd..3c46c78 100644
--- a/redback-authentication/redback-authentication-providers/redback-authentication-ldap/src/main/java/org/apache/archiva/redback/authentication/ldap/LdapBindAuthenticator.java
+++ b/redback-authentication/redback-authentication-providers/redback-authentication-ldap/src/main/java/org/apache/archiva/redback/authentication/ldap/LdapBindAuthenticator.java
@@ -20,6 +20,7 @@
  */
 
 import org.apache.archiva.redback.authentication.AbstractAuthenticator;
+import org.apache.archiva.redback.common.ldap.LdapUtils;
 import org.apache.archiva.redback.common.ldap.connection.DefaultLdapConnection;
 import org.apache.archiva.redback.common.ldap.connection.LdapConnection;
 import org.apache.archiva.redback.common.ldap.user.UserMapper;
@@ -100,7 +101,7 @@
 
         String filter = "(&(objectClass=" + mapper.getUserObjectClass() + ")" + ( mapper.getUserFilter() != null
             ? mapper.getUserFilter()
-            : "" ) + "(" + mapper.getUserIdAttribute() + "=" + source.getUsername() + "))";
+            : "" ) + "(" + mapper.getUserIdAttribute() + "=" + LdapUtils.encodeFilterValue( source.getUsername() ) + "))";
 
         log.debug( "Searching for users with filter: '{}' from base dn: {}", filter, mapper.getUserBaseDn() );
 
diff --git a/redback-authentication/redback-authentication-providers/redback-authentication-ldap/src/test/java/org/apache/archiva/redback/authentication/ldap/LdapBindAuthenticatorTest.java b/redback-authentication/redback-authentication-providers/redback-authentication-ldap/src/test/java/org/apache/archiva/redback/authentication/ldap/LdapBindAuthenticatorTest.java
index 84ee8be..e668f84 100644
--- a/redback-authentication/redback-authentication-providers/redback-authentication-ldap/src/test/java/org/apache/archiva/redback/authentication/ldap/LdapBindAuthenticatorTest.java
+++ b/redback-authentication/redback-authentication-providers/redback-authentication-ldap/src/test/java/org/apache/archiva/redback/authentication/ldap/LdapBindAuthenticatorTest.java
@@ -137,6 +137,17 @@
         assertTrue( result.isAuthenticated() );
     }
 
+    @Test
+    public void testAuthenticationWithInvalidChar()
+        throws Exception
+    {
+        PasswordBasedAuthenticationDataSource authDs = new PasswordBasedAuthenticationDataSource();
+        authDs.setPrincipal( "jesse)(mail=foo" );
+        authDs.setPassword( passwordEncoder.encodePassword( "foo" ) );
+        AuthenticationResult result = authnr.authenticate( authDs );
+        assertFalse( result.isAuthenticated() );
+    }
+
     // REDBACK-289/MRM-1488
     @Test
     public void testAuthenticationFromCache()
diff --git a/redback-common/redback-common-ldap/src/main/java/org/apache/archiva/redback/common/ldap/LdapUtils.java b/redback-common/redback-common-ldap/src/main/java/org/apache/archiva/redback/common/ldap/LdapUtils.java
index 529a2d9..b1c45cb 100644
--- a/redback-common/redback-common-ldap/src/main/java/org/apache/archiva/redback/common/ldap/LdapUtils.java
+++ b/redback-common/redback-common-ldap/src/main/java/org/apache/archiva/redback/common/ldap/LdapUtils.java
@@ -34,6 +34,28 @@
  */
 public final class LdapUtils
 {
+
+    private static String[] FILTER_ESCAPE_TABLE = new String['\\' + 1];
+
+
+    // Characters that must be escaped in a user filter
+    static {
+
+        // Filter encoding table -------------------------------------
+        // fill with char itself
+        for (char c = 0; c < FILTER_ESCAPE_TABLE.length; c++) {
+            FILTER_ESCAPE_TABLE[c] = String.valueOf(c);
+        }
+
+        // escapes (RFC2254)
+        FILTER_ESCAPE_TABLE['*'] = "\\2a";
+        FILTER_ESCAPE_TABLE['('] = "\\28";
+        FILTER_ESCAPE_TABLE[')'] = "\\29";
+        FILTER_ESCAPE_TABLE['\\'] = "\\5c";
+        FILTER_ESCAPE_TABLE[0] = "\\00";
+    }
+
+
     private LdapUtils()
     {
         // no op
@@ -172,4 +194,38 @@
         }
         return "";
     }
+
+    /**
+     * Escape a value for use in a filter.
+     * This method is copied from the spring framework class org.springframework.security.ldap.authentication.LdapEncoder
+     *
+     * @param value the value to escape.
+     * @return a properly escaped representation of the supplied value.
+     */
+    public static String encodeFilterValue(String value) {
+
+            if (value == null) {
+                return null;
+            }
+
+            // make buffer roomy
+            StringBuilder encodedValue = new StringBuilder(value.length() * 2);
+
+            int length = value.length();
+
+            for (int i = 0; i < length; i++) {
+
+                char c = value.charAt(i);
+
+                if (c < FILTER_ESCAPE_TABLE.length) {
+                    encodedValue.append(FILTER_ESCAPE_TABLE[c]);
+                }
+                else {
+                    // default: add the char
+                    encodedValue.append(c);
+                }
+            }
+
+            return encodedValue.toString();
+    }
 }
diff --git a/redback-users/redback-users-providers/redback-users-ldap/src/main/java/org/apache/archiva/redback/users/ldap/LdapUserQuery.java b/redback-users/redback-users-providers/redback-users-ldap/src/main/java/org/apache/archiva/redback/users/ldap/LdapUserQuery.java
index 6f09794..d20063a 100644
--- a/redback-users/redback-users-providers/redback-users-ldap/src/main/java/org/apache/archiva/redback/users/ldap/LdapUserQuery.java
+++ b/redback-users/redback-users-providers/redback-users-ldap/src/main/java/org/apache/archiva/redback/users/ldap/LdapUserQuery.java
@@ -19,6 +19,7 @@
  * under the License.
  */
 
+import org.apache.archiva.redback.common.ldap.LdapUtils;
 import org.apache.archiva.redback.common.ldap.user.UserMapper;
 import org.apache.archiva.redback.users.AbstractUserQuery;
 
@@ -49,13 +50,13 @@
         String filter = "";
         if (this.getEmail() != null )
         {
-            filter += "(" + mapper.getEmailAddressAttribute() + "=" + this.getEmail() + ")";
+            filter += "(" + mapper.getEmailAddressAttribute() + "=" + LdapUtils.encodeFilterValue( this.getEmail() ) + ")";
         }
         if ( this.getFullName() != null )
         {
-            filter += "(" + mapper.getUserFullNameAttribute() + "=" + this.getFullName() + ")";
+            filter += "(" + mapper.getUserFullNameAttribute() + "=" + LdapUtils.encodeFilterValue( this.getFullName() ) + ")";
         }
-        filter += "(" + mapper.getUserIdAttribute() + "=" + ( this.getUsername() != null ? this.getUsername() : "*" ) + ")";
+        filter += "(" + mapper.getUserIdAttribute() + "=" + ( this.getUsername() != null ? LdapUtils.encodeFilterValue( this.getUsername() ) : "*" ) + ")";
         
         return filter;
     }
diff --git a/redback-users/redback-users-providers/redback-users-ldap/src/test/java/org/apache/archiva/redback/users/ldap/LdapUserManagerTest.java b/redback-users/redback-users-providers/redback-users-ldap/src/test/java/org/apache/archiva/redback/users/ldap/LdapUserManagerTest.java
index 88ea922..ca06118 100644
--- a/redback-users/redback-users-providers/redback-users-ldap/src/test/java/org/apache/archiva/redback/users/ldap/LdapUserManagerTest.java
+++ b/redback-users/redback-users-providers/redback-users-ldap/src/test/java/org/apache/archiva/redback/users/ldap/LdapUserManagerTest.java
@@ -234,6 +234,21 @@
     }
 
     @Test
+    public void testUserWithInvalidChars()
+        throws Exception
+    {
+        try
+        {
+            userManager.findUser( "jesse)(mail=jesse@apache.org" );
+            fail( "UserNotFoundException should be thrown, if invalid filter chars are in the username" );
+        }
+        catch ( UserNotFoundException e )
+        {
+            // cool it works !
+        }
+    }
+
+    @Test
     public void testWithManyUsers()
         throws Exception
     {