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 2019/08/03 09:56:51 UTC

[isis] branch v2 updated: ISIS-2156 bump shiro version 1.3.2 -> 1.4.1

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

ahuber pushed a commit to branch v2
in repository https://gitbox.apache.org/repos/asf/isis.git


The following commit(s) were added to refs/heads/v2 by this push:
     new 780d73e  ISIS-2156 bump shiro version 1.3.2 -> 1.4.1
780d73e is described below

commit 780d73e9de7289d9edd877f2b763250c96e77344
Author: Andi Huber <ah...@apache.org>
AuthorDate: Sat Aug 3 11:56:35 2019 +0200

    ISIS-2156 bump shiro version 1.3.2 -> 1.4.1
    
    - despite the unresolved shiro issue [1], however the mentioned
    workaround appears to work:
    
    filterChainResolver =
    org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver
    
    
    [1] https://issues.apache.org/jira/browse/SHIRO-610
---
 .../commons/internal/memento/MementosTest.java     |  2 +-
 .../apache/isis/commons/internal/base/_Casts.java  | 48 ++++++++++++++-----
 .../apache/isis/security/shiro/WebModuleShiro.java | 11 +++++
 core/pom.xml                                       |  4 +-
 .../secman/shiro/AuthInfoForApplicationUser.java   | 21 +++++++--
 .../secman/shiro/IsisModuleSecurityRealm.java      | 55 ++++++++++++++++------
 .../isis/extensions/secman/shiro/ShiroUtils.java   |  5 ++
 7 files changed, 111 insertions(+), 35 deletions(-)

diff --git a/core/applib/src/test/java/org/apache/isis/commons/internal/memento/MementosTest.java b/core/applib/src/test/java/org/apache/isis/commons/internal/memento/MementosTest.java
index 537336a..5a121ef 100644
--- a/core/applib/src/test/java/org/apache/isis/commons/internal/memento/MementosTest.java
+++ b/core/applib/src/test/java/org/apache/isis/commons/internal/memento/MementosTest.java
@@ -64,7 +64,7 @@ public class MementosTest {
 
             @Override
             public <T> T read(Class<T> cls, Serializable value) {
-                return _Casts.castToOrElseGet(value, cls, ()->null);
+                return _Casts.castToOrElseNull(value, cls);
             }
         };
 
diff --git a/core/commons/src/main/java/org/apache/isis/commons/internal/base/_Casts.java b/core/commons/src/main/java/org/apache/isis/commons/internal/base/_Casts.java
index 2631251..cd433c1 100644
--- a/core/commons/src/main/java/org/apache/isis/commons/internal/base/_Casts.java
+++ b/core/commons/src/main/java/org/apache/isis/commons/internal/base/_Casts.java
@@ -19,8 +19,8 @@
 
 package org.apache.isis.commons.internal.base;
 
+import java.util.Optional;
 import java.util.function.BiFunction;
-import java.util.function.Supplier;
 
 import javax.annotation.Nullable;
 
@@ -48,22 +48,44 @@ public final class _Casts {
     }
 
     /**
-     * Returns the casts of {@code value} to {@code cls}, or if this fails returns the result of {@code orElse}
+     * Casts an object to the class or interface represented by given {@code cls} Class object, 
+     * then wraps the result in an {@link Optional}. The {@link Optional} is empty if the cast 
+     * fails or provided {@code value} is {@code null}.
+     * @param <T>
      * @param value
      * @param cls
-     * @param orElse
-     * @return
+     * @return non-null
      */
