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 852405f267 ISIS-3113: code de-duplication around 
AuthenticationConverter
852405f267 is described below

commit 852405f267c5394cd841a0886a07a412986eec89
Author: Andi Huber <[email protected]>
AuthorDate: Tue Aug 9 18:12:50 2022 +0200

    ISIS-3113: code de-duplication around AuthenticationConverter
---
 .../session/InteractionServiceDefault.java         | 16 ++++++-----
 extensions/pom.xml                                 |  6 ++++
 .../oauth2/IsisModuleExtSpringSecurityOAuth2.java  |  4 +--
 ...thenticationConverterOfOAuth2UserPrincipal.java | 23 +++++++--------
 .../authconverters/AuthenticationConverter.java    | 33 ++++++++++++++++++++--
 ...nticationConverterOfAuthenticatedPrincipal.java | 21 ++++++++------
 .../AuthenticationConverterOfStringPrincipal.java  | 24 +++++++++-------
 ...henticationConverterOfUserDetailsPrincipal.java | 22 ++++++++-------
 .../spring/webmodule/SpringSecurityFilter.java     | 18 ++++++++++--
 9 files changed, 112 insertions(+), 55 deletions(-)

diff --git 
a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/session/InteractionServiceDefault.java
 
b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/session/InteractionServiceDefault.java
index 99010fdf30..a0be50b44d 100644
--- 
a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/session/InteractionServiceDefault.java
+++ 
b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/session/InteractionServiceDefault.java
@@ -54,7 +54,6 @@ import org.apache.isis.applib.util.schema.CommandDtoUtils;
 import org.apache.isis.applib.util.schema.InteractionDtoUtils;
 import org.apache.isis.applib.util.schema.InteractionsDtoUtils;
 import org.apache.isis.commons.functional.ThrowingRunnable;
-import org.apache.isis.commons.internal.assertions._Assert;
 import org.apache.isis.commons.internal.base._Casts;
 import org.apache.isis.commons.internal.concurrent._ConcurrentContext;
 import org.apache.isis.commons.internal.concurrent._ConcurrentTaskList;
@@ -348,12 +347,15 @@ implements
 
     private void requestRollback(final Throwable cause) {
         val stack = interactionLayerStack.get();
-        _Assert.assertFalse(stack.isEmpty(), ()->
-                String.format(
-                        "unexpected state: missing interaction (layer) on 
interaction rollback; "
-                        + "rollback was caused by %s -> %s",
-                        cause.getClass().getName(),
-                        cause.getMessage()));
+        if(stack.isEmpty()) {
+            // seeing this code-path, when the corresponding runnable/callable
+            // by itself causes the interaction stack to be closed
+            log.warn("unexpected state: missing interaction (layer) on 
interaction rollback; "
+                    + "rollback was caused by {} -> {}",
+                    cause.getClass().getName(),
+                    cause.getMessage());
+            return;
+        }
         val interaction = 
_Casts.<IsisInteraction>uncheckedCast(stack.get(0).getInteraction());
         txBoundaryHandler.requestRollback(interaction);
     }
diff --git a/extensions/pom.xml b/extensions/pom.xml
index c5e338ba8b..db3535adfa 100644
--- a/extensions/pom.xml
+++ b/extensions/pom.xml
@@ -431,6 +431,12 @@
                                
<artifactId>isis-extensions-cors-impl</artifactId>
                                <version>2.0.0-SNAPSHOT</version>
                        </dependency>
+                       
+                       <dependency>
+                               <groupId>org.apache.isis.extensions</groupId>
+                               
<artifactId>isis-extensions-spring-security-oauth2</artifactId>
+                               <version>2.0.0-SNAPSHOT</version>
+                       </dependency>
 
                        <dependency>
                                <groupId>org.apache.isis.extensions</groupId>
diff --git 
a/extensions/security/spring-oauth2/src/main/java/org/apache/isis/extensions/spring/security/oauth2/IsisModuleExtSpringSecurityOAuth2.java
 
b/extensions/security/spring-oauth2/src/main/java/org/apache/isis/extensions/spring/security/oauth2/IsisModuleExtSpringSecurityOAuth2.java
index 5e9e82f627..7933309a3c 100644
--- 
a/extensions/security/spring-oauth2/src/main/java/org/apache/isis/extensions/spring/security/oauth2/IsisModuleExtSpringSecurityOAuth2.java
+++ 
b/extensions/security/spring-oauth2/src/main/java/org/apache/isis/extensions/spring/security/oauth2/IsisModuleExtSpringSecurityOAuth2.java
@@ -24,8 +24,6 @@ import org.springframework.context.annotation.Import;
 import 
