OPENDJ-3015: Oauth2 HTTP client must support HTTPS
* Add support for password file.
* Set JVM as default for TrustManager rather than trustAll.
* Use a common TrustManager & KeyManager
| | |
| | | { |
| | | "security": { |
| | | // Specifies the policy for trusting server certificates exchanged |
| | | // during SSL/StartTLS negotiation. This setting and the following |
| | | // trust policy settings will be ignored if there is no connection |
| | | // security. Acceptable values are: |
| | | // |
| | | // "trustAll" - blindly trust all server certificates |
| | | // "jvm" - only certificates signed by the authorities |
| | | // associated with the host JVM will be accepted (default) |
| | | // "file" - use a file-based trust store for validating |
| | | // certificates. This option requires the following |
| | | // "fileBasedTrustManager*" settings to be configured. |
| | | // |
| | | "trustManager": "jvm", |
| | | |
| | | // File based trust manager configuration (see above). |
| | | "fileBasedTrustManagerType": "JKS", |
| | | "fileBasedTrustManagerFile": "/path/to/truststore", |
| | | "fileBasedTrustManagerPasswordFile": "/path/to/pinfile", |
| | | |
| | | // Specifies the key-manager for authenticating the http client performing |
| | | // the request against the access-token resolver endpoint. Acceptable values are: |
| | | // |
| | | // "jvm" - use the JVM's default keystore for retrieving certificates. (default) |
| | | // "keystore" - use the named key store file for retrieving certificates. |
| | | // "pkcs11" - use a PKCS#11 token for retrieving certificates. |
| | | "keyManager": "jvm", |
| | | |
| | | // Keystore based key manager configuration (see above). |
| | | "keyStoreFile": "/path/to/keystore", |
| | | "keyStorePasswordFile": "/path/to/pinfile", |
| | | "keyStoreFormat": "JKS", |
| | | "keyStoreProvider": "", |
| | | |
| | | // PKCS11 based key manager configuration |
| | | "pkcs11PasswordFile": "/path/to/pinfile" |
| | | }, |
| | | |
| | | // The array of connection factories which will be used by the Rest2LDAP |
| | | // Servlet and authentication filter. |
| | | "ldapConnectionFactories" : { |
| | |
| | | // |
| | | "connectionSecurity" : "none", |
| | | |
| | | // Specifies the policy for trusting server certificates exchanged |
| | | // during SSL/StartTLS negotiation. This setting and the following |
| | | // trust policy settings will be ignored if there is no connection |
| | | // security. Acceptable values are: |
| | | // |
| | | // "trustAll" - blindly trust all server certificates (default) |
| | | // "jvm" - only certificates signed by the authorities |
| | | // associated with the host JVM will be accepted |
| | | // "file" - use a file-based trust store for validating |
| | | // certificates. This option requires the following |
| | | // "fileBasedTrustManager*" settings to be configured. |
| | | // |
| | | "trustManager" : "trustAll", |
| | | |
| | | // File based trust manager configuration (see above). |
| | | "fileBasedTrustManagerType" : "JKS", |
| | | "fileBasedTrustManagerFile" : "/path/to/truststore", |
| | | "fileBasedTrustManagerPassword" : "password", |
| | | // This alias points at an existing certificate that is used for SSL authentication for secure |
| | | // communication between this gateway and the remote LDAP server. |
| | | "sslCertAlias": "client-cert", |
| | | |
| | | // Re-usable pool of 24 connections per server. |
| | | "connectionPoolSize" : 24, |
| | |
| | | "heartBeatTimeoutMilliSeconds" : 500, |
| | | |
| | | // The preferred load-balancing pool. |
| | | "primaryLDAPServers" : [ |
| | | { |
| | | "primaryLDAPServers": [{ |
| | | "hostname" : "localhost", |
| | | "port" : 1389 |
| | | } |
| | | ], |
| | | }], |
| | | // The fail-over load-balancing pool (optional). |
| | | "secondaryLDAPServers" : [ |
| | | // Empty. |
| | |
| | | // This attribute is required and must have a string syntax. |
| | | "endpointURL": "http://openam.example.com:8080/openam/oauth2/tokeninfo", |
| | | |
| | | // This alias points at an existing certificate that is used for SSL authentication for secure |
| | | // communication between this gateway and the OpenAM access-token resolver. |
| | | "sslCertAlias": "client-cert", |
| | | |
| | | // The default authzIdTemplate demonstrates how an authorization DN may be constructed |
| | | // from the "uid" field in the following example OpenAM tokeninfo response: |
| | | // { |
| | |
| | | // This attribute is required and must have a string syntax. |
| | | "endpointURL": "http://openam.example.com:8080/openam/oauth2/myrealm/introspect", |
| | | |
| | | // This alias points at an existing certificate that is used for SSL authentication for secure |
| | | // communication between this gateway and the introspection access-token resolver. |
| | | "sslCertAlias": "client-cert", |
| | | |
| | | // Token introspect endpoint requires authentication. |
| | | // It should support HTTP basic authorization (a base64-encoded string of clientId:clientSecret) |
| | | // These attributes are mandatory. |
| | |
| | | "strategy" : "clientDNNaming", |
| | | "dnAttribute" : "uid" |
| | | }, |
| | | "additionalLDAPAttributes" : [ |
| | | { |
| | | "additionalLDAPAttributes": [{ |
| | | "type" : "objectClass", |
| | | "values" : [ |
| | | "top", |
| | |
| | | "organizationalPerson", |
| | | "inetOrgPerson" |
| | | ] |
| | | } |
| | | ], |
| | | }], |
| | | "attributes" : { |
| | | "schemas" : { "constant" : [ "urn:scim:schemas:core:1.0" ] }, |
| | | "_id" : { "simple" : { "ldapAttribute" : "uid", "isSingleValued" : true, "isRequired" : true, "writability" : "createOnly" } }, |
| | | "_rev" : { "simple" : { "ldapAttribute" : "etag", "isSingleValued" : true, "writability" : "readOnly" } }, |
| | | "userName" : { "simple" : { "ldapAttribute" : "mail", "isSingleValued" : true, "writability" : "readOnly" } }, |
| | | "displayName" : { "simple" : { "ldapAttribute" : "cn", "isSingleValued" : true, "isRequired" : true } }, |
| | | "name" : { "object" : { |
| | | "givenName" : { "simple" : { "ldapAttribute" : "givenName", "isSingleValued" : true } }, |
| | | "familyName" : { "simple" : { "ldapAttribute" : "sn", "isSingleValued" : true, "isRequired" : true } } |
| | | } }, |
| | | "manager" : { "reference" : { |
| | | "schemas": { |
| | | "constant": ["urn:scim:schemas:core:1.0"] |
| | | }, |
| | | "_id": { |
| | | "simple": { |
| | | "ldapAttribute": "uid", |
| | | "isSingleValued": true, |
| | | "isRequired": true, |
| | | "writability": "createOnly" |
| | | } |
| | | }, |
| | | "_rev": { |
| | | "simple": { |
| | | "ldapAttribute": "etag", |
| | | "isSingleValued": true, |
| | | "writability": "readOnly" |
| | | } |
| | | }, |
| | | "userName": { |
| | | "simple": { |
| | | "ldapAttribute": "mail", |
| | | "isSingleValued": true, |
| | | "writability": "readOnly" |
| | | } |
| | | }, |
| | | "displayName": { |
| | | "simple": { |
| | | "ldapAttribute": "cn", |
| | | "isSingleValued": true, |
| | | "isRequired": true |
| | | } |
| | | }, |
| | | "name": { |
| | | "object": { |
| | | "givenName": { |
| | | "simple": { |
| | | "ldapAttribute": "givenName", |
| | | "isSingleValued": true |
| | | } |
| | | }, |
| | | "familyName": { |
| | | "simple": { |
| | | "ldapAttribute": "sn", |
| | | "isSingleValued": true, |
| | | "isRequired": true |
| | | } |
| | | } |
| | | } |
| | | }, |
| | | "manager": { |
| | | "reference": { |
| | | "ldapAttribute" : "manager", |
| | | "baseDN" : "ou=people,dc=example,dc=com", |
| | | "primaryKey" : "uid", |
| | | "mapper" : { "object" : { |
| | | "_id" : { "simple" : { "ldapAttribute" : "uid", "isSingleValued" : true, "isRequired" : true } }, |
| | | "displayName" : { "simple" : { "ldapAttribute" : "cn", "isSingleValued" : true, "writability" : "readOnlyDiscardWrites" } } |
| | | } } |
| | | } }, |
| | | "groups" : { "reference" : { |
| | | "mapper": { |
| | | "object": { |
| | | "_id": { |
| | | "simple": { |
| | | "ldapAttribute": "uid", |
| | | "isSingleValued": true, |
| | | "isRequired": true |
| | | } |
| | | }, |
| | | "displayName": { |
| | | "simple": { |
| | | "ldapAttribute": "cn", |
| | | "isSingleValued": true, |
| | | "writability": "readOnlyDiscardWrites" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | }, |
| | | "groups": { |
| | | "reference": { |
| | | "ldapAttribute" : "isMemberOf", |
| | | "baseDN" : "ou=groups,dc=example,dc=com", |
| | | "writability" : "readOnly", |
| | | "primaryKey" : "cn", |
| | | "mapper" : { "object" : { |
| | | "_id" : { "simple" : { "ldapAttribute" : "cn", "isSingleValued" : true } } |
| | | } } |
| | | } }, |
| | | "contactInformation" : { "object" : { |
| | | "telephoneNumber" : { "simple" : { "ldapAttribute" : "telephoneNumber", "isSingleValued" : true } }, |
| | | "emailAddress" : { "simple" : { "ldapAttribute" : "mail", "isSingleValued" : true } } |
| | | } }, |
| | | "meta" : { "object" : { |
| | | "created" : { "simple" : { "ldapAttribute" : "createTimestamp", "isSingleValued" : true, "writability" : "readOnly" } }, |
| | | "lastModified" : { "simple" : { "ldapAttribute" : "modifyTimestamp", "isSingleValued" : true, "writability" : "readOnly" } } |
| | | } } |
| | | "mapper": { |
| | | "object": { |
| | | "_id": { |
| | | "simple": { |
| | | "ldapAttribute": "cn", |
| | | "isSingleValued": true |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | }, |
| | | "contactInformation": { |
| | | "object": { |
| | | "telephoneNumber": { |
| | | "simple": { |
| | | "ldapAttribute": "telephoneNumber", |
| | | "isSingleValued": true |
| | | } |
| | | }, |
| | | "emailAddress": { |
| | | "simple": { |
| | | "ldapAttribute": "mail", |
| | | "isSingleValued": true |
| | | } |
| | | } |
| | | } |
| | | }, |
| | | "meta": { |
| | | "object": { |
| | | "created": { |
| | | "simple": { |
| | | "ldapAttribute": "createTimestamp", |
| | | "isSingleValued": true, |
| | | "writability": "readOnly" |
| | | } |
| | | }, |
| | | "lastModified": { |
| | | "simple": { |
| | | "ldapAttribute": "modifyTimestamp", |
| | | "isSingleValued": true, |
| | | "writability": "readOnly" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | }, |
| | | "/groups" : { |
| | |
| | | "strategy" : "clientDNNaming", |
| | | "dnAttribute" : "cn" |
| | | }, |
| | | "additionalLDAPAttributes" : [ |
| | | { |
| | | "additionalLDAPAttributes": [{ |
| | | "type" : "objectClass", |
| | | "values" : [ |
| | | "top", |
| | | "groupOfUniqueNames" |
| | | ] |
| | | } |
| | | ], |
| | | }], |
| | | "attributes" : { |
| | | "schemas" : { "constant" : [ "urn:scim:schemas:core:1.0" ] }, |
| | | "_id" : { "simple" : { "ldapAttribute" : "cn", "isSingleValued" : true, "isRequired" : true, "writability" : "createOnly" } }, |
| | | "_rev" : { "simple" : { "ldapAttribute" : "etag", "isSingleValued" : true, "writability" : "readOnly" } }, |
| | | "displayName" : { "simple" : { "ldapAttribute" : "cn", "isSingleValued" : true, "isRequired" : true, "writability" : "readOnly" } }, |
| | | "members" : { "reference" : { |
| | | "schemas": { |
| | | "constant": ["urn:scim:schemas:core:1.0"] |
| | | }, |
| | | "_id": { |
| | | "simple": { |
| | | "ldapAttribute": "cn", |
| | | "isSingleValued": true, |
| | | "isRequired": true, |
| | | "writability": "createOnly" |
| | | } |
| | | }, |
| | | "_rev": { |
| | | "simple": { |
| | | "ldapAttribute": "etag", |
| | | "isSingleValued": true, |
| | | "writability": "readOnly" |
| | | } |
| | | }, |
| | | "displayName": { |
| | | "simple": { |
| | | "ldapAttribute": "cn", |
| | | "isSingleValued": true, |
| | | "isRequired": true, |
| | | "writability": "readOnly" |
| | | } |
| | | }, |
| | | "members": { |
| | | "reference": { |
| | | "ldapAttribute" : "uniqueMember", |
| | | "baseDN" : "dc=example,dc=com", |
| | | "primaryKey" : "uid", |
| | | "mapper" : { "object" : { |
| | | "_id" : { "simple" : { "ldapAttribute" : "uid", "isSingleValued" : true, "isRequired" : true } }, |
| | | "displayName" : { "simple" : { "ldapAttribute" : "cn", "isSingleValued" : true, "writability" : "readOnlyDiscardWrites" } } |
| | | } } |
| | | } }, |
| | | "meta" : { "object" : { |
| | | "created" : { "simple" : { "ldapAttribute" : "createTimestamp", "isSingleValued" : true, "writability" : "readOnly" } }, |
| | | "lastModified" : { "simple" : { "ldapAttribute" : "modifyTimestamp", "isSingleValued" : true, "writability" : "readOnly" } } |
| | | } } |
| | | "mapper": { |
| | | "object": { |
| | | "_id": { |
| | | "simple": { |
| | | "ldapAttribute": "uid", |
| | | "isSingleValued": true, |
| | | "isRequired": true |
| | | } |
| | | }, |
| | | "displayName": { |
| | | "simple": { |
| | | "ldapAttribute": "cn", |
| | | "isSingleValued": true, |
| | | "writability": "readOnlyDiscardWrites" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | } |
| | | }, |
| | | "meta": { |
| | | "object": { |
| | | "created": { |
| | | "simple": { |
| | | "ldapAttribute": "createTimestamp", |
| | | "isSingleValued": true, |
| | | "writability": "readOnly" |
| | | } |
| | | }, |
| | | "lastModified": { |
| | | "simple": { |
| | | "ldapAttribute": "modifyTimestamp", |
| | | "isSingleValued": true, |
| | | "writability": "readOnly" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | |
| | | import static org.forgerock.opendj.rest2ldap.Utils.newLocalizedIllegalArgumentException; |
| | | import static org.forgerock.opendj.rest2ldap.Utils.newJsonValueException; |
| | | import static org.forgerock.util.time.Duration.*; |
| | | import static org.forgerock.opendj.ldap.KeyManagers.useSingleCertificate; |
| | | |
| | | import java.io.IOException; |
| | | import java.security.GeneralSecurityException; |
| | | import java.util.ArrayList; |
| | | import java.util.LinkedHashMap; |
| | |
| | | import java.util.Set; |
| | | import java.util.concurrent.TimeUnit; |
| | | |
| | | import javax.net.ssl.TrustManager; |
| | | import javax.net.ssl.X509KeyManager; |
| | | |
| | | import org.forgerock.json.JsonValue; |
| | | import org.forgerock.json.resource.CollectionResourceProvider; |
| | | import org.forgerock.json.resource.ResourceException; |
| | |
| | | import org.forgerock.opendj.ldap.SSLContextBuilder; |
| | | import org.forgerock.opendj.ldap.SearchScope; |
| | | import org.forgerock.opendj.ldap.TimeoutResultException; |
| | | import org.forgerock.opendj.ldap.TrustManagers; |
| | | import org.forgerock.opendj.ldap.requests.BindRequest; |
| | | import org.forgerock.opendj.ldap.requests.Requests; |
| | | import org.forgerock.opendj.ldap.requests.SearchRequest; |
| | |
| | | * Specifies the mechanism which should be used for trusting certificates |
| | | * presented by the LDAP server. |
| | | */ |
| | | private enum TrustManagerType { |
| | | enum TrustManagerType { |
| | | TRUSTALL, JVM, FILE |
| | | } |
| | | |
| | | /** |
| | | * Specifies the mechanism which manage which X509 certificate-based key pairs should be used to authenticate the |
| | | * local side of a secure socket. |
| | | */ |
| | | enum KeyManagerType { |
| | | JVM, KEYSTORE, PKCS11 |
| | | } |
| | | |
| | | /** A builder for incrementally constructing LDAP resource collections. */ |
| | | public static final class Builder { |
| | | private final List<Attribute> additionalLDAPAttributes = new LinkedList<>(); |
| | |
| | | * The JSON configuration. |
| | | * @param name |
| | | * The name of the connection factory configuration to be parsed. |
| | | * @param trustManager |
| | | * The trust manager to use for secure connection. Can be {@code null} |
| | | * to use the default JVM trust manager. |
| | | * @param keyManager |
| | | * The key manager to use for secure connection. Can be {@code null} |
| | | * to use the default JVM key manager. |
| | | * @param providerClassLoader |
| | | * The {@link ClassLoader} used to fetch the |
| | | * {@link org.forgerock.opendj.ldap.spi.TransportProvider}. |
| | |
| | | */ |
| | | public static ConnectionFactory configureConnectionFactory(final JsonValue configuration, |
| | | final String name, |
| | | final TrustManager trustManager, |
| | | final X509KeyManager keyManager, |
| | | final ClassLoader providerClassLoader) { |
| | | final JsonValue normalizedConfiguration = |
| | | normalizeConnectionFactory(configuration, name, 0); |
| | | return configureConnectionFactory(normalizedConfiguration, providerClassLoader); |
| | | return configureConnectionFactory(normalizedConfiguration, trustManager, keyManager, providerClassLoader); |
| | | } |
| | | |
| | | /** |
| | |
| | | * The JSON configuration. |
| | | * @param name |
| | | * The name of the connection factory configuration to be parsed. |
| | | * @param trustManager |
| | | * The trust manager to use for secure connection. Can be {@code null} |
| | | * to use the default JVM trust manager. |
| | | * @param keyManager |
| | | * The key manager to use for secure connection. Can be {@code null} |
| | | * to use the default JVM key manager. |
| | | * @return A new connection factory using the provided JSON configuration. |
| | | * @throws IllegalArgumentException |
| | | * If the configuration is invalid. |
| | | */ |
| | | public static ConnectionFactory configureConnectionFactory(final JsonValue configuration, |
| | | final String name) { |
| | | return configureConnectionFactory(configuration, name, null); |
| | | final String name, final TrustManager trustManager, final X509KeyManager keyManager) { |
| | | return configureConnectionFactory(configuration, name, trustManager, keyManager, null); |
| | | } |
| | | |
| | | /** |
| | |
| | | } |
| | | |
| | | private static ConnectionFactory configureConnectionFactory(final JsonValue configuration, |
| | | final TrustManager trustManager, |
| | | final X509KeyManager keyManager, |
| | | final ClassLoader providerClassLoader) { |
| | | final long heartBeatIntervalSeconds = configuration.get("heartBeatIntervalSeconds").defaultTo(30L).asLong(); |
| | | final Duration heartBeatInterval = duration(Math.max(heartBeatIntervalSeconds, 1L), TimeUnit.SECONDS); |
| | |
| | | try { |
| | | // Configure SSL. |
| | | final SSLContextBuilder builder = new SSLContextBuilder(); |
| | | |
| | | // Parse trust store configuration. |
| | | final TrustManagerType trustManagerType = |
| | | configuration.get("trustManager").defaultTo(TrustManagerType.TRUSTALL) |
| | | .asEnum(TrustManagerType.class); |
| | | switch (trustManagerType) { |
| | | case TRUSTALL: |
| | | builder.setTrustManager(TrustManagers.trustAll()); |
| | | break; |
| | | case JVM: |
| | | // Do nothing: JVM trust manager is the default. |
| | | break; |
| | | case FILE: |
| | | final String fileName = |
| | | configuration.get("fileBasedTrustManagerFile").required().asString(); |
| | | final String password = |
| | | configuration.get("fileBasedTrustManagerPassword").asString(); |
| | | final String type = configuration.get("fileBasedTrustManagerType").asString(); |
| | | builder.setTrustManager(TrustManagers.checkUsingTrustStore(fileName, |
| | | password != null ? password.toCharArray() : null, type)); |
| | | break; |
| | | } |
| | | builder.setTrustManager(trustManager); |
| | | final String sslCertAlias = configuration.get("sslCertAlias").asString(); |
| | | builder.setKeyManager(sslCertAlias != null |
| | | ? useSingleCertificate(sslCertAlias, keyManager) |
| | | : keyManager); |
| | | options.set(SSL_CONTEXT, builder.getSSLContext()); |
| | | options.set(SSL_USE_STARTTLS, |
| | | connectionSecurity == ConnectionSecurity.STARTTLS); |
| | | } catch (GeneralSecurityException | IOException e) { |
| | | options.set(SSL_USE_STARTTLS, connectionSecurity == ConnectionSecurity.STARTTLS); |
| | | } catch (GeneralSecurityException e) { |
| | | // Rethrow as unchecked exception. |
| | | throw new IllegalArgumentException(e); |
| | | } |
| | |
| | | package org.forgerock.opendj.rest2ldap; |
| | | |
| | | import static org.forgerock.opendj.rest2ldap.Rest2ldapMessages.*; |
| | | import static org.forgerock.http.handler.HttpClientHandler.*; |
| | | import static org.forgerock.opendj.ldap.KeyManagers.*; |
| | | import static org.forgerock.opendj.ldap.TrustManagers.checkUsingTrustStore; |
| | | import static org.forgerock.opendj.ldap.TrustManagers.trustAll; |
| | | import static org.forgerock.http.util.Json.readJsonLenient; |
| | | import static org.forgerock.json.JsonValueFunctions.duration; |
| | | import static org.forgerock.json.JsonValueFunctions.enumConstant; |
| | |
| | | import static org.forgerock.util.Reject.checkNotNull; |
| | | import static org.forgerock.util.Utils.closeSilently; |
| | | import static org.forgerock.util.Utils.joinAsString; |
| | | import static org.forgerock.opendj.rest2ldap.Utils.readPasswordFromFile; |
| | | |
| | | import java.io.IOException; |
| | | import java.io.InputStream; |
| | | import java.net.URI; |
| | | import java.net.URISyntaxException; |
| | | import java.net.URL; |
| | | import java.security.GeneralSecurityException; |
| | | import java.util.ArrayList; |
| | | import java.util.HashMap; |
| | | import java.util.List; |
| | |
| | | import java.util.concurrent.Executors; |
| | | import java.util.concurrent.ScheduledExecutorService; |
| | | |
| | | import javax.net.ssl.KeyManager; |
| | | import javax.net.ssl.TrustManager; |
| | | import javax.net.ssl.X509KeyManager; |
| | | |
| | | import org.forgerock.openig.oauth2.AccessTokenInfo; |
| | | import org.forgerock.openig.oauth2.AccessTokenException; |
| | | import org.forgerock.openig.oauth2.AccessTokenResolver; |
| | |
| | | import org.forgerock.opendj.ldap.DN; |
| | | import org.forgerock.opendj.ldap.SearchScope; |
| | | import org.forgerock.opendj.ldap.schema.Schema; |
| | | import org.forgerock.opendj.rest2ldap.Rest2LDAP.KeyManagerType; |
| | | import org.forgerock.opendj.rest2ldap.Rest2LDAP.TrustManagerType; |
| | | import org.forgerock.opendj.rest2ldap.authz.AuthenticationStrategy; |
| | | import org.forgerock.opendj.rest2ldap.authz.ConditionalFilters.ConditionalFilter; |
| | | import org.forgerock.services.context.SecurityContext; |
| | | import org.forgerock.util.Factory; |
| | | import org.forgerock.util.Function; |
| | | import org.forgerock.util.Options; |
| | | import org.forgerock.util.Pair; |
| | | import org.forgerock.util.PerItemEvictionStrategyCache; |
| | | import org.forgerock.util.annotations.VisibleForTesting; |
| | |
| | | /** Used for token caching. */ |
| | | private ScheduledExecutorService executorService; |
| | | |
| | | private TrustManager trustManager; |
| | | private X509KeyManager keyManager; |
| | | |
| | | /** Define the method which should be used to resolve an OAuth2 access token. */ |
| | | private enum OAuth2ResolverType { |
| | | RFC7662, OPENAM, CTS, FILE; |
| | |
| | | try { |
| | | final JsonValue configuration = readJson(configurationUrl); |
| | | executorService = Executors.newSingleThreadScheduledExecutor(); |
| | | configureSecurity(configuration.get("security")); |
| | | configureConnectionFactories(configuration.get("ldapConnectionFactories")); |
| | | return Handlers.chainOf( |
| | | CrestHttp.newHttpHandler(configureRest2Ldap(configuration)), |
| | |
| | | return router; |
| | | } |
| | | |
| | | private void configureSecurity(final JsonValue configuration) { |
| | | try { |
| | | trustManager = configureTrustManager(configuration, TrustManagerType.JVM); |
| | | } catch (GeneralSecurityException | IOException e) { |
| | | throw new IllegalArgumentException(ERR_CONFIG_INVALID_TRUST_MANAGER |
| | | .get(configuration.getPointer(), e.getLocalizedMessage()).toString(), e); |
| | | } |
| | | |
| | | try { |
| | | keyManager = configureKeyManager(configuration, KeyManagerType.JVM); |
| | | } catch (GeneralSecurityException | IOException e) { |
| | | throw new IllegalArgumentException(ERR_CONFIG_INVALID_KEY_MANAGER |
| | | .get(configuration.getPointer(), e.getLocalizedMessage()).toString(), e); |
| | | } |
| | | } |
| | | |
| | | private TrustManager configureTrustManager(JsonValue config, TrustManagerType defaultIfMissing) |
| | | throws GeneralSecurityException, IOException { |
| | | // Parse trust store configuration. |
| | | final TrustManagerType trustManagerType = |
| | | config.get("trustManager").defaultTo(defaultIfMissing).as(enumConstant(TrustManagerType.class)); |
| | | switch (trustManagerType) { |
| | | case TRUSTALL: |
| | | return trustAll(); |
| | | case JVM: |
| | | return null; |
| | | case FILE: |
| | | final String fileName = config.get("fileBasedTrustManagerFile").required().asString(); |
| | | final String passwordFile = config.get("fileBasedTrustManagerPasswordFile").asString(); |
| | | final String password = passwordFile != null |
| | | ? readPasswordFromFile(passwordFile) |
| | | : config.get("fileBasedTrustManagerPassword").asString(); |
| | | final String type = config.get("fileBasedTrustManagerType").asString(); |
| | | return checkUsingTrustStore(fileName, password != null ? password.toCharArray() : null, type); |
| | | default: |
| | | throw new IllegalArgumentException("Unsupported trust-manager type: " + trustManagerType); |
| | | } |
| | | } |
| | | |
| | | private X509KeyManager configureKeyManager(JsonValue config, KeyManagerType defaultIfMissing) |
| | | throws GeneralSecurityException, IOException { |
| | | // Parse trust store configuration. |
| | | final KeyManagerType keyManagerType = config.get("keyManager").defaultTo(defaultIfMissing) |
| | | .as(enumConstant(KeyManagerType.class)); |
| | | switch (keyManagerType) { |
| | | case JVM: |
| | | return useJvmDefaultKeyStore(); |
| | | case KEYSTORE: |
| | | final String fileName = config.get("keyStoreFile").required().asString(); |
| | | final String passwordFile = config.get("keyStorePasswordFile").asString(); |
| | | final String password = passwordFile != null |
| | | ? readPasswordFromFile(passwordFile) |
| | | : config.get("keyStorePassword").asString(); |
| | | final String format = config.get("keyStoreFormat").asString(); |
| | | final String provider = config.get("keyStoreProvider").asString(); |
| | | return useKeyStoreFile(fileName, password != null ? password.toCharArray() : null, format, provider); |
| | | case PKCS11: |
| | | final String pkcs11PasswordFile = config.get("pkcs11PasswordFile").asString(); |
| | | return usePKCS11Token(pkcs11PasswordFile != null |
| | | ? readPasswordFromFile(pkcs11PasswordFile).toCharArray() |
| | | : null); |
| | | default: |
| | | throw new IllegalArgumentException("Unsupported key-manager type: " + keyManagerType); |
| | | } |
| | | } |
| | | |
| | | private void configureConnectionFactories(final JsonValue config) { |
| | | connectionFactories.clear(); |
| | | for (String name : config.keys()) { |
| | | connectionFactories.put(name, configureConnectionFactory(config, name)); |
| | | connectionFactories.put(name, configureConnectionFactory(config, name, trustManager, keyManager)); |
| | | } |
| | | } |
| | | |
| | |
| | | case RFC7662: |
| | | return parseRfc7662Resolver(configuration); |
| | | case OPENAM: |
| | | return new OpenAmAccessTokenResolver(new HttpClientHandler(), |
| | | final JsonValue openAm = configuration.get("openam"); |
| | | return new OpenAmAccessTokenResolver(newHttpClientHandler(openAm), |
| | | TimeService.SYSTEM, |
| | | configuration.get("openam").get("endpointURL").required().asString()); |
| | | openAm.get("endpointURL").required().asString()); |
| | | case CTS: |
| | | final JsonValue cts = configuration.get("cts").required(); |
| | | return newCtsAccessTokenResolver( |
| | |
| | | final JsonValue rfc7662 = configuration.get("rfc7662").required(); |
| | | final String introspectionEndPointURL = rfc7662.get("endpointURL").required().asString(); |
| | | try { |
| | | return newRfc7662AccessTokenResolver(new HttpClientHandler(), |
| | | return newRfc7662AccessTokenResolver(newHttpClientHandler(rfc7662), |
| | | new URI(introspectionEndPointURL), |
| | | rfc7662.get("clientId").required().asString(), |
| | | rfc7662.get("clientSecret").required().asString()); |
| | |
| | | } |
| | | } |
| | | |
| | | private HttpClientHandler newHttpClientHandler(final JsonValue config) throws HttpApplicationException { |
| | | final Options httpOptions = Options.defaultOptions(); |
| | | if (trustManager != null) { |
| | | httpOptions.set(OPTION_TRUST_MANAGERS, new TrustManager[] { trustManager }); |
| | | } |
| | | if (keyManager != null) { |
| | | final String keyAlias = config.get("sslCertAlias").asString(); |
| | | httpOptions.set(OPTION_KEY_MANAGERS, |
| | | new KeyManager[] { keyAlias != null ? useSingleCertificate(keyAlias, keyManager) : keyManager }); |
| | | } |
| | | return new HttpClientHandler(httpOptions); |
| | | } |
| | | |
| | | private Duration parseCacheExpiration(final JsonValue expirationJson) { |
| | | try { |
| | | final Duration expiration = expirationJson.as(duration()); |
| | |
| | | import static org.forgerock.opendj.ldap.schema.CoreSchema.getGeneralizedTimeSyntax; |
| | | import static org.forgerock.opendj.ldap.schema.CoreSchema.getIntegerSyntax; |
| | | |
| | | import java.io.BufferedReader; |
| | | import java.io.File; |
| | | import java.io.FileReader; |
| | | import java.io.IOException; |
| | | import java.util.ArrayList; |
| | | import java.util.Collection; |
| | | import java.util.Collections; |
| | |
| | | } |
| | | }; |
| | | |
| | | static String readPasswordFromFile(String fileName) throws IOException { |
| | | try (final BufferedReader reader = new BufferedReader(new FileReader(new File(fileName)))) { |
| | | return reader.readLine(); |
| | | } |
| | | } |
| | | |
| | | static Object attributeToJson(final Attribute a) { |
| | | final Function<ByteString, Object, NeverThrowsException> f = byteStringToJson(a.getAttributeDescription()); |
| | |
| | | ERR_RUNTIME_EXCEPTION_61=A runtime exception occurred wile processing the request '%s': '%s' |
| | | ERR_PASSWORD_MODIFY_REQUEST_IS_INVALID_62=The password modify request has been rejected because it is invalid. \ |
| | | A password modify request may contain two string valued fields 'oldPassword' and 'newPassword' |
| | | ERR_CONFIG_INVALID_TRUST_MANAGER_63=The trust-manager defined in '%s' is invalid: %s |
| | | ERR_CONFIG_INVALID_KEY_MANAGER_64=The key-manager defined in '%s' is invalid: %s |