-    public static <T> T castToOrElseGet(@Nullable Object value, Class<T> cls, Supplier<T> orElse) {
-
-        try {
-            return requires(cls, "cls").cast(value);
-        } catch (Exception e) {
-            return requires(orElse, "orElse").get();
+    public static <T> Optional<T> castTo(@Nullable Object value, Class<T> cls) {
+        if(value==null) {
+            return Optional.empty();
         }
-
+        requires(cls, "cls");
+        if(cls.isAssignableFrom(value.getClass())) {
+            return Optional.of(cls.cast(value)); 
+        }
+        return Optional.empty();
     }
-
+    
+    /**
+     * Casts an object to the class or interface represented by given {@code cls} Class object. 
+     * Returns {@code null}, if the cast fails or provided {@code value} is {@code null}.
+     * @param <T>
+     * @param value
+     * @param cls
+     * @return casted value, or null
+     */
+    public static @Nullable <T> T castToOrElseNull(@Nullable Object value, Class<T> cls) {
+        if(value==null) {
+            return null;
+        }
+        requires(cls, "cls");
+        if(cls.isAssignableFrom(value.getClass())) {
+            return cls.cast(value); 
+        }
+        return null;
+    }
+    
     /**
      * Dependent on whether left or right can be cast to {@code cls}, the appropriate functional 
      * interface is chosen to produce the result.
@@ -123,4 +145,6 @@ public final class _Casts {
     }
 
 
+
+
 }
diff --git a/core/plugins/security-shiro/src/main/java/org/apache/isis/security/shiro/WebModuleShiro.java b/core/plugins/security-shiro/src/main/java/org/apache/isis/security/shiro/WebModuleShiro.java
index cce7902..ba560a4 100644
--- a/core/plugins/security-shiro/src/main/java/org/apache/isis/security/shiro/WebModuleShiro.java
+++ b/core/plugins/security-shiro/src/main/java/org/apache/isis/security/shiro/WebModuleShiro.java
@@ -18,6 +18,9 @@
  */
 package org.apache.isis.security.shiro;
 
+import java.util.HashMap;
+import java.util.Map;
+
 import javax.inject.Singleton;
 import javax.servlet.FilterRegistration.Dynamic;
 import javax.servlet.ServletContext;
@@ -27,6 +30,7 @@ import javax.servlet.ServletException;
 import org.apache.shiro.config.Ini;
 import org.apache.shiro.web.env.IniWebEnvironment;
 import org.apache.shiro.web.env.WebEnvironment;
+import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;
 import org.springframework.core.Ordered;
 import org.springframework.core.annotation.Order;
 
@@ -78,6 +82,13 @@ public final class WebModuleShiro implements WebModule  {
             } 
             return null;
         }
+        // see https://issues.apache.org/jira/browse/SHIRO-610
+        @Override
+        protected Map<String, Object> getDefaults() {
+            Map<String, Object> defaults = new HashMap<String, Object>();
+            defaults.put(FILTER_CHAIN_RESOLVER_NAME, new PathMatchingFilterChainResolver());
+            return defaults;
+        }
     }
 
     public static void setShiroIniResource(String resourcePath) {
diff --git a/core/pom.xml b/core/pom.xml
index 8eba16c..00e9fc5 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -111,14 +111,12 @@
 		<resteasy4-jaxrs.version>4.0.0.Beta5</resteasy4-jaxrs.version>
 
 		<!-- failing to update to Shiro 1.4.0, running into unresolved issue: https://issues.apache.org/jira/browse/SHIRO-610 -->
-		<shiro.version>1.3.2</shiro.version>
+		<shiro.version>1.4.1</shiro.version>
 
 		<lombok.version>[1.18.,)</lombok.version>
 
 		<joda-time.version>2.9.4</joda-time.version>
 
-		<!-- wicket-guice 8.2.0 requires guice 4.2.2, which requires guava 25.1-android -->
-		<guice.version>4.1.0</guice.version>
 		<guava.version>19.0</guava.version>
 
 		<hamcrest.version>1.3</hamcrest.version>
diff --git a/extensions/secman/realm-shiro/src/main/java/org/apache/isis/extensions/secman/shiro/AuthInfoForApplicationUser.java b/extensions/secman/realm-shiro/src/main/java/org/apache/isis/extensions/secman/shiro/AuthInfoForApplicationUser.java
index a496670..81b7f8b 100644
--- a/extensions/secman/realm-shiro/src/main/java/org/apache/isis/extensions/secman/shiro/AuthInfoForApplicationUser.java
+++ b/extensions/secman/realm-shiro/src/main/java/org/apache/isis/extensions/secman/shiro/AuthInfoForApplicationUser.java
@@ -26,7 +26,10 @@ import org.apache.shiro.authz.Permission;
 import org.apache.shiro.subject.PrincipalCollection;
 import org.apache.shiro.subject.SimplePrincipalCollection;
 
+import org.apache.isis.commons.internal.base._Lazy;
+
 import lombok.Getter;
+import lombok.NonNull;
 import lombok.RequiredArgsConstructor;
 
 @RequiredArgsConstructor
@@ -34,13 +37,13 @@ class AuthInfoForApplicationUser implements AuthenticationInfo, AuthorizationInf
 
     private static final long serialVersionUID = 1L;
 
-    private final PrincipalForApplicationUser principal;
-    private final String realmName;
-    @Getter private final Object credentials;
+    @NonNull private final PrincipalForApplicationUser principal;
+    @NonNull private final String realmName;
+    @NonNull @Getter private final Object credentials;
 
     @Override
     public PrincipalCollection getPrincipals() {
-        return new SimplePrincipalCollection(principal, realmName);
+        return principalCollection.get();
     }
 
     @Override
@@ -57,4 +60,14 @@ class AuthInfoForApplicationUser implements AuthenticationInfo, AuthorizationInf
     public Collection<Permission> getObjectPermissions() {
         return principal.getObjectPermissions();
     }
+    
+    // -- HELPER
+    
+    private final transient _Lazy<PrincipalCollection> principalCollection = 
+            _Lazy.threadSafe(this::createPrincipalCollection);
+    
+    private PrincipalCollection createPrincipalCollection() {
+        return new SimplePrincipalCollection(principal, realmName);
+    }
+    
 }
