This is an automated email from the ASF dual-hosted git repository. jongyoul pushed a commit to branch branch-0.11 in repository https://gitbox.apache.org/repos/asf/zeppelin.git
commit 75ccdc73ff44c7e6f97d19ab13375313f078f567 Author: Jongyoul Lee <jongy...@gmail.com> AuthorDate: Wed Feb 28 00:16:13 2024 +0900 [HOTFIX] Escape Ldap search filters (#4714) (cherry picked from commit 65d0bcc1ee8ec3ec372d0a71ab513cd20e6522a0) --- .../java/org/apache/zeppelin/realm/LdapRealm.java | 73 +++++++++++++++++++++- .../org/apache/zeppelin/realm/LdapRealmTest.java | 13 ++++ 2 files changed, 84 insertions(+), 2 deletions(-) diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/realm/LdapRealm.java b/zeppelin-server/src/main/java/org/apache/zeppelin/realm/LdapRealm.java index 51c92c805a..e1d6d694c7 100644 --- a/zeppelin-server/src/main/java/org/apache/zeppelin/realm/LdapRealm.java +++ b/zeppelin-server/src/main/java/org/apache/zeppelin/realm/LdapRealm.java @@ -773,7 +773,7 @@ public class LdapRealm extends DefaultLdapRealm { } public void setUserSearchFilter(final String filter) { - this.userSearchFilter = (filter == null ? null : filter.trim()); + this.userSearchFilter = (filter == null ? null : escapeAttributeValue(filter.trim())); } public String getGroupSearchFilter() { @@ -781,7 +781,7 @@ public class LdapRealm extends DefaultLdapRealm { } public void setGroupSearchFilter(final String filter) { - this.groupSearchFilter = (filter == null ? null : filter.trim()); + this.groupSearchFilter = (filter == null ? null : escapeAttributeValue(filter.trim())); } public boolean getUserLowerCase() { @@ -941,6 +941,75 @@ public class LdapRealm extends DefaultLdapRealm { } } + // Implements the necessary escaping to represent an attribute value as a String as per RFC 4514. + // https://github.com/apache/tomcat/blob/main/java/org/apache/catalina/realm/JNDIRealm.java#L2921 + protected String escapeAttributeValue(String input) { + if (input == null) { + return null; + } + int len = input.length(); + StringBuilder result = new StringBuilder(); + + for (int i = 0; i < len; i++) { + char c = input.charAt(i); + switch (c) { + case ' ': { + if (i == 0 || i == (len - 1)) { + result.append("\\20"); + } else { + result.append(c); + } + break; + } + case '#': { + if (i == 0) { + result.append("\\23"); + } else { + result.append(c); + } + break; + } + case '\"': { + result.append("\\22"); + break; + } + case '+': { + result.append("\\2B"); + break; + } + case ',': { + result.append("\\2C"); + break; + } + case ';': { + result.append("\\3B"); + break; + } + case '<': { + result.append("\\3C"); + break; + } + case '>': { + result.append("\\3E"); + break; + } + case '\\': { + result.append("\\5C"); + break; + } + case '\u0000': { + result.append("\\00"); + break; + } + default: + result.append(c); + } + } + + return result.toString(); + } + + @Override protected AuthenticationInfo createAuthenticationInfo(AuthenticationToken token, Object ldapPrincipal, Object ldapCredentials, LdapContext ldapContext) diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/realm/LdapRealmTest.java b/zeppelin-server/src/test/java/org/apache/zeppelin/realm/LdapRealmTest.java index d5e3c6d84f..9abce28d4b 100644 --- a/zeppelin-server/src/test/java/org/apache/zeppelin/realm/LdapRealmTest.java +++ b/zeppelin-server/src/test/java/org/apache/zeppelin/realm/LdapRealmTest.java @@ -119,6 +119,19 @@ class LdapRealmTest { assertEquals(new HashSet<>(Arrays.asList("group-one", "zeppelin-role")), roles); } + @Test + void testFilterEscaping() { + LdapRealm realm = new LdapRealm(); + assertEquals("foo", realm.escapeAttributeValue("foo")); + assertEquals("foo\\2B", realm.escapeAttributeValue("foo+")); + assertEquals("foo\\5C", realm.escapeAttributeValue("foo\\")); + assertEquals("foo\\00", realm.escapeAttributeValue("foo\u0000")); + realm.setUserSearchFilter("uid=<{0}>"); + assertEquals("uid=\\3C{0}\\3E", realm.getUserSearchFilter()); + realm.setUserSearchFilter("gid=\\{0}\\"); + assertEquals("gid=\\5C{0}\\5C", realm.getUserSearchFilter()); + } + private NamingEnumeration<SearchResult> enumerationOf(BasicAttributes... attrs) { final Iterator<BasicAttributes> iterator = Arrays.asList(attrs).iterator(); return new NamingEnumeration<SearchResult>() {