mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

Guy Paddock
27.52.2017 a2a71e1777beb48f98e64811ca6cd43286b79be3
Tests for sub-tree flattening config

Also adds a few accessors in order to faciliate accessors for tests. More work is needed in this area -- FR's code is an inconsistent mess of final classes, fluent factory methods, and accessors with the wrong access level.
6 files modified
287 ■■■■■ changed files
opendj-rest2ldap-servlet/src/main/webapp/WEB-INF/classes/rest2ldap/endpoints/api/example-v1.json 14 ●●●●● patch | view | raw | blame | history
opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Resource.java 19 ●●●●● patch | view | raw | blame | history
opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/SubResource.java 25 ●●●● patch | view | raw | blame | history
opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/SubResourceCollection.java 11 ●●●●● patch | view | raw | blame | history
opendj-rest2ldap/src/test/java/org/forgerock/opendj/rest2ldap/Rest2LdapJsonConfiguratorTest.java 204 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/resource/config/rest2ldap/endpoints/api/example-v1.json 14 ●●●●● patch | view | raw | blame | history
opendj-rest2ldap-servlet/src/main/webapp/WEB-INF/classes/rest2ldap/endpoints/api/example-v1.json
@@ -33,6 +33,20 @@
                    },
                    "isReadOnly": true
                },
                // This resource provides a read-only view of all users in the system, including
                // users nested underneath entries like org units, organizations, etc., starting
                // from "ou=people,dc=example,dc=com" and working down.
                "all-users": {
                    "type": "collection",
                    "dnTemplate": "ou=people,dc=example,dc=com",
                    "resource": "frapi:opendj:rest2ldap:user:1.0",
                    "namingStrategy": {
                        "type": "clientDnNaming",
                        "dnAttribute": "uid"
                    },
                    "isReadOnly": true,
                    "flattenSubtree": true
                },
                "groups": {
                    "type": "collection",
                    "dnTemplate": "ou=groups,dc=example,dc=com",
opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Resource.java
@@ -34,6 +34,8 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
@@ -484,7 +486,7 @@
     * @return The unique service ID for this resource, given the specified writability.
     */
    String getServiceId(boolean isReadOnly) {
        StringBuilder serviceId = new StringBuilder(this.getResourceId());
        final StringBuilder serviceId = new StringBuilder(this.getResourceId());
        if (isReadOnly) {
            serviceId.append(":read-only");
@@ -495,6 +497,21 @@
        return serviceId.toString();
    }
    /**
     * Gets a map of the sub-resources under this resource, keyed by URL template.
     *
     * @return  The map of sub-resource URL templates to sub-resources.
     */
    Map<String, SubResource> getSubResourceMap() {
        final Map<String, SubResource> result = new HashMap<>();
        for (SubResource subResource : this.subResources) {
            result.put(subResource.getUrlTemplate(), subResource);
        }
        return result;
    }
    void build(final Rest2Ldap rest2Ldap) {
        // Prevent re-entrant calls.
        if (isBuilt) {
opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/SubResource.java
@@ -60,9 +60,10 @@
    String urlTemplate = "";
    String dnTemplateString = "";
    boolean isReadOnly = false;
    Rest2Ldap rest2Ldap;
    Resource resource;
    protected boolean isReadOnly = false;
    protected Rest2Ldap rest2Ldap;
    protected Resource resource;
    SubResource(final String resourceId) {
        this.resourceId = resourceId;
@@ -80,9 +81,27 @@
    @Override
    public final String toString() {
        return getUrlTemplate();
    }
    /**
     * Gets the URL template that must match for this sub-resource to apply to a given request.
     *
     * @return  The URL template for this sub-resource.
     */
    public String getUrlTemplate() {
        return urlTemplate;
    }
    /**
     * Gets whether or not this sub-resource has been configured for read-only access.
     *
     * @return  {@code true} if the sub-resource is read-only; {@code false} otherwise.
     */
    public boolean isReadOnly() {
        return isReadOnly;
    }
    final Resource getResource() {
        return resource;
    }
opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/SubResourceCollection.java
@@ -83,6 +83,17 @@
    }
    /**
     * Gets whether or not this sub-resource should flatten sub-entries in results.
     *
     * @return  {@code true} if entries deep in the sub-tree are included in a flattened
     *          collection view; {@code false} if only entries at the top level of the DN for this
     *          sub-resource should be returned.
     */
    public boolean shouldFlattenSubtree() {
        return flattenSubtree;
    }
    /**
     * Indicates that the JSON resource ID must be provided by the user, and will be used for naming the associated LDAP
     * entry. More specifically, LDAP entry names will be derived by appending a single RDN to the collection's base DN
     * composed of the specified attribute type and LDAP value taken from the LDAP entry once attribute mapping has been
opendj-rest2ldap/src/test/java/org/forgerock/opendj/rest2ldap/Rest2LdapJsonConfiguratorTest.java
@@ -29,6 +29,8 @@
import java.nio.file.Paths;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.forgerock.api.CrestApiProducer;
import org.forgerock.api.models.ApiDescription;
import org.forgerock.api.models.Items;
@@ -43,6 +45,7 @@
import org.forgerock.services.context.RootContext;
import org.forgerock.testng.ForgeRockTestCase;
import org.forgerock.util.Options;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -66,9 +69,8 @@
    @Test
    public void testConfigureEndpointsWithApiDescription() throws Exception {
        final DescribableRequestHandler handler =
            createDescribableHandler(CONFIG_DIR.resolve("endpoints").toFile());
        final File endpointsDir = CONFIG_DIR.resolve("endpoints").toFile();
        final DescribableRequestHandler handler = createDescribableHandler(endpointsDir);
        final ApiDescription api = requestApi(handler, "api/users/bjensen");
        assertThat(api).isNotNull();
@@ -82,6 +84,7 @@
        assertThat(api.getPaths().getNames()).containsOnly(
            "/api/users",
            "/api/read-only-users",
            "/api/all-users",
            "/api/groups");
        assertThat(api.getDefinitions().getNames()).containsOnly(
@@ -132,6 +135,201 @@
        }
    }
    @DataProvider
    public Object[][] invalidSubResourceConfigurations() {
        // @Checkstyle:off
        return new Object[][] {
                {
                        "{"
                                + "'example-v1': {"
                                        + "'subResources': {"
                                                + "'writeable-collection': {"
                                                        + "'type': 'collection',"
                                                        + "'dnTemplate': 'ou=people,dc=example,dc=com',"
                                                        + "'resource': 'frapi:opendj:rest2ldap:user:1.0',"
                                                        + "'namingStrategy': {"
                                                            + "'type': 'clientDnNaming',"
                                                            + "'dnAttribute': 'uid'"
                                                        + "},"
                                                        + "'flattenSubtree': true"
                                                + "}"
                                        + "}"
                                + "}"
                        + "}"
                },
                {
                        "{"
                                + "'example-v1': {"
                                        + "'subResources': {"
                                                + "'writeable-collection': {"
                                                        + "'type': 'collection',"
                                                        + "'dnTemplate': 'ou=people,dc=example,dc=com',"
                                                        + "'resource': 'frapi:opendj:rest2ldap:user:1.0',"
                                                        + "'namingStrategy': {"
                                                            + "'type': 'clientDnNaming',"
                                                            + "'dnAttribute': 'uid'"
                                                        + "},"
                                                        + "'isReadOnly': false,"
                                                        + "'flattenSubtree': true"
                                                + "}"
                                        + "}"
                                + "}"
                        + "}"
                }
        };
        // @Checkstyle:on
    }
    @DataProvider
    public Object[][] validSubResourceConfigurations() {
        // @Checkstyle:off
        return new Object[][] {
                {
                        false,
                        false,
                        "{"
                                + "'example-v1': {"
                                        + "'subResources': {"
                                                + "'all-users': {"
                                                        + "'type': 'collection',"
                                                        + "'dnTemplate': 'ou=people,dc=example,dc=com',"
                                                        + "'resource': 'frapi:opendj:rest2ldap:user:1.0',"
                                                        + "'namingStrategy': {"
                                                            + "'type': 'clientDnNaming',"
                                                            + "'dnAttribute': 'uid'"
                                                        + "},"
                                                        + "'flattenSubtree': false"
                                                + "}"
                                        + "}"
                                + "}"
                        + "}"
                },
                {
                        true,
                        false,
                        "{"
                                + "'example-v1': {"
                                        + "'subResources': {"
                                                + "'all-users': {"
                                                        + "'type': 'collection',"
                                                        + "'dnTemplate': 'ou=people,dc=example,dc=com',"
                                                        + "'resource': 'frapi:opendj:rest2ldap:user:1.0',"
                                                        + "'namingStrategy': {"
                                                            + "'type': 'clientDnNaming',"
                                                            + "'dnAttribute': 'uid'"
                                                        + "},"
                                                        + "'isReadOnly': true"
                                                + "}"
                                        + "}"
                                + "}"
                        + "}"
                },
                {
                        true,
                        false,
                        "{"
                                + "'example-v1': {"
                                        + "'subResources': {"
                                                + "'all-users': {"
                                                        + "'type': 'collection',"
                                                        + "'dnTemplate': 'ou=people,dc=example,dc=com',"
                                                        + "'resource': 'frapi:opendj:rest2ldap:user:1.0',"
                                                        + "'namingStrategy': {"
                                                            + "'type': 'clientDnNaming',"
                                                            + "'dnAttribute': 'uid'"
                                                        + "},"
                                                        + "'isReadOnly': true,"
                                                        + "'flattenSubtree': false"
                                                + "}"
                                        + "}"
                                + "}"
                        + "}"
                },
                {
                        false,
                        false,
                        "{"
                                + "'example-v1': {"
                                        + "'subResources': {"
                                                + "'all-users': {"
                                                        + "'type': 'collection',"
                                                        + "'dnTemplate': 'ou=people,dc=example,dc=com',"
                                                        + "'resource': 'frapi:opendj:rest2ldap:user:1.0',"
                                                        + "'namingStrategy': {"
                                                            + "'type': 'clientDnNaming',"
                                                            + "'dnAttribute': 'uid'"
                                                        + "},"
                                                        + "'isReadOnly': false,"
                                                        + "'flattenSubtree': false"
                                                + "}"
                                        + "}"
                                + "}"
                        + "}"
                },
                {
                        true,
                        true,
                        "{"
                                + "'example-v1': {"
                                        + "'subResources': {"
                                                + "'all-users': {"
                                                        + "'type': 'collection',"
                                                        + "'dnTemplate': 'ou=people,dc=example,dc=com',"
                                                        + "'resource': 'frapi:opendj:rest2ldap:user:1.0',"
                                                        + "'namingStrategy': {"
                                                            + "'type': 'clientDnNaming',"
                                                            + "'dnAttribute': 'uid'"
                                                        + "},"
                                                        + "'isReadOnly': true,"
                                                        + "'flattenSubtree': true"
                                                + "}"
                                        + "}"
                                + "}"
                        + "}"
                }
        };
        // @Checkstyle:on
    }
    @Test(dataProvider = "invalidSubResourceConfigurations")
    public void testInvalidSubResourceConfigurations(final String rawJson) throws Exception {
        try {
            Rest2LdapJsonConfigurator.configureResources(parseJson(rawJson));
            fail("Expected an IllegalArgumentException");
        }
        catch (IllegalArgumentException ex) {
            assertThat(ex.getMessage())
                .isEqualTo("Sub-resources must be read-only to support sub-tree flattening.");
        }
    }
    @Test(dataProvider = "validSubResourceConfigurations")
    public void testValidSubResourceConfigurations(final boolean expectingReadOnly,
                                                   final boolean expectingSubtreeFlattened,
                                                   final String rawJson) throws Exception {
        final List<org.forgerock.opendj.rest2ldap.Resource> resources =
            Rest2LdapJsonConfigurator.configureResources(parseJson(rawJson));
        final org.forgerock.opendj.rest2ldap.Resource firstResource;
        final Map<String, SubResource> subResources;
        final SubResourceCollection allUsersSubResource;
        assertThat(resources.size()).isEqualTo(1);
        firstResource = resources.get(0);
        assertThat(firstResource.getResourceId()).isEqualTo("example-v1");
        subResources = firstResource.getSubResourceMap();
        assertThat(subResources.size()).isEqualTo(1);
        allUsersSubResource = (SubResourceCollection)subResources.get("all-users");
        assertThat(allUsersSubResource.isReadOnly()).isEqualTo(expectingReadOnly);
        assertThat(allUsersSubResource.shouldFlattenSubtree()).isEqualTo(expectingSubtreeFlattened);
    }
    private RequestHandler createRequestHandler(final File endpointsDir) throws IOException {
        return Rest2LdapJsonConfigurator.configureEndpoints(endpointsDir, Options.defaultOptions());
    }
opendj-server-legacy/resource/config/rest2ldap/endpoints/api/example-v1.json
@@ -33,6 +33,20 @@
                    },
                    "isReadOnly": true
                },
                // This resource provides a read-only view of all users in the system, including
                // users nested underneath entries like org units, organizations, etc., starting
                // from "ou=people,dc=example,dc=com" and working down.
                "all-users": {
                    "type": "collection",
                    "dnTemplate": "ou=people,dc=example,dc=com",
                    "resource": "frapi:opendj:rest2ldap:user:1.0",
                    "namingStrategy": {
                        "type": "clientDnNaming",
                        "dnAttribute": "uid"
                    },
                    "isReadOnly": true,
                    "flattenSubtree": true
                },
                "groups": {
                    "type": "collection",
                    "dnTemplate": "ou=groups,dc=example,dc=com",