org.apache.nifi.registry.web.api.SecureLdapIT.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.nifi.registry.web.api.SecureLdapIT.java

Source

/*
 * 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.web.api;

import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.registry.SecureLdapTestApiApplication;
import org.apache.nifi.registry.authorization.AccessPolicy;
import org.apache.nifi.registry.authorization.AccessPolicySummary;
import org.apache.nifi.registry.authorization.CurrentUser;
import org.apache.nifi.registry.authorization.Permissions;
import org.apache.nifi.registry.authorization.Tenant;
import org.apache.nifi.registry.bucket.Bucket;
import org.apache.nifi.registry.extension.ExtensionManager;
import org.apache.nifi.registry.properties.AESSensitivePropertyProvider;
import org.apache.nifi.registry.properties.NiFiRegistryProperties;
import org.apache.nifi.registry.properties.SensitivePropertyProvider;
import org.apache.nifi.registry.security.authorization.Authorizer;
import org.apache.nifi.registry.security.authorization.AuthorizerFactory;
import org.apache.nifi.registry.security.crypto.BootstrapFileCryptoKeyProvider;
import org.apache.nifi.registry.security.crypto.CryptoKeyProvider;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.skyscreamer.jsonassert.JSONAssert;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Profile;
import org.springframework.test.context.jdbc.Sql;
import org.springframework.test.context.junit4.SpringRunner;

import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Form;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

/**
 * Deploy the Web API Application using an embedded Jetty Server for local integration testing, with the follow characteristics:
 *
 * - A NiFiRegistryProperties has to be explicitly provided to the ApplicationContext using a profile unique to this test suite.
 * - A NiFiRegistryClientConfig has been configured to create a client capable of completing one-way TLS
 * - The database is embed H2 using volatile (in-memory) persistence
 * - Custom SQL is clearing the DB before each test method by default, unless method overrides this behavior
 */
@RunWith(SpringRunner.class)
@SpringBootTest(classes = SecureLdapTestApiApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = "spring.profiles.include=ITSecureLdap")
@Import(SecureITClientConfiguration.class)
@Sql(executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD, scripts = "classpath:db/clearDB.sql")
public class SecureLdapIT extends IntegrationTestBase {

    private static final String tokenLoginPath = "access/token/login";
    private static final String tokenIdentityProviderPath = "access/token/identity-provider";

    @TestConfiguration
    @Profile("ITSecureLdap")
    public static class LdapTestConfiguration {

        static AuthorizerFactory authorizerFactory;

        @Primary
        @Bean
        @DependsOn({ "directoryServer" }) // Can't load LdapUserGroupProvider until the embedded LDAP server, which creates the "directoryServer" bean, is running
        public static Authorizer getAuthorizer(@Autowired NiFiRegistryProperties properties,
                ExtensionManager extensionManager) throws Exception {
            if (authorizerFactory == null) {
                authorizerFactory = new AuthorizerFactory(properties, extensionManager,
                        sensitivePropertyProvider());
            }
            return authorizerFactory.getAuthorizer();
        }

        @Primary
        @Bean
        public static SensitivePropertyProvider sensitivePropertyProvider() throws Exception {
            return new AESSensitivePropertyProvider(getNiFiRegistryMasterKeyProvider().getKey());
        }

        private static CryptoKeyProvider getNiFiRegistryMasterKeyProvider() {
            return new BootstrapFileCryptoKeyProvider("src/test/resources/conf/secure-ldap/bootstrap.conf");
        }

    }

    private String adminAuthToken;
    private List<AccessPolicy> beforeTestAccessPoliciesSnapshot;

    @Before
    public void setup() {
        final String basicAuthCredentials = encodeCredentialsForBasicAuth("nifiadmin", "password");
        final String token = client.target(createURL(tokenIdentityProviderPath)).request()
                .header("Authorization", "Basic " + basicAuthCredentials).post(null, String.class);
        adminAuthToken = token;

        beforeTestAccessPoliciesSnapshot = createAccessPoliciesSnapshot();
    }

    @After
    public void cleanup() {
        restoreAccessPoliciesSnapshot(beforeTestAccessPoliciesSnapshot);
    }

    @Test
    public void testTokenGenerationAndAccessStatus() throws Exception {

        // Note: this test intentionally does not use the token generated
        // for nifiadmin by the @Before method

        // Given: the client and server have been configured correctly for LDAP authentication
        String expectedJwtPayloadJson = "{" + "\"sub\":\"nobel\"," + "\"preferred_username\":\"nobel\","
                + "\"iss\":\"LdapIdentityProvider\"" + "}";
        String expectedAccessStatusJson = "{" + "\"identity\":\"nobel\"," + "\"anonymous\":false" + "}";

        // When: the /access/token/login endpoint is queried
        final String basicAuthCredentials = encodeCredentialsForBasicAuth("nobel", "password");
        final Response tokenResponse = client.target(createURL(tokenIdentityProviderPath)).request()
                .header("Authorization", "Basic " + basicAuthCredentials).post(null, Response.class);

        // Then: the server returns 200 OK with an access token
        assertEquals(201, tokenResponse.getStatus());
        String token = tokenResponse.readEntity(String.class);
        assertTrue(StringUtils.isNotEmpty(token));
        String[] jwtParts = token.split("\\.");
        assertEquals(3, jwtParts.length);
        String jwtPayload = new String(Base64.getDecoder().decode(jwtParts[1]), "UTF-8");
        JSONAssert.assertEquals(expectedJwtPayloadJson, jwtPayload, false);

        // When: the token is returned in the Authorization header
        final Response accessResponse = client.target(createURL("access")).request()
                .header("Authorization", "Bearer " + token).get(Response.class);

        // Then: the server acknowledges the client has access
        assertEquals(200, accessResponse.getStatus());
        String accessStatus = accessResponse.readEntity(String.class);
        JSONAssert.assertEquals(expectedAccessStatusJson, accessStatus, false);

    }

    @Test
    public void testTokenGenerationWithIdentityProvider() throws Exception {

        // Given: the client and server have been configured correctly for LDAP authentication
        String expectedJwtPayloadJson = "{" + "\"sub\":\"nobel\"," + "\"preferred_username\":\"nobel\","
                + "\"iss\":\"LdapIdentityProvider\"," + "\"aud\":\"LdapIdentityProvider\"" + "}";
        String expectedAccessStatusJson = "{" + "\"identity\":\"nobel\"," + "\"anonymous\":false" + "}";

        // When: the /access/token/identity-provider endpoint is queried
        final String basicAuthCredentials = encodeCredentialsForBasicAuth("nobel", "password");
        final Response tokenResponse = client.target(createURL(tokenIdentityProviderPath)).request()
                .header("Authorization", "Basic " + basicAuthCredentials).post(null, Response.class);

        // Then: the server returns 200 OK with an access token
        assertEquals(201, tokenResponse.getStatus());
        String token = tokenResponse.readEntity(String.class);
        assertTrue(StringUtils.isNotEmpty(token));
        String[] jwtParts = token.split("\\.");
        assertEquals(3, jwtParts.length);
        String jwtPayload = new String(Base64.getDecoder().decode(jwtParts[1]), "UTF-8");
        JSONAssert.assertEquals(expectedJwtPayloadJson, jwtPayload, false);

        // When: the token is returned in the Authorization header
        final Response accessResponse = client.target(createURL("access")).request()
                .header("Authorization", "Bearer " + token).get(Response.class);

        // Then: the server acknowledges the client has access
        assertEquals(200, accessResponse.getStatus());
        String accessStatus = accessResponse.readEntity(String.class);
        JSONAssert.assertEquals(expectedAccessStatusJson, accessStatus, false);

    }

    @Test
    public void testGetCurrentUserFailsForAnonymous() throws Exception {

        // Given: the client is connected to an unsecured NiFi Registry

        // When: the /access endpoint is queried with no credentials
        final Response response = client.target(createURL("/access")).request().get(Response.class);

        // Then: the server returns a 200 OK with the expected current user
        assertEquals(401, response.getStatus());

    }

