json patcher rmannibucau/json-patch-jwt-payload
authorRomain Manni-Bucau <rmannibucau@gmail.com>
Tue, 19 Jan 2021 17:08:19 +0000 (18:08 +0100)
committerRomain Manni-Bucau <rmannibucau@gmail.com>
Tue, 19 Jan 2021 17:08:19 +0000 (18:08 +0100)
src/main/java/org/apache/geronimo/microprofile/impl/jwtauth/jwt/JwtParser.java
src/main/java/org/apache/geronimo/microprofile/impl/jwtauth/jwt/JwtPatcher.java [new file with mode: 0644]

index 3b922dff058870c797555c349feb9d41c77e4b00..9e80a7c893afce134c7a2e57470ec13003ae6519 100644 (file)
@@ -54,6 +54,9 @@ public class JwtParser {
     @Inject
     private GeronimoJwtAuthExtension extension;
 
     @Inject
     private GeronimoJwtAuthExtension extension;
 
+    @Inject
+    private JwtPatcher patcher;
+
     private JsonReaderFactory readerFactory;
 
     private String defaultKid;
     private JsonReaderFactory readerFactory;
 
     private String defaultKid;
@@ -86,11 +89,12 @@ public class JwtParser {
             throw new JwtException("Invalid typ", HttpURLConnection.HTTP_UNAUTHORIZED);
         }
 
             throw new JwtException("Invalid typ", HttpURLConnection.HTTP_UNAUTHORIZED);
         }
 
-        final JsonObject payload = loadJson(jwt.substring(firstDot + 1, secondDot));
+        final String kid = getAttribute(header, "kid", defaultKid);
+
+        final JsonObject payload = patcher.patch(defaultKid.equals(kid) ? null : kid, loadJson(jwt.substring(firstDot + 1, secondDot)));
         dateValidator.checkInterval(payload);
 
         final String alg = getAttribute(header, "alg", defaultAlg);
         dateValidator.checkInterval(payload);
 
         final String alg = getAttribute(header, "alg", defaultAlg);
-        final String kid = getAttribute(header, "kid", defaultKid);
         final Collection<String> issuers = kidMapper.loadIssuers(kid);
         if (!issuers.isEmpty() && issuers.stream().noneMatch(it -> it.equals(payload.getString(Claims.iss.name())))) {
             throw new JwtException("Invalid issuer", HttpURLConnection.HTTP_UNAUTHORIZED);
         final Collection<String> issuers = kidMapper.loadIssuers(kid);
         if (!issuers.isEmpty() && issuers.stream().noneMatch(it -> it.equals(payload.getString(Claims.iss.name())))) {
             throw new JwtException("Invalid issuer", HttpURLConnection.HTTP_UNAUTHORIZED);
diff --git a/src/main/java/org/apache/geronimo/microprofile/impl/jwtauth/jwt/JwtPatcher.java b/src/main/java/org/apache/geronimo/microprofile/impl/jwtauth/jwt/JwtPatcher.java
new file mode 100644 (file)
index 0000000..35f213b
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.geronimo.microprofile.impl.jwtauth.jwt;
+
+import org.apache.geronimo.microprofile.impl.jwtauth.config.GeronimoJwtAuthConfig;
+import org.apache.geronimo.microprofile.impl.jwtauth.io.PropertiesLoader;
+
+import javax.annotation.PostConstruct;
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+import javax.json.Json;
+import javax.json.JsonObject;
+import javax.json.JsonPatch;
+import javax.json.JsonReader;
+import javax.json.JsonReaderFactory;
+import java.io.StringReader;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import static java.util.Collections.emptyMap;
+import static java.util.Optional.ofNullable;
+
+@ApplicationScoped
+public class JwtPatcher {
+    @Inject
+    private GeronimoJwtAuthConfig config;
+
+    private JsonReaderFactory readerFactory;
+    private JsonPatch defaultPatch;
+    private final ConcurrentMap<String, JsonPatch> patches = new ConcurrentHashMap<>();
+
+    @PostConstruct
+    private void init() {
+        readerFactory = Json.createReaderFactory(emptyMap());
+        defaultPatch = ofNullable(config.read("jwt.header.jwt.payload.patch.default", null))
+                .map(it -> {
+                    try (final JsonReader reader = readerFactory.createReader(new StringReader(it))) {
+                        return reader.readArray();
+                    }
+                })
+                .map(Json::createPatch)
+                .orElse(null);
+        ofNullable(config.read("jwt.payload.patch.mapping", null))
+                .map(String::trim)
+                .filter(s -> !s.isEmpty())
+                .map(PropertiesLoader::load)
+                .ifPresent(props -> props.stringPropertyNames().forEach(k -> {
+                    final String patch = props.getProperty(k);
+                    try (final JsonReader reader = readerFactory.createReader(new StringReader(patch))) {
+                        patches.put(k, Json.createPatch(reader.readArray()));
+                    }
+                }));
+    }
+
+    public JsonObject patch(final String kid, final JsonObject raw) {
+        final JsonPatch patch = getPatch(kid);
+        if (patch == null) {
+            return raw;
+        }
+        return patch.apply(raw);
+    }
+
+    protected /*can be overriden to be lazy*/ JsonPatch getPatch(final String kid) {
+        if (kid == null) {
+            return defaultPatch;
+        }
+        return kid == null ? defaultPatch : patches.get(kid);
+    }
+}