diff --git a/.config/pmd/java/ruleset.xml b/.config/pmd/java/ruleset.xml index ebdbd83a..267fa5e9 100644 --- a/.config/pmd/java/ruleset.xml +++ b/.config/pmd/java/ruleset.xml @@ -42,6 +42,7 @@ + @@ -138,6 +139,7 @@ + @@ -155,7 +157,7 @@ - + diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index df608bf3..6dcc9fbd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -87,7 +87,7 @@ jobs: - name: Create Release id: create-release - uses: shogo82148/actions-create-release@4661dc54f7b4b564074e9fbf73884d960de569a3 # v1 + uses: shogo82148/actions-create-release@7b89596097b26731bda0852f1504f813499079ee # v1 with: tag_name: v${{ steps.version.outputs.release }} release_name: v${{ steps.version.outputs.release }} diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index 6a6b8b2c..c0bcafe9 100644 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -1,17 +1,3 @@ -# 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. +wrapperVersion=3.3.4 +distributionType=only-script distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.11/apache-maven-3.9.11-bin.zip diff --git a/CHANGELOG.md b/CHANGELOG.md index f619fc78..f532b0da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,18 @@ +# 1.3.0 +* Actuator + * Added support for custom password hashers + * The default built-in password-hasher is still using `SHA-256` + * `ActuatorUserConfig#passwordSha256` was renamed to `passwordHash` + * `passwordSha256` is deprecated and will be removed in a future release + * Now utilizes password hash caching if possible + * The cache defaults to a maximum size of 100 and a cached duration of 1h + * Enabled when one of the following libraries is detected on the class-path: + * [caffeine](https://github.com/ben-manes/caffeine) + * [expiring-limited-cache](https://github.com/xdev-software/expiring-limited-cache) + * Can be disabled with `sse.sidecar.actuator.password-hash.cache.enabled` if required + * See source code for details configuration options +* Updated dependencies + # 1.2.2 * Minor code cleanup * Updated dependencies diff --git a/README.md b/README.md index 5f7d6d74..e37226cc 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ Please note that more detailed descriptions are available in the individual modu * [web-sidecar-actuator](./web-sidecar-actuator/) * Secures Spring Boot's Actuator * Multi-User support - * Allows securing different endpoint per user + * Allows securing different endpoints per user * Only password hashes are stored on the server side * [web-sidecar-common](./web-sidecar-common/) * Host static resources without creating sessions diff --git a/bom/pom.xml b/bom/pom.xml index 84de2599..8d3f4a96 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -6,7 +6,7 @@ software.xdev.sse bom - 1.2.3-SNAPSHOT + 1.3.0-SNAPSHOT pom bom @@ -51,62 +51,62 @@ software.xdev.sse client-storage - 1.2.3-SNAPSHOT + 1.3.0-SNAPSHOT software.xdev.sse crypto-symmetric - 1.2.3-SNAPSHOT + 1.3.0-SNAPSHOT software.xdev.sse crypto-symmetric-managed - 1.2.3-SNAPSHOT + 1.3.0-SNAPSHOT software.xdev.sse codec-sha256 - 1.2.3-SNAPSHOT + 1.3.0-SNAPSHOT software.xdev.sse csp - 1.2.3-SNAPSHOT + 1.3.0-SNAPSHOT software.xdev.sse metrics - 1.2.3-SNAPSHOT + 1.3.0-SNAPSHOT software.xdev.sse oauth2-oidc - 1.2.3-SNAPSHOT + 1.3.0-SNAPSHOT software.xdev.sse oauth2-oidc-remember-me - 1.2.3-SNAPSHOT + 1.3.0-SNAPSHOT software.xdev.sse vaadin - 1.2.3-SNAPSHOT + 1.3.0-SNAPSHOT software.xdev.sse web - 1.2.3-SNAPSHOT + 1.3.0-SNAPSHOT software.xdev.sse web-sidecar-actuator - 1.2.3-SNAPSHOT + 1.3.0-SNAPSHOT software.xdev.sse web-sidecar-common - 1.2.3-SNAPSHOT + 1.3.0-SNAPSHOT diff --git a/client-storage/pom.xml b/client-storage/pom.xml index 8994f1b0..dedad276 100644 --- a/client-storage/pom.xml +++ b/client-storage/pom.xml @@ -6,7 +6,7 @@ software.xdev.sse client-storage - 1.2.3-SNAPSHOT + 1.3.0-SNAPSHOT jar client-storage @@ -261,12 +261,12 @@ net.sourceforge.pmd pmd-core - 7.16.0 + 7.17.0 net.sourceforge.pmd pmd-java - 7.16.0 + 7.17.0 diff --git a/codec-sha256/pom.xml b/codec-sha256/pom.xml index 9d4b4883..85ef6a63 100644 --- a/codec-sha256/pom.xml +++ b/codec-sha256/pom.xml @@ -6,7 +6,7 @@ software.xdev.sse codec-sha256 - 1.2.3-SNAPSHOT + 1.3.0-SNAPSHOT jar codec-sha256 @@ -150,7 +150,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.5.3 + 3.5.4 @@ -268,12 +268,12 @@ net.sourceforge.pmd pmd-core - 7.16.0 + 7.17.0 net.sourceforge.pmd pmd-java - 7.16.0 + 7.17.0 diff --git a/crypto-symmetric-managed/pom.xml b/crypto-symmetric-managed/pom.xml index 1525489c..2087a8c7 100644 --- a/crypto-symmetric-managed/pom.xml +++ b/crypto-symmetric-managed/pom.xml @@ -6,7 +6,7 @@ software.xdev.sse crypto-symmetric-managed - 1.2.3-SNAPSHOT + 1.3.0-SNAPSHOT jar crypto-symmetric-managed @@ -54,7 +54,7 @@ org.springframework.boot spring-boot-dependencies - 3.5.5 + 3.5.6 pom import @@ -287,12 +287,12 @@ net.sourceforge.pmd pmd-core - 7.16.0 + 7.17.0 net.sourceforge.pmd pmd-java - 7.16.0 + 7.17.0 diff --git a/crypto-symmetric/pom.xml b/crypto-symmetric/pom.xml index 10f38db7..00447e61 100644 --- a/crypto-symmetric/pom.xml +++ b/crypto-symmetric/pom.xml @@ -6,7 +6,7 @@ software.xdev.sse crypto-symmetric - 1.2.3-SNAPSHOT + 1.3.0-SNAPSHOT jar crypto-symmetric @@ -150,7 +150,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.5.3 + 3.5.4 @@ -268,12 +268,12 @@ net.sourceforge.pmd pmd-core - 7.16.0 + 7.17.0 net.sourceforge.pmd pmd-java - 7.16.0 + 7.17.0 diff --git a/csp/pom.xml b/csp/pom.xml index e2dbaf4a..32f24823 100644 --- a/csp/pom.xml +++ b/csp/pom.xml @@ -6,7 +6,7 @@ software.xdev.sse csp - 1.2.3-SNAPSHOT + 1.3.0-SNAPSHOT jar csp @@ -54,7 +54,7 @@ org.springframework.boot spring-boot-dependencies - 3.5.5 + 3.5.6 pom import @@ -277,12 +277,12 @@ net.sourceforge.pmd pmd-core - 7.16.0 + 7.17.0 net.sourceforge.pmd pmd-java - 7.16.0 + 7.17.0 diff --git a/demo/entities-metamodel/pom.xml b/demo/entities-metamodel/pom.xml index 17c2d423..9ad05ef7 100644 --- a/demo/entities-metamodel/pom.xml +++ b/demo/entities-metamodel/pom.xml @@ -7,7 +7,7 @@ software.xdev.sse.demo demo - 1.2.3-SNAPSHOT + 1.3.0-SNAPSHOT entities-metamodel diff --git a/demo/entities/pom.xml b/demo/entities/pom.xml index 25dbd499..3624e068 100644 --- a/demo/entities/pom.xml +++ b/demo/entities/pom.xml @@ -7,7 +7,7 @@ software.xdev.sse.demo demo - 1.2.3-SNAPSHOT + 1.3.0-SNAPSHOT entities diff --git a/demo/integration-tests/pom.xml b/demo/integration-tests/pom.xml index b479d9a2..cf5b35be 100644 --- a/demo/integration-tests/pom.xml +++ b/demo/integration-tests/pom.xml @@ -7,12 +7,12 @@ software.xdev.sse.demo demo - 1.2.3-SNAPSHOT + 1.3.0-SNAPSHOT software.xdev.sse.demo.it integration-tests - 1.2.3-SNAPSHOT + 1.3.0-SNAPSHOT pom @@ -31,31 +31,31 @@ software.xdev.sse.demo.it tci-db - 1.2.3-SNAPSHOT + 1.3.0-SNAPSHOT software.xdev.sse.demo.it tci-webapp - 1.2.3-SNAPSHOT + 1.3.0-SNAPSHOT software.xdev.sse.demo.it tci-webapp-rest - 1.2.3-SNAPSHOT + 1.3.0-SNAPSHOT software.xdev.sse.demo.it tci-webapp-vaadin - 1.2.3-SNAPSHOT + 1.3.0-SNAPSHOT software.xdev.sse.demo.it webapp-it-base - 1.2.3-SNAPSHOT + 1.3.0-SNAPSHOT @@ -99,7 +99,7 @@ software.xdev.tci bom - 2.6.0 + 2.7.0 pom import diff --git a/demo/integration-tests/tci-db/pom.xml b/demo/integration-tests/tci-db/pom.xml index 9ccf7c34..d5e2ec44 100644 --- a/demo/integration-tests/tci-db/pom.xml +++ b/demo/integration-tests/tci-db/pom.xml @@ -7,7 +7,7 @@ software.xdev.sse.demo.it integration-tests - 1.2.3-SNAPSHOT + 1.3.0-SNAPSHOT tci-db diff --git a/demo/integration-tests/tci-webapp-rest/pom.xml b/demo/integration-tests/tci-webapp-rest/pom.xml index 2c7bd89f..5d84b136 100644 --- a/demo/integration-tests/tci-webapp-rest/pom.xml +++ b/demo/integration-tests/tci-webapp-rest/pom.xml @@ -7,7 +7,7 @@ software.xdev.sse.demo.it integration-tests - 1.2.3-SNAPSHOT + 1.3.0-SNAPSHOT tci-webapp-rest diff --git a/demo/integration-tests/tci-webapp-vaadin/pom.xml b/demo/integration-tests/tci-webapp-vaadin/pom.xml index d321328d..d24da608 100644 --- a/demo/integration-tests/tci-webapp-vaadin/pom.xml +++ b/demo/integration-tests/tci-webapp-vaadin/pom.xml @@ -7,7 +7,7 @@ software.xdev.sse.demo.it integration-tests - 1.2.3-SNAPSHOT + 1.3.0-SNAPSHOT tci-webapp-vaadin diff --git a/demo/integration-tests/tci-webapp/pom.xml b/demo/integration-tests/tci-webapp/pom.xml index 8501c8f1..e2f0d7af 100644 --- a/demo/integration-tests/tci-webapp/pom.xml +++ b/demo/integration-tests/tci-webapp/pom.xml @@ -7,7 +7,7 @@ software.xdev.sse.demo.it integration-tests - 1.2.3-SNAPSHOT + 1.3.0-SNAPSHOT tci-webapp diff --git a/demo/integration-tests/tci-webapp/src/main/java/software/xdev/sse/demo/tci/webapp/containers/WebAppContainer.java b/demo/integration-tests/tci-webapp/src/main/java/software/xdev/sse/demo/tci/webapp/containers/WebAppContainer.java index 7f204535..0579aac8 100644 --- a/demo/integration-tests/tci-webapp/src/main/java/software/xdev/sse/demo/tci/webapp/containers/WebAppContainer.java +++ b/demo/integration-tests/tci-webapp/src/main/java/software/xdev/sse/demo/tci/webapp/containers/WebAppContainer.java @@ -67,7 +67,7 @@ public SELF withAuth( public SELF withActuator(final String username, final String hash) { return this.withEnv("SSE_ACTUATOR_USERS_0_USERNAME", username) - .withEnv("SSE_ACTUATOR_USERS_0_PASSWORD-SHA-256", hash); + .withEnv("SSE_ACTUATOR_USERS_0_PASSWORD-HASH", hash); } public SELF withDisableHTTPS() diff --git a/demo/integration-tests/webapp-it-base/pom.xml b/demo/integration-tests/webapp-it-base/pom.xml index 16319d5b..2c3de8e1 100644 --- a/demo/integration-tests/webapp-it-base/pom.xml +++ b/demo/integration-tests/webapp-it-base/pom.xml @@ -7,7 +7,7 @@ software.xdev.sse.demo.it integration-tests - 1.2.3-SNAPSHOT + 1.3.0-SNAPSHOT webapp-it-base diff --git a/demo/integration-tests/webapp-it-base/src/main/java/software/xdev/sse/demo/webapp/base/AbstractBaseTest.java b/demo/integration-tests/webapp-it-base/src/main/java/software/xdev/sse/demo/webapp/base/AbstractBaseTest.java index 2d7ce823..759a1258 100644 --- a/demo/integration-tests/webapp-it-base/src/main/java/software/xdev/sse/demo/webapp/base/AbstractBaseTest.java +++ b/demo/integration-tests/webapp-it-base/src/main/java/software/xdev/sse/demo/webapp/base/AbstractBaseTest.java @@ -24,6 +24,7 @@ import software.xdev.sse.demo.tci.db.factory.DBTCIFactory; import software.xdev.sse.demo.tci.webapp.WebAppTCI; import software.xdev.tci.TCI; +import software.xdev.tci.concurrent.TCIExecutorServiceHolder; import software.xdev.tci.factory.prestart.PreStartableTCIFactory; import software.xdev.tci.factory.registry.TCIFactoryRegistry; import software.xdev.tci.network.LazyNetworkPool; @@ -100,10 +101,12 @@ protected void startBaseInfrastructure(final Consumer onDataBaseMigrated) this.network = LAZY_NETWORK_POOL.getNew(); cfOIDC = CompletableFuture.supplyAsync( - () -> OIDC_INFRA_FACTORY.getNew(this.network, DNS_NAME_OIDC)); + () -> OIDC_INFRA_FACTORY.getNew(this.network, DNS_NAME_OIDC), + TCIExecutorServiceHolder.instance()); cfApp = CompletableFuture.supplyAsync( - () -> this.appInfraFactory.getNew(this.network, DNS_NAME_WEBAPP)); + () -> this.appInfraFactory.getNew(this.network, DNS_NAME_WEBAPP), + TCIExecutorServiceHolder.instance()); this.dbInfra = DB_INFRA_FACTORY.getNew(this.network, DNS_NAME_DB); Optional.ofNullable(onDataBaseMigrated).ifPresent(c -> c.accept(this.dbInfra)); @@ -199,7 +202,7 @@ protected void stopWebDriver() { LOG.warn("Failed to stop WebDriver(async)", ex); } - }); + }, TCIExecutorServiceHolder.instance()); this.remoteWebDriver = null; this.browserInfra = null; @@ -223,7 +226,7 @@ protected void stopEverything() Stream.of(appInfra, oidcInfra, dbInfra) .filter(Objects::nonNull) .map(tci -> tci::stop)) - .map(CompletableFuture::runAsync) + .map(r -> CompletableFuture.runAsync(r, TCIExecutorServiceHolder.instance())) .toList() // collect so everything is getting executed async .forEach(CompletableFuture::join); @@ -233,7 +236,7 @@ protected void stopEverything() { LOG.error("Failed to stop everything(async)", ex); } - }); + }, TCIExecutorServiceHolder.instance()); this.appInfra = null; this.oidcInfra = null; diff --git a/demo/integration-tests/webapp-rest-it/pom.xml b/demo/integration-tests/webapp-rest-it/pom.xml index 8101e02e..faf831c2 100644 --- a/demo/integration-tests/webapp-rest-it/pom.xml +++ b/demo/integration-tests/webapp-rest-it/pom.xml @@ -7,7 +7,7 @@ software.xdev.sse.demo.it integration-tests - 1.2.3-SNAPSHOT + 1.3.0-SNAPSHOT webapp-rest-it diff --git a/demo/integration-tests/webapp-rest-it/src/test/java/software/xdev/sse/demo/rest/cases/LoginOtherTest.java b/demo/integration-tests/webapp-rest-it/src/test/java/software/xdev/sse/demo/rest/cases/LoginOtherTest.java index 33ed4d61..3238653c 100644 --- a/demo/integration-tests/webapp-rest-it/src/test/java/software/xdev/sse/demo/rest/cases/LoginOtherTest.java +++ b/demo/integration-tests/webapp-rest-it/src/test/java/software/xdev/sse/demo/rest/cases/LoginOtherTest.java @@ -124,7 +124,9 @@ static Stream checkNoSessionCreatedForActuator() ALL_SUPPORTED_HTTP_METHODS.stream() .map(method -> Arguments.of(false, false, method, HttpStatus.SC_UNAUTHORIZED)), // TRACE is not supported by Spring Boot - Stream.of(Arguments.of(false, false, HttpTrace.METHOD_NAME, HttpStatus.SC_BAD_REQUEST)) + Stream.of(false, true) + .map(existingPath -> + Arguments.of(false, existingPath, HttpTrace.METHOD_NAME, HttpStatus.SC_METHOD_NOT_ALLOWED)) ).flatMap(Function.identity()); } diff --git a/demo/integration-tests/webapp-vaadin-it/pom.xml b/demo/integration-tests/webapp-vaadin-it/pom.xml index 002d5ac3..a6d6c441 100644 --- a/demo/integration-tests/webapp-vaadin-it/pom.xml +++ b/demo/integration-tests/webapp-vaadin-it/pom.xml @@ -7,7 +7,7 @@ software.xdev.sse.demo.it integration-tests - 1.2.3-SNAPSHOT + 1.3.0-SNAPSHOT webapp-vaadin-it diff --git a/demo/persistence/pom.xml b/demo/persistence/pom.xml index a7a6838c..6b0ba266 100644 --- a/demo/persistence/pom.xml +++ b/demo/persistence/pom.xml @@ -7,7 +7,7 @@ software.xdev.sse.demo demo - 1.2.3-SNAPSHOT + 1.3.0-SNAPSHOT persistence diff --git a/demo/pom.xml b/demo/pom.xml index c4b74b5e..7bdd799b 100644 --- a/demo/pom.xml +++ b/demo/pom.xml @@ -6,7 +6,7 @@ software.xdev.sse.demo demo - 1.2.3-SNAPSHOT + 1.3.0-SNAPSHOT pom @@ -24,7 +24,7 @@ 24.8.8 - 3.5.5 + 3.5.6 @@ -43,25 +43,25 @@ software.xdev.sse.demo entities - 1.2.3-SNAPSHOT + 1.3.0-SNAPSHOT software.xdev.sse.demo entities-metamodel - 1.2.3-SNAPSHOT + 1.3.0-SNAPSHOT software.xdev.sse.demo persistence - 1.2.3-SNAPSHOT + 1.3.0-SNAPSHOT software.xdev.sse.demo webapp-shared - 1.2.3-SNAPSHOT + 1.3.0-SNAPSHOT @@ -91,6 +91,18 @@ 3.0.1 + + + software.xdev + expiring-limited-cache + 2.2.3 + + + com.github.ben-manes.caffeine + caffeine + 3.2.2 + + @@ -105,7 +117,7 @@ org.mariadb.jdbc mariadb-java-client - 3.5.5 + 3.5.6 @@ -137,27 +149,27 @@ software.xdev.sse csp - 1.2.3-SNAPSHOT + 1.3.0-SNAPSHOT software.xdev.sse oauth2-oidc - 1.2.3-SNAPSHOT + 1.3.0-SNAPSHOT software.xdev.sse oauth2-oidc-remember-me - 1.2.3-SNAPSHOT + 1.3.0-SNAPSHOT software.xdev.sse vaadin - 1.2.3-SNAPSHOT + 1.3.0-SNAPSHOT software.xdev.sse web-sidecar-actuator - 1.2.3-SNAPSHOT + 1.3.0-SNAPSHOT @@ -183,7 +195,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.5.3 + 3.5.4 @@ -289,12 +301,12 @@ net.sourceforge.pmd pmd-core - 7.16.0 + 7.17.0 net.sourceforge.pmd pmd-java - 7.16.0 + 7.17.0 diff --git a/demo/webapp-rest/pom.xml b/demo/webapp-rest/pom.xml index acfb23a5..02f67b5d 100644 --- a/demo/webapp-rest/pom.xml +++ b/demo/webapp-rest/pom.xml @@ -7,7 +7,7 @@ software.xdev.sse.demo demo - 1.2.3-SNAPSHOT + 1.3.0-SNAPSHOT webapp-rest diff --git a/demo/webapp-rest/src/main/java/software/xdev/sse/demo/Application.java b/demo/webapp-rest/src/main/java/software/xdev/sse/demo/Application.java index 6e8e5b66..c98f3042 100644 --- a/demo/webapp-rest/src/main/java/software/xdev/sse/demo/Application.java +++ b/demo/webapp-rest/src/main/java/software/xdev/sse/demo/Application.java @@ -5,11 +5,14 @@ import java.util.stream.Stream; import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration; @SuppressWarnings({"checkstyle:HideUtilityClassConstructor", "PMD.UseUtilityClass"}) @SpringBootApplication +@EnableAutoConfiguration(exclude = {ErrorMvcAutoConfiguration.class}) public class Application { @SuppressWarnings("PMD.AvoidSystemSetterCall") diff --git a/demo/webapp-rest/src/main/resources-dev/application-add.yml b/demo/webapp-rest/src/main/resources-dev/application-add.yml index d8b132e8..c546ec2c 100644 --- a/demo/webapp-rest/src/main/resources-dev/application-add.yml +++ b/demo/webapp-rest/src/main/resources-dev/application-add.yml @@ -3,9 +3,9 @@ sse: users: # username = password - username: actuator - password-sha-256: 425edd11c26ae24d6726f66925c024ad7978400bd4ebb10bc943854ab93b3778 + password-hash: 425edd11c26ae24d6726f66925c024ad7978400bd4ebb10bc943854ab93b3778 - username: prometheus - password-sha-256: 1809f7cd0c75acf34f56d8c19782b99c6b5fcd14128a3cc79aca38a4f94af3ff + password-hash: 1809f7cd0c75acf34f56d8c19782b99c6b5fcd14128a3cc79aca38a4f94af3ff allowed-endpoints: - prometheus diff --git a/demo/webapp-shared/pom.xml b/demo/webapp-shared/pom.xml index a803c193..6cca051b 100644 --- a/demo/webapp-shared/pom.xml +++ b/demo/webapp-shared/pom.xml @@ -7,7 +7,7 @@ software.xdev.sse.demo demo - 1.2.3-SNAPSHOT + 1.3.0-SNAPSHOT webapp-shared @@ -75,6 +75,16 @@ spring-security-advanced-authentication-ui + + + software.xdev + expiring-limited-cache + + + com.github.ben-manes.caffeine + caffeine + + software.xdev.sse diff --git a/demo/webapp-vaadin/pom.xml b/demo/webapp-vaadin/pom.xml index 3567593d..2876c0bc 100644 --- a/demo/webapp-vaadin/pom.xml +++ b/demo/webapp-vaadin/pom.xml @@ -7,7 +7,7 @@ software.xdev.sse.demo demo - 1.2.3-SNAPSHOT + 1.3.0-SNAPSHOT webapp-vaadin diff --git a/demo/webapp-vaadin/src/main/resources-dev/application-add.yml b/demo/webapp-vaadin/src/main/resources-dev/application-add.yml index 5b2e2621..f75abeca 100644 --- a/demo/webapp-vaadin/src/main/resources-dev/application-add.yml +++ b/demo/webapp-vaadin/src/main/resources-dev/application-add.yml @@ -14,9 +14,9 @@ sse: users: # username = password - username: actuator - password-sha-256: 425edd11c26ae24d6726f66925c024ad7978400bd4ebb10bc943854ab93b3778 + password-hash: 425edd11c26ae24d6726f66925c024ad7978400bd4ebb10bc943854ab93b3778 - username: prometheus - password-sha-256: 1809f7cd0c75acf34f56d8c19782b99c6b5fcd14128a3cc79aca38a4f94af3ff + password-hash: 1809f7cd0c75acf34f56d8c19782b99c6b5fcd14128a3cc79aca38a4f94af3ff allowed-endpoints: - prometheus diff --git a/metrics/pom.xml b/metrics/pom.xml index 0d8afc75..e9262e27 100644 --- a/metrics/pom.xml +++ b/metrics/pom.xml @@ -6,7 +6,7 @@ software.xdev.sse metrics - 1.2.3-SNAPSHOT + 1.3.0-SNAPSHOT jar metrics @@ -54,7 +54,7 @@ org.springframework.boot spring-boot-dependencies - 3.5.5 + 3.5.6 pom import @@ -272,12 +272,12 @@ net.sourceforge.pmd pmd-core - 7.16.0 + 7.17.0 net.sourceforge.pmd pmd-java - 7.16.0 + 7.17.0 diff --git a/mvnw b/mvnw index 08303327..bd8896bf 100755 --- a/mvnw +++ b/mvnw @@ -19,7 +19,7 @@ # ---------------------------------------------------------------------------- # ---------------------------------------------------------------------------- -# Apache Maven Wrapper startup batch script, version 3.3.0 +# Apache Maven Wrapper startup batch script, version 3.3.4 # # Optional ENV vars # ----------------- @@ -97,14 +97,25 @@ die() { exit 1 } +trim() { + # MWRAPPER-139: + # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds. + # Needed for removing poorly interpreted newline sequences when running in more + # exotic environments such as mingw bash on Windows. + printf "%s" "${1}" | tr -d '[:space:]' +} + +scriptDir="$(dirname "$0")" +scriptName="$(basename "$0")" + # parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties while IFS="=" read -r key value; do case "${key-}" in - distributionUrl) distributionUrl="${value-}" ;; - distributionSha256Sum) distributionSha256Sum="${value-}" ;; + distributionUrl) distributionUrl=$(trim "${value-}") ;; + distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;; esac -done <"${0%/*}/.mvn/wrapper/maven-wrapper.properties" -[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in ${0%/*}/.mvn/wrapper/maven-wrapper.properties" +done <"$scriptDir/.mvn/wrapper/maven-wrapper.properties" +[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" case "${distributionUrl##*/}" in maven-mvnd-*bin.*) @@ -122,7 +133,7 @@ maven-mvnd-*bin.*) distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip" ;; maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;; -*) MVN_CMD="mvn${0##*/mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;; +*) MVN_CMD="mvn${scriptName#mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;; esac # apply MVNW_REPOURL and calculate MAVEN_HOME @@ -131,7 +142,8 @@ esac distributionUrlName="${distributionUrl##*/}" distributionUrlNameMain="${distributionUrlName%.*}" distributionUrlNameMain="${distributionUrlNameMain%-bin}" -MAVEN_HOME="$HOME/.m2/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")" +MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}" +MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")" exec_maven() { unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || : @@ -199,7 +211,7 @@ elif set_java_home; then public static void main( String[] args ) throws Exception { setDefault( new Downloader() ); - java.nio.file.Files.copy( new java.net.URL( args[0] ).openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() ); + java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() ); } } END @@ -218,7 +230,7 @@ if [ -n "${distributionSha256Sum-}" ]; then echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 exit 1 elif command -v sha256sum >/dev/null; then - if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c >/dev/null 2>&1; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c - >/dev/null 2>&1; then distributionSha256Result=true fi elif command -v shasum >/dev/null; then @@ -243,8 +255,41 @@ if command -v unzip >/dev/null; then else tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar" fi -printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url" -mv -- "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME" + +# Find the actual extracted directory name (handles snapshots where filename != directory name) +actualDistributionDir="" + +# First try the expected directory name (for regular distributions) +if [ -d "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" ]; then + if [ -f "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/bin/$MVN_CMD" ]; then + actualDistributionDir="$distributionUrlNameMain" + fi +fi + +# If not found, search for any directory with the Maven executable (for snapshots) +if [ -z "$actualDistributionDir" ]; then + # enable globbing to iterate over items + set +f + for dir in "$TMP_DOWNLOAD_DIR"/*; do + if [ -d "$dir" ]; then + if [ -f "$dir/bin/$MVN_CMD" ]; then + actualDistributionDir="$(basename "$dir")" + break + fi + fi + done + set -f +fi + +if [ -z "$actualDistributionDir" ]; then + verbose "Contents of $TMP_DOWNLOAD_DIR:" + verbose "$(ls -la "$TMP_DOWNLOAD_DIR")" + die "Could not find Maven distribution directory in extracted archive" +fi + +verbose "Found extracted Maven distribution directory: $actualDistributionDir" +printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$actualDistributionDir/mvnw.url" +mv -- "$TMP_DOWNLOAD_DIR/$actualDistributionDir" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME" clean || : exec_maven "$@" diff --git a/mvnw.cmd b/mvnw.cmd index 136e686a..92450f93 100644 --- a/mvnw.cmd +++ b/mvnw.cmd @@ -19,7 +19,7 @@ @REM ---------------------------------------------------------------------------- @REM ---------------------------------------------------------------------------- -@REM Apache Maven Wrapper startup batch script, version 3.3.0 +@REM Apache Maven Wrapper startup batch script, version 3.3.4 @REM @REM Optional ENV vars @REM MVNW_REPOURL - repo url base for downloading maven distribution @@ -40,7 +40,7 @@ @SET __MVNW_ARG0_NAME__= @SET MVNW_USERNAME= @SET MVNW_PASSWORD= -@IF NOT "%__MVNW_CMD__%"=="" (%__MVNW_CMD__% %*) +@IF NOT "%__MVNW_CMD__%"=="" ("%__MVNW_CMD__%" %*) @echo Cannot start maven from wrapper >&2 && exit /b 1 @GOTO :EOF : end batch / begin powershell #> @@ -73,13 +73,30 @@ switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) { # apply MVNW_REPOURL and calculate MAVEN_HOME # maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ if ($env:MVNW_REPOURL) { - $MVNW_REPO_PATTERN = if ($USE_MVND) { "/org/apache/maven/" } else { "/maven/mvnd/" } - $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')" + $MVNW_REPO_PATTERN = if ($USE_MVND -eq $False) { "/org/apache/maven/" } else { "/maven/mvnd/" } + $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace "^.*$MVNW_REPO_PATTERN",'')" } $distributionUrlName = $distributionUrl -replace '^.*/','' $distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$','' -$MAVEN_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain" -$MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join '' + +$MAVEN_M2_PATH = "$HOME/.m2" +if ($env:MAVEN_USER_HOME) { + $MAVEN_M2_PATH = "$env:MAVEN_USER_HOME" +} + +if (-not (Test-Path -Path $MAVEN_M2_PATH)) { + New-Item -Path $MAVEN_M2_PATH -ItemType Directory | Out-Null +} + +$MAVEN_WRAPPER_DISTS = $null +if ((Get-Item $MAVEN_M2_PATH).Target[0] -eq $null) { + $MAVEN_WRAPPER_DISTS = "$MAVEN_M2_PATH/wrapper/dists" +} else { + $MAVEN_WRAPPER_DISTS = (Get-Item $MAVEN_M2_PATH).Target[0] + "/wrapper/dists" +} + +$MAVEN_HOME_PARENT = "$MAVEN_WRAPPER_DISTS/$distributionUrlNameMain" +$MAVEN_HOME_NAME = ([System.Security.Cryptography.SHA256]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join '' $MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME" if (Test-Path -Path "$MAVEN_HOME" -PathType Container) { @@ -131,7 +148,33 @@ if ($distributionSha256Sum) { # unzip and move Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null -Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName $MAVEN_HOME_NAME | Out-Null + +# Find the actual extracted directory name (handles snapshots where filename != directory name) +$actualDistributionDir = "" + +# First try the expected directory name (for regular distributions) +$expectedPath = Join-Path "$TMP_DOWNLOAD_DIR" "$distributionUrlNameMain" +$expectedMvnPath = Join-Path "$expectedPath" "bin/$MVN_CMD" +if ((Test-Path -Path $expectedPath -PathType Container) -and (Test-Path -Path $expectedMvnPath -PathType Leaf)) { + $actualDistributionDir = $distributionUrlNameMain +} + +# If not found, search for any directory with the Maven executable (for snapshots) +if (!$actualDistributionDir) { + Get-ChildItem -Path "$TMP_DOWNLOAD_DIR" -Directory | ForEach-Object { + $testPath = Join-Path $_.FullName "bin/$MVN_CMD" + if (Test-Path -Path $testPath -PathType Leaf) { + $actualDistributionDir = $_.Name + } + } +} + +if (!$actualDistributionDir) { + Write-Error "Could not find Maven distribution directory in extracted archive" +} + +Write-Verbose "Found extracted Maven distribution directory: $actualDistributionDir" +Rename-Item -Path "$TMP_DOWNLOAD_DIR/$actualDistributionDir" -NewName $MAVEN_HOME_NAME | Out-Null try { Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null } catch { diff --git a/oauth2-oidc-remember-me/README.md b/oauth2-oidc-remember-me/README.md index 7cb6d43a..015dc5c2 100644 --- a/oauth2-oidc-remember-me/README.md +++ b/oauth2-oidc-remember-me/README.md @@ -37,7 +37,7 @@ Spring Boot provides [Remember-Me authentication](https://docs.spring.io/spring- This is a lot better than using serializing the session, as it doesn't requires lots of serialization and data-storage. -However are still some problem with that: +However there are still some problem with that: * You need to revalidate if the client is still allowed to login * This means communicating with the OIDC server, to get relevant information * If the OIDC server is down you have a problem because you can't get the required data diff --git a/oauth2-oidc-remember-me/pom.xml b/oauth2-oidc-remember-me/pom.xml index 6d7b9977..38ab4ced 100644 --- a/oauth2-oidc-remember-me/pom.xml +++ b/oauth2-oidc-remember-me/pom.xml @@ -6,7 +6,7 @@ software.xdev.sse oauth2-oidc-remember-me - 1.2.3-SNAPSHOT + 1.3.0-SNAPSHOT jar oauth2-oidc-remember-me @@ -54,7 +54,7 @@ org.springframework.boot spring-boot-dependencies - 3.5.5 + 3.5.6 pom import @@ -184,7 +184,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.5.3 + 3.5.4 @@ -302,12 +302,12 @@ net.sourceforge.pmd pmd-core - 7.16.0 + 7.17.0 net.sourceforge.pmd pmd-java - 7.16.0 + 7.17.0 diff --git a/oauth2-oidc/pom.xml b/oauth2-oidc/pom.xml index da2d4200..95b98b13 100644 --- a/oauth2-oidc/pom.xml +++ b/oauth2-oidc/pom.xml @@ -6,7 +6,7 @@ software.xdev.sse oauth2-oidc - 1.2.3-SNAPSHOT + 1.3.0-SNAPSHOT jar oauth2-oidc @@ -54,7 +54,7 @@ org.springframework.boot spring-boot-dependencies - 3.5.5 + 3.5.6 pom import @@ -203,7 +203,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.5.3 + 3.5.4 @@ -321,12 +321,12 @@ net.sourceforge.pmd pmd-core - 7.16.0 + 7.17.0 net.sourceforge.pmd pmd-java - 7.16.0 + 7.17.0 diff --git a/pom.xml b/pom.xml index 004d37e3..950cf007 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ software.xdev.sse root - 1.2.3-SNAPSHOT + 1.3.0-SNAPSHOT pom @@ -129,12 +129,12 @@ net.sourceforge.pmd pmd-core - 7.16.0 + 7.17.0 net.sourceforge.pmd pmd-java - 7.16.0 + 7.17.0 diff --git a/vaadin/pom.xml b/vaadin/pom.xml index 98a490bc..8f18733d 100644 --- a/vaadin/pom.xml +++ b/vaadin/pom.xml @@ -6,7 +6,7 @@ software.xdev.sse vaadin - 1.2.3-SNAPSHOT + 1.3.0-SNAPSHOT jar vaadin @@ -54,7 +54,7 @@ org.springframework.boot spring-boot-dependencies - 3.5.5 + 3.5.6 pom import @@ -318,12 +318,12 @@ net.sourceforge.pmd pmd-core - 7.16.0 + 7.17.0 net.sourceforge.pmd pmd-java - 7.16.0 + 7.17.0 diff --git a/web-sidecar-actuator/README.md b/web-sidecar-actuator/README.md index 2880e93f..37d152bf 100644 --- a/web-sidecar-actuator/README.md +++ b/web-sidecar-actuator/README.md @@ -8,6 +8,11 @@ For more details about sidecars please have a look at [``../web-sidecar-common`` * Allows for multiple users * Allows only specific endpoints per user * Only the password hashes are stored on the server side +* The default built-in password-hasher is using `SHA-256` +* Utilizes password hash caching if possible + * Enabled when one of the following libraries is detected on the class-path: + * [caffeine](https://github.com/ben-manes/caffeine) + * [expiring-limited-cache](https://github.com/xdev-software/expiring-limited-cache) Example configuration: ```yml @@ -15,10 +20,11 @@ sse: actuator: users: # username = password + # Hash is using SHA-256 - username: actuator - password-sha-256: 425edd11c26ae24d6726f66925c024ad7978400bd4ebb10bc943854ab93b3778 + password-hash: 425edd11c26ae24d6726f66925c024ad7978400bd4ebb10bc943854ab93b3778 - username: prometheus - password-sha-256: 1809f7cd0c75acf34f56d8c19782b99c6b5fcd14128a3cc79aca38a4f94af3ff + password-hash: 1809f7cd0c75acf34f56d8c19782b99c6b5fcd14128a3cc79aca38a4f94af3ff allowed-endpoints: - prometheus ``` diff --git a/web-sidecar-actuator/pom.xml b/web-sidecar-actuator/pom.xml index 4452330a..5eba7eb9 100644 --- a/web-sidecar-actuator/pom.xml +++ b/web-sidecar-actuator/pom.xml @@ -6,7 +6,7 @@ software.xdev.sse web-sidecar-actuator - 1.2.3-SNAPSHOT + 1.3.0-SNAPSHOT jar web-sidecar-actuator @@ -54,7 +54,7 @@ org.springframework.boot spring-boot-dependencies - 3.5.5 + 3.5.6 pom import @@ -87,6 +87,20 @@ spring-boot-starter-actuator + + + software.xdev + expiring-limited-cache + 2.2.3 + provided + + + com.github.ben-manes.caffeine + caffeine + 3.2.2 + provided + + org.junit.jupiter junit-jupiter @@ -187,7 +201,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.5.3 + 3.5.4 @@ -305,12 +319,12 @@ net.sourceforge.pmd pmd-core - 7.16.0 + 7.17.0 net.sourceforge.pmd pmd-java - 7.16.0 + 7.17.0 diff --git a/web-sidecar-actuator/src/main/java/software/xdev/sse/web/sidecar/actuator/ActuatorWebSecurity.java b/web-sidecar-actuator/src/main/java/software/xdev/sse/web/sidecar/actuator/ActuatorWebSecurity.java index 0486d8e0..8f62fb24 100644 --- a/web-sidecar-actuator/src/main/java/software/xdev/sse/web/sidecar/actuator/ActuatorWebSecurity.java +++ b/web-sidecar-actuator/src/main/java/software/xdev/sse/web/sidecar/actuator/ActuatorWebSecurity.java @@ -42,9 +42,10 @@ import org.springframework.security.provisioning.InMemoryUserDetailsManager; import org.springframework.security.web.SecurityFilterChain; -import software.xdev.sse.codec.hash.SHA256Hashing; import software.xdev.sse.web.sidecar.actuator.config.ActuatorSecurityConfig; import software.xdev.sse.web.sidecar.actuator.metrics.ActuatorSecurityMetricsHandler; +import software.xdev.sse.web.sidecar.actuator.passwordhash.cache.PasswordHashCache; +import software.xdev.sse.web.sidecar.actuator.passwordhash.hasher.PasswordHasher; @ConditionalOnProperty(value = "sse.sidecar.actuator.enabled", matchIfMissing = true) @@ -55,13 +56,23 @@ public class ActuatorWebSecurity private static final Logger LOG = LoggerFactory.getLogger(ActuatorWebSecurity.class); protected final ActuatorSecurityConfig config; + protected final PasswordHasher passwordHasher; + protected final PasswordHashCache passwordHashCache; protected final List metricsHandlers; public ActuatorWebSecurity( final ActuatorSecurityConfig config, + final List allAvailablePasswordHashers, + final PasswordHashCache passwordHashCache, final List metricsHandlers) { this.config = config; + this.passwordHasher = allAvailablePasswordHashers.stream() + .filter(h -> config.getPasswordHasherId().equals(h.id())) + .findFirst() + .orElseThrow(() -> + new IllegalStateException("Failed to find PasswordHasher for " + config.getPasswordHasherId())); + this.passwordHashCache = passwordHashCache; this.metricsHandlers = metricsHandlers.stream() .filter(ActuatorSecurityMetricsHandler::enabled) .toList(); @@ -74,7 +85,15 @@ public SecurityFilterChain configureActuator( final WebEndpointProperties actuatorWebEndpointProperties, final HttpSecurity http) throws Exception { - LOG.info("Building SecurityFilterChain with {}", this.config); + LOG.info( + "Building SecurityFilterChain with {} [passwordHasher={},passwordHashCache={},metricHandlers={}]", + this.config, + this.passwordHasher.getClass().getSimpleName(), + this.passwordHashCache.getClass().getSimpleName(), + this.metricsHandlers.stream() + .map(ActuatorSecurityMetricsHandler::getClass) + .map(Class::getSimpleName) + .toList()); final Set alUserEndpoints = this.config.getUsers() .stream() @@ -112,7 +131,7 @@ protected AuthenticationProvider createActuatorAuthProvider() .stream() .map(au -> User.builder() .username(au.getUsername()) - .password(au.getPasswordSha256()) + .password(au.getPasswordHash()) .roles((au.getAllowedEndpoints().isEmpty() ? Stream.of(this.config.getDefaultRoleName()) : au.getAllowedEndpoints().stream().map(this::endpointToRole)) @@ -123,31 +142,24 @@ protected AuthenticationProvider createActuatorAuthProvider() new InMemoryUserDetailsManager(users)); final int passwordMaxLength = this.config.getPasswordMaxLength(); - daoAuthenticationProvider.setPasswordEncoder(new PasswordEncoder() - { - @Override - public boolean matches(final CharSequence rawPassword, final String encodedPassword) - { + daoAuthenticationProvider.setPasswordEncoder((MatchingOnlyPasswordEncoder) + (rawPassword, encodedPassword) -> { if(rawPassword == null || rawPassword.isEmpty() || rawPassword.length() > passwordMaxLength) { - ActuatorWebSecurity.this.metrics(ActuatorSecurityMetricsHandler::loginFailed); + this.metrics(ActuatorSecurityMetricsHandler::loginFailed); return false; } - final boolean success = SHA256Hashing.hash(rawPassword.toString()).equals(encodedPassword); - ActuatorWebSecurity.this.metrics(success + final boolean success = this.passwordHashCache.computeIfAbsent( + rawPassword.toString(), + this.passwordHasher::hash) + .equals(encodedPassword); + this.metrics(success ? ActuatorSecurityMetricsHandler::loginSuccess : ActuatorSecurityMetricsHandler::loginFailed); return success; - } - - @Override - public String encode(final CharSequence rawPassword) - { - return rawPassword.toString(); - } - }); + }); return daoAuthenticationProvider; } @@ -156,4 +168,17 @@ protected void metrics(final Consumer consumer) { this.metricsHandlers.forEach(consumer); } + + @FunctionalInterface + protected interface MatchingOnlyPasswordEncoder extends PasswordEncoder + { + @Override + boolean matches(CharSequence rawPassword, String encodedPassword); + + @Override + default String encode(final CharSequence rawPassword) + { + return rawPassword.toString(); + } + } } diff --git a/web-sidecar-actuator/src/main/java/software/xdev/sse/web/sidecar/actuator/auto/ActuatorWebSecurityAutoConfig.java b/web-sidecar-actuator/src/main/java/software/xdev/sse/web/sidecar/actuator/auto/ActuatorWebSecurityAutoConfig.java index 2507ee69..09ac7432 100644 --- a/web-sidecar-actuator/src/main/java/software/xdev/sse/web/sidecar/actuator/auto/ActuatorWebSecurityAutoConfig.java +++ b/web-sidecar-actuator/src/main/java/software/xdev/sse/web/sidecar/actuator/auto/ActuatorWebSecurityAutoConfig.java @@ -30,6 +30,9 @@ import software.xdev.sse.web.sidecar.actuator.config.ActuatorSecurityConfig; import software.xdev.sse.web.sidecar.actuator.metrics.ActuatorSecurityMetricsHandler; import software.xdev.sse.web.sidecar.actuator.metrics.DefaultActuatorSecurityMetricsHandler; +import software.xdev.sse.web.sidecar.actuator.passwordhash.cache.PasswordHashCache; +import software.xdev.sse.web.sidecar.actuator.passwordhash.cache.UnchachedPasswordHashCache; +import software.xdev.sse.web.sidecar.actuator.passwordhash.hasher.sha256.DefaultSHA256PasswordHasher; @ConditionalOnProperty(value = "sse.sidecar.actuator.enabled", matchIfMissing = true) @@ -62,4 +65,21 @@ public ActuatorBlackHolingPathsProvider actuatorBlackHolingPathsProvider( { return new ActuatorBlackHolingPathsProvider(webEndpointProperties); } + + @ConditionalOnProperty( + value = "sse.sidecar.actuator.password-hash.hasher." + DefaultSHA256PasswordHasher.ID + ".enabled", + matchIfMissing = true) + @ConditionalOnMissingBean + @Bean + public DefaultSHA256PasswordHasher defaultSHA256PasswordHasher() + { + return new DefaultSHA256PasswordHasher(); + } + + @ConditionalOnMissingBean + @Bean + public PasswordHashCache passwordHashCache() + { + return new UnchachedPasswordHashCache(); + } } diff --git a/web-sidecar-actuator/src/main/java/software/xdev/sse/web/sidecar/actuator/auto/passwordhash/cache/caffeine/CaffeinePasswordHashCacheAutoConfig.java b/web-sidecar-actuator/src/main/java/software/xdev/sse/web/sidecar/actuator/auto/passwordhash/cache/caffeine/CaffeinePasswordHashCacheAutoConfig.java new file mode 100644 index 00000000..dda1c994 --- /dev/null +++ b/web-sidecar-actuator/src/main/java/software/xdev/sse/web/sidecar/actuator/auto/passwordhash/cache/caffeine/CaffeinePasswordHashCacheAutoConfig.java @@ -0,0 +1,58 @@ +/* + * Copyright © 2025 XDEV Software (https://xdev.software) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.xdev.sse.web.sidecar.actuator.auto.passwordhash.cache.caffeine; + +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.AutoConfigureBefore; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; + +import software.xdev.sse.web.sidecar.actuator.auto.ActuatorWebSecurityAutoConfig; +import software.xdev.sse.web.sidecar.actuator.passwordhash.cache.PasswordHashCache; +import software.xdev.sse.web.sidecar.actuator.passwordhash.cache.caffeine.CaffeinePasswordHashCache; +import software.xdev.sse.web.sidecar.actuator.passwordhash.cache.caffeine.CaffeinePasswordHashCacheConfig; + + +@ConditionalOnClass(name = "com.github.benmanes.caffeine.cache.Caffeine") +@ConditionalOnProperty(value = "sse.sidecar.actuator.enabled", matchIfMissing = true) +@ConditionalOnProperty( + value = "sse.sidecar.actuator.password-hash.cache.enabled", + matchIfMissing = true) +@ConditionalOnProperty( + value = "sse.sidecar.actuator.password-hash.cache.caffeine.enabled", + matchIfMissing = true) +@AutoConfiguration +@AutoConfigureBefore(ActuatorWebSecurityAutoConfig.class) +public class CaffeinePasswordHashCacheAutoConfig +{ + @ConfigurationProperties("sse.sidecar.actuator.password-hash.cache.caffeine") + @ConditionalOnMissingBean + @Bean + public CaffeinePasswordHashCacheConfig caffeinePasswordHashCacheConfig() + { + return new CaffeinePasswordHashCacheConfig(); + } + + @ConditionalOnMissingBean + @Bean + public PasswordHashCache passwordHashCache(final CaffeinePasswordHashCacheConfig config) + { + return new CaffeinePasswordHashCache(config); + } +} diff --git a/web-sidecar-actuator/src/main/java/software/xdev/sse/web/sidecar/actuator/auto/passwordhash/cache/expiringlimited/ExpiringLimitedPasswordHashCacheAutoConfig.java b/web-sidecar-actuator/src/main/java/software/xdev/sse/web/sidecar/actuator/auto/passwordhash/cache/expiringlimited/ExpiringLimitedPasswordHashCacheAutoConfig.java new file mode 100644 index 00000000..9a749652 --- /dev/null +++ b/web-sidecar-actuator/src/main/java/software/xdev/sse/web/sidecar/actuator/auto/passwordhash/cache/expiringlimited/ExpiringLimitedPasswordHashCacheAutoConfig.java @@ -0,0 +1,58 @@ +/* + * Copyright © 2025 XDEV Software (https://xdev.software) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.xdev.sse.web.sidecar.actuator.auto.passwordhash.cache.expiringlimited; + +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.AutoConfigureBefore; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; + +import software.xdev.sse.web.sidecar.actuator.auto.ActuatorWebSecurityAutoConfig; +import software.xdev.sse.web.sidecar.actuator.passwordhash.cache.PasswordHashCache; +import software.xdev.sse.web.sidecar.actuator.passwordhash.cache.expiringlimited.ExpiringLimitedPasswordHashCache; +import software.xdev.sse.web.sidecar.actuator.passwordhash.cache.expiringlimited.ExpiringLimitedPasswordHashCacheConfig; + + +@ConditionalOnClass(name = "software.xdev.caching.ExpiringLimitedCache") +@ConditionalOnProperty(value = "sse.sidecar.actuator.enabled", matchIfMissing = true) +@ConditionalOnProperty( + value = "sse.sidecar.actuator.password-hash.cache.enabled", + matchIfMissing = true) +@ConditionalOnProperty( + value = "sse.sidecar.actuator.password-hash.cache.expiring-limited.enabled", + matchIfMissing = true) +@AutoConfiguration +@AutoConfigureBefore(ActuatorWebSecurityAutoConfig.class) +public class ExpiringLimitedPasswordHashCacheAutoConfig +{ + @ConfigurationProperties("sse.sidecar.actuator.password-hash.cache.expiring-limited") + @ConditionalOnMissingBean + @Bean + public ExpiringLimitedPasswordHashCacheConfig expiringLimitedCachedSHA256PasswordHasherConfig() + { + return new ExpiringLimitedPasswordHashCacheConfig(); + } + + @ConditionalOnMissingBean + @Bean + public PasswordHashCache passwordHashCache(final ExpiringLimitedPasswordHashCacheConfig config) + { + return new ExpiringLimitedPasswordHashCache(config); + } +} diff --git a/web-sidecar-actuator/src/main/java/software/xdev/sse/web/sidecar/actuator/config/ActuatorSecurityConfig.java b/web-sidecar-actuator/src/main/java/software/xdev/sse/web/sidecar/actuator/config/ActuatorSecurityConfig.java index fa080dd8..c2cea844 100644 --- a/web-sidecar-actuator/src/main/java/software/xdev/sse/web/sidecar/actuator/config/ActuatorSecurityConfig.java +++ b/web-sidecar-actuator/src/main/java/software/xdev/sse/web/sidecar/actuator/config/ActuatorSecurityConfig.java @@ -24,6 +24,8 @@ import org.springframework.validation.annotation.Validated; +import software.xdev.sse.web.sidecar.actuator.passwordhash.hasher.sha256.DefaultSHA256PasswordHasher; + @Validated public class ActuatorSecurityConfig @@ -34,6 +36,9 @@ public class ActuatorSecurityConfig @Min(1) private int passwordMaxLength = 200; + @NotBlank + private String passwordHasherId = DefaultSHA256PasswordHasher.ID; + @NotNull private Set users = new HashSet<>(); @@ -59,6 +64,16 @@ public int getPasswordMaxLength() return this.passwordMaxLength; } + public String getPasswordHasherId() + { + return this.passwordHasherId; + } + + public void setPasswordHasherId(final String passwordHasherId) + { + this.passwordHasherId = passwordHasherId; + } + public Set getUsers() { return this.users; @@ -88,6 +103,8 @@ public String toString() + this.defaultRoleName + ", passwordMaxLength=" + this.passwordMaxLength + + ", passwordHasherId=" + + this.passwordHasherId + ", users=" + this.users + ", defaultMetricsEnabled=" diff --git a/web-sidecar-actuator/src/main/java/software/xdev/sse/web/sidecar/actuator/config/ActuatorUserConfig.java b/web-sidecar-actuator/src/main/java/software/xdev/sse/web/sidecar/actuator/config/ActuatorUserConfig.java index 2d2f4cf5..8fcdf429 100644 --- a/web-sidecar-actuator/src/main/java/software/xdev/sse/web/sidecar/actuator/config/ActuatorUserConfig.java +++ b/web-sidecar-actuator/src/main/java/software/xdev/sse/web/sidecar/actuator/config/ActuatorUserConfig.java @@ -21,6 +21,8 @@ import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; +import org.slf4j.LoggerFactory; + public class ActuatorUserConfig { @@ -28,7 +30,7 @@ public class ActuatorUserConfig private String username; @NotBlank - private String passwordSha256; + private String passwordHash; @NotNull private Set allowedEndpoints = new HashSet<>(); // If empty -> ACCESS TO ALL ENDPOINTS @@ -43,14 +45,35 @@ public void setUsername(final String username) this.username = username; } + /** + * @deprecated Replaced by {@link #getPasswordHash()} + */ + @Deprecated(since = "1.3.0", forRemoval = true) public String getPasswordSha256() { - return this.passwordSha256; + return this.getPasswordHash(); } + /** + * @deprecated Replaced by {@link #setPasswordHash(String)} + */ + @Deprecated(since = "1.3.0", forRemoval = true) public void setPasswordSha256(final String passwordSha256) { - this.passwordSha256 = passwordSha256; + LoggerFactory.getLogger(this.getClass()) + .error("Detected usage of deprecated property 'passwordSha256' that will be removed soon." + + " Use 'passwordHash' instead!"); + this.setPasswordHash(passwordSha256); + } + + public String getPasswordHash() + { + return this.passwordHash; + } + + public void setPasswordHash(final String passwordHash) + { + this.passwordHash = passwordHash; } public void setAllowedEndpoints(final Set allowedEndpoints) @@ -69,7 +92,7 @@ public String toString() return "ActuatorUserConfig [" + "username=" + this.username - + ", passwordSha256=" + + ", passwordHash=" + "***" + ", allowedEndpoints=" + (this.allowedEndpoints.isEmpty() ? "" : this.allowedEndpoints) diff --git a/web-sidecar-actuator/src/main/java/software/xdev/sse/web/sidecar/actuator/passwordhash/cache/CommonPasswordHashCacheConfig.java b/web-sidecar-actuator/src/main/java/software/xdev/sse/web/sidecar/actuator/passwordhash/cache/CommonPasswordHashCacheConfig.java new file mode 100644 index 00000000..3980bef7 --- /dev/null +++ b/web-sidecar-actuator/src/main/java/software/xdev/sse/web/sidecar/actuator/passwordhash/cache/CommonPasswordHashCacheConfig.java @@ -0,0 +1,70 @@ +/* + * Copyright © 2025 XDEV Software (https://xdev.software) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.xdev.sse.web.sidecar.actuator.passwordhash.cache; + +import java.time.Duration; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; + +import org.springframework.validation.annotation.Validated; + + +@Validated +public abstract class CommonPasswordHashCacheConfig +{ + @NotNull + private Duration expiration = Duration.ofHours(1); + @Min(1) + private int maxSize = 100; + + public Duration getExpiration() + { + return this.expiration; + } + + public void setExpiration(final Duration expiration) + { + this.expiration = expiration; + } + + public int getMaxSize() + { + return this.maxSize; + } + + public void setMaxSize(final int maxSize) + { + this.maxSize = maxSize; + } + + protected String propertiesToString() + { + return "expiration=" + + this.expiration + + ", maxSize=" + + this.maxSize; + } + + @Override + public String toString() + { + return this.getClass().getSimpleName() + + " [" + + this.propertiesToString() + + "]"; + } +} diff --git a/web-sidecar-actuator/src/main/java/software/xdev/sse/web/sidecar/actuator/passwordhash/cache/PasswordHashCache.java b/web-sidecar-actuator/src/main/java/software/xdev/sse/web/sidecar/actuator/passwordhash/cache/PasswordHashCache.java new file mode 100644 index 00000000..8b1ad85a --- /dev/null +++ b/web-sidecar-actuator/src/main/java/software/xdev/sse/web/sidecar/actuator/passwordhash/cache/PasswordHashCache.java @@ -0,0 +1,24 @@ +/* + * Copyright © 2025 XDEV Software (https://xdev.software) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.xdev.sse.web.sidecar.actuator.passwordhash.cache; + +import java.util.function.Function; + + +public interface PasswordHashCache +{ + String computeIfAbsent(String input, Function compute); +} diff --git a/web-sidecar-actuator/src/main/java/software/xdev/sse/web/sidecar/actuator/passwordhash/cache/UnchachedPasswordHashCache.java b/web-sidecar-actuator/src/main/java/software/xdev/sse/web/sidecar/actuator/passwordhash/cache/UnchachedPasswordHashCache.java new file mode 100644 index 00000000..055feb04 --- /dev/null +++ b/web-sidecar-actuator/src/main/java/software/xdev/sse/web/sidecar/actuator/passwordhash/cache/UnchachedPasswordHashCache.java @@ -0,0 +1,28 @@ +/* + * Copyright © 2025 XDEV Software (https://xdev.software) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.xdev.sse.web.sidecar.actuator.passwordhash.cache; + +import java.util.function.Function; + + +public class UnchachedPasswordHashCache implements PasswordHashCache +{ + @Override + public String computeIfAbsent(final String input, final Function compute) + { + return compute.apply(input); + } +} diff --git a/web-sidecar-actuator/src/main/java/software/xdev/sse/web/sidecar/actuator/passwordhash/cache/caffeine/CaffeinePasswordHashCache.java b/web-sidecar-actuator/src/main/java/software/xdev/sse/web/sidecar/actuator/passwordhash/cache/caffeine/CaffeinePasswordHashCache.java new file mode 100644 index 00000000..30bb9c66 --- /dev/null +++ b/web-sidecar-actuator/src/main/java/software/xdev/sse/web/sidecar/actuator/passwordhash/cache/caffeine/CaffeinePasswordHashCache.java @@ -0,0 +1,44 @@ +/* + * Copyright © 2025 XDEV Software (https://xdev.software) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.xdev.sse.web.sidecar.actuator.passwordhash.cache.caffeine; + +import java.util.function.Function; + +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; + +import software.xdev.sse.web.sidecar.actuator.passwordhash.cache.PasswordHashCache; + + +public class CaffeinePasswordHashCache implements PasswordHashCache +{ + protected final Cache cache; + + public CaffeinePasswordHashCache(final CaffeinePasswordHashCacheConfig config) + { + this.cache = Caffeine.newBuilder() + .softValues() + .maximumSize(config.getMaxSize()) + .expireAfterAccess(config.getExpiration()) + .build(); + } + + @Override + public String computeIfAbsent(final String input, final Function compute) + { + return this.cache.get(input, compute); + } +} diff --git a/web-sidecar-actuator/src/main/java/software/xdev/sse/web/sidecar/actuator/passwordhash/cache/caffeine/CaffeinePasswordHashCacheConfig.java b/web-sidecar-actuator/src/main/java/software/xdev/sse/web/sidecar/actuator/passwordhash/cache/caffeine/CaffeinePasswordHashCacheConfig.java new file mode 100644 index 00000000..b810e626 --- /dev/null +++ b/web-sidecar-actuator/src/main/java/software/xdev/sse/web/sidecar/actuator/passwordhash/cache/caffeine/CaffeinePasswordHashCacheConfig.java @@ -0,0 +1,26 @@ +/* + * Copyright © 2025 XDEV Software (https://xdev.software) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.xdev.sse.web.sidecar.actuator.passwordhash.cache.caffeine; + +import org.springframework.validation.annotation.Validated; + +import software.xdev.sse.web.sidecar.actuator.passwordhash.cache.CommonPasswordHashCacheConfig; + + +@Validated +public class CaffeinePasswordHashCacheConfig extends CommonPasswordHashCacheConfig +{ +} diff --git a/web-sidecar-actuator/src/main/java/software/xdev/sse/web/sidecar/actuator/passwordhash/cache/expiringlimited/ExpiringLimitedPasswordHashCache.java b/web-sidecar-actuator/src/main/java/software/xdev/sse/web/sidecar/actuator/passwordhash/cache/expiringlimited/ExpiringLimitedPasswordHashCache.java new file mode 100644 index 00000000..f2d27449 --- /dev/null +++ b/web-sidecar-actuator/src/main/java/software/xdev/sse/web/sidecar/actuator/passwordhash/cache/expiringlimited/ExpiringLimitedPasswordHashCache.java @@ -0,0 +1,38 @@ +/* + * Copyright © 2025 XDEV Software (https://xdev.software) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.xdev.sse.web.sidecar.actuator.passwordhash.cache.expiringlimited; + +import java.util.function.Function; + +import software.xdev.caching.ExpiringLimitedCache; +import software.xdev.sse.web.sidecar.actuator.passwordhash.cache.PasswordHashCache; + + +public class ExpiringLimitedPasswordHashCache implements PasswordHashCache +{ + protected final ExpiringLimitedCache expiringLimitedCache; + + public ExpiringLimitedPasswordHashCache(final ExpiringLimitedPasswordHashCacheConfig config) + { + this.expiringLimitedCache = new ExpiringLimitedCache<>(config.getExpiration(), config.getMaxSize()); + } + + @Override + public String computeIfAbsent(final String input, final Function compute) + { + return this.expiringLimitedCache.computeIfAbsent(input, compute); + } +} diff --git a/web-sidecar-actuator/src/main/java/software/xdev/sse/web/sidecar/actuator/passwordhash/cache/expiringlimited/ExpiringLimitedPasswordHashCacheConfig.java b/web-sidecar-actuator/src/main/java/software/xdev/sse/web/sidecar/actuator/passwordhash/cache/expiringlimited/ExpiringLimitedPasswordHashCacheConfig.java new file mode 100644 index 00000000..0481bdfb --- /dev/null +++ b/web-sidecar-actuator/src/main/java/software/xdev/sse/web/sidecar/actuator/passwordhash/cache/expiringlimited/ExpiringLimitedPasswordHashCacheConfig.java @@ -0,0 +1,26 @@ +/* + * Copyright © 2025 XDEV Software (https://xdev.software) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.xdev.sse.web.sidecar.actuator.passwordhash.cache.expiringlimited; + +import org.springframework.validation.annotation.Validated; + +import software.xdev.sse.web.sidecar.actuator.passwordhash.cache.CommonPasswordHashCacheConfig; + + +@Validated +public class ExpiringLimitedPasswordHashCacheConfig extends CommonPasswordHashCacheConfig +{ +} diff --git a/web-sidecar-actuator/src/main/java/software/xdev/sse/web/sidecar/actuator/passwordhash/hasher/PasswordHasher.java b/web-sidecar-actuator/src/main/java/software/xdev/sse/web/sidecar/actuator/passwordhash/hasher/PasswordHasher.java new file mode 100644 index 00000000..79c15c0a --- /dev/null +++ b/web-sidecar-actuator/src/main/java/software/xdev/sse/web/sidecar/actuator/passwordhash/hasher/PasswordHasher.java @@ -0,0 +1,23 @@ +/* + * Copyright © 2025 XDEV Software (https://xdev.software) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.xdev.sse.web.sidecar.actuator.passwordhash.hasher; + +public interface PasswordHasher +{ + String id(); + + String hash(String input); +} diff --git a/web-sidecar-actuator/src/main/java/software/xdev/sse/web/sidecar/actuator/passwordhash/hasher/sha256/DefaultSHA256PasswordHasher.java b/web-sidecar-actuator/src/main/java/software/xdev/sse/web/sidecar/actuator/passwordhash/hasher/sha256/DefaultSHA256PasswordHasher.java new file mode 100644 index 00000000..edc5607e --- /dev/null +++ b/web-sidecar-actuator/src/main/java/software/xdev/sse/web/sidecar/actuator/passwordhash/hasher/sha256/DefaultSHA256PasswordHasher.java @@ -0,0 +1,37 @@ +/* + * Copyright © 2025 XDEV Software (https://xdev.software) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.xdev.sse.web.sidecar.actuator.passwordhash.hasher.sha256; + +import software.xdev.sse.codec.hash.SHA256Hashing; +import software.xdev.sse.web.sidecar.actuator.passwordhash.hasher.PasswordHasher; + + +public class DefaultSHA256PasswordHasher implements PasswordHasher +{ + public static final String ID = "default-sha256"; + + @Override + public String id() + { + return ID; + } + + @Override + public String hash(final String input) + { + return SHA256Hashing.hash(input); + } +} diff --git a/web-sidecar-actuator/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/web-sidecar-actuator/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index 102a375e..7e7914a5 100644 --- a/web-sidecar-actuator/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/web-sidecar-actuator/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -1,2 +1,4 @@ +software.xdev.sse.web.sidecar.actuator.auto.passwordhash.cache.caffeine.CaffeinePasswordHashCacheAutoConfig +software.xdev.sse.web.sidecar.actuator.auto.passwordhash.cache.expiringlimited.ExpiringLimitedPasswordHashCacheAutoConfig software.xdev.sse.web.sidecar.actuator.auto.ActuatorWebSecurityAutoConfig software.xdev.sse.web.sidecar.actuator.ActuatorWebSecurity diff --git a/web-sidecar-common/pom.xml b/web-sidecar-common/pom.xml index 4f3c60f6..9a3c5fdd 100644 --- a/web-sidecar-common/pom.xml +++ b/web-sidecar-common/pom.xml @@ -6,7 +6,7 @@ software.xdev.sse web-sidecar-common - 1.2.3-SNAPSHOT + 1.3.0-SNAPSHOT jar web-sidecar-common @@ -54,7 +54,7 @@ org.springframework.boot spring-boot-dependencies - 3.5.5 + 3.5.6 pom import @@ -282,12 +282,12 @@ net.sourceforge.pmd pmd-core - 7.16.0 + 7.17.0 net.sourceforge.pmd pmd-java - 7.16.0 + 7.17.0 diff --git a/web/pom.xml b/web/pom.xml index ce8b4a59..02ea3899 100644 --- a/web/pom.xml +++ b/web/pom.xml @@ -6,7 +6,7 @@ software.xdev.sse web - 1.2.3-SNAPSHOT + 1.3.0-SNAPSHOT jar web @@ -54,7 +54,7 @@ org.springframework.boot spring-boot-dependencies - 3.5.5 + 3.5.6 pom import @@ -272,12 +272,12 @@ net.sourceforge.pmd pmd-core - 7.16.0 + 7.17.0 net.sourceforge.pmd pmd-java - 7.16.0 + 7.17.0