NIFI-8977 Add KerberosUserService to Solr processors

Signed-off-by: Pierre Villard <pierre.villard.fr@gmail.com>

This closes #5395.
This commit is contained in:
Bryan Bende 2021-09-17 11:53:55 -04:00 committed by Pierre Villard
parent 6f87865e27
commit 1374f66f09
No known key found for this signature in database
GPG Key ID: F92A93B30C07C6D5
8 changed files with 161 additions and 23 deletions

View File

@ -85,6 +85,10 @@
<artifactId>nifi-kerberos-credentials-service-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-kerberos-user-service-api</artifactId>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>

View File

@ -77,6 +77,7 @@ import static org.apache.nifi.processors.solr.SolrUtils.COLLECTION;
import static org.apache.nifi.processors.solr.SolrUtils.KERBEROS_CREDENTIALS_SERVICE;
import static org.apache.nifi.processors.solr.SolrUtils.KERBEROS_PASSWORD;
import static org.apache.nifi.processors.solr.SolrUtils.KERBEROS_PRINCIPAL;
import static org.apache.nifi.processors.solr.SolrUtils.KERBEROS_USER_SERVICE;
import static org.apache.nifi.processors.solr.SolrUtils.RECORD_WRITER;
import static org.apache.nifi.processors.solr.SolrUtils.SOLR_CONNECTION_TIMEOUT;
import static org.apache.nifi.processors.solr.SolrUtils.SOLR_LOCATION;
@ -182,6 +183,7 @@ public class GetSolr extends SolrProcessor {
descriptors.add(RETURN_FIELDS);
descriptors.add(BATCH_SIZE);
descriptors.add(KERBEROS_CREDENTIALS_SERVICE);
descriptors.add(KERBEROS_USER_SERVICE);
descriptors.add(KERBEROS_PRINCIPAL);
descriptors.add(KERBEROS_PASSWORD);
descriptors.add(BASIC_USERNAME);

View File

@ -60,6 +60,7 @@ import static org.apache.nifi.processors.solr.SolrUtils.COLLECTION;
import static org.apache.nifi.processors.solr.SolrUtils.KERBEROS_CREDENTIALS_SERVICE;
import static org.apache.nifi.processors.solr.SolrUtils.KERBEROS_PASSWORD;
import static org.apache.nifi.processors.solr.SolrUtils.KERBEROS_PRINCIPAL;
import static org.apache.nifi.processors.solr.SolrUtils.KERBEROS_USER_SERVICE;
import static org.apache.nifi.processors.solr.SolrUtils.SOLR_CONNECTION_TIMEOUT;
import static org.apache.nifi.processors.solr.SolrUtils.SOLR_LOCATION;
import static org.apache.nifi.processors.solr.SolrUtils.SOLR_MAX_CONNECTIONS;
@ -138,6 +139,7 @@ public class PutSolrContentStream extends SolrProcessor {
descriptors.add(CONTENT_TYPE);
descriptors.add(COMMIT_WITHIN);
descriptors.add(KERBEROS_CREDENTIALS_SERVICE);
descriptors.add(KERBEROS_USER_SERVICE);
descriptors.add(KERBEROS_PRINCIPAL);
descriptors.add(KERBEROS_PASSWORD);
descriptors.add(BASIC_USERNAME);

View File

@ -66,6 +66,7 @@ import static org.apache.nifi.processors.solr.SolrUtils.COLLECTION;
import static org.apache.nifi.processors.solr.SolrUtils.KERBEROS_CREDENTIALS_SERVICE;
import static org.apache.nifi.processors.solr.SolrUtils.KERBEROS_PASSWORD;
import static org.apache.nifi.processors.solr.SolrUtils.KERBEROS_PRINCIPAL;
import static org.apache.nifi.processors.solr.SolrUtils.KERBEROS_USER_SERVICE;
import static org.apache.nifi.processors.solr.SolrUtils.SOLR_CONNECTION_TIMEOUT;
import static org.apache.nifi.processors.solr.SolrUtils.SOLR_LOCATION;
import static org.apache.nifi.processors.solr.SolrUtils.SOLR_MAX_CONNECTIONS;
@ -166,6 +167,7 @@ public class PutSolrRecord extends SolrProcessor {
descriptors.add(FIELDS_TO_INDEX);
descriptors.add(COMMIT_WITHIN);
descriptors.add(KERBEROS_CREDENTIALS_SERVICE);
descriptors.add(KERBEROS_USER_SERVICE);
descriptors.add(KERBEROS_PRINCIPAL);
descriptors.add(KERBEROS_PASSWORD);
descriptors.add(BASIC_USERNAME);

View File

@ -75,6 +75,7 @@ import java.util.concurrent.TimeUnit;
import static org.apache.nifi.processors.solr.SolrUtils.KERBEROS_CREDENTIALS_SERVICE;
import static org.apache.nifi.processors.solr.SolrUtils.KERBEROS_PASSWORD;
import static org.apache.nifi.processors.solr.SolrUtils.KERBEROS_PRINCIPAL;
import static org.apache.nifi.processors.solr.SolrUtils.KERBEROS_USER_SERVICE;
import static org.apache.nifi.processors.solr.SolrUtils.SOLR_TYPE;
import static org.apache.nifi.processors.solr.SolrUtils.COLLECTION;
import static org.apache.nifi.processors.solr.SolrUtils.SOLR_TYPE_CLOUD;
@ -262,6 +263,7 @@ public class QuerySolr extends SolrProcessor {
descriptors.add(SOLR_PARAM_ROWS);
descriptors.add(AMOUNT_DOCUMENTS_TO_RETURN);
descriptors.add(KERBEROS_CREDENTIALS_SERVICE);
descriptors.add(KERBEROS_USER_SERVICE);
descriptors.add(KERBEROS_PRINCIPAL);
descriptors.add(KERBEROS_PASSWORD);
descriptors.add(BASIC_USERNAME);

View File

@ -24,6 +24,7 @@ import org.apache.nifi.annotation.lifecycle.OnStopped;
import org.apache.nifi.components.ValidationContext;
import org.apache.nifi.components.ValidationResult;
import org.apache.nifi.kerberos.KerberosCredentialsService;
import org.apache.nifi.kerberos.KerberosUserService;
import org.apache.nifi.processor.AbstractProcessor;
import org.apache.nifi.processor.ProcessContext;
import org.apache.nifi.processor.ProcessSession;
@ -48,6 +49,7 @@ import static org.apache.nifi.processors.solr.SolrUtils.COLLECTION;
import static org.apache.nifi.processors.solr.SolrUtils.KERBEROS_CREDENTIALS_SERVICE;
import static org.apache.nifi.processors.solr.SolrUtils.KERBEROS_PASSWORD;
import static org.apache.nifi.processors.solr.SolrUtils.KERBEROS_PRINCIPAL;
import static org.apache.nifi.processors.solr.SolrUtils.KERBEROS_USER_SERVICE;
import static org.apache.nifi.processors.solr.SolrUtils.SOLR_LOCATION;
import static org.apache.nifi.processors.solr.SolrUtils.SOLR_TYPE;
import static org.apache.nifi.processors.solr.SolrUtils.SOLR_TYPE_CLOUD;
@ -82,12 +84,16 @@ public abstract class SolrProcessor extends AbstractProcessor {
final String kerberosPrincipal = context.getProperty(KERBEROS_PRINCIPAL).evaluateAttributeExpressions().getValue();
final String kerberosPassword = context.getProperty(KERBEROS_PASSWORD).getValue();
final KerberosCredentialsService kerberosCredentialsService = context.getProperty(KERBEROS_CREDENTIALS_SERVICE).asControllerService(KerberosCredentialsService.class);
if (kerberosCredentialsService != null) {
this.kerberosUser = createKerberosKeytabUser(kerberosCredentialsService);
} else if (!StringUtils.isBlank(kerberosPrincipal) && !StringUtils.isBlank(kerberosPassword)) {
this.kerberosUser = createKerberosPasswordUser(kerberosPrincipal, kerberosPassword);
final KerberosUserService kerberosUserService = context.getProperty(KERBEROS_USER_SERVICE).asControllerService(KerberosUserService.class);
if (kerberosUserService != null) {
this.kerberosUser = kerberosUserService.createKerberosUser();
} else {
final KerberosCredentialsService kerberosCredentialsService = context.getProperty(KERBEROS_CREDENTIALS_SERVICE).asControllerService(KerberosCredentialsService.class);
if (kerberosCredentialsService != null) {
this.kerberosUser = createKerberosKeytabUser(kerberosCredentialsService);
} else if (!StringUtils.isBlank(kerberosPrincipal) && !StringUtils.isBlank(kerberosPassword)) {
this.kerberosUser = createKerberosPasswordUser(kerberosPrincipal, kerberosPassword);
}
}
}
@ -276,6 +282,8 @@ public abstract class SolrProcessor extends AbstractProcessor {
// Validate that only one of kerberos princpal/password, kerberos creds service, or basic auth can be set
final KerberosCredentialsService kerberosCredentialsService = context.getProperty(KERBEROS_CREDENTIALS_SERVICE).asControllerService(KerberosCredentialsService.class);
final KerberosUserService kerberosUserService = context.getProperty(KERBEROS_USER_SERVICE).asControllerService(KerberosUserService.class);
if (kerberosCredentialsService != null && basicUsernameProvided && basicPasswordProvided) {
problems.add(new ValidationResult.Builder()
.subject(KERBEROS_CREDENTIALS_SERVICE.getDisplayName())
@ -300,6 +308,30 @@ public abstract class SolrProcessor extends AbstractProcessor {
.build());
}
if (kerberosUserService != null && basicUsernameProvided && basicPasswordProvided) {
problems.add(new ValidationResult.Builder()
.subject(KERBEROS_USER_SERVICE.getDisplayName())
.valid(false)
.explanation("basic auth and kerberos user service cannot be configured at the same time")
.build());
}
if (kerberosUserService != null && (kerberosPrincipalProvided || kerberosPasswordProvided)) {
problems.add(new ValidationResult.Builder()
.subject(KERBEROS_USER_SERVICE.getDisplayName())
.valid(false)
.explanation("kerberos principal/password and kerberos user service cannot be configured at the same time")
.build());
}
if (kerberosUserService != null && kerberosCredentialsService != null) {
problems.add(new ValidationResult.Builder()
.subject(KERBEROS_USER_SERVICE.getDisplayName())
.valid(false)
.explanation("kerberos user service and kerberos credential service cannot be configured at the same time")
.build());
}
Collection<ValidationResult> otherProblems = this.additionalCustomValidation(context);
if (otherProblems != null) {
problems.addAll(otherProblems);

View File

@ -33,6 +33,7 @@ import org.apache.nifi.expression.AttributeExpression;
import org.apache.nifi.expression.ExpressionLanguageScope;
import org.apache.nifi.flowfile.FlowFile;
import org.apache.nifi.kerberos.KerberosCredentialsService;
import org.apache.nifi.kerberos.KerberosUserService;
import org.apache.nifi.processor.ProcessContext;
import org.apache.nifi.processor.io.OutputStreamCallback;
import org.apache.nifi.processor.util.StandardValidators;
@ -151,6 +152,14 @@ public class SolrUtils {
.required(false)
.build();
static final PropertyDescriptor KERBEROS_USER_SERVICE = new PropertyDescriptor.Builder()
.name("kerberos-user-service")
.displayName("Kerberos User Service")
.description("Specifies the Kerberos User Controller Service that should be used for authenticating with Kerberos")
.identifiesControllerService(KerberosUserService.class)
.required(false)
.build();
public static final PropertyDescriptor KERBEROS_PRINCIPAL = new PropertyDescriptor.Builder()
.name("kerberos-principal")
.displayName("Kerberos Principal")

View File

@ -16,25 +16,9 @@
*/
package org.apache.nifi.processors.solr;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import javax.security.auth.login.LoginException;
import org.apache.nifi.controller.AbstractControllerService;
import org.apache.nifi.kerberos.KerberosCredentialsService;
import org.apache.nifi.kerberos.KerberosUserService;
import org.apache.nifi.processor.ProcessContext;
import org.apache.nifi.reporting.InitializationException;
import org.apache.nifi.security.krb.KerberosKeytabUser;
@ -56,6 +40,25 @@ import org.junit.Assert;
import org.junit.Test;
import org.mockito.Mockito;
import javax.security.auth.login.LoginException;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
* Test for PutSolr processor.
*/
@ -459,6 +462,30 @@ public class TestPutSolrContentStream {
Assert.assertEquals(keytab, ((KerberosKeytabUser)kerberosUser).getKeytabFile());
}
@Test
public void testBasicAuthAndKerberosUserServiceNotAllowedTogether() throws IOException, InitializationException {
final SolrClient solrClient = createEmbeddedSolrClient(DEFAULT_SOLR_CORE);
final TestableProcessor proc = new TestableProcessor(solrClient);
final TestRunner runner = createDefaultTestRunner(proc);
runner.assertValid();
runner.setProperty(SolrUtils.BASIC_USERNAME, "user1");
runner.setProperty(SolrUtils.BASIC_PASSWORD, "password");
runner.assertValid();
final KerberosUserService kerberosUserService = enableKerberosUserService(runner);
runner.setProperty(SolrUtils.KERBEROS_USER_SERVICE, kerberosUserService.getIdentifier());
runner.assertNotValid();
runner.removeProperty(SolrUtils.BASIC_USERNAME);
runner.removeProperty(SolrUtils.BASIC_PASSWORD);
runner.assertValid();
proc.onScheduled(runner.getProcessContext());
final KerberosUser kerberosUser = proc.getMockKerberosKeytabUser();
Assert.assertNotNull(kerberosUser);
}
@Test
public void testBasicAuthAndKerberosPrincipalPasswordNotAllowedTogether() throws IOException, InitializationException {
final SolrClient solrClient = createEmbeddedSolrClient(DEFAULT_SOLR_CORE);
@ -519,6 +546,54 @@ public class TestPutSolrContentStream {
Assert.assertEquals(keytab, ((KerberosKeytabUser)kerberosUser).getKeytabFile());
}
@Test
public void testKerberosPrincipalPasswordAndKerberosUserServiceNotAllowedTogether() throws IOException, InitializationException {
final SolrClient solrClient = createEmbeddedSolrClient(DEFAULT_SOLR_CORE);
final TestableProcessor proc = new TestableProcessor(solrClient);
final TestRunner runner = createDefaultTestRunner(proc);
runner.assertValid();
runner.setProperty(SolrUtils.KERBEROS_PRINCIPAL, "nifi@FOO.COM");
runner.setProperty(SolrUtils.KERBEROS_PASSWORD, "nifi");
final KerberosUserService kerberosUserService = enableKerberosUserService(runner);
runner.setProperty(SolrUtils.KERBEROS_USER_SERVICE, kerberosUserService.getIdentifier());
runner.assertNotValid();
runner.removeProperty(SolrUtils.KERBEROS_PRINCIPAL);
runner.removeProperty(SolrUtils.KERBEROS_PASSWORD);
runner.assertValid();
proc.onScheduled(runner.getProcessContext());
final KerberosUser kerberosUser = proc.getMockKerberosKeytabUser();
Assert.assertNotNull(kerberosUser);
}
@Test
public void testKerberosCredentialsServiceAndKerberosUserServiceNotAllowedTogether() throws IOException, InitializationException {
final SolrClient solrClient = createEmbeddedSolrClient(DEFAULT_SOLR_CORE);
final TestableProcessor proc = new TestableProcessor(solrClient);
final TestRunner runner = createDefaultTestRunner(proc);
runner.assertValid();
final KerberosCredentialsService kerberosCredentialsService = new MockKerberosCredentialsService("nifi@FOO.COM", "src/test/resources/foo.keytab");
runner.addControllerService("kerb-credentials", kerberosCredentialsService);
runner.enableControllerService(kerberosCredentialsService);
runner.setProperty(SolrUtils.KERBEROS_CREDENTIALS_SERVICE, "kerb-credentials");
runner.assertValid();
final KerberosUserService kerberosUserService = enableKerberosUserService(runner);
runner.setProperty(SolrUtils.KERBEROS_USER_SERVICE, kerberosUserService.getIdentifier());
runner.assertNotValid();
runner.removeProperty(SolrUtils.KERBEROS_CREDENTIALS_SERVICE);
runner.assertValid();
proc.onScheduled(runner.getProcessContext());
final KerberosUser kerberosUser = proc.getMockKerberosKeytabUser();
Assert.assertNotNull(kerberosUser);
}
@Test
public void testUpdateWithKerberosAuth() throws IOException, InitializationException, LoginException, PrivilegedActionException {
final String principal = "nifi@FOO.COM";
@ -730,4 +805,14 @@ public class TestPutSolrContentStream {
}
}
private KerberosUserService enableKerberosUserService(final TestRunner runner) throws InitializationException {
final KerberosUser kerberosUser = mock(KerberosUser.class);
final KerberosUserService kerberosUserService = mock(KerberosUserService.class);
when(kerberosUserService.getIdentifier()).thenReturn("userService1");
when(kerberosUserService.createKerberosUser()).thenReturn(kerberosUser);
runner.addControllerService(kerberosUserService.getIdentifier(), kerberosUserService);
runner.enableControllerService(kerberosUserService);
return kerberosUserService;
}
}