Partial fix for OPENDJ-694: Implement HTTP BASIC authentication
* make authorization policy configuration more user friendly and explicit.
1 files added
5 files modified
| | |
| | | import org.forgerock.json.resource.ConnectionFactory; |
| | | import org.forgerock.json.resource.Resources; |
| | | import org.forgerock.json.resource.Router; |
| | | import org.forgerock.opendj.rest2ldap.AuthorizationPolicy; |
| | | import org.forgerock.opendj.rest2ldap.Rest2LDAP; |
| | | import org.forgerock.opendj.rest2ldap.Rest2LDAP.Builder; |
| | | |
| | |
| | | final JsonValue configuration = new JsonValue(content); |
| | | |
| | | // Parse the authorization configuration. |
| | | final AuthorizationPolicy authzPolicy = |
| | | configuration.get("servlet").get("authorizationPolicy").required().asEnum( |
| | | AuthorizationPolicy.class); |
| | | final String proxyAuthzTemplate = |
| | | configuration.get("servlet").get("proxyAuthzIdTemplate").asString(); |
| | | |
| | | // Parse the connection factory if present. |
| | | final String ldapFactoryName = |
| | | configuration.get("servlet").get("ldapConnectionFactory").asString(); |
| | | final org.forgerock.opendj.ldap.ConnectionFactory ldapFactory; |
| | |
| | | for (final String mappingUrl : mappings.keys()) { |
| | | final JsonValue mapping = mappings.get(mappingUrl); |
| | | final CollectionResourceProvider provider = |
| | | Rest2LDAP.builder().connectionFactory(ldapFactory).useProxiedAuthorization( |
| | | proxyAuthzTemplate).configureMapping(mapping).build(); |
| | | Rest2LDAP.builder().connectionFactory(ldapFactory).authorizationPolicy( |
| | | authzPolicy).proxyAuthzIdTemplate(proxyAuthzTemplate) |
| | | .configureMapping(mapping).build(); |
| | | router.addRoute(mappingUrl, provider); |
| | | } |
| | | return Resources.newInternalConnectionFactory(router); |
| | |
| | | "servlet" : { |
| | | // The connection factory which will be used for performing LDAP |
| | | // operations. Pre-authenticated connections passed through from the |
| | | // authentication filter see "reuseAuthenticatedConnection") will be |
| | | // authentication filter (see "reuseAuthenticatedConnection") will be |
| | | // used in preference to this factory. Specifically, a connection |
| | | // factory does not need to be configured if a connection will always |
| | | // be passed on from the filter, which may not always be the case |
| | | // if the filter is configured to use HTTP sessions. |
| | | "ldapConnectionFactory" : "root", |
| | | |
| | | // The AuthzID template which will be used for proxied authorization. If |
| | | // no template is specified then proxied authorization will be disabled. |
| | | // Specifies how LDAP authorization should be performed. The method |
| | | // must be one of: |
| | | // |
| | | // "none" - use connections acquired from the LDAP connection |
| | | // factory. Don't use proxied authorization, and don't |
| | | // use cached pre-authenticated connections, |
| | | // "reuse" - use the connection obtained during LDAP |
| | | // authentication. If no connection was passed through |
| | | // the authorization will fail, |
| | | // "proxy" - use proxied authorization with an authorization ID |
| | | // derived from the "proxyAuthzIdTemplate". Proxied |
| | | // authorization will only be used if there is no |
| | | // pre-authenticated connection available. |
| | | "authorizationPolicy" : "none", |
| | | |
| | | // The AuthzID template which will be used for proxied authorization. |
| | | // The template should contain fields which are expected to be found in |
| | | // the security context create during authentication, e.g. "dn" and "id". |
| | | |
| | | // "proxyAuthzIdTemplate" : "dn:{dn}", |
| | | "proxyAuthzIdTemplate" : "dn:{dn}", |
| | | |
| | | // The REST APIs and their LDAP attribute mappings. |
| | | "mappings" : { |
| New file |
| | |
| | | /* |
| | | * The contents of this file are subject to the terms of the Common Development and |
| | | * Distribution License (the License). You may not use this file except in compliance with the |
| | | * License. |
| | | * |
| | | * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the |
| | | * specific language governing permission and limitations under the License. |
| | | * |
| | | * When distributing Covered Software, include this CDDL Header Notice in each file and include |
| | | * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL |
| | | * Header, with the fields enclosed by brackets [] replaced by your own identifying |
| | | * information: "Portions copyright [year] [name of copyright owner]". |
| | | * |
| | | * Copyright 2013 ForgeRock AS. |
| | | */ |
| | | |
| | | package org.forgerock.opendj.rest2ldap; |
| | | |
| | | /** |
| | | * The policy which should be for performing authorization. |
| | | */ |
| | | public enum AuthorizationPolicy { |
| | | /** |
| | | * Use connections acquired from the LDAP connection factory. Don't use |
| | | * proxied authorization, and don't use cached pre-authenticated |
| | | * connections. |
| | | */ |
| | | NONE, |
| | | |
| | | /** |
| | | * Use the connection obtained during LDAP authentication. If no connection |
| | | * was passed through the authorization will fail. |
| | | */ |
| | | REUSE, |
| | | |
| | | /** |
| | | * Use proxied authorization with an authorization ID derived from the |
| | | * proxied authorization ID template. Proxied authorization will only be |
| | | * used if there is no pre-authenticated connection available. |
| | | */ |
| | | PROXY; |
| | | } |
| | |
| | | final class Config { |
| | | private final ConnectionFactory factory; |
| | | private final DecodeOptions options; |
| | | private final AuthorizationPolicy authzPolicy; |
| | | private final AuthzIdTemplate proxiedAuthzTemplate; |
| | | private final ReadOnUpdatePolicy readOnUpdatePolicy; |
| | | private final Schema schema; |
| | | |
| | | Config(final ConnectionFactory factory, final ReadOnUpdatePolicy readOnUpdatePolicy, |
| | | final AuthzIdTemplate proxiedAuthzTemplate, final Schema schema) { |
| | | final AuthorizationPolicy authzPolicy, final AuthzIdTemplate proxiedAuthzTemplate, |
| | | final Schema schema) { |
| | | this.factory = factory; |
| | | this.readOnUpdatePolicy = readOnUpdatePolicy; |
| | | this.authzPolicy = authzPolicy; |
| | | this.proxiedAuthzTemplate = proxiedAuthzTemplate; |
| | | this.schema = schema; |
| | | this.options = new DecodeOptions().setSchema(schema); |
| | |
| | | } |
| | | |
| | | /** |
| | | * Returns the authorization policy which should be used for performing LDAP |
| | | * operations. |
| | | * |
| | | * @return The authorization policy which should be used for performing LDAP |
| | | * operations. |
| | | */ |
| | | AuthorizationPolicy getAuthorizationPolicy() { |
| | | return authzPolicy; |
| | | } |
| | | |
| | | /** |
| | | * Returns the authorization ID template which should be used when proxied |
| | | * authorization is enabled. |
| | | * |
| | | * @return The authorization ID template which should be used when proxied |
| | | * authorization is enabled, or {@code null} if proxied |
| | | * authorization is disabled. |
| | | * authorization is enabled. |
| | | */ |
| | | AuthzIdTemplate getProxiedAuthorizationTemplate() { |
| | | return proxiedAuthzTemplate; |
| | |
| | | Schema schema() { |
| | | return schema; |
| | | } |
| | | |
| | | /** |
| | | * Returns {@code true} if the proxied authorization should be used for |
| | | * authorizing LDAP requests. |
| | | * |
| | | * @return {@code true} if the proxied authorization should be used for |
| | | * authorizing LDAP requests. |
| | | */ |
| | | boolean useProxiedAuthorization() { |
| | | return proxiedAuthzTemplate != null; |
| | | } |
| | | } |
| | |
| | | Context(final Config config, final ServerContext context) { |
| | | this.config = config; |
| | | this.context = context; |
| | | if (context.containsContext(AuthenticatedConnectionContext.class)) { |
| | | |
| | | // Re-use the pre-authenticated connection if available and the authorization policy allows. |
| | | if (config.getAuthorizationPolicy() != AuthorizationPolicy.NONE |
| | | && context.containsContext(AuthenticatedConnectionContext.class)) { |
| | | final Connection connection = |
| | | context.asContext(AuthenticatedConnectionContext.class).getConnection(); |
| | | this.preAuthenticatedConnection = connection != null ? wrap(connection) : null; |
| | |
| | | * cached connection since cached connections are supposed to have been |
| | | * pre-authenticated and therefore do not require proxied authorization. |
| | | */ |
| | | if (preAuthenticatedConnection == null && config.useProxiedAuthorization()) { |
| | | if (preAuthenticatedConnection == null |
| | | && config.getAuthorizationPolicy() == AuthorizationPolicy.PROXY) { |
| | | if (context.containsContext(SecurityContext.class)) { |
| | | try { |
| | | final SecurityContext securityContext = |
| | |
| | | private ConnectionFactory factory; |
| | | private MVCCStrategy mvccStrategy; |
| | | private NameStrategy nameStrategy; |
| | | private AuthorizationPolicy authzPolicy = AuthorizationPolicy.NONE; |
| | | private AuthzIdTemplate proxiedAuthzTemplate; |
| | | private ReadOnUpdatePolicy readOnUpdatePolicy = CONTROLS; |
| | | private AttributeMapper rootMapper; |
| | |
| | | if (rootMapper == null) { |
| | | throw new IllegalStateException("No mappings provided"); |
| | | } |
| | | if (proxiedAuthzTemplate != null && factory == null) { |
| | | throw new IllegalStateException( |
| | | "No connection factory specified for use with proxied authorization"); |
| | | switch (authzPolicy) { |
| | | case NONE: |
| | | if (factory == null) { |
| | | throw new IllegalStateException( |
| | | "A connection factory must be specified when the authorization policy is 'none'"); |
| | | } |
| | | break; |
| | | case PROXY: |
| | | if (proxiedAuthzTemplate == null) { |
| | | throw new IllegalStateException( |
| | | "Proxied authorization enabled but no template defined"); |
| | | } |
| | | if (factory == null) { |
| | | throw new IllegalStateException( |
| | | "A connection factory must be specified when using proxied authorization"); |
| | | } |
| | | break; |
| | | case REUSE: |
| | | // This is always ok. |
| | | break; |
| | | } |
| | | return new LDAPCollectionResourceProvider(baseDN, rootMapper, nameStrategy, |
| | | mvccStrategy, new Config(factory, readOnUpdatePolicy, proxiedAuthzTemplate, |
| | | schema), additionalLDAPAttributes); |
| | | mvccStrategy, new Config(factory, readOnUpdatePolicy, authzPolicy, |
| | | proxiedAuthzTemplate, schema), additionalLDAPAttributes); |
| | | } |
| | | |
| | | /** |
| | |
| | | return useEtagAttribute(ad(attribute)); |
| | | } |
| | | |
| | | public Builder useProxiedAuthorization(final String template) { |
| | | public Builder authorizationPolicy(final AuthorizationPolicy policy) { |
| | | this.authzPolicy = ensureNotNull(policy); |
| | | return this; |
| | | } |
| | | |
| | | public Builder proxyAuthzIdTemplate(final String template) { |
| | | this.proxiedAuthzTemplate = template != null ? new AuthzIdTemplate(template) : null; |
| | | return this; |
| | | } |