org.apache.isis.extensions.spring.security.oauth2.authconverters.AuthenticationConverterOfOAuth2UserPrincipal;
 import org.apache.isis.security.spring.IsisModuleSecuritySpring;
 
-import lombok.extern.log4j.Log4j2;
-
 /**
  * Configuration Bean to support authentication using Spring Security's
  * OAuth2 client.
@@ -41,7 +39,7 @@ import lombok.extern.log4j.Log4j2;
         AuthenticationConverterOfOAuth2UserPrincipal.class,
 
 })
-@Log4j2
+//@Log4j2
 public class IsisModuleExtSpringSecurityOAuth2 {
 
 }
diff --git 
a/extensions/security/spring-oauth2/src/main/java/org/apache/isis/extensions/spring/security/oauth2/authconverters/AuthenticationConverterOfOAuth2UserPrincipal.java
 
b/extensions/security/spring-oauth2/src/main/java/org/apache/isis/extensions/spring/security/oauth2/authconverters/AuthenticationConverterOfOAuth2UserPrincipal.java
index 00a767ce6b..95f89e6a4b 100644
--- 
a/extensions/security/spring-oauth2/src/main/java/org/apache/isis/extensions/spring/security/oauth2/authconverters/AuthenticationConverterOfOAuth2UserPrincipal.java
+++ 
b/extensions/security/spring-oauth2/src/main/java/org/apache/isis/extensions/spring/security/oauth2/authconverters/AuthenticationConverterOfOAuth2UserPrincipal.java
@@ -29,25 +29,26 @@ import org.apache.isis.applib.annotation.PriorityPrecedence;
 import org.apache.isis.applib.services.user.UserMemento;
 import org.apache.isis.security.spring.authconverters.AuthenticationConverter;
 
+import lombok.NonNull;
 import lombok.val;
 
 /**
- * Interprets an {@link Authentication} as containing an OAuth2 principal.
+ * Applies if {@link Authentication} holds a principal of type {@link 
OAuth2User}.
  */
 @Component
 @javax.annotation.Priority(PriorityPrecedence.LATE - 150)
