You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@solr.apache.org by ja...@apache.org on 2022/02/09 01:22:03 UTC

[solr] branch branch_9x updated (9aebc1b -> 406350a)

This is an automated email from the ASF dual-hosted git repository.

janhoy pushed a change to branch branch_9x
in repository https://gitbox.apache.org/repos/asf/solr.git.


    from 9aebc1b  SOLR-14569: Configuring a shardHandlerFactory on the /select requestHandler results in HTTP 401 when searching on alias in secured Solr. (#574)
     new 8081b21  SOLR-15907 Apply spotless only on JWT classes (#616)
     new 4e12f79  SOLR-15907: Establish jwt-auth module
     new 88893cb  SOLR-15907 Upgrade JWT dependencies mock-oauth2-server and jose4j
     new 406350a  SOLR-15907 Spotless

The 4 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 gradle/maven/defaults-maven.gradle                 |   1 +
 gradle/validation/check-broken-links.gradle        |   2 +-
 settings.gradle                                    |   1 +
 solr/CHANGES.txt                                   |   2 +
 solr/benchmark/build.gradle                        |   1 +
 solr/core/build.gradle                             |  18 +-
 .../org/apache/solr/core/SolrResourceLoader.java   |   3 +-
 .../apache/solr/security/AuthenticationPlugin.java |   4 +-
 solr/licenses/accessors-smart-1.2.jar.sha1         |   1 -
 solr/licenses/accessors-smart-2.4.7.jar.sha1       |   1 +
 solr/licenses/asm-7.2.jar.sha1                     |   1 -
 solr/licenses/asm-9.1.jar.sha1                     |   1 +
 solr/licenses/asm-commons-7.2.jar.sha1             |   1 -
 solr/licenses/asm-commons-9.1.jar.sha1             |   1 +
 ...LICENSE-BSD_LIKE.txt => bcmail-LICENSE-MIT.txt} |   0
 solr/licenses/bcmail-jdk15on-1.69.jar.sha1         |   1 -
 solr/licenses/bcmail-jdk15on-1.70.jar.sha1         |   1 +
 ...LICENSE-BSD_LIKE.txt => bcpkix-LICENSE-MIT.txt} |   0
 ...bcpkix-jdk15on-NOTICE.txt => bcpkix-NOTICE.txt} |   0
 solr/licenses/bcpkix-jdk15on-1.69.jar.sha1         |   1 -
 solr/licenses/bcpkix-jdk15on-1.70.jar.sha1         |   1 +
 ...LICENSE-BSD_LIKE.txt => bcprov-LICENSE-MIT.txt} |   0
 solr/licenses/bcprov-jdk15on-1.69.jar.sha1         |   1 -
 solr/licenses/bcprov-jdk15on-1.70.jar.sha1         |   1 +
 ...LICENSE-BSD_LIKE.txt => bcutil-LICENSE-MIT.txt} |   2 +-
 solr/licenses/bcutil-jdk15on-1.70.jar.sha1         |   1 +
 solr/licenses/content-type-2.1.jar.sha1            |   1 -
 solr/licenses/content-type-2.2.jar.sha1            |   1 +
 solr/licenses/jackson-annotations-2.12.3.jar.sha1  |   1 -
 solr/licenses/jackson-annotations-2.13.1.jar.sha1  |   1 +
 solr/licenses/jackson-core-2.12.3.jar.sha1         |   1 -
 solr/licenses/jackson-core-2.13.1.jar.sha1         |   1 +
 solr/licenses/jackson-databind-2.12.3.jar.sha1     |   1 -
 solr/licenses/jackson-databind-2.13.1.jar.sha1     |   1 +
 .../jackson-dataformat-cbor-2.12.3.jar.sha1        |   1 -
 .../jackson-dataformat-cbor-2.13.1.jar.sha1        |   1 +
 .../jackson-dataformat-smile-2.12.3.jar.sha1       |   1 -
 .../jackson-dataformat-smile-2.13.1.jar.sha1       |   1 +
 .../jackson-dataformat-xml-2.12.3.jar.sha1         |   1 -
 .../jackson-dataformat-xml-2.13.1.jar.sha1         |   1 +
 .../licenses/jackson-datatype-jdk8-2.12.3.jar.sha1 |   1 -
 .../licenses/jackson-datatype-jdk8-2.13.1.jar.sha1 |   1 +
 .../jackson-datatype-jsr310-2.12.3.jar.sha1        |   1 -
 .../jackson-datatype-jsr310-2.13.1.jar.sha1        |   1 +
 ...jackson-module-jaxb-annotations-2.12.3.jar.sha1 |   1 -
 ...jackson-module-jaxb-annotations-LICENSE-ASL.txt | 201 --------
 .../jackson-module-jaxb-annotations-NOTICE.txt     |   0
 .../licenses/jackson-module-kotlin-2.12.3.jar.sha1 |   1 -
 .../licenses/jackson-module-kotlin-2.13.1.jar.sha1 |   1 +
 .../jackson-module-parameter-names-2.12.3.jar.sha1 |   1 -
 .../jackson-module-parameter-names-2.13.1.jar.sha1 |   1 +
 .../licenses/jakarta.activation-api-1.2.1.jar.sha1 |   1 -
 .../jakarta.activation-api-LICENSE-BSD_LIKE.txt    |  13 -
 solr/licenses/jakarta.activation-api-NOTICE.txt    |  25 -
 solr/licenses/jakarta.xml.bind-api-2.3.2.jar.sha1  |   1 -
 .../jakarta.xml.bind-api-LICENSE-BSD_LIKE.txt      |  13 -
 solr/licenses/jakarta.xml.bind-api-NOTICE.txt      |  44 --
 solr/licenses/jose4j-0.7.7.jar.sha1                |   1 -
 solr/licenses/jose4j-0.7.9.jar.sha1                |   1 +
 solr/licenses/json-smart-2.3.jar.sha1              |   1 -
 solr/licenses/json-smart-2.4.7.jar.sha1            |   1 +
 solr/licenses/kotlin-logging-jvm-2.0.6.jar.sha1    |   1 -
 solr/licenses/kotlin-logging-jvm-2.1.21.jar.sha1   |   1 +
 solr/licenses/kotlin-reflect-1.4.32.jar.sha1       |   1 -
 solr/licenses/kotlin-reflect-1.6.10.jar.sha1       |   1 +
 solr/licenses/kotlin-stdlib-1.4.32.jar.sha1        |   1 -
 solr/licenses/kotlin-stdlib-1.6.10.jar.sha1        |   1 +
 solr/licenses/kotlin-stdlib-common-1.4.32.jar.sha1 |   1 -
 solr/licenses/kotlin-stdlib-common-1.6.10.jar.sha1 |   1 +
 solr/licenses/kotlin-stdlib-jdk7-1.4.10.jar.sha1   |   1 -
 solr/licenses/kotlin-stdlib-jdk7-1.6.0.jar.sha1    |   1 +
 solr/licenses/kotlin-stdlib-jdk8-1.4.10.jar.sha1   |   1 -
 solr/licenses/kotlin-stdlib-jdk8-1.6.0.jar.sha1    |   1 +
 solr/licenses/lang-tag-1.4.4.jar.sha1              |   1 -
 solr/licenses/lang-tag-1.5.jar.sha1                |   1 +
 solr/licenses/mock-oauth2-server-0.3.3.jar.sha1    |   1 -
 solr/licenses/mock-oauth2-server-0.4.3.jar.sha1    |   1 +
 solr/licenses/mockwebserver-4.9.1.jar.sha1         |   1 -
 solr/licenses/mockwebserver-4.9.3.jar.sha1         |   1 +
 .../netty-codec-http-4.1.68.Final.jar.sha1         |   1 +
 solr/licenses/nimbus-jose-jwt-9.16.1.jar.sha1      |   1 +
 solr/licenses/nimbus-jose-jwt-9.6.1.jar.sha1       |   1 -
 solr/licenses/oauth2-oidc-sdk-9.2.3.jar.sha1       |   1 -
 solr/licenses/oauth2-oidc-sdk-9.22.2.jar.sha1      |   1 +
 solr/licenses/okhttp-4.9.1.jar.sha1                |   1 -
 solr/licenses/okhttp-4.9.3.jar.sha1                |   1 +
 solr/licenses/woodstox-core-6.2.4.jar.sha1         |   1 -
 solr/licenses/woodstox-core-6.2.7.jar.sha1         |   1 +
 solr/modules/jwt-auth/README.md                    |  25 +
 solr/modules/jwt-auth/build.gradle                 |  56 +++
 .../apache/solr/security/jwt}/JWTAuthPlugin.java   | 504 +++++++++++++--------
 .../apache/solr/security/jwt}/JWTIssuerConfig.java | 137 +++---
 .../apache/solr/security/jwt}/JWTPrincipal.java    |  26 +-
 .../security/jwt}/JWTPrincipalWithUserRoles.java   |  35 +-
 .../security/jwt}/JWTVerificationkeyResolver.java  |  90 ++--
 .../apache/solr/security/jwt/package-info.java}    |  21 +-
 solr/modules/jwt-auth/src/java/overview.html       |  21 +
 .../solr/configsets/cloud-minimal}/conf/schema.xml |   0
 .../configsets/cloud-minimal}/conf/solrconfig.xml  |   0
 .../solr/security/jwt_plugin_idp_cert.pem          |   0
 .../solr/security/jwt_plugin_idp_certs.p12         | Bin
 .../solr/security/jwt_plugin_idp_invalidcert.pem   |   0
 .../solr/security/jwt_plugin_idp_wrongcert.pem     |   0
 .../solr/security/jwt_plugin_jwk_security.json     |   0
 .../jwt_plugin_jwk_security_blockUnknownFalse.json |   0
 .../solr/security/jwt_plugin_jwk_url_security.json |   0
 .../solr/security/jwt_well-known-config.json       |   0
 .../jwt}/JWTAuthPluginIntegrationTest.java         | 327 +++++++------
 .../solr/security/jwt}/JWTAuthPluginTest.java      | 222 +++++----
 .../solr/security/jwt}/JWTIssuerConfigTest.java    | 127 ++++--
 .../jwt}/JWTVerificationkeyResolverTest.java       |  77 ++--
 solr/modules/langid/build.gradle                   |   3 +-
 solr/packaging/build.gradle                        |   1 +
 solr/prometheus-exporter/build.gradle              |   2 +-
 .../src/jwt-authentication-plugin.adoc             |   5 +
 .../src/major-changes-in-solr-9.adoc               |   3 +
 .../apache/solr/cloud/MiniSolrCloudCluster.java    |  13 +-
 .../apache/solr/cloud/SolrCloudAuthTestCase.java   |  16 +-
 versions.lock                                      |  87 ++--
 versions.props                                     |   4 +-
 120 files changed, 1174 insertions(+), 1027 deletions(-)
 delete mode 100644 solr/licenses/accessors-smart-1.2.jar.sha1
 create mode 100644 solr/licenses/accessors-smart-2.4.7.jar.sha1
 delete mode 100644 solr/licenses/asm-7.2.jar.sha1
 create mode 100644 solr/licenses/asm-9.1.jar.sha1
 delete mode 100644 solr/licenses/asm-commons-7.2.jar.sha1
 create mode 100644 solr/licenses/asm-commons-9.1.jar.sha1
 rename solr/licenses/{bcprov-LICENSE-BSD_LIKE.txt => bcmail-LICENSE-MIT.txt} (100%)
 delete mode 100644 solr/licenses/bcmail-jdk15on-1.69.jar.sha1
 create mode 100644 solr/licenses/bcmail-jdk15on-1.70.jar.sha1
 copy solr/licenses/{bcpkix-jdk15on-LICENSE-BSD_LIKE.txt => bcpkix-LICENSE-MIT.txt} (100%)
 rename solr/licenses/{bcpkix-jdk15on-NOTICE.txt => bcpkix-NOTICE.txt} (100%)
 delete mode 100644 solr/licenses/bcpkix-jdk15on-1.69.jar.sha1
 create mode 100644 solr/licenses/bcpkix-jdk15on-1.70.jar.sha1
 rename solr/licenses/{bcmail-LICENSE-BSD_LIKE.txt => bcprov-LICENSE-MIT.txt} (100%)
 delete mode 100644 solr/licenses/bcprov-jdk15on-1.69.jar.sha1
 create mode 100644 solr/licenses/bcprov-jdk15on-1.70.jar.sha1
 rename solr/licenses/{bcpkix-jdk15on-LICENSE-BSD_LIKE.txt => bcutil-LICENSE-MIT.txt} (94%)
 create mode 100644 solr/licenses/bcutil-jdk15on-1.70.jar.sha1
 delete mode 100644 solr/licenses/content-type-2.1.jar.sha1
 create mode 100644 solr/licenses/content-type-2.2.jar.sha1
 delete mode 100644 solr/licenses/jackson-annotations-2.12.3.jar.sha1
 create mode 100644 solr/licenses/jackson-annotations-2.13.1.jar.sha1
 delete mode 100644 solr/licenses/jackson-core-2.12.3.jar.sha1
 create mode 100644 solr/licenses/jackson-core-2.13.1.jar.sha1
 delete mode 100644 solr/licenses/jackson-databind-2.12.3.jar.sha1
 create mode 100644 solr/licenses/jackson-databind-2.13.1.jar.sha1
 delete mode 100644 solr/licenses/jackson-dataformat-cbor-2.12.3.jar.sha1
 create mode 100644 solr/licenses/jackson-dataformat-cbor-2.13.1.jar.sha1
 delete mode 100644 solr/licenses/jackson-dataformat-smile-2.12.3.jar.sha1
 create mode 100644 solr/licenses/jackson-dataformat-smile-2.13.1.jar.sha1
 delete mode 100644 solr/licenses/jackson-dataformat-xml-2.12.3.jar.sha1
 create mode 100644 solr/licenses/jackson-dataformat-xml-2.13.1.jar.sha1
 delete mode 100644 solr/licenses/jackson-datatype-jdk8-2.12.3.jar.sha1
 create mode 100644 solr/licenses/jackson-datatype-jdk8-2.13.1.jar.sha1
 delete mode 100644 solr/licenses/jackson-datatype-jsr310-2.12.3.jar.sha1
 create mode 100644 solr/licenses/jackson-datatype-jsr310-2.13.1.jar.sha1
 delete mode 100644 solr/licenses/jackson-module-jaxb-annotations-2.12.3.jar.sha1
 delete mode 100644 solr/licenses/jackson-module-jaxb-annotations-LICENSE-ASL.txt
 delete mode 100644 solr/licenses/jackson-module-jaxb-annotations-NOTICE.txt
 delete mode 100644 solr/licenses/jackson-module-kotlin-2.12.3.jar.sha1
 create mode 100644 solr/licenses/jackson-module-kotlin-2.13.1.jar.sha1
 delete mode 100644 solr/licenses/jackson-module-parameter-names-2.12.3.jar.sha1
 create mode 100644 solr/licenses/jackson-module-parameter-names-2.13.1.jar.sha1
 delete mode 100644 solr/licenses/jakarta.activation-api-1.2.1.jar.sha1
 delete mode 100644 solr/licenses/jakarta.activation-api-LICENSE-BSD_LIKE.txt
 delete mode 100644 solr/licenses/jakarta.activation-api-NOTICE.txt
 delete mode 100644 solr/licenses/jakarta.xml.bind-api-2.3.2.jar.sha1
 delete mode 100644 solr/licenses/jakarta.xml.bind-api-LICENSE-BSD_LIKE.txt
 delete mode 100644 solr/licenses/jakarta.xml.bind-api-NOTICE.txt
 delete mode 100644 solr/licenses/jose4j-0.7.7.jar.sha1
 create mode 100644 solr/licenses/jose4j-0.7.9.jar.sha1
 delete mode 100644 solr/licenses/json-smart-2.3.jar.sha1
 create mode 100644 solr/licenses/json-smart-2.4.7.jar.sha1
 delete mode 100644 solr/licenses/kotlin-logging-jvm-2.0.6.jar.sha1
 create mode 100644 solr/licenses/kotlin-logging-jvm-2.1.21.jar.sha1
 delete mode 100644 solr/licenses/kotlin-reflect-1.4.32.jar.sha1
 create mode 100644 solr/licenses/kotlin-reflect-1.6.10.jar.sha1
 delete mode 100644 solr/licenses/kotlin-stdlib-1.4.32.jar.sha1
 create mode 100644 solr/licenses/kotlin-stdlib-1.6.10.jar.sha1
 delete mode 100644 solr/licenses/kotlin-stdlib-common-1.4.32.jar.sha1
 create mode 100644 solr/licenses/kotlin-stdlib-common-1.6.10.jar.sha1
 delete mode 100644 solr/licenses/kotlin-stdlib-jdk7-1.4.10.jar.sha1
 create mode 100644 solr/licenses/kotlin-stdlib-jdk7-1.6.0.jar.sha1
 delete mode 100644 solr/licenses/kotlin-stdlib-jdk8-1.4.10.jar.sha1
 create mode 100644 solr/licenses/kotlin-stdlib-jdk8-1.6.0.jar.sha1
 delete mode 100644 solr/licenses/lang-tag-1.4.4.jar.sha1
 create mode 100644 solr/licenses/lang-tag-1.5.jar.sha1
 delete mode 100644 solr/licenses/mock-oauth2-server-0.3.3.jar.sha1
 create mode 100644 solr/licenses/mock-oauth2-server-0.4.3.jar.sha1
 delete mode 100644 solr/licenses/mockwebserver-4.9.1.jar.sha1
 create mode 100644 solr/licenses/mockwebserver-4.9.3.jar.sha1
 create mode 100644 solr/licenses/netty-codec-http-4.1.68.Final.jar.sha1
 create mode 100644 solr/licenses/nimbus-jose-jwt-9.16.1.jar.sha1
 delete mode 100644 solr/licenses/nimbus-jose-jwt-9.6.1.jar.sha1
 delete mode 100644 solr/licenses/oauth2-oidc-sdk-9.2.3.jar.sha1
 create mode 100644 solr/licenses/oauth2-oidc-sdk-9.22.2.jar.sha1
 delete mode 100644 solr/licenses/okhttp-4.9.1.jar.sha1
 create mode 100644 solr/licenses/okhttp-4.9.3.jar.sha1
 delete mode 100644 solr/licenses/woodstox-core-6.2.4.jar.sha1
 create mode 100644 solr/licenses/woodstox-core-6.2.7.jar.sha1
 create mode 100644 solr/modules/jwt-auth/README.md
 create mode 100644 solr/modules/jwt-auth/build.gradle
 rename solr/{core/src/java/org/apache/solr/security => modules/jwt-auth/src/java/org/apache/solr/security/jwt}/JWTAuthPlugin.java (61%)
 rename solr/{core/src/java/org/apache/solr/security => modules/jwt-auth/src/java/org/apache/solr/security/jwt}/JWTIssuerConfig.java (81%)
 rename solr/{core/src/java/org/apache/solr/security => modules/jwt-auth/src/java/org/apache/solr/security/jwt}/JWTPrincipal.java (81%)
 rename solr/{core/src/java/org/apache/solr/security => modules/jwt-auth/src/java/org/apache/solr/security/jwt}/JWTPrincipalWithUserRoles.java (66%)
 rename solr/{core/src/java/org/apache/solr/security => modules/jwt-auth/src/java/org/apache/solr/security/jwt}/JWTVerificationkeyResolver.java (64%)
 copy solr/modules/{langid/build.gradle => jwt-auth/src/java/org/apache/solr/security/jwt/package-info.java} (54%)
 create mode 100644 solr/modules/jwt-auth/src/java/overview.html
 copy solr/modules/{s3-repository/src/test-files => jwt-auth/src/test-files/solr/configsets/cloud-minimal}/conf/schema.xml (100%)
 copy solr/modules/{s3-repository/src/test-files => jwt-auth/src/test-files/solr/configsets/cloud-minimal}/conf/solrconfig.xml (100%)
 rename solr/{core => modules/jwt-auth}/src/test-files/solr/security/jwt_plugin_idp_cert.pem (100%)
 rename solr/{core => modules/jwt-auth}/src/test-files/solr/security/jwt_plugin_idp_certs.p12 (100%)
 rename solr/{core => modules/jwt-auth}/src/test-files/solr/security/jwt_plugin_idp_invalidcert.pem (100%)
 rename solr/{core => modules/jwt-auth}/src/test-files/solr/security/jwt_plugin_idp_wrongcert.pem (100%)
 rename solr/{core => modules/jwt-auth}/src/test-files/solr/security/jwt_plugin_jwk_security.json (100%)
 rename solr/{core => modules/jwt-auth}/src/test-files/solr/security/jwt_plugin_jwk_security_blockUnknownFalse.json (100%)
 rename solr/{core => modules/jwt-auth}/src/test-files/solr/security/jwt_plugin_jwk_url_security.json (100%)
 rename solr/{core => modules/jwt-auth}/src/test-files/solr/security/jwt_well-known-config.json (100%)
 rename solr/{core/src/test/org/apache/solr/security => modules/jwt-auth/src/test/org/apache/solr/security/jwt}/JWTAuthPluginIntegrationTest.java (66%)
 rename solr/{core/src/test/org/apache/solr/security => modules/jwt-auth/src/test/org/apache/solr/security/jwt}/JWTAuthPluginTest.java (74%)
 rename solr/{core/src/test/org/apache/solr/security => modules/jwt-auth/src/test/org/apache/solr/security/jwt}/JWTIssuerConfigTest.java (60%)
 rename solr/{core/src/test/org/apache/solr/security => modules/jwt-auth/src/test/org/apache/solr/security/jwt}/JWTVerificationkeyResolverTest.java (72%)

[solr] 02/04: SOLR-15907: Establish jwt-auth module

Posted by ja...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

janhoy pushed a commit to branch branch_9x
in repository https://gitbox.apache.org/repos/asf/solr.git

commit 4e12f79b7b54b3ecd94f19072fa724f65fab9ca9
Author: Jan Høydahl <ja...@users.noreply.github.com>
AuthorDate: Mon Feb 7 22:26:33 2022 +0100

    SOLR-15907: Establish jwt-auth module
---
 gradle/maven/defaults-maven.gradle                 |   1 +
 gradle/validation/check-broken-links.gradle        |   2 +-
 settings.gradle                                    |   1 +
 solr/CHANGES.txt                                   |   2 +
 solr/benchmark/build.gradle                        |   1 +
 solr/core/build.gradle                             |  18 +----
 .../org/apache/solr/core/SolrResourceLoader.java   |   3 +-
 .../apache/solr/security/AuthenticationPlugin.java |   4 +-
 .../netty-codec-http-4.1.68.Final.jar.sha1         |   1 +
 solr/modules/jwt-auth/README.md                    |  25 +++++++
 solr/modules/jwt-auth/build.gradle                 |  56 +++++++++++++++
 .../apache/solr/security/jwt}/JWTAuthPlugin.java   |  13 ++--
 .../apache/solr/security/jwt}/JWTIssuerConfig.java |  16 ++---
 .../apache/solr/security/jwt}/JWTPrincipal.java    |  15 ++--
 .../security/jwt}/JWTPrincipalWithUserRoles.java   |  22 +++---
 .../security/jwt}/JWTVerificationkeyResolver.java  |  36 +++++-----
 .../apache/solr/security/jwt/package-info.java}    |  21 +-----
 solr/modules/jwt-auth/src/java/overview.html       |  21 ++++++
 .../solr/configsets/cloud-minimal/conf/schema.xml  |  29 ++++++++
 .../configsets/cloud-minimal/conf/solrconfig.xml   |  51 ++++++++++++++
 .../solr/security/jwt_plugin_idp_cert.pem          |   0
 .../solr/security/jwt_plugin_idp_certs.p12         | Bin
 .../solr/security/jwt_plugin_idp_invalidcert.pem   |   0
 .../solr/security/jwt_plugin_idp_wrongcert.pem     |   0
 .../solr/security/jwt_plugin_jwk_security.json     |   0
 .../jwt_plugin_jwk_security_blockUnknownFalse.json |   0
 .../solr/security/jwt_plugin_jwk_url_security.json |   0
 .../solr/security/jwt_well-known-config.json       |   0
 .../jwt}/JWTAuthPluginIntegrationTest.java         |  43 ++++++++----
 .../solr/security/jwt}/JWTAuthPluginTest.java      |  76 +++++++++------------
 .../solr/security/jwt}/JWTIssuerConfigTest.java    |  10 +--
 .../jwt}/JWTVerificationkeyResolverTest.java       |   9 +--
 solr/modules/langid/build.gradle                   |   3 +-
 solr/packaging/build.gradle                        |   1 +
 solr/prometheus-exporter/build.gradle              |   2 +-
 .../src/jwt-authentication-plugin.adoc             |   5 ++
 .../src/major-changes-in-solr-9.adoc               |   3 +
 .../apache/solr/cloud/MiniSolrCloudCluster.java    |  13 +++-
 .../apache/solr/cloud/SolrCloudAuthTestCase.java   |  16 +----
 versions.lock                                      |  11 +--
 40 files changed, 345 insertions(+), 185 deletions(-)

diff --git a/gradle/maven/defaults-maven.gradle b/gradle/maven/defaults-maven.gradle
index 278ad68..509b707 100644
--- a/gradle/maven/defaults-maven.gradle
+++ b/gradle/maven/defaults-maven.gradle
@@ -33,6 +33,7 @@ configure(rootProject) {
         ":solr:modules:extraction",
         ":solr:modules:gcs-repository",
         ":solr:modules:jaegertracer-configurator",
+        ":solr:modules:jwt-auth",
         ":solr:modules:langid",
         ":solr:modules:ltr",
         ":solr:modules:s3-repository",
diff --git a/gradle/validation/check-broken-links.gradle b/gradle/validation/check-broken-links.gradle
index d032f41..885e1a7 100644
--- a/gradle/validation/check-broken-links.gradle
+++ b/gradle/validation/check-broken-links.gradle
@@ -64,7 +64,7 @@ class CheckBrokenLinksTask extends DefaultTask {
     }
 
     if (result.getExitValue() != 0) {
-      throw new GradleException("Broken links check failed. Command output at: ${outputFile}")
+      throw new GradleException("Broken links check failed. Command output at: ${outputFile.get()}")
     }
   }
 }
diff --git a/settings.gradle b/settings.gradle
index 6a78dd4..9a2dcd7 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -35,6 +35,7 @@ include "solr:modules:clustering"
 include "solr:modules:extraction"
 include "solr:modules:langid"
 include "solr:modules:jaegertracer-configurator"
+include "solr:modules:jwt-auth"
 include "solr:modules:s3-repository"
 include "solr:modules:scripting"
 include "solr:modules:ltr"
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index 1a3b6a6..a8b892d9f 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -512,6 +512,8 @@ Other Changes
 
 * SOLR-15807: New LogListener class for tests to use to make assertions about what Log messages should or should not be produced by a test (hossman)
 
+* SOLR-15907: Move JWT Authenticaion plugin to module 'jwt-auth' (janhoy)
+
 * SOLR-15845: Add a new SolrVersion class to manage Solr's version independently from the Lucene version we consume (janhoy)
 
 * SOLR-14858: Add the server WEB-INF/lib directory to the classpath for the solr-exporter script. Will allow the script
diff --git a/solr/benchmark/build.gradle b/solr/benchmark/build.gradle
index cbfb9d1..8575d36 100644
--- a/solr/benchmark/build.gradle
+++ b/solr/benchmark/build.gradle
@@ -56,6 +56,7 @@ dependencies {
   implementation 'org.jctools:jctools-core'
   implementation 'org.quicktheories:quicktheories'
   implementation 'org.openjdk.jmh:jmh-core'
+  implementation 'org.slf4j:slf4j-api'
   annotationProcessor 'org.openjdk.jmh:jmh-generator-annprocess'
   implementation 'org.slf4j:slf4j-api'
 }
