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);
         }

Reply via email to