-public class AuthenticationConverterOfOAuth2UserPrincipal implements 
AuthenticationConverter {
+public class AuthenticationConverterOfOAuth2UserPrincipal
+extends AuthenticationConverter.Abstract<OAuth2User> {
+
+    public AuthenticationConverterOfOAuth2UserPrincipal() {
+        super(OAuth2User.class);
+    }
 
     @Override
-    public UserMemento convert(final Authentication authentication) {
-        val principal = authentication.getPrincipal();
-        if (principal instanceof OAuth2User) {
-            val oAuth2User = (OAuth2User) principal;
-            return UserMemento.ofNameAndRoleNames(usernameFrom(oAuth2User))
-                    .withAvatarUrl(avatarUrlFrom(oAuth2User))
-                    .withRealName(realNameFrom(oAuth2User));
-        }
-        return null;
+    protected UserMemento convertPrincipal(final @NonNull OAuth2User 
oAuth2User) {
+        return UserMemento.ofNameAndRoleNames(usernameFrom(oAuth2User))
+                .withAvatarUrl(avatarUrlFrom(oAuth2User))
+                .withRealName(realNameFrom(oAuth2User));
     }
 
     protected static String usernameFrom(final OAuth2User oAuth2User) {
diff --git 
a/security/spring/src/main/java/org/apache/isis/security/spring/authconverters/AuthenticationConverter.java
 
b/security/spring/src/main/java/org/apache/isis/security/spring/authconverters/AuthenticationConverter.java
index 8ba60ef213..62ef5121e8 100644
--- 
a/security/spring/src/main/java/org/apache/isis/security/spring/authconverters/AuthenticationConverter.java
+++ 
b/security/spring/src/main/java/org/apache/isis/security/spring/authconverters/AuthenticationConverter.java
@@ -21,10 +21,17 @@ package org.apache.isis.security.spring.authconverters;
 import org.springframework.context.annotation.ComponentScan;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Import;
+import org.springframework.lang.Nullable;
 import org.springframework.security.core.Authentication;
 import org.springframework.stereotype.Component;
 
 import org.apache.isis.applib.services.user.UserMemento;
+import org.apache.isis.commons.internal.base._Casts;
+
+import lombok.AccessLevel;
+import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
+import lombok.val;
 
 /**
  * Defines an SPI to attempt to convert a Spring {@link Authentication} into
@@ -61,7 +68,7 @@ public interface AuthenticationConverter {
      *
      * <p>
      *     There are many different implementations of {@link Authentication},
-     *     so the implementation should be targetted at a specific
+     *     so the implementation should be targeted at a specific
      *     implementation.
      * </p>
      *
@@ -73,5 +80,27 @@ public interface AuthenticationConverter {
      * @param authentication to attempt to convert
      * @return non-null if could be converted
      */
-    UserMemento convert(final Authentication authentication);
+    @Nullable
+    UserMemento convert(@NonNull Authentication authentication);
+
+    // -- BASE CLASS FOR CONVENIENCE
+
+    @RequiredArgsConstructor(access = AccessLevel.PROTECTED)
+    abstract class Abstract<T> implements AuthenticationConverter {
+
+        private final @NonNull Class<T> principalClass;
+
+        @Override
+        public final UserMemento convert(final @NonNull Authentication 
authentication) {
+            val principal = authentication.getPrincipal();
+            return _Casts.castTo(principalClass, principal)
+                    .map(this::convertPrincipal)
+                    .orElse(null);
+        }
+
+        protected abstract UserMemento convertPrincipal(@NonNull T principal);
+
+    }
+
+
 }
diff --git 
a/security/spring/src/main/java/org/apache/isis/security/spring/authconverters/AuthenticationConverterOfAuthenticatedPrincipal.java
 
b/security/spring/src/main/java/org/apache/isis/security/spring/authconverters/AuthenticationConverterOfAuthenticatedPrincipal.java
index 3c04d7af65..9a83a71b29 100644
--- 
a/security/spring/src/main/java/org/apache/isis/security/spring/authconverters/AuthenticationConverterOfAuthenticatedPrincipal.java
+++ 
b/security/spring/src/main/java/org/apache/isis/security/spring/authconverters/AuthenticationConverterOfAuthenticatedPrincipal.java
@@ -25,19 +25,22 @@ import org.springframework.stereotype.Component;
 import org.apache.isis.applib.annotation.PriorityPrecedence;
 import org.apache.isis.applib.services.user.UserMemento;
 
-import lombok.val;
+import lombok.NonNull;
 
+/**
+ * Applies if {@link Authentication} holds a principal of type {@link 
AuthenticatedPrincipal}.
+ */
 @Component
 @javax.annotation.Priority(PriorityPrecedence.LATE - 100)
-public class AuthenticationConverterOfAuthenticatedPrincipal implements 
AuthenticationConverter {
+public class AuthenticationConverterOfAuthenticatedPrincipal
+extends AuthenticationConverter.Abstract<AuthenticatedPrincipal> {
+
+    public AuthenticationConverterOfAuthenticatedPrincipal() {
+        super(AuthenticatedPrincipal.class);
+    }
 
     @Override
-    public UserMemento convert(Authentication authentication) {
-        val principal = authentication.getPrincipal();
-        if (principal instanceof AuthenticatedPrincipal) {
-            val authenticatedPrincipal = (AuthenticatedPrincipal) principal;
-            return 
UserMemento.ofNameAndRoleNames(authenticatedPrincipal.getName());
-        }
-        return null;
+    protected UserMemento convertPrincipal(final @NonNull 
AuthenticatedPrincipal principal) {
+        return UserMemento.ofNameAndRoleNames(principal.getName());
     }
 }
diff --git 
a/security/spring/src/main/java/org/apache/isis/security/spring/authconverters/AuthenticationConverterOfStringPrincipal.java
 
b/security/spring/src/main/java/org/apache/isis/security/spring/authconverters/AuthenticationConverterOfStringPrincipal.java
index b50970993e..a998928bbb 100644
--- 
a/security/spring/src/main/java/org/apache/isis/security/spring/authconverters/AuthenticationConverterOfStringPrincipal.java
+++ 
b/security/spring/src/main/java/org/apache/isis/security/spring/authconverters/AuthenticationConverterOfStringPrincipal.java
@@ -24,20 +24,24 @@ import org.springframework.stereotype.Component;
 import org.apache.isis.applib.annotation.PriorityPrecedence;
 import org.apache.isis.applib.services.user.UserMemento;
 
-import lombok.val;
+import lombok.NonNull;
 
+/**
+ * Applies if {@link Authentication} holds a principal of type {@link String}.
+ */
 @Component
 @javax.annotation.Priority(PriorityPrecedence.LATE + 100)
-public class AuthenticationConverterOfStringPrincipal implements 
AuthenticationConverter {
+public class AuthenticationConverterOfStringPrincipal
+extends AuthenticationConverter.Abstract<String> {
+
+    public AuthenticationConverterOfStringPrincipal() {
+        super(String.class);
+    }
 
     @Override
-    public UserMemento convert(Authentication authentication) {
-        val principal = authentication.getPrincipal();
-        if (principal instanceof String) {
-            val name = (String) principal;
-            return UserMemento.ofNameAndRoleNames(name);
-        } else {
-            return null;
-        }
+    protected UserMemento convertPrincipal(final @NonNull String name) {
+        return name.isEmpty()
+                ? null
+                : UserMemento.ofNameAndRoleNames(name);
     }
 }
diff --git 
a/security/spring/src/main/java/org/apache/isis/security/spring/authconverters/AuthenticationConverterOfUserDetailsPrincipal.java
 
b/security/spring/src/main/java/org/apache/isis/security/spring/authconverters/AuthenticationConverterOfUserDetailsPrincipal.java
index d06b847865..4650ecebfb 100644
--- 
a/security/spring/src/main/java/org/apache/isis/security/spring/authconverters/AuthenticationConverterOfUserDetailsPrincipal.java
+++ 
b/security/spring/src/main/java/org/apache/isis/security/spring/authconverters/AuthenticationConverterOfUserDetailsPrincipal.java
@@ -25,20 +25,22 @@ import org.springframework.stereotype.Component;
 import org.apache.isis.applib.annotation.PriorityPrecedence;
 import org.apache.isis.applib.services.user.UserMemento;
 
-import lombok.val;
+import lombok.NonNull;
 
+/**
+ * Applies if {@link Authentication} holds a principal of type {@link 
UserDetails}.
+ */
 @Component
 @javax.annotation.Priority(PriorityPrecedence.LATE - 200)
-public class AuthenticationConverterOfUserDetailsPrincipal implements 
AuthenticationConverter {
+public class AuthenticationConverterOfUserDetailsPrincipal
+extends AuthenticationConverter.Abstract<UserDetails> {
+
+    protected AuthenticationConverterOfUserDetailsPrincipal() {
+        super(UserDetails.class);
+    }
 
     @Override
-    public UserMemento convert(Authentication authentication) {
-        val principal = authentication.getPrincipal();
-        if (principal instanceof UserDetails) {
-            val userDetails = (UserDetails) principal;
-            return UserMemento.ofNameAndRoleNames(userDetails.getUsername());
-        } else {
-            return null;
-        }
+    protected UserMemento convertPrincipal(final @NonNull UserDetails 
userDetails) {
+        return UserMemento.ofNameAndRoleNames(userDetails.getUsername());
     }
 }
diff --git 
a/security/spring/src/main/java/org/apache/isis/security/spring/webmodule/SpringSecurityFilter.java
 
b/security/spring/src/main/java/org/apache/isis/security/spring/webmodule/SpringSecurityFilter.java
index 96f6b8e2fe..405a810152 100644
--- 
a/security/spring/src/main/java/org/apache/isis/security/spring/webmodule/SpringSecurityFilter.java
+++ 
b/security/spring/src/main/java/org/apache/isis/security/spring/webmodule/SpringSecurityFilter.java
@@ -65,20 +65,32 @@ public class SpringSecurityFilter implements Filter {
                 .orElse(null);
 
         if (userMemento == null) {
-            ((HttpServletResponse) 
servletResponse).setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+            setUnauthorized(servletResponse);
             return; // either not authenticated or unknown principal type (not 
handled)
         }
 
         val interactionContext = 
InteractionContext.ofUserWithSystemDefaults(userMemento)
                 
.withTimeZoneIfAny(userCurrentSessionTimeZoneHolder.getUserTimeZone());
 
-        interactionService.run(
+        val result = interactionService.runAndCatch(
                 interactionContext,
                 ()->filterChain.doFilter(servletRequest, servletResponse));
+
+        result.ifFailure(failure->{
+            failure.printStackTrace(); // debug
+            setUnauthorized(servletResponse);
+        });
+
+        // re-throw
+        result.ifFailureFail();
     }
 
     // -- HELPER
 
+    private void setUnauthorized(final ServletResponse servletResponse){
+        ((HttpServletResponse) 
servletResponse).setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+    }
+
     /**
      * Optionally Spring's {@link Authentication}, based on presence
      * (no matter whether actually authenticated).
@@ -110,7 +122,7 @@ public class SpringSecurityFilter implements Filter {
                                 
.withRoleAdded(UserMemento.AUTHORIZED_USER_ROLE)
                                 
.withAuthenticationSource(AuthenticationSource.EXTERNAL));
                 }
-            } catch(final Exception ignored) {
+            } catch(final Throwable ignored) {
             }
         }
         return Optional.empty();

Reply via email to