001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2014-2015 ForgeRock AS.
015 */
016
017package org.forgerock.jaspi.modules.openid.resolvers.service;
018
019import static org.forgerock.caf.authentication.framework.AuthenticationFramework.LOG;
020
021import java.net.URL;
022import java.security.PublicKey;
023import java.util.concurrent.ConcurrentHashMap;
024import java.util.concurrent.ConcurrentMap;
025
026import org.forgerock.jaspi.modules.openid.exceptions.FailedToLoadJWKException;
027import org.forgerock.jaspi.modules.openid.resolvers.OpenIdResolver;
028import org.forgerock.jaspi.modules.openid.resolvers.OpenIdResolverFactory;
029import org.forgerock.json.jose.utils.KeystoreManager;
030import org.forgerock.json.jose.utils.KeystoreManagerException;
031
032/**
033 * Holds a copy of the current OpenID Resolvers.
034 *
035 * As new resolvers are configured, this class loads up the appropriate verification key and
036 * stores it along with the other information necessary for it to perform its task.
037 *
038 * This service stores {@link org.forgerock.jaspi.modules.openid.resolvers.OpenIdResolver}s against their issuer key,
039 * so the appropriate {@link org.forgerock.jaspi.modules.openid.resolvers.OpenIdResolver} can be looked up.
040 */
041public class OpenIdResolverServiceImpl implements OpenIdResolverService {
042
043    private final ConcurrentMap<String, OpenIdResolver> openIdResolvers = new ConcurrentHashMap<>();
044
045    private final int readTimeout;
046    private final int connTimeout;
047
048    private final OpenIdResolverFactory openIdResolverFactory;
049
050    /**
051     * Constructor for the OpenIdResolverServiceImpl which will use the supplied
052     * read and connection timeouts when communicating over HTTP.
053     *
054     * @param readTimeout HTTP read timeout for resolvers
055     * @param connTimeout HTTP connection timeout for resolvers
056     */
057    public OpenIdResolverServiceImpl(final int readTimeout, final int connTimeout) {
058        this.readTimeout = readTimeout;
059        this.connTimeout = connTimeout;
060        this.openIdResolverFactory = new OpenIdResolverFactory(readTimeout, connTimeout);
061    }
062
063    /**
064     * For tests.
065     *
066     * @param openIdResolverFactory Factory to provide resolvers
067     * @param readTimeout HTTP read timeout for resolvers
068     * @param connTimeout HTTP connection timeout for resolvers
069     */
070    OpenIdResolverServiceImpl(OpenIdResolverFactory openIdResolverFactory, final int readTimeout,
071                              final int connTimeout) {
072        this.readTimeout = readTimeout;
073        this.connTimeout = connTimeout;
074        this.openIdResolverFactory = openIdResolverFactory;
075    }
076
077    /**
078     * Returns the appropriate resolver for the given issuer - if it exists. Otherwise null.
079     *
080     * @param issuer The name of the issuer of the Open Id Connect token to check
081     * @return A resolver which can handle verification of the Open Id Connect token
082     */
083    public OpenIdResolver getResolverForIssuer(final String issuer) {
084        return openIdResolvers.get(issuer);
085    }
086
087    /**
088     * Configures a new Resolver by finding the appropriate public key in the supplied keystore,
089     * and adds it to the Map of current resolvers.
090     *
091     * @param issuer The issuer which provides the Open ID Connect auth token
092     * @param keyAlias The alias under which the public key is stored
093     * @param keystoreLocation location of the keystore file
094     * @param keystoreType type of the keystore file
095     * @param keystorePassword password to enter the keystore
096     * @return true if the resolver was configured successfully, false otherwise
097     */
098    public boolean configureResolverWithKey(final String issuer,
099                                            final String keyAlias, final String keystoreLocation,
100                                            final String keystoreType, final String keystorePassword) {
101
102        try {
103            // Do not need the private key password as we are only ever getting the public key
104            final KeystoreManager keystoreManager = new KeystoreManager(keystoreType, keystoreLocation,
105                    keystorePassword);
106            final PublicKey key = keystoreManager.getPublicKey(keyAlias);
107
108            final OpenIdResolver impl = openIdResolverFactory.createPublicKeyResolver(issuer, key);
109            openIdResolvers.put(issuer, impl);
110        } catch (KeystoreManagerException kme) {
111            LOG.debug("Error accessing the KeystoreManager", kme);
112            return false;
113        } catch (NullPointerException npe) {
114            LOG.debug("No key found in keystore with appropriate alias", npe);
115            return false;
116        }
117
118        return true;
119    }
120
121    /**
122     * Configures a new Resolver by finding the appropriate public key in the supplied keystore,
123     * and adds it to the Map of current resolvers.
124     *
125     * @param issuer The issuer which provides the Open ID Connect auth token
126     * @param sharedSecret The known-to-both-parties secret String
127     * @return true if the resolver was configured successfully, false otherwise
128     */
129    public boolean configureResolverWithSecret(final String issuer, final String sharedSecret) {
130
131        try {
132            final OpenIdResolver impl = openIdResolverFactory.createSharedSecretResolver(issuer, sharedSecret);
133            openIdResolvers.put(issuer, impl);
134        } catch (IllegalArgumentException iae) {
135            LOG.debug("Shared secret must not be null", iae);
136            return false;
137        }
138
139        return true;
140    }
141
142    /**
143     * Configures a new Resolver by setting it up to download public keys from the supplied url.
144     *
145     * @param issuer The issuer which provides the Open ID Connect auth token
146     * @param jwkUrl location from which to determine which public key to use
147     * @return true if the resolver was configured successfully, false otherwise
148     */
149    public boolean configureResolverWithJWK(final String issuer,
150                                            final URL jwkUrl) {
151
152        try {
153            final OpenIdResolver impl = openIdResolverFactory.createJWKResolver(issuer, jwkUrl,
154                    readTimeout, connTimeout);
155            openIdResolvers.put(issuer, impl);
156        } catch (FailedToLoadJWKException e) {
157            LOG.debug("Unable to load JSON Web Keys", e);
158            return false;
159        }
160
161        return true;
162    }
163
164    /**
165     * Configures a new Resolver by setting it up to download public keys from the supplied
166     * well-known Open Id Connect URL.
167     *
168     * @param configUrl location from which to determine which public key to use
169     * @return true if the resolver was configured successfully, false otherwise
170     */
171    public boolean configureResolverWithWellKnownOpenIdConfiguration(final URL configUrl) {
172
173        try {
174            final OpenIdResolver impl = openIdResolverFactory.createFromOpenIDConfigUrl(configUrl);
175            openIdResolvers.put(impl.getIssuer(), impl);
176        } catch (FailedToLoadJWKException e) {
177            LOG.debug("Unable to load JSON Web Keys", e);
178            return false;
179        }
180
181        return true;
182    }
183
184}