You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@causeway.apache.org by da...@apache.org on 2023/07/23 11:40:25 UTC

[causeway] 01/05: CAUSEWAY-3524: introduce ApplicationFeatureIdTransformer for secman

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

danhaywood pushed a commit to branch CAUSEWAY-3524
in repository https://gitbox.apache.org/repos/asf/causeway.git

commit 3746c62ae520829c7ba5dfed201be0cbc25eb39c
Author: danhaywood <da...@haywood-associates.co.uk>
AuthorDate: Fri Jul 7 19:02:13 2023 +0200

    CAUSEWAY-3524: introduce ApplicationFeatureIdTransformer for secman
    
    to provide an SPI allowing backward compatibility of security perms
---
 antora/supplemental-ui/index.html                  | 59 +++++++++---------
 .../permission/dom/ApplicationPermissionValue.java |  4 ++
 .../CausewayModuleExtSecmanIntegration.java        |  3 +
 .../ApplicationFeatureIdTransformer.java           | 72 ++++++++++++++++++++++
 .../PermissionsEvaluationServiceForSecman.java     | 18 +++++-
 5 files changed, 124 insertions(+), 32 deletions(-)

diff --git a/antora/supplemental-ui/index.html b/antora/supplemental-ui/index.html
index d0892f3889..f7bea3b86f 100644
--- a/antora/supplemental-ui/index.html
+++ b/antora/supplemental-ui/index.html
@@ -72,8 +72,8 @@
       <i class="fa fa-envelope-o"></i> <a href="mailto:users@causeway.apache.org">users@causeway.apache.org</a>
     </div>
     <div class="social-links float-right">
-      <a title="Mailing List" href="docs/2.0.0-RC2/support/mailing-list.html" class="email"><i class="fa fa-at"></i></a>
-      <a title="Slack Channel" href="docs/2.0.0-RC2/support/slack-channel.html" class="slack"><i class="fa fa-slack"></i></a>
+      <a title="Mailing List" href="docs/latest/support/mailing-list.html" class="email"><i class="fa fa-at"></i></a>
+      <a title="Slack Channel" href="docs/latest/support/slack-channel.html" class="slack"><i class="fa fa-slack"></i></a>
       <a title="Bug Reports (Jira)" href="https://issues.apache.org/jira/secure/RapidBoard.jspa?rapidView=87" class="bug"><i class="fa fa-bug"></i></a>
       <a title="Stackoverflow" href="https://stackoverflow.com/questions/tagged/causeway" class="stackoverflow"><i class="fa fa-stack-overflow"></i></a>
       <a title="Twitter" href="https://twitter.com/ApacheCauseway" class="twitter"><i class="fa fa-twitter"></i></a>
@@ -112,23 +112,23 @@
         </li>
         <li><a href="#call-to-action">Quick Start</a></li>
         <li class="menu-has-children">
-          <a href="docs/2.0.0-RC2/about.html">Docs</a>
+          <a href="docs/latest/about.html">Docs</a>
           <ul>
-            <li><a href="docs/2.0.0-RC2/about.html">Table of Contents</a></li>
+            <li><a href="docs/latest/about.html">Table of Contents</a></li>
             <li><hr/></li>
-            <li><a href="docs/2.0.0-RC2/landing-page/guides.html">Guides</a></li>
-            <li><a href="docs/2.0.0-RC2/landing-page/components.html">Components</a></li>
-            <li><a href="extensions/2.0.0-RC2/about.html">Extensions</a></li>
-            <li><a href="valuetypes/2.0.0-RC2/about.html">Value Types</a></li>
+            <li><a href="docs/latest/landing-page/guides.html">Guides</a></li>
+            <li><a href="docs/latest/landing-page/components.html">Components</a></li>
+            <li><a href="extensions/latest/about.html">Extensions</a></li>
+            <li><a href="valuetypes/latest/about.html">Value Types</a></li>
           </ul>
         </li>
         <li class="menu-has-children">
           <a href="#">Download</a>
           <ul>
-            <li><a href="docs/2.0.0-RC2/starters/about.html">Starter Apps</a></li>
-            <li><a href="docs/2.0.0-RC2/downloads/how-to#formal-releases">Source Releases</a></li>
-            <li><a href="docs/2.0.0-RC2/landing-page/releases.html">Binary Releases</a></li>
-            <li><a href="docs/2.0.0-RC2/downloads/how-to#source-code">Source @ Github</a></li>
+            <li><a href="docs/latest/starters/about.html">Starter Apps</a></li>
+            <li><a href="docs/latest/downloads/how-to#formal-releases">Source Releases</a></li>
+            <li><a href="docs/latest/landing-page/releases.html">Binary Releases</a></li>
+            <li><a href="docs/latest/downloads/how-to#source-code">Source @ Github</a></li>
           </ul>
         </li>
         <li class="menu-has-children">
@@ -162,7 +162,7 @@
       <p>
         <a href="#testimonials" class="btn-projects scrollto">&nbsp;Reviews&nbsp;</a>
         <a href="#call-to-action" class="btn-get-started scrollto">Quick Start</a>
-        <a href="docs/2.0.0-RC2/about.html" class="btn-docs">&nbsp;&nbsp;&nbsp;Docs&nbsp;&nbsp;&nbsp;</a>
+        <a href="docs/latest/about.html" class="btn-docs">&nbsp;&nbsp;&nbsp;Docs&nbsp;&nbsp;&nbsp;</a>
       </p>
       <p>
       <div class="nav-panel-search hide-for-print">
@@ -218,7 +218,7 @@
     <div class="container">
       <div class="section-header">
         <h2>Quotes</h2>
-        <p>We gathered together some quotes from current users of the framework.  You can find out who said what <a href="docs/2.0.0-RC2/quotes/quotes.html">here</a>.</p>
+        <p>We gathered together some quotes from current users of the framework.  You can find out who said what <a href="docs/latest/quotes/quotes.html">here</a>.</p>
       </div>
       <div class="owl-carousel testimonials-carousel">
 
@@ -561,7 +561,7 @@
             <div class="col-lg-6 text-left text-lg-left">
               <h4 class="cta-text">Try out ...</h4>
                 <p class="cta-text text-left" style="margin-block-end: 0; ">
-                  Try out the framework with the minimal <a href="docs/2.0.0-RC2/starters/helloworld.html">helloworld</a> starter&nbsp;app.
+                  Try out the framework with the minimal <a href="docs/latest/starters/helloworld.html">helloworld</a> starter&nbsp;app.
                   <p>
                   <p class="cta-text text-left" style="margin-block-end: 0; ">
                     Run it locally (JPA or JDO) using:
@@ -586,7 +586,7 @@ mvn spring-boot:run</pre>
             <div class="col-lg-6 text-left text-lg-left">
                 <h4 class="cta-text">Develop your own ...</h4>
               <p class="cta-text text-left" style="margin-block-end: 0; ">
-                Develop your own app with the structured <a href="docs/2.0.0-RC2/starters/simpleapp.html">simpleapp</a> starter&nbsp;app.
+                Develop your own app with the structured <a href="docs/latest/starters/simpleapp.html">simpleapp</a> starter&nbsp;app.
                 <p>
                 <p class="cta-text text-left" style="margin-block-end: 0; ">
                 Run it locally (JPA or JDO) using:
@@ -656,7 +656,7 @@ docker run -p 8080:8080 apache/causeway-app-demo-jdo:latest</pre>
           <div class="contact-email">
             <i class="ion-ios-filing-outline"></i>
             <h3>Slack</h3>
-            <p><a href="docs/2.0.0-RC2/support/slack-channel.html">Join #apache-causeway <br/>(on the ASF slack workspace)</a></p>
+            <p><a href="docs/latest/support/slack-channel.html">Join #apache-causeway <br/>(on the ASF slack workspace)</a></p>
           </div>
         </div>
 
@@ -793,18 +793,18 @@ docker run -p 8080:8080 apache/causeway-app-demo-jdo:latest</pre>
 
 <script src="https://cdn.jsdelivr.net/npm/docsearch.js@2/dist/cdn/docsearch.min.js"></script>
 <script>