diff --git a/solr/core/build.gradle b/solr/core/build.gradle
index ed43b04..a709f83 100644
--- a/solr/core/build.gradle
+++ b/solr/core/build.gradle
@@ -39,6 +39,9 @@ dependencies {
   api "org.apache.lucene:lucene-analysis-common"
   api "org.apache.lucene:lucene-queries"
 
+  // We export logging api with dependencies, which is useful for all modules
+  api 'org.slf4j:slf4j-api'
+
   api project(':solr:solrj')
   api project(':solr:server')
 
@@ -70,8 +73,6 @@ dependencies {
 
   implementation('com.github.ben-manes.caffeine:caffeine') { transitive = false }
 
-  implementation 'org.slf4j:slf4j-api'
-
   implementation 'commons-codec:commons-codec'
 
   implementation 'commons-cli:commons-cli'
@@ -148,9 +149,6 @@ dependencies {
 
   testImplementation 'org.slf4j:jcl-over-slf4j'
 
-  // JWT Auth plugin
-  api 'org.bitbucket.b_c:jose4j'
-
   // For faster XML processing than the JDK
   implementation 'org.codehaus.woodstox:stax2-api'
   implementation 'com.fasterxml.woodstox:woodstox-core'
@@ -204,16 +202,6 @@ dependencies {
   testImplementation('org.mockito:mockito-core', {
     exclude group: "net.bytebuddy", module: "byte-buddy-agent"
   })
-
-  testImplementation('no.nav.security:mock-oauth2-server', {
-    exclude group: "ch.qos.logback", module: "logback-core"
-    exclude group: "io.netty", module: "netty-all"
-    exclude group: "ch.qos.logback", module: "logback-classic"
-  })
-  
-  testImplementation 'com.nimbusds:nimbus-jose-jwt'
-  testImplementation 'com.squareup.okhttp3:mockwebserver'
-  testImplementation 'com.squareup.okhttp3:okhttp'
 }
 
 
diff --git a/solr/core/src/java/org/apache/solr/core/SolrResourceLoader.java b/solr/core/src/java/org/apache/solr/core/SolrResourceLoader.java
index 661b061..2109694 100644
--- a/solr/core/src/java/org/apache/solr/core/SolrResourceLoader.java
+++ b/solr/core/src/java/org/apache/solr/core/SolrResourceLoader.java
@@ -87,7 +87,8 @@ public class SolrResourceLoader implements ResourceLoader, Closeable, SolrClassL
   private static final String[] packages = {
       "", "analysis.", "schema.", "handler.", "handler.tagger.", "search.", "update.", "core.", "response.", "request.",
       "update.processor.", "util.", "spelling.", "handler.component.",
-      "spelling.suggest.", "spelling.suggest.fst.", "rest.schema.analysis.", "security.", "handler.admin."
+      "spelling.suggest.", "spelling.suggest.fst.", "rest.schema.analysis.", "security.", "handler.admin.",
+      "security.jwt."
   };
   private static final Charset UTF_8 = StandardCharsets.UTF_8;
   public static final String SOLR_ALLOW_UNSAFE_RESOURCELOADING_PARAM = "solr.allow.unsafe.resourceloading";
diff --git a/solr/core/src/java/org/apache/solr/security/AuthenticationPlugin.java b/solr/core/src/java/org/apache/solr/security/AuthenticationPlugin.java
index 0f4442c..b39bc41 100644
--- a/solr/core/src/java/org/apache/solr/security/AuthenticationPlugin.java
+++ b/solr/core/src/java/org/apache/solr/security/AuthenticationPlugin.java
@@ -95,11 +95,11 @@ public abstract class AuthenticationPlugin implements SolrInfoBean {
     }
   }
 
-  HttpServletRequest wrapWithPrincipal(HttpServletRequest request, Principal principal) {
+  protected HttpServletRequest wrapWithPrincipal(HttpServletRequest request, Principal principal) {
       return wrapWithPrincipal(request, principal, principal.getName());
   }
 
-  HttpServletRequest wrapWithPrincipal(HttpServletRequest request, Principal principal, String username) {
+  protected HttpServletRequest wrapWithPrincipal(HttpServletRequest request, Principal principal, String username) {
     return new HttpServletRequestWrapper(request) {
       @Override
       public Principal getUserPrincipal() {
diff --git a/solr/licenses/netty-codec-http-4.1.68.Final.jar.sha1 b/solr/licenses/netty-codec-http-4.1.68.Final.jar.sha1
new file mode 100644
index 0000000..a05d228
--- /dev/null
+++ b/solr/licenses/netty-codec-http-4.1.68.Final.jar.sha1
@@ -0,0 +1 @@
+fc2e0526ceba7fe1d0ca1adfedc301afcc47bc51
diff --git a/solr/modules/jwt-auth/README.md b/solr/modules/jwt-auth/README.md
new file mode 100644
index 0000000..eb2a275
--- /dev/null
+++ b/solr/modules/jwt-auth/README.md
@@ -0,0 +1,25 @@
+Apache Solr JWT Authentication Plugin
+=====================================
+
+Introduction
+------------
+Solr can support [JSON Web Token](https://en.wikipedia.org/wiki/JSON_Web_Token) (JWT) based 
+Bearer authentication with the use of the JWTAuthPlugin.
+
+This allows Solr to assert that a user is already authenticated with an external 
+[Identity Provider (IdP)](https://en.wikipedia.org/wiki/Identity_provider) by validating 
+that the JWT formatted [access token](https://en.wikipedia.org/wiki/Access_token) 
+is digitally signed by the Identity Provider.
+
+The typical use case is to integrate Solr with an [OpenID Connect](https://en.wikipedia.org/wiki/OpenID_Connect) 
+enabled Identity Provider.
+
+
+Getting Started
+---------------
+Please refer to the Solr Ref Guide at https://solr.apache.org/guide/jwt-authentication-plugin.html
+for more information.
+
+User interface
+--------------
+A User interface for this module is part of Solr Core.
\ No newline at end of file
diff --git a/solr/modules/jwt-auth/build.gradle b/solr/modules/jwt-auth/build.gradle
new file mode 100644
index 0000000..e747491
--- /dev/null
+++ b/solr/modules/jwt-auth/build.gradle
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+apply plugin: 'java-library'
+
+description = 'JWT / OpenID Connect / OAuth2 authentication plugin'
+
+dependencies {
+  implementation project(':solr:core')
+  implementation project(':solr:solrj')
+
+  implementation 'org.bitbucket.b_c:jose4j'
+
+  implementation 'commons-io:commons-io'
+  implementation 'io.dropwizard.metrics:metrics-core'
+  implementation 'javax.servlet:javax.servlet-api'
+  implementation 'org.apache.commons:commons-lang3'
+  implementation 'org.apache.httpcomponents:httpclient'
+  implementation 'org.apache.httpcomponents:httpcore'
+  implementation 'org.eclipse.jetty:jetty-client'
+  implementation ('com.google.guava:guava') { transitive = false }
+  implementation 'org.slf4j:slf4j-api'
+
+  implementation 'com.fasterxml.jackson.core:jackson-databind'
+
+  testImplementation project(':solr:test-framework')
+  testImplementation 'org.apache.lucene:lucene-test-framework'
+  testImplementation 'junit:junit'
+
+  testImplementation('org.mockito:mockito-core', {
+    exclude group: "net.bytebuddy", module: "byte-buddy-agent"
+  })
+  testImplementation('no.nav.security:mock-oauth2-server', {
+    exclude group: "ch.qos.logback", module: "logback-core"
+    exclude group: "io.netty", module: "netty-all"
+    exclude group: "ch.qos.logback", module: "logback-classic"
+  })
+  testImplementation 'com.nimbusds:nimbus-jose-jwt'
+  testImplementation 'com.squareup.okhttp3:mockwebserver'
+  testImplementation 'com.squareup.okhttp3:okhttp'
+  testImplementation 'io.netty:netty-codec-http'
+}
diff --git a/solr/core/src/java/org/apache/solr/security/JWTAuthPlugin.java b/solr/modules/jwt-auth/src/java/org/apache/solr/security/jwt/JWTAuthPlugin.java
similarity index 99%
rename from solr/core/src/java/org/apache/solr/security/JWTAuthPlugin.java
rename to solr/modules/jwt-auth/src/java/org/apache/solr/security/jwt/JWTAuthPlugin.java
index ab040bc..83fcb48 100644
--- a/solr/core/src/java/org/apache/solr/security/JWTAuthPlugin.java
+++ b/solr/modules/jwt-auth/src/java/org/apache/solr/security/jwt/JWTAuthPlugin.java
@@ -14,9 +14,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.solr.security;
+package org.apache.solr.security.jwt;
 
-import com.google.common.collect.ImmutableSet;
 import java.io.IOException;
 import java.io.InputStream;
 import java.lang.invoke.MethodHandles;
@@ -58,7 +57,9 @@ import org.apache.solr.common.util.CommandOperation;
 import org.apache.solr.common.util.Utils;
 import org.apache.solr.common.util.ValidatingJsonMap;
 import org.apache.solr.core.CoreContainer;
-import org.apache.solr.security.JWTAuthPlugin.JWTAuthenticationResponse.AuthCode;
+import org.apache.solr.security.AuthenticationPlugin;
+import org.apache.solr.security.ConfigEditablePlugin;
+import org.apache.solr.security.jwt.JWTAuthPlugin.JWTAuthenticationResponse.AuthCode;
 import org.apache.solr.util.CryptoKeys;
 import org.eclipse.jetty.client.api.Request;
 import org.jose4j.jwa.AlgorithmConstraints;
@@ -103,7 +104,7 @@ public class JWTAuthPlugin extends AuthenticationPlugin
   private static final String PARAM_ALG_WHITELIST = "algWhitelist";
 
   private static final Set<String> PROPS =
-      ImmutableSet.of(
+      Set.of(
           PARAM_BLOCK_UNKNOWN,
           PARAM_PRINCIPAL_CLAIM,
           PARAM_REQUIRE_EXPIRATIONTIME,
@@ -693,7 +694,7 @@ public class JWTAuthPlugin extends AuthenticationPlugin
   }
 
   @Override
-  public void close() throws IOException {
+  public void close() {
     jwtConsumer = null;
   }
 
@@ -781,7 +782,7 @@ public class JWTAuthPlugin extends AuthenticationPlugin
   }
 
   /** Response for authentication attempt */
-  static class JWTAuthenticationResponse {
+  protected static class JWTAuthenticationResponse {
     private final Principal principal;
     private String errorMessage;
     private final AuthCode authCode;
diff --git a/solr/core/src/java/org/apache/solr/security/JWTIssuerConfig.java b/solr/modules/jwt-auth/src/java/org/apache/solr/security/jwt/JWTIssuerConfig.java
similarity index 97%
rename from solr/core/src/java/org/apache/solr/security/JWTIssuerConfig.java
rename to solr/modules/jwt-auth/src/java/org/apache/solr/security/jwt/JWTIssuerConfig.java
index 11b4115..9b3d06a 100644
--- a/solr/core/src/java/org/apache/solr/security/JWTIssuerConfig.java
+++ b/solr/modules/jwt-auth/src/java/org/apache/solr/security/jwt/JWTIssuerConfig.java
@@ -15,13 +15,12 @@
  * limitations under the License.
  */
 
-package org.apache.solr.security;
+package org.apache.solr.security.jwt;
 
 import com.google.common.annotations.VisibleForTesting;
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.lang.invoke.MethodHandles;
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.nio.charset.Charset;
@@ -43,12 +42,9 @@ import org.jose4j.jwk.HttpsJwks;
 import org.jose4j.jwk.JsonWebKey;
 import org.jose4j.jwk.JsonWebKeySet;
 import org.jose4j.lang.JoseException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 /** Holds information about an IdP (issuer), such as issuer ID, JWK url(s), keys etc */
 public class JWTIssuerConfig {
-  private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
   static final String PARAM_ISS_NAME = "name";
   static final String PARAM_JWKS_URL = "jwksUrl";
   static final String PARAM_JWK = "jwk";
@@ -378,8 +374,7 @@ public class JWTIssuerConfig {
     return this.trustedCerts;
   }
 
-  /** */
-  static class HttpsJwksFactory {
+  public static class HttpsJwksFactory {
     private final long jwkCacheDuration;
     private final long refreshReprieveThreshold;
     private Collection<X509Certificate> trustedCerts;
@@ -398,7 +393,7 @@ public class JWTIssuerConfig {
       this.trustedCerts = trustedCerts;
     }
 
-    /**
+    /*
      * While the class name is HttpsJwks, it actually works with plain http formatted url as well.
      *
      * @param url the Url to connect to for JWK details.
@@ -433,8 +428,9 @@ public class JWTIssuerConfig {
   }
 
   /**
-   * Config object for a OpenId Connect well-known config Typically exposed through
-   * /.well-known/openid-configuration endpoint
+   * Config object for a OpenId Connect well-known config.
+   *
+   * <p>Typically exposed through <code>/.well-known/openid-configuration endpoint</code>.
    */
   public static class WellKnownDiscoveryConfig {
     private final Map<String, Object> securityConf;
diff --git a/solr/core/src/java/org/apache/solr/security/JWTPrincipal.java b/solr/modules/jwt-auth/src/java/org/apache/solr/security/jwt/JWTPrincipal.java
similarity index 91%
rename from solr/core/src/java/org/apache/solr/security/JWTPrincipal.java
rename to solr/modules/jwt-auth/src/java/org/apache/solr/security/jwt/JWTPrincipal.java
index a779fad..ec70bb2 100644
--- a/solr/core/src/java/org/apache/solr/security/JWTPrincipal.java
+++ b/solr/modules/jwt-auth/src/java/org/apache/solr/security/jwt/JWTPrincipal.java
@@ -15,9 +15,10 @@
  * limitations under the License.
  */
 
-package org.apache.solr.security;
+package org.apache.solr.security.jwt;
 
 import java.security.Principal;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
 import org.apache.http.util.Args;
@@ -75,15 +76,7 @@ public class JWTPrincipal implements Principal {
 
   @Override
   public String toString() {
-    return "JWTPrincipal{"
-        + "username='"
-        + username
-        + '\''
-        + ", token='"
-        + "*****"
-        + '\''
-        + ", claims="
-        + claims
-        + '}';
+    return String.format(
+        Locale.ROOT, "JWTPrincipal{username='%s', token='*****', claims=%s}", username, claims);
   }
 }
diff --git a/solr/core/src/java/org/apache/solr/security/JWTPrincipalWithUserRoles.java b/solr/modules/jwt-auth/src/java/org/apache/solr/security/jwt/JWTPrincipalWithUserRoles.java
similarity index 87%
rename from solr/core/src/java/org/apache/solr/security/JWTPrincipalWithUserRoles.java
rename to solr/modules/jwt-auth/src/java/org/apache/solr/security/jwt/JWTPrincipalWithUserRoles.java
index 856e6c2..1f298a1 100644
--- a/solr/core/src/java/org/apache/solr/security/JWTPrincipalWithUserRoles.java
+++ b/solr/modules/jwt-auth/src/java/org/apache/solr/security/jwt/JWTPrincipalWithUserRoles.java
@@ -15,12 +15,14 @@
  * limitations under the License.
  */
 
-package org.apache.solr.security;
+package org.apache.solr.security.jwt;
 
+import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 import org.apache.http.util.Args;
+import org.apache.solr.security.VerifiedUserRoles;
 
 /**
  * JWT principal that contains username, token, claims and a list of roles the user has, so one can
@@ -58,17 +60,11 @@ public class JWTPrincipalWithUserRoles extends JWTPrincipal implements VerifiedU
 
   @Override
   public String toString() {
-    return "JWTPrincipalWithUserRoles{"
-        + "username='"
-        + username
-        + '\''
-        + ", token='"
-        + "*****"
-        + '\''
-        + ", claims="
-        + claims
-        + ", roles="
-        + roles
-        + '}';
+    return String.format(
+        Locale.ROOT,
+        "JWTPrincipalWithUserRoles{username='%s', token='*****', claims=%s, roles=%s}",
+        username,
+        claims,
+        roles);
   }
 }
diff --git a/solr/core/src/java/org/apache/solr/security/JWTVerificationkeyResolver.java b/solr/modules/jwt-auth/src/java/org/apache/solr/security/jwt/JWTVerificationkeyResolver.java
similarity index 88%
rename from solr/core/src/java/org/apache/solr/security/JWTVerificationkeyResolver.java
rename to solr/modules/jwt-auth/src/java/org/apache/solr/security/jwt/JWTVerificationkeyResolver.java
index 08e5cb5..dd7a54b 100644
--- a/solr/core/src/java/org/apache/solr/security/JWTVerificationkeyResolver.java
+++ b/solr/modules/jwt-auth/src/java/org/apache/solr/security/jwt/JWTVerificationkeyResolver.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.solr.security;
+package org.apache.solr.security.jwt;
 
 import java.io.IOException;
 import java.lang.invoke.MethodHandles;
@@ -24,6 +24,7 @@ import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
 import javax.net.ssl.SSLHandshakeException;
@@ -154,25 +155,26 @@ public class JWTVerificationkeyResolver implements VerificationKeyResolver {
         theChosenOne = verificationJwkSelector.select(jws, jsonWebKeys);
       }
     } catch (JoseException | IOException | InvalidJwtException | MalformedClaimException e) {
-      StringBuilder sb = new StringBuilder();
-      sb.append("Unable to find a suitable verification key for JWS w/ header ")
-          .append(jws.getHeaders().getFullHeaderAsJsonString());
-      sb.append(" due to an unexpected exception (")
-          .append(e)
-          .append(") while obtaining or using keys from source ");
-      sb.append(keysSource);
-      throw new UnresolvableKeyException(sb.toString(), e);
+      String msg =
+          String.format(
+              Locale.ROOT,
+              "Unable to find a suitable verification key for JWS w/ header %s due to an unexpected exception (%s) "
+                  + "while obtaining or using keys from source %s",
+              jws.getHeaders().getFullHeaderAsJsonString(),
+              e,
+              keysSource);
+      throw new UnresolvableKeyException(msg, e);
     }
 
     if (theChosenOne == null) {
-      StringBuilder sb = new StringBuilder();
-      sb.append("Unable to find a suitable verification key for JWS w/ header ")
-          .append(jws.getHeaders().getFullHeaderAsJsonString());
-      sb.append(" from ")
-          .append(jsonWebKeys.size())
-          .append(" keys from source ")
-          .append(keysSource);
-      throw new UnresolvableKeyException(sb.toString());
+      String msg =
+          String.format(
+              Locale.ROOT,
+              "Unable to find a suitable verification key for JWS w/ header %s from %d keys from source %s",
+              jws.getHeaders().getFullHeaderAsJsonString(),
+              jsonWebKeys.size(),
+              keysSource);
+      throw new UnresolvableKeyException(msg);
     }
 
     return theChosenOne.getKey();
diff --git a/solr/modules/langid/build.gradle b/solr/modules/jwt-auth/src/java/org/apache/solr/security/jwt/package-info.java
similarity index 54%
copy from solr/modules/langid/build.gradle
copy to solr/modules/jwt-auth/src/java/org/apache/solr/security/jwt/package-info.java
index 5576d9d..1cf2ccd 100644
--- a/solr/modules/langid/build.gradle
+++ b/solr/modules/jwt-auth/src/java/org/apache/solr/security/jwt/package-info.java
@@ -15,22 +15,5 @@
  * limitations under the License.
  */
 
-apply plugin: 'java-library'
-
-description = 'Language Identifier module for extracting language from a document being indexed'
-
-dependencies {
-  implementation project(':solr:core')
-  implementation project(':solr:solrj')
-
-  implementation ('org.apache.tika:tika-core') { transitive = false }
-  implementation 'commons-io:commons-io'
-  implementation 'com.cybozu.labs:langdetect'
-  implementation 'net.arnx:jsonic'
-  implementation 'org.apache.opennlp:opennlp-tools'
-  implementation 'org.slf4j:slf4j-api'
-
-  testImplementation project(':solr:test-framework')
-  testImplementation 'com.carrotsearch.randomizedtesting:randomizedtesting-runner'
-  testImplementation 'junit:junit'
-}
+/** JWT authentication plugin */
+package org.apache.solr.security.jwt;
diff --git a/solr/modules/jwt-auth/src/java/overview.html b/solr/modules/jwt-auth/src/java/overview.html
new file mode 100644
index 0000000..69fcc17
--- /dev/null
+++ b/solr/modules/jwt-auth/src/java/overview.html
@@ -0,0 +1,21 @@
+<!--
+ 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.
+-->
+<html>
+<body>
+Apache Solr Search Server: Solr Oauth JWT Authentication plugin
+</body>
+</html>
diff --git a/solr/modules/jwt-auth/src/test-files/solr/configsets/cloud-minimal/conf/schema.xml b/solr/modules/jwt-auth/src/test-files/solr/configsets/cloud-minimal/conf/schema.xml
new file mode 100644
index 0000000..4124fea
--- /dev/null
+++ b/solr/modules/jwt-auth/src/test-files/solr/configsets/cloud-minimal/conf/schema.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+ 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.
+-->
+<schema name="minimal" version="1.1">
+  <fieldType name="string" class="solr.StrField"/>
+  <fieldType name="int" class="${solr.tests.IntegerFieldType}" docValues="${solr.tests.numeric.dv}" precisionStep="0" omitNorms="true" positionIncrementGap="0"/>
+  <fieldType name="long" class="${solr.tests.LongFieldType}" docValues="${solr.tests.numeric.dv}" precisionStep="0" omitNorms="true" positionIncrementGap="0"/>
+  <dynamicField name="*" type="string" indexed="true" stored="true"/>
+  <!-- for versioning -->
+  <field name="_version_" type="long" indexed="true" stored="true"/>
+  <field name="_root_" type="string" indexed="true" stored="true" multiValued="false" required="false"/>
+  <field name="id" type="string" indexed="true" stored="true"/>
+  <dynamicField name="*_s"  type="string"  indexed="true"  stored="true" />
+  <uniqueKey>id</uniqueKey>
+</schema>
diff --git a/solr/modules/jwt-auth/src/test-files/solr/configsets/cloud-minimal/conf/solrconfig.xml b/solr/modules/jwt-auth/src/test-files/solr/configsets/cloud-minimal/conf/solrconfig.xml
new file mode 100644
index 0000000..853ba65
--- /dev/null
+++ b/solr/modules/jwt-auth/src/test-files/solr/configsets/cloud-minimal/conf/solrconfig.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" ?>
+
+<!--
+ 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.
+-->
+
+<!-- Minimal solrconfig.xml with /select, /admin and /update only -->
+
+<config>
+
+  <dataDir>${solr.data.dir:}</dataDir>
+
+  <directoryFactory name="DirectoryFactory"
+                    class="${solr.directoryFactory:solr.NRTCachingDirectoryFactory}"/>
+  <schemaFactory class="ClassicIndexSchemaFactory"/>
+
+  <luceneMatchVersion>${tests.luceneMatchVersion:LATEST}</luceneMatchVersion>
+
+  <updateHandler class="solr.DirectUpdateHandler2">
+    <commitWithin>
+      <softCommit>${solr.commitwithin.softcommit:true}</softCommit>
+    </commitWithin>
+    <updateLog class="${solr.ulog:solr.UpdateLog}"></updateLog>
+  </updateHandler>
+
+  <requestHandler name="/select" class="solr.SearchHandler">
+    <lst name="defaults">
+      <str name="echoParams">explicit</str>
+      <str name="indent">true</str>
+      <str name="df">text</str>
+    </lst>
+
+  </requestHandler>
+  <indexConfig>
+    <mergeScheduler class="${solr.mscheduler:org.apache.lucene.index.ConcurrentMergeScheduler}"/>
+:  </indexConfig>
+</config>
+
diff --git a/solr/core/src/test-files/solr/security/jwt_plugin_idp_cert.pem b/solr/modules/jwt-auth/src/test-files/solr/security/jwt_plugin_idp_cert.pem
similarity index 100%
rename from solr/core/src/test-files/solr/security/jwt_plugin_idp_cert.pem
rename to solr/modules/jwt-auth/src/test-files/solr/security/jwt_plugin_idp_cert.pem
diff --git a/solr/core/src/test-files/solr/security/jwt_plugin_idp_certs.p12 b/solr/modules/jwt-auth/src/test-files/solr/security/jwt_plugin_idp_certs.p12
similarity index 100%
rename from solr/core/src/test-files/solr/security/jwt_plugin_idp_certs.p12
rename to solr/modules/jwt-auth/src/test-files/solr/security/jwt_plugin_idp_certs.p12
diff --git a/solr/core/src/test-files/solr/security/jwt_plugin_idp_invalidcert.pem b/solr/modules/jwt-auth/src/test-files/solr/security/jwt_plugin_idp_invalidcert.pem
similarity index 100%
rename from solr/core/src/test-files/solr/security/jwt_plugin_idp_invalidcert.pem
rename to solr/modules/jwt-auth/src/test-files/solr/security/jwt_plugin_idp_invalidcert.pem
diff --git a/solr/core/src/test-files/solr/security/jwt_plugin_idp_wrongcert.pem b/solr/modules/jwt-auth/src/test-files/solr/security/jwt_plugin_idp_wrongcert.pem
similarity index 100%
rename from solr/core/src/test-files/solr/security/jwt_plugin_idp_wrongcert.pem
rename to solr/modules/jwt-auth/src/test-files/solr/security/jwt_plugin_idp_wrongcert.pem
diff --git a/solr/core/src/test-files/solr/security/jwt_plugin_jwk_security.json b/solr/modules/jwt-auth/src/test-files/solr/security/jwt_plugin_jwk_security.json
similarity index 100%
rename from solr/core/src/test-files/solr/security/jwt_plugin_jwk_security.json
rename to solr/modules/jwt-auth/src/test-files/solr/security/jwt_plugin_jwk_security.json
diff --git a/solr/core/src/test-files/solr/security/jwt_plugin_jwk_security_blockUnknownFalse.json b/solr/modules/jwt-auth/src/test-files/solr/security/jwt_plugin_jwk_security_blockUnknownFalse.json
similarity index 100%
rename from solr/core/src/test-files/solr/security/jwt_plugin_jwk_security_blockUnknownFalse.json
rename to solr/modules/jwt-auth/src/test-files/solr/security/jwt_plugin_jwk_security_blockUnknownFalse.json
diff --git a/solr/core/src/test-files/solr/security/jwt_plugin_jwk_url_security.json b/solr/modules/jwt-auth/src/test-files/solr/security/jwt_plugin_jwk_url_security.json
similarity index 100%
rename from solr/core/src/test-files/solr/security/jwt_plugin_jwk_url_security.json
rename to solr/modules/jwt-auth/src/test-files/solr/security/jwt_plugin_jwk_url_security.json
diff --git a/solr/core/src/test-files/solr/security/jwt_well-known-config.json b/solr/modules/jwt-auth/src/test-files/solr/security/jwt_well-known-config.json
similarity index 100%
rename from solr/core/src/test-files/solr/security/jwt_well-known-config.json
rename to solr/modules/jwt-auth/src/test-files/solr/security/jwt_well-known-config.json
diff --git a/solr/core/src/test/org/apache/solr/security/JWTAuthPluginIntegrationTest.java b/solr/modules/jwt-auth/src/test/org/apache/solr/security/jwt/JWTAuthPluginIntegrationTest.java
similarity index 94%
rename from solr/core/src/test/org/apache/solr/security/JWTAuthPluginIntegrationTest.java
rename to solr/modules/jwt-auth/src/test/org/apache/solr/security/jwt/JWTAuthPluginIntegrationTest.java
index bfb0b8e..7933644 100644
--- a/solr/core/src/test/org/apache/solr/security/JWTAuthPluginIntegrationTest.java
+++ b/solr/modules/jwt-auth/src/test/org/apache/solr/security/jwt/JWTAuthPluginIntegrationTest.java
@@ -14,9 +14,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.solr.security;
+package org.apache.solr.security.jwt;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.apache.solr.security.jwt.JWTAuthPluginTest.JWT_TEST_PATH;
 
 import java.io.BufferedReader;
 import java.io.IOException;
@@ -72,6 +73,7 @@ import org.jose4j.jwk.RsaJwkGenerator;
 import org.jose4j.jws.AlgorithmIdentifiers;
 import org.jose4j.jws.JsonWebSignature;
 import org.jose4j.jwt.JwtClaims;
+import org.jose4j.lang.JoseException;
 import org.junit.After;
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
@@ -100,11 +102,9 @@ public class JWTAuthPluginIntegrationTest extends SolrCloudAuthTestCase {
   @BeforeClass
   public static void beforeClass() throws Exception {
     // Setup an OAuth2 mock server with SSL
-    Path p12Cert =
-        SolrTestCaseJ4.TEST_PATH().resolve("security").resolve("jwt_plugin_idp_certs.p12");
-    pemFilePath = SolrTestCaseJ4.TEST_PATH().resolve("security").resolve("jwt_plugin_idp_cert.pem");
-    wrongPemFilePath =
-        SolrTestCaseJ4.TEST_PATH().resolve("security").resolve("jwt_plugin_idp_wrongcert.pem");
+    Path p12Cert = JWT_TEST_PATH().resolve("security").resolve("jwt_plugin_idp_certs.p12");
+    pemFilePath = JWT_TEST_PATH().resolve("security").resolve("jwt_plugin_idp_cert.pem");
+    wrongPemFilePath = JWT_TEST_PATH().resolve("security").resolve("jwt_plugin_idp_wrongcert.pem");
 
     mockOAuth2Server = createMockOAuthServer(p12Cert, "secret");
     mockOAuth2Server.start();
@@ -215,13 +215,24 @@ public class JWTAuthPluginIntegrationTest extends SolrCloudAuthTestCase {
     assertAuthMetricsMinimums(2, 1, 0, 0, 1, 0);
     executeCommand(baseUrl + authcPrefix, cl, "{set-property : { blockUnknown: false}}", jws);
     verifySecurityStatus(
-        cl, baseUrl + authcPrefix, "authentication/blockUnknown", "false", 20, jws);
+        cl,
+        baseUrl + authcPrefix,
+        "authentication/blockUnknown",
+        "false",
+        20,
+        getBearerAuthHeader(jws));
     // Pass through
     verifySecurityStatus(cl, baseUrl + "/admin/info/key", "key", NOT_NULL_PREDICATE, 20);
     // Now succeeds since blockUnknown=false
     get(baseUrl + "/" + COLLECTION + "/query?q=*:*", null);
     executeCommand(baseUrl + authcPrefix, cl, "{set-property : { blockUnknown: true}}", null);
-    verifySecurityStatus(cl, baseUrl + authcPrefix, "authentication/blockUnknown", "true", 20, jws);
+    verifySecurityStatus(
+        cl,
+        baseUrl + authcPrefix,
+        "authentication/blockUnknown",
+        "true",
+        20,
+        getBearerAuthHeader(jws));
 
     assertAuthMetricsMinimums(9, 4, 4, 0, 1, 0);
 
@@ -268,6 +279,10 @@ public class JWTAuthPluginIntegrationTest extends SolrCloudAuthTestCase {
     HttpClientUtil.close(cl);
   }
 
+  static String getBearerAuthHeader(JsonWebSignature jws) throws JoseException {
+    return "Bearer " + jws.getCompactSerialization();
+  }
+
   /**
    * Configure solr cluster with a security.json talking to MockOAuth2 server
    *
@@ -282,13 +297,12 @@ public class JWTAuthPluginIntegrationTest extends SolrCloudAuthTestCase {
     MiniSolrCloudCluster myCluster =
         configureCluster(numNodes) // nodes
             .addConfig(
-                "conf1", TEST_PATH().resolve("configsets").resolve("cloud-minimal").resolve("conf"))
+                "conf1",
+                JWT_TEST_PATH().resolve("configsets").resolve("cloud-minimal").resolve("conf"))
             .withDefaultClusterProperty("useLegacyReplicaAssignment", "false")
             .build();
     String securityJson = createMockOAuthSecurityJson(pemFilePath);
-    myCluster
-        .getZkClient()
-        .setData("/security.json", securityJson.getBytes(Charset.defaultCharset()), true);
+    myCluster.zkSetData("/security.json", securityJson.getBytes(Charset.defaultCharset()), true);
     RTimer timer = new RTimer();
     do { // Wait timeoutMs time for the security.json change to take effect
       Thread.sleep(200);
@@ -311,9 +325,10 @@ public class JWTAuthPluginIntegrationTest extends SolrCloudAuthTestCase {
       throws Exception {
     MiniSolrCloudCluster myCluster =
         configureCluster(2) // nodes
-            .withSecurityJson(TEST_PATH().resolve("security").resolve(securityJsonFilename))
+            .withSecurityJson(JWT_TEST_PATH().resolve("security").resolve(securityJsonFilename))
             .addConfig(
-                "conf1", TEST_PATH().resolve("configsets").resolve("cloud-minimal").resolve("conf"))
+                "conf1",
+                JWT_TEST_PATH().resolve("configsets").resolve("cloud-minimal").resolve("conf"))
             .withDefaultClusterProperty("useLegacyReplicaAssignment", "false")
             .build();
 
diff --git a/solr/core/src/test/org/apache/solr/security/JWTAuthPluginTest.java b/solr/modules/jwt-auth/src/test/org/apache/solr/security/jwt/JWTAuthPluginTest.java
similarity index 89%
rename from solr/core/src/test/org/apache/solr/security/JWTAuthPluginTest.java
rename to solr/modules/jwt-auth/src/test/org/apache/solr/security/jwt/JWTAuthPluginTest.java
index 2e1c241..e25d2fb 100644
--- a/solr/core/src/test/org/apache/solr/security/JWTAuthPluginTest.java
+++ b/solr/modules/jwt-auth/src/test/org/apache/solr/security/jwt/JWTAuthPluginTest.java
@@ -14,9 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.solr.security;
+package org.apache.solr.security.jwt;
 
-import static org.apache.solr.security.JWTAuthPlugin.JWTAuthenticationResponse.AuthCode.*;
+import static org.apache.solr.security.jwt.JWTAuthPlugin.JWTAuthenticationResponse.AuthCode.*;
 
 import java.io.IOException;
 import java.io.InputStream;
@@ -38,6 +38,7 @@ import org.apache.commons.io.IOUtils;
 import org.apache.solr.SolrTestCaseJ4;
 import org.apache.solr.common.SolrException;
 import org.apache.solr.common.util.Utils;
+import org.apache.solr.security.VerifiedUserRoles;
 import org.apache.solr.util.CryptoKeys;
 import org.jose4j.jwk.RsaJsonWebKey;
 import org.jose4j.jwk.RsaJwkGenerator;
@@ -63,6 +64,10 @@ public class JWTAuthPluginTest extends SolrTestCaseJ4 {
   // Shared with other tests
   static HashMap<String, Object> testJwk;
 
+  public static Path JWT_TEST_PATH() {
+    return getFile("solr/security").getParentFile().toPath();
+  }
+
   static {
     // Generate an RSA key pair, which will be used for signing and verification of the JWT, wrapped
     // in a JWK
@@ -82,7 +87,7 @@ public class JWTAuthPluginTest extends SolrTestCaseJ4 {
           "n", BigEndianBigInteger.toBase64Url(rsaJsonWebKey.getRsaPublicKey().getModulus()));
 
       trustedPemCert =
-          Files.readString(TEST_PATH().resolve("security").resolve("jwt_plugin_idp_cert.pem"));
+          Files.readString(JWT_TEST_PATH().resolve("security").resolve("jwt_plugin_idp_cert.pem"));
     } catch (JoseException | IOException e) {
       fail("Failed static initialization: " + e.getMessage());
     }
@@ -142,13 +147,13 @@ public class JWTAuthPluginTest extends SolrTestCaseJ4 {
     plugin = new JWTAuthPlugin();
 
     testConfig = new HashMap<>();
-    testConfig.put("class", "org.apache.solr.security.JWTAuthPlugin");
+    testConfig.put("class", "org.apache.solr.security.jwt.JWTAuthPlugin");
     testConfig.put("principalClaim", "customPrincipal");
     testConfig.put("jwk", testJwk);
     plugin.init(testConfig);
 
     minimalConfig = new HashMap<>();
-    minimalConfig.put("class", "org.apache.solr.security.JWTAuthPlugin");
+    minimalConfig.put("class", "org.apache.solr.security.jwt.JWTAuthPlugin");
   }
 
   @Override
@@ -169,7 +174,7 @@ public class JWTAuthPluginTest extends SolrTestCaseJ4 {
 
   @Test
   public void initFromSecurityJSONLocalJWK() throws Exception {
-    Path securityJson = TEST_PATH().resolve("security").resolve("jwt_plugin_jwk_security.json");
+    Path securityJson = JWT_TEST_PATH().resolve("security").resolve("jwt_plugin_jwk_security.json");
     InputStream is = Files.newInputStream(securityJson);
     Map<String, Object> securityConf = (Map<String, Object>) Utils.fromJSON(is);
     Map<String, Object> authConf = (Map<String, Object>) securityConf.get("authentication");
@@ -178,16 +183,15 @@ public class JWTAuthPluginTest extends SolrTestCaseJ4 {
 
   @Test
   public void initFromSecurityJSONUrlJwk() throws Exception {
-    Path securityJson = TEST_PATH().resolve("security").resolve("jwt_plugin_jwk_url_security.json");
+    Path securityJson =
+        JWT_TEST_PATH().resolve("security").resolve("jwt_plugin_jwk_url_security.json");
     InputStream is = Files.newInputStream(securityJson);
     Map<String, Object> securityConf = (Map<String, Object>) Utils.fromJSON(is);
     Map<String, Object> authConf = (Map<String, Object>) securityConf.get("authentication");
     plugin.init(authConf);
 
     JWTAuthPlugin.JWTAuthenticationResponse resp = plugin.authenticate(testHeader);
-    assertEquals(
-        JWTAuthPlugin.JWTAuthenticationResponse.AuthCode.JWT_VALIDATION_EXCEPTION,
-        resp.getAuthCode());
+    assertEquals(JWT_VALIDATION_EXCEPTION, resp.getAuthCode());
     assertTrue(resp.getJwtException().getMessage().contains("Connection refused"));
   }
 
@@ -236,9 +240,7 @@ public class JWTAuthPluginTest extends SolrTestCaseJ4 {
     plugin.init(minimalConfig);
     JWTAuthPlugin.JWTAuthenticationResponse resp = plugin.authenticate(testHeader);
     assertFalse(resp.isAuthenticated());
-    assertEquals(
-        JWTAuthPlugin.JWTAuthenticationResponse.AuthCode.JWT_VALIDATION_EXCEPTION,
-        resp.getAuthCode());
+    assertEquals(JWT_VALIDATION_EXCEPTION, resp.getAuthCode());
 
     testConfig.put("principalClaim", "sub"); // testConfig has subject = solruser
     plugin.init(testConfig);
@@ -252,9 +254,7 @@ public class JWTAuthPluginTest extends SolrTestCaseJ4 {
     plugin.init(testConfig);
     JWTAuthPlugin.JWTAuthenticationResponse resp = plugin.authenticate(testHeader);
     assertFalse(resp.isAuthenticated());
-    assertEquals(
-        JWTAuthPlugin.JWTAuthenticationResponse.AuthCode.JWT_VALIDATION_EXCEPTION,
-        resp.getAuthCode());
+    assertEquals(JWT_VALIDATION_EXCEPTION, resp.getAuthCode());
 
     testConfig.put("iss", "IDServer");
     plugin.init(testConfig);
@@ -268,9 +268,7 @@ public class JWTAuthPluginTest extends SolrTestCaseJ4 {
     plugin.init(testConfig);
     JWTAuthPlugin.JWTAuthenticationResponse resp = plugin.authenticate(testHeader);
     assertFalse(resp.isAuthenticated());
-    assertEquals(
-        JWTAuthPlugin.JWTAuthenticationResponse.AuthCode.JWT_VALIDATION_EXCEPTION,
-        resp.getAuthCode());
+    assertEquals(JWT_VALIDATION_EXCEPTION, resp.getAuthCode());
 
     testConfig.put("aud", "Solr");
     plugin.init(testConfig);
@@ -289,8 +287,7 @@ public class JWTAuthPluginTest extends SolrTestCaseJ4 {
     plugin.init(testConfig);
     resp = plugin.authenticate(testHeader);
     assertFalse(resp.isAuthenticated());
-    assertEquals(
-        JWTAuthPlugin.JWTAuthenticationResponse.AuthCode.PRINCIPAL_MISSING, resp.getAuthCode());
+    assertEquals(PRINCIPAL_MISSING, resp.getAuthCode());
   }
 
   @Test
@@ -310,15 +307,13 @@ public class JWTAuthPluginTest extends SolrTestCaseJ4 {
     shouldMatch.put("claim9", "NA");
     plugin.init(testConfig);
     resp = plugin.authenticate(testHeader);
-    assertEquals(
-        JWTAuthPlugin.JWTAuthenticationResponse.AuthCode.CLAIM_MISMATCH, resp.getAuthCode());
+    assertEquals(CLAIM_MISMATCH, resp.getAuthCode());
 
     // Required claim does not match regex
     shouldMatch.clear();
     shouldMatch.put("claim1", "NA");
     resp = plugin.authenticate(testHeader);
-    assertEquals(
-        JWTAuthPlugin.JWTAuthenticationResponse.AuthCode.CLAIM_MISMATCH, resp.getAuthCode());
+    assertEquals(CLAIM_MISMATCH, resp.getAuthCode());
   }
 
   @Test
@@ -333,18 +328,14 @@ public class JWTAuthPluginTest extends SolrTestCaseJ4 {
     testConfig.put("requireExp", true);
     plugin.init(testConfig);
     resp = plugin.authenticate(slimHeader);
-    assertEquals(
-        JWTAuthPlugin.JWTAuthenticationResponse.AuthCode.JWT_VALIDATION_EXCEPTION,
-        resp.getAuthCode());
+    assertEquals(JWT_VALIDATION_EXCEPTION, resp.getAuthCode());
     testConfig.put("requireExp", false);
 
     // Missing issuer claim
     testConfig.put("requireIss", true);
     plugin.init(testConfig);
     resp = plugin.authenticate(slimHeader);
-    assertEquals(
-        JWTAuthPlugin.JWTAuthenticationResponse.AuthCode.JWT_VALIDATION_EXCEPTION,
-        resp.getAuthCode());
+    assertEquals(JWT_VALIDATION_EXCEPTION, resp.getAuthCode());
   }
 
   @Test
@@ -352,9 +343,7 @@ public class JWTAuthPluginTest extends SolrTestCaseJ4 {
     testConfig.put("algAllowlist", Arrays.asList("PS384", "PS512"));
     plugin.init(testConfig);
     JWTAuthPlugin.JWTAuthenticationResponse resp = plugin.authenticate(testHeader);
-    assertEquals(
-        JWTAuthPlugin.JWTAuthenticationResponse.AuthCode.JWT_VALIDATION_EXCEPTION,
-        resp.getAuthCode());
+    assertEquals(JWT_VALIDATION_EXCEPTION, resp.getAuthCode());
     assertTrue(resp.getErrorMessage().contains("not a permitted algorithm"));
   }
 
@@ -413,7 +402,7 @@ public class JWTAuthPluginTest extends SolrTestCaseJ4 {
     testConfig.put("blockUnknown", false);
     plugin.init(testConfig);
     JWTAuthPlugin.JWTAuthenticationResponse resp = plugin.authenticate(null);
-    assertEquals(JWTAuthPlugin.JWTAuthenticationResponse.AuthCode.PASS_THROUGH, resp.getAuthCode());
+    assertEquals(PASS_THROUGH, resp.getAuthCode());
   }
 
   @Test
@@ -421,13 +410,13 @@ public class JWTAuthPluginTest extends SolrTestCaseJ4 {
     minimalConfig.put("blockUnknown", false);
     plugin.init(minimalConfig);
     JWTAuthPlugin.JWTAuthenticationResponse resp = plugin.authenticate(null);
-    assertEquals(JWTAuthPlugin.JWTAuthenticationResponse.AuthCode.PASS_THROUGH, resp.getAuthCode());
+    assertEquals(PASS_THROUGH, resp.getAuthCode());
   }
 
   @Test
   public void wellKnownConfigNoHeaderPassThrough() {
     String wellKnownUrl =
-        TEST_PATH()
+        JWT_TEST_PATH()
             .resolve("security")
             .resolve("jwt_well-known-config.json")
             .toAbsolutePath()
@@ -437,13 +426,13 @@ public class JWTAuthPluginTest extends SolrTestCaseJ4 {
     testConfig.remove("jwk");
     plugin.init(testConfig);
     JWTAuthPlugin.JWTAuthenticationResponse resp = plugin.authenticate(null);
-    assertEquals(JWTAuthPlugin.JWTAuthenticationResponse.AuthCode.PASS_THROUGH, resp.getAuthCode());
+    assertEquals(PASS_THROUGH, resp.getAuthCode());
   }
 
   @Test
   public void defaultRealm() {
     String wellKnownUrl =
-        TEST_PATH()
+        JWT_TEST_PATH()
             .resolve("security")
             .resolve("jwt_well-known-config.json")
             .toAbsolutePath()
@@ -458,7 +447,7 @@ public class JWTAuthPluginTest extends SolrTestCaseJ4 {
   @Test
   public void configureRealm() {
     String wellKnownUrl =
-        TEST_PATH()
+        JWT_TEST_PATH()
             .resolve("security")
             .resolve("jwt_well-known-config.json")
             .toAbsolutePath()
@@ -560,7 +549,7 @@ public class JWTAuthPluginTest extends SolrTestCaseJ4 {
     authConf.put("jwksUrl", "https://127.0.0.1:9999/foo.jwk");
     authConf.put(
         "trustedCertsFile",
-        TEST_PATH().resolve("security").resolve("jwt_plugin_idp_cert.pem").toString());
+        JWT_TEST_PATH().resolve("security").resolve("jwt_plugin_idp_cert.pem").toString());
     plugin = new JWTAuthPlugin();
     plugin.init(authConf);
     assertEquals(2, plugin.getIssuerConfigs().get(0).getTrustedCerts().size());
@@ -581,7 +570,7 @@ public class JWTAuthPluginTest extends SolrTestCaseJ4 {
     authConf.put("jwksUrl", "https://127.0.0.1:9999/foo.jwk");
     authConf.put(
         "trustedCertsFile",
-        TEST_PATH().resolve("security").resolve("jwt_plugin_idp_invalidcert.pem").toString());
+        JWT_TEST_PATH().resolve("security").resolve("jwt_plugin_idp_invalidcert.pem").toString());
     plugin = new JWTAuthPlugin();
     expectThrows(SolrException.class, () -> plugin.init(authConf));
   }
@@ -622,8 +611,7 @@ public class JWTAuthPluginTest extends SolrTestCaseJ4 {
 
   @Test
   public void extractCertificate() throws IOException {
-    Path pemFilePath =
-        SolrTestCaseJ4.TEST_PATH().resolve("security").resolve("jwt_plugin_idp_cert.pem");
+    Path pemFilePath = JWT_TEST_PATH().resolve("security").resolve("jwt_plugin_idp_cert.pem");
     String cert = CryptoKeys.extractCertificateFromPem(Files.readString(pemFilePath));
     assertEquals(
         2, CryptoKeys.parseX509Certs(IOUtils.toInputStream(cert, StandardCharsets.UTF_8)).size());
diff --git a/solr/core/src/test/org/apache/solr/security/JWTIssuerConfigTest.java b/solr/modules/jwt-auth/src/test/org/apache/solr/security/jwt/JWTIssuerConfigTest.java
similarity index 95%
rename from solr/core/src/test/org/apache/solr/security/JWTIssuerConfigTest.java
rename to solr/modules/jwt-auth/src/test/org/apache/solr/security/jwt/JWTIssuerConfigTest.java
index f15f4e4..d51ae82 100644
--- a/solr/core/src/test/org/apache/solr/security/JWTIssuerConfigTest.java
+++ b/solr/modules/jwt-auth/src/test/org/apache/solr/security/jwt/JWTIssuerConfigTest.java
@@ -15,10 +15,10 @@
  * limitations under the License.
  */
 
-package org.apache.solr.security;
+package org.apache.solr.security.jwt;
 
-import static org.apache.solr.SolrTestCaseJ4.TEST_PATH;
-import static org.apache.solr.security.JWTAuthPluginTest.testJwk;
+import static org.apache.solr.security.jwt.JWTAuthPluginTest.JWT_TEST_PATH;
+import static org.apache.solr.security.jwt.JWTAuthPluginTest.testJwk;
 
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
@@ -157,7 +157,7 @@ public class JWTIssuerConfigTest extends SolrTestCase {
 
   @Test
   public void wellKnownConfigFromInputstream() throws IOException {
-    Path configJson = TEST_PATH().resolve("security").resolve("jwt_well-known-config.json");
+    Path configJson = JWT_TEST_PATH().resolve("security").resolve("jwt_well-known-config.json");
     JWTIssuerConfig.WellKnownDiscoveryConfig config =
         JWTIssuerConfig.WellKnownDiscoveryConfig.parse(Files.newInputStream(configJson));
     assertEquals("https://acmepaymentscorp/oauth/jwks", config.getJwksUrl());
@@ -165,7 +165,7 @@ public class JWTIssuerConfigTest extends SolrTestCase {
 
   @Test
   public void wellKnownConfigFromString() throws IOException {
-    Path configJson = TEST_PATH().resolve("security").resolve("jwt_well-known-config.json");
+    Path configJson = JWT_TEST_PATH().resolve("security").resolve("jwt_well-known-config.json");
     String configString = StringUtils.join(Files.readAllLines(configJson), "\n");
     JWTIssuerConfig.WellKnownDiscoveryConfig config =
         JWTIssuerConfig.WellKnownDiscoveryConfig.parse(configString, StandardCharsets.UTF_8);
diff --git a/solr/core/src/test/org/apache/solr/security/JWTVerificationkeyResolverTest.java b/solr/modules/jwt-auth/src/test/org/apache/solr/security/jwt/JWTVerificationkeyResolverTest.java
similarity index 95%
rename from solr/core/src/test/org/apache/solr/security/JWTVerificationkeyResolverTest.java
rename to solr/modules/jwt-auth/src/test/org/apache/solr/security/jwt/JWTVerificationkeyResolverTest.java
index c24b9d8..3de70e6 100644
--- a/solr/core/src/test/org/apache/solr/security/JWTVerificationkeyResolverTest.java
+++ b/solr/modules/jwt-auth/src/test/org/apache/solr/security/jwt/JWTVerificationkeyResolverTest.java
@@ -15,10 +15,9 @@
  * limitations under the License.
  */
 
-package org.apache.solr.security;
+package org.apache.solr.security.jwt;
 
 import static java.util.Arrays.asList;
-import static org.mockito.ArgumentMatchers.anyList;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.when;
 
@@ -26,7 +25,7 @@ import java.util.Arrays;
 import java.util.Iterator;
 import java.util.List;
 import org.apache.solr.SolrTestCaseJ4;
-import org.apache.solr.security.JWTIssuerConfig.HttpsJwksFactory;
+import org.apache.solr.security.jwt.JWTIssuerConfig.HttpsJwksFactory;
 import org.jose4j.jwk.HttpsJwks;
 import org.jose4j.jwk.JsonWebKey;
 import org.jose4j.jwk.RsaJsonWebKey;
@@ -38,6 +37,7 @@ import org.jose4j.lang.UnresolvableKeyException;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
+import org.mockito.ArgumentMatchers;
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
@@ -85,7 +85,8 @@ public class JWTVerificationkeyResolverTest extends SolrTestCaseJ4 {
                 keysToReturnFromSecondJwk = refreshSequenceForSecondJwk.next();
               return keysToReturnFromSecondJwk;
             });
-    when(httpsJwksFactory.createList(anyList())).thenReturn(asList(firstJwkList, secondJwkList));
+    when(httpsJwksFactory.createList(ArgumentMatchers.anyList()))
+        .thenReturn(asList(firstJwkList, secondJwkList));
 
     JWTIssuerConfig issuerConfig =
         new JWTIssuerConfig("primary").setIss("foo").setJwksUrl(asList("url1", "url2"));
diff --git a/solr/modules/langid/build.gradle b/solr/modules/langid/build.gradle
index 5576d9d..a88a202 100644
--- a/solr/modules/langid/build.gradle
+++ b/solr/modules/langid/build.gradle
@@ -28,8 +28,7 @@ dependencies {
   implementation 'com.cybozu.labs:langdetect'
   implementation 'net.arnx:jsonic'
   implementation 'org.apache.opennlp:opennlp-tools'
-  implementation 'org.slf4j:slf4j-api'
-
+implementation 'org.slf4j:slf4j-api'
   testImplementation project(':solr:test-framework')
   testImplementation 'com.carrotsearch.randomizedtesting:randomizedtesting-runner'
   testImplementation 'junit:junit'
diff --git a/solr/packaging/build.gradle b/solr/packaging/build.gradle
index 40006f0..da324d8 100644
--- a/solr/packaging/build.gradle
+++ b/solr/packaging/build.gradle
@@ -53,6 +53,7 @@ dependencies {
    ":solr:modules:gcs-repository",
    ":solr:modules:hdfs",
    ":solr:modules:jaegertracer-configurator",
+   ":solr:modules:jwt-auth",
    ":solr:modules:langid",
    ":solr:modules:ltr",
    ":solr:modules:s3-repository",
diff --git a/solr/prometheus-exporter/build.gradle b/solr/prometheus-exporter/build.gradle
index ecd1437..9402fce 100644
--- a/solr/prometheus-exporter/build.gradle
+++ b/solr/prometheus-exporter/build.gradle
@@ -22,7 +22,6 @@ description = 'Prometheus exporter for exposing metrics from Solr using Metrics
 
 dependencies {
   implementation project(':solr:solrj')
-  implementation 'org.slf4j:slf4j-api'
   implementation('org.apache.zookeeper:zookeeper') { transitive = false } // ideally remove ZK dep
 
   implementation ('io.prometheus:simpleclient')
@@ -36,6 +35,7 @@ dependencies {
   implementation ('net.sourceforge.argparse4j:argparse4j')
   implementation ('com.github.ben-manes.caffeine:caffeine') { transitive = false }
   implementation 'commons-io:commons-io'
+  implementation 'org.slf4j:slf4j-api'
 
   runtimeOnly 'org.apache.logging.log4j:log4j-api'
   runtimeOnly 'org.apache.logging.log4j:log4j-core'
diff --git a/solr/solr-ref-guide/src/jwt-authentication-plugin.adoc b/solr/solr-ref-guide/src/jwt-authentication-plugin.adoc
index bcf3d72..c928e60 100644
--- a/solr/solr-ref-guide/src/jwt-authentication-plugin.adoc
+++ b/solr/solr-ref-guide/src/jwt-authentication-plugin.adoc
@@ -21,6 +21,11 @@ Solr can support https://en.wikipedia.org/wiki/JSON_Web_Token[JSON Web Token] (J
 This allows Solr to assert that a user is already authenticated with an external https://en.wikipedia.org/wiki/Identity_provider[Identity Provider] by validating that the JWT formatted https://en.wikipedia.org/wiki/Access_token[access token] is digitally signed by the Identity Provider.
 The typical use case is to integrate Solr with an https://en.wikipedia.org/wiki/OpenID_Connect[OpenID Connect] enabled IdP.
 
+== Module
+
+This is provided via a Solr Module that needs to be added to the classpath before use. Since this is a node-level
+plugin it must go in `sharedLib`, see <<configuring-solr-xml.adoc#,Configuring solr.xml>> for details.
+
 == Enable JWT Authentication
 
 To use JWT Bearer authentication, the `security.json` file must have an `authentication` part which defines the class being used for authentication along with configuration parameters.
diff --git a/solr/solr-ref-guide/src/major-changes-in-solr-9.adoc b/solr/solr-ref-guide/src/major-changes-in-solr-9.adoc
index 1b9893a..be50a72 100644
--- a/solr/solr-ref-guide/src/major-changes-in-solr-9.adoc
+++ b/solr/solr-ref-guide/src/major-changes-in-solr-9.adoc
@@ -110,6 +110,9 @@ This is only applicable for users returning information in JSON format, which is
 * SOLR-14660: HDFS storage support has been moved to a module. Existing Solr configurations do not need any HDFS-related
 changes, however the module needs to be installed - see <<solr-on-hdfs.adoc#,Running Solr on HDFS>>.
 
+* SOLR-15097: JWTAuthPlugin has been moved to a module. Users need to add the module to classpath. The plugin has also
+  changed package name to `org.apache.solr.security.jwt`, but can still be loaded as shortform `class="solr.JWTAuthPlugin"`.
+
 == New Features & Enhancements
 
 * Replica placement plugins
diff --git a/solr/test-framework/src/java/org/apache/solr/cloud/MiniSolrCloudCluster.java b/solr/test-framework/src/java/org/apache/solr/cloud/MiniSolrCloudCluster.java
index aa9e533..d11d22e 100644
--- a/solr/test-framework/src/java/org/apache/solr/cloud/MiniSolrCloudCluster.java
+++ b/solr/test-framework/src/java/org/apache/solr/cloud/MiniSolrCloudCluster.java
@@ -644,7 +644,18 @@ public class MiniSolrCloudCluster {
   public SolrZkClient getZkClient() {
     return solrClient.getZkStateReader().getZkClient();
   }
-  
+
+  /**
+   * Set data in zk without exposing caller to the ZK API, i.e. tests won't need to include Zookeeper dependencies
+   */
+  public void zkSetData(String path, byte[] data, boolean retryOnConnLoss) throws InterruptedException {
+    try {
+      getZkClient().setData(path, data, -1, retryOnConnLoss);
+    } catch (KeeperException e) {
+      throw new SolrException(ErrorCode.UNKNOWN, "Failed writing to Zookeeper", e);
+    }
+  }
+
   protected CloudSolrClient buildSolrClient() {
     return new CloudSolrClient.Builder(Collections.singletonList(getZkServer().getZkAddress()), Optional.empty())
         .withSocketTimeout(90000).withConnectionTimeout(15000).build(); // we choose 90 because we run in some harsh envs
diff --git a/solr/test-framework/src/java/org/apache/solr/cloud/SolrCloudAuthTestCase.java b/solr/test-framework/src/java/org/apache/solr/cloud/SolrCloudAuthTestCase.java
index f918562..b8ff1e8 100644
--- a/solr/test-framework/src/java/org/apache/solr/cloud/SolrCloudAuthTestCase.java
+++ b/solr/test-framework/src/java/org/apache/solr/cloud/SolrCloudAuthTestCase.java
@@ -44,8 +44,6 @@ import org.apache.solr.client.solrj.embedded.JettySolrRunner;
 import org.apache.solr.common.util.StrUtils;
 import org.apache.solr.common.util.Utils;
 import org.apache.solr.util.TimeOut;
-import org.jose4j.jws.JsonWebSignature;
-import org.jose4j.lang.JoseException;
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
 import org.slf4j.Logger;
@@ -195,15 +193,9 @@ public class SolrCloudAuthTestCase extends SolrCloudTestCase {
     verifySecurityStatus(cl, url, objPath, expected, count, makeBasicAuthHeader(user, pwd));
   }
 
-  protected void verifySecurityStatus(HttpClient cl, String url, String objPath,
-                                      Object expected, int count, JsonWebSignature jws) throws Exception {
-    verifySecurityStatus(cl, url, objPath, expected, count, getBearerAuthHeader(jws));
-  }
-
-
   @SuppressWarnings({"unchecked"})
-  private static void verifySecurityStatus(HttpClient cl, String url, String objPath,
-                                            Object expected, int count, String authHeader) throws IOException, InterruptedException {
+  protected static void verifySecurityStatus(HttpClient cl, String url, String objPath,
+                                             Object expected, int count, String authHeader) throws IOException, InterruptedException {
     boolean success = false;
     String s = null;
     List<String> hierarchy = StrUtils.splitSmart(objPath, '/');
@@ -242,10 +234,6 @@ public class SolrCloudAuthTestCase extends SolrCloudTestCase {
     return "Basic " + Base64.getEncoder().encodeToString(userPass.getBytes(UTF_8));
   }
 
-  static String getBearerAuthHeader(JsonWebSignature jws) throws JoseException {
-    return "Bearer " + jws.getCompactSerialization();
-  }
-  
   public static void setAuthorizationHeader(AbstractHttpMessage httpMsg, String headerString) {
     httpMsg.setHeader(new BasicHeader("Authorization", headerString));
     log.info("Added Authorization Header {}", headerString);
diff --git a/versions.lock b/versions.lock
index b673d06..14d5eea 100644
--- a/versions.lock
+++ b/versions.lock
@@ -70,12 +70,12 @@ io.dropwizard.metrics:metrics-jvm:4.1.5 (1 constraints: 0c050736)
 io.grpc:grpc-context:1.36.0 (3 constraints: 5a22231e)
 io.jaegertracing:jaeger-core:1.6.0 (2 constraints: 6912c420)
 io.jaegertracing:jaeger-thrift:1.6.0 (1 constraints: 09050236)
-io.netty:netty-buffer:4.1.68.Final (5 constraints: df4da774)
-io.netty:netty-codec:4.1.68.Final (1 constraints: a60ccb09)
-io.netty:netty-common:4.1.68.Final (7 constraints: 59675dcf)
-io.netty:netty-handler:4.1.68.Final (1 constraints: db0f158f)
+io.netty:netty-buffer:4.1.68.Final (6 constraints: 915bfcb7)
+io.netty:netty-codec:4.1.68.Final (2 constraints: 581a4d48)
+io.netty:netty-common:4.1.68.Final (8 constraints: 0b75841f)
+io.netty:netty-handler:4.1.68.Final (2 constraints: 8d1d363b)
 io.netty:netty-resolver:4.1.68.Final (2 constraints: 5a1a4732)
-io.netty:netty-transport:4.1.68.Final (4 constraints: 2b402b76)
+io.netty:netty-transport:4.1.68.Final (5 constraints: dd4d9295)
 io.netty:netty-transport-native-epoll:4.1.68.Final (1 constraints: db0f158f)
 io.netty:netty-transport-native-unix-common:4.1.68.Final (1 constraints: b212fc1e)
 io.opencensus:opencensus-api:0.28.0 (5 constraints: 21426a37)
@@ -295,6 +295,7 @@ com.vaadin.external.google:android-json:0.0.20131108.vaadin1 (1 constraints: 340
 io.github.microutils:kotlin-logging:2.0.6 (1 constraints: be0e9d62)
 io.github.microutils:kotlin-logging-jvm:2.0.6 (1 constraints: 810f877c)
 io.micrometer:micrometer-core:1.5.14 (1 constraints: fc161b19)
+io.netty:netty-codec-http:4.1.68.Final (1 constraints: 5d077961)
 io.opentracing:opentracing-mock:0.33.0 (1 constraints: 3805343b)
 jakarta.activation:jakarta.activation-api:1.2.1 (2 constraints: b928fbbd)
 jakarta.annotation:jakarta.annotation-api:1.3.5 (1 constraints: 3a131133)

[solr] 03/04: SOLR-15907 Upgrade JWT dependencies mock-oauth2-server and jose4j

Posted by ja...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

janhoy pushed a commit to branch branch_9x
in repository https://gitbox.apache.org/repos/asf/solr.git

commit 88893cbb11dbb554527d58dded0e63e0af40ea31
Author: Jan Høydahl <ja...@users.noreply.github.com>
AuthorDate: Wed Feb 9 01:43:25 2022 +0100

    SOLR-15907 Upgrade JWT dependencies mock-oauth2-server and jose4j
---
 solr/licenses/accessors-smart-1.2.jar.sha1         |   1 -
 solr/licenses/accessors-smart-2.4.7.jar.sha1       |   1 +
 solr/licenses/asm-7.2.jar.sha1                     |   1 -
 solr/licenses/asm-9.1.jar.sha1                     |   1 +
 solr/licenses/asm-commons-7.2.jar.sha1             |   1 -
 solr/licenses/asm-commons-9.1.jar.sha1             |   1 +
 ...LICENSE-BSD_LIKE.txt => bcmail-LICENSE-MIT.txt} |   0
 solr/licenses/bcmail-jdk15on-1.69.jar.sha1         |   1 -
 solr/licenses/bcmail-jdk15on-1.70.jar.sha1         |   1 +
 ...LICENSE-BSD_LIKE.txt => bcpkix-LICENSE-MIT.txt} |   0
 ...bcpkix-jdk15on-NOTICE.txt => bcpkix-NOTICE.txt} |   0
 solr/licenses/bcpkix-jdk15on-1.69.jar.sha1         |   1 -
 solr/licenses/bcpkix-jdk15on-1.70.jar.sha1         |   1 +
 ...LICENSE-BSD_LIKE.txt => bcprov-LICENSE-MIT.txt} |   0
 solr/licenses/bcprov-jdk15on-1.69.jar.sha1         |   1 -
 solr/licenses/bcprov-jdk15on-1.70.jar.sha1         |   1 +
 ...LICENSE-BSD_LIKE.txt => bcutil-LICENSE-MIT.txt} |   2 +-
 solr/licenses/bcutil-jdk15on-1.70.jar.sha1         |   1 +
 solr/licenses/content-type-2.1.jar.sha1            |   1 -
 solr/licenses/content-type-2.2.jar.sha1            |   1 +
 solr/licenses/jackson-annotations-2.12.3.jar.sha1  |   1 -
 solr/licenses/jackson-annotations-2.13.1.jar.sha1  |   1 +
 solr/licenses/jackson-core-2.12.3.jar.sha1         |   1 -
 solr/licenses/jackson-core-2.13.1.jar.sha1         |   1 +
 solr/licenses/jackson-databind-2.12.3.jar.sha1     |   1 -
 solr/licenses/jackson-databind-2.13.1.jar.sha1     |   1 +
 .../jackson-dataformat-cbor-2.12.3.jar.sha1        |   1 -
 .../jackson-dataformat-cbor-2.13.1.jar.sha1        |   1 +
 .../jackson-dataformat-smile-2.12.3.jar.sha1       |   1 -
 .../jackson-dataformat-smile-2.13.1.jar.sha1       |   1 +
 .../jackson-dataformat-xml-2.12.3.jar.sha1         |   1 -
 .../jackson-dataformat-xml-2.13.1.jar.sha1         |   1 +
 .../licenses/jackson-datatype-jdk8-2.12.3.jar.sha1 |   1 -
 .../licenses/jackson-datatype-jdk8-2.13.1.jar.sha1 |   1 +
 .../jackson-datatype-jsr310-2.12.3.jar.sha1        |   1 -
 .../jackson-datatype-jsr310-2.13.1.jar.sha1        |   1 +
 ...jackson-module-jaxb-annotations-2.12.3.jar.sha1 |   1 -
 ...jackson-module-jaxb-annotations-LICENSE-ASL.txt | 201 ---------------------
 .../jackson-module-jaxb-annotations-NOTICE.txt     |   0
 .../licenses/jackson-module-kotlin-2.12.3.jar.sha1 |   1 -
 .../licenses/jackson-module-kotlin-2.13.1.jar.sha1 |   1 +
 .../jackson-module-parameter-names-2.12.3.jar.sha1 |   1 -
 .../jackson-module-parameter-names-2.13.1.jar.sha1 |   1 +
 .../licenses/jakarta.activation-api-1.2.1.jar.sha1 |   1 -
 .../jakarta.activation-api-LICENSE-BSD_LIKE.txt    |  13 --
 solr/licenses/jakarta.activation-api-NOTICE.txt    |  25 ---
 solr/licenses/jakarta.xml.bind-api-2.3.2.jar.sha1  |   1 -
 .../jakarta.xml.bind-api-LICENSE-BSD_LIKE.txt      |  13 --
 solr/licenses/jakarta.xml.bind-api-NOTICE.txt      |  44 -----
 solr/licenses/jose4j-0.7.7.jar.sha1                |   1 -
 solr/licenses/jose4j-0.7.9.jar.sha1                |   1 +
 solr/licenses/json-smart-2.3.jar.sha1              |   1 -
 solr/licenses/json-smart-2.4.7.jar.sha1            |   1 +
 solr/licenses/kotlin-logging-jvm-2.0.6.jar.sha1    |   1 -
 solr/licenses/kotlin-logging-jvm-2.1.21.jar.sha1   |   1 +
 solr/licenses/kotlin-reflect-1.4.32.jar.sha1       |   1 -
 solr/licenses/kotlin-reflect-1.6.10.jar.sha1       |   1 +
 solr/licenses/kotlin-stdlib-1.4.32.jar.sha1        |   1 -
 solr/licenses/kotlin-stdlib-1.6.10.jar.sha1        |   1 +
 solr/licenses/kotlin-stdlib-common-1.4.32.jar.sha1 |   1 -
 solr/licenses/kotlin-stdlib-common-1.6.10.jar.sha1 |   1 +
 solr/licenses/kotlin-stdlib-jdk7-1.4.10.jar.sha1   |   1 -
 solr/licenses/kotlin-stdlib-jdk7-1.6.0.jar.sha1    |   1 +
 solr/licenses/kotlin-stdlib-jdk8-1.4.10.jar.sha1   |   1 -
 solr/licenses/kotlin-stdlib-jdk8-1.6.0.jar.sha1    |   1 +
 solr/licenses/lang-tag-1.4.4.jar.sha1              |   1 -
 solr/licenses/lang-tag-1.5.jar.sha1                |   1 +
 solr/licenses/mock-oauth2-server-0.3.3.jar.sha1    |   1 -
 solr/licenses/mock-oauth2-server-0.4.3.jar.sha1    |   1 +
 solr/licenses/mockwebserver-4.9.1.jar.sha1         |   1 -
 solr/licenses/mockwebserver-4.9.3.jar.sha1         |   1 +
 solr/licenses/nimbus-jose-jwt-9.16.1.jar.sha1      |   1 +
 solr/licenses/nimbus-jose-jwt-9.6.1.jar.sha1       |   1 -
 solr/licenses/oauth2-oidc-sdk-9.2.3.jar.sha1       |   1 -
 solr/licenses/oauth2-oidc-sdk-9.22.2.jar.sha1      |   1 +
 solr/licenses/okhttp-4.9.1.jar.sha1                |   1 -
 solr/licenses/okhttp-4.9.3.jar.sha1                |   1 +
 solr/licenses/woodstox-core-6.2.4.jar.sha1         |   1 -
 solr/licenses/woodstox-core-6.2.7.jar.sha1         |   1 +
 .../security/jwt/JWTAuthPluginIntegrationTest.java |   8 +-
 versions.lock                                      |  76 ++++----
 versions.props                                     |   4 +-
 82 files changed, 76 insertions(+), 378 deletions(-)

diff --git a/solr/licenses/accessors-smart-1.2.jar.sha1 b/solr/licenses/accessors-smart-1.2.jar.sha1
deleted file mode 100644
index 00907b4..0000000
--- a/solr/licenses/accessors-smart-1.2.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-c592b500269bfde36096641b01238a8350f8aa31
diff --git a/solr/licenses/accessors-smart-2.4.7.jar.sha1 b/solr/licenses/accessors-smart-2.4.7.jar.sha1
new file mode 100644
index 0000000..6d865c8
--- /dev/null
+++ b/solr/licenses/accessors-smart-2.4.7.jar.sha1
@@ -0,0 +1 @@
+3970cfc505e6657ca60f3aa57c849f6043000d7a
diff --git a/solr/licenses/asm-7.2.jar.sha1 b/solr/licenses/asm-7.2.jar.sha1
deleted file mode 100644
index ccb16fa..0000000
--- a/solr/licenses/asm-7.2.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-fa637eb67eb7628c915d73762b681ae7ff0b9731
diff --git a/solr/licenses/asm-9.1.jar.sha1 b/solr/licenses/asm-9.1.jar.sha1
new file mode 100644
index 0000000..5625582
--- /dev/null
+++ b/solr/licenses/asm-9.1.jar.sha1
@@ -0,0 +1 @@
+a99500cf6eea30535eeac6be73899d048f8d12a8
diff --git a/solr/licenses/asm-commons-7.2.jar.sha1 b/solr/licenses/asm-commons-7.2.jar.sha1
deleted file mode 100644
index 3db5dac..0000000
--- a/solr/licenses/asm-commons-7.2.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-ca2954e8d92a05bacc28ff465b25c70e0f512497
diff --git a/solr/licenses/asm-commons-9.1.jar.sha1 b/solr/licenses/asm-commons-9.1.jar.sha1
new file mode 100644
index 0000000..aac6596
--- /dev/null
+++ b/solr/licenses/asm-commons-9.1.jar.sha1
@@ -0,0 +1 @@
+8b971b182eb5cf100b9e8d4119152d83e00e0fdd
diff --git a/solr/licenses/bcprov-LICENSE-BSD_LIKE.txt b/solr/licenses/bcmail-LICENSE-MIT.txt
similarity index 100%
rename from solr/licenses/bcprov-LICENSE-BSD_LIKE.txt
rename to solr/licenses/bcmail-LICENSE-MIT.txt
diff --git a/solr/licenses/bcmail-jdk15on-1.69.jar.sha1 b/solr/licenses/bcmail-jdk15on-1.69.jar.sha1
deleted file mode 100644
index a2b1f8d..0000000
--- a/solr/licenses/bcmail-jdk15on-1.69.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-759f9233ac99a576e4963bd09b0f830a2716d2ec
diff --git a/solr/licenses/bcmail-jdk15on-1.70.jar.sha1 b/solr/licenses/bcmail-jdk15on-1.70.jar.sha1
new file mode 100644
index 0000000..acc49ba
--- /dev/null
+++ b/solr/licenses/bcmail-jdk15on-1.70.jar.sha1
@@ -0,0 +1 @@
+08f4aafad90f6cc7f16b9992279828ae848c9e0d
diff --git a/solr/licenses/bcpkix-jdk15on-LICENSE-BSD_LIKE.txt b/solr/licenses/bcpkix-LICENSE-MIT.txt
similarity index 100%
copy from solr/licenses/bcpkix-jdk15on-LICENSE-BSD_LIKE.txt
copy to solr/licenses/bcpkix-LICENSE-MIT.txt
diff --git a/solr/licenses/bcpkix-jdk15on-NOTICE.txt b/solr/licenses/bcpkix-NOTICE.txt
similarity index 100%
rename from solr/licenses/bcpkix-jdk15on-NOTICE.txt
rename to solr/licenses/bcpkix-NOTICE.txt
diff --git a/solr/licenses/bcpkix-jdk15on-1.69.jar.sha1 b/solr/licenses/bcpkix-jdk15on-1.69.jar.sha1
deleted file mode 100644
index 76113f2..0000000
--- a/solr/licenses/bcpkix-jdk15on-1.69.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-45c36fb72fafb0b688c6af795e6cc803f6f79ee5
diff --git a/solr/licenses/bcpkix-jdk15on-1.70.jar.sha1 b/solr/licenses/bcpkix-jdk15on-1.70.jar.sha1
new file mode 100644
index 0000000..07cad6d
--- /dev/null
+++ b/solr/licenses/bcpkix-jdk15on-1.70.jar.sha1
@@ -0,0 +1 @@
+f81e5af49571a9d5a109a88f239a73ce87055417
diff --git a/solr/licenses/bcmail-LICENSE-BSD_LIKE.txt b/solr/licenses/bcprov-LICENSE-MIT.txt
similarity index 100%
rename from solr/licenses/bcmail-LICENSE-BSD_LIKE.txt
rename to solr/licenses/bcprov-LICENSE-MIT.txt
diff --git a/solr/licenses/bcprov-jdk15on-1.69.jar.sha1 b/solr/licenses/bcprov-jdk15on-1.69.jar.sha1
deleted file mode 100644
index 865e022..0000000
--- a/solr/licenses/bcprov-jdk15on-1.69.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-91e1628251cf3ca90093ce9d0fe67e5b7dab3850
diff --git a/solr/licenses/bcprov-jdk15on-1.70.jar.sha1 b/solr/licenses/bcprov-jdk15on-1.70.jar.sha1
new file mode 100644
index 0000000..bef2daf
--- /dev/null
+++ b/solr/licenses/bcprov-jdk15on-1.70.jar.sha1
@@ -0,0 +1 @@
+4636a0d01f74acaf28082fb62b317f1080118371
diff --git a/solr/licenses/bcpkix-jdk15on-LICENSE-BSD_LIKE.txt b/solr/licenses/bcutil-LICENSE-MIT.txt
similarity index 94%
rename from solr/licenses/bcpkix-jdk15on-LICENSE-BSD_LIKE.txt
rename to solr/licenses/bcutil-LICENSE-MIT.txt
index 9dfdf7c..f40b132 100644
--- a/solr/licenses/bcpkix-jdk15on-LICENSE-BSD_LIKE.txt
+++ b/solr/licenses/bcutil-LICENSE-MIT.txt
@@ -1,4 +1,4 @@
-Copyright (c) 2000-2010 The Legion Of The Bouncy Castle (http://www.bouncycastle.org)
+Copyright (c) 2000-2021 The Legion Of The Bouncy Castle (http://www.bouncycastle.org)
 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
 associated documentation files (the "Software"), to deal in the Software without restriction,
 including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
diff --git a/solr/licenses/bcutil-jdk15on-1.70.jar.sha1 b/solr/licenses/bcutil-jdk15on-1.70.jar.sha1
new file mode 100644
index 0000000..73d7873
--- /dev/null
+++ b/solr/licenses/bcutil-jdk15on-1.70.jar.sha1
@@ -0,0 +1 @@
+54280e7195a7430d7911ded93fc01e07300b9526
diff --git a/solr/licenses/content-type-2.1.jar.sha1 b/solr/licenses/content-type-2.1.jar.sha1
deleted file mode 100644
index efc8504..0000000
--- a/solr/licenses/content-type-2.1.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-66d618739859bc75ab9643b96a9839ac7802ec90
diff --git a/solr/licenses/content-type-2.2.jar.sha1 b/solr/licenses/content-type-2.2.jar.sha1
new file mode 100644
index 0000000..77741b9
--- /dev/null
+++ b/solr/licenses/content-type-2.2.jar.sha1
@@ -0,0 +1 @@
+9a894bce7646dd4086652d85b88013229f23724b
diff --git a/solr/licenses/jackson-annotations-2.12.3.jar.sha1 b/solr/licenses/jackson-annotations-2.12.3.jar.sha1
deleted file mode 100644
index de7b361..0000000
--- a/solr/licenses/jackson-annotations-2.12.3.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-7275513412694a1aafd08c0287f48469fa0e6e17
diff --git a/solr/licenses/jackson-annotations-2.13.1.jar.sha1 b/solr/licenses/jackson-annotations-2.13.1.jar.sha1
new file mode 100644
index 0000000..80ab910
--- /dev/null
+++ b/solr/licenses/jackson-annotations-2.13.1.jar.sha1
@@ -0,0 +1 @@
+1cbcbe4623113e6af92ccaa89884a345270f1a87
diff --git a/solr/licenses/jackson-core-2.12.3.jar.sha1 b/solr/licenses/jackson-core-2.12.3.jar.sha1
deleted file mode 100644
index 8d21e9b..0000000
--- a/solr/licenses/jackson-core-2.12.3.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-deb23fe2a7f2b773e18ced2b50d4acc1df8fa366
diff --git a/solr/licenses/jackson-core-2.13.1.jar.sha1 b/solr/licenses/jackson-core-2.13.1.jar.sha1
new file mode 100644
index 0000000..983ca6a
--- /dev/null
+++ b/solr/licenses/jackson-core-2.13.1.jar.sha1
@@ -0,0 +1 @@
+51ae921a2ed1e06ca8876f12f32f265e83c0b2b8
diff --git a/solr/licenses/jackson-databind-2.12.3.jar.sha1 b/solr/licenses/jackson-databind-2.12.3.jar.sha1
deleted file mode 100644
index 3c762ab..0000000
--- a/solr/licenses/jackson-databind-2.12.3.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-d6153f8fc60c479ab0f9efb35c034526436a4953
diff --git a/solr/licenses/jackson-databind-2.13.1.jar.sha1 b/solr/licenses/jackson-databind-2.13.1.jar.sha1
new file mode 100644
index 0000000..acf95e6
--- /dev/null
+++ b/solr/licenses/jackson-databind-2.13.1.jar.sha1
@@ -0,0 +1 @@
+698b2d2b15d9a1b7aae025f1d9f576842285e7f6
diff --git a/solr/licenses/jackson-dataformat-cbor-2.12.3.jar.sha1 b/solr/licenses/jackson-dataformat-cbor-2.12.3.jar.sha1
deleted file mode 100644
index 4edbe76..0000000
--- a/solr/licenses/jackson-dataformat-cbor-2.12.3.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-d72a74232dd4cb9cd7b07fdec3bc87bf0078491f
diff --git a/solr/licenses/jackson-dataformat-cbor-2.13.1.jar.sha1 b/solr/licenses/jackson-dataformat-cbor-2.13.1.jar.sha1
new file mode 100644
index 0000000..831df05
--- /dev/null
+++ b/solr/licenses/jackson-dataformat-cbor-2.13.1.jar.sha1
@@ -0,0 +1 @@
+35cc80045c638fe6a383d64c87f2d513502abffa
diff --git a/solr/licenses/jackson-dataformat-smile-2.12.3.jar.sha1 b/solr/licenses/jackson-dataformat-smile-2.12.3.jar.sha1
deleted file mode 100644
index 0610c41..0000000
--- a/solr/licenses/jackson-dataformat-smile-2.12.3.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-f9be9f3ee1a04aafc501f1cf6850b18852f2178a
diff --git a/solr/licenses/jackson-dataformat-smile-2.13.1.jar.sha1 b/solr/licenses/jackson-dataformat-smile-2.13.1.jar.sha1
new file mode 100644
index 0000000..f7cfdd9
--- /dev/null
+++ b/solr/licenses/jackson-dataformat-smile-2.13.1.jar.sha1
@@ -0,0 +1 @@
+439357c78116a9c4e5dfff10e2eb4dea2b776c2f
diff --git a/solr/licenses/jackson-dataformat-xml-2.12.3.jar.sha1 b/solr/licenses/jackson-dataformat-xml-2.12.3.jar.sha1
deleted file mode 100644
index 2bd78bd..0000000
--- a/solr/licenses/jackson-dataformat-xml-2.12.3.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-d2a90460fecffa0c7ae9fd836272bbce91b5241a
diff --git a/solr/licenses/jackson-dataformat-xml-2.13.1.jar.sha1 b/solr/licenses/jackson-dataformat-xml-2.13.1.jar.sha1
new file mode 100644
index 0000000..a4531b8
--- /dev/null
+++ b/solr/licenses/jackson-dataformat-xml-2.13.1.jar.sha1
@@ -0,0 +1 @@
+bcc5e3a3056e6e98020856b52e53895ad5fdfc4b
diff --git a/solr/licenses/jackson-datatype-jdk8-2.12.3.jar.sha1 b/solr/licenses/jackson-datatype-jdk8-2.12.3.jar.sha1
deleted file mode 100644
index 51220d4..0000000
--- a/solr/licenses/jackson-datatype-jdk8-2.12.3.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-77424ea087313312e308dae5ff8445608aabb5e1
diff --git a/solr/licenses/jackson-datatype-jdk8-2.13.1.jar.sha1 b/solr/licenses/jackson-datatype-jdk8-2.13.1.jar.sha1
new file mode 100644
index 0000000..2e1499b
--- /dev/null
+++ b/solr/licenses/jackson-datatype-jdk8-2.13.1.jar.sha1
@@ -0,0 +1 @@
+8ecfa9bcd714269fdf22c33f9fd00d0643bd0e21
diff --git a/solr/licenses/jackson-datatype-jsr310-2.12.3.jar.sha1 b/solr/licenses/jackson-datatype-jsr310-2.12.3.jar.sha1
deleted file mode 100644
index 899e236..0000000
--- a/solr/licenses/jackson-datatype-jsr310-2.12.3.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-f69c636438dcf19c49960c1fe8901320ab85f989
diff --git a/solr/licenses/jackson-datatype-jsr310-2.13.1.jar.sha1 b/solr/licenses/jackson-datatype-jsr310-2.13.1.jar.sha1
new file mode 100644
index 0000000..0598f30
--- /dev/null
+++ b/solr/licenses/jackson-datatype-jsr310-2.13.1.jar.sha1
@@ -0,0 +1 @@
+1ece5a87b59701328215e0083448b4d451857cbd
diff --git a/solr/licenses/jackson-module-jaxb-annotations-2.12.3.jar.sha1 b/solr/licenses/jackson-module-jaxb-annotations-2.12.3.jar.sha1
deleted file mode 100644
index e9823b1..0000000
--- a/solr/licenses/jackson-module-jaxb-annotations-2.12.3.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-ea843951d2efdd5e6844d6faa8aabf71698463a8
diff --git a/solr/licenses/jackson-module-jaxb-annotations-LICENSE-ASL.txt b/solr/licenses/jackson-module-jaxb-annotations-LICENSE-ASL.txt
deleted file mode 100644
index 261eeb9..0000000
--- a/solr/licenses/jackson-module-jaxb-annotations-LICENSE-ASL.txt
+++ /dev/null
@@ -1,201 +0,0 @@
-                                 Apache License
-                           Version 2.0, January 2004
-                        http://www.apache.org/licenses/
-
-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-   1. Definitions.
-
-      "License" shall mean the terms and conditions for use, reproduction,
-      and distribution as defined by Sections 1 through 9 of this document.
-
-      "Licensor" shall mean the copyright owner or entity authorized by
-      the copyright owner that is granting the License.
-
-      "Legal Entity" shall mean the union of the acting entity and all
-      other entities that control, are controlled by, or are under common
-      control with that entity. For the purposes of this definition,
-      "control" means (i) the power, direct or indirect, to cause the
-      direction or management of such entity, whether by contract or
-      otherwise, or (ii) ownership of fifty percent (50%) or more of the
-      outstanding shares, or (iii) beneficial ownership of such entity.
-
-      "You" (or "Your") shall mean an individual or Legal Entity
-      exercising permissions granted by this License.
-
-      "Source" form shall mean the preferred form for making modifications,
-      including but not limited to software source code, documentation
-      source, and configuration files.
-
-      "Object" form shall mean any form resulting from mechanical
-      transformation or translation of a Source form, including but
-      not limited to compiled object code, generated documentation,
-      and conversions to other media types.
-
-      "Work" shall mean the work of authorship, whether in Source or
-      Object form, made available under the License, as indicated by a
-      copyright notice that is included in or attached to the work
-      (an example is provided in the Appendix below).
-
-      "Derivative Works" shall mean any work, whether in Source or Object
-      form, that is based on (or derived from) the Work and for which the
-      editorial revisions, annotations, elaborations, or other modifications
-      represent, as a whole, an original work of authorship. For the purposes
-      of this License, Derivative Works shall not include works that remain
-      separable from, or merely link (or bind by name) to the interfaces of,
-      the Work and Derivative Works thereof.
-
-      "Contribution" shall mean any work of authorship, including
-      the original version of the Work and any modifications or additions
-      to that Work or Derivative Works thereof, that is intentionally
-      submitted to Licensor for inclusion in the Work by the copyright owner
-      or by an individual or Legal Entity authorized to submit on behalf of
-      the copyright owner. For the purposes of this definition, "submitted"
-      means any form of electronic, verbal, or written communication sent
-      to the Licensor or its representatives, including but not limited to
-      communication on electronic mailing lists, source code control systems,
-      and issue tracking systems that are managed by, or on behalf of, the
-      Licensor for the purpose of discussing and improving the Work, but
-      excluding communication that is conspicuously marked or otherwise
-      designated in writing by the copyright owner as "Not a Contribution."
-
-      "Contributor" shall mean Licensor and any individual or Legal Entity
-      on behalf of whom a Contribution has been received by Licensor and
-      subsequently incorporated within the Work.
-
-   2. Grant of Copyright License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      copyright license to reproduce, prepare Derivative Works of,
-      publicly display, publicly perform, sublicense, and distribute the
-      Work and such Derivative Works in Source or Object form.
-
-   3. Grant of Patent License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      (except as stated in this section) patent license to make, have made,
-      use, offer to sell, sell, import, and otherwise transfer the Work,
-      where such license applies only to those patent claims licensable
-      by such Contributor that are necessarily infringed by their
-      Contribution(s) alone or by combination of their Contribution(s)
-      with the Work to which such Contribution(s) was submitted. If You
-      institute patent litigation against any entity (including a
-      cross-claim or counterclaim in a lawsuit) alleging that the Work
-      or a Contribution incorporated within the Work constitutes direct
-      or contributory patent infringement, then any patent licenses
-      granted to You under this License for that Work shall terminate
-      as of the date such litigation is filed.
-
-   4. Redistribution. You may reproduce and distribute copies of the
-      Work or Derivative Works thereof in any medium, with or without
-      modifications, and in Source or Object form, provided that You
-      meet the following conditions:
-
-      (a) You must give any other recipients of the Work or
-          Derivative Works a copy of this License; and
-
-      (b) You must cause any modified files to carry prominent notices
-          stating that You changed the files; and
-
-      (c) You must retain, in the Source form of any Derivative Works
-          that You distribute, all copyright, patent, trademark, and
-          attribution notices from the Source form of the Work,
-          excluding those notices that do not pertain to any part of
-          the Derivative Works; and
-
-      (d) If the Work includes a "NOTICE" text file as part of its
-          distribution, then any Derivative Works that You distribute must
-          include a readable copy of the attribution notices contained
-          within such NOTICE file, excluding those notices that do not
-          pertain to any part of the Derivative Works, in at least one
-          of the following places: within a NOTICE text file distributed
-          as part of the Derivative Works; within the Source form or
-          documentation, if provided along with the Derivative Works; or,
-          within a display generated by the Derivative Works, if and
-          wherever such third-party notices normally appear. The contents
-          of the NOTICE file are for informational purposes only and
-          do not modify the License. You may add Your own attribution
-          notices within Derivative Works that You distribute, alongside
-          or as an addendum to the NOTICE text from the Work, provided
-          that such additional attribution notices cannot be construed
-          as modifying the License.
-
-      You may add Your own copyright statement to Your modifications and
-      may provide additional or different license terms and conditions
-      for use, reproduction, or distribution of Your modifications, or
-      for any such Derivative Works as a whole, provided Your use,
-      reproduction, and distribution of the Work otherwise complies with
-      the conditions stated in this License.
-
-   5. Submission of Contributions. Unless You explicitly state otherwise,
-      any Contribution intentionally submitted for inclusion in the Work
-      by You to the Licensor shall be under the terms and conditions of
-      this License, without any additional terms or conditions.
-      Notwithstanding the above, nothing herein shall supersede or modify
-      the terms of any separate license agreement you may have executed
-      with Licensor regarding such Contributions.
-
-   6. Trademarks. This License does not grant permission to use the trade
-      names, trademarks, service marks, or product names of the Licensor,
-      except as required for reasonable and customary use in describing the
-      origin of the Work and reproducing the content of the NOTICE file.
-
-   7. Disclaimer of Warranty. Unless required by applicable law or
-      agreed to in writing, Licensor provides the Work (and each
-      Contributor provides its Contributions) on an "AS IS" BASIS,
-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-      implied, including, without limitation, any warranties or conditions
-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-      PARTICULAR PURPOSE. You are solely responsible for determining the
-      appropriateness of using or redistributing the Work and assume any
-      risks associated with Your exercise of permissions under this License.
-
-   8. Limitation of Liability. In no event and under no legal theory,
-      whether in tort (including negligence), contract, or otherwise,
-      unless required by applicable law (such as deliberate and grossly
-      negligent acts) or agreed to in writing, shall any Contributor be
-      liable to You for damages, including any direct, indirect, special,
-      incidental, or consequential damages of any character arising as a
-      result of this License or out of the use or inability to use the
-      Work (including but not limited to damages for loss of goodwill,
-      work stoppage, computer failure or malfunction, or any and all
-      other commercial damages or losses), even if such Contributor
-      has been advised of the possibility of such damages.
-
-   9. Accepting Warranty or Additional Liability. While redistributing
-      the Work or Derivative Works thereof, You may choose to offer,
-      and charge a fee for, acceptance of support, warranty, indemnity,
-      or other liability obligations and/or rights consistent with this
-      License. However, in accepting such obligations, You may act only
-      on Your own behalf and on Your sole responsibility, not on behalf
-      of any other Contributor, and only if You agree to indemnify,
-      defend, and hold each Contributor harmless for any liability
-      incurred by, or claims asserted against, such Contributor by reason
-      of your accepting any such warranty or additional liability.
-
-   END OF TERMS AND CONDITIONS
-
-   APPENDIX: How to apply the Apache License to your work.
-
-      To apply the Apache License to your work, attach the following
-      boilerplate notice, with the fields enclosed by brackets "[]"
-      replaced with your own identifying information. (Don't include
-      the brackets!)  The text should be enclosed in the appropriate
-      comment syntax for the file format. We also recommend that a
-      file or class name and description of purpose be included on the
-      same "printed page" as the copyright notice for easier
-      identification within third-party archives.
-
-   Copyright [yyyy] [name of copyright owner]
-
-   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.
diff --git a/solr/licenses/jackson-module-jaxb-annotations-NOTICE.txt b/solr/licenses/jackson-module-jaxb-annotations-NOTICE.txt
deleted file mode 100644
index e69de29..0000000
diff --git a/solr/licenses/jackson-module-kotlin-2.12.3.jar.sha1 b/solr/licenses/jackson-module-kotlin-2.12.3.jar.sha1
deleted file mode 100644
index 93fa80d..0000000
--- a/solr/licenses/jackson-module-kotlin-2.12.3.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-c8da7d998ed2e9253e62fdf15d1112c54b3ce6db
diff --git a/solr/licenses/jackson-module-kotlin-2.13.1.jar.sha1 b/solr/licenses/jackson-module-kotlin-2.13.1.jar.sha1
new file mode 100644
index 0000000..77b1cdd
--- /dev/null
+++ b/solr/licenses/jackson-module-kotlin-2.13.1.jar.sha1
@@ -0,0 +1 @@
+e8633bb172310b84462ebf586058b3babdbaf4b1
diff --git a/solr/licenses/jackson-module-parameter-names-2.12.3.jar.sha1 b/solr/licenses/jackson-module-parameter-names-2.12.3.jar.sha1
deleted file mode 100644
index da9bbb0..0000000
--- a/solr/licenses/jackson-module-parameter-names-2.12.3.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-592a882beaf1bd57b8fe960b937a2706b090b4d7
diff --git a/solr/licenses/jackson-module-parameter-names-2.13.1.jar.sha1 b/solr/licenses/jackson-module-parameter-names-2.13.1.jar.sha1
new file mode 100644
index 0000000..3125dfd
--- /dev/null
+++ b/solr/licenses/jackson-module-parameter-names-2.13.1.jar.sha1
@@ -0,0 +1 @@
+cbeec2259213c555ef451a2e05f35ed1dbfbf799
diff --git a/solr/licenses/jakarta.activation-api-1.2.1.jar.sha1 b/solr/licenses/jakarta.activation-api-1.2.1.jar.sha1
deleted file mode 100644
index 7e7decf..0000000
--- a/solr/licenses/jakarta.activation-api-1.2.1.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-562a587face36ec7eff2db7f2fc95425c6602bc1
diff --git a/solr/licenses/jakarta.activation-api-LICENSE-BSD_LIKE.txt b/solr/licenses/jakarta.activation-api-LICENSE-BSD_LIKE.txt
deleted file mode 100644
index e0cdfb1..0000000
--- a/solr/licenses/jakarta.activation-api-LICENSE-BSD_LIKE.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-Eclipse Distribution License - v 1.0
-
-Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors.
-
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
-- Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-- Neither the name of the Eclipse Foundation, Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFI [...]
\ No newline at end of file
diff --git a/solr/licenses/jakarta.activation-api-NOTICE.txt b/solr/licenses/jakarta.activation-api-NOTICE.txt
deleted file mode 100644
index 78e216f..0000000
--- a/solr/licenses/jakarta.activation-api-NOTICE.txt
+++ /dev/null
@@ -1,25 +0,0 @@
-# Notices for Jakarta Activation
-
-This content is produced and maintained by Jakarta Activation project.
-
-* Project home: https://projects.eclipse.org/projects/ee4j.jaf
-
-## Copyright
-
-All content is the property of the respective authors or their employers. For
-more information regarding authorship of content, please consult the listed
-source code repository logs.
-
-## Declared Project Licenses
-
-This program and the accompanying materials are made available under the terms
-of the Eclipse Distribution License v. 1.0,
-which is available at http://www.eclipse.org/org/documents/edl-v10.php.
-
-SPDX-License-Identifier: BSD-3-Clause
-
-## Source Code
-
-The project maintains the following source code repositories:
-
-* https://github.com/eclipse-ee4j/jaf
diff --git a/solr/licenses/jakarta.xml.bind-api-2.3.2.jar.sha1 b/solr/licenses/jakarta.xml.bind-api-2.3.2.jar.sha1
deleted file mode 100644
index 14f8f9d..0000000
--- a/solr/licenses/jakarta.xml.bind-api-2.3.2.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-8d49996a4338670764d7ca4b85a1c4ccf7fe665d
diff --git a/solr/licenses/jakarta.xml.bind-api-LICENSE-BSD_LIKE.txt b/solr/licenses/jakarta.xml.bind-api-LICENSE-BSD_LIKE.txt
deleted file mode 100644
index e0cdfb1..0000000
--- a/solr/licenses/jakarta.xml.bind-api-LICENSE-BSD_LIKE.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-Eclipse Distribution License - v 1.0
-
-Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors.
-
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
-- Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-- Neither the name of the Eclipse Foundation, Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFI [...]
\ No newline at end of file
diff --git a/solr/licenses/jakarta.xml.bind-api-NOTICE.txt b/solr/licenses/jakarta.xml.bind-api-NOTICE.txt
deleted file mode 100644
index 5aac171..0000000
--- a/solr/licenses/jakarta.xml.bind-api-NOTICE.txt
+++ /dev/null
@@ -1,44 +0,0 @@
-# Notices for Eclipse Project for JAXB
-
-This content is produced and maintained by the Eclipse Project for JAXB project.
-
-* Project home: https://projects.eclipse.org/projects/ee4j.jaxb
-
-## Trademarks
-
-Eclipse Project for JAXB is a trademark of the Eclipse Foundation.
-
-## Copyright
-
-All content is the property of the respective authors or their employers. For
-more information regarding authorship of content, please consult the listed
-source code repository logs.
-
-## Declared Project Licenses
-
-This program and the accompanying materials are made available under the terms
-of the Eclipse Distribution License v. 1.0 which is available
-at http://www.eclipse.org/org/documents/edl-v10.php.
-
-SPDX-License-Identifier: BSD-3-Clause
-
-## Source Code
-
-The project maintains the following source code repositories:
-
-* https://github.com/eclipse-ee4j/jaxb-api
-
-## Third-party Content
-
-This project leverages the following third party content.
-
-None
-
-## Cryptography
-
-Content may contain encryption software. The country in which you are currently
-may have restrictions on the import, possession, and use, and/or re-export to
-another country, of encryption software. BEFORE using any encryption software,
-please check the country's laws, regulations and policies concerning the import,
-possession, or use, and re-export of encryption software, to see if this is
-permitted.
diff --git a/solr/licenses/jose4j-0.7.7.jar.sha1 b/solr/licenses/jose4j-0.7.7.jar.sha1
deleted file mode 100644
index c47ba06..0000000
--- a/solr/licenses/jose4j-0.7.7.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-cf1a02839a251731e92ba0134268fec3f79d4868
diff --git a/solr/licenses/jose4j-0.7.9.jar.sha1 b/solr/licenses/jose4j-0.7.9.jar.sha1
new file mode 100644
index 0000000..ff27428
--- /dev/null
+++ b/solr/licenses/jose4j-0.7.9.jar.sha1
@@ -0,0 +1 @@
+b44a2235728ab1cad9ffd06013500f09a5f1d241
diff --git a/solr/licenses/json-smart-2.3.jar.sha1 b/solr/licenses/json-smart-2.3.jar.sha1
deleted file mode 100644
index 5fc001e..0000000
--- a/solr/licenses/json-smart-2.3.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-007396407491352ce4fa30de92efb158adb76b5b
diff --git a/solr/licenses/json-smart-2.4.7.jar.sha1 b/solr/licenses/json-smart-2.4.7.jar.sha1
new file mode 100644
index 0000000..92aebfd
--- /dev/null
+++ b/solr/licenses/json-smart-2.4.7.jar.sha1
@@ -0,0 +1 @@
+8d7f4c1530c07c54930935f3da85f48b83b3c109
diff --git a/solr/licenses/kotlin-logging-jvm-2.0.6.jar.sha1 b/solr/licenses/kotlin-logging-jvm-2.0.6.jar.sha1
deleted file mode 100644
index bd2d62c..0000000
--- a/solr/licenses/kotlin-logging-jvm-2.0.6.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-539f788429038f058f9c0da3cac14f7b653f0db8
diff --git a/solr/licenses/kotlin-logging-jvm-2.1.21.jar.sha1 b/solr/licenses/kotlin-logging-jvm-2.1.21.jar.sha1
new file mode 100644
index 0000000..590ef71
--- /dev/null
+++ b/solr/licenses/kotlin-logging-jvm-2.1.21.jar.sha1
@@ -0,0 +1 @@
+7a65a2789a27c67c281a4fe9e78689cf5e2b36d8
diff --git a/solr/licenses/kotlin-reflect-1.4.32.jar.sha1 b/solr/licenses/kotlin-reflect-1.4.32.jar.sha1
deleted file mode 100644
index ba9373e..0000000
--- a/solr/licenses/kotlin-reflect-1.4.32.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-ce852b166d97f0f1991b5130c2bb02e2ef6c554e
diff --git a/solr/licenses/kotlin-reflect-1.6.10.jar.sha1 b/solr/licenses/kotlin-reflect-1.6.10.jar.sha1
new file mode 100644
index 0000000..c8ade64
--- /dev/null
+++ b/solr/licenses/kotlin-reflect-1.6.10.jar.sha1
@@ -0,0 +1 @@
+1cbe9c92c12a94eea200d23c2bbaedaf3daf5132
diff --git a/solr/licenses/kotlin-stdlib-1.4.32.jar.sha1 b/solr/licenses/kotlin-stdlib-1.4.32.jar.sha1
deleted file mode 100644
index d51c831..0000000
--- a/solr/licenses/kotlin-stdlib-1.4.32.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-461367948840adbb0839c51d91ed74ef4a9ccb52
diff --git a/solr/licenses/kotlin-stdlib-1.6.10.jar.sha1 b/solr/licenses/kotlin-stdlib-1.6.10.jar.sha1
new file mode 100644
index 0000000..0014384
--- /dev/null
+++ b/solr/licenses/kotlin-stdlib-1.6.10.jar.sha1
@@ -0,0 +1 @@
+b8af3fe6f1ca88526914929add63cf5e7c5049af
diff --git a/solr/licenses/kotlin-stdlib-common-1.4.32.jar.sha1 b/solr/licenses/kotlin-stdlib-common-1.4.32.jar.sha1
deleted file mode 100644
index 969ef6a..0000000
--- a/solr/licenses/kotlin-stdlib-common-1.4.32.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-ef50bfa2c0491a11dcc35d9822edbfd6170e1ea2
diff --git a/solr/licenses/kotlin-stdlib-common-1.6.10.jar.sha1 b/solr/licenses/kotlin-stdlib-common-1.6.10.jar.sha1
new file mode 100644
index 0000000..d502658
--- /dev/null
+++ b/solr/licenses/kotlin-stdlib-common-1.6.10.jar.sha1
@@ -0,0 +1 @@
+0c118700e3a33c8a0d9adc920e9dec0831171925
diff --git a/solr/licenses/kotlin-stdlib-jdk7-1.4.10.jar.sha1 b/solr/licenses/kotlin-stdlib-jdk7-1.4.10.jar.sha1
deleted file mode 100644
index b505b7b..0000000
--- a/solr/licenses/kotlin-stdlib-jdk7-1.4.10.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-30e46450b0bb3dbf43898d2f461be4a942784780
diff --git a/solr/licenses/kotlin-stdlib-jdk7-1.6.0.jar.sha1 b/solr/licenses/kotlin-stdlib-jdk7-1.6.0.jar.sha1
new file mode 100644
index 0000000..a488934
--- /dev/null
+++ b/solr/licenses/kotlin-stdlib-jdk7-1.6.0.jar.sha1
@@ -0,0 +1 @@
+da6bdc87391322974a43ccc00a25536ae74dad51
diff --git a/solr/licenses/kotlin-stdlib-jdk8-1.4.10.jar.sha1 b/solr/licenses/kotlin-stdlib-jdk8-1.4.10.jar.sha1
deleted file mode 100644
index 3036192..0000000
--- a/solr/licenses/kotlin-stdlib-jdk8-1.4.10.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-998caa30623f73223194a8b657abd2baec4880ea
diff --git a/solr/licenses/kotlin-stdlib-jdk8-1.6.0.jar.sha1 b/solr/licenses/kotlin-stdlib-jdk8-1.6.0.jar.sha1
new file mode 100644
index 0000000..50cd854
--- /dev/null
+++ b/solr/licenses/kotlin-stdlib-jdk8-1.6.0.jar.sha1
@@ -0,0 +1 @@
+baf82c475e9372c25407f3d132439e4aa803b8b8
diff --git a/solr/licenses/lang-tag-1.4.4.jar.sha1 b/solr/licenses/lang-tag-1.4.4.jar.sha1
deleted file mode 100644
index a48d1f9..0000000
--- a/solr/licenses/lang-tag-1.4.4.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-1db9a709239ae473a69b5424c7e78d0b7108229d
diff --git a/solr/licenses/lang-tag-1.5.jar.sha1 b/solr/licenses/lang-tag-1.5.jar.sha1
new file mode 100644
index 0000000..3a38e49
--- /dev/null
+++ b/solr/licenses/lang-tag-1.5.jar.sha1
@@ -0,0 +1 @@
+7e82e3c4c593f85addf4bd209abde4f8ff933a07
diff --git a/solr/licenses/mock-oauth2-server-0.3.3.jar.sha1 b/solr/licenses/mock-oauth2-server-0.3.3.jar.sha1
deleted file mode 100644
index 3fd888f..0000000
--- a/solr/licenses/mock-oauth2-server-0.3.3.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-79dee9cc305eb31ce4f95e02043ad0a39f0ab249
diff --git a/solr/licenses/mock-oauth2-server-0.4.3.jar.sha1 b/solr/licenses/mock-oauth2-server-0.4.3.jar.sha1
new file mode 100644
index 0000000..3347ef0
--- /dev/null
+++ b/solr/licenses/mock-oauth2-server-0.4.3.jar.sha1
@@ -0,0 +1 @@
+28586c0d946551b69198535c46ae55389ad39cae
diff --git a/solr/licenses/mockwebserver-4.9.1.jar.sha1 b/solr/licenses/mockwebserver-4.9.1.jar.sha1
deleted file mode 100644
index 0d64f53..0000000
--- a/solr/licenses/mockwebserver-4.9.1.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-25e7b8352606b61c6b66f93db3b1590c5588eece
diff --git a/solr/licenses/mockwebserver-4.9.3.jar.sha1 b/solr/licenses/mockwebserver-4.9.3.jar.sha1
new file mode 100644
index 0000000..c950542
--- /dev/null
+++ b/solr/licenses/mockwebserver-4.9.3.jar.sha1
@@ -0,0 +1 @@
+436df81ae01a2326b72d013783a782b595f173a5
diff --git a/solr/licenses/nimbus-jose-jwt-9.16.1.jar.sha1 b/solr/licenses/nimbus-jose-jwt-9.16.1.jar.sha1
new file mode 100644
index 0000000..c6839fc
--- /dev/null
+++ b/solr/licenses/nimbus-jose-jwt-9.16.1.jar.sha1
@@ -0,0 +1 @@
+6bd47fe347968ea624355dda6144e98596b62d12
diff --git a/solr/licenses/nimbus-jose-jwt-9.6.1.jar.sha1 b/solr/licenses/nimbus-jose-jwt-9.6.1.jar.sha1
deleted file mode 100644
index 492333fd..0000000
--- a/solr/licenses/nimbus-jose-jwt-9.6.1.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-13973a67cdca487129e4012eec57fd22e51aa894
diff --git a/solr/licenses/oauth2-oidc-sdk-9.2.3.jar.sha1 b/solr/licenses/oauth2-oidc-sdk-9.2.3.jar.sha1
deleted file mode 100644
index b79bf8d..0000000
--- a/solr/licenses/oauth2-oidc-sdk-9.2.3.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-74fa6dbebede173028edb9a10b608f07ed11aff6
diff --git a/solr/licenses/oauth2-oidc-sdk-9.22.2.jar.sha1 b/solr/licenses/oauth2-oidc-sdk-9.22.2.jar.sha1
new file mode 100644
index 0000000..21e8570
--- /dev/null
+++ b/solr/licenses/oauth2-oidc-sdk-9.22.2.jar.sha1
@@ -0,0 +1 @@
+2339e9e610683544bd69d7c3c839df2f0e97f7aa
diff --git a/solr/licenses/okhttp-4.9.1.jar.sha1 b/solr/licenses/okhttp-4.9.1.jar.sha1
deleted file mode 100644
index 5dd6663..0000000
--- a/solr/licenses/okhttp-4.9.1.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-51215279c3fe472c59b6b7dd7491e6ac2e28a81b
diff --git a/solr/licenses/okhttp-4.9.3.jar.sha1 b/solr/licenses/okhttp-4.9.3.jar.sha1
new file mode 100644
index 0000000..07197f8
--- /dev/null
+++ b/solr/licenses/okhttp-4.9.3.jar.sha1
@@ -0,0 +1 @@
+b0b14b3d12980912723fb8b66afb48dcda742fcb
diff --git a/solr/licenses/woodstox-core-6.2.4.jar.sha1 b/solr/licenses/woodstox-core-6.2.4.jar.sha1
deleted file mode 100644
index e440440..0000000
--- a/solr/licenses/woodstox-core-6.2.4.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-16b9f8ab972e67eb21872ea2c40046249d543989
diff --git a/solr/licenses/woodstox-core-6.2.7.jar.sha1 b/solr/licenses/woodstox-core-6.2.7.jar.sha1
new file mode 100644
index 0000000..d5eff18
--- /dev/null
+++ b/solr/licenses/woodstox-core-6.2.7.jar.sha1
@@ -0,0 +1 @@
+86622cfd0a9933628b6b876d0c92589148d3b42e
diff --git a/solr/modules/jwt-auth/src/test/org/apache/solr/security/jwt/JWTAuthPluginIntegrationTest.java b/solr/modules/jwt-auth/src/test/org/apache/solr/security/jwt/JWTAuthPluginIntegrationTest.java
index 7933644..e3b1620 100644
--- a/solr/modules/jwt-auth/src/test/org/apache/solr/security/jwt/JWTAuthPluginIntegrationTest.java
+++ b/solr/modules/jwt-auth/src/test/org/apache/solr/security/jwt/JWTAuthPluginIntegrationTest.java
@@ -32,7 +32,6 @@ import java.nio.file.Files;
 import java.nio.file.Path;
 import java.security.KeyStore;
 import java.util.Base64;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Set;
@@ -46,7 +45,6 @@ import no.nav.security.mock.oauth2.MockOAuth2Server;
 import no.nav.security.mock.oauth2.OAuth2Config;
 import no.nav.security.mock.oauth2.http.MockWebServerWrapper;
 import no.nav.security.mock.oauth2.token.DefaultOAuth2TokenCallback;
-import no.nav.security.mock.oauth2.token.OAuth2TokenProvider;
 import okhttp3.mockwebserver.MockWebServer;
 import org.apache.commons.io.IOUtils;
 import org.apache.http.HttpHeaders;
@@ -530,9 +528,9 @@ public class JWTAuthPluginIntegrationTest extends SolrCloudAuthTestCase {
       SSLSocketFactory sf = sslContext.getSocketFactory();
       mockWebServer.useHttps(sf, false);
 
-      OAuth2Config config =
-          new OAuth2Config(
-              false, new OAuth2TokenProvider(), Collections.emptySet(), mockWebServerWrapper);
+      OAuth2Config config = new OAuth2Config();
+      ((MockWebServerWrapper) config.getHttpServer()).getMockWebServer().useHttps(sf, false);
+
       return new MockOAuth2Server(config);
     } catch (Exception e) {
       throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Failed initializing SSL", e);
diff --git a/versions.lock b/versions.lock
index 14d5eea..6e2580d 100644
--- a/versions.lock
+++ b/versions.lock
@@ -6,12 +6,12 @@ com.cybozu.labs:langdetect:1.1-20120112 (1 constraints: 5c066d5e)
 com.drewnoakes:metadata-extractor:2.11.0 (1 constraints: 3605323b)
 com.epam:parso:2.0.11 (1 constraints: 36052c3b)
 com.esri.geometry:esri-geometry-api:2.2.0 (2 constraints: 6112842b)
-com.fasterxml.jackson:jackson-bom:2.12.3 (11 constraints: fdf28813)
-com.fasterxml.jackson.core:jackson-annotations:2.12.3 (9 constraints: 42a15458)
-com.fasterxml.jackson.core:jackson-core:2.12.3 (14 constraints: 1c0a99f0)
-com.fasterxml.jackson.core:jackson-databind:2.12.3 (17 constraints: 5e455fcf)
-com.fasterxml.jackson.dataformat:jackson-dataformat-smile:2.12.3 (2 constraints: f1137d82)
-com.fasterxml.woodstox:woodstox-core:6.2.4 (2 constraints: 4d1cac9f)
+com.fasterxml.jackson:jackson-bom:2.13.1 (10 constraints: 93d98ca5)
+com.fasterxml.jackson.core:jackson-annotations:2.13.1 (8 constraints: dd8797a5)
+com.fasterxml.jackson.core:jackson-core:2.13.1 (13 constraints: a5f07d58)
+com.fasterxml.jackson.core:jackson-databind:2.13.1 (16 constraints: f62b03fa)
+com.fasterxml.jackson.dataformat:jackson-dataformat-smile:2.13.1 (2 constraints: f0137e82)
+com.fasterxml.woodstox:woodstox-core:6.2.7 (2 constraints: 501caf9f)
 com.github.ben-manes.caffeine:caffeine:3.0.5 (1 constraints: 0a05ff35)
 com.github.virtuald:curvesapi:1.06 (1 constraints: db04f530)
 com.github.zafarkhaja:java-semver:0.9.0 (1 constraints: 0b050636)
@@ -88,7 +88,7 @@ io.prometheus:simpleclient_common:0.2.0 (2 constraints: e8159ecb)
 io.prometheus:simpleclient_httpserver:0.2.0 (1 constraints: 0405f135)
 io.sgr:s2-geometry-library-java:1.0.0 (2 constraints: b215dedf)
 javax.servlet:javax.servlet-api:3.1.0 (3 constraints: 75209943)
-junit:junit:4.13.1 (7 constraints: 786812d9)
+junit:junit:4.13.1 (6 constraints: c059a781)
 net.arnx:jsonic:1.2.7 (2 constraints: db10d4d1)
 net.jcip:jcip-annotations:1.0 (1 constraints: 560ff165)
 net.sf.jopt-simple:jopt-simple:4.6 (1 constraints: 610a91b7)
@@ -174,10 +174,11 @@ org.apache.zookeeper:zookeeper:3.7.0 (2 constraints: 9e13f25e)
 org.apache.zookeeper:zookeeper-jute:3.7.0 (2 constraints: 95120523)
 org.apiguardian:apiguardian-api:1.1.0 (3 constraints: 5f208479)
 org.aspectj:aspectjrt:1.8.0 (1 constraints: 0b050836)
-org.bitbucket.b_c:jose4j:0.7.7 (1 constraints: 10050736)
-org.bouncycastle:bcmail-jdk15on:1.69 (1 constraints: e4040431)
-org.bouncycastle:bcpkix-jdk15on:1.69 (1 constraints: e4040431)
-org.bouncycastle:bcprov-jdk15on:1.69 (1 constraints: e4040431)
+org.bitbucket.b_c:jose4j:0.7.9 (1 constraints: 12050936)
+org.bouncycastle:bcmail-jdk15on:1.70 (1 constraints: e4040431)
+org.bouncycastle:bcpkix-jdk15on:1.70 (2 constraints: 73133c52)
+org.bouncycastle:bcprov-jdk15on:1.70 (3 constraints: 941ff808)
+org.bouncycastle:bcutil-jdk15on:1.70 (1 constraints: 580d0529)
 org.brotli:dec:0.1.2 (1 constraints: 0505f035)
 org.carrot2:carrot2-core:4.0.4 (1 constraints: 0a050336)
 org.carrot2:morfologik-fsa:2.1.5 (1 constraints: d70d9836)
@@ -221,12 +222,12 @@ org.jctools:jctools-core:3.3.0 (1 constraints: 08050336)
 org.jdom:jdom2:2.0.6 (1 constraints: 0a05fb35)
 org.locationtech.spatial4j:spatial4j:0.8 (2 constraints: 03150d9b)
 org.openjdk.jmh:jmh-core:1.32 (1 constraints: da04f730)
-org.ow2.asm:asm:7.2 (3 constraints: a325a35c)
-org.ow2.asm:asm-commons:7.2 (1 constraints: 6b0f7267)
+org.ow2.asm:asm:9.1 (3 constraints: 4725b829)
+org.ow2.asm:asm-commons:9.1 (1 constraints: 6b0f7267)
 org.quicktheories:quicktheories:0.26 (1 constraints: dc04f530)
 org.reactivestreams:reactive-streams:1.0.3 (3 constraints: 3c2b02fd)
 org.slf4j:jcl-over-slf4j:1.7.24 (1 constraints: 4005473b)
-org.slf4j:slf4j-api:1.7.24 (24 constraints: 6d4546ac)
+org.slf4j:slf4j-api:1.7.24 (24 constraints: 67456b9d)
 org.tallison:jmatio:1.5 (1 constraints: aa041f2c)
 org.threeten:threetenbp:1.5.0 (4 constraints: f82b9e86)
 org.tukaani:xz:1.8 (1 constraints: ad04222c)
@@ -262,13 +263,12 @@ com.amazonaws:aws-java-sdk-kms:1.12.15 (1 constraints: d60cb42a)
 com.amazonaws:aws-java-sdk-s3:1.12.15 (1 constraints: e0125c30)
 com.amazonaws:jmespath-java:1.12.15 (2 constraints: 501a5183)
 com.beust:jcommander:1.35 (1 constraints: b50c1901)
-com.fasterxml.jackson.dataformat:jackson-dataformat-cbor:2.12.3 (2 constraints: 5e1c0ef1)
-com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.12.3 (2 constraints: a519c412)
-com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.12.3 (2 constraints: 0724ba81)
-com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.12.3 (3 constraints: 753d9b0e)
-com.fasterxml.jackson.module:jackson-module-jaxb-annotations:2.12.3 (2 constraints: 2326f760)
-com.fasterxml.jackson.module:jackson-module-kotlin:2.12.3 (2 constraints: a41dd15f)
-com.fasterxml.jackson.module:jackson-module-parameter-names:2.12.3 (2 constraints: 0724ba81)
+com.fasterxml.jackson.dataformat:jackson-dataformat-cbor:2.13.1 (2 constraints: 5d1c0ff1)
+com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.13.1 (2 constraints: a419c512)
+com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.13.1 (2 constraints: 06248081)
+com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.13.1 (3 constraints: 743d1c0e)
+com.fasterxml.jackson.module:jackson-module-kotlin:2.13.1 (2 constraints: a31da85f)
+com.fasterxml.jackson.module:jackson-module-parameter-names:2.13.1 (2 constraints: 06248081)
 com.github.jnr:jffi:1.2.18 (1 constraints: b20902ab)
 com.github.jnr:jnr-constants:0.9.12 (4 constraints: ed2c9d5d)
 com.github.jnr:jnr-enxio:0.19 (2 constraints: 2a167d08)
@@ -283,31 +283,29 @@ com.headius:modulator:1.0 (1 constraints: 19098b8e)
 com.headius:options:1.4 (1 constraints: 1d098f8e)
 com.jcraft:jzlib:1.1.3 (1 constraints: 7b094fa1)
 com.martiansoftware:nailgun-server:0.9.1 (1 constraints: 800960a1)
-com.nimbusds:content-type:2.1 (1 constraints: d70b67eb)
-com.nimbusds:lang-tag:1.4.4 (1 constraints: 3b0cb803)
-com.nimbusds:nimbus-jose-jwt:9.6.1 (1 constraints: 420ce303)
-com.nimbusds:oauth2-oidc-sdk:9.2.3 (1 constraints: c40ec362)
-com.squareup.okhttp3:mockwebserver:4.9.1 (1 constraints: c40ebd62)
-com.squareup.okhttp3:okhttp:4.9.1 (1 constraints: 3d0f4f6d)
+com.nimbusds:content-type:2.2 (1 constraints: d80b68eb)
+com.nimbusds:lang-tag:1.5 (1 constraints: da0b68eb)
+com.nimbusds:nimbus-jose-jwt:9.16.1 (1 constraints: 730c5410)
+com.nimbusds:oauth2-oidc-sdk:9.22.2 (1 constraints: f50ebb71)
+com.squareup.okhttp3:mockwebserver:4.9.3 (1 constraints: c60ebf62)
+com.squareup.okhttp3:okhttp:4.9.3 (1 constraints: 3f0f516d)
 com.squareup.okio:okio:2.8.0 (1 constraints: 540c39fd)
 com.sun.jersey:jersey-servlet:1.19 (1 constraints: df04fa30)
 com.vaadin.external.google:android-json:0.0.20131108.vaadin1 (1 constraints: 34092a9e)
-io.github.microutils:kotlin-logging:2.0.6 (1 constraints: be0e9d62)
-io.github.microutils:kotlin-logging-jvm:2.0.6 (1 constraints: 810f877c)
+io.github.microutils:kotlin-logging:2.1.21 (1 constraints: ec0e8871)
+io.github.microutils:kotlin-logging-jvm:2.1.21 (1 constraints: af0f358c)
 io.micrometer:micrometer-core:1.5.14 (1 constraints: fc161b19)
 io.netty:netty-codec-http:4.1.68.Final (1 constraints: 5d077961)
 io.opentracing:opentracing-mock:0.33.0 (1 constraints: 3805343b)
-jakarta.activation:jakarta.activation-api:1.2.1 (2 constraints: b928fbbd)
 jakarta.annotation:jakarta.annotation-api:1.3.5 (1 constraints: 3a131133)
 jakarta.servlet:jakarta.servlet-api:4.0.4 (1 constraints: 961568b9)
 jakarta.websocket:jakarta.websocket-api:1.1.2 (1 constraints: 92155ab9)
-jakarta.xml.bind:jakarta.xml.bind-api:2.3.2 (1 constraints: 30198ba6)
 javax.inject:javax.inject:1 (1 constraints: 7a0df617)
 joda-time:joda-time:2.9.9 (2 constraints: 0317195a)
 net.bytebuddy:byte-buddy:1.10.20 (2 constraints: 7c10a9d0)
-net.minidev:accessors-smart:1.2 (1 constraints: e60926a4)
-net.minidev:json-smart:2.3 (1 constraints: ae0de552)
-no.nav.security:mock-oauth2-server:0.3.3 (1 constraints: 0805f735)
+net.minidev:accessors-smart:2.4.7 (1 constraints: 4e0a90b8)
+net.minidev:json-smart:2.4.7 (1 constraints: 160e936e)
+no.nav.security:mock-oauth2-server:0.4.3 (1 constraints: 0905fa35)
 org.apache.hadoop:hadoop-hdfs:3.2.0 (1 constraints: 07050036)
 org.apache.hadoop:hadoop-minikdc:3.2.0 (1 constraints: 07050036)
 org.apache.kerby:kerb-admin:1.0.1 (1 constraints: 840d892f)
@@ -324,11 +322,11 @@ org.glassfish:jakarta.el:3.0.3 (1 constraints: 941562b9)
 org.hdrhistogram:HdrHistogram:2.1.12 (1 constraints: 520d2029)
 org.hsqldb:hsqldb:2.4.0 (1 constraints: 08050136)
 org.jetbrains:annotations:13.0 (1 constraints: df0e795c)
-org.jetbrains.kotlin:kotlin-reflect:1.4.32 (2 constraints: 612453e2)
-org.jetbrains.kotlin:kotlin-stdlib:1.4.32 (7 constraints: 4168b6de)
-org.jetbrains.kotlin:kotlin-stdlib-common:1.4.32 (3 constraints: b92a7abb)
-org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.10 (1 constraints: dd10ebd2)
-org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.10 (1 constraints: 650f967c)
+org.jetbrains.kotlin:kotlin-reflect:1.6.10 (2 constraints: 602486e2)
+org.jetbrains.kotlin:kotlin-stdlib:1.6.10 (6 constraints: b456d578)
+org.jetbrains.kotlin:kotlin-stdlib-common:1.6.10 (3 constraints: 852a7097)
+org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.6.0 (1 constraints: ae1013c2)
+org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.0 (2 constraints: 5e203d24)
 org.jruby:dirgra:0.3 (1 constraints: 1b098b8e)
 org.jruby:jruby:9.2.6.0 (1 constraints: 490d7d28)
 org.jruby:jruby-core:9.2.6.0 (1 constraints: 0f08b57d)
diff --git a/versions.props b/versions.props
index b9409cf..3aa3d1b 100644
--- a/versions.props
+++ b/versions.props
@@ -68,7 +68,7 @@ net.arnx:jsonic=1.2.7
 net.bytebuddy:byte-buddy=1.9.3
 net.sourceforge.argparse4j:argparse4j=0.8.1
 net.thisptr:jackson-jq=0.0.8
-no.nav.security:mock-oauth2-server=0.3.3
+no.nav.security:mock-oauth2-server=0.4.3
 org.antlr:antlr4-runtime=4.5.1-1
 org.apache.calcite.avatica:avatica-core=1.18.0
 org.apache.calcite:*=1.27.0
@@ -100,7 +100,7 @@ org.apache.zookeeper:*=3.7.0
 org.apiguardian:apiguardian-api=1.1.0
 org.asciidoctor:asciidoctorj=1.6.2
 org.aspectj:aspectjrt=1.8.0
-org.bitbucket.b_c:jose4j=0.7.7
+org.bitbucket.b_c:jose4j=0.7.9
 org.bouncycastle:*=1.69
 org.brotli:dec=0.1.2
 org.carrot2:carrot2-core=4.0.4

[solr] 01/04: SOLR-15907 Apply spotless only on JWT classes (#616)

Posted by ja...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

janhoy pushed a commit to branch branch_9x
in repository https://gitbox.apache.org/repos/asf/solr.git

commit 8081b216e6e17f1827ff4d6102cb408c3c599cfe
Author: Jan Høydahl <ja...@users.noreply.github.com>
AuthorDate: Tue Feb 8 22:28:22 2022 +0100

    SOLR-15907 Apply spotless only on JWT classes (#616)
    
    (Cherry-picked from a14df618ad502087a95c92bd31a0c143865505ca)
---
 .../org/apache/solr/security/JWTAuthPlugin.java    | 493 +++++++++++++--------
 .../org/apache/solr/security/JWTIssuerConfig.java  | 131 ++++--
 .../org/apache/solr/security/JWTPrincipal.java     |  31 +-
 .../solr/security/JWTPrincipalWithUserRoles.java   |  37 +-
 .../solr/security/JWTVerificationkeyResolver.java  |  76 ++--
 .../security/JWTAuthPluginIntegrationTest.java     | 307 +++++++------
 .../apache/solr/security/JWTAuthPluginTest.java    | 218 ++++++---
 .../apache/solr/security/JWTIssuerConfigTest.java  | 117 +++--
 .../security/JWTVerificationkeyResolverTest.java   |  68 ++-
 9 files changed, 924 insertions(+), 554 deletions(-)

diff --git a/solr/core/src/java/org/apache/solr/security/JWTAuthPlugin.java b/solr/core/src/java/org/apache/solr/security/JWTAuthPlugin.java
index 99531b6..ab040bc 100644
--- a/solr/core/src/java/org/apache/solr/security/JWTAuthPlugin.java
+++ b/solr/core/src/java/org/apache/solr/security/JWTAuthPlugin.java
@@ -17,6 +17,34 @@
 package org.apache.solr.security;
 
 import com.google.common.collect.ImmutableSet;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.invoke.MethodHandles;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.security.Principal;
+import java.security.cert.X509Certificate;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Base64;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+import javax.servlet.FilterChain;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
 import org.apache.commons.io.IOUtils;
 import org.apache.http.HttpHeaders;
 import org.apache.http.HttpRequest;
@@ -45,39 +73,9 @@ import org.jose4j.lang.JoseException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import javax.servlet.FilterChain;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.io.IOException;
-import java.io.InputStream;
-import java.lang.invoke.MethodHandles;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.security.Principal;
-import java.security.cert.X509Certificate;
-import java.time.Instant;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Base64;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.Set;
-import java.util.StringTokenizer;
-import java.util.regex.Pattern;
-import java.util.stream.Collectors;
-
-/**
- * Authenticaion plugin that finds logged in user by validating the signature of a JWT token
- */
-public class JWTAuthPlugin extends AuthenticationPlugin implements SpecProvider, ConfigEditablePlugin {
+/** Authenticaion plugin that finds logged in user by validating the signature of a JWT token */
+public class JWTAuthPlugin extends AuthenticationPlugin
+    implements SpecProvider, ConfigEditablePlugin {
   private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
   private static final String PARAM_BLOCK_UNKNOWN = "blockUnknown";
   private static final String PARAM_REQUIRE_ISSUER = "requireIss";
@@ -104,15 +102,31 @@ public class JWTAuthPlugin extends AuthenticationPlugin implements SpecProvider,
   @Deprecated(since = "9.0") // Remove in 10.0
   private static final String PARAM_ALG_WHITELIST = "algWhitelist";
 
-  private static final Set<String> PROPS = ImmutableSet.of(PARAM_BLOCK_UNKNOWN,
-      PARAM_PRINCIPAL_CLAIM, PARAM_REQUIRE_EXPIRATIONTIME, PARAM_ALG_ALLOWLIST,
-      PARAM_JWK_CACHE_DURATION, PARAM_CLAIMS_MATCH, PARAM_SCOPE, PARAM_REALM, PARAM_ROLES_CLAIM,
-      PARAM_ADMINUI_SCOPE, PARAM_REDIRECT_URIS, PARAM_REQUIRE_ISSUER, PARAM_ISSUERS,
-      PARAM_TRUSTED_CERTS_FILE, PARAM_TRUSTED_CERTS,
-      // These keys are supported for now to enable PRIMARY issuer config through top-level keys
-      JWTIssuerConfig.PARAM_JWKS_URL, JWTIssuerConfig.PARAM_JWK, JWTIssuerConfig.PARAM_ISSUER,
-      JWTIssuerConfig.PARAM_CLIENT_ID, JWTIssuerConfig.PARAM_WELL_KNOWN_URL, JWTIssuerConfig.PARAM_AUDIENCE,
-      JWTIssuerConfig.PARAM_AUTHORIZATION_ENDPOINT);
+  private static final Set<String> PROPS =
+      ImmutableSet.of(
+          PARAM_BLOCK_UNKNOWN,
+          PARAM_PRINCIPAL_CLAIM,
+          PARAM_REQUIRE_EXPIRATIONTIME,
+          PARAM_ALG_ALLOWLIST,
+          PARAM_JWK_CACHE_DURATION,
+          PARAM_CLAIMS_MATCH,
+          PARAM_SCOPE,
+          PARAM_REALM,
+          PARAM_ROLES_CLAIM,
+          PARAM_ADMINUI_SCOPE,
+          PARAM_REDIRECT_URIS,
+          PARAM_REQUIRE_ISSUER,
+          PARAM_ISSUERS,
+          PARAM_TRUSTED_CERTS_FILE,
+          PARAM_TRUSTED_CERTS,
+          // These keys are supported for now to enable PRIMARY issuer config through top-level keys
+          JWTIssuerConfig.PARAM_JWKS_URL,
+          JWTIssuerConfig.PARAM_JWK,
+          JWTIssuerConfig.PARAM_ISSUER,
+          JWTIssuerConfig.PARAM_CLIENT_ID,
+          JWTIssuerConfig.PARAM_WELL_KNOWN_URL,
+          JWTIssuerConfig.PARAM_AUDIENCE,
+          JWTIssuerConfig.PARAM_AUTHORIZATION_ENDPOINT);
 
   private JwtConsumer jwtConsumer;
   private boolean requireExpirationTime;
@@ -133,9 +147,7 @@ public class JWTAuthPlugin extends AuthenticationPlugin implements SpecProvider,
   String realm;
   private final CoreContainer coreContainer;
 
-  /**
-   * Initialize plugin
-   */
+  /** Initialize plugin */
   public JWTAuthPlugin() {
     this(null);
   }
@@ -149,23 +161,34 @@ public class JWTAuthPlugin extends AuthenticationPlugin implements SpecProvider,
   public void init(Map<String, Object> pluginConfig) {
     this.pluginConfig = pluginConfig;
     this.issuerConfigs = null;
-    List<String> unknownKeys = pluginConfig.keySet().stream().filter(k -> !PROPS.contains(k)).collect(Collectors.toList());
+    List<String> unknownKeys =
+        pluginConfig.keySet().stream().filter(k -> !PROPS.contains(k)).collect(Collectors.toList());
     unknownKeys.remove("class");
     unknownKeys.remove("");
     if (!unknownKeys.isEmpty()) {
-      throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Invalid JwtAuth configuration parameter " + unknownKeys);
-    }
-
-    blockUnknown = Boolean.parseBoolean(String.valueOf(pluginConfig.getOrDefault(PARAM_BLOCK_UNKNOWN, false)));
-    requireIssuer = Boolean.parseBoolean(String.valueOf(pluginConfig.getOrDefault(PARAM_REQUIRE_ISSUER, "true")));
-    requireExpirationTime = Boolean.parseBoolean(String.valueOf(pluginConfig.getOrDefault(PARAM_REQUIRE_EXPIRATIONTIME, "true")));
+      throw new SolrException(
+          SolrException.ErrorCode.SERVER_ERROR,
+          "Invalid JwtAuth configuration parameter " + unknownKeys);
+    }
+
+    blockUnknown =
+        Boolean.parseBoolean(String.valueOf(pluginConfig.getOrDefault(PARAM_BLOCK_UNKNOWN, false)));
+    requireIssuer =
+        Boolean.parseBoolean(
+            String.valueOf(pluginConfig.getOrDefault(PARAM_REQUIRE_ISSUER, "true")));
+    requireExpirationTime =
+        Boolean.parseBoolean(
+            String.valueOf(pluginConfig.getOrDefault(PARAM_REQUIRE_EXPIRATIONTIME, "true")));
     principalClaim = (String) pluginConfig.getOrDefault(PARAM_PRINCIPAL_CLAIM, "sub");
 
     rolesClaim = (String) pluginConfig.get(PARAM_ROLES_CLAIM);
     algAllowlist = (List<String>) pluginConfig.get(PARAM_ALG_ALLOWLIST);
     // TODO: Remove deprecated warning in Solr 10.0
-    if ((algAllowlist == null || algAllowlist.isEmpty()) && pluginConfig.containsKey(PARAM_ALG_WHITELIST)) {
-      log.warn("Found use of deprecated parameter algWhitelist. Please use {} instead.", PARAM_ALG_ALLOWLIST);
+    if ((algAllowlist == null || algAllowlist.isEmpty())
+        && pluginConfig.containsKey(PARAM_ALG_WHITELIST)) {
+      log.warn(
+          "Found use of deprecated parameter algWhitelist. Please use {} instead.",
+          PARAM_ALG_ALLOWLIST);
       algAllowlist = (List<String>) pluginConfig.get(PARAM_ALG_WHITELIST);
     }
     realm = (String) pluginConfig.getOrDefault(PARAM_REALM, DEFAULT_AUTH_REALM);
@@ -188,7 +211,13 @@ public class JWTAuthPlugin extends AuthenticationPlugin implements SpecProvider,
     String trustedCertsFile = (String) pluginConfig.get(PARAM_TRUSTED_CERTS_FILE);
     String trustedCerts = (String) pluginConfig.get(PARAM_TRUSTED_CERTS);
     if (trustedCertsFile != null && trustedCerts != null) {
-      throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Found both " + PARAM_TRUSTED_CERTS_FILE + " and " + PARAM_TRUSTED_CERTS + ", please use only one");
+      throw new SolrException(
+          SolrException.ErrorCode.SERVER_ERROR,
+          "Found both "
+              + PARAM_TRUSTED_CERTS_FILE
+              + " and "
+              + PARAM_TRUSTED_CERTS
+              + ", please use only one");
     }
     if (trustedCertsFile != null) {
       try {
@@ -199,7 +228,8 @@ public class JWTAuthPlugin extends AuthenticationPlugin implements SpecProvider,
         trustedCertsStream = Files.newInputStream(trustedCertsPath);
         log.info("Reading trustedCerts from file {}", trustedCertsFile);
       } catch (IOException e) {
-        throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Failed to read file " + trustedCertsFile, e);
+        throw new SolrException(
+            SolrException.ErrorCode.SERVER_ERROR, "Failed to read file " + trustedCertsFile, e);
       }
     }
     if (trustedCerts != null) {
@@ -210,19 +240,23 @@ public class JWTAuthPlugin extends AuthenticationPlugin implements SpecProvider,
       trustedSslCerts = CryptoKeys.parseX509Certs(trustedCertsStream);
     }
 
-    long jwkCacheDuration = Long.parseLong((String) pluginConfig.getOrDefault(PARAM_JWK_CACHE_DURATION, "3600"));
+    long jwkCacheDuration =
+        Long.parseLong((String) pluginConfig.getOrDefault(PARAM_JWK_CACHE_DURATION, "3600"));
 
-    JWTIssuerConfig.setHttpsJwksFactory(new JWTIssuerConfig.HttpsJwksFactory(
-        jwkCacheDuration, DEFAULT_REFRESH_REPRIEVE_THRESHOLD, trustedSslCerts));
+    JWTIssuerConfig.setHttpsJwksFactory(
+        new JWTIssuerConfig.HttpsJwksFactory(
+            jwkCacheDuration, DEFAULT_REFRESH_REPRIEVE_THRESHOLD, trustedSslCerts));
 
     issuerConfigs = new ArrayList<>();
 
     // Try to parse an issuer from top level config, and add first (primary issuer)
     Optional<JWTIssuerConfig> topLevelIssuer = parseIssuerFromTopLevelConfig(pluginConfig);
-    topLevelIssuer.ifPresent(ic -> {
-      issuerConfigs.add(ic);
-      log.warn("JWTAuthPlugin issuer is configured using top-level configuration keys. Please consider using the 'issuers' array instead.");
-    });
+    topLevelIssuer.ifPresent(
+        ic -> {
+          issuerConfigs.add(ic);
+          log.warn(
+              "JWTAuthPlugin issuer is configured using top-level configuration keys. Please consider using the 'issuers' array instead.");
+        });
 
     // Add issuers from 'issuers' key
     issuerConfigs.addAll(parseIssuers(pluginConfig));
@@ -232,12 +266,14 @@ public class JWTAuthPlugin extends AuthenticationPlugin implements SpecProvider,
       adminUiScope = (String) pluginConfig.get(PARAM_ADMINUI_SCOPE);
       if (adminUiScope == null && requiredScopes.size() > 0) {
         adminUiScope = requiredScopes.get(0);
-        log.warn("No adminUiScope given, using first scope in 'scope' list as required scope for accessing Admin UI");
+        log.warn(
+            "No adminUiScope given, using first scope in 'scope' list as required scope for accessing Admin UI");
       }
 
       if (adminUiScope == null) {
         adminUiScope = "solr";
-        log.info("No adminUiScope provided, fallback to 'solr' as required scope for Admin UI login may not work");
+        log.info(
+            "No adminUiScope provided, fallback to 'solr' as required scope for Admin UI login may not work");
       }
 
       Object redirectUrisObj = pluginConfig.get(PARAM_REDIRECT_URIS);
@@ -259,15 +295,18 @@ public class JWTAuthPlugin extends AuthenticationPlugin implements SpecProvider,
   @SuppressWarnings("unchecked")
   private Optional<JWTIssuerConfig> parseIssuerFromTopLevelConfig(Map<String, Object> conf) {
     try {
-      JWTIssuerConfig primary = new JWTIssuerConfig(PRIMARY_ISSUER)
-          .setIss((String) conf.get(JWTIssuerConfig.PARAM_ISSUER))
-          .setAud((String) conf.get(JWTIssuerConfig.PARAM_AUDIENCE))
-          .setJwksUrl(conf.get(JWTIssuerConfig.PARAM_JWKS_URL))
-          .setAuthorizationEndpoint((String) conf.get(JWTIssuerConfig.PARAM_AUTHORIZATION_ENDPOINT))
-          .setClientId((String) conf.get(JWTIssuerConfig.PARAM_CLIENT_ID))
-          .setWellKnownUrl((String) conf.get(JWTIssuerConfig.PARAM_WELL_KNOWN_URL));
+      JWTIssuerConfig primary =
+          new JWTIssuerConfig(PRIMARY_ISSUER)
+              .setIss((String) conf.get(JWTIssuerConfig.PARAM_ISSUER))
+              .setAud((String) conf.get(JWTIssuerConfig.PARAM_AUDIENCE))
+              .setJwksUrl(conf.get(JWTIssuerConfig.PARAM_JWKS_URL))
+              .setAuthorizationEndpoint(
+                  (String) conf.get(JWTIssuerConfig.PARAM_AUTHORIZATION_ENDPOINT))
+              .setClientId((String) conf.get(JWTIssuerConfig.PARAM_CLIENT_ID))
+              .setWellKnownUrl((String) conf.get(JWTIssuerConfig.PARAM_WELL_KNOWN_URL));
       if (conf.get(JWTIssuerConfig.PARAM_JWK) != null) {
-        primary.setJsonWebKeySet(JWTIssuerConfig.parseJwkSet((Map<String, Object>) conf.get(JWTIssuerConfig.PARAM_JWK)));
+        primary.setJsonWebKeySet(
+            JWTIssuerConfig.parseJwkSet((Map<String, Object>) conf.get(JWTIssuerConfig.PARAM_JWK)));
       }
       if (primary.isValid()) {
         log.debug("Found issuer in top level config");
@@ -279,13 +318,16 @@ public class JWTAuthPlugin extends AuthenticationPlugin implements SpecProvider,
         return Optional.empty();
       }
     } catch (JoseException je) {
-      throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Failed parsing issuer from top level config", je);
+      throw new SolrException(
+          SolrException.ErrorCode.SERVER_ERROR, "Failed parsing issuer from top level config", je);
     }
   }
 
   /**
-   * Fetch the primary issuer to be used for Admin UI authentication. Callers of this method must ensure that at least
-   * one issuer is configured. The primary issuer is defined as the first issuer configured in the list.
+   * Fetch the primary issuer to be used for Admin UI authentication. Callers of this method must
+   * ensure that at least one issuer is configured. The primary issuer is defined as the first
+   * issuer configured in the list.
+   *
    * @return JWTIssuerConfig object for the primary issuer
    */
   JWTIssuerConfig getPrimaryIssuer() {
@@ -297,6 +339,7 @@ public class JWTAuthPlugin extends AuthenticationPlugin implements SpecProvider,
 
   /**
    * Initialize optional additional issuers configured in 'issuers' config map
+   *
    * @param pluginConfig the main config object
    * @return a list of parsed {@link JWTIssuerConfig} objects
    */
@@ -304,64 +347,80 @@ public class JWTAuthPlugin extends AuthenticationPlugin implements SpecProvider,
   List<JWTIssuerConfig> parseIssuers(Map<String, Object> pluginConfig) {
     List<JWTIssuerConfig> configs = new ArrayList<>();
     try {
-      List<Map<String, Object>> issuers = (List<Map<String, Object>>) pluginConfig.get(PARAM_ISSUERS);
+      List<Map<String, Object>> issuers =
+          (List<Map<String, Object>>) pluginConfig.get(PARAM_ISSUERS);
       if (issuers != null) {
-        issuers.forEach(issuerConf -> {
-          JWTIssuerConfig ic = new JWTIssuerConfig(issuerConf);
-          ic.setTrustedCerts(trustedSslCerts);
-          ic.init();
-          configs.add(ic);
-          if (log.isDebugEnabled()) {
-            log.debug("Found issuer with name {} and issuerId {}", ic.getName(), ic.getIss());
-          }
-        });
+        issuers.forEach(
+            issuerConf -> {
+              JWTIssuerConfig ic = new JWTIssuerConfig(issuerConf);
+              ic.setTrustedCerts(trustedSslCerts);
+              ic.init();
+              configs.add(ic);
+              if (log.isDebugEnabled()) {
+                log.debug("Found issuer with name {} and issuerId {}", ic.getName(), ic.getIss());
+              }
+            });
       }
       return configs;
-    } catch(ClassCastException cce) {
-      throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Parameter " + PARAM_ISSUERS + " has wrong format.", cce);
+    } catch (ClassCastException cce) {
+      throw new SolrException(
+          SolrException.ErrorCode.SERVER_ERROR,
+          "Parameter " + PARAM_ISSUERS + " has wrong format.",
+          cce);
     }
   }
 
-  /**
-   * Main authentication method that looks for correct JWT token in the Authorization header
-   */
+  /** Main authentication method that looks for correct JWT token in the Authorization header */
   @Override
-  public boolean doAuthenticate(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws Exception {
+  public boolean doAuthenticate(
+      HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
+      throws Exception {
     String header = request.getHeader(HttpHeaders.AUTHORIZATION);
 
     if (jwtConsumer == null) {
       if (header == null && !blockUnknown) {
-        log.info("JWTAuth not configured, but allowing anonymous access since {}==false", PARAM_BLOCK_UNKNOWN);
+        log.info(
+            "JWTAuth not configured, but allowing anonymous access since {}==false",
+            PARAM_BLOCK_UNKNOWN);
         numPassThrough.inc();
         filterChain.doFilter(request, response);
         return true;
       }
       // Retry config
       if (lastInitTime.plusSeconds(RETRY_INIT_DELAY_SECONDS).isAfter(Instant.now())) {
-        log.info("Retrying JWTAuthPlugin initialization (retry delay={}s)", RETRY_INIT_DELAY_SECONDS);
+        log.info(
+            "Retrying JWTAuthPlugin initialization (retry delay={}s)", RETRY_INIT_DELAY_SECONDS);
         init(pluginConfig);
       }
       if (jwtConsumer == null) {
         log.warn("JWTAuth not configured");
         numErrors.mark();
-        throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "JWTAuth plugin not correctly configured");
+        throw new SolrException(
+            SolrException.ErrorCode.SERVER_ERROR, "JWTAuth plugin not correctly configured");
       }
     }
 
     JWTAuthenticationResponse authResponse = authenticate(header);
-    String exceptionMessage = authResponse.getJwtException() != null ? authResponse.getJwtException().getMessage() : "";
+    String exceptionMessage =
+        authResponse.getJwtException() != null ? authResponse.getJwtException().getMessage() : "";
     if (AuthCode.SIGNATURE_INVALID.equals(authResponse.getAuthCode())) {
       String issuer = jwtConsumer.processToClaims(header).getIssuer();
       if (issuer != null) {
-        Optional<JWTIssuerConfig> issuerConfig = issuerConfigs.stream().filter(ic -> issuer.equals(ic.getIss())).findFirst();
+        Optional<JWTIssuerConfig> issuerConfig =
+            issuerConfigs.stream().filter(ic -> issuer.equals(ic.getIss())).findFirst();
         if (issuerConfig.isPresent() && issuerConfig.get().usesHttpsJwk()) {
-          log.info("Signature validation failed for issuer {}. Refreshing JWKs from IdP before trying again: {}",
-              issuer, exceptionMessage);
+          log.info(
+              "Signature validation failed for issuer {}. Refreshing JWKs from IdP before trying again: {}",
+              issuer,
+              exceptionMessage);
           for (HttpsJwks httpsJwks : issuerConfig.get().getHttpsJwks()) {
             httpsJwks.refresh();
           }
           authResponse = authenticate(header); // Retry
-          exceptionMessage = authResponse.getJwtException() != null ? authResponse.getJwtException().getMessage() : "";
+          exceptionMessage =
+              authResponse.getJwtException() != null
+                  ? authResponse.getJwtException().getMessage()
+                  : "";
         }
       }
     }
@@ -372,10 +431,13 @@ public class JWTAuthPlugin extends AuthenticationPlugin implements SpecProvider,
         request = wrapWithPrincipal(request, principal);
         if (!(principal instanceof JWTPrincipal)) {
           numErrors.mark();
-          throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "JWTAuth plugin says AUTHENTICATED but no token extracted");
+          throw new SolrException(
+              SolrException.ErrorCode.SERVER_ERROR,
+              "JWTAuth plugin says AUTHENTICATED but no token extracted");
         }
-        if (log.isDebugEnabled())
+        if (log.isDebugEnabled()) {
           log.debug("Authentication SUCCESS");
+        }
         numAuthenticated.inc();
         filterChain.doFilter(request, response);
         return true;
@@ -390,9 +452,16 @@ public class JWTAuthPlugin extends AuthenticationPlugin implements SpecProvider,
 
       case AUTZ_HEADER_PROBLEM:
       case JWT_PARSE_ERROR:
-        log.warn("Authentication failed. {}, {}", authResponse.getAuthCode(), authResponse.getAuthCode().getMsg());
+        log.warn(
+            "Authentication failed. {}, {}",
+            authResponse.getAuthCode(),
+            authResponse.getAuthCode().getMsg());
         numErrors.mark();
-        authenticationFailure(response, authResponse.getAuthCode().getMsg(), HttpServletResponse.SC_BAD_REQUEST, BearerWwwAuthErrorCode.invalid_request);
+        authenticationFailure(
+            response,
+            authResponse.getAuthCode().getMsg(),
+            HttpServletResponse.SC_BAD_REQUEST,
+            BearerWwwAuthErrorCode.invalid_request);
         return false;
 
       case CLAIM_MISMATCH:
@@ -401,24 +470,40 @@ public class JWTAuthPlugin extends AuthenticationPlugin implements SpecProvider,
       case PRINCIPAL_MISSING:
         log.warn("Authentication failed. {}, {}", authResponse.getAuthCode(), exceptionMessage);
         numWrongCredentials.inc();
-        authenticationFailure(response, authResponse.getAuthCode().getMsg(), HttpServletResponse.SC_UNAUTHORIZED, BearerWwwAuthErrorCode.invalid_token);
+        authenticationFailure(
+            response,
+            authResponse.getAuthCode().getMsg(),
+            HttpServletResponse.SC_UNAUTHORIZED,
+            BearerWwwAuthErrorCode.invalid_token);
         return false;
 
       case SIGNATURE_INVALID:
         log.warn("Signature validation failed: {}", exceptionMessage);
         numWrongCredentials.inc();
-        authenticationFailure(response, authResponse.getAuthCode().getMsg(), HttpServletResponse.SC_UNAUTHORIZED, BearerWwwAuthErrorCode.invalid_token);
+        authenticationFailure(
+            response,
+            authResponse.getAuthCode().getMsg(),
+            HttpServletResponse.SC_UNAUTHORIZED,
+            BearerWwwAuthErrorCode.invalid_token);
         return false;
 
       case SCOPE_MISSING:
         numWrongCredentials.inc();
-        authenticationFailure(response, authResponse.getAuthCode().getMsg(), HttpServletResponse.SC_UNAUTHORIZED, BearerWwwAuthErrorCode.insufficient_scope);
+        authenticationFailure(
+            response,
+            authResponse.getAuthCode().getMsg(),
+            HttpServletResponse.SC_UNAUTHORIZED,
+            BearerWwwAuthErrorCode.insufficient_scope);
         return false;
 
       case NO_AUTZ_HEADER:
       default:
         numMissingCredentials.inc();
-        authenticationFailure(response, authResponse.getAuthCode().getMsg(), HttpServletResponse.SC_UNAUTHORIZED, null);
+        authenticationFailure(
+            response,
+            authResponse.getAuthCode().getMsg(),
+            HttpServletResponse.SC_UNAUTHORIZED,
+            null);
         return false;
     }
   }
@@ -438,25 +523,38 @@ public class JWTAuthPlugin extends AuthenticationPlugin implements SpecProvider,
             JwtClaims jwtClaims = jwtConsumer.processToClaims(jwtCompact);
             String principal = jwtClaims.getStringClaimValue(principalClaim);
             if (principal == null || principal.isEmpty()) {
-              return new JWTAuthenticationResponse(AuthCode.PRINCIPAL_MISSING, "Cannot identify principal from JWT. Required claim " + principalClaim + " missing. Cannot authenticate");
+              return new JWTAuthenticationResponse(
+                  AuthCode.PRINCIPAL_MISSING,
+                  "Cannot identify principal from JWT. Required claim "
+                      + principalClaim
+                      + " missing. Cannot authenticate");
             }
             if (claimsMatchCompiled != null) {
               for (Map.Entry<String, Pattern> entry : claimsMatchCompiled.entrySet()) {
                 String claim = entry.getKey();
                 if (jwtClaims.hasClaim(claim)) {
                   if (!entry.getValue().matcher(jwtClaims.getStringClaimValue(claim)).matches()) {
-                    return new JWTAuthenticationResponse(AuthCode.CLAIM_MISMATCH,
-                        "Claim " + claim + "=" + jwtClaims.getStringClaimValue(claim)
-                            + " does not match required regular expression " + entry.getValue().pattern());
+                    return new JWTAuthenticationResponse(
+                        AuthCode.CLAIM_MISMATCH,
+                        "Claim "
+                            + claim
+                            + "="
+                            + jwtClaims.getStringClaimValue(claim)
+                            + " does not match required regular expression "
+                            + entry.getValue().pattern());
                   }
                 } else {
-                  return new JWTAuthenticationResponse(AuthCode.CLAIM_MISMATCH, "Claim " + claim + " is required but does not exist in JWT");
+                  return new JWTAuthenticationResponse(
+                      AuthCode.CLAIM_MISMATCH,
+                      "Claim " + claim + " is required but does not exist in JWT");
                 }
               }
             }
             if (!requiredScopes.isEmpty() && !jwtClaims.hasClaim(CLAIM_SCOPE)) {
               // Fail if we require scopes but they don't exist
-              return new JWTAuthenticationResponse(AuthCode.CLAIM_MISMATCH, "Claim " + CLAIM_SCOPE + " is required but does not exist in JWT");
+              return new JWTAuthenticationResponse(
+                  AuthCode.CLAIM_MISMATCH,
+                  "Claim " + CLAIM_SCOPE + " is required but does not exist in JWT");
             }
 
             // Find scopes for user
@@ -471,7 +569,12 @@ public class JWTAuthPlugin extends AuthenticationPlugin implements SpecProvider,
               // Validate that at least one of the required scopes are present in the scope claim
               if (!requiredScopes.isEmpty()) {
                 if (scopes.stream().noneMatch(requiredScopes::contains)) {
-                  return new JWTAuthenticationResponse(AuthCode.SCOPE_MISSING, "Claim " + CLAIM_SCOPE + " does not contain any of the required scopes: " + requiredScopes);
+                  return new JWTAuthenticationResponse(
+                      AuthCode.SCOPE_MISSING,
+                      "Claim "
+                          + CLAIM_SCOPE
+                          + " does not contain any of the required scopes: "
+                          + requiredScopes);
                 }
               }
             }
@@ -479,11 +582,13 @@ public class JWTAuthPlugin extends AuthenticationPlugin implements SpecProvider,
             // Determine roles of user, either from 'rolesClaim' or from 'scope' as parsed above
             final Set<String> finalRoles = new HashSet<>();
             if (rolesClaim == null) {
-              // Pass scopes with principal to signal to any Authorization plugins that user has some verified role claims
+              // Pass scopes with principal to signal to any Authorization plugins that user has
+              // some verified role claims
               finalRoles.addAll(scopes);
               finalRoles.remove("openid"); // Remove standard scope
             } else {
-              // Pull roles from separate claim, either as whitespace separated list or as JSON array
+              // Pull roles from separate claim, either as whitespace separated list or as JSON
+              // array
               Object rolesObj = jwtClaims.getClaimValue(rolesClaim);
               if (rolesObj != null) {
                 if (rolesObj instanceof String) {
@@ -494,32 +599,46 @@ public class JWTAuthPlugin extends AuthenticationPlugin implements SpecProvider,
               }
             }
             if (finalRoles.size() > 0) {
-              return new JWTAuthenticationResponse(AuthCode.AUTHENTICATED, new JWTPrincipalWithUserRoles(principal, jwtCompact, jwtClaims.getClaimsMap(), finalRoles));
+              return new JWTAuthenticationResponse(
+                  AuthCode.AUTHENTICATED,
+                  new JWTPrincipalWithUserRoles(
+                      principal, jwtCompact, jwtClaims.getClaimsMap(), finalRoles));
             } else {
-              return new JWTAuthenticationResponse(AuthCode.AUTHENTICATED, new JWTPrincipal(principal, jwtCompact, jwtClaims.getClaimsMap()));
+              return new JWTAuthenticationResponse(
+                  AuthCode.AUTHENTICATED,
+                  new JWTPrincipal(principal, jwtCompact, jwtClaims.getClaimsMap()));
             }
           } catch (InvalidJwtSignatureException ise) {
             return new JWTAuthenticationResponse(AuthCode.SIGNATURE_INVALID, ise);
           } catch (InvalidJwtException e) {
             // Whether or not the JWT has expired being one common reason for invalidity
             if (e.hasExpired()) {
-              return new JWTAuthenticationResponse(AuthCode.JWT_EXPIRED, "Authentication failed due to expired JWT token. Expired at " + e.getJwtContext().getJwtClaims().getExpirationTime());
+              return new JWTAuthenticationResponse(
+                  AuthCode.JWT_EXPIRED,
+                  "Authentication failed due to expired JWT token. Expired at "
+                      + e.getJwtContext().getJwtClaims().getExpirationTime());
             }
-            if (e.getCause() != null && e.getCause() instanceof JoseException && e.getCause().getMessage().contains("Invalid JOSE Compact Serialization")) {
-              return new JWTAuthenticationResponse(AuthCode.JWT_PARSE_ERROR, e.getCause().getMessage());
+            if (e.getCause() != null
+                && e.getCause() instanceof JoseException
+                && e.getCause().getMessage().contains("Invalid JOSE Compact Serialization")) {
+              return new JWTAuthenticationResponse(
+                  AuthCode.JWT_PARSE_ERROR, e.getCause().getMessage());
             }
             return new JWTAuthenticationResponse(AuthCode.JWT_VALIDATION_EXCEPTION, e);
           }
         } catch (MalformedClaimException e) {
-          return new JWTAuthenticationResponse(AuthCode.JWT_PARSE_ERROR, "Malformed claim, error was: " + e.getMessage());
+          return new JWTAuthenticationResponse(
+              AuthCode.JWT_PARSE_ERROR, "Malformed claim, error was: " + e.getMessage());
         }
       } else {
-        return new JWTAuthenticationResponse(AuthCode.AUTZ_HEADER_PROBLEM, "Authorization header is not in correct format");
+        return new JWTAuthenticationResponse(
+            AuthCode.AUTZ_HEADER_PROBLEM, "Authorization header is not in correct format");
       }
     } else {
       // No Authorization header
       if (blockUnknown) {
-        return new JWTAuthenticationResponse(AuthCode.NO_AUTZ_HEADER, "Missing Authorization header");
+        return new JWTAuthenticationResponse(
+            AuthCode.NO_AUTZ_HEADER, "Missing Authorization header");
       } else {
         log.debug("No user authenticated, but blockUnknown=false, so letting request through");
         return new JWTAuthenticationResponse(AuthCode.PASS_THROUGH);
@@ -539,23 +658,36 @@ public class JWTAuthPlugin extends AuthenticationPlugin implements SpecProvider,
   }
 
   private void initConsumer() {
-    JwtConsumerBuilder jwtConsumerBuilder = new JwtConsumerBuilder()
-        .setAllowedClockSkewInSeconds(30); // allow some leeway in validating time based claims to account for clock skew
-    String[] issuers = issuerConfigs.stream().map(JWTIssuerConfig::getIss).filter(Objects::nonNull).toArray(String[]::new);
+    JwtConsumerBuilder jwtConsumerBuilder =
+        new JwtConsumerBuilder()
+            .setAllowedClockSkewInSeconds(
+                30); // allow some leeway in validating time based claims to account for clock skew
+    String[] issuers =
+        issuerConfigs.stream()
+            .map(JWTIssuerConfig::getIss)
+            .filter(Objects::nonNull)
+            .toArray(String[]::new);
     if (issuers.length > 0) {
-      jwtConsumerBuilder.setExpectedIssuers(requireIssuer, issuers); // whom the JWT needs to have been issued by
-    }
-    String[] audiences = issuerConfigs.stream().map(JWTIssuerConfig::getAud).filter(Objects::nonNull).toArray(String[]::new);
+      jwtConsumerBuilder.setExpectedIssuers(
+          requireIssuer, issuers); // whom the JWT needs to have been issued by
+    }
+    String[] audiences =
+        issuerConfigs.stream()
+            .map(JWTIssuerConfig::getAud)
+            .filter(Objects::nonNull)
+            .toArray(String[]::new);
     if (audiences.length > 0) {
       jwtConsumerBuilder.setExpectedAudience(audiences); // to whom the JWT is intended for
     } else {
       jwtConsumerBuilder.setSkipDefaultAudienceValidation();
     }
-    if (requireExpirationTime)
-      jwtConsumerBuilder.setRequireExpirationTime();
+    if (requireExpirationTime) jwtConsumerBuilder.setRequireExpirationTime();
     if (algAllowlist != null)
-      jwtConsumerBuilder.setJwsAlgorithmConstraints( // only allow the expected signature algorithm(s) in the given context
-          new AlgorithmConstraints(AlgorithmConstraints.ConstraintType.PERMIT, algAllowlist.toArray(new String[0])));
+      jwtConsumerBuilder
+          .setJwsAlgorithmConstraints( // only allow the expected signature algorithm(s) in the
+              // given context
+              new AlgorithmConstraints(
+                  AlgorithmConstraints.ConstraintType.PERMIT, algAllowlist.toArray(new String[0])));
     jwtConsumerBuilder.setVerificationKeyResolver(verificationKeyResolver);
     jwtConsumer = jwtConsumerBuilder.build(); // create the JwtConsumer instance
   }
@@ -571,11 +703,10 @@ public class JWTAuthPlugin extends AuthenticationPlugin implements SpecProvider,
   }
 
   /**
-   * Operate the commands on the latest conf and return a new conf object
-   * If there are errors in the commands , throw a SolrException. return a null
-   * if no changes are to be made as a result of this edit. It is the responsibility
-   * of the implementation to ensure that the returned config is valid . The framework
-   * does no validation of the data
+   * Operate the commands on the latest conf and return a new conf object If there are errors in the
+   * commands , throw a SolrException. return a null if no changes are to be made as a result of
+   * this edit. It is the responsibility of the implementation to ensure that the returned config is
+   * valid . The framework does no validation of the data
    *
    * @param latestConf latest version of config
    * @param commands the list of command operations to perform
@@ -598,9 +729,18 @@ public class JWTAuthPlugin extends AuthenticationPlugin implements SpecProvider,
     return latestConf;
   }
 
-  private enum BearerWwwAuthErrorCode { invalid_request, invalid_token, insufficient_scope}
+  private enum BearerWwwAuthErrorCode {
+    invalid_request,
+    invalid_token,
+    insufficient_scope
+  }
 
-  private void authenticationFailure(HttpServletResponse response, String message, int httpCode, BearerWwwAuthErrorCode responseError) throws IOException {
+  private void authenticationFailure(
+      HttpServletResponse response,
+      String message,
+      int httpCode,
+      BearerWwwAuthErrorCode responseError)
+      throws IOException {
     getPromptHeaders(responseError, message).forEach(response::setHeader);
     response.sendError(httpCode, message);
     log.info("JWT Authentication attempt failed: {}", message);
@@ -608,12 +748,15 @@ public class JWTAuthPlugin extends AuthenticationPlugin implements SpecProvider,
 
   /**
    * Generate proper response prompt headers
-   * @param responseError standardized error code. Set to 'null' to generate WWW-Authenticate header with no error
+   *
+   * @param responseError standardized error code. Set to 'null' to generate WWW-Authenticate header
+   *     with no error
    * @param message custom message string to return in www-authenticate, or null if no error
    * @return map of headers to add to response
    */
-  private Map<String, String> getPromptHeaders(BearerWwwAuthErrorCode responseError, String message) {
-    Map<String,String> headers = new HashMap<>();
+  private Map<String, String> getPromptHeaders(
+      BearerWwwAuthErrorCode responseError, String message) {
+    Map<String, String> headers = new HashMap<>();
     List<String> wwwAuthParams = new ArrayList<>();
     wwwAuthParams.add("Bearer realm=\"" + realm + "\"");
     if (responseError != null) {
@@ -627,8 +770,9 @@ public class JWTAuthPlugin extends AuthenticationPlugin implements SpecProvider,
 
   protected String generateAuthDataHeader() {
     JWTIssuerConfig primaryIssuer = getPrimaryIssuer();
-    Map<String,Object> data = new HashMap<>();
-    data.put(JWTIssuerConfig.PARAM_AUTHORIZATION_ENDPOINT, primaryIssuer.getAuthorizationEndpoint());
+    Map<String, Object> data = new HashMap<>();
+    data.put(
+        JWTIssuerConfig.PARAM_AUTHORIZATION_ENDPOINT, primaryIssuer.getAuthorizationEndpoint());
     data.put("client_id", primaryIssuer.getClientId());
     data.put("scope", adminUiScope);
     data.put("redirect_uris", redirectUris);
@@ -636,78 +780,80 @@ public class JWTAuthPlugin extends AuthenticationPlugin implements SpecProvider,
     return Base64.getEncoder().encodeToString(headerJson.getBytes(StandardCharsets.UTF_8));
   }
 
-  /**
-   * Response for authentication attempt
-   */
+  /** Response for authentication attempt */
   static class JWTAuthenticationResponse {
     private final Principal principal;
     private String errorMessage;
     private final AuthCode authCode;
     private InvalidJwtException jwtException;
-  
+
     enum AuthCode {
-      PASS_THROUGH("No user, pass through"),             // Returned when no user authentication but block_unknown=false 
-      AUTHENTICATED("Authenticated"),                    // Returned when authentication OK 
-      PRINCIPAL_MISSING("No principal in JWT"),          // JWT token does not contain necessary principal (typically sub)  
-      JWT_PARSE_ERROR("Invalid JWT"),                    // Problems with parsing the JWT itself 
-      AUTZ_HEADER_PROBLEM("Wrong header"),               // The Authorization header exists but is not correct 
-      NO_AUTZ_HEADER("Require authentication"),          // The Authorization header is missing 
-      JWT_EXPIRED("JWT token expired"),                  // JWT token has expired 
-      CLAIM_MISMATCH("Required JWT claim missing"),      // Some required claims are missing or wrong 
-      JWT_VALIDATION_EXCEPTION("JWT validation failed"), // The JWT parser failed validation. More details in exception
-      SCOPE_MISSING("Required scope missing in JWT"),    // None of the required scopes were present in JWT
-      SIGNATURE_INVALID("Signature invalid");            // Validation of JWT signature failed
+      PASS_THROUGH(
+          "No user, pass through"), // Returned when no user authentication but block_unknown=false
+      AUTHENTICATED("Authenticated"), // Returned when authentication OK
+      PRINCIPAL_MISSING(
+          "No principal in JWT"), // JWT token does not contain necessary principal (typically sub)
+      JWT_PARSE_ERROR("Invalid JWT"), // Problems with parsing the JWT itself
+      AUTZ_HEADER_PROBLEM("Wrong header"), // The Authorization header exists but is not correct
+      NO_AUTZ_HEADER("Require authentication"), // The Authorization header is missing
+      JWT_EXPIRED("JWT token expired"), // JWT token has expired
+      CLAIM_MISMATCH("Required JWT claim missing"), // Some required claims are missing or wrong
+      JWT_VALIDATION_EXCEPTION(
+          "JWT validation failed"), // The JWT parser failed validation. More details in exception
+      SCOPE_MISSING(
+          "Required scope missing in JWT"), // None of the required scopes were present in JWT
+      SIGNATURE_INVALID("Signature invalid"); // Validation of JWT signature failed
 
       public String getMsg() {
         return msg;
       }
-  
+
       private final String msg;
-  
+
       AuthCode(String msg) {
         this.msg = msg;
       }
     }
-  
+
     JWTAuthenticationResponse(AuthCode authCode, InvalidJwtException e) {
       this.authCode = authCode;
       this.jwtException = e;
       principal = null;
       this.errorMessage = e.getMessage();
     }
-    
+
     JWTAuthenticationResponse(AuthCode authCode, String errorMessage) {
       this.authCode = authCode;
       this.errorMessage = errorMessage;
       principal = null;
     }
-  
+
     JWTAuthenticationResponse(AuthCode authCode, Principal principal) {
       this.authCode = authCode;
       this.principal = principal;
     }
-    
+
     JWTAuthenticationResponse(AuthCode authCode) {
       this.authCode = authCode;
       principal = null;
     }
-  
+
     boolean isAuthenticated() {
       return authCode.equals(AuthCode.AUTHENTICATED);
     }
-  
+
     public Principal getPrincipal() {
       return principal;
     }
-  
+
     String getErrorMessage() {
       return errorMessage;
     }
-  
+
     InvalidJwtException getJwtException() {
       return jwtException;
     }
-  
+
     AuthCode getAuthCode() {
       return authCode;
     }
@@ -743,6 +889,7 @@ public class JWTAuthPlugin extends AuthenticationPlugin implements SpecProvider,
 
   /**
    * Lookup issuer config by its name
+   *
    * @param name name property of config
    * @return issuer config object or null if not found
    */
diff --git a/solr/core/src/java/org/apache/solr/security/JWTIssuerConfig.java b/solr/core/src/java/org/apache/solr/security/JWTIssuerConfig.java
index c7e67bf..11b4115 100644
--- a/solr/core/src/java/org/apache/solr/security/JWTIssuerConfig.java
+++ b/solr/core/src/java/org/apache/solr/security/JWTIssuerConfig.java
@@ -18,18 +18,6 @@
 package org.apache.solr.security;
 
 import com.google.common.annotations.VisibleForTesting;
-import org.apache.commons.io.IOUtils;
-import org.apache.solr.common.SolrException;
-import org.apache.solr.common.util.Utils;
-import org.jose4j.http.Get;
-import org.jose4j.http.SimpleResponse;
-import org.jose4j.jwk.HttpsJwks;
-import org.jose4j.jwk.JsonWebKey;
-import org.jose4j.jwk.JsonWebKeySet;
-import org.jose4j.lang.JoseException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
@@ -46,10 +34,19 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.stream.Collectors;
+import org.apache.commons.io.IOUtils;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.util.Utils;
+import org.jose4j.http.Get;
+import org.jose4j.http.SimpleResponse;
+import org.jose4j.jwk.HttpsJwks;
+import org.jose4j.jwk.JsonWebKey;
+import org.jose4j.jwk.JsonWebKeySet;
+import org.jose4j.lang.JoseException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
-/**
- * Holds information about an IdP (issuer), such as issuer ID, JWK url(s), keys etc
- */
+/** Holds information about an IdP (issuer), such as issuer ID, JWK url(s), keys etc */
 public class JWTIssuerConfig {
   private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
   static final String PARAM_ISS_NAME = "name";
@@ -61,8 +58,7 @@ public class JWTIssuerConfig {
   static final String PARAM_AUTHORIZATION_ENDPOINT = "authorizationEndpoint";
   static final String PARAM_CLIENT_ID = "clientId";
 
-  private static HttpsJwksFactory httpsJwksFactory =
-      new HttpsJwksFactory(3600, 5000);
+  private static HttpsJwksFactory httpsJwksFactory = new HttpsJwksFactory(3600, 5000);
   private String iss;
   private String aud;
   private JsonWebKeySet jsonWebKeySet;
@@ -75,12 +71,14 @@ public class JWTIssuerConfig {
   private String authorizationEndpoint;
   private Collection<X509Certificate> trustedCerts;
 
-  public static boolean ALLOW_OUTBOUND_HTTP = Boolean.parseBoolean(System.getProperty("solr.auth.jwt.allowOutboundHttp", "false"));
-  public static final String ALLOW_OUTBOUND_HTTP_ERR_MSG = "HTTPS required for IDP communication. Please use SSL or start your nodes with -Dsolr.auth.jwt.allowOutboundHttp=true to allow HTTP for test purposes.";
+  public static boolean ALLOW_OUTBOUND_HTTP =
+      Boolean.parseBoolean(System.getProperty("solr.auth.jwt.allowOutboundHttp", "false"));
+  public static final String ALLOW_OUTBOUND_HTTP_ERR_MSG =
+      "HTTPS required for IDP communication. Please use SSL or start your nodes with -Dsolr.auth.jwt.allowOutboundHttp=true to allow HTTP for test purposes.";
 
   /**
-   * Create config for further configuration with setters, builder style.
-   * Once all values are set, call {@link #init()} before further use
+   * Create config for further configuration with setters, builder style. Once all values are set,
+   * call {@link #init()} before further use
    *
    * @param name a unique name for this issuer
    */
@@ -98,8 +96,9 @@ public class JWTIssuerConfig {
   }
 
   /**
-   * Call this to validate and initialize an object which is populated with setters.
-   * Init will fetch wellKnownUrl if relevant
+   * Call this to validate and initialize an object which is populated with setters. Init will fetch
+   * wellKnownUrl if relevant
+   *
    * @throws SolrException if issuer is missing
    */
   public void init() {
@@ -110,7 +109,9 @@ public class JWTIssuerConfig {
       try {
         wellKnownDiscoveryConfig = fetchWellKnown(new URL(wellKnownUrl));
       } catch (MalformedURLException e) {
-        throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Wrong URL given for well-known endpoint " + wellKnownUrl);
+        throw new SolrException(
+            SolrException.ErrorCode.SERVER_ERROR,
+            "Wrong URL given for well-known endpoint " + wellKnownUrl);
       }
       if (iss == null) {
         iss = wellKnownDiscoveryConfig.getIssuer();
@@ -123,12 +124,15 @@ public class JWTIssuerConfig {
       }
     }
     if (iss == null && usesHttpsJwk() && !JWTAuthPlugin.PRIMARY_ISSUER.equals(name)) {
-      throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Missing required config 'iss' for issuer " + getName());
+      throw new SolrException(
+          SolrException.ErrorCode.SERVER_ERROR,
+          "Missing required config 'iss' for issuer " + getName());
     }
   }
 
   /**
    * Parses configuration for one IssuerConfig and sets all variables found
+   *
    * @throws SolrException if unknown parameter names found in config
    */
   protected void parseConfigMap(Map<String, Object> configMap) {
@@ -153,12 +157,15 @@ public class JWTIssuerConfig {
     conf.remove(PARAM_AUTHORIZATION_ENDPOINT);
 
     if (!conf.isEmpty()) {
-      throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Unknown configuration key " + conf.keySet() + " for issuer " + name);
+      throw new SolrException(
+          SolrException.ErrorCode.SERVER_ERROR,
+          "Unknown configuration key " + conf.keySet() + " for issuer " + name);
     }
   }
 
   /**
    * Setter that takes a jwk config object, parses it into a {@link JsonWebKeySet} and sets it
+   *
    * @param jwksObject the config object to parse
    */
   @SuppressWarnings("unchecked")
@@ -168,7 +175,10 @@ public class JWTIssuerConfig {
         jsonWebKeySet = parseJwkSet((Map<String, Object>) jwksObject);
       }
     } catch (JoseException e) {
-      throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Failed parsing parameter 'jwk' for issuer " + getName(), e);
+      throw new SolrException(
+          SolrException.ErrorCode.SERVER_ERROR,
+          "Failed parsing parameter 'jwk' for issuer " + getName(),
+          e);
     }
   }
 
@@ -228,6 +238,7 @@ public class JWTIssuerConfig {
 
   /**
    * Setter that converts from String or List into a list
+   *
    * @param jwksUrlListOrString object that should be either string or list
    * @return this for builder pattern
    * @throws SolrException if wrong type
@@ -236,10 +247,11 @@ public class JWTIssuerConfig {
   public JWTIssuerConfig setJwksUrl(Object jwksUrlListOrString) {
     if (jwksUrlListOrString instanceof String)
       this.jwksUrl = Collections.singletonList((String) jwksUrlListOrString);
-    else if (jwksUrlListOrString instanceof List)
-      this.jwksUrl = (List<String>) jwksUrlListOrString;
+    else if (jwksUrlListOrString instanceof List) this.jwksUrl = (List<String>) jwksUrlListOrString;
     else if (jwksUrlListOrString != null)
-      throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Parameter " + PARAM_JWKS_URL + " must be either List or String");
+      throw new SolrException(
+          SolrException.ErrorCode.SERVER_ERROR,
+          "Parameter " + PARAM_JWKS_URL + " must be either List or String");
     return this;
   }
 
@@ -252,6 +264,7 @@ public class JWTIssuerConfig {
 
   /**
    * Set the factory to use when creating HttpsJwks objects
+   *
    * @param httpsJwksFactory factory with custom settings
    */
   public static void setHttpsJwksFactory(HttpsJwksFactory httpsJwksFactory) {
@@ -269,6 +282,7 @@ public class JWTIssuerConfig {
 
   /**
    * Check if the issuer is backed by HttpsJwk url(s)
+   *
    * @return true if keys are fetched over https
    */
   public boolean usesHttpsJwk() {
@@ -306,8 +320,8 @@ public class JWTIssuerConfig {
     return this;
   }
 
-  public Map<String,Object> asConfig() {
-    HashMap<String,Object> config = new HashMap<>();
+  public Map<String, Object> asConfig() {
+    HashMap<String, Object> config = new HashMap<>();
     putIfNotNull(config, PARAM_ISS_NAME, name);
     putIfNotNull(config, PARAM_ISSUER, iss);
     putIfNotNull(config, PARAM_AUDIENCE, aud);
@@ -329,6 +343,7 @@ public class JWTIssuerConfig {
 
   /**
    * Validates that this config has a name and either jwksUrl, wellkKownUrl or jwk
+   *
    * @return true if a configuration is found and is valid, otherwise false
    * @throws SolrException if configuration is present but wrong
    */
@@ -337,11 +352,18 @@ public class JWTIssuerConfig {
     jwkConfigured += jwksUrl != null ? 2 : 0;
     jwkConfigured += jsonWebKeySet != null ? 2 : 0;
     if (jwkConfigured > 3) {
-      throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "JWTAuthPlugin needs to configure exactly one of " +
-          PARAM_WELL_KNOWN_URL + ", " + PARAM_JWKS_URL + " and " + PARAM_JWK);
+      throw new SolrException(
+          SolrException.ErrorCode.SERVER_ERROR,
+          "JWTAuthPlugin needs to configure exactly one of "
+              + PARAM_WELL_KNOWN_URL
+              + ", "
+              + PARAM_JWKS_URL
+              + " and "
+              + PARAM_JWK);
     }
     if (jwkConfigured > 0 && name == null) {
-      throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
+      throw new SolrException(
+          SolrException.ErrorCode.SERVER_ERROR,
           "Parameter 'name' is required for issuer configurations");
     }
     return jwkConfigured > 0;
@@ -356,9 +378,7 @@ public class JWTIssuerConfig {
     return this.trustedCerts;
   }
 
-  /**
-   *
-   */
+  /** */
   static class HttpsJwksFactory {
     private final long jwkCacheDuration;
     private final long refreshReprieveThreshold;
@@ -369,7 +389,10 @@ public class JWTIssuerConfig {
       this.refreshReprieveThreshold = refreshReprieveThreshold;
     }
 
-    public HttpsJwksFactory(long jwkCacheDuration, long refreshReprieveThreshold, Collection<X509Certificate> trustedCerts) {
+    public HttpsJwksFactory(
+        long jwkCacheDuration,
+        long refreshReprieveThreshold,
+        Collection<X509Certificate> trustedCerts) {
       this.jwkCacheDuration = jwkCacheDuration;
       this.refreshReprieveThreshold = refreshReprieveThreshold;
       this.trustedCerts = trustedCerts;
@@ -386,7 +409,9 @@ public class JWTIssuerConfig {
         jwksUrl = new URL(url);
         checkAllowOutboundHttpConnections(PARAM_JWKS_URL, jwksUrl);
       } catch (MalformedURLException e) {
-        throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Url " + url + " configured in " + PARAM_JWKS_URL + " is not a valid URL");
+        throw new SolrException(
+            SolrException.ErrorCode.SERVER_ERROR,
+            "Url " + url + " configured in " + PARAM_JWKS_URL + " is not a valid URL");
       }
       HttpsJwks httpsJkws = new HttpsJwks(url);
       httpsJkws.setDefaultCacheDuration(jwkCacheDuration);
@@ -408,8 +433,8 @@ public class JWTIssuerConfig {
   }
 
   /**
-   * Config object for a OpenId Connect well-known config
-   * Typically exposed through /.well-known/openid-configuration endpoint
+   * Config object for a OpenId Connect well-known config Typically exposed through
+   * /.well-known/openid-configuration endpoint
    */
   public static class WellKnownDiscoveryConfig {
     private final Map<String, Object> securityConf;
@@ -424,14 +449,19 @@ public class JWTIssuerConfig {
 
     /**
      * Fetch well-known config from a URL, with optional list of trusted certificates
+     *
      * @param url the url to fetch
-     * @param trustedCerts optional list of trusted SSL certs. May be null to fall-back to Java's defaults
+     * @param trustedCerts optional list of trusted SSL certs. May be null to fall-back to Java's
+     *     defaults
      * @return an instance of WellKnownDiscoveryConfig object
      */
-    public static WellKnownDiscoveryConfig parse(URL url, Collection<X509Certificate> trustedCerts) {
+    public static WellKnownDiscoveryConfig parse(
+        URL url, Collection<X509Certificate> trustedCerts) {
       try {
         if (!Arrays.asList("https", "file", "http").contains(url.getProtocol())) {
-          throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Well-known config URL must be one of HTTPS or HTTP or file");
+          throw new SolrException(
+              SolrException.ErrorCode.BAD_REQUEST,
+              "Well-known config URL must be one of HTTPS or HTTP or file");
         }
         checkAllowOutboundHttpConnections(PARAM_WELL_KNOWN_URL, url);
 
@@ -449,7 +479,10 @@ public class JWTIssuerConfig {
           return parse(IOUtils.toInputStream(resp.getBody(), StandardCharsets.UTF_8));
         }
       } catch (IOException e) {
-        throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Well-known config could not be read from url " + url, e);
+        throw new SolrException(
+            SolrException.ErrorCode.SERVER_ERROR,
+            "Well-known config could not be read from url " + url,
+            e);
       }
     }
 
@@ -463,7 +496,6 @@ public class JWTIssuerConfig {
       return new WellKnownDiscoveryConfig((Map<String, Object>) Utils.fromJSON(configStream));
     }
 
-
     public String getJwksUrl() {
       return (String) securityConf.get("jwks_uri");
     }
@@ -498,9 +530,10 @@ public class JWTIssuerConfig {
   public static void checkAllowOutboundHttpConnections(String parameterName, URL url) {
     if ("http".equalsIgnoreCase(url.getProtocol())) {
       if (!ALLOW_OUTBOUND_HTTP) {
-        throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, parameterName + " is using http protocol. " + ALLOW_OUTBOUND_HTTP_ERR_MSG);
+        throw new SolrException(
+            SolrException.ErrorCode.BAD_REQUEST,
+            parameterName + " is using http protocol. " + ALLOW_OUTBOUND_HTTP_ERR_MSG);
       }
     }
   }
- 
 }
diff --git a/solr/core/src/java/org/apache/solr/security/JWTPrincipal.java b/solr/core/src/java/org/apache/solr/security/JWTPrincipal.java
index 810e49c..a779fad 100644
--- a/solr/core/src/java/org/apache/solr/security/JWTPrincipal.java
+++ b/solr/core/src/java/org/apache/solr/security/JWTPrincipal.java
@@ -20,24 +20,22 @@ package org.apache.solr.security;
 import java.security.Principal;
 import java.util.Map;
 import java.util.Objects;
-
 import org.apache.http.util.Args;
 
-/**
- * Principal object that carries JWT token and claims for authenticated user.
- */
+/** Principal object that carries JWT token and claims for authenticated user. */
 public class JWTPrincipal implements Principal {
   final String username;
   String token;
-  Map<String,Object> claims;
+  Map<String, Object> claims;
 
   /**
    * User principal with user name as well as one or more roles that he/she belong to
+   *
    * @param username string with user name for user
    * @param token compact string representation of JWT token
    * @param claims list of verified JWT claims as a map
    */
-  public JWTPrincipal(final String username, String token, Map<String,Object> claims) {
+  public JWTPrincipal(final String username, String token, Map<String, Object> claims) {
     super();
     Args.notNull(username, "User name");
     Args.notNull(token, "JWT token");
@@ -65,9 +63,9 @@ public class JWTPrincipal implements Principal {
     if (this == o) return true;
     if (o == null || getClass() != o.getClass()) return false;
     JWTPrincipal that = (JWTPrincipal) o;
-    return Objects.equals(username, that.username) &&
-        Objects.equals(token, that.token) &&
-        Objects.equals(claims, that.claims);
+    return Objects.equals(username, that.username)
+        && Objects.equals(token, that.token)
+        && Objects.equals(claims, that.claims);
   }
 
   @Override
@@ -77,10 +75,15 @@ public class JWTPrincipal implements Principal {
 
   @Override
   public String toString() {
-    return "JWTPrincipal{" +
-        "username='" + username + '\'' +
-        ", token='" + "*****" + '\'' +
-        ", claims=" + claims +
-        '}';
+    return "JWTPrincipal{"
+        + "username='"
+        + username
+        + '\''
+        + ", token='"
+        + "*****"
+        + '\''
+        + ", claims="
+        + claims
+        + '}';
   }
 }
diff --git a/solr/core/src/java/org/apache/solr/security/JWTPrincipalWithUserRoles.java b/solr/core/src/java/org/apache/solr/security/JWTPrincipalWithUserRoles.java
index 850dc1f..856e6c2 100644
--- a/solr/core/src/java/org/apache/solr/security/JWTPrincipalWithUserRoles.java
+++ b/solr/core/src/java/org/apache/solr/security/JWTPrincipalWithUserRoles.java
@@ -20,27 +20,25 @@ package org.apache.solr.security;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
-
 import org.apache.http.util.Args;
 
 /**
- * JWT principal that contains username, token, claims and a list of roles the user has, 
- * so one can keep track of user-role mappings in an Identity Server external to Solr and 
- * pass the information to Solr in a signed JWT token. The role information can then be used to authorize
- * requests without the need to maintain or lookup what roles each user belongs to.
+ * JWT principal that contains username, token, claims and a list of roles the user has, so one can
+ * keep track of user-role mappings in an Identity Server external to Solr and pass the information
+ * to Solr in a signed JWT token. The role information can then be used to authorize requests
+ * without the need to maintain or lookup what roles each user belongs to.
  */
 public class JWTPrincipalWithUserRoles extends JWTPrincipal implements VerifiedUserRoles {
   private final Set<String> roles;
 
-  public JWTPrincipalWithUserRoles(final String username, String token, Map<String,Object> claims, Set<String> roles) {
+  public JWTPrincipalWithUserRoles(
+      final String username, String token, Map<String, Object> claims, Set<String> roles) {
     super(username, token, claims);
     Args.notNull(roles, "User roles");
     this.roles = roles;
   }
 
-  /**
-   * Gets the list of roles
-   */
+  /** Gets the list of roles */
   @Override
   public Set<String> getVerifiedRoles() {
     return roles;
@@ -48,8 +46,7 @@ public class JWTPrincipalWithUserRoles extends JWTPrincipal implements VerifiedU
 
   @Override
   public boolean equals(Object o) {
-    if (!(o instanceof JWTPrincipalWithUserRoles))
-      return false;
+    if (!(o instanceof JWTPrincipalWithUserRoles)) return false;
     JWTPrincipalWithUserRoles that = (JWTPrincipalWithUserRoles) o;
     return super.equals(o) && roles.equals(that.roles);
   }
@@ -61,11 +58,17 @@ public class JWTPrincipalWithUserRoles extends JWTPrincipal implements VerifiedU
 
   @Override
   public String toString() {
-    return "JWTPrincipalWithUserRoles{" +
-        "username='" + username + '\'' +
-        ", token='" + "*****" + '\'' +
-        ", claims=" + claims +
-        ", roles=" + roles +
-        '}';
+    return "JWTPrincipalWithUserRoles{"
+        + "username='"
+        + username
+        + '\''
+        + ", token='"
+        + "*****"
+        + '\''
+        + ", claims="
+        + claims
+        + ", roles="
+        + roles
+        + '}';
   }
 }
diff --git a/solr/core/src/java/org/apache/solr/security/JWTVerificationkeyResolver.java b/solr/core/src/java/org/apache/solr/security/JWTVerificationkeyResolver.java
index f3a9c8c..08e5cb5 100644
--- a/solr/core/src/java/org/apache/solr/security/JWTVerificationkeyResolver.java
+++ b/solr/core/src/java/org/apache/solr/security/JWTVerificationkeyResolver.java
@@ -26,7 +26,7 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-
+import javax.net.ssl.SSLHandshakeException;
 import org.apache.solr.common.SolrException;
 import org.jose4j.jwk.HttpsJwks;
 import org.jose4j.jwk.JsonWebKey;
@@ -42,18 +42,18 @@ import org.jose4j.lang.UnresolvableKeyException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import javax.net.ssl.SSLHandshakeException;
-
 /**
- * Resolves jws signature verification keys from a set of {@link JWTIssuerConfig} objects, which
- * may represent any valid configuration in Solr's security.json, i.e. static list of JWKs
- * or keys retrieved from HTTPs JWK endpoints.
+ * Resolves jws signature verification keys from a set of {@link JWTIssuerConfig} objects, which may
+ * represent any valid configuration in Solr's security.json, i.e. static list of JWKs or keys
+ * retrieved from HTTPs JWK endpoints.
  *
- * This implementation maintains a map of issuers, each with its own list of {@link JsonWebKey},
- * and resolves correct key from correct issuer similar to HttpsJwksVerificationKeyResolver.
- * If issuer claim is not required, we will select the first IssuerConfig if there is exactly one such config.
+ * <p>This implementation maintains a map of issuers, each with its own list of {@link JsonWebKey},
+ * and resolves correct key from correct issuer similar to HttpsJwksVerificationKeyResolver. If
+ * issuer claim is not required, we will select the first IssuerConfig if there is exactly one such
+ * config.
  *
- * If a key is not found, and issuer is backed by HTTPsJWKs, we attempt one cache refresh before failing.
+ * <p>If a key is not found, and issuer is backed by HTTPsJWKs, we attempt one cache refresh before
+ * failing.
  */
 public class JWTVerificationkeyResolver implements VerificationKeyResolver {
   private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
@@ -65,18 +65,22 @@ public class JWTVerificationkeyResolver implements VerificationKeyResolver {
 
   /**
    * Resolves key from a JWKs from one or more IssuerConfigs
+   *
    * @param issuerConfigs Collection of configuration objects for the issuer(s)
    * @param requireIssuer if true, will require 'iss' claim on jws
    */
-  public JWTVerificationkeyResolver(Collection<JWTIssuerConfig> issuerConfigs, boolean requireIssuer) {
+  public JWTVerificationkeyResolver(
+      Collection<JWTIssuerConfig> issuerConfigs, boolean requireIssuer) {
     this.requireIssuer = requireIssuer;
-    issuerConfigs.forEach(ic -> {
-      this.issuerConfigs.put(ic.getIss(), ic);
-    });
+    issuerConfigs.forEach(
+        ic -> {
+          this.issuerConfigs.put(ic.getIss(), ic);
+        });
   }
 
   @Override
-  public Key resolveKey(JsonWebSignature jws, List<JsonWebStructure> nestingContext) throws UnresolvableKeyException {
+  public Key resolveKey(JsonWebSignature jws, List<JsonWebStructure> nestingContext)
+      throws UnresolvableKeyException {
     JsonWebKey theChosenOne;
     List<JsonWebKey> jsonWebKeys = new ArrayList<>();
 
@@ -90,19 +94,23 @@ public class JWTVerificationkeyResolver implements VerificationKeyResolver {
         } else if (issuerConfigs.size() == 1) {
           issuerConfig = issuerConfigs.values().iterator().next();
         } else {
-          throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
+          throw new SolrException(
+              SolrException.ErrorCode.SERVER_ERROR,
               "Signature verifiction not supported for multiple issuers without 'iss' claim in token.");
         }
       } else {
         issuerConfig = issuerConfigs.get(tokenIssuer);
         if (issuerConfig == null) {
           if (issuerConfigs.size() > 1) {
-            throw new UnresolvableKeyException("No issuers configured for iss='" + tokenIssuer + "', cannot validate signature");
+            throw new UnresolvableKeyException(
+                "No issuers configured for iss='" + tokenIssuer + "', cannot validate signature");
           } else if (issuerConfigs.size() == 1) {
             issuerConfig = issuerConfigs.values().iterator().next();
-            log.debug("No issuer matching token's iss claim, but exactly one configured, selecting that one");
+            log.debug(
+                "No issuer matching token's iss claim, but exactly one configured, selecting that one");
           } else {
-            throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
+            throw new SolrException(
+                SolrException.ErrorCode.SERVER_ERROR,
                 "Signature verifiction failed due to no configured issuer with id " + tokenIssuer);
           }
         }
@@ -115,8 +123,12 @@ public class JWTVerificationkeyResolver implements VerificationKeyResolver {
           try {
             jsonWebKeys.addAll(hjwks.getJsonWebKeys());
           } catch (SSLHandshakeException e) {
-            throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
-                "Failed to connect with " + hjwks.getLocation() + ", do you have the correct SSL certificate configured?", e);
+            throw new SolrException(
+                SolrException.ErrorCode.SERVER_ERROR,
+                "Failed to connect with "
+                    + hjwks.getLocation()
+                    + ", do you have the correct SSL certificate configured?",
+                e);
           }
         }
       } else {
@@ -127,8 +139,11 @@ public class JWTVerificationkeyResolver implements VerificationKeyResolver {
       theChosenOne = verificationJwkSelector.select(jws, jsonWebKeys);
       if (theChosenOne == null && issuerConfig.usesHttpsJwk()) {
         if (log.isDebugEnabled()) {
-          log.debug("Refreshing JWKs from all {} locations, as no suitable verification key for JWS w/ header {} was found in {}",
-              issuerConfig.getHttpsJwks().size(), jws.getHeaders().getFullHeaderAsJsonString(), jsonWebKeys);
+          log.debug(
+              "Refreshing JWKs from all {} locations, as no suitable verification key for JWS w/ header {} was found in {}",
+              issuerConfig.getHttpsJwks().size(),
+              jws.getHeaders().getFullHeaderAsJsonString(),
+              jsonWebKeys);
         }
 
         jsonWebKeys.clear();
@@ -140,16 +155,23 @@ public class JWTVerificationkeyResolver implements VerificationKeyResolver {
       }
     } catch (JoseException | IOException | InvalidJwtException | MalformedClaimException e) {
       StringBuilder sb = new StringBuilder();
-      sb.append("Unable to find a suitable verification key for JWS w/ header ").append(jws.getHeaders().getFullHeaderAsJsonString());
-      sb.append(" due to an unexpected exception (").append(e).append(") while obtaining or using keys from source ");
+      sb.append("Unable to find a suitable verification key for JWS w/ header ")
+          .append(jws.getHeaders().getFullHeaderAsJsonString());
+      sb.append(" due to an unexpected exception (")
+          .append(e)
+          .append(") while obtaining or using keys from source ");
       sb.append(keysSource);
       throw new UnresolvableKeyException(sb.toString(), e);
     }
 
     if (theChosenOne == null) {
       StringBuilder sb = new StringBuilder();
-      sb.append("Unable to find a suitable verification key for JWS w/ header ").append(jws.getHeaders().getFullHeaderAsJsonString());
-      sb.append(" from ").append(jsonWebKeys.size()).append(" keys from source ").append(keysSource);
+      sb.append("Unable to find a suitable verification key for JWS w/ header ")
+          .append(jws.getHeaders().getFullHeaderAsJsonString());
+      sb.append(" from ")
+          .append(jsonWebKeys.size())
+          .append(" keys from source ")
+          .append(keysSource);
       throw new UnresolvableKeyException(sb.toString());
     }
 
diff --git a/solr/core/src/test/org/apache/solr/security/JWTAuthPluginIntegrationTest.java b/solr/core/src/test/org/apache/solr/security/JWTAuthPluginIntegrationTest.java
index c7bcb27..bfb0b8e 100644
--- a/solr/core/src/test/org/apache/solr/security/JWTAuthPluginIntegrationTest.java
+++ b/solr/core/src/test/org/apache/solr/security/JWTAuthPluginIntegrationTest.java
@@ -16,6 +16,31 @@
  */
 package org.apache.solr.security;
 
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.security.KeyStore;
+import java.util.Base64;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManagerFactory;
 import no.nav.security.mock.oauth2.MockOAuth2Server;
 import no.nav.security.mock.oauth2.OAuth2Config;
 import no.nav.security.mock.oauth2.http.MockWebServerWrapper;
@@ -52,38 +77,13 @@ import org.junit.AfterClass;
 import org.junit.BeforeClass;
 import org.junit.Test;
 
-import javax.net.ssl.KeyManagerFactory;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLSocketFactory;
-import javax.net.ssl.TrustManagerFactory;
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.security.KeyStore;
-import java.util.Base64;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.TimeUnit;
-import java.util.stream.Collectors;
-
-import static java.nio.charset.StandardCharsets.UTF_8;
-
 /**
  * Validate that JWT token authentication works in a real cluster.
- * <p> 
- * TODO: Test also using SolrJ as client. But that requires a way to set Authorization header on request, see SOLR-13070<br>
- *       This is also the reason we use {@link org.apache.solr.SolrTestCaseJ4.SuppressSSL} annotation, since we use HttpUrlConnection
- * </p>
+ *
+ * <p>TODO: Test also using SolrJ as client. But that requires a way to set Authorization header on
+ * request, see SOLR-13070<br>
+ * This is also the reason we use {@link org.apache.solr.SolrTestCaseJ4.SuppressSSL} annotation,
+ * since we use HttpUrlConnection
  */
 @SolrTestCaseJ4.SuppressSSL
 public class JWTAuthPluginIntegrationTest extends SolrCloudAuthTestCase {
@@ -100,15 +100,18 @@ public class JWTAuthPluginIntegrationTest extends SolrCloudAuthTestCase {
   @BeforeClass
   public static void beforeClass() throws Exception {
     // Setup an OAuth2 mock server with SSL
-    Path p12Cert = SolrTestCaseJ4.TEST_PATH().resolve("security").resolve("jwt_plugin_idp_certs.p12");
+    Path p12Cert =
+        SolrTestCaseJ4.TEST_PATH().resolve("security").resolve("jwt_plugin_idp_certs.p12");
     pemFilePath = SolrTestCaseJ4.TEST_PATH().resolve("security").resolve("jwt_plugin_idp_cert.pem");
-    wrongPemFilePath = SolrTestCaseJ4.TEST_PATH().resolve("security").resolve("jwt_plugin_idp_wrongcert.pem");
+    wrongPemFilePath =
+        SolrTestCaseJ4.TEST_PATH().resolve("security").resolve("jwt_plugin_idp_wrongcert.pem");
 
-    mockOAuth2Server = createMockOAuthServer(p12Cert,"secret");
+    mockOAuth2Server = createMockOAuthServer(p12Cert, "secret");
     mockOAuth2Server.start();
-    mockOAuthToken = mockOAuth2Server.issueToken("default",
-        "myClientId",
-        new DefaultOAuth2TokenCallback()).serialize();
+    mockOAuthToken =
+        mockOAuth2Server
+            .issueToken("default", "myClientId", new DefaultOAuth2TokenCallback())
+            .serialize();
     initStaticJwt();
   }
 
@@ -160,29 +163,35 @@ public class JWTAuthPluginIntegrationTest extends SolrCloudAuthTestCase {
     assertEquals("Should have received 401 code", "401", headers.get("code"));
     assertEquals("Bearer realm=\"my-solr-jwt\"", headers.get("WWW-Authenticate"));
     String authData = new String(Base64.getDecoder().decode(headers.get("X-Solr-AuthData")), UTF_8);
-    assertEquals("{\n" +
-        "  \"scope\":\"solr:admin\",\n" +
-        "  \"redirect_uris\":[],\n" +
-        "  \"authorizationEndpoint\":\"http://acmepaymentscorp/oauth/auz/authorize\",\n" +
-        "  \"client_id\":\"solr-cluster\"}", authData);
+    assertEquals(
+        "{\n"
+            + "  \"scope\":\"solr:admin\",\n"
+            + "  \"redirect_uris\":[],\n"
+            + "  \"authorizationEndpoint\":\"http://acmepaymentscorp/oauth/auz/authorize\",\n"
+            + "  \"client_id\":\"solr-cluster\"}",
+        authData);
     myCluster.shutdown();
   }
 
   @Test
   public void infoRequestValidateXSolrAuthHeadersBlockUnknownFalse() throws Exception {
     // https://issues.apache.org/jira/browse/SOLR-14196
-    MiniSolrCloudCluster myCluster = configureClusterStaticKeys("jwt_plugin_jwk_security_blockUnknownFalse.json");
+    MiniSolrCloudCluster myCluster =
+        configureClusterStaticKeys("jwt_plugin_jwk_security_blockUnknownFalse.json");
     String baseUrl = myCluster.getRandomJetty(random()).getBaseUrl().toString();
 
     Map<String, String> headers = getHeaders(baseUrl + "/admin/info/system", null);
     assertEquals("Should have received 401 code", "401", headers.get("code"));
-    assertEquals("Bearer realm=\"my-solr-jwt-blockunknown-false\"", headers.get("WWW-Authenticate"));
+    assertEquals(
+        "Bearer realm=\"my-solr-jwt-blockunknown-false\"", headers.get("WWW-Authenticate"));
     String authData = new String(Base64.getDecoder().decode(headers.get("X-Solr-AuthData")), UTF_8);
-    assertEquals("{\n" +
-        "  \"scope\":\"solr:admin\",\n" +
-        "  \"redirect_uris\":[],\n" +
-        "  \"authorizationEndpoint\":\"http://acmepaymentscorp/oauth/auz/authorize\",\n" +
-        "  \"client_id\":\"solr-cluster\"}", authData);
+    assertEquals(
+        "{\n"
+            + "  \"scope\":\"solr:admin\",\n"
+            + "  \"redirect_uris\":[],\n"
+            + "  \"authorizationEndpoint\":\"http://acmepaymentscorp/oauth/auz/authorize\",\n"
+            + "  \"client_id\":\"solr-cluster\"}",
+        authData);
     myCluster.shutdown();
   }
 
@@ -193,28 +202,29 @@ public class JWTAuthPluginIntegrationTest extends SolrCloudAuthTestCase {
 
     boolean isUseV2Api = random().nextBoolean();
     String authcPrefix = "/admin/authentication";
-    if(isUseV2Api){
+    if (isUseV2Api) {
       authcPrefix = "/____v2/cluster/security/authentication";
     }
     String baseUrl = cluster.getRandomJetty(random()).getBaseUrl().toString();
     CloseableHttpClient cl = HttpClientUtil.createClient(null);
-    
+
     createCollection(cluster, COLLECTION);
-    
+
     // Missing token
     getAndFail(baseUrl + "/" + COLLECTION + "/query?q=*:*", null);
     assertAuthMetricsMinimums(2, 1, 0, 0, 1, 0);
     executeCommand(baseUrl + authcPrefix, cl, "{set-property : { blockUnknown: false}}", jws);
-    verifySecurityStatus(cl, baseUrl + authcPrefix, "authentication/blockUnknown", "false", 20, jws);
+    verifySecurityStatus(
+        cl, baseUrl + authcPrefix, "authentication/blockUnknown", "false", 20, jws);
     // Pass through
     verifySecurityStatus(cl, baseUrl + "/admin/info/key", "key", NOT_NULL_PREDICATE, 20);
-    // Now succeeds since blockUnknown=false 
+    // Now succeeds since blockUnknown=false
     get(baseUrl + "/" + COLLECTION + "/query?q=*:*", null);
     executeCommand(baseUrl + authcPrefix, cl, "{set-property : { blockUnknown: true}}", null);
     verifySecurityStatus(cl, baseUrl + authcPrefix, "authentication/blockUnknown", "true", 20, jws);
 
     assertAuthMetricsMinimums(9, 4, 4, 0, 1, 0);
-    
+
     // Wrong Credentials
     getAndFail(baseUrl + "/" + COLLECTION + "/query?q=*:*", jwtTokenWrongSignature);
     assertAuthMetricsMinimums(10, 4, 4, 1, 1, 0);
@@ -227,7 +237,11 @@ public class JWTAuthPluginIntegrationTest extends SolrCloudAuthTestCase {
     // Now update three documents
     assertAuthMetricsMinimums(1, 1, 0, 0, 0, 0);
     assertPkiAuthMetricsMinimums(2, 2, 0, 0, 0, 0);
-    Pair<String,Integer> result = post(baseUrl + "/" + COLLECTION + "/update?commit=true", "[{\"id\" : \"1\"}, {\"id\": \"2\"}, {\"id\": \"3\"}]", jwtStaticTestToken);
+    Pair<String, Integer> result =
+        post(
+            baseUrl + "/" + COLLECTION + "/update?commit=true",
+            "[{\"id\" : \"1\"}, {\"id\": \"2\"}, {\"id\": \"3\"}]",
+            jwtStaticTestToken);
     assertEquals(Integer.valueOf(200), result.second());
     assertAuthMetricsMinimums(4, 4, 0, 0, 0, 0);
     assertPkiAuthMetricsMinimums(2, 2, 0, 0, 0, 0);
@@ -243,7 +257,11 @@ public class JWTAuthPluginIntegrationTest extends SolrCloudAuthTestCase {
     assertAuthMetricsMinimums(10, 10, 0, 0, 0, 0);
 
     // Delete
-    assertEquals(200, get(baseUrl + "/admin/collections?action=DELETE&name=" + COLLECTION, jwtStaticTestToken).second().intValue());
+    assertEquals(
+        200,
+        get(baseUrl + "/admin/collections?action=DELETE&name=" + COLLECTION, jwtStaticTestToken)
+            .second()
+            .intValue());
     assertAuthMetricsMinimums(11, 11, 0, 0, 0, 0);
     assertPkiAuthMetricsMinimums(4, 4, 0, 0, 0, 0);
 
@@ -252,25 +270,31 @@ public class JWTAuthPluginIntegrationTest extends SolrCloudAuthTestCase {
 
   /**
    * Configure solr cluster with a security.json talking to MockOAuth2 server
+   *
    * @param numNodes number of nodes in cluster
    * @param pemFilePath path to PEM file for SSL cert to trust for OAuth2 server
    * @param timeoutMs how long to wait until the new security.json is applied to the cluster
    * @return an instance of the created cluster that the test can talk to
    */
   @SuppressWarnings("BusyWait")
-  private MiniSolrCloudCluster configureClusterMockOauth(int numNodes, Path pemFilePath, long timeoutMs) throws Exception {
-    MiniSolrCloudCluster myCluster = configureCluster(numNodes)// nodes
-        .addConfig("conf1", TEST_PATH().resolve("configsets").resolve("cloud-minimal").resolve("conf"))
-        .withDefaultClusterProperty("useLegacyReplicaAssignment", "false")
-        .build();
+  private MiniSolrCloudCluster configureClusterMockOauth(
+      int numNodes, Path pemFilePath, long timeoutMs) throws Exception {
+    MiniSolrCloudCluster myCluster =
+        configureCluster(numNodes) // nodes
+            .addConfig(
+                "conf1", TEST_PATH().resolve("configsets").resolve("cloud-minimal").resolve("conf"))
+            .withDefaultClusterProperty("useLegacyReplicaAssignment", "false")
+            .build();
     String securityJson = createMockOAuthSecurityJson(pemFilePath);
-    myCluster.getZkClient().setData("/security.json", securityJson.getBytes(Charset.defaultCharset()), true);
+    myCluster
+        .getZkClient()
+        .setData("/security.json", securityJson.getBytes(Charset.defaultCharset()), true);
     RTimer timer = new RTimer();
     do { // Wait timeoutMs time for the security.json change to take effect
       Thread.sleep(200);
       if (timer.getTime() > timeoutMs) {
         myCluster.shutdown();
-        throw new Exception("Custom 'security.json' not applied in " + timeoutMs +"ms");
+        throw new Exception("Custom 'security.json' not applied in " + timeoutMs + "ms");
       }
     } while (myCluster.getJettySolrRunner(0).getCoreContainer().getAuthenticationPlugin() == null);
     myCluster.waitForAllNodes(10);
@@ -279,33 +303,36 @@ public class JWTAuthPluginIntegrationTest extends SolrCloudAuthTestCase {
 
   /**
    * Configure solr cluster with a security.json made for static keys
+   *
    * @param securityJsonFilename file name of test json, relative to test-files/solr/security
    * @return an instance of the created cluster that the test can talk to
    */
-  private MiniSolrCloudCluster configureClusterStaticKeys(String securityJsonFilename) throws Exception {
-    MiniSolrCloudCluster myCluster = configureCluster(2)// nodes
-        .withSecurityJson(TEST_PATH().resolve("security").resolve(securityJsonFilename))
-        .addConfig("conf1", TEST_PATH().resolve("configsets").resolve("cloud-minimal").resolve("conf"))
-        .withDefaultClusterProperty("useLegacyReplicaAssignment", "false")
-        .build();
+  private MiniSolrCloudCluster configureClusterStaticKeys(String securityJsonFilename)
+      throws Exception {
+    MiniSolrCloudCluster myCluster =
+        configureCluster(2) // nodes
+            .withSecurityJson(TEST_PATH().resolve("security").resolve(securityJsonFilename))
+            .addConfig(
+                "conf1", TEST_PATH().resolve("configsets").resolve("cloud-minimal").resolve("conf"))
+            .withDefaultClusterProperty("useLegacyReplicaAssignment", "false")
+            .build();
 
     myCluster.waitForAllNodes(10);
     return myCluster;
   }
 
-  /**
-   * Initialize some static JWT keys
-   */
+  /** Initialize some static JWT keys */
   private static void initStaticJwt() throws Exception {
-    String jwkJSON = "{\n" +
-        "  \"kty\": \"RSA\",\n" +
-        "  \"d\": \"i6pyv2z3o-MlYytWsOr3IE1olu2RXZBzjPRBNgWAP1TlLNaphHEvH5aHhe_CtBAastgFFMuP29CFhaL3_tGczkvWJkSveZQN2AHWHgRShKgoSVMspkhOt3Ghha4CvpnZ9BnQzVHnaBnHDTTTfVgXz7P1ZNBhQY4URG61DKIF-JSSClyh1xKuMoJX0lILXDYGGcjVTZL_hci4IXPPTpOJHV51-pxuO7WU5M9252UYoiYyCJ56ai8N49aKIMsqhdGuO4aWUwsGIW4oQpjtce5eEojCprYl-9rDhTwLAFoBtjy6LvkqlR2Ae5dKZYpStljBjK8PJrBvWZjXAEMDdQ8PuQ\",\n" +
-        "  \"e\": \"AQAB\",\n" +
-        "  \"use\": \"sig\",\n" +
-        "  \"kid\": \"test\",\n" +
-        "  \"alg\": \"RS256\",\n" +
-        "  \"n\": \"jeyrvOaZrmKWjyNXt0myAc_pJ1hNt3aRupExJEx1ewPaL9J9HFgSCjMrYxCB1ETO1NDyZ3nSgjZis-jHHDqBxBjRdq_t1E2rkGFaYbxAyKt220Pwgme_SFTB9MXVrFQGkKyjmQeVmOmV6zM3KK8uMdKQJ4aoKmwBcF5Zg7EZdDcKOFgpgva1Jq-FlEsaJ2xrYDYo3KnGcOHIt9_0NQeLsqZbeWYLxYni7uROFncXYV5FhSJCeR4A_rrbwlaCydGxE0ToC_9HNYibUHlkJjqyUhAgORCbNS8JLCJH8NUi5sDdIawK9GTSyvsJXZ-QHqo4cMUuxWV5AJtaRGghuMUfqQ\"\n" +
-        "}";
+    String jwkJSON =
+        "{\n"
+            + "  \"kty\": \"RSA\",\n"
+            + "  \"d\": \"i6pyv2z3o-MlYytWsOr3IE1olu2RXZBzjPRBNgWAP1TlLNaphHEvH5aHhe_CtBAastgFFMuP29CFhaL3_tGczkvWJkSveZQN2AHWHgRShKgoSVMspkhOt3Ghha4CvpnZ9BnQzVHnaBnHDTTTfVgXz7P1ZNBhQY4URG61DKIF-JSSClyh1xKuMoJX0lILXDYGGcjVTZL_hci4IXPPTpOJHV51-pxuO7WU5M9252UYoiYyCJ56ai8N49aKIMsqhdGuO4aWUwsGIW4oQpjtce5eEojCprYl-9rDhTwLAFoBtjy6LvkqlR2Ae5dKZYpStljBjK8PJrBvWZjXAEMDdQ8PuQ\",\n"
+            + "  \"e\": \"AQAB\",\n"
+            + "  \"use\": \"sig\",\n"
+            + "  \"kid\": \"test\",\n"
+            + "  \"alg\": \"RS256\",\n"
+            + "  \"n\": \"jeyrvOaZrmKWjyNXt0myAc_pJ1hNt3aRupExJEx1ewPaL9J9HFgSCjMrYxCB1ETO1NDyZ3nSgjZis-jHHDqBxBjRdq_t1E2rkGFaYbxAyKt220Pwgme_SFTB9MXVrFQGkKyjmQeVmOmV6zM3KK8uMdKQJ4aoKmwBcF5Zg7EZdDcKOFgpgva1Jq-FlEsaJ2xrYDYo3KnGcOHIt9_0NQeLsqZbeWYLxYni7uROFncXYV5FhSJCeR4A_rrbwlaCydGxE0ToC_9HNYibUHlkJjqyUhAgORCbNS8JLCJH8NUi5sDdIawK9GTSyvsJXZ-QHqo4cMUuxWV5AJtaRGghuMUfqQ\"\n"
+            + "}";
 
     PublicJsonWebKey jwk = RsaJsonWebKey.Factory.newPublicJwk(jwkJSON);
     JwtClaims claims = JWTAuthPluginTest.generateClaims();
@@ -331,30 +358,32 @@ public class JWTAuthPluginIntegrationTest extends SolrCloudAuthTestCase {
     try {
       get(url, token);
       fail("Request to " + url + " with token " + token + " should have failed");
-    } catch(Exception e) { /* Fall through */ }
+    } catch (Exception e) {
+      /* Fall through */
+    }
   }
-  
+
   private Pair<String, Integer> get(String url, String token) throws IOException {
     URL createUrl = new URL(url);
     HttpURLConnection createConn = (HttpURLConnection) createUrl.openConnection();
-    if (token != null)
-      createConn.setRequestProperty("Authorization", "Bearer " + token);
-    BufferedReader br2 = new BufferedReader(new InputStreamReader((InputStream) createConn.getContent(), StandardCharsets.UTF_8));
+    if (token != null) createConn.setRequestProperty("Authorization", "Bearer " + token);
+    BufferedReader br2 =
+        new BufferedReader(
+            new InputStreamReader((InputStream) createConn.getContent(), StandardCharsets.UTF_8));
     String result = br2.lines().collect(Collectors.joining("\n"));
     int code = createConn.getResponseCode();
     createConn.disconnect();
     return new Pair<>(result, code);
   }
 
-  private Map<String,String> getHeaders(String url, String token) throws IOException {
+  private Map<String, String> getHeaders(String url, String token) throws IOException {
     URL createUrl = new URL(url);
     HttpURLConnection conn = (HttpURLConnection) createUrl.openConnection();
-    if (token != null)
-      conn.setRequestProperty("Authorization", "Bearer " + token);
+    if (token != null) conn.setRequestProperty("Authorization", "Bearer " + token);
     conn.connect();
     int code = conn.getResponseCode();
     Map<String, String> result = new HashMap<>();
-    conn.getHeaderFields().forEach((k,v) -> result.put(k, v.get(0)));
+    conn.getHeaderFields().forEach((k, v) -> result.put(k, v.get(0)));
     result.put("code", String.valueOf(code));
     conn.disconnect();
     return result;
@@ -365,8 +394,7 @@ public class JWTAuthPluginIntegrationTest extends SolrCloudAuthTestCase {
     HttpURLConnection con = (HttpURLConnection) createUrl.openConnection();
     con.setRequestMethod("POST");
     con.setRequestProperty(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.getMimeType());
-    if (token != null)
-      con.setRequestProperty("Authorization", "Bearer " + token);
+    if (token != null) con.setRequestProperty("Authorization", "Bearer " + token);
 
     con.setDoOutput(true);
     OutputStream os = con.getOutputStream();
@@ -375,73 +403,94 @@ public class JWTAuthPluginIntegrationTest extends SolrCloudAuthTestCase {
     os.close();
 
     con.connect();
-    BufferedReader br2 = new BufferedReader(new InputStreamReader((InputStream) con.getContent(), StandardCharsets.UTF_8));
+    BufferedReader br2 =
+        new BufferedReader(
+            new InputStreamReader((InputStream) con.getContent(), StandardCharsets.UTF_8));
     String result = br2.lines().collect(Collectors.joining("\n"));
     int code = con.getResponseCode();
     con.disconnect();
     return new Pair<>(result, code);
   }
 
-  private void createCollection(MiniSolrCloudCluster myCluster, String collectionName) throws IOException {
+  private void createCollection(MiniSolrCloudCluster myCluster, String collectionName)
+      throws IOException {
     String baseUrl = myCluster.getRandomJetty(random()).getBaseUrl().toString();
-    assertEquals(200, get(baseUrl + "/admin/collections?action=CREATE&name=" + collectionName + "&numShards=2", jwtStaticTestToken).second().intValue());
+    assertEquals(
+        200,
+        get(
+                baseUrl
+                    + "/admin/collections?action=CREATE&name="
+                    + collectionName
+                    + "&numShards=2",
+                jwtStaticTestToken)
+            .second()
+            .intValue());
     myCluster.waitForActiveCollection(collectionName, 2, 2);
   }
 
   private void executeCommand(String url, HttpClient cl, String payload, JsonWebSignature jws)
-    throws Exception {
-    
+      throws Exception {
+
     // HACK: work around for SOLR-13464...
     //
     // note the authz/authn objects in use on each node before executing the command,
     // then wait until we see new objects on every node *after* executing the command
     // before returning...
-    final Set<Map.Entry<String,Object>> initialPlugins
-      = getAuthPluginsInUseForCluster(url).entrySet();
-    
+    final Set<Map.Entry<String, Object>> initialPlugins =
+        getAuthPluginsInUseForCluster(url).entrySet();
+
     HttpPost httpPost;
     HttpResponse r;
     httpPost = new HttpPost(url);
-    if (jws != null)
-      setAuthorizationHeader(httpPost, "Bearer " + jws.getCompactSerialization());
+    if (jws != null) setAuthorizationHeader(httpPost, "Bearer " + jws.getCompactSerialization());
     httpPost.setEntity(new ByteArrayEntity(payload.getBytes(UTF_8)));
     httpPost.addHeader("Content-Type", "application/json; charset=UTF-8");
     r = cl.execute(httpPost);
     String response = IOUtils.toString(r.getEntity().getContent(), StandardCharsets.UTF_8);
-    assertEquals("Non-200 response code. Response was " + response, 200, r.getStatusLine().getStatusCode());
+    assertEquals(
+        "Non-200 response code. Response was " + response, 200, r.getStatusLine().getStatusCode());
     assertFalse("Response contained errors: " + response, response.contains("errorMessages"));
     Utils.consumeFully(r.getEntity());
 
     // HACK (continued)...
     final TimeOut timeout = new TimeOut(30, TimeUnit.SECONDS, TimeSource.NANO_TIME);
-    timeout.waitFor("core containers never fully updated their auth plugins",
-                    () -> {
-                      final Set<Map.Entry<String,Object>> tmpSet
-                        = getAuthPluginsInUseForCluster(url).entrySet();
-                      tmpSet.retainAll(initialPlugins);
-                      return tmpSet.isEmpty();
-                    });
-    
+    timeout.waitFor(
+        "core containers never fully updated their auth plugins",
+        () -> {
+          final Set<Map.Entry<String, Object>> tmpSet =
+              getAuthPluginsInUseForCluster(url).entrySet();
+          tmpSet.retainAll(initialPlugins);
+          return tmpSet.isEmpty();
+        });
   }
 
   /**
-   * Creates a security.json string which points to the MockOAuth server using it's well-known URL and trusting its SSL
+   * Creates a security.json string which points to the MockOAuth server using it's well-known URL
+   * and trusting its SSL
    */
   private static String createMockOAuthSecurityJson(Path pemFilePath) throws IOException {
-    String wellKnown = mockOAuth2Server.wellKnownUrl("default").toString();
+    String wellKnown =
+        mockOAuth2Server
+            .wellKnownUrl("default")
+            .toString();
     String pemCert = CryptoKeys.extractCertificateFromPem(Files.readString(pemFilePath)).replaceAll("\n", "\\\\n");
-    return "{\n" +
-        "  \"authentication\" : {\n" +
-        "    \"class\": \"solr.JWTAuthPlugin\",\n" +
-        "    \"wellKnownUrl\": \"" + wellKnown + "\",\n" +
-        "    \"blockUnknown\": true\n" +
-        "    \"trustedCerts\": \"" + pemCert + "\"\n" +
-        "  }\n" +
-        "}";
+    return "{\n"
+        + "  \"authentication\" : {\n"
+        + "    \"class\": \"solr.JWTAuthPlugin\",\n"
+        + "    \"wellKnownUrl\": \""
+        + wellKnown
+        + "\",\n"
+        + "    \"blockUnknown\": true\n"
+        + "    \"trustedCerts\": \""
+        + pemCert
+        + "\"\n"
+        + "  }\n"
+        + "}";
   }
 
   /**
    * Create and return a MockOAuth2Server with given SSL certificate
+   *
    * @param p12CertPath path to a p12 certificate store
    * @param secretKeyPass password to secret key
    */
@@ -449,20 +498,26 @@ public class JWTAuthPluginIntegrationTest extends SolrCloudAuthTestCase {
     try {
       final KeyStore keystore = KeyStore.getInstance("pkcs12");
       keystore.load(Files.newInputStream(p12CertPath), secretKeyPass.toCharArray());
-      final KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
+      final KeyManagerFactory keyManagerFactory =
+          KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
       keyManagerFactory.init(keystore, secretKeyPass.toCharArray());
-      final TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
-          TrustManagerFactory.getDefaultAlgorithm());
+      final TrustManagerFactory trustManagerFactory =
+          TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
       trustManagerFactory.init(keystore);
 
       MockWebServerWrapper mockWebServerWrapper = new MockWebServerWrapper();
       MockWebServer mockWebServer = mockWebServerWrapper.getMockWebServer();
       SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
-      sslContext.init(keyManagerFactory.getKeyManagers(), /*trustManagerFactory.getTrustManagers()*/ null, null);
+      sslContext.init(
+          keyManagerFactory.getKeyManagers(), /*trustManagerFactory.getTrustManagers()*/
+          null,
+          null);
       SSLSocketFactory sf = sslContext.getSocketFactory();
       mockWebServer.useHttps(sf, false);
 
-      OAuth2Config config = new OAuth2Config(false, new OAuth2TokenProvider(), Collections.emptySet(), mockWebServerWrapper);
+      OAuth2Config config =
+          new OAuth2Config(
+              false, new OAuth2TokenProvider(), Collections.emptySet(), mockWebServerWrapper);
       return new MockOAuth2Server(config);
     } catch (Exception e) {
       throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Failed initializing SSL", e);
diff --git a/solr/core/src/test/org/apache/solr/security/JWTAuthPluginTest.java b/solr/core/src/test/org/apache/solr/security/JWTAuthPluginTest.java
index 3735d0a..2e1c241 100644
--- a/solr/core/src/test/org/apache/solr/security/JWTAuthPluginTest.java
+++ b/solr/core/src/test/org/apache/solr/security/JWTAuthPluginTest.java
@@ -16,22 +16,7 @@
  */
 package org.apache.solr.security;
 
-import org.apache.commons.io.IOUtils;
-import org.apache.solr.SolrTestCaseJ4;
-import org.apache.solr.common.SolrException;
-import org.apache.solr.common.util.Utils;
-import org.apache.solr.util.CryptoKeys;
-import org.jose4j.jwk.RsaJsonWebKey;
-import org.jose4j.jwk.RsaJwkGenerator;
-import org.jose4j.jws.AlgorithmIdentifiers;
-import org.jose4j.jws.JsonWebSignature;
-import org.jose4j.jwt.JwtClaims;
-import org.jose4j.keys.BigEndianBigInteger;
-import org.jose4j.lang.JoseException;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Test;
+import static org.apache.solr.security.JWTAuthPlugin.JWTAuthenticationResponse.AuthCode.*;
 
 import java.io.IOException;
 import java.io.InputStream;
@@ -49,8 +34,22 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-
-import static org.apache.solr.security.JWTAuthPlugin.JWTAuthenticationResponse.AuthCode.*;
+import org.apache.commons.io.IOUtils;
+import org.apache.solr.SolrTestCaseJ4;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.util.Utils;
+import org.apache.solr.util.CryptoKeys;
+import org.jose4j.jwk.RsaJsonWebKey;
+import org.jose4j.jwk.RsaJwkGenerator;
+import org.jose4j.jws.AlgorithmIdentifiers;
+import org.jose4j.jws.JsonWebSignature;
+import org.jose4j.jwt.JwtClaims;
+import org.jose4j.keys.BigEndianBigInteger;
+import org.jose4j.lang.JoseException;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
 
 @SuppressWarnings("unchecked")
 public class JWTAuthPluginTest extends SolrTestCaseJ4 {
@@ -65,20 +64,25 @@ public class JWTAuthPluginTest extends SolrTestCaseJ4 {
   static HashMap<String, Object> testJwk;
 
   static {
-    // Generate an RSA key pair, which will be used for signing and verification of the JWT, wrapped in a JWK
+    // Generate an RSA key pair, which will be used for signing and verification of the JWT, wrapped
+    // in a JWK
     try {
       rsaJsonWebKey = RsaJwkGenerator.generateJwk(2048);
       rsaJsonWebKey.setKeyId("k1");
 
       testJwk = new HashMap<>();
       testJwk.put("kty", rsaJsonWebKey.getKeyType());
-      testJwk.put("e", BigEndianBigInteger.toBase64Url(rsaJsonWebKey.getRsaPublicKey().getPublicExponent()));
+      testJwk.put(
+          "e",
+          BigEndianBigInteger.toBase64Url(rsaJsonWebKey.getRsaPublicKey().getPublicExponent()));
       testJwk.put("use", rsaJsonWebKey.getUse());
       testJwk.put("kid", rsaJsonWebKey.getKeyId());
       testJwk.put("alg", rsaJsonWebKey.getAlgorithm());
-      testJwk.put("n", BigEndianBigInteger.toBase64Url(rsaJsonWebKey.getRsaPublicKey().getModulus()));
+      testJwk.put(
+          "n", BigEndianBigInteger.toBase64Url(rsaJsonWebKey.getRsaPublicKey().getModulus()));
 
-      trustedPemCert = Files.readString(TEST_PATH().resolve("security").resolve("jwt_plugin_idp_cert.pem"));
+      trustedPemCert =
+          Files.readString(TEST_PATH().resolve("security").resolve("jwt_plugin_idp_cert.pem"));
     } catch (JoseException | IOException e) {
       fail("Failed static initialization: " + e.getMessage());
     }
@@ -107,21 +111,26 @@ public class JWTAuthPluginTest extends SolrTestCaseJ4 {
 
   protected static JwtClaims generateClaims() {
     JwtClaims claims = new JwtClaims();
-    claims.setIssuer("IDServer");  // who creates the token and signs it
+    claims.setIssuer("IDServer"); // who creates the token and signs it
     claims.setAudience("Solr"); // to whom the token is intended to be sent
-    claims.setExpirationTimeMinutesInTheFuture(10); // time when the token will expire (10 minutes from now)
+    claims.setExpirationTimeMinutesInTheFuture(
+        10); // time when the token will expire (10 minutes from now)
     claims.setGeneratedJwtId(); // a unique identifier for the token
-    claims.setIssuedAtToNow();  // when the token was issued/created (now)
-    claims.setNotBeforeMinutesInThePast(2); // time before which the token is not yet valid (2 minutes ago)
+    claims.setIssuedAtToNow(); // when the token was issued/created (now)
+    claims.setNotBeforeMinutesInThePast(
+        2); // time before which the token is not yet valid (2 minutes ago)
     claims.setSubject("solruser"); // the subject/principal is whom the token is about
     claims.setStringClaim("scope", "solr:read");
-    claims.setClaim("name", "Solr User"); // additional claims/attributes about the subject can be added
-    claims.setClaim("customPrincipal", "custom"); // additional claims/attributes about the subject can be added
+    claims.setClaim(
+        "name", "Solr User"); // additional claims/attributes about the subject can be added
+    claims.setClaim(
+        "customPrincipal", "custom"); // additional claims/attributes about the subject can be added
     claims.setClaim("claim1", "foo"); // additional claims/attributes about the subject can be added
     claims.setClaim("claim2", "bar"); // additional claims/attributes about the subject can be added
     claims.setClaim("claim3", "foo"); // additional claims/attributes about the subject can be added
     List<String> roles = Arrays.asList("group-one", "other-group", "group-three");
-    claims.setStringListClaim("roles", roles); // multi-valued claims work too and will end up as a JSON array
+    claims.setStringListClaim(
+        "roles", roles); // multi-valued claims work too and will end up as a JSON array
     return claims;
   }
 
@@ -162,7 +171,7 @@ public class JWTAuthPluginTest extends SolrTestCaseJ4 {
   public void initFromSecurityJSONLocalJWK() throws Exception {
     Path securityJson = TEST_PATH().resolve("security").resolve("jwt_plugin_jwk_security.json");
     InputStream is = Files.newInputStream(securityJson);
-    Map<String,Object> securityConf = (Map<String, Object>) Utils.fromJSON(is);
+    Map<String, Object> securityConf = (Map<String, Object>) Utils.fromJSON(is);
     Map<String, Object> authConf = (Map<String, Object>) securityConf.get("authentication");
     plugin.init(authConf);
   }
@@ -171,12 +180,14 @@ public class JWTAuthPluginTest extends SolrTestCaseJ4 {
   public void initFromSecurityJSONUrlJwk() throws Exception {
     Path securityJson = TEST_PATH().resolve("security").resolve("jwt_plugin_jwk_url_security.json");
     InputStream is = Files.newInputStream(securityJson);
-    Map<String,Object> securityConf = (Map<String, Object>) Utils.fromJSON(is);
+    Map<String, Object> securityConf = (Map<String, Object>) Utils.fromJSON(is);
     Map<String, Object> authConf = (Map<String, Object>) securityConf.get("authentication");
     plugin.init(authConf);
 
     JWTAuthPlugin.JWTAuthenticationResponse resp = plugin.authenticate(testHeader);
-    assertEquals(JWTAuthPlugin.JWTAuthenticationResponse.AuthCode.JWT_VALIDATION_EXCEPTION, resp.getAuthCode());
+    assertEquals(
+        JWTAuthPlugin.JWTAuthenticationResponse.AuthCode.JWT_VALIDATION_EXCEPTION,
+        resp.getAuthCode());
     assertTrue(resp.getJwtException().getMessage().contains("Connection refused"));
   }
 
@@ -201,7 +212,9 @@ public class JWTAuthPluginTest extends SolrTestCaseJ4 {
   @Test
   public void initWithJwksUrlArray() {
     HashMap<String, Object> authConf = new HashMap<>();
-    authConf.put("jwksUrl", Arrays.asList("https://127.0.0.1:9999/foo.jwk", "https://127.0.0.1:9999/foo2.jwk"));
+    authConf.put(
+        "jwksUrl",
+        Arrays.asList("https://127.0.0.1:9999/foo.jwk", "https://127.0.0.1:9999/foo2.jwk"));
     authConf.put("iss", "myIssuer");
     plugin = new JWTAuthPlugin();
     plugin.init(authConf);
@@ -213,18 +226,21 @@ public class JWTAuthPluginTest extends SolrTestCaseJ4 {
   public void authenticateOk() {
     JWTAuthPlugin.JWTAuthenticationResponse resp = plugin.authenticate(testHeader);
     assertTrue(resp.isAuthenticated());
-    assertEquals("custom", resp.getPrincipal().getName()); // principalClaim = customPrincipal, not sub here
+    assertEquals(
+        "custom", resp.getPrincipal().getName()); // principalClaim = customPrincipal, not sub here
   }
 
   @Test
   public void authFailedMissingSubject() {
-    minimalConfig.put("principalClaim","sub");  // minimalConfig has no subject specified
+    minimalConfig.put("principalClaim", "sub"); // minimalConfig has no subject specified
     plugin.init(minimalConfig);
     JWTAuthPlugin.JWTAuthenticationResponse resp = plugin.authenticate(testHeader);
     assertFalse(resp.isAuthenticated());
-    assertEquals(JWTAuthPlugin.JWTAuthenticationResponse.AuthCode.JWT_VALIDATION_EXCEPTION, resp.getAuthCode());
+    assertEquals(
+        JWTAuthPlugin.JWTAuthenticationResponse.AuthCode.JWT_VALIDATION_EXCEPTION,
+        resp.getAuthCode());
 
-    testConfig.put("principalClaim","sub");  // testConfig has subject = solruser
+    testConfig.put("principalClaim", "sub"); // testConfig has subject = solruser
     plugin.init(testConfig);
     resp = plugin.authenticate(testHeader);
     assertTrue(resp.isAuthenticated());
@@ -236,7 +252,9 @@ public class JWTAuthPluginTest extends SolrTestCaseJ4 {
     plugin.init(testConfig);
     JWTAuthPlugin.JWTAuthenticationResponse resp = plugin.authenticate(testHeader);
     assertFalse(resp.isAuthenticated());
-    assertEquals(JWTAuthPlugin.JWTAuthenticationResponse.AuthCode.JWT_VALIDATION_EXCEPTION, resp.getAuthCode());
+    assertEquals(
+        JWTAuthPlugin.JWTAuthenticationResponse.AuthCode.JWT_VALIDATION_EXCEPTION,
+        resp.getAuthCode());
 
     testConfig.put("iss", "IDServer");
     plugin.init(testConfig);
@@ -250,7 +268,9 @@ public class JWTAuthPluginTest extends SolrTestCaseJ4 {
     plugin.init(testConfig);
     JWTAuthPlugin.JWTAuthenticationResponse resp = plugin.authenticate(testHeader);
     assertFalse(resp.isAuthenticated());
-    assertEquals(JWTAuthPlugin.JWTAuthenticationResponse.AuthCode.JWT_VALIDATION_EXCEPTION, resp.getAuthCode());
+    assertEquals(
+        JWTAuthPlugin.JWTAuthenticationResponse.AuthCode.JWT_VALIDATION_EXCEPTION,
+        resp.getAuthCode());
 
     testConfig.put("aud", "Solr");
     plugin.init(testConfig);
@@ -269,7 +289,8 @@ public class JWTAuthPluginTest extends SolrTestCaseJ4 {
     plugin.init(testConfig);
     resp = plugin.authenticate(testHeader);
     assertFalse(resp.isAuthenticated());
-    assertEquals(JWTAuthPlugin.JWTAuthenticationResponse.AuthCode.PRINCIPAL_MISSING, resp.getAuthCode());
+    assertEquals(
+        JWTAuthPlugin.JWTAuthenticationResponse.AuthCode.PRINCIPAL_MISSING, resp.getAuthCode());
   }
 
   @Test
@@ -289,13 +310,15 @@ public class JWTAuthPluginTest extends SolrTestCaseJ4 {
     shouldMatch.put("claim9", "NA");
     plugin.init(testConfig);
     resp = plugin.authenticate(testHeader);
-    assertEquals(JWTAuthPlugin.JWTAuthenticationResponse.AuthCode.CLAIM_MISMATCH, resp.getAuthCode());
+    assertEquals(
+        JWTAuthPlugin.JWTAuthenticationResponse.AuthCode.CLAIM_MISMATCH, resp.getAuthCode());
 
     // Required claim does not match regex
     shouldMatch.clear();
     shouldMatch.put("claim1", "NA");
     resp = plugin.authenticate(testHeader);
-    assertEquals(JWTAuthPlugin.JWTAuthenticationResponse.AuthCode.CLAIM_MISMATCH, resp.getAuthCode());
+    assertEquals(
+        JWTAuthPlugin.JWTAuthenticationResponse.AuthCode.CLAIM_MISMATCH, resp.getAuthCode());
   }
 
   @Test
@@ -310,14 +333,18 @@ public class JWTAuthPluginTest extends SolrTestCaseJ4 {
     testConfig.put("requireExp", true);
     plugin.init(testConfig);
     resp = plugin.authenticate(slimHeader);
-    assertEquals(JWTAuthPlugin.JWTAuthenticationResponse.AuthCode.JWT_VALIDATION_EXCEPTION, resp.getAuthCode());
+    assertEquals(
+        JWTAuthPlugin.JWTAuthenticationResponse.AuthCode.JWT_VALIDATION_EXCEPTION,
+        resp.getAuthCode());
     testConfig.put("requireExp", false);
 
     // Missing issuer claim
     testConfig.put("requireIss", true);
     plugin.init(testConfig);
     resp = plugin.authenticate(slimHeader);
-    assertEquals(JWTAuthPlugin.JWTAuthenticationResponse.AuthCode.JWT_VALIDATION_EXCEPTION, resp.getAuthCode());
+    assertEquals(
+        JWTAuthPlugin.JWTAuthenticationResponse.AuthCode.JWT_VALIDATION_EXCEPTION,
+        resp.getAuthCode());
   }
 
   @Test
@@ -325,7 +352,9 @@ public class JWTAuthPluginTest extends SolrTestCaseJ4 {
     testConfig.put("algAllowlist", Arrays.asList("PS384", "PS512"));
     plugin.init(testConfig);
     JWTAuthPlugin.JWTAuthenticationResponse resp = plugin.authenticate(testHeader);
-    assertEquals(JWTAuthPlugin.JWTAuthenticationResponse.AuthCode.JWT_VALIDATION_EXCEPTION, resp.getAuthCode());
+    assertEquals(
+        JWTAuthPlugin.JWTAuthenticationResponse.AuthCode.JWT_VALIDATION_EXCEPTION,
+        resp.getAuthCode());
     assertTrue(resp.getErrorMessage().contains("not a permitted algorithm"));
   }
 
@@ -339,7 +368,7 @@ public class JWTAuthPluginTest extends SolrTestCaseJ4 {
     // When 'rolesClaim' is not defined in config, then all scopes are registered as roles
     Principal principal = resp.getPrincipal();
     assertTrue(principal instanceof VerifiedUserRoles);
-    Set<String> roles = ((VerifiedUserRoles)principal).getVerifiedRoles();
+    Set<String> roles = ((VerifiedUserRoles) principal).getVerifiedRoles();
     assertEquals(1, roles.size());
     assertTrue(roles.contains("solr:read"));
   }
@@ -354,7 +383,7 @@ public class JWTAuthPluginTest extends SolrTestCaseJ4 {
     // When 'rolesClaim' is defined in config, then roles from that claim are used instead of claims
     Principal principal = resp.getPrincipal();
     assertTrue(principal instanceof VerifiedUserRoles);
-    Set<String> roles = ((VerifiedUserRoles)principal).getVerifiedRoles();
+    Set<String> roles = ((VerifiedUserRoles) principal).getVerifiedRoles();
     assertEquals(3, roles.size());
     assertTrue(roles.contains("group-one"));
     assertTrue(roles.contains("other-group"));
@@ -397,7 +426,13 @@ public class JWTAuthPluginTest extends SolrTestCaseJ4 {
 
   @Test
   public void wellKnownConfigNoHeaderPassThrough() {
-    String wellKnownUrl = TEST_PATH().resolve("security").resolve("jwt_well-known-config.json").toAbsolutePath().toUri().toString();
+    String wellKnownUrl =
+        TEST_PATH()
+            .resolve("security")
+            .resolve("jwt_well-known-config.json")
+            .toAbsolutePath()
+            .toUri()
+            .toString();
     testConfig.put("wellKnownUrl", wellKnownUrl);
     testConfig.remove("jwk");
     plugin.init(testConfig);
@@ -407,7 +442,13 @@ public class JWTAuthPluginTest extends SolrTestCaseJ4 {
 
   @Test
   public void defaultRealm() {
-    String wellKnownUrl = TEST_PATH().resolve("security").resolve("jwt_well-known-config.json").toAbsolutePath().toUri().toString();
+    String wellKnownUrl =
+        TEST_PATH()
+            .resolve("security")
+            .resolve("jwt_well-known-config.json")
+            .toAbsolutePath()
+            .toUri()
+            .toString();
     testConfig.put("wellKnownUrl", wellKnownUrl);
     testConfig.remove("jwk");
     plugin.init(testConfig);
@@ -416,7 +457,13 @@ public class JWTAuthPluginTest extends SolrTestCaseJ4 {
 
   @Test
   public void configureRealm() {
-    String wellKnownUrl = TEST_PATH().resolve("security").resolve("jwt_well-known-config.json").toAbsolutePath().toUri().toString();
+    String wellKnownUrl =
+        TEST_PATH()
+            .resolve("security")
+            .resolve("jwt_well-known-config.json")
+            .toAbsolutePath()
+            .toUri()
+            .toString();
     testConfig.put("wellKnownUrl", wellKnownUrl);
     testConfig.remove("jwk");
     testConfig.put("realm", "myRealm");
@@ -437,20 +484,29 @@ public class JWTAuthPluginTest extends SolrTestCaseJ4 {
     testConfig.put("clientId", "solr-cluster");
     plugin.init(testConfig);
     String headerBase64 = plugin.generateAuthDataHeader();
-    String headerJson = new String(Base64.getDecoder().decode(headerBase64), StandardCharsets.UTF_8);
-    Map<String,String> parsed = (Map<String, String>) Utils.fromJSONString(headerJson);
+    String headerJson =
+        new String(Base64.getDecoder().decode(headerBase64), StandardCharsets.UTF_8);
+    Map<String, String> parsed = (Map<String, String>) Utils.fromJSONString(headerJson);
     assertEquals("solr:admin", parsed.get("scope"));
-    assertEquals("http://acmepaymentscorp/oauth/auz/authorize", parsed.get("authorizationEndpoint"));
+    assertEquals(
+        "http://acmepaymentscorp/oauth/auz/authorize", parsed.get("authorizationEndpoint"));
     assertEquals("solr-cluster", parsed.get("client_id"));
   }
 
   @Test
   public void initWithTwoIssuers() {
     HashMap<String, Object> authConf = new HashMap<>();
-    JWTIssuerConfig iss1 = new JWTIssuerConfig("iss1").setIss("1").setAud("aud1")
-        .setJwksUrl("https://127.0.0.1:9999/foo.jwk");
-    JWTIssuerConfig iss2 = new JWTIssuerConfig("iss2").setIss("2").setAud("aud2")
-        .setJwksUrl(Arrays.asList("https://127.0.0.1:9999/foo.jwk", "https://127.0.0.1:9999/foo2.jwk"));
+    JWTIssuerConfig iss1 =
+        new JWTIssuerConfig("iss1")
+            .setIss("1")
+            .setAud("aud1")
+            .setJwksUrl("https://127.0.0.1:9999/foo.jwk");
+    JWTIssuerConfig iss2 =
+        new JWTIssuerConfig("iss2")
+            .setIss("2")
+            .setAud("aud2")
+            .setJwksUrl(
+                Arrays.asList("https://127.0.0.1:9999/foo.jwk", "https://127.0.0.1:9999/foo2.jwk"));
     authConf.put("issuers", Arrays.asList(iss1.asConfig(), iss2.asConfig()));
     plugin = new JWTAuthPlugin();
     plugin.init(authConf);
@@ -469,11 +525,16 @@ public class JWTAuthPluginTest extends SolrTestCaseJ4 {
   @Test
   public void initWithToplevelAndIssuersCombined() {
     HashMap<String, Object> authConf = new HashMap<>();
-    JWTIssuerConfig iss1 = new JWTIssuerConfig("iss1").setIss("1").setAud("aud1")
-        .setJwksUrl("https://127.0.0.1:9999/foo.jwk");
+    JWTIssuerConfig iss1 =
+        new JWTIssuerConfig("iss1")
+            .setIss("1")
+            .setAud("aud1")
+            .setJwksUrl("https://127.0.0.1:9999/foo.jwk");
     authConf.put("issuers", Collections.singletonList(iss1.asConfig()));
     authConf.put("aud", "aud2");
-    authConf.put("jwksUrl", Arrays.asList("https://127.0.0.1:9999/foo.jwk", "https://127.0.0.1:9999/foo2.jwk"));
+    authConf.put(
+        "jwksUrl",
+        Arrays.asList("https://127.0.0.1:9999/foo.jwk", "https://127.0.0.1:9999/foo2.jwk"));
 
     plugin = new JWTAuthPlugin();
     plugin.init(authConf);
@@ -497,7 +558,9 @@ public class JWTAuthPluginTest extends SolrTestCaseJ4 {
   public void initWithTrustedCertsFile() {
     HashMap<String, Object> authConf = new HashMap<>();
     authConf.put("jwksUrl", "https://127.0.0.1:9999/foo.jwk");
-    authConf.put("trustedCertsFile", TEST_PATH().resolve("security").resolve("jwt_plugin_idp_cert.pem").toString());
+    authConf.put(
+        "trustedCertsFile",
+        TEST_PATH().resolve("security").resolve("jwt_plugin_idp_cert.pem").toString());
     plugin = new JWTAuthPlugin();
     plugin.init(authConf);
     assertEquals(2, plugin.getIssuerConfigs().get(0).getTrustedCerts().size());
@@ -516,7 +579,9 @@ public class JWTAuthPluginTest extends SolrTestCaseJ4 {
   public void initWithInvalidTrustedCertsFile() {
     HashMap<String, Object> authConf = new HashMap<>();
     authConf.put("jwksUrl", "https://127.0.0.1:9999/foo.jwk");
-    authConf.put("trustedCertsFile", TEST_PATH().resolve("security").resolve("jwt_plugin_idp_invalidcert.pem").toString());
+    authConf.put(
+        "trustedCertsFile",
+        TEST_PATH().resolve("security").resolve("jwt_plugin_idp_invalidcert.pem").toString());
     plugin = new JWTAuthPlugin();
     expectThrows(SolrException.class, () -> plugin.init(authConf));
   }
@@ -528,9 +593,11 @@ public class JWTAuthPluginTest extends SolrTestCaseJ4 {
     authConf.put("trustedCerts", trustedPemCert);
     authConf.put("trustedCertsFile", "/path/to/cert.pem");
     plugin = new JWTAuthPlugin();
-    expectThrows(SolrException.class, () -> {
-      plugin.init(authConf);
-    });
+    expectThrows(
+        SolrException.class,
+        () -> {
+          plugin.init(authConf);
+        });
   }
 
   @Test
@@ -542,18 +609,23 @@ public class JWTAuthPluginTest extends SolrTestCaseJ4 {
 
   @Test
   public void parseInvalidPemToX509() {
-    expectThrows(SolrException.class, CertificateException.class, () -> {
-      CryptoKeys.parseX509Certs(IOUtils.toInputStream(
-          "-----BEGIN CERTIFICATE-----\n" +
-              "foo\n" +
-              "-----END CERTIFICATE-----\n", StandardCharsets.UTF_8));
-    });
+    expectThrows(
+        SolrException.class,
+        CertificateException.class,
+        () -> {
+          CryptoKeys.parseX509Certs(
+              IOUtils.toInputStream(
+                  "-----BEGIN CERTIFICATE-----\n" + "foo\n" + "-----END CERTIFICATE-----\n",
+                  StandardCharsets.UTF_8));
+        });
   }
 
   @Test
   public void extractCertificate() throws IOException {
-    Path pemFilePath = SolrTestCaseJ4.TEST_PATH().resolve("security").resolve("jwt_plugin_idp_cert.pem");
+    Path pemFilePath =
+        SolrTestCaseJ4.TEST_PATH().resolve("security").resolve("jwt_plugin_idp_cert.pem");
     String cert = CryptoKeys.extractCertificateFromPem(Files.readString(pemFilePath));
-    assertEquals(2, CryptoKeys.parseX509Certs(IOUtils.toInputStream(cert, StandardCharsets.UTF_8)).size());
+    assertEquals(
+        2, CryptoKeys.parseX509Certs(IOUtils.toInputStream(cert, StandardCharsets.UTF_8)).size());
   }
 }
diff --git a/solr/core/src/test/org/apache/solr/security/JWTIssuerConfigTest.java b/solr/core/src/test/org/apache/solr/security/JWTIssuerConfigTest.java
index 9e9a4ca..f15f4e4 100644
--- a/solr/core/src/test/org/apache/solr/security/JWTIssuerConfigTest.java
+++ b/solr/core/src/test/org/apache/solr/security/JWTIssuerConfigTest.java
@@ -29,7 +29,6 @@ import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-
 import org.apache.commons.lang3.StringUtils;
 import org.apache.solr.SolrTestCase;
 import org.apache.solr.common.SolrException;
@@ -47,31 +46,33 @@ public class JWTIssuerConfigTest extends SolrTestCase {
   @Before
   public void setUp() throws Exception {
     super.setUp();
-    testIssuer = new JWTIssuerConfig("name")
-        .setJwksUrl("https://issuer/path")
-        .setIss("issuer")
-        .setAud("audience")
-        .setClientId("clientid")
-        .setWellKnownUrl("wellknown")
-        .setAuthorizationEndpoint("https://issuer/authz");
+    testIssuer =
+        new JWTIssuerConfig("name")
+            .setJwksUrl("https://issuer/path")
+            .setIss("issuer")
+            .setAud("audience")
+            .setClientId("clientid")
+            .setWellKnownUrl("wellknown")
+            .setAuthorizationEndpoint("https://issuer/authz");
 
     testIssuerConfigMap = testIssuer.asConfig();
 
-    testIssuerJson = "{\n" +
-        "  \"aud\":\"audience\",\n" +
-        "  \"wellKnownUrl\":\"wellknown\",\n" +
-        "  \"clientId\":\"clientid\",\n" +
-        "  \"jwksUrl\":[\"https://issuer/path\"],\n" +
-        "  \"name\":\"name\",\n" +
-        "  \"iss\":\"issuer\",\n" +
-        "  \"authorizationEndpoint\":\"https://issuer/authz\"}";
+    testIssuerJson =
+        "{\n"
+            + "  \"aud\":\"audience\",\n"
+            + "  \"wellKnownUrl\":\"wellknown\",\n"
+            + "  \"clientId\":\"clientid\",\n"
+            + "  \"jwksUrl\":[\"https://issuer/path\"],\n"
+            + "  \"name\":\"name\",\n"
+            + "  \"iss\":\"issuer\",\n"
+            + "  \"authorizationEndpoint\":\"https://issuer/authz\"}";
   }
-  
+
   @After
   public void tearDown() throws Exception {
     super.tearDown();
     JWTIssuerConfig.ALLOW_OUTBOUND_HTTP = false;
-  }  
+  }
 
   @Test
   public void parseConfigMap() {
@@ -134,28 +135,31 @@ public class JWTIssuerConfigTest extends SolrTestCase {
 
   @Test
   public void jwksUrlwithHttpBehaviors() {
-    
+
     HashMap<String, Object> issuerConfigMap = new HashMap<>();
     issuerConfigMap.put("name", "myName");
     issuerConfigMap.put("iss", "myIss");
     issuerConfigMap.put("jwksUrl", "http://host/jwk");
 
     JWTIssuerConfig issuerConfig = new JWTIssuerConfig(issuerConfigMap);
-    
+
     SolrException e = expectThrows(SolrException.class, () -> issuerConfig.getHttpsJwks());
     assertEquals(400, e.code());
-    assertEquals("jwksUrl is using http protocol. HTTPS required for IDP communication. Please use SSL or start your nodes with -Dsolr.auth.jwt.allowOutboundHttp=true to allow HTTP for test purposes.", e.getMessage());
-    
+    assertEquals(
+        "jwksUrl is using http protocol. HTTPS required for IDP communication. Please use SSL or start your nodes with -Dsolr.auth.jwt.allowOutboundHttp=true to allow HTTP for test purposes.",
+        e.getMessage());
+
     JWTIssuerConfig.ALLOW_OUTBOUND_HTTP = true;
 
     assertEquals(1, issuerConfig.getHttpsJwks().size());
-    assertEquals("http://host/jwk", issuerConfig.getHttpsJwks().get(0).getLocation());    
+    assertEquals("http://host/jwk", issuerConfig.getHttpsJwks().get(0).getLocation());
   }
-  
+
   @Test
   public void wellKnownConfigFromInputstream() throws IOException {
     Path configJson = TEST_PATH().resolve("security").resolve("jwt_well-known-config.json");
-    JWTIssuerConfig.WellKnownDiscoveryConfig config = JWTIssuerConfig.WellKnownDiscoveryConfig.parse(Files.newInputStream(configJson));
+    JWTIssuerConfig.WellKnownDiscoveryConfig config =
+        JWTIssuerConfig.WellKnownDiscoveryConfig.parse(Files.newInputStream(configJson));
     assertEquals("https://acmepaymentscorp/oauth/jwks", config.getJwksUrl());
   }
 
@@ -163,32 +167,67 @@ public class JWTIssuerConfigTest extends SolrTestCase {
   public void wellKnownConfigFromString() throws IOException {
     Path configJson = TEST_PATH().resolve("security").resolve("jwt_well-known-config.json");
     String configString = StringUtils.join(Files.readAllLines(configJson), "\n");
-    JWTIssuerConfig.WellKnownDiscoveryConfig config = JWTIssuerConfig.WellKnownDiscoveryConfig.parse(configString, StandardCharsets.UTF_8);
+    JWTIssuerConfig.WellKnownDiscoveryConfig config =
+        JWTIssuerConfig.WellKnownDiscoveryConfig.parse(configString, StandardCharsets.UTF_8);
     assertEquals("https://acmepaymentscorp/oauth/jwks", config.getJwksUrl());
     assertEquals("http://acmepaymentscorp", config.getIssuer());
     assertEquals("http://acmepaymentscorp/oauth/auz/authorize", config.getAuthorizationEndpoint());
-    assertEquals(Arrays.asList("READ", "WRITE", "DELETE", "openid", "scope", "profile", "email", "address", "phone"), config.getScopesSupported());
-    assertEquals(Arrays.asList("code", "code id_token", "code token", "code id_token token", "token", "id_token", "id_token token"), config.getResponseTypesSupported());
+    assertEquals(
+        Arrays.asList(
+            "READ", "WRITE", "DELETE", "openid", "scope", "profile", "email", "address", "phone"),
+        config.getScopesSupported());
+    assertEquals(
+        Arrays.asList(
+            "code",
+            "code id_token",
+            "code token",
+            "code id_token token",
+            "token",
+            "id_token",
+            "id_token token"),
+        config.getResponseTypesSupported());
   }
 
   @Test
   public void wellKnownConfigWithHttpBehaviors() {
-    SolrException e = expectThrows(SolrException.class, () -> JWTIssuerConfig.WellKnownDiscoveryConfig.parse("http://127.0.0.1:45678/.well-known/config"));
+    SolrException e =
+        expectThrows(
+            SolrException.class,
+            () ->
+                JWTIssuerConfig.WellKnownDiscoveryConfig.parse(
+                    "http://127.0.0.1:45678/.well-known/config"));
     assertEquals(400, e.code());
-    assertEquals("wellKnownUrl is using http protocol. HTTPS required for IDP communication. Please use SSL or start your nodes with -Dsolr.auth.jwt.allowOutboundHttp=true to allow HTTP for test purposes.", e.getMessage());
-    
+    assertEquals(
+        "wellKnownUrl is using http protocol. HTTPS required for IDP communication. Please use SSL or start your nodes with -Dsolr.auth.jwt.allowOutboundHttp=true to allow HTTP for test purposes.",
+        e.getMessage());
+
     JWTIssuerConfig.ALLOW_OUTBOUND_HTTP = true;
-    
-    e = expectThrows(SolrException.class, () -> JWTIssuerConfig.WellKnownDiscoveryConfig.parse("http://127.0.0.1:45678/.well-known/config"));
+
+    e =
+        expectThrows(
+            SolrException.class,
+            () ->
+                JWTIssuerConfig.WellKnownDiscoveryConfig.parse(
+                    "http://127.0.0.1:45678/.well-known/config"));
     assertEquals(500, e.code());
-    // We open a connection in the code path to a server that doesn't exist, which causes this.  Should really be mocked.
-    assertEquals("Well-known config could not be read from url http://127.0.0.1:45678/.well-known/config", e.getMessage());
+    // We open a connection in the code path to a server that doesn't exist, which causes this.
+    // Should really be mocked.
+    assertEquals(
+        "Well-known config could not be read from url http://127.0.0.1:45678/.well-known/config",
+        e.getMessage());
   }
-  
+
   @Test
   public void wellKnownConfigNotReachable() {
-    SolrException e = expectThrows(SolrException.class, () -> JWTIssuerConfig.WellKnownDiscoveryConfig.parse("https://127.0.0.1:45678/.well-known/config"));
+    SolrException e =
+        expectThrows(
+            SolrException.class,
+            () ->
+                JWTIssuerConfig.WellKnownDiscoveryConfig.parse(
+                    "https://127.0.0.1:45678/.well-known/config"));
     assertEquals(500, e.code());
-    assertEquals("Well-known config could not be read from url https://127.0.0.1:45678/.well-known/config", e.getMessage());
+    assertEquals(
+        "Well-known config could not be read from url https://127.0.0.1:45678/.well-known/config",
+        e.getMessage());
   }
-}
\ No newline at end of file
+}
diff --git a/solr/core/src/test/org/apache/solr/security/JWTVerificationkeyResolverTest.java b/solr/core/src/test/org/apache/solr/security/JWTVerificationkeyResolverTest.java
index 8ee5bcd..c24b9d8 100644
--- a/solr/core/src/test/org/apache/solr/security/JWTVerificationkeyResolverTest.java
+++ b/solr/core/src/test/org/apache/solr/security/JWTVerificationkeyResolverTest.java
@@ -17,10 +17,14 @@
 
 package org.apache.solr.security;
 
+import static java.util.Arrays.asList;
+import static org.mockito.ArgumentMatchers.anyList;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.when;
+
 import java.util.Arrays;
 import java.util.Iterator;
 import java.util.List;
-
 import org.apache.solr.SolrTestCaseJ4;
 import org.apache.solr.security.JWTIssuerConfig.HttpsJwksFactory;
 import org.jose4j.jwk.HttpsJwks;
@@ -38,26 +42,15 @@ import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 
-import static java.util.Arrays.asList;
-import static org.mockito.ArgumentMatchers.anyList;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.when;
-
-/**
- * Tests the multi jwks resolver that can fetch keys from multiple JWKs
- */
+/** Tests the multi jwks resolver that can fetch keys from multiple JWKs */
 public class JWTVerificationkeyResolverTest extends SolrTestCaseJ4 {
   private JWTVerificationkeyResolver resolver;
 
-  @Rule
-  public MockitoRule mockitoRule = MockitoJUnit.rule();
+  @Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
 
-  @Mock
-  private HttpsJwks firstJwkList;
-  @Mock
-  private HttpsJwks secondJwkList;
-  @Mock
-  private HttpsJwksFactory httpsJwksFactory;
+  @Mock private HttpsJwks firstJwkList;
+  @Mock private HttpsJwks secondJwkList;
+  @Mock private HttpsJwksFactory httpsJwksFactory;
 
   private KeyHolder k1;
   private KeyHolder k2;
@@ -77,19 +70,25 @@ public class JWTVerificationkeyResolverTest extends SolrTestCaseJ4 {
     k5 = new KeyHolder("k5");
 
     when(firstJwkList.getJsonWebKeys()).thenReturn(asList(k1.getJwk(), k2.getJwk()));
-    doAnswer(invocation -> {
-      keysToReturnFromSecondJwk = refreshSequenceForSecondJwk.next();
-      System.out.println("Refresh called, next to return is " + keysToReturnFromSecondJwk);
-      return null;
-    }).when(secondJwkList).refresh();
-    when(secondJwkList.getJsonWebKeys()).then(inv -> {
-      if (keysToReturnFromSecondJwk == null)
-        keysToReturnFromSecondJwk = refreshSequenceForSecondJwk.next();
-      return keysToReturnFromSecondJwk;
-    });
+    doAnswer(
+            invocation -> {
+              keysToReturnFromSecondJwk = refreshSequenceForSecondJwk.next();
+              System.out.println("Refresh called, next to return is " + keysToReturnFromSecondJwk);
+              return null;
+            })
+        .when(secondJwkList)
+        .refresh();
+    when(secondJwkList.getJsonWebKeys())
+        .then(
+            inv -> {
+              if (keysToReturnFromSecondJwk == null)
+                keysToReturnFromSecondJwk = refreshSequenceForSecondJwk.next();
+              return keysToReturnFromSecondJwk;
+            });
     when(httpsJwksFactory.createList(anyList())).thenReturn(asList(firstJwkList, secondJwkList));
 
-    JWTIssuerConfig issuerConfig = new JWTIssuerConfig("primary").setIss("foo").setJwksUrl(asList("url1", "url2"));
+    JWTIssuerConfig issuerConfig =
+        new JWTIssuerConfig("primary").setIss("foo").setJwksUrl(asList("url1", "url2"));
     JWTIssuerConfig.setHttpsJwksFactory(httpsJwksFactory);
     resolver = new JWTVerificationkeyResolver(Arrays.asList(issuerConfig), true);
 
@@ -98,9 +97,8 @@ public class JWTVerificationkeyResolverTest extends SolrTestCaseJ4 {
 
   @Test
   public void findKeyFromFirstList() throws JoseException {
-    refreshSequenceForSecondJwk = asList(
-        asList(k3.getJwk(), k4.getJwk()),
-        asList(k5.getJwk())).iterator();
+    refreshSequenceForSecondJwk =
+        asList(asList(k3.getJwk(), k4.getJwk()), asList(k5.getJwk())).iterator();
     resolver.resolveKey(k1.getJws(), null);
     resolver.resolveKey(k2.getJws(), null);
     resolver.resolveKey(k3.getJws(), null);
@@ -111,10 +109,8 @@ public class JWTVerificationkeyResolverTest extends SolrTestCaseJ4 {
 
   @Test(expected = UnresolvableKeyException.class)
   public void notFoundKey() throws JoseException {
-    refreshSequenceForSecondJwk = asList(
-        asList(k3.getJwk()),
-        asList(k4.getJwk()),
-        asList(k5.getJwk())).iterator();
+    refreshSequenceForSecondJwk =
+        asList(asList(k3.getJwk()), asList(k4.getJwk()), asList(k5.getJwk())).iterator();
     // Will not find key since first refresh returns k4, and we only try one refresh.
     resolver.resolveKey(k5.getJws(), null);
   }
@@ -153,4 +149,4 @@ public class JWTVerificationkeyResolverTest extends SolrTestCaseJ4 {
       return rsaJsonWebKey;
     }
   }
-}
\ No newline at end of file
+}

[solr] 04/04: SOLR-15907 Spotless

Posted by ja...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

janhoy pushed a commit to branch branch_9x
in repository https://gitbox.apache.org/repos/asf/solr.git

commit 406350aab7fa3623f33da096fbcf0dcdc79d8c83
Author: Jan Høydahl <ja...@users.noreply.github.com>
AuthorDate: Wed Feb 9 02:17:03 2022 +0100

    SOLR-15907 Spotless
---
 .../apache/solr/security/jwt/JWTAuthPluginIntegrationTest.java   | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/solr/modules/jwt-auth/src/test/org/apache/solr/security/jwt/JWTAuthPluginIntegrationTest.java b/solr/modules/jwt-auth/src/test/org/apache/solr/security/jwt/JWTAuthPluginIntegrationTest.java
index e3b1620..7832046 100644
--- a/solr/modules/jwt-auth/src/test/org/apache/solr/security/jwt/JWTAuthPluginIntegrationTest.java
+++ b/solr/modules/jwt-auth/src/test/org/apache/solr/security/jwt/JWTAuthPluginIntegrationTest.java
@@ -482,11 +482,10 @@ public class JWTAuthPluginIntegrationTest extends SolrCloudAuthTestCase {
    * and trusting its SSL
    */
   private static String createMockOAuthSecurityJson(Path pemFilePath) throws IOException {
-    String wellKnown =
-        mockOAuth2Server
-            .wellKnownUrl("default")
-            .toString();
-    String pemCert = CryptoKeys.extractCertificateFromPem(Files.readString(pemFilePath)).replaceAll("\n", "\\\\n");
+    String wellKnown = mockOAuth2Server.wellKnownUrl("default").toString();
+    String pemCert =
+        CryptoKeys.extractCertificateFromPem(Files.readString(pemFilePath))
+            .replaceAll("\n", "\\\\n");
     return "{\n"
         + "  \"authentication\" : {\n"
         + "    \"class\": \"solr.JWTAuthPlugin\",\n"