SEC-2469: Support Spring LDAP 2.0.1+
This commit is contained in:
parent
058b9debef
commit
e17adad878
|
@ -27,12 +27,15 @@ allprojects {
|
|||
ext.snapshotBuild = version.endsWith('SNAPSHOT')
|
||||
ext.springVersion = '3.2.7.RELEASE'
|
||||
ext.spring4Version = '4.0.1.RELEASE'
|
||||
ext.springLdapVersion = '1.3.2.RELEASE'
|
||||
ext.springLdap2Version = '2.0.1.CI-SNAPSHOT'
|
||||
|
||||
group = 'org.springframework.security'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven { url "http://repo.springsource.org/plugins-release" }
|
||||
maven { url "https://repo.spring.io/libs-snapshot" }
|
||||
maven { url "https://repo.spring.io/plugins-release" }
|
||||
maven { url "http://repo.terracotta.org/maven2/" }
|
||||
}
|
||||
|
||||
|
@ -117,6 +120,9 @@ configure(coreModuleProjects) {
|
|||
if (details.requested.name == 'ehcache-terracotta') {
|
||||
details.useVersion '2.1.1'
|
||||
}
|
||||
if (details.requested.group == 'org.springframework.ldap') {
|
||||
details.useVersion springLdap2Version
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,6 @@ apply plugin: 'propdeps-eclipse'
|
|||
sourceCompatibility = 1.5
|
||||
targetCompatibility = 1.5
|
||||
|
||||
ext.springLdapVersion = '1.3.2.RELEASE'
|
||||
ext.ehcacheVersion = '1.6.2'
|
||||
ext.aspectjVersion = '1.6.10'
|
||||
ext.apacheDsVersion = '1.5.5'
|
||||
|
|
|
@ -0,0 +1,237 @@
|
|||
/*
|
||||
* Copyright 2005-2010 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.security.ldap;
|
||||
|
||||
import org.springframework.ldap.BadLdapGrammarException;
|
||||
|
||||
/**
|
||||
* Helper class to encode and decode ldap names and values.
|
||||
*
|
||||
* <p>
|
||||
* NOTE: This is a copy from Spring LDAP so that both Spring LDAP 1.x and 2.x
|
||||
* can be supported without reflection.
|
||||
* </p>
|
||||
*
|
||||
* @author Adam Skogman
|
||||
* @author Mattias Hellborg Arthursson
|
||||
*/
|
||||
final class LdapEncoder {
|
||||
|
||||
private static final int HEX = 16;
|
||||
private static String[] NAME_ESCAPE_TABLE = new String[96];
|
||||
|
||||
private static String[] FILTER_ESCAPE_TABLE = new String['\\' + 1];
|
||||
|
||||
static {
|
||||
|
||||
// Name encoding table -------------------------------------
|
||||
|
||||
// all below 0x20 (control chars)
|
||||
for (char c = 0; c < ' '; c++) {
|
||||
NAME_ESCAPE_TABLE[c] = "\\" + toTwoCharHex(c);
|
||||
}
|
||||
|
||||
NAME_ESCAPE_TABLE['#'] = "\\#";
|
||||
NAME_ESCAPE_TABLE[','] = "\\,";
|
||||
NAME_ESCAPE_TABLE[';'] = "\\;";
|
||||
NAME_ESCAPE_TABLE['='] = "\\=";
|
||||
NAME_ESCAPE_TABLE['+'] = "\\+";
|
||||
NAME_ESCAPE_TABLE['<'] = "\\<";
|
||||
NAME_ESCAPE_TABLE['>'] = "\\>";
|
||||
NAME_ESCAPE_TABLE['\"'] = "\\\"";
|
||||
NAME_ESCAPE_TABLE['\\'] = "\\\\";
|
||||
|
||||
// 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";
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* All static methods - not to be instantiated.
|
||||
*/
|
||||
private LdapEncoder() {
|
||||
}
|
||||
|
||||
protected static String toTwoCharHex(char c) {
|
||||
|
||||
String raw = Integer.toHexString(c).toUpperCase();
|
||||
|
||||
if (raw.length() > 1) {
|
||||
return raw;
|
||||
} else {
|
||||
return "0" + raw;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape a value for use in a filter.
|
||||
*
|
||||
* @param value
|
||||
* the value to escape.
|
||||
* @return a properly escaped representation of the supplied value.
|
||||
*/
|
||||
public static String filterEncode(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();
|
||||
}
|
||||
|
||||
/**
|
||||
* LDAP Encodes a value for use with a DN. Escapes for LDAP, not JNDI!
|
||||
*
|
||||
* <br/>Escapes:<br/> ' ' [space] - "\ " [if first or last] <br/> '#'
|
||||
* [hash] - "\#" <br/> ',' [comma] - "\," <br/> ';' [semicolon] - "\;" <br/> '=
|
||||
* [equals] - "\=" <br/> '+' [plus] - "\+" <br/> '<' [less than] -
|
||||
* "\<" <br/> '>' [greater than] - "\>" <br/> '"' [double quote] -
|
||||
* "\"" <br/> '\' [backslash] - "\\" <br/>
|
||||
*
|
||||
* @param value
|
||||
* the value to escape.
|
||||
* @return The escaped value.
|
||||
*/
|
||||
public static String nameEncode(String value) {
|
||||
|
||||
if (value == null)
|
||||
return null;
|
||||
|
||||
// make buffer roomy
|
||||
StringBuilder encodedValue = new StringBuilder(value.length() * 2);
|
||||
|
||||
int length = value.length();
|
||||
int last = length - 1;
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
|
||||
char c = value.charAt(i);
|
||||
|
||||
// space first or last
|
||||
if (c == ' ' && (i == 0 || i == last)) {
|
||||
encodedValue.append("\\ ");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c < NAME_ESCAPE_TABLE.length) {
|
||||
// check in table for escapes
|
||||
String esc = NAME_ESCAPE_TABLE[c];
|
||||
|
||||
if (esc != null) {
|
||||
encodedValue.append(esc);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// default: add the char
|
||||
encodedValue.append(c);
|
||||
}
|
||||
|
||||
return encodedValue.toString();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a value. Converts escaped chars to ordinary chars.
|
||||
*
|
||||
* @param value
|
||||
* Trimmed value, so no leading an trailing blanks, except an
|
||||
* escaped space last.
|
||||
* @return The decoded value as a string.
|
||||
* @throws BadLdapGrammarException
|
||||
*/
|
||||
static public String nameDecode(String value)
|
||||
throws BadLdapGrammarException {
|
||||
|
||||
if (value == null)
|
||||
return null;
|
||||
|
||||
// make buffer same size
|
||||
StringBuilder decoded = new StringBuilder(value.length());
|
||||
|
||||
int i = 0;
|
||||
while (i < value.length()) {
|
||||
char currentChar = value.charAt(i);
|
||||
if (currentChar == '\\') {
|
||||
if (value.length() <= i + 1) {
|
||||
// Ending with a single backslash is not allowed
|
||||
throw new BadLdapGrammarException(
|
||||
"Unexpected end of value " + "unterminated '\\'");
|
||||
} else {
|
||||
char nextChar = value.charAt(i + 1);
|
||||
if (nextChar == ',' || nextChar == '=' || nextChar == '+'
|
||||
|| nextChar == '<' || nextChar == '>'
|
||||
|| nextChar == '#' || nextChar == ';'
|
||||
|| nextChar == '\\' || nextChar == '\"'
|
||||
|| nextChar == ' ') {
|
||||
// Normal backslash escape
|
||||
decoded.append(nextChar);
|
||||
i += 2;
|
||||
} else {
|
||||
if (value.length() <= i + 2) {
|
||||
throw new BadLdapGrammarException(
|
||||
"Unexpected end of value "
|
||||
+ "expected special or hex, found '"
|
||||
+ nextChar + "'");
|
||||
} else {
|
||||
// This should be a hex value
|
||||
String hexString = "" + nextChar
|
||||
+ value.charAt(i + 2);
|
||||
decoded.append((char) Integer.parseInt(hexString,
|
||||
HEX));
|
||||
i += 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// This character wasn't escaped - just append it
|
||||
decoded.append(currentChar);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
return decoded.toString();
|
||||
|
||||
}
|
||||
}
|
|
@ -24,7 +24,6 @@ import org.springframework.ldap.core.ContextSource;
|
|||
import org.springframework.ldap.core.DirContextAdapter;
|
||||
import org.springframework.ldap.core.DirContextOperations;
|
||||
import org.springframework.ldap.core.DistinguishedName;
|
||||
import org.springframework.ldap.core.LdapEncoder;
|
||||
import org.springframework.ldap.core.LdapTemplate;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
|
||||
package org.springframework.security.ldap.authentication;
|
||||
|
||||
import org.springframework.ldap.core.LdapEncoder;
|
||||
import org.springframework.security.core.SpringSecurityMessageSource;
|
||||
import org.springframework.security.ldap.search.LdapUserSearch;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
|
|
|
@ -0,0 +1,237 @@
|
|||
/*
|
||||
* Copyright 2005-2010 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.security.ldap.authentication;
|
||||
|
||||
import org.springframework.ldap.BadLdapGrammarException;
|
||||
|
||||
/**
|
||||
* Helper class to encode and decode ldap names and values.
|
||||
*
|
||||
* <p>
|
||||
* NOTE: This is a copy from Spring LDAP so that both Spring LDAP 1.x and 2.x
|
||||
* can be supported without reflection.
|
||||
* </p>
|
||||
*
|
||||
* @author Adam Skogman
|
||||
* @author Mattias Hellborg Arthursson
|
||||
*/
|
||||
final class LdapEncoder {
|
||||
|
||||
private static final int HEX = 16;
|
||||
private static String[] NAME_ESCAPE_TABLE = new String[96];
|
||||
|
||||
private static String[] FILTER_ESCAPE_TABLE = new String['\\' + 1];
|
||||
|
||||
static {
|
||||
|
||||
// Name encoding table -------------------------------------
|
||||
|
||||
// all below 0x20 (control chars)
|
||||
for (char c = 0; c < ' '; c++) {
|
||||
NAME_ESCAPE_TABLE[c] = "\\" + toTwoCharHex(c);
|
||||
}
|
||||
|
||||
NAME_ESCAPE_TABLE['#'] = "\\#";
|
||||
NAME_ESCAPE_TABLE[','] = "\\,";
|
||||
NAME_ESCAPE_TABLE[';'] = "\\;";
|
||||
NAME_ESCAPE_TABLE['='] = "\\=";
|
||||
NAME_ESCAPE_TABLE['+'] = "\\+";
|
||||
NAME_ESCAPE_TABLE['<'] = "\\<";
|
||||
NAME_ESCAPE_TABLE['>'] = "\\>";
|
||||
NAME_ESCAPE_TABLE['\"'] = "\\\"";
|
||||
NAME_ESCAPE_TABLE['\\'] = "\\\\";
|
||||
|
||||
// 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";
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* All static methods - not to be instantiated.
|
||||
*/
|
||||
private LdapEncoder() {
|
||||
}
|
||||
|
||||
protected static String toTwoCharHex(char c) {
|
||||
|
||||
String raw = Integer.toHexString(c).toUpperCase();
|
||||
|
||||
if (raw.length() > 1) {
|
||||
return raw;
|
||||
} else {
|
||||
return "0" + raw;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape a value for use in a filter.
|
||||
*
|
||||
* @param value
|
||||
* the value to escape.
|
||||
* @return a properly escaped representation of the supplied value.
|
||||
*/
|
||||
public static String filterEncode(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();
|
||||
}
|
||||
|
||||
/**
|
||||
* LDAP Encodes a value for use with a DN. Escapes for LDAP, not JNDI!
|
||||
*
|
||||
* <br/>Escapes:<br/> ' ' [space] - "\ " [if first or last] <br/> '#'
|
||||
* [hash] - "\#" <br/> ',' [comma] - "\," <br/> ';' [semicolon] - "\;" <br/> '=
|
||||
* [equals] - "\=" <br/> '+' [plus] - "\+" <br/> '<' [less than] -
|
||||
* "\<" <br/> '>' [greater than] - "\>" <br/> '"' [double quote] -
|
||||
* "\"" <br/> '\' [backslash] - "\\" <br/>
|
||||
*
|
||||
* @param value
|
||||
* the value to escape.
|
||||
* @return The escaped value.
|
||||
*/
|
||||
public static String nameEncode(String value) {
|
||||
|
||||
if (value == null)
|
||||
return null;
|
||||
|
||||
// make buffer roomy
|
||||
StringBuilder encodedValue = new StringBuilder(value.length() * 2);
|
||||
|
||||
int length = value.length();
|
||||
int last = length - 1;
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
|
||||
char c = value.charAt(i);
|
||||
|
||||
// space first or last
|
||||
if (c == ' ' && (i == 0 || i == last)) {
|
||||
encodedValue.append("\\ ");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c < NAME_ESCAPE_TABLE.length) {
|
||||
// check in table for escapes
|
||||
String esc = NAME_ESCAPE_TABLE[c];
|
||||
|
||||
if (esc != null) {
|
||||
encodedValue.append(esc);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// default: add the char
|
||||
encodedValue.append(c);
|
||||
}
|
||||
|
||||
return encodedValue.toString();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a value. Converts escaped chars to ordinary chars.
|
||||
*
|
||||
* @param value
|
||||
* Trimmed value, so no leading an trailing blanks, except an
|
||||
* escaped space last.
|
||||
* @return The decoded value as a string.
|
||||
* @throws BadLdapGrammarException
|
||||
*/
|
||||
static public String nameDecode(String value)
|
||||
throws BadLdapGrammarException {
|
||||
|
||||
if (value == null)
|
||||
return null;
|
||||
|
||||
// make buffer same size
|
||||
StringBuilder decoded = new StringBuilder(value.length());
|
||||
|
||||
int i = 0;
|
||||
while (i < value.length()) {
|
||||
char currentChar = value.charAt(i);
|
||||
if (currentChar == '\\') {
|
||||
if (value.length() <= i + 1) {
|
||||
// Ending with a single backslash is not allowed
|
||||
throw new BadLdapGrammarException(
|
||||
"Unexpected end of value " + "unterminated '\\'");
|
||||
} else {
|
||||
char nextChar = value.charAt(i + 1);
|
||||
if (nextChar == ',' || nextChar == '=' || nextChar == '+'
|
||||
|| nextChar == '<' || nextChar == '>'
|
||||
|| nextChar == '#' || nextChar == ';'
|
||||
|| nextChar == '\\' || nextChar == '\"'
|
||||
|| nextChar == ' ') {
|
||||
// Normal backslash escape
|
||||
decoded.append(nextChar);
|
||||
i += 2;
|
||||
} else {
|
||||
if (value.length() <= i + 2) {
|
||||
throw new BadLdapGrammarException(
|
||||
"Unexpected end of value "
|
||||
+ "expected special or hex, found '"
|
||||
+ nextChar + "'");
|
||||
} else {
|
||||
// This should be a hex value
|
||||
String hexString = "" + nextChar
|
||||
+ value.charAt(i + 2);
|
||||
decoded.append((char) Integer.parseInt(hexString,
|
||||
HEX));
|
||||
i += 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// This character wasn't escaped - just append it
|
||||
decoded.append(currentChar);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
return decoded.toString();
|
||||
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue