From a2d67f674f7e80dcd9ca901ea63df41ef47f4214 Mon Sep 17 00:00:00 2001
From: vharseko <vharseko@openam.org.ru>
Date: Wed, 22 Nov 2017 05:08:57 +0000
Subject: [PATCH] Merge pull request #3 from GuyPaddock/wren/feature/subtree-flattening
---
opendj-rest2ldap/src/test/java/org/forgerock/opendj/rest2ldap/Rest2LdapJsonConfiguratorTest.java | 394 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 383 insertions(+), 11 deletions(-)
diff --git a/opendj-rest2ldap/src/test/java/org/forgerock/opendj/rest2ldap/Rest2LdapJsonConfiguratorTest.java b/opendj-rest2ldap/src/test/java/org/forgerock/opendj/rest2ldap/Rest2LdapJsonConfiguratorTest.java
index 2690bd4..f948f30 100644
--- a/opendj-rest2ldap/src/test/java/org/forgerock/opendj/rest2ldap/Rest2LdapJsonConfiguratorTest.java
+++ b/opendj-rest2ldap/src/test/java/org/forgerock/opendj/rest2ldap/Rest2LdapJsonConfiguratorTest.java
@@ -12,6 +12,7 @@
* information: "Portions Copyright [year] [name of copyright owner]".
*
* Copyright 2016 ForgeRock AS.
+ * Portions Copyright 2017 Rosie Applications, Inc.
*/
package org.forgerock.opendj.rest2ldap;
@@ -23,22 +24,30 @@
import static org.forgerock.util.Options.*;
import java.io.File;
+import java.io.IOException;
import java.io.StringReader;
import java.nio.file.Path;
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;
+import org.forgerock.api.models.Resource;
+import org.forgerock.api.models.Services;
import org.forgerock.http.routing.UriRouterContext;
import org.forgerock.http.util.Json;
import org.forgerock.json.JsonValue;
import org.forgerock.json.resource.Request;
import org.forgerock.json.resource.RequestHandler;
+import org.forgerock.opendj.ldap.Filter;
import org.forgerock.services.context.Context;
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;
@@ -53,13 +62,19 @@
public class Rest2LdapJsonConfiguratorTest extends ForgeRockTestCase {
private static final String ID = "frapi:opendj:rest2ldap";
private static final String VERSION = "4.0.0";
+
+ private static final Path SERVLET_MODULE_PATH =
+ getPathToMavenModule("opendj-rest2ldap-servlet");
+
private static final Path CONFIG_DIR = Paths.get(
- "../opendj-rest2ldap-servlet/src/main/webapp/WEB-INF/classes/rest2ldap");
+ SERVLET_MODULE_PATH.toString(), "src", "main", "webapp", "WEB-INF", "classes", "rest2ldap");
@Test
public void testConfigureEndpointsWithApiDescription() throws Exception {
- final DescribableRequestHandler handler = configureEndpoints(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();
// Ensure we can can pretty print and parse back the generated api description
@@ -67,44 +82,401 @@
assertThat(api.getId()).isEqualTo(ID);
assertThat(api.getVersion()).isEqualTo(VERSION);
- assertThat(api.getPaths().getNames()).containsOnly("/api/users", "/api/groups");
+
+ assertThat(api.getPaths().getNames()).containsOnly(
+ "/api/users",
+ "/api/read-only-users",
+ "/api/all-users",
+ "/api/groups");
+
assertThat(api.getDefinitions().getNames()).containsOnly(
"frapi:opendj:rest2ldap:object:1.0",
"frapi:opendj:rest2ldap:group:1.0",
"frapi:opendj:rest2ldap:user:1.0",
"frapi:opendj:rest2ldap:posixUser:1.0");
+
+ final Services services = api.getServices();
+
+ assertThat(services.getNames()).containsOnly(
+ "frapi:opendj:rest2ldap:user:1.0:read-write",
+ "frapi:opendj:rest2ldap:user:1.0:read-only",
+ "frapi:opendj:rest2ldap:group:1.0:read-write");
+
+ final String[] readOnlyServices = new String[] {
+ "frapi:opendj:rest2ldap:user:1.0:read-only"
+ };
+
+ for (String serviceName : readOnlyServices) {
+ final Resource service = services.get(serviceName);
+ final Items items = service.getItems();
+
+ assertThat(service.getCreate()).isNull();
+ assertThat(items.getCreate()).isNull();
+ assertThat(items.getUpdate()).isNull();
+ assertThat(items.getDelete()).isNull();
+ assertThat(items.getPatch()).isNull();
+
+ assertThat(items.getRead()).isNotNull();
+ }
+
+ final String[] writableServices = new String[] {
+ "frapi:opendj:rest2ldap:user:1.0:read-write",
+ "frapi:opendj:rest2ldap:group:1.0:read-write"
+ };
+
+ for (String serviceName : writableServices) {
+ final Resource service = services.get(serviceName);
+ final Items items = service.getItems();
+
+ assertThat(service.getCreate()).isNotNull();
+ assertThat(items.getCreate()).isNotNull();
+ assertThat(items.getUpdate()).isNotNull();
+ assertThat(items.getDelete()).isNotNull();
+ assertThat(items.getPatch()).isNotNull();
+ assertThat(items.getRead()).isNotNull();
+ }
}
- private DescribableRequestHandler configureEndpoints(final File endpointsDir) throws Exception {
- final RequestHandler rh = Rest2LdapJsonConfigurator.configureEndpoints(endpointsDir, Options.defaultOptions());
- DescribableRequestHandler handler = new DescribableRequestHandler(rh);
+ @DataProvider
+ public Object[][] invalidSubResourceSubtreeFlatteningConfigurations() {
+ // @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,
+ null,
+ "{"
+ + "'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'"
+ + "}"
+ + "}"
+ + "}"
+ + "}"
+ + "}"
+ },
+ {
+ false,
+ false,
+ "(objectClass=person)",
+ "{"
+ + "'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'"
+ + "},"
+ + "'baseSearchFilter': '(objectClass=person)'"
+ + "}"
+ + "}"
+ + "}"
+ + "}"
+ },
+ {
+ false,
+ false,
+ null,
+ "{"
+ + "'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,
+ null,
+ "{"
+ + "'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,
+ null,
+ "{"
+ + "'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,
+ null,
+ "{"
+ + "'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,
+ null,
+ "{"
+ + "'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 = "invalidSubResourceSubtreeFlatteningConfigurations")
+ public void testInvalidSubResourceSubtreeFlatteningConfigurations(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
+ public void testInvalidSubResourceSearchFilterConfiguration()
+ throws Exception {
+ final String rawJson =
+ "{"
+ + "'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'"
+ + "},"
+ + "'baseSearchFilter': 'badFilter'"
+ + "}"
+ + "}"
+ + "}"
+ + "}";
+
+ try {
+ Rest2LdapJsonConfigurator.configureResources(parseJson(rawJson));
+
+ fail("Expected an IllegalArgumentException");
+ }
+ catch (IllegalArgumentException ex) {
+ assertThat(ex.getMessage())
+ .isEqualTo(
+ "The provided search filter \"badFilter\" was missing an equal sign in the " +
+ "suspected simple filter component between positions 0 and 9");
+ }
+ }
+
+ @Test(dataProvider = "validSubResourceConfigurations")
+ public void testValidSubResourceConfigurations(final boolean expectedReadOnly,
+ final boolean expectedSubtreeFlattened,
+ final String expectedSearchFilter,
+ 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(expectedReadOnly);
+ assertThat(allUsersSubResource.shouldFlattenSubtree()).isEqualTo(expectedSubtreeFlattened);
+
+ if (expectedSearchFilter == null) {
+ assertThat(allUsersSubResource.getBaseSearchFilter()).isNull();
+ }
+ else {
+ assertThat(allUsersSubResource.getBaseSearchFilter().toString())
+ .isEqualTo(expectedSearchFilter);
+ }
+ }
+
+ private RequestHandler createRequestHandler(final File endpointsDir) throws IOException {
+ return Rest2LdapJsonConfigurator.configureEndpoints(endpointsDir, Options.defaultOptions());
+ }
+
+ private DescribableRequestHandler createDescribableHandler(final File endpointsDir)
+ throws Exception {
+ final RequestHandler rh = createRequestHandler(endpointsDir);
+ final DescribableRequestHandler handler = new DescribableRequestHandler(rh);
+
handler.api(new CrestApiProducer(ID, VERSION));
+
return handler;
}
- private ApiDescription requestApi(final DescribableRequestHandler handler, String uriPath) {
- Context context = newRouterContext(uriPath);
- Request request = newApiRequest(resourcePath(uriPath));
+ private ApiDescription requestApi(final DescribableRequestHandler handler,
+ final String uriPath) {
+ final Context context = newRouterContext(uriPath);
+ final Request request = newApiRequest(resourcePath(uriPath));
+
return handler.handleApiRequest(context, request);
}
private Context newRouterContext(final String uriPath) {
Context ctx = new RootContext();
+
ctx = new Rest2LdapContext(ctx, rest2Ldap(defaultOptions()));
ctx = new UriRouterContext(ctx, null, uriPath, Collections.<String, String> emptyMap());
+
return ctx;
}
private String prettyPrint(Object o) throws Exception {
final ObjectMapper objectMapper =
- new ObjectMapper().registerModules(new Json.LocalizableStringModule(), new Json.JsonValueModule());
+ new ObjectMapper().registerModules(
+ new Json.LocalizableStringModule(),
+ new Json.JsonValueModule());
+
final ObjectWriter writer = objectMapper.writer().withDefaultPrettyPrinter();
+
return writer.writeValueAsString(o);
}
- static JsonValue parseJson(final String json) throws Exception {
+ private static JsonValue parseJson(final String json) throws Exception {
try (StringReader r = new StringReader(json)) {
return new JsonValue(readJsonLenient(r));
}
}
+
+ private static Path getPathToClass(Class<?> clazz) {
+ return Paths.get(clazz.getProtectionDomain().getCodeSource().getLocation().getPath());
+ }
+
+ private static Path getPathToMavenModule(String moduleName) {
+ final Path testClassPath = getPathToClass(Rest2LdapJsonConfiguratorTest.class);
+
+ return Paths.get(testClassPath.toString(), "..", "..", "..", moduleName);
+ }
}
--
Gitblit v1.10.0