    @Test
    public void testGetCurrentUser() throws Exception {

        // Given: the client is connected to an unsecured NiFi Registry
        String expectedJson = "{" + "\"identity\":\"nifiadmin\"," + "\"anonymous\":false,"
                + "\"resourcePermissions\":{"
                + "\"anyTopLevelResource\":{\"canRead\":true,\"canWrite\":true,\"canDelete\":true},"
                + "\"buckets\":{\"canRead\":true,\"canWrite\":true,\"canDelete\":true},"
                + "\"tenants\":{\"canRead\":true,\"canWrite\":true,\"canDelete\":true},"
                + "\"policies\":{\"canRead\":true,\"canWrite\":true,\"canDelete\":true},"
                + "\"proxy\":{\"canRead\":false,\"canWrite\":true,\"canDelete\":false}}" + "}";

        // When: the /access endpoint is queried using a JWT for the nifiadmin LDAP user
        final Response response = client.target(createURL("/access")).request()
                .header("Authorization", "Bearer " + adminAuthToken).get(Response.class);

        // Then: the server returns a 200 OK with the expected current user
        assertEquals(200, response.getStatus());
        String actualJson = response.readEntity(String.class);
        JSONAssert.assertEquals(expectedJson, actualJson, false);

    }

    @Test
    public void testUsers() throws Exception {

        // Given: the client and server have been configured correctly for LDAP authentication
        String expectedJson = "[" + "{\"identity\":\"nifiadmin\",\"userGroups\":[],\"configurable\":false,"
                + "\"resourcePermissions\":{"
                + "\"anyTopLevelResource\":{\"canRead\":true,\"canWrite\":true,\"canDelete\":true},"
                + "\"buckets\":{\"canRead\":true,\"canWrite\":true,\"canDelete\":true},"
                + "\"tenants\":{\"canRead\":true,\"canWrite\":true,\"canDelete\":true},"
                + "\"policies\":{\"canRead\":true,\"canWrite\":true,\"canDelete\":true},"
                + "\"proxy\":{\"canRead\":false,\"canWrite\":true,\"canDelete\":false}}},"
                + "{\"identity\":\"euler\",\"userGroups\":[{\"identity\":\"mathematicians\"}],\"accessPolicies\":[],\"configurable\":false},"
                + "{\"identity\":\"euclid\",\"userGroups\":[{\"identity\":\"mathematicians\"}],\"accessPolicies\":[],\"configurable\":false},"
                + "{\"identity\":\"boyle\",\"userGroups\":[{\"identity\":\"chemists\"}],\"accessPolicies\":[],\"configurable\":false},"
                + "{\"identity\":\"newton\",\"userGroups\":[{\"identity\":\"scientists\"}],\"accessPolicies\":[],\"configurable\":false},"
                + "{\"identity\":\"riemann\",\"userGroups\":[{\"identity\":\"mathematicians\"}],\"accessPolicies\":[],\"configurable\":false},"
                + "{\"identity\":\"gauss\",\"userGroups\":[{\"identity\":\"mathematicians\"}],\"accessPolicies\":[],\"configurable\":false},"
                + "{\"identity\":\"galileo\",\"userGroups\":[{\"identity\":\"scientists\"},{\"identity\":\"italians\"}],\"accessPolicies\":[],\"configurable\":false},"
                + "{\"identity\":\"nobel\",\"userGroups\":[{\"identity\":\"chemists\"}],\"accessPolicies\":[],\"configurable\":false},"
                + "{\"identity\":\"pasteur\",\"userGroups\":[{\"identity\":\"chemists\"}],\"accessPolicies\":[],\"configurable\":false},"
                + "{\"identity\":\"tesla\",\"userGroups\":[{\"identity\":\"scientists\"}],\"accessPolicies\":[],\"configurable\":false},"
                + "{\"identity\":\"nogroup\",\"userGroups\":[],\"accessPolicies\":[],\"configurable\":false},"
                + "{\"identity\":\"einstein\",\"userGroups\":[{\"identity\":\"scientists\"}],\"accessPolicies\":[],\"configurable\":false},"
                + "{\"identity\":\"curie\",\"userGroups\":[{\"identity\":\"chemists\"}],\"accessPolicies\":[],\"configurable\":false}]";

        // When: the /tenants/users endpoint is queried
        final String usersJson = client.target(createURL("tenants/users")).request()
                .header("Authorization", "Bearer " + adminAuthToken).get(String.class);

        // Then: the server returns a list of all users (see test-ldap-data.ldif)
        JSONAssert.assertEquals(expectedJson, usersJson, false);
    }

