From a2a71e1777beb48f98e64811ca6cd43286b79be3 Mon Sep 17 00:00:00 2001
From: Guy Paddock <guy@rosieapp.com>
Date: Fri, 27 Oct 2017 04:49:24 +0000
Subject: [PATCH] Tests for sub-tree flattening config
---
opendj-server-legacy/resource/config/rest2ldap/endpoints/api/example-v1.json | 14 ++
opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/SubResourceCollection.java | 11 ++
opendj-rest2ldap/src/test/java/org/forgerock/opendj/rest2ldap/Rest2LdapJsonConfiguratorTest.java | 204 ++++++++++++++++++++++++++++++++++++++++
opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/SubResource.java | 25 ++++
opendj-rest2ldap-servlet/src/main/webapp/WEB-INF/classes/rest2ldap/endpoints/api/example-v1.json | 14 ++
opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Resource.java | 19 +++
6 files changed, 280 insertions(+), 7 deletions(-)
diff --git a/opendj-rest2ldap-servlet/src/main/webapp/WEB-INF/classes/rest2ldap/endpoints/api/example-v1.json b/opendj-rest2ldap-servlet/src/main/webapp/WEB-INF/classes/rest2ldap/endpoints/api/example-v1.json
index b5a0b02..90de7e3 100644
--- a/opendj-rest2ldap-servlet/src/main/webapp/WEB-INF/classes/rest2ldap/endpoints/api/example-v1.json
+++ b/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",
diff --git a/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Resource.java b/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Resource.java
index 35825f6..e7964bd 100644
--- a/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Resource.java
+++ b/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) {
diff --git a/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/SubResource.java b/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/SubResource.java
index 0e61043..ee6a16a 100644
--- a/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/SubResource.java
+++ b/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;
}
diff --git a/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/SubResourceCollection.java b/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/SubResourceCollection.java
index d66681a..85fd139 100644
--- a/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/SubResourceCollection.java
+++ b/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
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 7c2e399..2a74a2d 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
@@ -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());
}
diff --git a/opendj-server-legacy/resource/config/rest2ldap/endpoints/api/example-v1.json b/opendj-server-legacy/resource/config/rest2ldap/endpoints/api/example-v1.json
index b5a0b02..90de7e3 100644
--- a/opendj-server-legacy/resource/config/rest2ldap/endpoints/api/example-v1.json
+++ b/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",
--
Gitblit v1.10.0