-    function focusSearchInput () { document.querySelector('#algolia-search-input').focus() }
-    var search = docsearch({
-        appId: '5ISP5TFAEN',
-        apiKey: '0fc51c28b4ad46e7318e96d4e97fab7c',
-        indexName: 'causeway-apache-org',
-        inputSelector: '#algolia-search-input',
-        autocompleteOptions: { hint: false, keyboardShortcuts: ['s'] },
-        debug: false,
-    }).autocomplete
-    search.on('autocomplete:closed', function () { search.autocomplete.setVal() })
-    focusSearchInput()
-    window.addEventListener('load', focusSearchInput);
+  function focusSearchInput () { document.querySelector('#algolia-search-input').focus() }
+  var search = docsearch({
+    appId: '5ISP5TFAEN',
+    apiKey: '0fc51c28b4ad46e7318e96d4e97fab7c',
+    indexName: 'causeway-apache-org',
+    inputSelector: '#algolia-search-input',
+    autocompleteOptions: { hint: false, keyboardShortcuts: ['s'] },
+    debug: false,
+  }).autocomplete
+  search.on('autocomplete:closed', function () { search.autocomplete.setVal() })
+  focusSearchInput()
+  window.addEventListener('load', focusSearchInput);
 </script>
 
 <!--
@@ -816,6 +816,5 @@ docker run -p 8080:8080 apache/causeway-app-demo-jdo:latest</pre>
   algoliaOptions: { hitsPerPage: 6 },
 -->
 
-
 </body>
 </html>
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/causeway/extensions/secman/applib/permission/dom/ApplicationPermissionValue.java b/extensions/security/secman/applib/src/main/java/org/apache/causeway/extensions/secman/applib/permission/dom/ApplicationPermissionValue.java
index 3aff46724d..77ed07c631 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/causeway/extensions/secman/applib/permission/dom/ApplicationPermissionValue.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/causeway/extensions/secman/applib/permission/dom/ApplicationPermissionValue.java
@@ -117,6 +117,10 @@ public class ApplicationPermissionValue implements Comparable<ApplicationPermiss
         return false;
     }
 
+    @Programmatic
+    public ApplicationPermissionValue withFeatureId(ApplicationFeatureId applicationFeatureId) {
+        return new ApplicationPermissionValue(applicationFeatureId, this.rule, this.mode);
+    }
 
 
     // -- Comparators
diff --git a/extensions/security/secman/integration/src/main/java/org/apache/causeway/extensions/secman/integration/CausewayModuleExtSecmanIntegration.java b/extensions/security/secman/integration/src/main/java/org/apache/causeway/extensions/secman/integration/CausewayModuleExtSecmanIntegration.java
index 6e45015dcd..b8c8bb0ef3 100644
--- a/extensions/security/secman/integration/src/main/java/org/apache/causeway/extensions/secman/integration/CausewayModuleExtSecmanIntegration.java
+++ b/extensions/security/secman/integration/src/main/java/org/apache/causeway/extensions/secman/integration/CausewayModuleExtSecmanIntegration.java
@@ -18,6 +18,8 @@
  */
 package org.apache.causeway.extensions.secman.integration;
 
+import org.apache.causeway.extensions.secman.integration.permissions.ApplicationFeatureIdTransformer;
+
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Import;
 
@@ -44,6 +46,7 @@ import org.apache.causeway.extensions.secman.integration.userreg.UserRegistratio
         TableColumnVisibilityServiceForSecman.class,
         ImpersonateMenuAdvisorForSecman.class, //not activated by default yet
         PermissionsEvaluationServiceForSecman.class,
+        ApplicationFeatureIdTransformer.Identity.class,
         UserRegistrationServiceForSecman.class,
 
         UserMementoRefinerFromApplicationUser.class,