    @Test
    public void testUserGroups() throws Exception {

        // Given: the client and server have been configured correctly for LDAP authentication
        String expectedJson = "[" + "{" + "\"identity\":\"chemists\","
                + "\"users\":[{\"identity\":\"pasteur\"},{\"identity\":\"boyle\"},{\"identity\":\"curie\"},{\"identity\":\"nobel\"}],"
                + "\"accessPolicies\":[]," + "\"configurable\":false" + "}," + "{"
                + "\"identity\":\"mathematicians\","
                + "\"users\":[{\"identity\":\"gauss\"},{\"identity\":\"euclid\"},{\"identity\":\"riemann\"},{\"identity\":\"euler\"}],"
                + "\"accessPolicies\":[]," + "\"configurable\":false" + "}," + "{" + "\"identity\":\"scientists\","
                + "\"users\":[{\"identity\":\"einstein\"},{\"identity\":\"tesla\"},{\"identity\":\"newton\"},{\"identity\":\"galileo\"}],"
                + "\"accessPolicies\":[]," + "\"configurable\":false" + "}," + "{" + "\"identity\":\"italians\","
                + "\"users\":[{\"identity\":\"galileo\"}]," + "\"accessPolicies\":[]," + "\"configurable\":false"
                + "}]";

        // When: the /tenants/users endpoint is queried
        final String groupsJson = client.target(createURL("tenants/user-groups")).request()
                .header("Authorization", "Bearer " + adminAuthToken).get(String.class);

        // Then: the server returns a list of all users (see test-ldap-data.ldif)
        JSONAssert.assertEquals(expectedJson, groupsJson, false);
    }

    @Test
    public void testCreateTenantFails() throws Exception {

        // Given: the server has been configured with the LdapUserGroupProvider, which is non-configurable,
        //   and: the client wants to create a tenant
        Tenant tenant = new Tenant();
        tenant.setIdentity("new_tenant");

        // When: the POST /tenants/users endpoint is accessed
        final Response createUserResponse = client.target(createURL("tenants/users")).request()
                .header("Authorization", "Bearer " + adminAuthToken)
                .post(Entity.entity(tenant, MediaType.APPLICATION_JSON_TYPE), Response.class);

        // Then: an error is returned
        assertEquals(409, createUserResponse.getStatus());

        // When: the POST /tenants/users endpoint is accessed
        final Response createUserGroupResponse = client.target(createURL("tenants/user-groups")).request()
                .header("Authorization", "Bearer " + adminAuthToken)
                .post(Entity.entity(tenant, MediaType.APPLICATION_JSON_TYPE), Response.class);

        // Then: an error is returned because the UserGroupProvider is non-configurable
        assertEquals(409, createUserGroupResponse.getStatus());
    }

