NIFI-6400 Better options, consistent ids for ShellUserGroupProvider.

NIFI-6400 Fixes to address PR feedback.
NIFI-6400 Accepts proposed changes.

This closes #3637
This commit is contained in:
Troy Melhase 2019-08-06 13:33:05 -08:00 committed by Matt Gilman
parent a1b02ec113
commit 4596ef7c8a
No known key found for this signature in database
GPG Key ID: DF61EC19432AEE37
5 changed files with 235 additions and 285 deletions

View File

@ -469,6 +469,8 @@ The ShellUserGroupProvider has the following properties:
| Property Name | Description
|`Initial Refresh Delay` | Duration of initial delay before first user and group refresh. (i.e. `10 secs`). Default is `5 mins`.
|`Refresh Delay` | Duration of delay between each user and group refresh. (i.e. `10 secs`). Default is `5 mins`.
|`Exclude Groups` | Regular expression used to exclude groups. Default is '', which means no groups are excluded.
|`Exclude Users` | Regular expression used to exclude users. Default is '', which means no users are excluded.
|==================================================================================================================================================
Like LdapUserGroupProvider, the ShellUserGroupProvider is commented out in the _authorizers.xml_ file. Refer to that comment for usage examples.

View File

@ -175,15 +175,17 @@
on systems that support `sh`. Implementations available for Linux and Mac OS, and are selected by the
provider based on the system property `os.name`.
'Initial Refresh Delay' - duration to wait before first refresh. Default is '5 mins'.
'Refresh Delay' - duration to wait between subsequent refreshes. Default is '5 mins'.
'Exclude Groups' - regular expression used to exclude groups. Default is '', which means no groups are excluded.
'Exclude Users' - regular expression used to exclude users. Default is '', which means no users are excluded.
-->
<!-- To enable the shell-user-group-provider remove 2 lines. This is 1 of 2.
<userGroupProvider>
<identifier>shell-user-group-provider</identifier>
<class>org.apache.nifi.authorization.ShellUserGroupProvider</class>
<property name="Initial Refresh Delay">5 mins</property>
<property name="Refresh Delay">5 mins</property>
<property name="Exclude Groups"></property>
<property name="Exclude Users"></property>
</userGroupProvider>
To enable the shell-user-group-provider remove 2 lines. This is 2 of 2. -->

View File