diff --git a/extensions/security/secman/integration/src/main/java/org/apache/causeway/extensions/secman/integration/permissions/ApplicationFeatureIdTransformer.java b/extensions/security/secman/integration/src/main/java/org/apache/causeway/extensions/secman/integration/permissions/ApplicationFeatureIdTransformer.java
new file mode 100644
index 0000000000..a843a19710
--- /dev/null
+++ b/extensions/security/secman/integration/src/main/java/org/apache/causeway/extensions/secman/integration/permissions/ApplicationFeatureIdTransformer.java
@@ -0,0 +1,72 @@
+/*
+ *  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.causeway.extensions.secman.integration.permissions;
+
+import org.apache.causeway.applib.annotation.PriorityPrecedence;
+import org.apache.causeway.applib.annotation.Programmatic;
+import org.apache.causeway.applib.services.appfeat.ApplicationFeatureId;
+
+import org.apache.causeway.extensions.secman.applib.permission.dom.ApplicationPermissionValue;
+
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Priority;
+
+import java.util.Collection;
+import java.util.stream.Collectors;
+
+/**
+ * Provides an SPI used by {@link PermissionsEvaluationServiceForSecman} that
+ * pre-processes {@link ApplicationFeatureId}s before evaluating them.
+ *
+ * <p>
+ *     The primary use case is to enable backward compatibility; rather than evaluating using the
+ *     logicalTypeName#memberId, an alternative transformer could use the packageName#memberId (as was done in v1).
+ * </p>
+ *
+ */
+public interface ApplicationFeatureIdTransformer {
+
+    @Programmatic
+    ApplicationFeatureId transform(ApplicationFeatureId applicationFeatureId);
+
+    @Programmatic
+    default Collection<ApplicationPermissionValue> transform(Collection<ApplicationPermissionValue> permissionValues) {
+        return permissionValues.stream()
+                .map(apv -> apv.withFeatureId(transform(apv.getFeatureId())))
+                .collect(Collectors.toList());
+    }
+
+    @Service
+    @Priority(PriorityPrecedence.LATE)
+    class Identity implements ApplicationFeatureIdTransformer {
+
+        @Programmatic
+        @Override
+        public ApplicationFeatureId transform(ApplicationFeatureId applicationFeatureId) {
+            return applicationFeatureId;
+        }
+
+        @Programmatic
+        @Override
+        public Collection<ApplicationPermissionValue> transform(Collection<ApplicationPermissionValue> permissionValues) {
+            return permissionValues;
+        }
+    }
+}
diff --git a/extensions/security/secman/integration/src/main/java/org/apache/causeway/extensions/secman/integration/permissions/PermissionsEvaluationServiceForSecman.java b/extensions/security/secman/integration/src/main/java/org/apache/causeway/extensions/secman/integration/permissions/PermissionsEvaluationServiceForSecman.java
index 3496bb1b6a..b0991ec9fc 100644
--- a/extensions/security/secman/integration/src/main/java/org/apache/causeway/extensions/secman/integration/permissions/PermissionsEvaluationServiceForSecman.java
+++ b/extensions/security/secman/integration/src/main/java/org/apache/causeway/extensions/secman/integration/permissions/PermissionsEvaluationServiceForSecman.java
@@ -20,6 +20,7 @@ package org.apache.causeway.extensions.secman.integration.permissions;
 
 import java.util.Collection;
 import java.util.Collections;
+import java.util.List;
 import java.util.Optional;
 
 import javax.inject.Inject;
@@ -71,9 +72,12 @@ implements PermissionsEvaluationService {
 
     @Override
     public ApplicationPermissionValueSet.Evaluation evaluate(
-            final ApplicationFeatureId targetMemberId,
+            final ApplicationFeatureId targetMemberIdInput,
             final ApplicationPermissionMode mode,
-            final Collection<ApplicationPermissionValue> permissionValues) {
+            final Collection<ApplicationPermissionValue> permissionValuesInput) {
+
+        final ApplicationFeatureId targetMemberId = applicationFeatureIdTransformer().transform(targetMemberIdInput);
+        final Collection<ApplicationPermissionValue> permissionValues = applicationFeatureIdTransformer().transform(permissionValuesInput);
 
         if(_NullSafe.isEmpty(permissionValues)) {
             return null;
@@ -112,4 +116,14 @@ implements PermissionsEvaluationService {
         throw _Exceptions.illegalArgument("PermissionsEvaluationPolicy '%s' not recognised", policy);
     }
 
+    private ApplicationFeatureIdTransformer applicationFeatureIdTransformerCached;
+    ApplicationFeatureIdTransformer applicationFeatureIdTransformer() {
+        if (applicationFeatureIdTransformerCached == null) {
+            applicationFeatureIdTransformerCached = applicationFeatureIdTransformers == null || applicationFeatureIdTransformers.isEmpty() ? new ApplicationFeatureIdTransformer.Identity() : applicationFeatureIdTransformers.get(0);
+        }
+        return applicationFeatureIdTransformerCached;
+    };
+
+    @Inject List<ApplicationFeatureIdTransformer> applicationFeatureIdTransformers;
+
 }