    @Test
    public void testAccessPolicyCreation() throws Exception {

        // Given: the server has been configured with an initial admin "nifiadmin" and a user with no accessPolicies "nobel"
        String nobelId = getTenantIdentifierByIdentity("nobel");
        String chemistsId = getTenantIdentifierByIdentity("chemists"); // a group containing user "nobel"

        final String basicAuthCredentials = encodeCredentialsForBasicAuth("nobel", "password");
        final String nobelAuthToken = client.target(createURL(tokenIdentityProviderPath)).request()
                .header("Authorization", "Basic " + basicAuthCredentials).post(null, String.class);

        // When: user nobel re-checks top-level permissions
        final CurrentUser currentUser = client.target(createURL("/access")).request()
                .header("Authorization", "Bearer " + nobelAuthToken).get(CurrentUser.class);

        // Then: 200 OK is returned indicating user has access to no top-level resources
        assertEquals(new Permissions(), currentUser.getResourcePermissions().getBuckets());
        assertEquals(new Permissions(), currentUser.getResourcePermissions().getTenants());
        assertEquals(new Permissions(), currentUser.getResourcePermissions().getPolicies());
        assertEquals(new Permissions(), currentUser.getResourcePermissions().getProxy());

        // When: nifiadmin creates a bucket
        final Bucket bucket = new Bucket();
        bucket.setName("Integration Test Bucket");
        bucket.setDescription("A bucket created by an integration test.");
        Response adminCreatesBucketResponse = client.target(createURL("buckets")).request()
                .header("Authorization", "Bearer " + adminAuthToken)
                .post(Entity.entity(bucket, MediaType.APPLICATION_JSON), Response.class);

        // Then: the server returns a 200 OK
        assertEquals(200, adminCreatesBucketResponse.getStatus());
        Bucket createdBucket = adminCreatesBucketResponse.readEntity(Bucket.class);

        // When: user nobel initial queries /buckets
        final Bucket[] buckets1 = client.target(createURL("buckets")).request()
                .header("Authorization", "Bearer " + nobelAuthToken).get(Bucket[].class);

        // Then: an empty list is returned (nobel has no read access yet)
        assertNotNull(buckets1);
        assertEquals(0, buckets1.length);

        // When: nifiadmin grants read access on createdBucket to 'chemists' a group containing nobel
        AccessPolicy readPolicy = new AccessPolicy();
        readPolicy.setResource("/buckets/" + createdBucket.getIdentifier());
        readPolicy.setAction("read");
        readPolicy.addUserGroups(Arrays.asList(new Tenant(chemistsId, "chemists")));
        Response adminGrantsReadAccessResponse = client.target(createURL("policies")).request()
                .header("Authorization", "Bearer " + adminAuthToken)
                .post(Entity.entity(readPolicy, MediaType.APPLICATION_JSON), Response.class);

        // Then: the server returns a 201 Created
        assertEquals(201, adminGrantsReadAccessResponse.getStatus());

        // When: nifiadmin tries to list all buckets
        final Bucket[] adminBuckets = client.target(createURL("buckets")).request()
                .header("Authorization", "Bearer " + adminAuthToken).get(Bucket[].class);

        // Then: the full list is returned (verifies that per-bucket access policies are additive to base /buckets policy)
        assertNotNull(adminBuckets);
        assertEquals(1, adminBuckets.length);
        assertEquals(createdBucket.getIdentifier(), adminBuckets[0].getIdentifier());
        assertEquals(new Permissions().withCanRead(true).withCanWrite(true).withCanDelete(true),
                adminBuckets[0].getPermissions());

        // When: user nobel re-queries /buckets
        final Bucket[] buckets2 = client.target(createURL("buckets")).request()
                .header("Authorization", "Bearer " + nobelAuthToken).get(Bucket[].class);

        // Then: the created bucket is now present
        assertNotNull(buckets2);
        assertEquals(1, buckets2.length);
        assertEquals(createdBucket.getIdentifier(), buckets2[0].getIdentifier());
        assertEquals(new Permissions().withCanRead(true), buckets2[0].getPermissions());

        // When: nifiadmin grants write access on createdBucket to user 'nobel'
        AccessPolicy writePolicy = new AccessPolicy();
        writePolicy.setResource("/buckets/" + createdBucket.getIdentifier());
        writePolicy.setAction("write");
        writePolicy.addUsers(Arrays.asList(new Tenant(nobelId, "nobel")));
        Response adminGrantsWriteAccessResponse = client.target(createURL("policies")).request()
                .header("Authorization", "Bearer " + adminAuthToken)
                .post(Entity.entity(writePolicy, MediaType.APPLICATION_JSON), Response.class);

        // Then: the server returns a 201 Created
        assertEquals(201, adminGrantsWriteAccessResponse.getStatus());

        // When: user nobel re-queries /buckets
        final Bucket[] buckets3 = client.target(createURL("buckets")).request()
                .header("Authorization", "Bearer " + nobelAuthToken).get(Bucket[].class);

        // Then: the authorizedActions are updated
        assertNotNull(buckets3);
        assertEquals(1, buckets3.length);
        assertEquals(createdBucket.getIdentifier(), buckets3[0].getIdentifier());
        assertEquals(new Permissions().withCanRead(true).withCanWrite(true), buckets3[0].getPermissions());

    }

