You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by ah...@apache.org on 2021/11/25 12:57:05 UTC
[isis] branch master updated: ISIS-2884: fixes invalid use of password hash matching
This is an automated email from the ASF dual-hosted git repository.
ahuber pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/isis.git
The following commit(s) were added to refs/heads/master by this push:
new 8d4b3f8 ISIS-2884: fixes invalid use of password hash matching
8d4b3f8 is described below
commit 8d4b3f8dde0e81d21613b8a22fd75ecac579c2a9
Author: Andi Huber <ah...@apache.org>
AuthorDate: Thu Nov 25 13:56:53 2021 +0100
ISIS-2884: fixes invalid use of password hash matching
- also ditching SecMan's PasswordEncryptionService in favor of Spring's
PasswordEncoder
- also adding IsisModuleExtSecmanEncryptionSpring as an alternative to
IsisModuleExtSecmanEncryptionJbcrypt
---
.../modules/ROOT/pages/2021/2.0.0-M7/mignotes.adoc | 11 ++++
extensions/security/secman/applib/pom.xml | 5 ++
.../dom/ApplicationUserRepositoryAbstract.java | 35 +++++++------
.../dom/mixins/ApplicationUser_updatePassword.java | 13 ++---
.../applib/user/spi/PasswordEncryptionService.java | 33 ------------
.../security/secman/encryption-jbcrypt/pom.xml | 2 -
.../IsisModuleExtSecmanEncryptionJbcrypt.java | 4 +-
...crypt.java => PasswordEncoderUsingJBcrypt.java} | 23 +++++----
.../pom.xml | 15 ++----
.../IsisModuleExtSecmanEncryptionSpring.java | 58 ++++++++++++++++++++++
.../authenticator/AuthenticatorSecman.java | 24 ++++++---
.../AuthenticatorSecmanAutoConfiguration.java | 8 +--
extensions/security/secman/pom.xml | 1 +
.../shiro/IsisModuleExtSecmanShiroRealm.java | 24 +++++----
14 files changed, 152 insertions(+), 104 deletions(-)
diff --git a/antora/components/relnotes/modules/ROOT/pages/2021/2.0.0-M7/mignotes.adoc b/antora/components/relnotes/modules/ROOT/pages/2021/2.0.0-M7/mignotes.adoc
index 5bc2366..325c9f0 100644
--- a/antora/components/relnotes/modules/ROOT/pages/2021/2.0.0-M7/mignotes.adoc
+++ b/antora/components/relnotes/modules/ROOT/pages/2021/2.0.0-M7/mignotes.adoc
@@ -26,6 +26,9 @@ for non-public class member introspection.
| `org.apache.isis.applib.jaxb.JodaTimeJaxbAdapters`
| moved to `org.apache.isis.valuetypes.jodatime.applib.jaxb`
+| _SecMan's_ `PasswordEncryptionService` was removed
+| Using Spring's `org.springframework.security.crypto.password.PasswordEncoder` instead.
+
|===
== Configuration
@@ -44,6 +47,14 @@ isis.core.meta-model.introspector.policy=ANNOTATION_REQUIRED
----
+=== Password Hashing
+
+_SecMan_ provides a _Jbcrypt_ based password encryption service.
+We added an alternative based on _Spring-Security_.
+To switch out the old vs the new replace `IsisModuleExtSecmanEncryptionJbcrypt` with
+`IsisModuleExtSecmanEncryptionSpring`. However note, that the new password hashes are
+not compatible with the old ones.
+
== Maven Artifacts
`-dn5` suffix was removed from artifacts, because we migrated DataNucleus 5.x to 6.x
diff --git a/extensions/security/secman/applib/pom.xml b/extensions/security/secman/applib/pom.xml
index 341c626..8fd4aff 100644
--- a/extensions/security/secman/applib/pom.xml
+++ b/extensions/security/secman/applib/pom.xml
@@ -49,6 +49,11 @@
</exclusion>
</exclusions>
</dependency>
+
+ <dependency>
+ <groupId>org.springframework.security</groupId>
+ <artifactId>spring-security-crypto</artifactId>
+ </dependency>
<dependency>
<groupId>org.apache.isis.mavendeps</groupId>
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/ApplicationUserRepositoryAbstract.java b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/ApplicationUserRepositoryAbstract.java
index 61a103f..db69145 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/ApplicationUserRepositoryAbstract.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/ApplicationUserRepositoryAbstract.java
@@ -25,7 +25,10 @@ import java.util.function.Consumer;
import javax.inject.Inject;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.lang.Nullable;
+import org.springframework.security.crypto.password.PasswordEncoder;
import org.apache.isis.applib.query.Query;
import org.apache.isis.applib.services.eventbus.EventBusService;
@@ -35,7 +38,6 @@ import org.apache.isis.applib.services.repository.RepositoryService;
import org.apache.isis.commons.internal.base._Casts;
import org.apache.isis.commons.internal.base._NullSafe;
import org.apache.isis.commons.internal.collections._Sets;
-import org.apache.isis.commons.internal.exceptions._Exceptions;
import org.apache.isis.core.config.IsisConfiguration;
import org.apache.isis.extensions.secman.applib.SecmanConfiguration;
import org.apache.isis.extensions.secman.applib.role.dom.ApplicationRole;
@@ -43,7 +45,6 @@ import org.apache.isis.extensions.secman.applib.tenancy.dom.ApplicationTenancy;
import org.apache.isis.extensions.secman.applib.user.dom.mixins.ApplicationUser_lock;
import org.apache.isis.extensions.secman.applib.user.dom.mixins.ApplicationUser_unlock;
import org.apache.isis.extensions.secman.applib.user.events.UserCreatedEvent;
-import org.apache.isis.extensions.secman.applib.user.spi.PasswordEncryptionService;
import org.apache.isis.extensions.secman.applib.util.RegexReplacer;
import lombok.NonNull;
@@ -55,16 +56,18 @@ implements ApplicationUserRepository {
@Inject private FactoryService factoryService;
@Inject private RepositoryService repository;
@Inject private SecmanConfiguration configBean;
- @Inject private Optional<PasswordEncryptionService> passwordEncryptionService; // empty if no candidate is available
@Inject protected IsisConfiguration isisConfiguration;
@Inject private EventBusService eventBusService;
@Inject RegexReplacer regexReplacer;
+ //@Inject private Optional<PasswordEncryptionService> passwordEncryptionService; // empty if no candidate is available
+ @Autowired(required = false) @Qualifier("secman") PasswordEncoder passwordEncoder;
+
@Inject private javax.inject.Provider<QueryResultsCache> queryResultsCacheProvider;
private final Class<U> applicationUserClass;
- protected ApplicationUserRepositoryAbstract(Class<U> applicationUserClass) {
+ protected ApplicationUserRepositoryAbstract(final Class<U> applicationUserClass) {
this.applicationUserClass = applicationUserClass;
}
@@ -145,7 +148,7 @@ implements ApplicationUserRepository {
}
@Override
- public Collection<ApplicationUser> findByRole(ApplicationRole role) {
+ public Collection<ApplicationUser> findByRole(final ApplicationRole role) {
return _NullSafe.stream(role.getUsers())
.collect(_Sets.toUnmodifiableSorted());
@@ -179,7 +182,7 @@ implements ApplicationUserRepository {
// -- UPDATE USER STATE
@Override
- public void enable(ApplicationUser user) {
+ public void enable(final ApplicationUser user) {
if(user.getStatus() != ApplicationUserStatus.UNLOCKED) {
factoryService.mixin(ApplicationUser_unlock.class, user)
.act();
@@ -187,7 +190,7 @@ implements ApplicationUserRepository {
}
@Override
- public void disable(ApplicationUser user) {
+ public void disable(final ApplicationUser user) {
if(user.getStatus() != ApplicationUserStatus.LOCKED) {
factoryService.mixin(ApplicationUser_lock.class, user)
.act();
@@ -195,15 +198,15 @@ implements ApplicationUserRepository {
}
@Override
- public boolean isAdminUser(ApplicationUser user) {
+ public boolean isAdminUser(final ApplicationUser user) {
return configBean.getAdminUserName().equals(user.getName());
}
@Override
public ApplicationUser newUser(
- @NonNull String username,
- @Nullable AccountType accountType,
- Consumer<ApplicationUser> beforePersist) {
+ @NonNull final String username,
+ @Nullable final AccountType accountType,
+ final Consumer<ApplicationUser> beforePersist) {
val user = newApplicationUser();
user.setUsername(username);
@@ -227,19 +230,15 @@ implements ApplicationUserRepository {
if(!isPasswordFeatureEnabled(user)) {
return false;
}
- val encrypter = passwordEncryptionService.orElseThrow(_Exceptions::unexpectedCodeReach);
- user.setEncryptedPassword(encrypter.encrypt(password));
+ user.setEncryptedPassword(passwordEncoder.encode(password));
repository.persistAndFlush(user);
return true;
}
@Override
- public boolean isPasswordFeatureEnabled(ApplicationUser user) {
+ public boolean isPasswordFeatureEnabled(final ApplicationUser user) {
return user.isLocalAccount()
- /*sonar-ignore-on*/
- && passwordEncryptionService != null // if for any reason injection fails
- /*sonar-ignore-off*/
- && passwordEncryptionService.isPresent();
+ && passwordEncoder != null;
}
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_updatePassword.java b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_updatePassword.java
index 0208ba1..8ac1f90 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_updatePassword.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_updatePassword.java
@@ -19,21 +19,22 @@
package org.apache.isis.extensions.secman.applib.user.dom.mixins;
import java.util.Objects;
-import java.util.Optional;
import javax.inject.Inject;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.security.crypto.password.PasswordEncoder;
+
import org.apache.isis.applib.annotation.Action;
import org.apache.isis.applib.annotation.ActionLayout;
import org.apache.isis.applib.annotation.MemberSupport;
import org.apache.isis.applib.annotation.SemanticsOf;
import org.apache.isis.applib.value.Password;
-import org.apache.isis.commons.internal.exceptions._Exceptions;
import org.apache.isis.extensions.secman.applib.IsisModuleExtSecmanApplib;
import org.apache.isis.extensions.secman.applib.user.dom.ApplicationUser;
import org.apache.isis.extensions.secman.applib.user.dom.ApplicationUserRepository;
import org.apache.isis.extensions.secman.applib.user.dom.mixins.ApplicationUser_updatePassword.DomainEvent;
-import org.apache.isis.extensions.secman.applib.user.spi.PasswordEncryptionService;
import lombok.RequiredArgsConstructor;
import lombok.val;
@@ -53,7 +54,7 @@ public class ApplicationUser_updatePassword {
extends IsisModuleExtSecmanApplib.ActionDomainEvent<ApplicationUser_updatePassword> {}
@Inject private ApplicationUserRepository applicationUserRepository;
- @Inject private Optional<PasswordEncryptionService> passwordEncryptionService; // empty if no candidate is available
+ @Autowired(required = false) private @Qualifier("secman") PasswordEncoder passwordEncoder;
private final ApplicationUser target;
@@ -87,12 +88,12 @@ public class ApplicationUser_updatePassword {
return "Password feature is not available for this User";
}
- val encrypter = passwordEncryptionService.orElseThrow(_Exceptions::unexpectedCodeReach);
+ Objects.requireNonNull(passwordEncoder);
val encryptedPassword = target.getEncryptedPassword();
if(target.getEncryptedPassword() != null) {
- if (!encrypter.matches(existingPassword.getPassword(), encryptedPassword)) {
+ if (!passwordEncoder.matches(existingPassword.getPassword(), encryptedPassword)) {
return "Existing password is incorrect";
}
}
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/spi/PasswordEncryptionService.java b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/spi/PasswordEncryptionService.java
deleted file mode 100644
index a6c25f0..0000000
--- a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/spi/PasswordEncryptionService.java
+++ /dev/null
@@ -1,33 +0,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.
- */
-package org.apache.isis.extensions.secman.applib.user.spi;
-
-import org.apache.isis.applib.annotation.Programmatic;
-
-/**
- * @since 2.0 {@index}
- */
-public interface PasswordEncryptionService {
-
- @Programmatic
- public String encrypt(final String password);
-
- @Programmatic
- public boolean matches(final String candidate, final String encrypted);
-}
diff --git a/extensions/security/secman/encryption-jbcrypt/pom.xml b/extensions/security/secman/encryption-jbcrypt/pom.xml
index 8e20131..525133c 100644
--- a/extensions/security/secman/encryption-jbcrypt/pom.xml
+++ b/extensions/security/secman/encryption-jbcrypt/pom.xml
@@ -34,8 +34,6 @@
<properties>
<jar-plugin.automaticModuleName>org.apache.isis.extensions.secman.encryption.jbcrypt</jar-plugin.automaticModuleName>
<git-plugin.propertiesDir>org/apache/isis/extensions/secman/encryption/jbcrypt</git-plugin.propertiesDir>
-
- <jbcrypt.version>0.4</jbcrypt.version>
</properties>
<dependencies>
diff --git a/extensions/security/secman/encryption-jbcrypt/src/main/java/org/apache/isis/extensions/secman/encryption/jbcrypt/IsisModuleExtSecmanEncryptionJbcrypt.java b/extensions/security/secman/encryption-jbcrypt/src/main/java/org/apache/isis/extensions/secman/encryption/jbcrypt/IsisModuleExtSecmanEncryptionJbcrypt.java
index 903c112..24f1e31 100644
--- a/extensions/security/secman/encryption-jbcrypt/src/main/java/org/apache/isis/extensions/secman/encryption/jbcrypt/IsisModuleExtSecmanEncryptionJbcrypt.java
+++ b/extensions/security/secman/encryption-jbcrypt/src/main/java/org/apache/isis/extensions/secman/encryption/jbcrypt/IsisModuleExtSecmanEncryptionJbcrypt.java
@@ -21,14 +21,14 @@ package org.apache.isis.extensions.secman.encryption.jbcrypt;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
-import org.apache.isis.extensions.secman.encryption.jbcrypt.services.PasswordEncryptionServiceUsingJBcrypt;
+import org.apache.isis.extensions.secman.encryption.jbcrypt.services.PasswordEncoderUsingJBcrypt;
/**
* @since 2.0 {@index}
*/
@Configuration
@Import({
- PasswordEncryptionServiceUsingJBcrypt.class
+ PasswordEncoderUsingJBcrypt.class
})
public class IsisModuleExtSecmanEncryptionJbcrypt {
diff --git a/extensions/security/secman/encryption-jbcrypt/src/main/java/org/apache/isis/extensions/secman/encryption/jbcrypt/services/PasswordEncryptionServiceUsingJBcrypt.java b/extensions/security/secman/encryption-jbcrypt/src/main/java/org/apache/isis/extensions/secman/encryption/jbcrypt/services/PasswordEncoderUsingJBcrypt.java
similarity index 69%
rename from extensions/security/secman/encryption-jbcrypt/src/main/java/org/apache/isis/extensions/secman/encryption/jbcrypt/services/PasswordEncryptionServiceUsingJBcrypt.java
rename to extensions/security/secman/encryption-jbcrypt/src/main/java/org/apache/isis/extensions/secman/encryption/jbcrypt/services/PasswordEncoderUsingJBcrypt.java
index 5499ab1..f00bbfd 100644
--- a/extensions/security/secman/encryption-jbcrypt/src/main/java/org/apache/isis/extensions/secman/encryption/jbcrypt/services/PasswordEncryptionServiceUsingJBcrypt.java
+++ b/extensions/security/secman/encryption-jbcrypt/src/main/java/org/apache/isis/extensions/secman/encryption/jbcrypt/services/PasswordEncoderUsingJBcrypt.java
@@ -20,21 +20,22 @@ package org.apache.isis.extensions.secman.encryption.jbcrypt.services;
import javax.inject.Named;
-import org.apache.isis.applib.annotation.PriorityPrecedence;
import org.mindrot.jbcrypt.BCrypt;
import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
-import org.apache.isis.extensions.secman.applib.user.spi.PasswordEncryptionService;
+import org.apache.isis.applib.annotation.PriorityPrecedence;
/**
* @since 2.0 {@index}
*/
@Service
-@Named("isis.ext.secman.PasswordEncryptionServiceUsingJBcrypt")
+@Named("isis.ext.secman.PasswordEncoderUsingJBcrypt")
@javax.annotation.Priority(PriorityPrecedence.MIDPOINT)
-@Qualifier("JBCrypt")
-public class PasswordEncryptionServiceUsingJBcrypt implements PasswordEncryptionService {
+@Qualifier("secman")
+public class PasswordEncoderUsingJBcrypt
+implements PasswordEncoder {
private String salt;
@@ -46,18 +47,18 @@ public class PasswordEncryptionServiceUsingJBcrypt implements PasswordEncryption
}
@Override
- public String encrypt(String password) {
- return password == null ? null : BCrypt.hashpw(password, getSalt());
+ public String encode(final CharSequence rawPassword) {
+ return rawPassword == null ? null : BCrypt.hashpw(rawPassword.toString(), getSalt());
}
@Override
- public boolean matches(String candidate, String encrypted) {
- if (candidate == null && encrypted == null) {
+ public boolean matches(final CharSequence rawPassword, final String encodedPassword) {
+ if (rawPassword == null && encodedPassword == null) {
return true;
}
- if (candidate == null || encrypted == null) {
+ if (rawPassword == null || encodedPassword == null) {
return false;
}
- return BCrypt.checkpw(candidate, encrypted);
+ return BCrypt.checkpw(rawPassword.toString(), encodedPassword);
}
}
diff --git a/extensions/security/secman/encryption-jbcrypt/pom.xml b/extensions/security/secman/encryption-spring/pom.xml
similarity index 81%
copy from extensions/security/secman/encryption-jbcrypt/pom.xml
copy to extensions/security/secman/encryption-spring/pom.xml
index 8e20131..2b4f814 100644
--- a/extensions/security/secman/encryption-jbcrypt/pom.xml
+++ b/extensions/security/secman/encryption-spring/pom.xml
@@ -27,15 +27,13 @@
<relativePath>../pom.xml</relativePath>
</parent>
- <artifactId>isis-extensions-secman-encryption-jbcrypt</artifactId>
- <name>Apache Isis Ext - Sec Man Encryption (Using jbcrypt)</name>
+ <artifactId>isis-extensions-secman-encryption-spring</artifactId>
+ <name>Apache Isis Ext - Sec Man Encryption (Using Spring)</name>
<description></description>
<properties>
- <jar-plugin.automaticModuleName>org.apache.isis.extensions.secman.encryption.jbcrypt</jar-plugin.automaticModuleName>
- <git-plugin.propertiesDir>org/apache/isis/extensions/secman/encryption/jbcrypt</git-plugin.propertiesDir>
-
- <jbcrypt.version>0.4</jbcrypt.version>
+ <jar-plugin.automaticModuleName>org.apache.isis.extensions.secman.encryption.spring</jar-plugin.automaticModuleName>
+ <git-plugin.propertiesDir>org/apache/isis/extensions/secman/encryption/spring</git-plugin.propertiesDir>
</properties>
<dependencies>
@@ -52,11 +50,6 @@
<scope>provided</scope>
</dependency>
- <dependency>
- <groupId>org.mindrot</groupId>
- <artifactId>jbcrypt</artifactId>
- </dependency>
-
</dependencies>
</project>
diff --git a/extensions/security/secman/encryption-spring/src/main/java/org/apache/isis/extensions/secman/encryption/spring/IsisModuleExtSecmanEncryptionSpring.java b/extensions/security/secman/encryption-spring/src/main/java/org/apache/isis/extensions/secman/encryption/spring/IsisModuleExtSecmanEncryptionSpring.java
new file mode 100644
index 0000000..90be1aa
--- /dev/null
+++ b/extensions/security/secman/encryption-spring/src/main/java/org/apache/isis/extensions/secman/encryption/spring/IsisModuleExtSecmanEncryptionSpring.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.isis.extensions.secman.encryption.spring;
+
+import java.util.Map;
+
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+import org.springframework.core.Ordered;
+import org.springframework.core.annotation.Order;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.crypto.password.DelegatingPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.security.crypto.scrypt.SCryptPasswordEncoder;
+
+import lombok.val;
+
+/**
+ * @since 2.0 {@index}
+ */
+@Configuration
+@Import({
+
+})
+public class IsisModuleExtSecmanEncryptionSpring {
+
+ /**
+ * @see "https://www.baeldung.com/spring-security-5-default-password-encoder"
+ */
+ @Bean @Qualifier("secman") @Order(Ordered.LOWEST_PRECEDENCE)
+ public PasswordEncoder passwordEncoder() {
+ // set up the list of supported encoders and their prefixes
+ val encoders = Map.<String, PasswordEncoder>of(
+ "bcrypt", new BCryptPasswordEncoder(),
+ "scrypt", new SCryptPasswordEncoder());
+
+ return new DelegatingPasswordEncoder("bcrypt", encoders);
+ }
+
+}
diff --git a/extensions/security/secman/integration/src/main/java/org/apache/isis/extensions/secman/integration/authenticator/AuthenticatorSecman.java b/extensions/security/secman/integration/src/main/java/org/apache/isis/extensions/secman/integration/authenticator/AuthenticatorSecman.java
index 052a62d..a6bcf42 100644
--- a/extensions/security/secman/integration/src/main/java/org/apache/isis/extensions/secman/integration/authenticator/AuthenticatorSecman.java
+++ b/extensions/security/secman/integration/src/main/java/org/apache/isis/extensions/secman/integration/authenticator/AuthenticatorSecman.java
@@ -22,6 +22,9 @@ import java.util.stream.Stream;
import javax.inject.Inject;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.security.crypto.password.PasswordEncoder;
+
import org.apache.isis.applib.services.iactnlayer.InteractionContext;
import org.apache.isis.applib.services.user.UserMemento;
import org.apache.isis.core.security.authentication.AuthenticationRequest;
@@ -30,9 +33,7 @@ import org.apache.isis.core.security.authentication.Authenticator;
import org.apache.isis.extensions.secman.applib.role.dom.ApplicationRole;
import org.apache.isis.extensions.secman.applib.user.dom.ApplicationUser;
import org.apache.isis.extensions.secman.applib.user.dom.ApplicationUserRepository;
-import org.apache.isis.extensions.secman.applib.user.spi.PasswordEncryptionService;
-import lombok.RequiredArgsConstructor;
import lombok.val;
import lombok.extern.log4j.Log4j2;
@@ -50,11 +51,19 @@ import lombok.extern.log4j.Log4j2;
* @since 2.0 {@index}
*/
@Log4j2
-@RequiredArgsConstructor(onConstructor_ = {@Inject})
+//@RequiredArgsConstructor(onConstructor_ = {@Inject})
public class AuthenticatorSecman implements Authenticator {
private final ApplicationUserRepository applicationUserRepository;
- private final PasswordEncryptionService passwordEncryptionService;
+ private final PasswordEncoder passwordEncoder;
+
+ @Inject
+ public AuthenticatorSecman(
+ final ApplicationUserRepository applicationUserRepository,
+ final @Qualifier("secman") PasswordEncoder passwordEncoder) {
+ this.applicationUserRepository = applicationUserRepository;
+ this.passwordEncoder = passwordEncoder;
+ }
@Override
public final boolean canAuthenticate(final Class<? extends AuthenticationRequest> authenticationRequestClass) {
@@ -65,15 +74,14 @@ public class AuthenticatorSecman implements Authenticator {
public InteractionContext authenticate(final AuthenticationRequest request, final String code) {
val authRequest = (AuthenticationRequestPassword) request;
val username = authRequest.getName();
- val password = authRequest.getPassword();
- val encryptedPassword = passwordEncryptionService.encrypt(password);
+ val rawPassword = authRequest.getPassword();
if(username == null) {
log.info("login failed: username is null");
return null;
}
return applicationUserRepository.findByUsername(username)
- .filter(appUser -> appUser.getEncryptedPassword().equals(encryptedPassword))
+ .filter(appUser -> passwordEncoder.matches(rawPassword, appUser.getEncryptedPassword()))
.map(appUser -> {
val roleNames = Stream.concat(
appUser.getRoles().stream().map(ApplicationRole::getName),
@@ -93,4 +101,6 @@ public class AuthenticatorSecman implements Authenticator {
// be re-authenticated.
}
+
+
}
diff --git a/extensions/security/secman/integration/src/main/java/org/apache/isis/extensions/secman/integration/authenticator/AuthenticatorSecmanAutoConfiguration.java b/extensions/security/secman/integration/src/main/java/org/apache/isis/extensions/secman/integration/authenticator/AuthenticatorSecmanAutoConfiguration.java
index 2e39f75..566cf50 100644
--- a/extensions/security/secman/integration/src/main/java/org/apache/isis/extensions/secman/integration/authenticator/AuthenticatorSecmanAutoConfiguration.java
+++ b/extensions/security/secman/integration/src/main/java/org/apache/isis/extensions/secman/integration/authenticator/AuthenticatorSecmanAutoConfiguration.java
@@ -18,16 +18,16 @@
*/
package org.apache.isis.extensions.secman.integration.authenticator;
-import org.apache.isis.applib.annotation.PriorityPrecedence;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.apache.isis.applib.annotation.PriorityPrecedence;
import org.apache.isis.core.security.authentication.Authenticator;
import org.apache.isis.extensions.secman.applib.user.dom.ApplicationUserRepository;
-import org.apache.isis.extensions.secman.applib.user.spi.PasswordEncryptionService;
/**
* @since 2.0 {@index}
@@ -41,8 +41,8 @@ public class AuthenticatorSecmanAutoConfiguration {
@Qualifier("Secman")
public Authenticator authenticatorSecman(
final ApplicationUserRepository applicationUserRepository,
- final PasswordEncryptionService passwordEncryptionService) {
- return new AuthenticatorSecman(applicationUserRepository, passwordEncryptionService);
+ final @Qualifier("secman") PasswordEncoder passwordEncoder) {
+ return new AuthenticatorSecman(applicationUserRepository, passwordEncoder);
}
}
diff --git a/extensions/security/secman/pom.xml b/extensions/security/secman/pom.xml
index 6ddedf2..4e9737d 100644
--- a/extensions/security/secman/pom.xml
+++ b/extensions/security/secman/pom.xml
@@ -102,6 +102,7 @@
<module>applib</module>
<module>integration</module>
<module>encryption-jbcrypt</module>
+ <module>encryption-spring</module>
<module>persistence-jdo</module>
<module>persistence-jpa</module>
<module>shiro-realm</module>
diff --git a/extensions/security/secman/shiro-realm/src/main/java/org/apache/isis/extensions/secman/shiro/IsisModuleExtSecmanShiroRealm.java b/extensions/security/secman/shiro-realm/src/main/java/org/apache/isis/extensions/secman/shiro/IsisModuleExtSecmanShiroRealm.java
index d8866e4..ab50f49 100644
--- a/extensions/security/secman/shiro-realm/src/main/java/org/apache/isis/extensions/secman/shiro/IsisModuleExtSecmanShiroRealm.java
+++ b/extensions/security/secman/shiro-realm/src/main/java/org/apache/isis/extensions/secman/shiro/IsisModuleExtSecmanShiroRealm.java
@@ -34,6 +34,9 @@ import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthenticatingRealm;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.support.TransactionTemplate;
@@ -47,7 +50,6 @@ import org.apache.isis.core.security.authorization.Authorizor;
import org.apache.isis.extensions.secman.applib.SecmanConfiguration;
import org.apache.isis.extensions.secman.applib.user.dom.AccountType;
import org.apache.isis.extensions.secman.applib.user.dom.ApplicationUserRepository;
-import org.apache.isis.extensions.secman.applib.user.spi.PasswordEncryptionService;
import org.apache.isis.extensions.secman.shiro.util.ShiroUtils;
import lombok.Getter;
@@ -85,7 +87,7 @@ public class IsisModuleExtSecmanShiroRealm extends AuthorizingRealm {
* invalid password.
*/
@Override
- protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
+ protected AuthenticationInfo doGetAuthenticationInfo(final AuthenticationToken token) throws AuthenticationException {
if (!(token instanceof UsernamePasswordToken)) {
throw new AuthenticationException();
}
@@ -157,7 +159,7 @@ public class IsisModuleExtSecmanShiroRealm extends AuthorizingRealm {
}
@Override
- protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
+ protected AuthorizationInfo doGetAuthorizationInfo(final PrincipalCollection principals) {
return principals.oneByType(PrincipalForApplicationUser.class);
}
@@ -174,7 +176,7 @@ public class IsisModuleExtSecmanShiroRealm extends AuthorizingRealm {
* @return {@code null} if not applicable
*/
private PrincipalForApplicationUser getPrincipal_fromAlreadyAuthenticatedSubjectIfApplicable(
- AuthenticationToken token) {
+ final AuthenticationToken token) {
// this optimization is only implemented for the simple case of a single realm setup
if(!ShiroUtils.isSingleRealm()) {
@@ -198,7 +200,7 @@ public class IsisModuleExtSecmanShiroRealm extends AuthorizingRealm {
return null;
}
- private DisabledAccountException disabledAccountException(String username) {
+ private DisabledAccountException disabledAccountException(final String username) {
return new DisabledAccountException(String.format("username='%s'", username));
}
@@ -215,7 +217,7 @@ public class IsisModuleExtSecmanShiroRealm extends AuthorizingRealm {
};
}
- private void authenticateElseThrow_usingDelegatedMechanism(AuthenticationToken token) {
+ private void authenticateElseThrow_usingDelegatedMechanism(final AuthenticationToken token) {
AuthenticationInfo delegateAccount = null;
try {
delegateAccount = delegateAuthenticationRealm.getAuthenticationInfo(token);
@@ -260,17 +262,19 @@ public class IsisModuleExtSecmanShiroRealm extends AuthorizingRealm {
private CheckPasswordResult checkPassword(final char[] candidate, final String actualEncryptedPassword) {
return execute(new Supplier<CheckPasswordResult>() {
+
+ @Autowired(required = false) private @Qualifier("secman") PasswordEncoder passwordEncoder;
+
@Override
public CheckPasswordResult get() {
- if (passwordEncryptionService == null) {
+ if (passwordEncoder == null) {
return CheckPasswordResult.NO_PASSWORD_ENCRYPTION_SERVICE_CONFIGURED;
}
- return passwordEncryptionService.matches(new String(candidate), actualEncryptedPassword)
+ return passwordEncoder.matches(new String(candidate), actualEncryptedPassword)
? CheckPasswordResult.OK
- : CheckPasswordResult.BAD_PASSWORD;
+ : CheckPasswordResult.BAD_PASSWORD;
}
- @Inject private PasswordEncryptionService passwordEncryptionService;
});
}