diff --git a/extensions/secman/realm-shiro/src/main/java/org/apache/isis/extensions/secman/shiro/IsisModuleSecurityRealm.java b/extensions/secman/realm-shiro/src/main/java/org/apache/isis/extensions/secman/shiro/IsisModuleSecurityRealm.java
index 8ba458e..245faf2 100644
--- a/extensions/secman/realm-shiro/src/main/java/org/apache/isis/extensions/secman/shiro/IsisModuleSecurityRealm.java
+++ b/extensions/secman/realm-shiro/src/main/java/org/apache/isis/extensions/secman/shiro/IsisModuleSecurityRealm.java
@@ -83,24 +83,16 @@ public class IsisModuleSecurityRealm extends AuthorizingRealm implements Securit
         
 
         // this code block is just an optimization, entirely optional
-        // we reuse principal information on subjects that are already authenticated
         {
-            val currentSubject = SecurityUtils.getSubject();
-            if(currentSubject!=null && currentSubject.isAuthenticated()) {
-                val authenticatedPrincipalObject = currentSubject.getPrincipal();
-                if(authenticatedPrincipalObject instanceof PrincipalForApplicationUser) {
-                    val authenticatedPrincipal = (PrincipalForApplicationUser) authenticatedPrincipalObject;
-                    val authenticatedUsername = authenticatedPrincipal.getUsername();
-                    val isAuthenticatedWithThisRealm = username.equals(authenticatedUsername);
-                    if(isAuthenticatedWithThisRealm) {
-                        val credentials = token.getCredentials();
-                        val realmName = getName();
-                        return new AuthInfoForApplicationUser(authenticatedPrincipal, realmName, credentials);                        
-                    }
-                }
+            val alreadyAuthenticatedPrincipal = 
+                    getPrincipal_fromAlreadyAuthenticatedSubjectIfApplicable(token);
+            if(alreadyAuthenticatedPrincipal!=null) {
+                val credentials = token.getCredentials();
+                val realmName = getName();
+                return new AuthInfoForApplicationUser(alreadyAuthenticatedPrincipal, realmName, credentials);
             }
         }
-
+        
         // lookup from database, for roles/perms
         val principal = lookupPrincipal_inApplicationUserRepository(username);
 
@@ -165,6 +157,39 @@ public class IsisModuleSecurityRealm extends AuthorizingRealm implements Securit
     }
 
     // -- HELPER
+    
+    /**
+     * This is just an optimization, entirely optional.
+     * <p>
+     * We reuse principal information on subjects that are already authenticated, 
+     * provided we are in a single realm authentication scenario.
+     * @param token
+     * @return {@code null} if not applicable
+     */
+    private PrincipalForApplicationUser getPrincipal_fromAlreadyAuthenticatedSubjectIfApplicable(
+            AuthenticationToken token) {
+        
+        // this optimization is only implemented for the simple case of a single realm setup
+        if(!ShiroUtils.isSingleRealm()) {
+            return null;   
+        }
+        
+        val currentSubject = SecurityUtils.getSubject();
+        if(currentSubject!=null && currentSubject.isAuthenticated()) {
+            val authenticatedPrincipalObject = currentSubject.getPrincipal();
+            if(authenticatedPrincipalObject instanceof PrincipalForApplicationUser) {
+                val authenticatedPrincipal = (PrincipalForApplicationUser) authenticatedPrincipalObject;
+                val authenticatedUsername = authenticatedPrincipal.getUsername();
+                val usernamePasswordToken = (UsernamePasswordToken) token;
+                val username = usernamePasswordToken.getUsername();
+                val isAuthenticatedWithThisRealm = username.equals(authenticatedUsername);
+                if(isAuthenticatedWithThisRealm) {
+                    return authenticatedPrincipal;
+                }
+            }
+        }
+        return null;
+    }
 
     private DisabledAccountException disabledAccountException(String username) {
         return new DisabledAccountException(String.format("username='%s'", username));
diff --git a/extensions/secman/realm-shiro/src/main/java/org/apache/isis/extensions/secman/shiro/ShiroUtils.java b/extensions/secman/realm-shiro/src/main/java/org/apache/isis/extensions/secman/shiro/ShiroUtils.java
index add72a8..fe1669d 100644
--- a/extensions/secman/realm-shiro/src/main/java/org/apache/isis/extensions/secman/shiro/ShiroUtils.java
+++ b/extensions/secman/realm-shiro/src/main/java/org/apache/isis/extensions/secman/shiro/ShiroUtils.java
@@ -56,4 +56,9 @@ final class ShiroUtils {
         return null;
     }
 
+    public static boolean isSingleRealm() {
+        final RealmSecurityManager securityManager = getSecurityManager();
+        return securityManager.getRealms().size() == 1;
+    }
+    
 }