blob: 7f522ff0f32aef2bb0594bec60605e6d2f839f7c [file] [log] [blame]
/*
* 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.authorization.database;
import org.apache.nifi.registry.db.DatabaseBaseTest;
import org.apache.nifi.registry.properties.NiFiRegistryProperties;
import org.apache.nifi.registry.security.authorization.AbstractConfigurableAccessPolicyProvider;
import org.apache.nifi.registry.security.authorization.AccessPolicy;
import org.apache.nifi.registry.security.authorization.AuthorizerConfigurationContext;
import org.apache.nifi.registry.security.authorization.AuthorizerInitializationContext;
import org.apache.nifi.registry.security.authorization.ConfigurableAccessPolicyProvider;
import org.apache.nifi.registry.security.authorization.Group;
import org.apache.nifi.registry.security.authorization.RequestAction;
import org.apache.nifi.registry.security.authorization.User;
import org.apache.nifi.registry.security.authorization.UserGroupProvider;
import org.apache.nifi.registry.security.authorization.UserGroupProviderLookup;
import org.apache.nifi.registry.security.authorization.resource.ResourceFactory;
import org.apache.nifi.registry.security.authorization.util.AccessPolicyProviderUtils;
import org.apache.nifi.registry.security.exception.SecurityProviderCreationException;
import org.apache.nifi.registry.security.identity.DefaultIdentityMapper;
import org.apache.nifi.registry.security.identity.IdentityMapper;
import org.apache.nifi.registry.util.StandardPropertyValue;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import javax.sql.DataSource;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class TestDatabaseAccessPolicyProvider extends DatabaseBaseTest {
private static final String UGP_IDENTIFIER = "mock-user-group-provider";
private static final User ADMIN_USER = new User.Builder().identifierGenerateRandom().identity("admin").build();
private static final User ADMIN_USER2 = new User.Builder().identifierGenerateRandom().identity("admin2").build();
private static final User NIFI1_USER = new User.Builder().identifierGenerateRandom().identity("nifi1").build();
private static final User NIFI2_USER = new User.Builder().identifierGenerateRandom().identity("nifi2").build();
private static final User NIFI3_USER = new User.Builder().identifierGenerateRandom().identity("nifi3").build();
private static final Set<String> NIFI_IDENTITIES = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
NIFI1_USER.getIdentity(), NIFI2_USER.getIdentity(), NIFI3_USER.getIdentity())));
private static final Group NIFI_GROUP = new Group.Builder().identifierGenerateRandom().name("nifi-nodes").build();
private static final Group OTHERS_GROUP = new Group.Builder().identifierGenerateRandom().name("other-members").build();
@Autowired
private DataSource dataSource;
private JdbcTemplate jdbcTemplate;
private NiFiRegistryProperties properties;
private IdentityMapper identityMapper;
private UserGroupProviderLookup userGroupProviderLookup;
private UserGroupProvider userGroupProvider;
// Class under test
private ConfigurableAccessPolicyProvider policyProvider;
@Before
public void setup() {
properties = new NiFiRegistryProperties();
identityMapper = new DefaultIdentityMapper(properties);
jdbcTemplate = new JdbcTemplate(dataSource);
userGroupProvider = mock(UserGroupProvider.class);
when(userGroupProvider.getUserByIdentity(ADMIN_USER.getIdentity())).thenReturn(ADMIN_USER);
when(userGroupProvider.getUserByIdentity(ADMIN_USER2.getIdentity())).thenReturn(ADMIN_USER2);
when(userGroupProvider.getUserByIdentity(NIFI1_USER.getIdentity())).thenReturn(NIFI1_USER);
when(userGroupProvider.getUserByIdentity(NIFI2_USER.getIdentity())).thenReturn(NIFI2_USER);
when(userGroupProvider.getUserByIdentity(NIFI3_USER.getIdentity())).thenReturn(NIFI3_USER);
when(userGroupProvider.getGroups()).thenReturn(Collections.unmodifiableSet(
new HashSet<>(Arrays.asList(OTHERS_GROUP, NIFI_GROUP))));
userGroupProviderLookup = mock(UserGroupProviderLookup.class);
when(userGroupProviderLookup.getUserGroupProvider(UGP_IDENTIFIER)).thenReturn(userGroupProvider);
final AuthorizerInitializationContext initializationContext = mock(AuthorizerInitializationContext.class);
when(initializationContext.getUserGroupProviderLookup()).thenReturn(userGroupProviderLookup);
final DatabaseAccessPolicyProvider databaseProvider = new DatabaseAccessPolicyProvider();
databaseProvider.setDataSource(dataSource);
databaseProvider.setIdentityMapper(identityMapper);
databaseProvider.initialize(initializationContext);
policyProvider = databaseProvider;
}
private void configure(final String initialAdmin, final Set<String> nifiIdentifies) {
configure(initialAdmin, nifiIdentifies, null);
}
/**
* Helper method to call onConfigured with a configuration context.
*
* @param initialAdmin the initial admin identity to put in the context, or null
* @param nifiIdentifies the nifi identities to put in the context, or null
* @param nifiGroupName the name of the nifi group
*/
private void configure(final String initialAdmin, final Set<String> nifiIdentifies, final String nifiGroupName) {
final Map<String,String> properties = new HashMap<>();
properties.put(AbstractConfigurableAccessPolicyProvider.PROP_USER_GROUP_PROVIDER, UGP_IDENTIFIER);
if (initialAdmin != null) {
properties.put(AccessPolicyProviderUtils.PROP_INITIAL_ADMIN_IDENTITY, initialAdmin);
}
if (nifiIdentifies != null) {
int i = 1;
for (final String nifiIdentity : nifiIdentifies) {
properties.put(AccessPolicyProviderUtils.PROP_NIFI_IDENTITY_PREFIX + i++, nifiIdentity);
}
}
if (nifiGroupName != null) {
properties.put(AccessPolicyProviderUtils.PROP_NIFI_GROUP_NAME, nifiGroupName);
}
final AuthorizerConfigurationContext configurationContext = mock(AuthorizerConfigurationContext.class);
when(configurationContext.getProperties()).thenReturn(properties);
when(configurationContext.getProperty(AbstractConfigurableAccessPolicyProvider.PROP_USER_GROUP_PROVIDER))
.thenReturn(new StandardPropertyValue(UGP_IDENTIFIER));
when(configurationContext.getProperty(AccessPolicyProviderUtils.PROP_INITIAL_ADMIN_IDENTITY))
.thenReturn(new StandardPropertyValue(initialAdmin));
when(configurationContext.getProperty(AccessPolicyProviderUtils.PROP_NIFI_GROUP_NAME))
.thenReturn(new StandardPropertyValue(nifiGroupName));
policyProvider.onConfigured(configurationContext);
}
private void configure() {
configure(null, null, null);
}
// -- Helper methods for accessing the DB outside of the provider
private int getPolicyCount() {
return jdbcTemplate.queryForObject("SELECT COUNT(*) FROM APP_POLICY", Integer.class);
}
private void createPolicy(final String identifier, final String resource, final RequestAction action) {
final String policySql = "INSERT INTO APP_POLICY(IDENTIFIER, RESOURCE, ACTION) VALUES (?, ?, ?)";
final int rowsUpdated = jdbcTemplate.update(policySql, identifier, resource, action.toString());
assertEquals(1, rowsUpdated);
}
private void addUserToPolicy(final String policyIdentifier, final String userIdentifier) {
final String policyUserSql = "INSERT INTO APP_POLICY_USER(POLICY_IDENTIFIER, USER_IDENTIFIER) VALUES (?, ?)";
final int rowsUpdated = jdbcTemplate.update(policyUserSql, policyIdentifier, userIdentifier);
assertEquals(1, rowsUpdated);
}
private void addGroupToPolicy(final String policyIdentifier, final String groupIdentifier) {
final String policyGroupSql = "INSERT INTO APP_POLICY_GROUP(POLICY_IDENTIFIER, GROUP_IDENTIFIER) VALUES (?, ?)";
final int rowsUpdated = jdbcTemplate.update(policyGroupSql, policyIdentifier, groupIdentifier);
assertEquals(1, rowsUpdated);
}
// -- Test onConfigured
@Test
public void testOnConfiguredCreatesInitialPolicies() {
// verify no policies in DB
assertEquals(0, getPolicyCount());
configure(ADMIN_USER.getIdentity(), NIFI_IDENTITIES, NIFI_GROUP.getName());
// verify policies got created for admin and NiFi identities
final Set<AccessPolicy> policies = policyProvider.getAccessPolicies();
assertNotNull(policies);
assertEquals(18, policies.size());
}
@Test
public void testOnConfiguredWhenOnlyInitialAdmin() {
// verify no policies in DB
assertEquals(0, getPolicyCount());
configure(ADMIN_USER.getIdentity(), null);
// verify policies got created for admin and NiFi identities
final Set<AccessPolicy> policies = policyProvider.getAccessPolicies();
assertNotNull(policies);
assertEquals(18, policies.size());
// verify each policy only has the initial admin
policies.forEach(p -> {
assertNotNull(p.getUsers());
assertEquals(1, p.getUsers().size());
assertTrue(p.getUsers().contains(ADMIN_USER.getIdentifier()));
});
}
@Test
public void testOnConfiguredWhenOnlyInitialNiFiIdentities() {
// verify no policies in DB
assertEquals(0, getPolicyCount());
configure(null, NIFI_IDENTITIES);
// verify policies got created for admin and NiFi identities
final Set<AccessPolicy> policies = policyProvider.getAccessPolicies();
assertNotNull(policies);
assertEquals(4, policies.size());
// verify each policy only has the initial admin
policies.forEach(p -> {
assertNotNull(p.getUsers());
assertEquals(3, p.getUsers().size());
assertFalse(p.getUsers().contains(ADMIN_USER.getIdentifier()));
});
}
@Test
public void testOnConfiguredWhenOnlyNiFiGroupName() {
// verify no policies in DB
assertEquals(0, getPolicyCount());
configure(null, null, NIFI_GROUP.getName());
// verify policies got created for admin and NiFi identities
final Set<AccessPolicy> policies = policyProvider.getAccessPolicies();
assertNotNull(policies);
assertEquals(4, policies.size());
// verify each policy only has the initial admin
policies.forEach(p -> {
assertNotNull(p.getGroups());
assertEquals(1, p.getGroups().size());
assertTrue(p.getGroups().contains(NIFI_GROUP.getIdentifier()));
});
}
@Test
public void testOnConfiguredStillCreatesInitialPoliciesWhenPoliciesAlreadyExist() {
// create one policy
createPolicy("policy1", "/foo", RequestAction.READ);
assertEquals(1, getPolicyCount());
configure(ADMIN_USER.getIdentity(), NIFI_IDENTITIES);
assertTrue(getPolicyCount() > 1);
}
@Test
public void testOnConfiguredWhenChangingInitialAdmin() {
configure(ADMIN_USER.getIdentity(), NIFI_IDENTITIES);
configure(ADMIN_USER.getIdentity(), NIFI_IDENTITIES);
configure("admin2", NIFI_IDENTITIES);
final AccessPolicy readBuckets = policyProvider.getAccessPolicy(
ResourceFactory.getBucketsResource().getIdentifier(), RequestAction.READ);
assertNotNull(readBuckets);
assertEquals(5, readBuckets.getUsers().size());
assertTrue(readBuckets.getUsers().contains(ADMIN_USER.getIdentifier()));
assertTrue(readBuckets.getUsers().contains(ADMIN_USER2.getIdentifier()));
assertTrue(readBuckets.getUsers().contains(NIFI1_USER.getIdentifier()));
assertTrue(readBuckets.getUsers().contains(NIFI2_USER.getIdentifier()));
assertTrue(readBuckets.getUsers().contains(NIFI3_USER.getIdentifier()));
}
@Test
public void testOnConfiguredAppliesIdentityMappings() {
// Set up an identity mapping for kerberos principals
properties.setProperty("nifi.registry.security.identity.mapping.pattern.kerb", "^(.*?)@(.*?)$");
properties.setProperty("nifi.registry.security.identity.mapping.value.kerb", "$1");
identityMapper = new DefaultIdentityMapper(properties);
((DatabaseAccessPolicyProvider)policyProvider).setIdentityMapper(identityMapper);
// Call configure with full admin identity, should get mapped to just 'admin' before looking up user
configure("admin@HDF.COM", null);
// verify policies got created for admin and NiFi identities
final Set<AccessPolicy> policies = policyProvider.getAccessPolicies();
assertNotNull(policies);
assertEquals(18, policies.size());
// verify each policy only has the initial admin
policies.forEach(p -> {
assertNotNull(p.getUsers());
assertEquals(1, p.getUsers().size());
assertTrue(p.getUsers().contains(ADMIN_USER.getIdentifier()));
});
}
@Test
public void testOnConfiguredAppliesGroupMappings() {
// Set up an identity mapping for kerberos principals
properties.setProperty("nifi.registry.security.group.mapping.pattern.anyGroup", "^(.*)$");
properties.setProperty("nifi.registry.security.group.mapping.value.anyGroup", "$1");
properties.setProperty("nifi.registry.security.group.mapping.transform.anyGroup", "LOWER");
identityMapper = new DefaultIdentityMapper(properties);
((DatabaseAccessPolicyProvider)policyProvider).setIdentityMapper(identityMapper);
// Call configure with NiFi Group in all uppercase, should get mapped to lower case
configure(null, null, NIFI_GROUP.getName().toUpperCase());
// verify policies got created for admin and NiFi identities
final Set<AccessPolicy> policies = policyProvider.getAccessPolicies();
assertNotNull(policies);
assertEquals(4, policies.size());
// verify each policy only has the initial admin
policies.forEach(p -> {
assertNotNull(p.getGroups());
assertEquals(1, p.getGroups().size());
assertTrue(p.getGroups().contains(NIFI_GROUP.getIdentifier()));
});
}
@Test(expected = SecurityProviderCreationException.class)
public void testOnConfiguredWhenInitialAdminNotFound() {
configure("does-not-exist", null);
}
@Test(expected = SecurityProviderCreationException.class)
public void testOnConfiguredWhenNiFiIdentityNotFound() {
configure(null, Collections.singleton("does-not-exist"));
}
// -- Test AccessPolicy methods
@Test
public void testAddAccessPolicy() {
configure();
assertEquals(0, getPolicyCount());
final AccessPolicy policy = new AccessPolicy.Builder()
.identifierGenerateRandom()
.resource("/buckets")
.action(RequestAction.READ)
.addUser("user1")
.addGroup("group1")
.build();
final AccessPolicy createdPolicy = policyProvider.addAccessPolicy(policy);
assertNotNull(createdPolicy);
final AccessPolicy retrievedPolicy = policyProvider.getAccessPolicy(policy.getIdentifier());
verifyPoliciesEqual(policy, retrievedPolicy);
}
@Test
public void testGetPolicyByIdentifierWhenExists() {
configure(ADMIN_USER.getIdentity(), NIFI_IDENTITIES);
final Set<AccessPolicy> policies = policyProvider.getAccessPolicies();
assertNotNull(policies);
assertTrue(policies.size() > 0);
final AccessPolicy existingPolicy = policies.stream().findFirst().get();
final AccessPolicy retrievedPolicy = policyProvider.getAccessPolicy(existingPolicy.getIdentifier());
verifyPoliciesEqual(existingPolicy, retrievedPolicy);
}
@Test
public void testGetPolicyByIdentifierWhenDoesNotExist() {
configure(ADMIN_USER.getIdentity(), NIFI_IDENTITIES);
final AccessPolicy retrievedPolicy = policyProvider.getAccessPolicy("does-not-exist");
assertNull(retrievedPolicy);
}
@Test
public void testGetPolicyByResourceAndActionWhenExists() {
configure(ADMIN_USER.getIdentity(), NIFI_IDENTITIES);
final Set<AccessPolicy> policies = policyProvider.getAccessPolicies();
assertNotNull(policies);
assertTrue(policies.size() > 0);
final AccessPolicy existingPolicy = policies.stream().findFirst().get();
final AccessPolicy retrievedPolicy = policyProvider.getAccessPolicy(existingPolicy.getResource(), existingPolicy.getAction());
verifyPoliciesEqual(existingPolicy, retrievedPolicy);
}
@Test
public void testGetPolicyByResourceAndActionWhenDoesNotExist() {
configure(ADMIN_USER.getIdentity(), NIFI_IDENTITIES);
final AccessPolicy retrievedPolicy = policyProvider.getAccessPolicy("does-not-exist", RequestAction.READ);
assertNull(retrievedPolicy);
}
@Test
public void testUpdatePolicyWhenExists() {
configure(ADMIN_USER.getIdentity(), NIFI_IDENTITIES);
final AccessPolicy readBucketsPolicy = policyProvider.getAccessPolicy(
ResourceFactory.getBucketsResource().getIdentifier(), RequestAction.READ);
assertNotNull(readBucketsPolicy);
assertEquals(4, readBucketsPolicy.getUsers().size());
assertEquals(0, readBucketsPolicy.getGroups().size());
final AccessPolicy updatedPolicy = new AccessPolicy.Builder(readBucketsPolicy)
.addUser("user1")
.addGroup("group1")
.build();
final AccessPolicy returnedPolicy = policyProvider.updateAccessPolicy(updatedPolicy);
assertNotNull(returnedPolicy);
final AccessPolicy retrievedPolicy = policyProvider.getAccessPolicy(readBucketsPolicy.getIdentifier());
verifyPoliciesEqual(updatedPolicy, retrievedPolicy);
}
@Test
public void testUpdatePolicyWhenDoesNotExist() {
configure(ADMIN_USER.getIdentity(), NIFI_IDENTITIES);
final AccessPolicy policy = new AccessPolicy.Builder()
.identifierGenerateRandom()
.resource("/foo")
.action(RequestAction.READ)
.addUser("user1")
.addGroup("group1")
.build();
final AccessPolicy returnedPolicy = policyProvider.updateAccessPolicy(policy);
assertNull(returnedPolicy);
final AccessPolicy retrievedPolicy = policyProvider.getAccessPolicy(policy.getIdentifier());
assertNull(retrievedPolicy);
}
@Test
public void testDeletePolicyWhenExists() {
configure(ADMIN_USER.getIdentity(), NIFI_IDENTITIES);
final AccessPolicy readBucketsPolicy = policyProvider.getAccessPolicy(
ResourceFactory.getBucketsResource().getIdentifier(), RequestAction.READ);
assertNotNull(readBucketsPolicy);
assertEquals(4, readBucketsPolicy.getUsers().size());
assertEquals(0, readBucketsPolicy.getGroups().size());
final AccessPolicy deletedPolicy = policyProvider.deleteAccessPolicy(readBucketsPolicy);
assertNotNull(deletedPolicy);
final AccessPolicy retrievedPolicy = policyProvider.getAccessPolicy(readBucketsPolicy.getIdentifier());
assertNull(retrievedPolicy);
}
@Test
public void testDeletePolicyWhenDoesNotExist() {
configure(ADMIN_USER.getIdentity(), NIFI_IDENTITIES);
final AccessPolicy policy = new AccessPolicy.Builder()
.identifierGenerateRandom()
.resource("/foo")
.action(RequestAction.READ)
.addUser("user1")
.addGroup("group1")
.build();
final AccessPolicy deletedPolicy = policyProvider.deleteAccessPolicy(policy);
assertNull(deletedPolicy);
}
private void verifyPoliciesEqual(final AccessPolicy policy1, final AccessPolicy policy2) {
assertNotNull(policy1);
assertNotNull(policy2);
assertEquals(policy1.getIdentifier(), policy2.getIdentifier());
assertEquals(policy1.getResource(), policy2.getResource());
assertEquals(policy1.getAction(), policy2.getAction());
assertEquals(policy1.getUsers().size(), policy2.getUsers().size());
assertEquals(policy1.getGroups().size(), policy2.getGroups().size());
}
}