mirror of https://github.com/apache/nifi.git
NIFI-5945 Add support for password login to kerberos code in nifi-security-utils
Fixing solr test Signed-off-by: Matthew Burgess <mattyb149@apache.org> This closes #3256
This commit is contained in:
parent
cf7ab0ce18
commit
2bbfb3217b
|
@ -23,7 +23,6 @@ import org.slf4j.LoggerFactory;
|
|||
import javax.security.auth.Subject;
|
||||
import javax.security.auth.kerberos.KerberosPrincipal;
|
||||
import javax.security.auth.kerberos.KerberosTicket;
|
||||
import javax.security.auth.login.Configuration;
|
||||
import javax.security.auth.login.LoginContext;
|
||||
import javax.security.auth.login.LoginException;
|
||||
import java.security.PrivilegedAction;
|
||||
|
@ -34,14 +33,9 @@ import java.util.Date;
|
|||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
* Used to authenticate and execute actions when Kerberos is enabled and a keytab is being used.
|
||||
*
|
||||
* Some of the functionality in this class is adapted from Hadoop's UserGroupInformation.
|
||||
*/
|
||||
public class StandardKeytabUser implements KeytabUser {
|
||||
public abstract class AbstractKerberosUser implements KerberosUser {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(StandardKeytabUser.class);
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractKerberosUser.class);
|
||||
|
||||
static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'";
|
||||
|
||||
|
@ -50,18 +44,15 @@ public class StandardKeytabUser implements KeytabUser {
|
|||
*/
|
||||
static final float TICKET_RENEW_WINDOW = 0.80f;
|
||||
|
||||
private final String principal;
|
||||
private final String keytabFile;
|
||||
private final AtomicBoolean loggedIn = new AtomicBoolean(false);
|
||||
protected final String principal;
|
||||
protected final AtomicBoolean loggedIn = new AtomicBoolean(false);
|
||||
|
||||
private Subject subject;
|
||||
private LoginContext loginContext;
|
||||
protected Subject subject;
|
||||
protected LoginContext loginContext;
|
||||
|
||||
public StandardKeytabUser(final String principal, final String keytabFile) {
|
||||
public AbstractKerberosUser(final String principal) {
|
||||
this.principal = principal;
|
||||
this.keytabFile = keytabFile;
|
||||
Validate.notBlank(principal);
|
||||
Validate.notBlank(keytabFile);
|
||||
Validate.notBlank(this.principal);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -80,19 +71,19 @@ public class StandardKeytabUser implements KeytabUser {
|
|||
if (loginContext == null) {
|
||||
LOGGER.debug("Initializing new login context...");
|
||||
this.subject = new Subject();
|
||||
|
||||
final Configuration config = new KeytabConfiguration(principal, keytabFile);
|
||||
this.loginContext = new LoginContext("KeytabConf", subject, null, config);
|
||||
this.loginContext = createLoginContext(subject);
|
||||
}
|
||||
|
||||
loginContext.login();
|
||||
loggedIn.set(true);
|
||||
LOGGER.debug("Successful login for {}", new Object[]{principal});
|
||||
} catch (LoginException le) {
|
||||
throw new LoginException("Unable to login with " + principal + " and " + keytabFile + " due to: " + le.getMessage());
|
||||
throw new LoginException("Unable to login with " + principal + " due to: " + le.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract LoginContext createLoginContext(final Subject subject) throws LoginException;
|
||||
|
||||
/**
|
||||
* Performs a logout of the current user.
|
||||
*
|
||||
|
@ -244,14 +235,6 @@ public class StandardKeytabUser implements KeytabUser {
|
|||
return principal;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the keytab file for this user
|
||||
*/
|
||||
@Override
|
||||
public String getKeytabFile() {
|
||||
return keytabFile;
|
||||
}
|
||||
|
||||
// Visible for testing
|
||||
Subject getSubject() {
|
||||
return this.subject;
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* 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.krb;
|
||||
|
||||
public interface ConfigurationUtil {
|
||||
|
||||
boolean IS_IBM = System.getProperty("java.vendor", "").contains("IBM");
|
||||
String IBM_KRB5_LOGIN_MODULE = "com.ibm.security.auth.module.Krb5LoginModule";
|
||||
String SUN_KRB5_LOGIN_MODULE = "com.sun.security.auth.module.Krb5LoginModule";
|
||||
|
||||
}
|
|
@ -25,24 +25,24 @@ import javax.security.auth.login.LoginException;
|
|||
import java.security.PrivilegedAction;
|
||||
|
||||
/**
|
||||
* Helper class for processors to perform an action as a KeytabUser.
|
||||
* Helper class for processors to perform an action as a KerberosUser.
|
||||
*/
|
||||
public class KeytabAction {
|
||||
public class KerberosAction {
|
||||
|
||||
private final KeytabUser keytabUser;
|
||||
private final KerberosUser kerberosUser;
|
||||
private final PrivilegedAction action;
|
||||
private final ProcessContext context;
|
||||
private final ComponentLog logger;
|
||||
|
||||
public KeytabAction(final KeytabUser keytabUser,
|
||||
final PrivilegedAction action,
|
||||
final ProcessContext context,
|
||||
final ComponentLog logger) {
|
||||
this.keytabUser = keytabUser;
|
||||
public KerberosAction(final KerberosUser kerberosUser,
|
||||
final PrivilegedAction action,
|
||||
final ProcessContext context,
|
||||
final ComponentLog logger) {
|
||||
this.kerberosUser = kerberosUser;
|
||||
this.action = action;
|
||||
this.context = context;
|
||||
this.logger = logger;
|
||||
Validate.notNull(this.keytabUser);
|
||||
Validate.notNull(this.kerberosUser);
|
||||
Validate.notNull(this.action);
|
||||
Validate.notNull(this.context);
|
||||
Validate.notNull(this.logger);
|
||||
|
@ -50,10 +50,10 @@ public class KeytabAction {
|
|||
|
||||
public void execute() {
|
||||
// lazily login the first time the processor executes
|
||||
if (!keytabUser.isLoggedIn()) {
|
||||
if (!kerberosUser.isLoggedIn()) {
|
||||
try {
|
||||
keytabUser.login();
|
||||
logger.info("Successful login for {}", new Object[]{keytabUser.getPrincipal()});
|
||||
kerberosUser.login();
|
||||
logger.info("Successful login for {}", new Object[]{kerberosUser.getPrincipal()});
|
||||
} catch (LoginException e) {
|
||||
// make sure to yield so the processor doesn't keep retrying the rolled back flow files immediately
|
||||
context.yield();
|
||||
|
@ -63,7 +63,7 @@ public class KeytabAction {
|
|||
|
||||
// check if we need to re-login, will only happen if re-login window is reached (80% of TGT life)
|
||||
try {
|
||||
keytabUser.checkTGTAndRelogin();
|
||||
kerberosUser.checkTGTAndRelogin();
|
||||
} catch (LoginException e) {
|
||||
// make sure to yield so the processor doesn't keep retrying the rolled back flow files immediately
|
||||
context.yield();
|
||||
|
@ -72,15 +72,15 @@ public class KeytabAction {
|
|||
|
||||
// attempt to execute the action, if an exception is caught attempt to logout/login and retry
|
||||
try {
|
||||
keytabUser.doAs(action);
|
||||
kerberosUser.doAs(action);
|
||||
} catch (SecurityException se) {
|
||||
logger.info("Privileged action failed, attempting relogin and retrying...");
|
||||
logger.debug("", se);
|
||||
|
||||
try {
|
||||
keytabUser.logout();
|
||||
keytabUser.login();
|
||||
keytabUser.doAs(action);
|
||||
kerberosUser.logout();
|
||||
kerberosUser.login();
|
||||
kerberosUser.doAs(action);
|
||||
} catch (Exception e) {
|
||||
// make sure to yield so the processor doesn't keep retrying the rolled back flow files immediately
|
||||
context.yield();
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* 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.krb;
|
||||
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
||||
import javax.security.auth.Subject;
|
||||
import javax.security.auth.login.Configuration;
|
||||
import javax.security.auth.login.LoginContext;
|
||||
import javax.security.auth.login.LoginException;
|
||||
|
||||
/**
|
||||
* Used to authenticate and execute actions when Kerberos is enabled and a keytab is being used.
|
||||
*
|
||||
* Some of the functionality in this class is adapted from Hadoop's UserGroupInformation.
|
||||
*/
|
||||
public class KerberosKeytabUser extends AbstractKerberosUser {
|
||||
|
||||
private final String keytabFile;
|
||||
|
||||
public KerberosKeytabUser(final String principal, final String keytabFile) {
|
||||
super(principal);
|
||||
this.keytabFile = keytabFile;
|
||||
Validate.notBlank(keytabFile);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LoginContext createLoginContext(Subject subject) throws LoginException {
|
||||
final Configuration config = new KeytabConfiguration(principal, keytabFile);
|
||||
return new LoginContext("KeytabConf", subject, null, config);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the keytab file for this user
|
||||
*/
|
||||
public String getKeytabFile() {
|
||||
return keytabFile;
|
||||
}
|
||||
|
||||
// Visible for testing
|
||||
Subject getSubject() {
|
||||
return this.subject;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* 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.krb;
|
||||
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
||||
import javax.security.auth.Subject;
|
||||
import javax.security.auth.callback.Callback;
|
||||
import javax.security.auth.callback.CallbackHandler;
|
||||
import javax.security.auth.callback.NameCallback;
|
||||
import javax.security.auth.callback.PasswordCallback;
|
||||
import javax.security.auth.callback.UnsupportedCallbackException;
|
||||
import javax.security.auth.login.AppConfigurationEntry;
|
||||
import javax.security.auth.login.Configuration;
|
||||
import javax.security.auth.login.LoginContext;
|
||||
import javax.security.auth.login.LoginException;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* KerberosUser that authenticates via username and password instead of keytab.
|
||||
*/
|
||||
public class KerberosPasswordUser extends AbstractKerberosUser {
|
||||
|
||||
private final String password;
|
||||
|
||||
public KerberosPasswordUser(final String principal, final String password) {
|
||||
super(principal);
|
||||
this.password = password;
|
||||
Validate.notBlank(this.password);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LoginContext createLoginContext(final Subject subject) throws LoginException {
|
||||
final Configuration configuration = new PasswordConfig();
|
||||
final CallbackHandler callbackHandler = new UsernamePasswordCallbackHandler(principal, password);
|
||||
return new LoginContext("PasswordConf", subject, callbackHandler, configuration);
|
||||
}
|
||||
|
||||
/**
|
||||
* JAAS Configuration to use when logging in with username/password.
|
||||
*/
|
||||
private static class PasswordConfig extends Configuration {
|
||||
|
||||
@Override
|
||||
public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
|
||||
HashMap<String, String> options = new HashMap<String, String>();
|
||||
options.put("storeKey", "true");
|
||||
options.put("refreshKrb5Config", "true");
|
||||
|
||||
final String krbLoginModuleName = ConfigurationUtil.IS_IBM
|
||||
? ConfigurationUtil.IBM_KRB5_LOGIN_MODULE : ConfigurationUtil.SUN_KRB5_LOGIN_MODULE;
|
||||
|
||||
return new AppConfigurationEntry[] {
|
||||
new AppConfigurationEntry(
|
||||
krbLoginModuleName,
|
||||
AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
|
||||
options
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* CallbackHandler that provides the given username and password.
|
||||
*/
|
||||
private static class UsernamePasswordCallbackHandler implements CallbackHandler {
|
||||
|
||||
private final String username;
|
||||
private final String password;
|
||||
|
||||
public UsernamePasswordCallbackHandler(final String username, final String password) {
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
Validate.notBlank(this.username);
|
||||
Validate.notBlank(this.password);
|
||||
}
|
||||
|
||||
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
|
||||
for (final Callback callback : callbacks) {
|
||||
if (callback instanceof NameCallback) {
|
||||
final NameCallback nameCallback = (NameCallback) callback;
|
||||
nameCallback.setName(username);
|
||||
} else if (callback instanceof PasswordCallback) {
|
||||
final PasswordCallback passwordCallback = (PasswordCallback) callback;
|
||||
passwordCallback.setPassword(password.toCharArray());
|
||||
} else {
|
||||
throw new IllegalStateException("Unexpected callback type: " + callback.getClass().getCanonicalName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -24,7 +24,7 @@ import java.security.PrivilegedExceptionAction;
|
|||
/**
|
||||
* A keytab-based user that can login/logout and perform actions as the given user.
|
||||
*/
|
||||
public interface KeytabUser {
|
||||
public interface KerberosUser {
|
||||
|
||||
/**
|
||||
* Performs a login for the given user.
|
||||
|
@ -80,9 +80,4 @@ public interface KeytabUser {
|
|||
*/
|
||||
String getPrincipal();
|
||||
|
||||
/**
|
||||
* @return the keytab file for this user
|
||||
*/
|
||||
String getKeytabFile();
|
||||
|
||||
}
|
|
@ -28,10 +28,6 @@ import java.util.Map;
|
|||
*/
|
||||
public class KeytabConfiguration extends Configuration {
|
||||
|
||||
static final boolean IS_IBM = System.getProperty("java.vendor", "").contains("IBM");
|
||||
static final String IBM_KRB5_LOGIN_MODULE = "com.ibm.security.auth.module.Krb5LoginModule";
|
||||
static final String SUN_KRB5_LOGIN_MODULE = "com.sun.security.auth.module.Krb5LoginModule";
|
||||
|
||||
private final String principal;
|
||||
private final String keytabFile;
|
||||
|
||||
|
@ -53,7 +49,7 @@ public class KeytabConfiguration extends Configuration {
|
|||
options.put("principal", principal);
|
||||
options.put("refreshKrb5Config", "true");
|
||||
|
||||
if (IS_IBM) {
|
||||
if (ConfigurationUtil.IS_IBM) {
|
||||
options.put("useKeytab", keytabFile);
|
||||
options.put("credsType", "both");
|
||||
} else {
|
||||
|
@ -64,7 +60,8 @@ public class KeytabConfiguration extends Configuration {
|
|||
options.put("storeKey", "true");
|
||||
}
|
||||
|
||||
final String krbLoginModuleName = IS_IBM ? IBM_KRB5_LOGIN_MODULE : SUN_KRB5_LOGIN_MODULE;
|
||||
final String krbLoginModuleName = ConfigurationUtil.IS_IBM
|
||||
? ConfigurationUtil.IBM_KRB5_LOGIN_MODULE : ConfigurationUtil.SUN_KRB5_LOGIN_MODULE;
|
||||
|
||||
this.kerberosKeytabConfigEntry = new AppConfigurationEntry(
|
||||
krbLoginModuleName, AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options);
|
||||
|
|
|
@ -67,8 +67,11 @@ public class KDCServer {
|
|||
return kdc.getRealm();
|
||||
}
|
||||
|
||||
public void createKeytabFile(final File keytabFile, final String... names) throws Exception {
|
||||
public void createKeytabPrincipal(final File keytabFile, final String... names) throws Exception {
|
||||
kdc.createPrincipal(keytabFile, names);
|
||||
}
|
||||
|
||||
public void createPasswordPrincipal(final String principal, final String password) throws Exception {
|
||||
kdc.createPrincipal(principal, password);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ import static org.junit.Assert.assertEquals;
|
|||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
public class KeytabUserIT {
|
||||
public class KerberosUserIT {
|
||||
|
||||
@ClassRule
|
||||
public static TemporaryFolder tmpDir = new TemporaryFolder();
|
||||
|
@ -50,6 +50,9 @@ public class KeytabUserIT {
|
|||
private static KerberosPrincipal principal2;
|
||||
private static File principal2KeytabFile;
|
||||
|
||||
private static KerberosPrincipal principal3;
|
||||
private static final String principal3Password = "changeme";
|
||||
|
||||
@BeforeClass
|
||||
public static void setupClass() throws Exception {
|
||||
kdc = new KDCServer(tmpDir.newFolder("mini-kdc_"));
|
||||
|
@ -58,31 +61,34 @@ public class KeytabUserIT {
|
|||
|
||||
principal1 = new KerberosPrincipal("user1@" + kdc.getRealm());
|
||||
principal1KeytabFile = tmpDir.newFile("user1.keytab");
|
||||
kdc.createKeytabFile(principal1KeytabFile, "user1");
|
||||
kdc.createKeytabPrincipal(principal1KeytabFile, "user1");
|
||||
|
||||
principal2 = new KerberosPrincipal("user2@" + kdc.getRealm());
|
||||
principal2KeytabFile = tmpDir.newFile("user2.keytab");
|
||||
kdc.createKeytabFile(principal2KeytabFile, "user2");
|
||||
kdc.createKeytabPrincipal(principal2KeytabFile, "user2");
|
||||
|
||||
principal3 = new KerberosPrincipal("user3@" + kdc.getRealm());
|
||||
kdc.createPasswordPrincipal("user3", principal3Password);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccessfulLoginAndLogout() throws LoginException {
|
||||
public void testKeytabUserSuccessfulLoginAndLogout() throws LoginException {
|
||||
// perform login for user1
|
||||
final KeytabUser user1 = new StandardKeytabUser(principal1.getName(), principal1KeytabFile.getAbsolutePath());
|
||||
final KerberosUser user1 = new KerberosKeytabUser(principal1.getName(), principal1KeytabFile.getAbsolutePath());
|
||||
user1.login();
|
||||
|
||||
// perform login for user2
|
||||
final KeytabUser user2 = new StandardKeytabUser(principal2.getName(), principal2KeytabFile.getAbsolutePath());
|
||||
final KerberosUser user2 = new KerberosKeytabUser(principal2.getName(), principal2KeytabFile.getAbsolutePath());
|
||||
user2.login();
|
||||
|
||||
// verify user1 Subject only has user1 principal
|
||||
final Subject user1Subject = ((StandardKeytabUser) user1).getSubject();
|
||||
final Subject user1Subject = ((KerberosKeytabUser) user1).getSubject();
|
||||
final Set<Principal> user1SubjectPrincipals = user1Subject.getPrincipals();
|
||||
assertEquals(1, user1SubjectPrincipals.size());
|
||||
assertEquals(principal1.getName(), user1SubjectPrincipals.iterator().next().getName());
|
||||
|
||||
// verify user2 Subject only has user2 principal
|
||||
final Subject user2Subject = ((StandardKeytabUser) user2).getSubject();
|
||||
final Subject user2Subject = ((KerberosKeytabUser) user2).getSubject();
|
||||
final Set<Principal> user2SubjectPrincipals = user2Subject.getPrincipals();
|
||||
assertEquals(1, user2SubjectPrincipals.size());
|
||||
assertEquals(principal2.getName(), user2SubjectPrincipals.iterator().next().getName());
|
||||
|
@ -101,9 +107,9 @@ public class KeytabUserIT {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testLoginWithUnknownPrincipal() throws LoginException {
|
||||
public void testKeytabLoginWithUnknownPrincipal() throws LoginException {
|
||||
final String unknownPrincipal = "doesnotexist@" + kdc.getRealm();
|
||||
final KeytabUser user1 = new StandardKeytabUser(unknownPrincipal, principal1KeytabFile.getAbsolutePath());
|
||||
final KerberosUser user1 = new KerberosKeytabUser(unknownPrincipal, principal1KeytabFile.getAbsolutePath());
|
||||
try {
|
||||
user1.login();
|
||||
fail("Login should have failed");
|
||||
|
@ -113,9 +119,38 @@ public class KeytabUserIT {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPasswordUserSuccessfulLoginAndLogout() throws LoginException {
|
||||
// perform login for user
|
||||
final KerberosUser user = new KerberosPasswordUser(principal3.getName(), principal3Password);
|
||||
user.login();
|
||||
|
||||
// verify user Subject only has user principal
|
||||
final Subject userSubject = ((KerberosPasswordUser) user).getSubject();
|
||||
final Set<Principal> userSubjectPrincipals = userSubject.getPrincipals();
|
||||
assertEquals(1, userSubjectPrincipals.size());
|
||||
assertEquals(principal3.getName(), userSubjectPrincipals.iterator().next().getName());
|
||||
|
||||
// call check/relogin and verify neither user performed a relogin
|
||||
assertFalse(user.checkTGTAndRelogin());
|
||||
|
||||
// perform logout for both users
|
||||
user.logout();
|
||||
|
||||
// verify subjects have no more principals
|
||||
assertEquals(0, userSubject.getPrincipals().size());
|
||||
}
|
||||
|
||||
@Test(expected = LoginException.class)
|
||||
public void testPasswordUserLoginWithInvalidPassword() throws LoginException {
|
||||
// perform login for user
|
||||
final KerberosUser user = new KerberosPasswordUser("user3", "NOT THE PASSWORD");
|
||||
user.login();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCheckTGTAndRelogin() throws LoginException, InterruptedException {
|
||||
final KeytabUser user1 = new StandardKeytabUser(principal1.getName(), principal1KeytabFile.getAbsolutePath());
|
||||
final KerberosUser user1 = new KerberosKeytabUser(principal1.getName(), principal1KeytabFile.getAbsolutePath());
|
||||
user1.login();
|
||||
|
||||
// Since we set the lifetime to 15 seconds we should hit a relogin before 15 attempts
|
||||
|
@ -136,7 +171,7 @@ public class KeytabUserIT {
|
|||
|
||||
@Test
|
||||
public void testKeytabAction() {
|
||||
final KeytabUser user1 = new StandardKeytabUser(principal1.getName(), principal1KeytabFile.getAbsolutePath());
|
||||
final KerberosUser user1 = new KerberosKeytabUser(principal1.getName(), principal1KeytabFile.getAbsolutePath());
|
||||
|
||||
final AtomicReference<String> resultHolder = new AtomicReference<>(null);
|
||||
final PrivilegedAction privilegedAction = () -> {
|
||||
|
@ -148,8 +183,8 @@ public class KeytabUserIT {
|
|||
final ComponentLog logger = Mockito.mock(ComponentLog.class);
|
||||
|
||||
// create the action to test and execute it
|
||||
final KeytabAction keytabAction = new KeytabAction(user1, privilegedAction, context, logger);
|
||||
keytabAction.execute();
|
||||
final KerberosAction kerberosAction = new KerberosAction(user1, privilegedAction, context, logger);
|
||||
kerberosAction.execute();
|
||||
|
||||
// if the result holder has the string success then we know the action executed
|
||||
assertEquals("SUCCESS", resultHolder.get());
|
|
@ -39,7 +39,7 @@ public class TestKeytabConfiguration {
|
|||
assertEquals(1, entries.length);
|
||||
|
||||
final AppConfigurationEntry entry = entries[0];
|
||||
assertEquals(KeytabConfiguration.SUN_KRB5_LOGIN_MODULE, entry.getLoginModuleName());
|
||||
assertEquals(ConfigurationUtil.SUN_KRB5_LOGIN_MODULE, entry.getLoginModuleName());
|
||||
assertEquals(principal, entry.getOptions().get("principal"));
|
||||
assertEquals(keytab, entry.getOptions().get("keyTab"));
|
||||
}
|
||||
|
|
|
@ -28,9 +28,9 @@ import org.apache.nifi.processor.AbstractProcessor;
|
|||
import org.apache.nifi.processor.ProcessContext;
|
||||
import org.apache.nifi.processor.ProcessSession;
|
||||
import org.apache.nifi.processor.exception.ProcessException;
|
||||
import org.apache.nifi.security.krb.KeytabAction;
|
||||
import org.apache.nifi.security.krb.KeytabUser;
|
||||
import org.apache.nifi.security.krb.StandardKeytabUser;
|
||||
import org.apache.nifi.security.krb.KerberosAction;
|
||||
import org.apache.nifi.security.krb.KerberosUser;
|
||||
import org.apache.nifi.security.krb.KerberosKeytabUser;
|
||||
import org.apache.nifi.ssl.SSLContextService;
|
||||
import org.apache.solr.client.solrj.SolrClient;
|
||||
|
||||
|
@ -63,7 +63,7 @@ public abstract class SolrProcessor extends AbstractProcessor {
|
|||
private volatile String basicPassword;
|
||||
private volatile boolean basicAuthEnabled = false;
|
||||
|
||||
private volatile KeytabUser keytabUser;
|
||||
private volatile KerberosUser kerberosUser;
|
||||
|
||||
@OnScheduled
|
||||
public final void onScheduled(final ProcessContext context) throws IOException {
|
||||
|
@ -78,12 +78,12 @@ public abstract class SolrProcessor extends AbstractProcessor {
|
|||
|
||||
final KerberosCredentialsService kerberosCredentialsService = context.getProperty(KERBEROS_CREDENTIALS_SERVICE).asControllerService(KerberosCredentialsService.class);
|
||||
if (kerberosCredentialsService != null) {
|
||||
this.keytabUser = createKeytabUser(kerberosCredentialsService);
|
||||
this.kerberosUser = createKeytabUser(kerberosCredentialsService);
|
||||
}
|
||||
}
|
||||
|
||||
protected KeytabUser createKeytabUser(final KerberosCredentialsService kerberosCredentialsService) {
|
||||
return new StandardKeytabUser(kerberosCredentialsService.getPrincipal(), kerberosCredentialsService.getKeytab());
|
||||
protected KerberosUser createKeytabUser(final KerberosCredentialsService kerberosCredentialsService) {
|
||||
return new KerberosKeytabUser(kerberosCredentialsService.getPrincipal(), kerberosCredentialsService.getKeytab());
|
||||
}
|
||||
|
||||
@OnStopped
|
||||
|
@ -96,10 +96,10 @@ public abstract class SolrProcessor extends AbstractProcessor {
|
|||
}
|
||||
}
|
||||
|
||||
if (keytabUser != null) {
|
||||
if (kerberosUser != null) {
|
||||
try {
|
||||
keytabUser.logout();
|
||||
keytabUser = null;
|
||||
kerberosUser.logout();
|
||||
kerberosUser = null;
|
||||
} catch (LoginException e) {
|
||||
getLogger().debug("Error logging out keytab user", e);
|
||||
}
|
||||
|
@ -108,8 +108,8 @@ public abstract class SolrProcessor extends AbstractProcessor {
|
|||
|
||||
@Override
|
||||
public final void onTrigger(final ProcessContext context, final ProcessSession session) throws ProcessException {
|
||||
final KeytabUser keytabUser = getKerberosKeytabUser();
|
||||
if (keytabUser == null) {
|
||||
final KerberosUser kerberosUser = getKerberosKeytabUser();
|
||||
if (kerberosUser == null) {
|
||||
doOnTrigger(context, session);
|
||||
} else {
|
||||
// wrap doOnTrigger in a privileged action
|
||||
|
@ -119,8 +119,8 @@ public abstract class SolrProcessor extends AbstractProcessor {
|
|||
};
|
||||
|
||||
// execute the privileged action as the given keytab user
|
||||
final KeytabAction keytabAction = new KeytabAction(keytabUser, action, context, getLogger());
|
||||
keytabAction.execute();
|
||||
final KerberosAction kerberosAction = new KerberosAction(kerberosUser, action, context, getLogger());
|
||||
kerberosAction.execute();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -168,8 +168,8 @@ public abstract class SolrProcessor extends AbstractProcessor {
|
|||
return basicAuthEnabled;
|
||||
}
|
||||
|
||||
protected final KeytabUser getKerberosKeytabUser() {
|
||||
return keytabUser;
|
||||
protected final KerberosUser getKerberosKeytabUser() {
|
||||
return kerberosUser;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -21,7 +21,8 @@ import org.apache.nifi.kerberos.KerberosCredentialsService;
|
|||
import org.apache.nifi.processor.ProcessContext;
|
||||
import org.apache.nifi.processor.exception.ProcessException;
|
||||
import org.apache.nifi.reporting.InitializationException;
|
||||
import org.apache.nifi.security.krb.KeytabUser;
|
||||
import org.apache.nifi.security.krb.KerberosKeytabUser;
|
||||
import org.apache.nifi.security.krb.KerberosUser;
|
||||
import org.apache.nifi.ssl.SSLContextService;
|
||||
import org.apache.nifi.util.TestRunner;
|
||||
import org.apache.nifi.util.TestRunners;
|
||||
|
@ -451,10 +452,10 @@ public class TestPutSolrContentStream {
|
|||
runner.assertValid();
|
||||
|
||||
proc.onScheduled(runner.getProcessContext());
|
||||
final KeytabUser keytabUser = proc.getMockKerberosKeytabUser();
|
||||
Assert.assertNotNull(keytabUser);
|
||||
Assert.assertEquals(principal, keytabUser.getPrincipal());
|
||||
Assert.assertEquals(keytab, keytabUser.getKeytabFile());
|
||||
final KerberosUser kerberosUser = proc.getMockKerberosKeytabUser();;
|
||||
Assert.assertNotNull(kerberosUser);
|
||||
Assert.assertEquals(principal, kerberosUser.getPrincipal());
|
||||
Assert.assertEquals(keytab, ((KerberosKeytabUser)kerberosUser).getKeytabFile());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -462,20 +463,20 @@ public class TestPutSolrContentStream {
|
|||
final String principal = "nifi@FOO.COM";
|
||||
final String keytab = "src/test/resources/foo.keytab";
|
||||
|
||||
// Setup a mock KeytabUser that will still execute the privileged action
|
||||
final KeytabUser keytabUser = Mockito.mock(KeytabUser.class);
|
||||
when(keytabUser.getPrincipal()).thenReturn(principal);
|
||||
when(keytabUser.getKeytabFile()).thenReturn(keytab);
|
||||
when(keytabUser.doAs(any(PrivilegedAction.class))).thenAnswer((invocation -> {
|
||||
// Setup a mock KerberosUser that will still execute the privileged action
|
||||
final KerberosKeytabUser kerberosUser = Mockito.mock(KerberosKeytabUser.class);
|
||||
when(kerberosUser.getPrincipal()).thenReturn(principal);
|
||||
when(kerberosUser.getKeytabFile()).thenReturn(keytab);
|
||||
when(kerberosUser.doAs(any(PrivilegedAction.class))).thenAnswer((invocation -> {
|
||||
final PrivilegedAction action = (PrivilegedAction) invocation.getArguments()[0];
|
||||
action.run();
|
||||
return null;
|
||||
})
|
||||
);
|
||||
|
||||
// Configure the processor with the mock KeytabUser and with a credentials service
|
||||
// Configure the processor with the mock KerberosUser and with a credentials service
|
||||
final SolrClient solrClient = createEmbeddedSolrClient(DEFAULT_SOLR_CORE);
|
||||
final TestableProcessor proc = new TestableProcessor(solrClient, keytabUser);
|
||||
final TestableProcessor proc = new TestableProcessor(solrClient, kerberosUser);
|
||||
final TestRunner runner = createDefaultTestRunner(proc);
|
||||
|
||||
final KerberosCredentialsService kerberosCredentialsService = new MockKerberosCredentialsService(principal, keytab);
|
||||
|
@ -499,9 +500,9 @@ public class TestPutSolrContentStream {
|
|||
}
|
||||
|
||||
// Verify that during the update the user was logged in, TGT was checked, and the action was executed
|
||||
verify(keytabUser, times(1)).login();
|
||||
verify(keytabUser, times(1)).checkTGTAndRelogin();
|
||||
verify(keytabUser, times(1)).doAs(any(PrivilegedAction.class));
|
||||
verify(kerberosUser, times(1)).login();
|
||||
verify(kerberosUser, times(1)).checkTGTAndRelogin();
|
||||
verify(kerberosUser, times(1)).doAs(any(PrivilegedAction.class));
|
||||
}
|
||||
|
||||
|
||||
|
@ -647,15 +648,15 @@ public class TestPutSolrContentStream {
|
|||
// Override createSolrClient and return the passed in SolrClient
|
||||
private class TestableProcessor extends PutSolrContentStream {
|
||||
private SolrClient solrClient;
|
||||
private KeytabUser keytabUser;
|
||||
private KerberosUser kerberosUser;
|
||||
|
||||
public TestableProcessor(SolrClient solrClient) {
|
||||
this.solrClient = solrClient;
|
||||
}
|
||||
|
||||
public TestableProcessor(SolrClient solrClient, KeytabUser keytabUser) {
|
||||
public TestableProcessor(SolrClient solrClient, KerberosUser kerberosUser) {
|
||||
this.solrClient = solrClient;
|
||||
this.keytabUser = keytabUser;
|
||||
this.kerberosUser = kerberosUser;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -664,15 +665,15 @@ public class TestPutSolrContentStream {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected KeytabUser createKeytabUser(KerberosCredentialsService kerberosCredentialsService) {
|
||||
if (keytabUser != null) {
|
||||
return keytabUser;
|
||||
protected KerberosUser createKeytabUser(KerberosCredentialsService kerberosCredentialsService) {
|
||||
if (kerberosUser != null) {
|
||||
return kerberosUser;
|
||||
} else {
|
||||
return super.createKeytabUser(kerberosCredentialsService);
|
||||
}
|
||||
}
|
||||
|
||||
public KeytabUser getMockKerberosKeytabUser() {
|
||||
public KerberosUser getMockKerberosKeytabUser() {
|
||||
return super.getKerberosKeytabUser();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue