This is an automated email from the ASF dual-hosted git repository.
kdoran pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nifi.git
The following commit(s) were added to refs/heads/master by this push:
new 0e5a80d NIFI-6224 Updated KerberosProvider to use the "Default Realm"
property
0e5a80d is described below
commit 0e5a80d23f0432947b4ef05b45ea3571686304ba
Author: Jeff Storck <[email protected]>
AuthorDate: Thu Apr 18 00:29:36 2019 -0400
NIFI-6224 Updated KerberosProvider to use the "Default Realm" property
Updated usage of deprecated FormatUtils.getTimeDuration to
FormatUtils.getPreciseTimeDuration
Implemented prioritized handling of appending the default realm
A realm-qualified principal will not be modified before authentication
A principal shortname will have Default Realm appended to it when it is
not blank before authentication
A principal shortname will not be modified if Default Realm is blank,
and the underlying kerberos implementation will append the default_realm
configured in krb5.conf
In nifi-security-util
added KerberosPrincipalParser for determining the realm of a kerberos
principal
added tests for KerberosPrincipalParser
updated pom with spock-core as a test dependency
This closes #3446.
Signed-off-by: Kevin Doran <[email protected]>
---
nifi-commons/nifi-security-utils/pom.xml | 5 ++
.../security/util/krb/KerberosPrincipalParser.java | 60 ++++++++++++++++++++++
.../util/krb/KerberosPrincipalParserSpec.groovy | 44 ++++++++++++++++
.../org/apache/nifi/kerberos/KerberosProvider.java | 44 +++++++++++++---
4 files changed, 147 insertions(+), 6 deletions(-)
diff --git a/nifi-commons/nifi-security-utils/pom.xml
b/nifi-commons/nifi-security-utils/pom.xml
index 1284ea3..3bf5b0b 100644
--- a/nifi-commons/nifi-security-utils/pom.xml
+++ b/nifi-commons/nifi-security-utils/pom.xml
@@ -79,6 +79,11 @@
<version>3.1.0</version>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.spockframework</groupId>
+ <artifactId>spock-core</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
diff --git
a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/krb/KerberosPrincipalParser.java
b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/krb/KerberosPrincipalParser.java
new file mode 100644
index 0000000..8726c9c
--- /dev/null
+++
b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/krb/KerberosPrincipalParser.java
@@ -0,0 +1,60 @@
+/*
+ * 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.nifi.security.util.krb;
+
+import org.apache.nifi.util.StringUtils;
+
+public class KerberosPrincipalParser {
+
+ /**
+ * <p>Determines the realm specified in the given kerberos principal.
+ *
+ * <p>The content of the given {@code principal} after the occurrence
+ * of the last non-escaped realm delimiter ("@") will be considered
+ * the realm of the principal.
+ *
+ * <p>The validity of the given {@code principal} and the determined realm
+ * is not be verified by this method.
+ *
+ * @param principal the principal for which the realm will be determined
+ * @return the realm of the given principal
+ */
+ public static String getRealm(String principal) {
+ if (StringUtils.isBlank(principal)) {
+ throw new IllegalArgumentException("principal can not be null or
empty");
+ }
+
+ char previousChar = 0;
+ int realmDelimiterIndex = -1;
+ char currentChar;
+ boolean realmDelimiterFound = false;
+ int principalLength = principal.length();
+
+ // find the last non-escaped occurrence of the realm delimiter
+ for (int i = 0; i < principalLength; ++i) {
+ currentChar = principal.charAt(i);
+ if (currentChar == '@' && previousChar != '\\' ) {
+ realmDelimiterIndex = i;
+ realmDelimiterFound = true;
+ }
+ previousChar = currentChar;
+ }
+
+ String principalAfterLastRealmDelimiter =
principal.substring(realmDelimiterIndex + 1);
+ return realmDelimiterFound && realmDelimiterIndex + 1 <
principalLength ? principalAfterLastRealmDelimiter : null;
+ }
+}
diff --git
a/nifi-commons/nifi-security-utils/src/test/groovy/org/apache/nifi/security/util/krb/KerberosPrincipalParserSpec.groovy
b/nifi-commons/nifi-security-utils/src/test/groovy/org/apache/nifi/security/util/krb/KerberosPrincipalParserSpec.groovy
new file mode 100644
index 0000000..883dc31
--- /dev/null
+++
b/nifi-commons/nifi-security-utils/src/test/groovy/org/apache/nifi/security/util/krb/KerberosPrincipalParserSpec.groovy
@@ -0,0 +1,44 @@
+/*
+ * 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.nifi.security.util.krb
+
+import spock.lang.Specification
+import spock.lang.Unroll
+
+class KerberosPrincipalParserSpec extends Specification {
+
+ @Unroll
+ def "Verify parsed realm from '#testPrincipal' == '#expectedRealm'"() {
+ expect:
+ KerberosPrincipalParser.getRealm(testPrincipal) == expectedRealm
+
+ where:
+ testPrincipal || expectedRealm
+ "user" || null
+ "user@" || null
+ "[email protected]" || "EXAMPLE.COM"
+ "user@[email protected]" || "EXAMPLE.COM"
+ "user\\@" || null
+ "user\\@name" || null
+ "user\\@[email protected]" || "EXAMPLE.COM"
+ "[email protected]\\@" || "EXAMPLE.COM\\@"
+ "user@@name@\\@@\\@" || "\\@"
+ "user@@name@\\@@\\@@EXAMPLE.COM" || "EXAMPLE.COM"
+ "user@@name@\\@@\\@@EXAMPLE.COM@" || null
+ "user\\@\\@[email protected]" || "EXAMPLE.COM"
+ }
+}
diff --git
a/nifi-nar-bundles/nifi-kerberos-iaa-providers-bundle/nifi-kerberos-iaa-providers/src/main/java/org/apache/nifi/kerberos/KerberosProvider.java
b/nifi-nar-bundles/nifi-kerberos-iaa-providers-bundle/nifi-kerberos-iaa-providers/src/main/java/org/apache/nifi/kerberos/KerberosProvider.java
index f985602..2e46f6a 100644
---
a/nifi-nar-bundles/nifi-kerberos-iaa-providers-bundle/nifi-kerberos-iaa-providers/src/main/java/org/apache/nifi/kerberos/KerberosProvider.java
+++
b/nifi-nar-bundles/nifi-kerberos-iaa-providers-bundle/nifi-kerberos-iaa-providers/src/main/java/org/apache/nifi/kerberos/KerberosProvider.java
@@ -26,6 +26,7 @@ import
org.apache.nifi.authentication.exception.IdentityAccessException;
import
org.apache.nifi.authentication.exception.InvalidLoginCredentialsException;
import org.apache.nifi.authentication.exception.ProviderCreationException;
import org.apache.nifi.authentication.exception.ProviderDestructionException;
+import org.apache.nifi.security.util.krb.KerberosPrincipalParser;
import org.apache.nifi.util.FormatUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -46,6 +47,7 @@ public class KerberosProvider implements
LoginIdentityProvider {
private KerberosAuthenticationProvider provider;
private String issuer;
+ private String defaultRealm;
private long expiration;
@Override
@@ -61,11 +63,16 @@ public class KerberosProvider implements
LoginIdentityProvider {
}
try {
- expiration = FormatUtils.getTimeDuration(rawExpiration,
TimeUnit.MILLISECONDS);
+ expiration =
Double.valueOf(FormatUtils.getPreciseTimeDuration(rawExpiration,
TimeUnit.MILLISECONDS)).longValue();
} catch (final IllegalArgumentException iae) {
throw new ProviderCreationException(String.format("The Expiration
Duration '%s' is not a valid time duration", rawExpiration));
}
+ defaultRealm = configurationContext.getProperty("Default Realm");
+ if (StringUtils.isNotBlank(defaultRealm) &&
defaultRealm.contains("@")) {
+ throw new ProviderCreationException(String.format("The Default
Realm '%s' must not contain \"@\"", defaultRealm));
+ }
+
provider = new KerberosAuthenticationProvider();
SunJaasKerberosClient client = new SunJaasKerberosClient();
client.setDebug(true);
@@ -80,15 +87,40 @@ public class KerberosProvider implements
LoginIdentityProvider {
}
try {
+ final String rawPrincipal = credentials.getUsername();
+ final String parsedRealm =
KerberosPrincipalParser.getRealm(rawPrincipal);
+
+ // Apply default realm from KerberosIdentityProvider's
configuration specified in login-identity-providers.xml if a principal without
a realm was given
+ // Otherwise, the default realm configured from the krb5
configuration specified in the nifi.kerberos.krb5.file property will end up
being used
+ boolean realmInRawPrincipal = StringUtils.isNotBlank(parsedRealm);
+ final String identity;
+ if (realmInRawPrincipal) {
+ // there's a realm already in the given principal, use it
+ identity = rawPrincipal;
+ logger.debug("Realm was specified in principal {}, default
realm was not added to the identity being authenticated", rawPrincipal);
+ } else if (StringUtils.isNotBlank(defaultRealm)) {
+ // the value for the default realm is not blank, append the
realm to the given principal
+ identity = StringUtils.joinWith("@", rawPrincipal,
defaultRealm);
+ logger.debug("Realm was not specified in principal {}, default
realm {} was added to the identity being authenticated", rawPrincipal,
defaultRealm);
+ } else {
+ // otherwise, use the given principal, which will use the
default realm as specified in the krb5 configuration
+ identity = rawPrincipal;
+ logger.debug("Realm was not specified in principal {}, default
realm is blank and was not added to the identity being authenticated",
rawPrincipal);
+ }
+
// Perform the authentication
- final UsernamePasswordAuthenticationToken token = new
UsernamePasswordAuthenticationToken(credentials.getUsername(),
credentials.getPassword());
- logger.debug("Created authentication token for principal {} with
name {} and is authenticated {}", token.getPrincipal(), token.getName(),
token.isAuthenticated());
+ final UsernamePasswordAuthenticationToken token = new
UsernamePasswordAuthenticationToken(identity, credentials.getPassword());
+ if (logger.isDebugEnabled()) {
+ logger.debug("Created authentication token for principal {}
with name {} and is authenticated {}", token.getPrincipal(), token.getName(),
token.isAuthenticated());
+ }
final Authentication authentication = provider.authenticate(token);
- logger.debug("Ran provider.authenticate() and returned
authentication for " +
- "principal {} with name {} and is authenticated {}",
authentication.getPrincipal(), authentication.getName(),
authentication.isAuthenticated());
+ if (logger.isDebugEnabled()) {
+ logger.debug("Ran provider.authenticate() and returned
authentication for " +
+ "principal {} with name {} and is authenticated {}",
authentication.getPrincipal(), authentication.getName(),
authentication.isAuthenticated());
+ }
- return new AuthenticationResponse(authentication.getName(),
credentials.getUsername(), expiration, issuer);
+ return new AuthenticationResponse(authentication.getName(),
identity, expiration, issuer);
} catch (final AuthenticationException e) {
throw new InvalidLoginCredentialsException(e.getMessage(), e);
}