@ -16,6 +16,15 @@
*/
package org.apache.nifi.authorization;
import org.apache.nifi.authorization.exception.AuthorizationAccessException;
import org.apache.nifi.authorization.exception.AuthorizerCreationException;
import org.apache.nifi.authorization.exception.AuthorizerDestructionException;
import org.apache.nifi.authorization.util.ShellRunner;
import org.apache.nifi.components.PropertyValue;
import org.apache.nifi.util.FormatUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
@ -26,21 +35,14 @@ import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import org.apache.nifi.authorization.exception.AuthorizationAccessException;
import org.apache.nifi.authorization.exception.AuthorizerCreationException;
import org.apache.nifi.authorization.exception.AuthorizerDestructionException;
import org.apache.nifi.authorization.util.ShellRunner;
import org.apache.nifi.components.PropertyValue;
import org.apache.nifi.util.FormatUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
/*
* ShellUserGroupProvider implements UserGroupProvider by way of shell commands.
*/
public class ShellUserGroupProvider implements UserGroupProvider {
private final static Logger logger = LoggerFactory.getLogger(ShellUserGroupProvider.class);
private final static String OS_TYPE_ERROR = "Unsupported operating system.";
@ -49,12 +51,15 @@ public class ShellUserGroupProvider implements UserGroupProvider {
private final static Map<String, User> usersByName = new HashMap<>(); // name == identity
private final static Map<String, Group> groupsById = new HashMap<>();
public static final String INITIAL_REFRESH_DELAY_PROPERTY = "Initial Refresh Delay";
public static final String REFRESH_DELAY_PROPERTY = "Refresh Delay";
private static final long MINIMUM_SYNC_INTERVAL_MILLISECONDS = 10_000;
private long initialDelay;
public static final String EXCLUDE_USER_PROPERTY = "Exclude Users";
public static final String EXCLUDE_GROUP_PROPERTY = "Exclude Groups";
private long fixedDelay;
private Pattern excludeUsers;
private Pattern excludeGroups;
// Our scheduler has one thread for users, one for groups:
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
@ -98,11 +103,6 @@ public class ShellUserGroupProvider implements UserGroupProvider {
user = usersById.get(identifier);
}
if (user == null) {
refreshOneUser(selectedShellCommands.getUserById(identifier), "Get Single User by Id");
user = usersById.get(identifier);
}
if (user == null) {
logger.debug("getUser (by id) user not found: " + identifier);
} else {
@ -235,7 +235,6 @@ public class ShellUserGroupProvider implements UserGroupProvider {
*/
@Override
public void onConfigured(AuthorizerConfigurationContext configurationContext) throws AuthorizerCreationException {
initialDelay = getDelayProperty(configurationContext, INITIAL_REFRESH_DELAY_PROPERTY, "5 mins");
fixedDelay = getDelayProperty(configurationContext, REFRESH_DELAY_PROPERTY, "5 mins");
// Our next init step is to select the command set based on the operating system name:
@ -255,11 +254,26 @@ public class ShellUserGroupProvider implements UserGroupProvider {
throw new AuthorizerCreationException(SYS_CHECK_ERROR, ioexc.getCause());
}
// The next step is to add the user and group exclude regexes:
try {
excludeGroups = Pattern.compile(getProperty(configurationContext, EXCLUDE_GROUP_PROPERTY, ""));
excludeUsers = Pattern.compile(getProperty(configurationContext, EXCLUDE_USER_PROPERTY, ""));
} catch (final PatternSyntaxException e) {
throw new AuthorizerCreationException(e);
}
// With our command set selected, and our system check passed, we can pull in the users and groups:
refreshUsersAndGroups();
// And finally, our last init step is to fire off the refresh thread:
scheduler.scheduleWithFixedDelay(this::refreshUsersAndGroups, initialDelay, fixedDelay, TimeUnit.SECONDS);
scheduler.scheduleWithFixedDelay(() -> {
try {
refreshUsersAndGroups();
}catch (final Throwable t) {
logger.error("", t);
}
}, fixedDelay, fixedDelay, TimeUnit.SECONDS);
}
private static ShellCommandsProvider getCommandsProviderFromName(String osName) {
@ -280,6 +294,19 @@ public class ShellUserGroupProvider implements UserGroupProvider {
return commands;
}
private String getProperty(AuthorizerConfigurationContext authContext, String propertyName, String defaultValue) {
final PropertyValue property = authContext.getProperty(propertyName);
final String value;
if (property != null && property.isSet()) {
value = property.getValue();
} else {
value = defaultValue;
}
return value;
}
private long getDelayProperty(AuthorizerConfigurationContext authContext, String propertyName, String defaultValue) {
final PropertyValue intervalProperty = authContext.getProperty(propertyName);
final String propertyValue;
@ -445,20 +472,21 @@ public class ShellUserGroupProvider implements UserGroupProvider {
if (record.length > 2) {
String name = record[0], id = record[1], gid = record[2];
if (name != null && id != null && !name.equals("") && !id.equals("")) {
User user = new User.Builder().identity(name).identifier(id).build();
idToUser.put(id, user);
if (name != null && id != null && !name.equals("") && !id.equals("") && !excludeUsers.matcher(name).matches()) {
User user = new User.Builder().identity(name).identifierGenerateFromSeed(id).build();
idToUser.put(user.getIdentifier(), user);
usernameToUser.put(name, user);
if (gid != null && !gid.equals("")) {
gidToUser.put(gid, user);
// create a temporary group to deterministically generate the group id and associate this user
Group group = new Group.Builder().name(gid).identifierGenerateFromSeed(gid).build();
gidToUser.put(group.getIdentifier(), user);
} else {
logger.warn("Null or empty primary group id for: " + name);
}
} else {
logger.warn("Null or empty user name: " + name + " or id: " + id);
logger.warn("Null, empty, or skipped user name: " + name + " or id: " + id);
}
} else {
logger.warn("Unexpected record format. Expected 3 or more colon separated values per line.");
@ -499,12 +527,12 @@ public class ShellUserGroupProvider implements UserGroupProvider {
logger.error("list membership shell exception: " + ioexc);
}
if (name != null && id != null && !name.equals("") && !id.equals("")) {
Group group = new Group.Builder().name(name).identifier(id).addUsers(users).build();
groupsById.put(id, group);
if (name != null && id != null && !name.equals("") && !id.equals("") && !excludeGroups.matcher(name).matches()) {
Group group = new Group.Builder().name(name).identifierGenerateFromSeed(id).addUsers(users).build();
groupsById.put(group.getIdentifier(), group);
logger.debug("Refreshed group: " + group);
} else {
logger.warn("Null or empty group name: " + name + " or id: " + id);
logger.warn("Null, empty, or skipped group name: " + name + " or id: " + id);
}
} else {
logger.warn("Unexpected record format. Expected 1 or more comma separated values.");
@ -525,26 +553,18 @@ public class ShellUserGroupProvider implements UserGroupProvider {
if (primaryGroup == null) {
logger.warn("user: " + primaryUser + " primary group not found");
} else {
} else if (!excludeGroups.matcher(primaryGroup.getName()).matches()) {
Set<String> groupUsers = primaryGroup.getUsers();
if (!groupUsers.contains(primaryUser.getIdentity())) {
Set<String> secondSet = new HashSet<>(groupUsers);
secondSet.add(primaryUser.getIdentity());
Group group = new Group.Builder().name(primaryGroup.getName()).identifier(primaryGid).addUsers(secondSet).build();
gidToGroup.put(primaryGid, group);
Group group = new Group.Builder().name(primaryGroup.getName()).identifierGenerateFromSeed(primaryGid).addUsers(secondSet).build();
gidToGroup.put(group.getIdentifier(), group);
}
}
});
}
/**
* @return The initial refresh delay.
*/
public long getInitialRefreshDelay() {
return initialDelay;
}
/**
* @return The fixed refresh delay.
*/

View File

@ -1,176 +0,0 @@
/*
* 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.authorization;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
abstract class ShellUserGroupProviderBase {
private static final Logger logger = LoggerFactory.getLogger(ShellUserGroupProviderBase.class);
private final String KNOWN_USER = "root";
private final String KNOWN_UID = "0";
@SuppressWarnings("FieldCanBeLocal")
private final String KNOWN_GROUP = "root";
@SuppressWarnings("FieldCanBeLocal")
private final String OTHER_GROUP = "wheel"; // e.g., macos
private final String KNOWN_GID = "0";
// We're using this knob to control the test runs on Travis. The issue there is that tests
// running on Travis do not have `getent`, thus not behaving like a typical Linux installation.
protected static boolean systemCheckFailed = false;
/**
* Ensures that the test can run because Docker is available and the remote instance can be reached via ssh.
*
* @return true if Docker is available on this OS
*/
protected boolean isSSHAvailable() {
return !systemCheckFailed;
}
/**
* Tests the provider behavior by getting its users and checking minimum size.
*
* @param provider {@link UserGroupProvider}
*/
void testGetUsersAndUsersMinimumCount(UserGroupProvider provider) {
assumeTrue(isSSHAvailable());
Set<User> users = provider.getUsers();
assertNotNull(users);
assertTrue(users.size() > 0);
}
/**
* Tests the provider behavior by getting a known user by uid.
*
* @param provider {@link UserGroupProvider}
*/
void testGetKnownUserByUsername(UserGroupProvider provider) {
// assumeTrue(isSSHAvailable());
User root = provider.getUser(KNOWN_UID);
assertNotNull(root);
assertEquals(KNOWN_USER, root.getIdentity());
assertEquals(KNOWN_UID, root.getIdentifier());
}
/**
* Tests the provider behavior by getting a known user by id.
*
* @param provider {@link UserGroupProvider}
*/
void testGetKnownUserByUid(UserGroupProvider provider) {
assumeTrue(isSSHAvailable());
User root = provider.getUserByIdentity(KNOWN_USER);
assertNotNull(root);
assertEquals(KNOWN_USER, root.getIdentity());
assertEquals(KNOWN_UID, root.getIdentifier());
}
/**
* Tests the provider behavior by getting its groups and checking minimum size.
*
* @param provider {@link UserGroupProvider}
*/
void testGetGroupsAndMinimumGroupCount(UserGroupProvider provider) {
assumeTrue(isSSHAvailable());
Set<Group> groups = provider.getGroups();
assertNotNull(groups);
assertTrue(groups.size() > 0);
}
/**
* Tests the provider behavior by getting a known group by GID.
*
* @param provider {@link UserGroupProvider}
*/
void testGetKnownGroupByGid(UserGroupProvider provider) {
assumeTrue(isSSHAvailable());
Group group = provider.getGroup(KNOWN_GID);
assertNotNull(group);
assertTrue(group.getName().equals(KNOWN_GROUP) || group.getName().equals(OTHER_GROUP));
assertEquals(KNOWN_GID, group.getIdentifier());
}
/**
* Tests the provider behavior by getting a known group and checking for a known member of it.
*
* @param provider {@link UserGroupProvider}
*/
void testGetGroupByGidAndGetGroupMembership(UserGroupProvider provider) {
assumeTrue(isSSHAvailable());
Group group = provider.getGroup(KNOWN_GID);
assertNotNull(group);
// These next few try/catch blocks are here for debugging. The user-to-group relationship
// is delicate with this implementation, and this approach allows us a measure of control.
// Check your logs if you're having problems!
try {
assertTrue(group.getUsers().size() > 0);
logger.info("root group count: " + group.getUsers().size());
} catch (final AssertionError ignored) {
logger.info("root group count zero on this system");
}
try {
assertTrue(group.getUsers().contains(KNOWN_USER));
logger.info("root group membership: " + group.getUsers());
} catch (final AssertionError ignored) {
logger.info("root group membership unexpected on this system");
}
}
/**
* Tests the provider behavior by getting a known user and checking its group membership.
*
* @param provider {@link UserGroupProvider}
*/
void testGetUserByIdentityAndGetGroupMembership(UserGroupProvider provider) {
assumeTrue(isSSHAvailable());
UserAndGroups user = provider.getUserAndGroups(KNOWN_USER);
assertNotNull(user);
try {
assertTrue(user.getGroups().size() > 0);
logger.info("root user group count: " + user.getGroups().size());
} catch (final AssertionError ignored) {
logger.info("root user and groups group count zero on this system");
}
Set<Group> groups = provider.getGroups();
assertTrue(groups.size() > user.getGroups().size());
}
}

View File

@ -20,6 +20,8 @@ import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import org.apache.nifi.authorization.exception.AuthorizerCreationException;
import org.apache.nifi.authorization.util.ShellRunner;
import org.apache.nifi.util.MockPropertyValue;
@ -29,6 +31,7 @@ import org.junit.Assume;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
@ -40,7 +43,7 @@ import org.testcontainers.containers.GenericContainer;
import org.testcontainers.utility.MountableFile;
public class ShellUserGroupProviderIT extends ShellUserGroupProviderBase {
public class ShellUserGroupProviderIT {
private static final Logger logger = LoggerFactory.getLogger(ShellUserGroupProviderIT.class);
// These images are publicly available on the hub.docker.com, and the source to each
@ -61,10 +64,24 @@ public class ShellUserGroupProviderIT extends ShellUserGroupProviderBase {
private final static String CONTAINER_SSH_AUTH_KEYS = "/root/.ssh/authorized_keys";
private final static Integer CONTAINER_SSH_PORT = 22;
private final String KNOWN_USER = "root";
private final String KNOWN_UID = "0";
@SuppressWarnings("FieldCanBeLocal")
private final String KNOWN_GROUP = "root";
@SuppressWarnings("FieldCanBeLocal")
private final String OTHER_GROUP = "wheel"; // e.g., macos
private final String KNOWN_GID = "0";
// We're using this knob to control the test runs on Travis. The issue there is that tests
// running on Travis do not have `getent`, thus not behaving like a typical Linux installation.
protected static boolean systemCheckFailed = false;
private static String sshPrivKeyFile;
private static String sshPubKeyFile;
private AuthorizerConfigurationContext authContext;
private AuthorizerConfigurationContext authContext = Mockito.mock(AuthorizerConfigurationContext.class);
private ShellUserGroupProvider localProvider;
private UserGroupProviderInitializationContext initContext;
@ -103,8 +120,9 @@ public class ShellUserGroupProviderIT extends ShellUserGroupProviderBase {
authContext = Mockito.mock(AuthorizerConfigurationContext.class);
initContext = Mockito.mock(UserGroupProviderInitializationContext.class);
Mockito.when(authContext.getProperty(Mockito.eq(ShellUserGroupProvider.INITIAL_REFRESH_DELAY_PROPERTY))).thenReturn(new MockPropertyValue("10 sec"));
Mockito.when(authContext.getProperty(Mockito.eq(ShellUserGroupProvider.REFRESH_DELAY_PROPERTY))).thenReturn(new MockPropertyValue("15 sec"));
Mockito.when(authContext.getProperty(Mockito.eq(ShellUserGroupProvider.REFRESH_DELAY_PROPERTY))).thenReturn(new MockPropertyValue("10 sec"));
Mockito.when(authContext.getProperty(Mockito.eq(ShellUserGroupProvider.EXCLUDE_GROUP_PROPERTY))).thenReturn(new MockPropertyValue(".*d$"));
Mockito.when(authContext.getProperty(Mockito.eq(ShellUserGroupProvider.EXCLUDE_USER_PROPERTY))).thenReturn(new MockPropertyValue("^s.*"));
localProvider = new ShellUserGroupProvider();
try {
@ -115,8 +133,7 @@ public class ShellUserGroupProviderIT extends ShellUserGroupProviderBase {
logger.error("setup() exception: " + exc + "; tests cannot run on this system.");
return;
}
Assert.assertEquals(10000, localProvider.getInitialRefreshDelay());
Assert.assertEquals(15000, localProvider.getRefreshDelay());
Assert.assertEquals(10000, localProvider.getRefreshDelay());
}
@After
@ -124,45 +141,6 @@ public class ShellUserGroupProviderIT extends ShellUserGroupProviderBase {
localProvider.preDestruction();
}
// Our primary test methods all accept a provider; here we define overloads to those methods to
// use the local provider. This allows the reuse of those test methods with the remote provider.
@Test
public void testGetUsersAndUsersMinimumCount() {
testGetUsersAndUsersMinimumCount(localProvider);
}
@Test
public void testGetKnownUserByUsername() {
testGetKnownUserByUsername(localProvider);
}
@Test
public void testGetKnownUserByUid() {
testGetKnownUserByUid(localProvider);
}
@Test
public void testGetGroupsAndMinimumGroupCount() {
testGetGroupsAndMinimumGroupCount(localProvider);
}
@Test
public void testGetKnownGroupByGid() {
testGetKnownGroupByGid(localProvider);
}
@Test
public void testGetGroupByGidAndGetGroupMembership() {
testGetGroupByGidAndGetGroupMembership(localProvider);
}
@Test
public void testGetUserByIdentityAndGetGroupMembership() {
testGetUserByIdentityAndGetGroupMembership(localProvider);
}
@SuppressWarnings("RedundantThrows")
private GenericContainer createContainer(String image) throws IOException, InterruptedException {
GenericContainer container = new GenericContainer(image)
.withEnv("SSH_ENABLE_ROOT", "true").withExposedPorts(CONTAINER_SSH_PORT);
@ -192,22 +170,59 @@ public class ShellUserGroupProviderIT extends ShellUserGroupProviderBase {
public void testTooShortDelayIntervalThrowsException() throws AuthorizerCreationException {
final AuthorizerConfigurationContext authContext = Mockito.mock(AuthorizerConfigurationContext.class);
final ShellUserGroupProvider localProvider = new ShellUserGroupProvider();
Mockito.when(authContext.getProperty(Mockito.eq(ShellUserGroupProvider.INITIAL_REFRESH_DELAY_PROPERTY))).thenReturn(new MockPropertyValue("1 milliseconds"));
Mockito.when(authContext.getProperty(Mockito.eq(ShellUserGroupProvider.REFRESH_DELAY_PROPERTY))).thenReturn(new MockPropertyValue("1 milliseconds"));
expectedException.expect(AuthorizerCreationException.class);
expectedException.expectMessage("The Initial Refresh Delay '1 milliseconds' is below the minimum value of '10000 ms'");
expectedException.expectMessage("The Refresh Delay '1 milliseconds' is below the minimum value of '10000 ms'");
localProvider.onConfigured(authContext);
}
@Test
public void testInvalidGroupExcludeExpressionsThrowsException() throws AuthorizerCreationException {
AuthorizerConfigurationContext authContext = Mockito.mock(AuthorizerConfigurationContext.class);
ShellUserGroupProvider localProvider = new ShellUserGroupProvider();
Mockito.when(authContext.getProperty(Mockito.eq(ShellUserGroupProvider.REFRESH_DELAY_PROPERTY))).thenReturn(new MockPropertyValue("3 minutes"));
Mockito.when(authContext.getProperty(Mockito.eq(ShellUserGroupProvider.EXCLUDE_GROUP_PROPERTY))).thenReturn(new MockPropertyValue("(3"));
expectedException.expect(AuthorizerCreationException.class);
expectedException.expectMessage("Unclosed group near index");
localProvider.onConfigured(authContext);
}
@Test
public void testInvalidUserExcludeExpressionsThrowsException() throws AuthorizerCreationException {
AuthorizerConfigurationContext authContext = Mockito.mock(AuthorizerConfigurationContext.class);
ShellUserGroupProvider localProvider = new ShellUserGroupProvider();
Mockito.when(authContext.getProperty(Mockito.eq(ShellUserGroupProvider.REFRESH_DELAY_PROPERTY))).thenReturn(new MockPropertyValue("3 minutes"));
Mockito.when(authContext.getProperty(Mockito.eq(ShellUserGroupProvider.EXCLUDE_USER_PROPERTY))).thenReturn(new MockPropertyValue("*"));
expectedException.expect(AuthorizerCreationException.class);
expectedException.expectMessage("Dangling meta character");
localProvider.onConfigured(authContext);
}
@Test
public void testMissingExcludeExpressionsAllowed() throws AuthorizerCreationException {
AuthorizerConfigurationContext authContext = Mockito.mock(AuthorizerConfigurationContext.class);
ShellUserGroupProvider localProvider = new ShellUserGroupProvider();
Mockito.when(authContext.getProperty(Mockito.eq(ShellUserGroupProvider.REFRESH_DELAY_PROPERTY))).thenReturn(new MockPropertyValue("3 minutes"));
localProvider.onConfigured(authContext);
verifyUsersAndUsersMinimumCount(localProvider);
}
@Test
public void testInvalidDelayIntervalThrowsException() throws AuthorizerCreationException {
final AuthorizerConfigurationContext authContext = Mockito.mock(AuthorizerConfigurationContext.class);
final ShellUserGroupProvider localProvider = new ShellUserGroupProvider();
Mockito.when(authContext.getProperty(Mockito.eq(ShellUserGroupProvider.INITIAL_REFRESH_DELAY_PROPERTY))).thenReturn(new MockPropertyValue("Not an interval"));
Mockito.when(authContext.getProperty(Mockito.eq(ShellUserGroupProvider.REFRESH_DELAY_PROPERTY))).thenReturn(new MockPropertyValue("Not an interval"));
expectedException.expect(AuthorizerCreationException.class);
expectedException.expectMessage("The Initial Refresh Delay 'Not an interval' is not a valid time interval.");
expectedException.expectMessage("The Refresh Delay 'Not an interval' is not a valid time interval.");
localProvider.onConfigured(authContext);
}
@ -220,23 +235,26 @@ public class ShellUserGroupProviderIT extends ShellUserGroupProviderBase {
}
@Test
public void testGetOneUserAfterClearingCaches() {
// assert known state: empty, testable, not empty
localProvider.clearCaches();
testGetKnownUserByUid(localProvider);
assert localProvider.userCacheSize() > 0;
public void testLocalGetUsersAndUsersMinimumCount() {
verifyUsersAndUsersMinimumCount(localProvider);
}
@Test
public void testGetOneGroupAfterClearingCaches() {
Assume.assumeTrue(isSSHAvailable());
// assert known state: empty, testable, not empty
localProvider.clearCaches();
testGetKnownGroupByGid(localProvider);
assert localProvider.groupCacheSize() > 0;
public void testLocalGetKnownUserByUsername() {
verifyKnownUserByUsername(localProvider);
}
@Test
public void testLocalGetGroupsAndMinimumGroupCount() {
verifyGroupsAndMinimumGroupCount(localProvider);
}
@Test
public void testLocalGetUserByIdentityAndGetGroupMembership() {
verifyGetUserByIdentityAndGetGroupMembership(localProvider);
}
// @Ignore // for now
@Test
public void testVariousSystemImages() {
// Here we explicitly clear the system check flag to allow the remote checks that follow:
@ -262,12 +280,10 @@ public class ShellUserGroupProviderIT extends ShellUserGroupProviderBase {
}
try {
testGetUsersAndUsersMinimumCount(remoteProvider);
testGetKnownUserByUsername(remoteProvider);
testGetGroupsAndMinimumGroupCount(remoteProvider);
testGetKnownGroupByGid(remoteProvider);
testGetGroupByGidAndGetGroupMembership(remoteProvider);
testGetUserByIdentityAndGetGroupMembership(remoteProvider);
verifyUsersAndUsersMinimumCount(remoteProvider);
verifyKnownUserByUsername(remoteProvider);
verifyGroupsAndMinimumGroupCount(remoteProvider);
verifyGetUserByIdentityAndGetGroupMembership(remoteProvider);
} catch (final Exception e) {
// Some environments don't allow our tests to work.
logger.error("Exception running remote provider on image: " + image + ", exception: " + e);
@ -280,4 +296,90 @@ public class ShellUserGroupProviderIT extends ShellUserGroupProviderBase {
}
// TODO: Make test which retrieves list of users and then getUserByIdentity to ensure the user is populated in the response
/**
* Ensures that the test can run because Docker is available and the remote instance can be reached via ssh.
*
* @return true if Docker is available on this OS
*/
private boolean isSSHAvailable() {
return !systemCheckFailed;
}
/**
* Tests the provider behavior by getting its users and checking minimum size.
*
* @param provider {@link UserGroupProvider}
*/
private void verifyUsersAndUsersMinimumCount(UserGroupProvider provider) {
Assume.assumeTrue(isSSHAvailable());
Set<User> users = provider.getUsers();
// This shows that we don't have any users matching the exclude regex, which is likely because those users
// exist but were excluded:
for (User user : users) {
Assert.assertFalse(user.getIdentifier().startsWith("s"));
}
Assert.assertNotNull(users);
Assert.assertTrue(users.size() > 0);
}
/**
* Tests the provider behavior by getting a known user by id.
*
* @param provider {@link UserGroupProvider}
*/
private void verifyKnownUserByUsername(UserGroupProvider provider) {
Assume.assumeTrue(isSSHAvailable());
User root = provider.getUserByIdentity(KNOWN_USER);
Assert.assertNotNull(root);
Assert.assertEquals(KNOWN_USER, root.getIdentity());
}
/**
* Tests the provider behavior by getting its groups and checking minimum size.
*
* @param provider {@link UserGroupProvider}
*/
private void verifyGroupsAndMinimumGroupCount(UserGroupProvider provider) {
Assume.assumeTrue(isSSHAvailable());
Set<Group> groups = provider.getGroups();
// This shows that we don't have any groups matching the exclude regex, which is likely because those groups
// exist but were excluded:
for (Group group : groups) {
Assert.assertFalse(group.getName().endsWith("d"));
}
Assert.assertNotNull(groups);
Assert.assertTrue(groups.size() > 0);
}
/**
* Tests the provider behavior by getting a known user and checking its group membership.
*
* @param provider {@link UserGroupProvider}
*/
private void verifyGetUserByIdentityAndGetGroupMembership(UserGroupProvider provider) {
Assume.assumeTrue(isSSHAvailable());
UserAndGroups user = provider.getUserAndGroups(KNOWN_USER);
Assert.assertNotNull(user);
try {
Assert.assertTrue(user.getGroups().size() > 0);
logger.info("root user group count: " + user.getGroups().size());
} catch (final AssertionError ignored) {
logger.info("root user and groups group count zero on this system");
}
Set<Group> groups = provider.getGroups();
Assert.assertTrue(groups.size() > user.getGroups().size());
}
}