Repository: kafka Updated Branches: refs/heads/trunk 0a508a436 -> b08882213
KAFKA-5764; Add toLowerCase support to sasl.kerberos.principal.to.local rule (KIP-203) Author: Manikumar Reddy <manikumar.re...@gmail.com> Reviewers: Jason Gustafson <ja...@confluent.io> Closes #3800 from omkreddy/KAFKA-5764-REGEX Project: http://git-wip-us.apache.org/repos/asf/kafka/repo Commit: http://git-wip-us.apache.org/repos/asf/kafka/commit/b0888221 Tree: http://git-wip-us.apache.org/repos/asf/kafka/tree/b0888221 Diff: http://git-wip-us.apache.org/repos/asf/kafka/diff/b0888221 Branch: refs/heads/trunk Commit: b08882213489706c663785f2c94d76584ba6e856 Parents: 0a508a4 Author: Manikumar Reddy <manikumar.re...@gmail.com> Authored: Tue Dec 12 15:47:49 2017 -0800 Committer: Jason Gustafson <ja...@confluent.io> Committed: Tue Dec 12 15:47:57 2017 -0800 ---------------------------------------------------------------------- .../common/security/kerberos/KerberosRule.java | 12 ++++- .../security/kerberos/KerberosShortNamer.java | 13 ++--- .../security/kerberos/KerberosNameTest.java | 57 ++++++++++++++++++-- docs/security.html | 11 +++- 4 files changed, 82 insertions(+), 11 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/kafka/blob/b0888221/clients/src/main/java/org/apache/kafka/common/security/kerberos/KerberosRule.java ---------------------------------------------------------------------- diff --git a/clients/src/main/java/org/apache/kafka/common/security/kerberos/KerberosRule.java b/clients/src/main/java/org/apache/kafka/common/security/kerberos/KerberosRule.java index 40a9a1f..37820df 100644 --- a/clients/src/main/java/org/apache/kafka/common/security/kerberos/KerberosRule.java +++ b/clients/src/main/java/org/apache/kafka/common/security/kerberos/KerberosRule.java @@ -17,6 +17,7 @@ package org.apache.kafka.common.security.kerberos; import java.io.IOException; +import java.util.Locale; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -44,6 +45,7 @@ class KerberosRule { private final Pattern fromPattern; private final String toPattern; private final boolean repeat; + private final boolean toLowerCase; KerberosRule(String defaultRealm) { this.defaultRealm = defaultRealm; @@ -54,10 +56,11 @@ class KerberosRule { fromPattern = null; toPattern = null; repeat = false; + toLowerCase = false; } KerberosRule(String defaultRealm, int numOfComponents, String format, String match, String fromPattern, - String toPattern, boolean repeat) { + String toPattern, boolean repeat, boolean toLowerCase) { this.defaultRealm = defaultRealm; isDefault = false; this.numOfComponents = numOfComponents; @@ -67,6 +70,7 @@ class KerberosRule { fromPattern == null ? null : Pattern.compile(fromPattern); this.toPattern = toPattern; this.repeat = repeat; + this.toLowerCase = toLowerCase; } @Override @@ -95,6 +99,9 @@ class KerberosRule { buf.append('g'); } } + if (toLowerCase) { + buf.append("/L"); + } } return buf.toString(); } @@ -182,6 +189,9 @@ class KerberosRule { if (result != null && NON_SIMPLE_PATTERN.matcher(result).find()) { throw new NoMatchingRule("Non-simple name " + result + " after auth_to_local rule " + this); } + if (toLowerCase && result != null) { + result = result.toLowerCase(Locale.ENGLISH); + } return result; } } http://git-wip-us.apache.org/repos/asf/kafka/blob/b0888221/clients/src/main/java/org/apache/kafka/common/security/kerberos/KerberosShortNamer.java ---------------------------------------------------------------------- diff --git a/clients/src/main/java/org/apache/kafka/common/security/kerberos/KerberosShortNamer.java b/clients/src/main/java/org/apache/kafka/common/security/kerberos/KerberosShortNamer.java index 1db984a..69b4689 100644 --- a/clients/src/main/java/org/apache/kafka/common/security/kerberos/KerberosShortNamer.java +++ b/clients/src/main/java/org/apache/kafka/common/security/kerberos/KerberosShortNamer.java @@ -33,7 +33,7 @@ public class KerberosShortNamer { /** * A pattern for parsing a auth_to_local rule. */ - private static final Pattern RULE_PARSER = Pattern.compile("((DEFAULT)|(RULE:\\[(\\d*):([^\\]]*)](\\(([^)]*)\\))?(s/([^/]*)/([^/]*)/(g)?)?))"); + private static final Pattern RULE_PARSER = Pattern.compile("((DEFAULT)|((RULE:\\[(\\d*):([^\\]]*)](\\(([^)]*)\\))?(s/([^/]*)/([^/]*)/(g)?)?/?(L)?)))"); /* Rules for the translation of the principal name into an operating system name */ private final List<KerberosRule> principalToLocalRules; @@ -60,12 +60,13 @@ public class KerberosShortNamer { result.add(new KerberosRule(defaultRealm)); } else { result.add(new KerberosRule(defaultRealm, - Integer.parseInt(matcher.group(4)), - matcher.group(5), - matcher.group(7), - matcher.group(9), + Integer.parseInt(matcher.group(5)), + matcher.group(6), + matcher.group(8), matcher.group(10), - "g".equals(matcher.group(11)))); + matcher.group(11), + "g".equals(matcher.group(12)), + "L".equals(matcher.group(13)))); } } http://git-wip-us.apache.org/repos/asf/kafka/blob/b0888221/clients/src/test/java/org/apache/kafka/common/security/kerberos/KerberosNameTest.java ---------------------------------------------------------------------- diff --git a/clients/src/test/java/org/apache/kafka/common/security/kerberos/KerberosNameTest.java b/clients/src/test/java/org/apache/kafka/common/security/kerberos/KerberosNameTest.java index 3fbd310..1acd80c 100644 --- a/clients/src/test/java/org/apache/kafka/common/security/kerberos/KerberosNameTest.java +++ b/clients/src/test/java/org/apache/kafka/common/security/kerberos/KerberosNameTest.java @@ -19,22 +19,23 @@ package org.apache.kafka.common.security.kerberos; import org.junit.Test; import java.io.IOException; -import java.util.ArrayList; import java.util.Arrays; import java.util.List; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; +import static org.junit.Assert.fail; public class KerberosNameTest { @Test public void testParse() throws IOException { - List<String> rules = new ArrayList<>(Arrays.asList( + List<String> rules = Arrays.asList( "RULE:[1:$1](App\\..*)s/App\\.(.*)/$1/g", "RULE:[2:$1](App\\..*)s/App\\.(.*)/$1/g", "DEFAULT" - )); + ); + KerberosShortNamer shortNamer = KerberosShortNamer.fromUnparsedRules("REALM.COM", rules); KerberosName name = KerberosName.parse("App.service-name/example....@realm.com"); @@ -55,4 +56,54 @@ public class KerberosNameTest { assertEquals("REALM.COM", name.realm()); assertEquals("user", shortNamer.shortName(name)); } + + @Test + public void testToLowerCase() throws Exception { + List<String> rules = Arrays.asList( + "RULE:[1:$1]/L", + "RULE:[2:$1](Test.*)s/ABC///L", + "RULE:[2:$1](ABC.*)s/ABC/XYZ/g/L", + "RULE:[2:$1](App\\..*)s/App\\.(.*)/$1/g/L", + "RULE:[2:$1]/L", + "DEFAULT" + ); + + KerberosShortNamer shortNamer = KerberosShortNamer.fromUnparsedRules("REALM.COM", rules); + + KerberosName name = KerberosName.parse("u...@realm.com"); + assertEquals("user", shortNamer.shortName(name)); + + name = KerberosName.parse("TestABC/h...@foo.com"); + assertEquals("test", shortNamer.shortName(name)); + + name = KerberosName.parse("ABC_User_ABC/h...@foo.com"); + assertEquals("xyz_user_xyz", shortNamer.shortName(name)); + + name = KerberosName.parse("App.SERVICE-name/example....@realm.com"); + assertEquals("service-name", shortNamer.shortName(name)); + + name = KerberosName.parse("User/r...@realm.com"); + assertEquals("user", shortNamer.shortName(name)); + } + + @Test + public void testInvalidRules() throws Exception { + testInvalidRule(Arrays.asList("default")); + testInvalidRule(Arrays.asList("DEFAUL")); + testInvalidRule(Arrays.asList("DEFAULT/L")); + testInvalidRule(Arrays.asList("DEFAULT/g")); + + testInvalidRule(Arrays.asList("rule:[1:$1]")); + testInvalidRule(Arrays.asList("RULE:[1:$1/L")); + testInvalidRule(Arrays.asList("RULE:[1:$1]/l")); + testInvalidRule(Arrays.asList("RULE:[2:$1](ABC.*)s/ABC/XYZ/L/g")); + } + + private void testInvalidRule(List<String> rules) { + try { + KerberosShortNamer.fromUnparsedRules("REALM.COM", rules); + fail("should have thrown IllegalArgumentException"); + } catch (IllegalArgumentException e) { + } + } } http://git-wip-us.apache.org/repos/asf/kafka/blob/b0888221/docs/security.html ---------------------------------------------------------------------- diff --git a/docs/security.html b/docs/security.html index 8c1bda9..c8e20ea 100644 --- a/docs/security.html +++ b/docs/security.html @@ -671,7 +671,16 @@ By default, the SSL user name will be of the form "CN=writeuser,OU=Unknown,O=Unknown,L=Unknown,ST=Unknown,C=Unknown". One can change that by setting a customized PrincipalBuilder in server.properties like the following. <pre>principal.builder.class=CustomizedPrincipalBuilderClass</pre> By default, the SASL user name will be the primary part of the Kerberos principal. One can change that by setting <code>sasl.kerberos.principal.to.local.rules</code> to a customized rule in server.properties. - The format of <code>sasl.kerberos.principal.to.local.rules</code> is a list where each rule works in the same way as the auth_to_local in <a href="http://web.mit.edu/Kerberos/krb5-latest/doc/admin/conf_files/krb5_conf.html">Kerberos configuration file (krb5.conf)</a>. Each rules starts with RULE: and contains an expression in the format [n:string](regexp)s/pattern/replacement/g. See the kerberos documentation for more details. An example of adding a rule to properly translate u...@mydomain.com to user while also keeping the default rule in place is: + The format of <code>sasl.kerberos.principal.to.local.rules</code> is a list where each rule works in the same way as the auth_to_local in <a href="http://web.mit.edu/Kerberos/krb5-latest/doc/admin/conf_files/krb5_conf.html">Kerberos configuration file (krb5.conf)</a>. This also support additional lowercase rule, to force the translated result to be all lower case. This is done by adding a "/L" to the end of the rule. check below formats for syntax. + Each rules starts with RULE: and contains an expression as the following formats. See the kerberos documentation for more details. + <pre> + RULE:[n:string](regexp)s/pattern/replacement/ + RULE:[n:string](regexp)s/pattern/replacement/g + RULE:[n:string](regexp)s/pattern/replacement//L + RULE:[n:string](regexp)s/pattern/replacement/g/L + </pre> + + An example of adding a rule to properly translate u...@mydomain.com to user while also keeping the default rule in place is: <pre>sasl.kerberos.principal.to.local.rules=RULE:[1:$1@$0](.*@MYDOMAIN.COM)s/@.*//,DEFAULT</pre> <h4><a id="security_authz_cli" href="#security_authz_cli">Command Line Interface</a></h4>