    /** A helper method to lookup identifiers for tenant identities using the REST API
     *
     * @param tenantIdentity - the identity to lookup
     * @return A string containing the identifier of the tenant, or null if the tenant identity is not found.
     */
    private String getTenantIdentifierByIdentity(String tenantIdentity) {

        final Tenant[] users = client.target(createURL("tenants/users")).request()
                .header("Authorization", "Bearer " + adminAuthToken).get(Tenant[].class);

        final Tenant[] groups = client.target(createURL("tenants/user-groups")).request()
                .header("Authorization", "Bearer " + adminAuthToken).get(Tenant[].class);

        final Tenant matchedTenant = Stream.concat(Arrays.stream(users), Arrays.stream(groups))
                .filter(tenant -> tenant.getIdentity().equalsIgnoreCase(tenantIdentity)).findFirst().orElse(null);

        return matchedTenant != null ? matchedTenant.getIdentifier() : null;
    }

    /** A helper method to lookup access policies
     *
     * @return A string containing the identifier of the policy, or null if the policy identity is not found.
     */
    private AccessPolicy getPolicyByResourceAction(String action, String resource) {

        final AccessPolicySummary[] policies = client.target(createURL("policies")).request()
                .header("Authorization", "Bearer " + adminAuthToken).get(AccessPolicySummary[].class);

        final AccessPolicySummary matchedPolicy = Arrays.stream(policies)
                .filter(p -> p.getAction().equalsIgnoreCase(action) && p.getResource().equalsIgnoreCase(resource))
                .findFirst().orElse(null);

        if (matchedPolicy == null) {
            return null;
        }

        String policyId = matchedPolicy.getIdentifier();

        final AccessPolicy policy = client.target(createURL("policies/" + policyId)).request()
                .header("Authorization", "Bearer " + adminAuthToken).get(AccessPolicy.class);

        return policy;
    }

    private List<AccessPolicy> createAccessPoliciesSnapshot() {

        final AccessPolicySummary[] policySummaries = client.target(createURL("policies")).request()
                .header("Authorization", "Bearer " + adminAuthToken).get(AccessPolicySummary[].class);

        final List<AccessPolicy> policies = new ArrayList<>(policySummaries.length);
        for (AccessPolicySummary s : policySummaries) {
            AccessPolicy policy = client.target(createURL("policies/" + s.getIdentifier())).request()
                    .header("Authorization", "Bearer " + adminAuthToken).get(AccessPolicy.class);
            policies.add(policy);
        }

        return policies;
    }

    private void restoreAccessPoliciesSnapshot(List<AccessPolicy> accessPoliciesSnapshot) {

        List<AccessPolicy> currentAccessPolicies = createAccessPoliciesSnapshot();

        Set<String> policiesToRestore = accessPoliciesSnapshot.stream().map(AccessPolicy::getIdentifier)
                .collect(Collectors.toSet());

        Set<String> policiesToDelete = currentAccessPolicies.stream()
                .filter(p -> !policiesToRestore.contains(p.getIdentifier())).map(AccessPolicy::getIdentifier)
                .collect(Collectors.toSet());

        for (AccessPolicy originalPolicy : accessPoliciesSnapshot) {

            Response getCurrentPolicy = client.target(createURL("policies/" + originalPolicy.getIdentifier()))
                    .request().header("Authorization", "Bearer " + adminAuthToken).get(Response.class);

            if (getCurrentPolicy.getStatus() == 200) {
                // update policy to match original
                client.target(createURL("policies/" + originalPolicy.getIdentifier())).request()
                        .header("Authorization", "Bearer " + adminAuthToken)
                        .put(Entity.entity(originalPolicy, MediaType.APPLICATION_JSON));
            } else {
                // post the original policy
                client.target(createURL("policies")).request().header("Authorization", "Bearer " + adminAuthToken)
                        .post(Entity.entity(originalPolicy, MediaType.APPLICATION_JSON));
            }

        }

        for (String id : policiesToDelete) {
            try {
                client.target(createURL("policies/" + id)).request()
                        .header("Authorization", "Bearer " + adminAuthToken).delete();
            } catch (Exception e) {
                // do nothing
            }
        }

    }

    private static Form encodeCredentialsForURLFormParams(String username, String password) {
        return new Form().param("username", username).param("password", password);
    }

    private static String encodeCredentialsForBasicAuth(String username, String password) {
        final String credentials = username + ":" + password;
        final String base64credentials = new String(
                Base64.getEncoder().encode(credentials.getBytes(Charset.forName("UTF-8"))));
        return base64credentials;
    }
}