All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.helidon.security.jwt.jwk.JwkKeys Maven / Gradle / Ivy

There is a newer version: 4.1.6
Show newest version
/*
 * Copyright (c) 2018, 2024 Oracle and/or its affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.helidon.security.jwt.jwk;

import java.io.IOException;
import java.io.InputStream;
import java.lang.System.Logger.Level;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.charset.UnsupportedCharsetException;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

import io.helidon.common.NativeImageHelper;
import io.helidon.common.configurable.Resource;

import jakarta.json.Json;
import jakarta.json.JsonArray;
import jakarta.json.JsonObject;
import jakarta.json.JsonReaderFactory;

/**
 * A representation of the JSON web keys document - a map of key ids to corresponding web keys.
 * The keys may be of different types and algorithms.
 *
 * Example (as used in unit test):
 * 
 * JwkKeys keys = JwkKeys.builder()
 * .resource("jwk_data.json")
 * .build();
 *
 * Optional<Jwk> key = keys
 * .forKeyId("cc34c0a0-bd5a-4a3c-a50d-a2a7db7643df");
 * 
*/ public final class JwkKeys { private static final System.Logger LOGGER = System.getLogger(JwkKeys.class.getName()); private static final JsonReaderFactory JSON = Json.createReaderFactory(Collections.emptyMap()); private final Map keyMap = new HashMap<>(); private final List noKeyIdKeys = new LinkedList<>(); private static final boolean AUTOMATIC_CHARSET_DETECTION; // Workaround for https://github.com/eclipse-ee4j/parsson/issues/121 static { boolean utf32Available = false; try { Charset.forName("UTF-32LE"); Charset.forName("UTF-32BE"); utf32Available = true; } catch (UnsupportedCharsetException e) { if (NativeImageHelper.isNativeImage()) { LOGGER.log(Level.TRACE, "Automatic JSON unicode detection not available." + " Add -H:+AddAllCharsets to build your native image with UTF-32 support.", e); } else { LOGGER.log(Level.TRACE, "Automatic JSON unicode detection not available.", e); } } AUTOMATIC_CHARSET_DETECTION = utf32Available; } private JwkKeys(Builder builder) { this.keyMap.putAll(builder.keyMap); this.noKeyIdKeys.addAll(builder.noKeyIdKeys); } /** * Create a new builder for this class. * * @return builder instance */ public static Builder builder() { return new Builder(); } /** * Create Jwk keys from its JSON representation. * * @param json json with jwk keys * @return keys set up from the provided json */ public static JwkKeys create(JsonObject json) { return builder().json(json).build(); } /** * Get a JWK for defined key id if present. * * @param keyId keyId of the key to obtain from this keys * @return Jwk if present */ public Optional forKeyId(String keyId) { return Optional.ofNullable(keyMap.get(keyId)); } /** * List of keys in this instance. * * @return all keys configured */ public List keys() { List result = new LinkedList<>(); result.addAll(noKeyIdKeys); result.addAll(keyMap.values()); return result; } /** * Builder of {@link JwkKeys}. */ public static final class Builder implements io.helidon.common.Builder { private final List noKeyIdKeys = new LinkedList<>(); private final Map keyMap = new HashMap<>(); private Builder() { } /** * Build a new keys instance. * * @return JwkKeys created from this builder */ @Override public JwkKeys build() { return new JwkKeys(this); } /** * Add a new JWK to this keys. * * @param key the JWK instance * @return updated builder instance */ public Builder addKey(Jwk key) { Objects.requireNonNull(key, "Key must not be null"); if (null == key.keyId()) { noKeyIdKeys.add(key); } else { keyMap.put(key.keyId(), key); } return this; } /** * Load keys from a resource (must point to JSON text content). * * @param resource the resource with JSON data (file, classpath, URI etc.) * @return updated builder instance * @throws NullPointerException in case the path is null */ public Builder resource(Resource resource) { Objects.requireNonNull(resource, "Json resource must not be null"); try (InputStream is = resource.stream()) { JsonObject jsonObject = AUTOMATIC_CHARSET_DETECTION ? JSON.createReader(is).readObject() : JSON.createReader(is, StandardCharsets.UTF_8).readObject(); addKeys(jsonObject); } catch (IOException e) { LOGGER.log(Level.WARNING, "Failed to close input stream on resource: " + resource); } return this; } /** * Load keys from JSON. * * @param json the JSON data * @return updated builder instance */ public Builder json(JsonObject json) { addKeys(json); return this; } private void addKeys(JsonObject jsonObject) { JsonArray keyArray = jsonObject.getJsonArray("keys"); keyArray.forEach(it -> { JsonObject aKey = (JsonObject) it; try { addKey(Jwk.create(aKey)); } catch (Exception e) { LOGGER.log(Level.WARNING, "Could not process a key from JWK JSON, this key will not be available", e); } }); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy