You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by da...@apache.org on 2021/05/25 18:09:28 UTC

[isis] 03/03: ISIS-2697: simplifies secman's support for user registration

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

danhaywood pushed a commit to branch ISIS-2697
in repository https://gitbox.apache.org/repos/asf/isis.git

commit 69e68922147605c4ceca0e19510aa26c9ce65252
Author: danhaywood <da...@haywood-associates.co.uk>
AuthorDate: Tue May 25 19:05:04 2021 +0100

    ISIS-2697: simplifies secman's support for user registration
---
 ...UserRegistrationService_010-implementation.adoc | 26 +-------
 .../apache/isis/core/config/IsisConfiguration.java | 19 ++++++
 .../adoc/modules/secman/pages/setting-up.adoc      | 20 ++++++
 .../secman/model/IsisModuleExtSecmanModel.java     |  4 +-
 ....java => UserRegistrationServiceForSecman.java} | 71 +++++++++++-----------
 5 files changed, 79 insertions(+), 61 deletions(-)

diff --git a/antora/components/refguide-index/modules/applib/pages/index/services/userreg/hooks/UserRegistrationService_010-implementation.adoc b/antora/components/refguide-index/modules/applib/pages/index/services/userreg/hooks/UserRegistrationService_010-implementation.adoc
index 66c8c0b..243bc20 100644
--- a/antora/components/refguide-index/modules/applib/pages/index/services/userreg/hooks/UserRegistrationService_010-implementation.adoc
+++ b/antora/components/refguide-index/modules/applib/pages/index/services/userreg/hooks/UserRegistrationService_010-implementation.adoc
@@ -10,28 +10,6 @@ Rather, the implementation will depend on the security mechanism being used.
 
 === SecMan
 
-If you have configured your app to use the xref:security:ROOT:about.adoc[SecMan extension] then note that it _does_ provide an abstract implementation (`SecurityModuleAppUserRegistrationServiceAbstract`) of the `UserRegistrationService`.
-You will need to extend that service and provide implementation for the two abstract methods: `getInitialRole()` and `getAdditionalInitialRoles()`.
-This is needed so that the self-registered users are assigned automatically to your application role(s) and be able to use the application.
-Without any role such user will be able only to see/use the logout link of the application.
-
-For example:
-
-[source,java]
-----
-@Service
-public class AppUserRegistrationService
-        extends SecurityModuleAppUserRegistrationServiceAbstract {
-
-    protected ApplicationRole getInitialRole() {
-        return applicationRoles.findRoleByName("regular-user");
-    }
-    protected Set<ApplicationRole> getAdditionalInitialRoles() {
-        return Collections.singleton(
-            applicationRoles.findRoleByName("self-registered-user"));
-    }
-
-    @Inject ApplicationRoles applicationRoles;
-}
-----
+If you have configured your app to use the xref:security:ROOT:about.adoc[SecMan extension] then note that it _does_ provide an implementation (`UserRegistrationServiceForSecman`) of this `UserRegistrationService`.
+This can be configured to set up initial roles; see xref:security:secman:setting-up.adoc#user-registration-aka-sign-up[secman docs] for details.
 
diff --git a/core/config/src/main/java/org/apache/isis/core/config/IsisConfiguration.java b/core/config/src/main/java/org/apache/isis/core/config/IsisConfiguration.java
index 835280f..aab2e3a 100644
--- a/core/config/src/main/java/org/apache/isis/core/config/IsisConfiguration.java
+++ b/core/config/src/main/java/org/apache/isis/core/config/IsisConfiguration.java
@@ -2947,7 +2947,26 @@ public class IsisConfiguration {
             }
         }
 
+        private final Secman secman = new Secman();
+        @Data
+        public static class Secman {
 
+            private final UserRegistration userRegistration = new UserRegistration();
+            @Data
+            public static class UserRegistration {
+                /**
+                 * The set of roles that users registering with the app are granted
+                 * automatically.
+                 *
+                 * <p>
+                 *     If using the wicket viewer, also requires
+                 *     {@link Viewer.Wicket#isSuppressSignUp() isis.viewer.wicket.suppress-signup} to be set
+                 *     <code>false</code>, along with any other of its other prereqs.
+                 * </p>
+                 */
+                private final List<String> initialRoleNames = new ArrayList<>();
+            }
+        }
     }
 
     private static List<String> listOf(final String ...values) {
diff --git a/extensions/security/secman/adoc/modules/secman/pages/setting-up.adoc b/extensions/security/secman/adoc/modules/secman/pages/setting-up.adoc
index 02d7b1e..670ced5 100644
--- a/extensions/security/secman/adoc/modules/secman/pages/setting-up.adoc
+++ b/extensions/security/secman/adoc/modules/secman/pages/setting-up.adoc
@@ -371,4 +371,24 @@ isisModuleSecurityRealm.delegateAuthenticationRealm=$ldapRealm               #<.
 <.> specify the LDAP realm as the delegate realm for SecMan's own realm.
 
 
+[#user-registration-aka-sign-up]
+== User registration (aka Sign-up)
+
+Secman provides an implementation of the xref:refguide:applib:index/services/userreg/UserRegistrationService.adoc[UserRegistrationService] SPI.
+This means, if the viewer supports it (eg the Wicket viewer's xref:vw::features/user-registration.adoc[sign-up support]), then end-users can sign-up to create an account via email.
+The Secman implementation sets up the user with appropriate initial roles.
+
+The exact roles to setup are specified using configuration property:
+
+[source,yaml]
+.application.yaml
+----
+isis:
+    extensions:
+        secman:
+            user-registration:
+                initial-roles:
+                    - "self-registered-user-role"
+                    - "regular-user-role"
+----
 
diff --git a/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/IsisModuleExtSecmanModel.java b/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/IsisModuleExtSecmanModel.java
index 23c9624..197836f 100644
--- a/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/IsisModuleExtSecmanModel.java
+++ b/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/IsisModuleExtSecmanModel.java
@@ -26,6 +26,7 @@ import org.apache.isis.extensions.secman.model.facets.TenantedAuthorizationPostP
 import org.apache.isis.extensions.secman.model.seed.SeedSecurityModuleService;
 import org.apache.isis.extensions.secman.model.spiimpl.ImpersonateMenuAdvisorForSecman;
 import org.apache.isis.extensions.secman.model.spiimpl.TableColumnVisibilityServiceForSecman;
+import org.apache.isis.extensions.secman.model.userreg.UserRegistrationServiceForSecman;
 
 /**
  * @since 2.0 {@index}
@@ -35,10 +36,11 @@ import org.apache.isis.extensions.secman.model.spiimpl.TableColumnVisibilityServ
         // Module
         IsisModuleExtSecmanApi.class,
 
-        // @Component
+        // @Component or @Service
         TenantedAuthorizationPostProcessor.Register.class,
         TableColumnVisibilityServiceForSecman.class,
         ImpersonateMenuAdvisorForSecman.class, //not activated by default yet
+        UserRegistrationServiceForSecman.class,
 
         SeedSecurityModuleService.class,
 
diff --git a/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/userreg/SecurityModuleAppUserRegistrationServiceAbstract.java b/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/userreg/UserRegistrationServiceForSecman.java
similarity index 58%
rename from extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/userreg/SecurityModuleAppUserRegistrationServiceAbstract.java
rename to extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/userreg/UserRegistrationServiceForSecman.java
index 7c38f95..a2cd8f0 100644
--- a/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/userreg/SecurityModuleAppUserRegistrationServiceAbstract.java
+++ b/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/userreg/UserRegistrationServiceForSecman.java
@@ -18,30 +18,46 @@
  */
 package org.apache.isis.extensions.secman.model.userreg;
 
-import java.util.Set;
+import java.util.Optional;
 
 import javax.inject.Inject;
+import javax.inject.Named;
 
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Service;
+
+import org.apache.isis.applib.annotation.OrderPrecedence;
 import org.apache.isis.applib.services.userreg.UserDetails;
 import org.apache.isis.applib.services.userreg.UserRegistrationService;
 import org.apache.isis.applib.value.Password;
 import org.apache.isis.commons.internal.base._Strings;
-import org.apache.isis.extensions.secman.api.role.dom.ApplicationRole;
+import org.apache.isis.core.config.IsisConfiguration;
 import org.apache.isis.extensions.secman.api.role.dom.ApplicationRoleRepository;
 import org.apache.isis.extensions.secman.api.user.dom.ApplicationUser;
 import org.apache.isis.extensions.secman.api.user.dom.ApplicationUserRepository;
 import org.apache.isis.extensions.secman.api.user.dom.ApplicationUserStatus;
 
+import lombok.RequiredArgsConstructor;
+import lombok.val;
+
 /**
- * An abstract implementation of {@link org.apache.isis.applib.services.userreg.UserRegistrationService}
- * with a single abstract method for the initial role of newly created local users
+ * An implementation of {@link org.apache.isis.applib.services.userreg.UserRegistrationService}
+ * to allow users to be automatically created with the configured initial
+ * role(s).
  *
  * @since 2.0 {@index}
  */
-public abstract class SecurityModuleAppUserRegistrationServiceAbstract implements UserRegistrationService {
+@Service
+@Named("isis.ext.secman.UserRegistrationServiceForSecman")
+@Order(OrderPrecedence.MIDPOINT)
+@Qualifier("SecMan")
+@RequiredArgsConstructor(onConstructor_ = {@Inject})
+public class UserRegistrationServiceForSecman implements UserRegistrationService {
 
-    @Inject private ApplicationUserRepository applicationUserRepository;
-    @Inject private ApplicationRoleRepository applicationRoleRepository;
+    private final ApplicationUserRepository applicationUserRepository;
+    private final ApplicationRoleRepository applicationRoleRepository;
+    private final IsisConfiguration isisConfiguration;
 
     @Override
     public boolean usernameExists(final String username) {
@@ -53,27 +69,21 @@ public abstract class SecurityModuleAppUserRegistrationServiceAbstract implement
             final UserDetails userDetails) {
 
         final Password password = new Password(userDetails.getPassword());
-        final ApplicationRole initialRole = getInitialRole();
 
         final String username = userDetails.getUsername();
         final String emailAddress = userDetails.getEmailAddress();
-        final ApplicationUser applicationUser = (ApplicationUser) applicationUserRepository
+        final ApplicationUser applicationUser = applicationUserRepository
                 .newLocalUser(username, password, ApplicationUserStatus.UNLOCKED);
 
         if(_Strings.isNotEmpty(emailAddress)) {
             applicationUser.setEmailAddress(emailAddress);
         }
-        if(initialRole!=null) {
-            applicationRoleRepository.addRoleToUser(initialRole, applicationUser);
-        }
-
-        final Set<ApplicationRole> additionalRoles = getAdditionalInitialRoles();
-        if(additionalRoles != null) {
-            for (final ApplicationRole additionalRole : additionalRoles) {
-                applicationRoleRepository.addRoleToUser(additionalRole, applicationUser);
-            }
-        }
 
+        isisConfiguration.getExtensions().getSecman().getUserRegistration().getInitialRoleNames().stream()
+                .map(applicationRoleRepository::findByName)
+                .filter(Optional::isPresent)
+                .map(Optional::get)
+                .forEach(role -> applicationRoleRepository.addRoleToUser(role, applicationUser));
     }
 
     @Override
@@ -83,23 +93,12 @@ public abstract class SecurityModuleAppUserRegistrationServiceAbstract implement
 
     @Override
     public boolean updatePasswordByEmail(final String emailAddress, final String password) {
-        boolean passwordUpdated = false;
-        final ApplicationUser user = applicationUserRepository.findByEmailAddress(emailAddress)
-                .orElse(null);
-        if (user != null) {
-            passwordUpdated = applicationUserRepository.updatePassword(user, password);;
-        }
-        return passwordUpdated;
+        return applicationUserRepository.findByEmailAddress(emailAddress)
+                .map(user -> {
+                    val passwordWasUpdated = applicationUserRepository.updatePassword(user, password);
+                    return passwordWasUpdated;
+                })
+                .orElse(false);
     }
 
-    /**
-     * @return The role to use for newly created local users
-     */
-    protected abstract ApplicationRole getInitialRole();
-
-    /**
-     * @return Additional roles for newly created local users
-     */
-    protected abstract Set<ApplicationRole> getAdditionalInitialRoles();
-
 }