Extends configurator for search filter support
- Adds logic to configure base search filter using the new `baseSearchFilter` option for collection sub-resources.
- Re-factors a bit of sub-resource configuration to make the code easier to follow and less of a mess.
| | |
| | | }, |
| | | // 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. |
| | | // from "ou=people,dc=example,dc=com" and working down. It filters out any other |
| | | // structural elements, including organizations, org units, etc. |
| | | "all-users": { |
| | | "type": "collection", |
| | | "dnTemplate": "ou=people,dc=example,dc=com", |
| | |
| | | "dnAttribute": "uid" |
| | | }, |
| | | "isReadOnly": true, |
| | | "flattenSubtree": true |
| | | "flattenSubtree": true, |
| | | "baseSearchFilter": "(objectClass=person)" |
| | | }, |
| | | "groups": { |
| | | "type": "collection", |
| | |
| | | final String dnTemplate = config.get("dnTemplate").defaultTo("").asString(); |
| | | final Boolean isReadOnly = config.get("isReadOnly").defaultTo(false).asBoolean(); |
| | | final String resourceId = config.get("resource").required().asString(); |
| | | final Boolean flattenSubtree = |
| | | config.get("flattenSubtree").defaultTo(false).asBoolean(); |
| | | |
| | | final SubResourceType subResourceType = |
| | | config.get("type").required().as(enumConstant(SubResourceType.class)); |
| | | |
| | | if (subResourceType == SubResourceType.COLLECTION) { |
| | | return configureCollectionSubResource( |
| | | config, resourceId, urlTemplate, dnTemplate, isReadOnly); |
| | | } else { |
| | | return configureSingletonSubResource( |
| | | config, resourceId, urlTemplate, dnTemplate, isReadOnly); |
| | | } |
| | | } |
| | | |
| | | private static SubResource configureCollectionSubResource(final JsonValue config, |
| | | final String resourceId, |
| | | final String urlTemplate, |
| | | final String dnTemplate, |
| | | final Boolean isReadOnly) { |
| | | final String[] glueObjectClasses = |
| | | config.get("glueObjectClasses") |
| | | .defaultTo(emptyList()) |
| | | .asList(String.class) |
| | | .toArray(new String[0]); |
| | | |
| | | final Boolean flattenSubtree = config.get("flattenSubtree").defaultTo(false).asBoolean(); |
| | | final String searchFilter = config.get("baseSearchFilter").asString(); |
| | | |
| | | final SubResourceCollection collection = |
| | | collectionOf(resourceId) |
| | | .urlTemplate(urlTemplate) |
| | | .dnTemplate(dnTemplate) |
| | | .isReadOnly(isReadOnly) |
| | | .glueObjectClasses(glueObjectClasses) |
| | | .flattenSubtree(flattenSubtree); |
| | | .flattenSubtree(flattenSubtree) |
| | | .baseSearchFilter(searchFilter); |
| | | |
| | | configureCollectionNamingStrategy(config, collection); |
| | | |
| | | return collection; |
| | | } |
| | | |
| | | private static void configureCollectionNamingStrategy(final JsonValue config, |
| | | final SubResourceCollection collection) { |
| | | final JsonValue namingStrategy = config.get("namingStrategy").required(); |
| | | final NamingStrategyType namingStrategyType = |
| | | namingStrategy.get("type").required().as(enumConstant(NamingStrategyType.class)); |
| | |
| | | namingStrategy.get("idAttribute").required().asString()); |
| | | break; |
| | | } |
| | | } |
| | | |
| | | return collection; |
| | | } else { |
| | | private static SubResource configureSingletonSubResource(final JsonValue config, |
| | | final String resourceId, |
| | | final String urlTemplate, |
| | | final String dnTemplate, |
| | | final Boolean isReadOnly) { |
| | | return singletonOf(resourceId) |
| | | .urlTemplate(urlTemplate) |
| | | .dnTemplate(dnTemplate) |
| | | .isReadOnly(isReadOnly); |
| | | } |
| | | } |
| | | |
| | | private static PropertyMapper configurePropertyMapper(final JsonValue mapper, final String defaultLdapAttribute) { |
| | | switch (mapper.get("type").required().asString()) { |
| | |
| | | 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; |
| | |
| | | } |
| | | |
| | | @DataProvider |
| | | public Object[][] invalidSubResourceConfigurations() { |
| | | public Object[][] invalidSubResourceSubtreeFlatteningConfigurations() { |
| | | // @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': {" |
| | |
| | | { |
| | | true, |
| | | false, |
| | | null, |
| | | "{" |
| | | + "'example-v1': {" |
| | | + "'subResources': {" |
| | |
| | | { |
| | | true, |
| | | false, |
| | | null, |
| | | "{" |
| | | + "'example-v1': {" |
| | | + "'subResources': {" |
| | |
| | | { |
| | | false, |
| | | false, |
| | | null, |
| | | "{" |
| | | + "'example-v1': {" |
| | | + "'subResources': {" |
| | |
| | | { |
| | | true, |
| | | true, |
| | | null, |
| | | "{" |
| | | + "'example-v1': {" |
| | | + "'subResources': {" |
| | |
| | | // @Checkstyle:on |
| | | } |
| | | |
| | | @Test(dataProvider = "invalidSubResourceConfigurations") |
| | | public void testInvalidSubResourceConfigurations(final String rawJson) throws Exception { |
| | | @Test(dataProvider = "invalidSubResourceSubtreeFlatteningConfigurations") |
| | | public void testInvalidSubResourceSubtreeFlatteningConfigurations(final String rawJson) |
| | | throws Exception { |
| | | try { |
| | | Rest2LdapJsonConfigurator.configureResources(parseJson(rawJson)); |
| | | |
| | |
| | | } |
| | | } |
| | | |
| | | @Test |
| | | public void testInvalidSubResourceSearchFilterConfigurations() |
| | | 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 expectingReadOnly, |
| | | final boolean expectingSubtreeFlattened, |
| | | 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)); |
| | |
| | | |
| | | allUsersSubResource = (SubResourceCollection)subResources.get("all-users"); |
| | | |
| | | assertThat(allUsersSubResource.isReadOnly()).isEqualTo(expectingReadOnly); |
| | | assertThat(allUsersSubResource.shouldFlattenSubtree()).isEqualTo(expectingSubtreeFlattened); |
| | | 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 { |
| | |
| | | }, |
| | | // 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. |
| | | // from "ou=people,dc=example,dc=com" and working down. It filters out any other |
| | | // structural elements, including organizations, org units, etc. |
| | | "all-users": { |
| | | "type": "collection", |
| | | "dnTemplate": "ou=people,dc=example,dc=com", |
| | |
| | | "dnAttribute": "uid" |
| | | }, |
| | | "isReadOnly": true, |
| | | "flattenSubtree": true |
| | | "flattenSubtree": true, |
| | | "baseSearchFilter": "(objectClass=person)" |
| | | }, |
| | | "groups": { |
| | | "type": "collection", |