You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by kd...@apache.org on 2019/04/24 19:38:55 UTC
[nifi-registry] branch master updated: NIFIREG-253 Updated
KerberosIdentityProvider to use the "Default Realm" property
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-registry.git
The following commit(s) were added to refs/heads/master by this push:
new ca2c020 NIFIREG-253 Updated KerberosIdentityProvider to use the "Default Realm" property
ca2c020 is described below
commit ca2c020afad3e66d302022bed221276f9107b4af
Author: Jeff Storck <jt...@gmail.com>
AuthorDate: Fri Apr 19 17:31:19 2019 -0400
NIFIREG-253 Updated KerberosIdentityProvider to use the "Default Realm" property
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-registry-security-utils
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 #172.
Signed-off-by: Kevin Doran <kd...@apache.org>
---
.../nifi-registry-security-utils/pom.xml | 6 +++
.../util/kerberos/KerberosPrincipalParser.java | 60 ++++++++++++++++++++++
.../kerberos/KerberosPrincipalParserSpec.groovy | 44 ++++++++++++++++
.../kerberos/KerberosIdentityProvider.java | 54 ++++++++++++++-----
4 files changed, 152 insertions(+), 12 deletions(-)
diff --git a/nifi-registry-core/nifi-registry-security-utils/pom.xml b/nifi-registry-core/nifi-registry-security-utils/pom.xml
index 952f769..c60ca0e 100644
--- a/nifi-registry-core/nifi-registry-security-utils/pom.xml
+++ b/nifi-registry-core/nifi-registry-security-utils/pom.xml
@@ -37,6 +37,12 @@
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.spockframework</groupId>
+ <artifactId>spock-core</artifactId>
+ <version>1.0-groovy-2.4</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
</project>
diff --git a/nifi-registry-core/nifi-registry-security-utils/src/main/java/org/apache/nifi/registry/security/util/kerberos/KerberosPrincipalParser.java b/nifi-registry-core/nifi-registry-security-utils/src/main/java/org/apache/nifi/registry/security/util/kerberos/KerberosPrincipalParser.java
new file mode 100644
index 0000000..22328e9
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-security-utils/src/main/java/org/apache/nifi/registry/security/util/kerberos/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.registry.security.util.kerberos;
+
+import org.apache.commons.lang3.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-registry-core/nifi-registry-security-utils/src/test/groovy/org/apache/nifi/registry/security/util/kerberos/KerberosPrincipalParserSpec.groovy b/nifi-registry-core/nifi-registry-security-utils/src/test/groovy/org/apache/nifi/registry/security/util/kerberos/KerberosPrincipalParserSpec.groovy
new file mode 100644
index 0000000..e96307b
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-security-utils/src/test/groovy/org/apache/nifi/registry/security/util/kerberos/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.registry.security.util.kerberos
+
+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
+ "user@EXAMPLE.COM" || "EXAMPLE.COM"
+ "user@name@EXAMPLE.COM" || "EXAMPLE.COM"
+ "user\\@" || null
+ "user\\@name" || null
+ "user\\@name@EXAMPLE.COM" || "EXAMPLE.COM"
+ "user@EXAMPLE.COM\\@" || "EXAMPLE.COM\\@"
+ "user@@name@\\@@\\@" || "\\@"
+ "user@@name@\\@@\\@@EXAMPLE.COM" || "EXAMPLE.COM"
+ "user@@name@\\@@\\@@EXAMPLE.COM@" || null
+ "user\\@\\@name@EXAMPLE.COM" || "EXAMPLE.COM"
+ }
+}
diff --git a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/kerberos/KerberosIdentityProvider.java b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/kerberos/KerberosIdentityProvider.java
index 5e6e7bb..ee55c02 100644
--- a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/kerberos/KerberosIdentityProvider.java
+++ b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/kerberos/KerberosIdentityProvider.java
@@ -25,6 +25,7 @@ import org.apache.nifi.registry.security.authentication.exception.IdentityAccess
import org.apache.nifi.registry.security.authentication.exception.InvalidCredentialsException;
import org.apache.nifi.registry.security.exception.SecurityProviderCreationException;
import org.apache.nifi.registry.security.exception.SecurityProviderDestructionException;
+import org.apache.nifi.registry.security.util.kerberos.KerberosPrincipalParser;
import org.apache.nifi.registry.util.FormatUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -44,6 +45,7 @@ public class KerberosIdentityProvider extends BasicAuthIdentityProvider {
private KerberosAuthenticationProvider provider;
+ private String defaultRealm;
private long expiration;
@Override
@@ -65,6 +67,11 @@ public class KerberosIdentityProvider extends BasicAuthIdentityProvider {
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 SecurityProviderCreationException(String.format("The Default Realm '%s' must not contain \"@\"", defaultRealm));
+ }
+
provider = new KerberosAuthenticationProvider();
SunJaasKerberosClient client = new SunJaasKerberosClient();
client.setDebug(enableDebug);
@@ -80,24 +87,47 @@ public class KerberosIdentityProvider extends BasicAuthIdentityProvider {
throw new IdentityAccessException("The Kerberos authentication provider is not initialized.");
}
+
try {
- // perform the authentication
- final String username = authenticationRequest.getUsername();
+ final String rawPrincipal = authenticationRequest.getUsername();
final Object credentials = authenticationRequest.getCredentials();
- final String password = credentials != null && credentials instanceof String ? (String) credentials : null;
+ final String parsedRealm = KerberosPrincipalParser.getRealm(rawPrincipal);
+
+ // Apply default realm from KerberosIdentityProvider's configuration specified in identity-providers.xml if a principal without a realm was given
+ // Otherwise, the default realm configured from the krb5 configuration specified in the nifi.registry.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(username, credentials);
- logger.debug("Created authentication token " + token.toString());
+ final UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(identity, credentials);
- final Authentication authentication = provider.authenticate(token);
- logger.debug("Ran provider.authenticate(token) and returned authentication for " +
- "principal={} with name={} and isAuthenticated={}",
- authentication.getPrincipal(),
- authentication.getName(),
- authentication.isAuthenticated());
+ if (logger.isDebugEnabled()) {
+ logger.debug("Created authentication token " + token.toString());
+ }
- return new AuthenticationResponse(authentication.getName(), username, expiration, issuer);
+ final Authentication authentication = provider.authenticate(token);
+ if (logger.isDebugEnabled()) {
+ logger.debug("Ran provider.authenticate(token) and returned authentication for " +
+ "principal={} with name={} and isAuthenticated={}",
+ authentication.getPrincipal(),
+ authentication.getName(),
+ authentication.isAuthenticated());
+ }
+
+ return new AuthenticationResponse(authentication.getName(), identity, expiration, issuer);
} catch (final AuthenticationException e) {
throw new InvalidCredentialsException(e.getMessage(), e);
}