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

ahuber pushed a commit to branch 3698-security.simpleauth
in repository https://gitbox.apache.org/repos/asf/causeway.git

commit 6eab0d3ed74404556d3c84962a92c67d46d5e99a
Author: Andi Huber <[email protected]>
AuthorDate: Thu Mar 21 09:51:01 2024 +0100

    CAUSEWAY-3698: adds new Simple Auth as new module
---
 core/pom.xml                                       |  1 +
 security/simple/pom.xml                            | 49 +++++++++++
 security/simple/src/main/adoc/antora.yml           | 19 +++++
 .../simple/src/main/adoc/modules/simple/nav.adoc   |  4 +
 .../src/main/adoc/modules/simple/pages/about.adoc  | 38 +++++++++
 .../adoc/modules/simple/partials/module-nav.adoc   |  5 ++
 security/simple/src/main/java/module-info.java     | 32 ++++++++
 .../simple/CausewayModuleSecuritySimple.java       | 47 +++++++++++
 .../simple/authentication/SimpleAuthenticator.java | 94 ++++++++++++++++++++++
 .../simple/authorization/SimpleAuthorizor.java     | 69 ++++++++++++++++
 .../security/simple/realm/SimpleRealm.java         | 79 ++++++++++++++++++
 11 files changed, 437 insertions(+)

diff --git a/core/pom.xml b/core/pom.xml
index a4fce58e1e..0c6752308d 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -301,6 +301,7 @@
                <module>../security/bypass</module>
                <module>../security/keycloak</module>
                <module>../security/shiro</module>
+               <module>../security/simple</module>
                <module>../security/spring</module>
 
                <module>../viewers/commons</module>
diff --git a/security/simple/pom.xml b/security/simple/pom.xml
new file mode 100644
index 0000000000..4108f31da6
--- /dev/null
+++ b/security/simple/pom.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/maven-v4_0_0.xsd";>
+       <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.causeway.core</groupId>
+        <artifactId>causeway-core</artifactId>
+        <version>2.0.0-SNAPSHOT</version>
+        <relativePath>../../core/pom.xml</relativePath>
+    </parent>
+
+    <groupId>org.apache.causeway.security</groupId>
+    <artifactId>causeway-security-simple</artifactId>
+    <name>Apache Causeway Security - Simple</name>
+
+    <properties>
+        
<jar-plugin.automaticModuleName>org.apache.causeway.security.simple</jar-plugin.automaticModuleName>
+        
<git-plugin.propertiesDir>org/apache/causeway/security/simple</git-plugin.propertiesDir>
+    </properties>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>org.apache.causeway.core</groupId>
+            <artifactId>causeway-core-runtimeservices</artifactId>
+        </dependency>
+        
+    </dependencies>
+
+
+</project>
diff --git a/security/simple/src/main/adoc/antora.yml 
b/security/simple/src/main/adoc/antora.yml
new file mode 100644
index 0000000000..2be11515fd
--- /dev/null
+++ b/security/simple/src/main/adoc/antora.yml
@@ -0,0 +1,19 @@
+#  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.
+
+name: security
+version: latest
diff --git a/security/simple/src/main/adoc/modules/simple/nav.adoc 
b/security/simple/src/main/adoc/modules/simple/nav.adoc
new file mode 100644
index 0000000000..ae4837e12f
--- /dev/null
+++ b/security/simple/src/main/adoc/modules/simple/nav.adoc
@@ -0,0 +1,4 @@
+
+:Notice: 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 ag [...]
+
+include::security:ROOT:partial$component-nav.adoc[]
diff --git a/security/simple/src/main/adoc/modules/simple/pages/about.adoc 
b/security/simple/src/main/adoc/modules/simple/pages/about.adoc
new file mode 100644
index 0000000000..3c0e4dd911
--- /dev/null
+++ b/security/simple/src/main/adoc/modules/simple/pages/about.adoc
@@ -0,0 +1,38 @@
+= Simple (Authenticator & Authorizor)
+
+:Notice: 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 ag [...]
+:page-partial:
+
+TODO[CAUSEWAY-3698] yet is just a copy from bypass
+
+The bypass implementation, as its name suggests, allows both authentication 
and authorization to be bypassed.
+This is typically used for integration tests or for prototyping.
+
+
+include::docs:mavendeps:partial$setup-and-configure-mavendeps-webapp.adoc[leveloffset=+1]
+
+
+
+== Update AppManifest
+
+In your application's `AppManifest` (top-level Spring `@Configuration` used to 
bootstrap the app), import the
+
+[source,java]
+.AppManifest.java
+----
+@Configuration
+@Import({
+        ...
+        CausewayModuleSecurityBypass.class,
+        ...
+})
+public class AppManifest {
+}
+----
+
+Make sure that no other `CausewayModuleSecurityXxx` module is imported.
+
+
+== In Use
+
+With bypass installed, you should be able to login with any credentials.
diff --git 
a/security/simple/src/main/adoc/modules/simple/partials/module-nav.adoc 
b/security/simple/src/main/adoc/modules/simple/partials/module-nav.adoc
new file mode 100644
index 0000000000..05fac24728
--- /dev/null
+++ b/security/simple/src/main/adoc/modules/simple/partials/module-nav.adoc
@@ -0,0 +1,5 @@
+
+
+
+* xref:security:simple:about.adoc[Simple (Authenticator & Authorizor)]
+
diff --git a/security/simple/src/main/java/module-info.java 
b/security/simple/src/main/java/module-info.java
new file mode 100644
index 0000000000..7b14242f9d
--- /dev/null
+++ b/security/simple/src/main/java/module-info.java
@@ -0,0 +1,32 @@
+/*
+ *  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.
+ */
+module org.apache.causeway.security.simple {
+    exports org.apache.causeway.security.simple;
+    exports org.apache.causeway.security.simple.authentication;
+    exports org.apache.causeway.security.simple.authorization;
+    exports org.apache.causeway.security.simple.realm;
+
+    requires org.apache.causeway.applib;
+    requires org.apache.causeway.security.api;
+    requires org.apache.causeway.core.runtimeservices;
+    requires java.annotation;
+    requires java.inject;
+    requires spring.beans;
+    requires spring.context;
+}
\ No newline at end of file
diff --git 
a/security/simple/src/main/java/org/apache/causeway/security/simple/CausewayModuleSecuritySimple.java
 
b/security/simple/src/main/java/org/apache/causeway/security/simple/CausewayModuleSecuritySimple.java
new file mode 100644
index 0000000000..47fbe65591
--- /dev/null
+++ 
b/security/simple/src/main/java/org/apache/causeway/security/simple/CausewayModuleSecuritySimple.java
@@ -0,0 +1,47 @@
+/*
+ *  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.security.simple;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+
+import 
org.apache.causeway.core.runtimeservices.CausewayModuleCoreRuntimeServices;
+import org.apache.causeway.security.simple.authentication.SimpleAuthenticator;
+import org.apache.causeway.security.simple.authorization.SimpleAuthorizor;
+
+/**
+ * Simple in-memory authorization and authentication module.
+ *
+ * @since 2.x {@index}
+ */
+@Configuration
+@Import({
+        // modules
+        CausewayModuleCoreRuntimeServices.class,
+
+        // @Service's
+        SimpleAuthenticator.class,
+        SimpleAuthorizor.class,
+
+})
+public class CausewayModuleSecuritySimple {
+
+    public static final String NAMESPACE = "causeway.security.simple";
+
+}
diff --git 
a/security/simple/src/main/java/org/apache/causeway/security/simple/authentication/SimpleAuthenticator.java
 
b/security/simple/src/main/java/org/apache/causeway/security/simple/authentication/SimpleAuthenticator.java
new file mode 100644
index 0000000000..409f1d0a34
--- /dev/null
+++ 
b/security/simple/src/main/java/org/apache/causeway/security/simple/authentication/SimpleAuthenticator.java
@@ -0,0 +1,94 @@
+/*
+ *  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.security.simple.authentication;
+
+import java.util.stream.Stream;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.stereotype.Service;
+
+import org.apache.causeway.applib.annotation.PriorityPrecedence;
+import org.apache.causeway.applib.services.iactnlayer.InteractionContext;
+import org.apache.causeway.applib.services.user.UserMemento;
+import org.apache.causeway.core.security.authentication.AuthenticationRequest;
+import 
org.apache.causeway.core.security.authentication.AuthenticationRequestPassword;
+import org.apache.causeway.core.security.authentication.Authenticator;
+import org.apache.causeway.security.simple.CausewayModuleSecuritySimple;
+import org.apache.causeway.security.simple.realm.SimpleRealm;
+
+/**
+ * Simple in-memory {@link Authenticator} implementation.
+ *
+ * @since 2.x {@index}
+ */
+@Service
+@Named(CausewayModuleSecuritySimple.NAMESPACE + ".SimpleAuthenticator")
[email protected](PriorityPrecedence.LATE - 10) // ensure earlier 
than bypass
+@Qualifier("Simple")
+public class SimpleAuthenticator implements Authenticator {
+
+    @Inject protected SimpleRealm realm;
+
+    @Override
+    public final boolean canAuthenticate(final Class<? extends 
AuthenticationRequest> authenticationRequestClass) {
+        return 
AuthenticationRequestPassword.class.isAssignableFrom(authenticationRequestClass);
+    }
+
+    @Override
+    public final InteractionContext authenticate(
+            final AuthenticationRequest request,
+            final String validationCode) {
+
+        if(request instanceof AuthenticationRequestPassword) {
+            if (!isValid((AuthenticationRequestPassword) request)) {
+                return null;
+            }
+        } else {
+            return null; // request type not supported
+        }
+
+        var user = UserMemento.ofNameAndRoleNames(request.getName(), 
Stream.concat(
+                    request.streamRoles(),
+                    realm.lookupUserByName(request.getName()).orElseThrow()
+                        .roles()
+                        .stream()
+                        .map(SimpleRealm.Role::name))
+                )
+                .withAuthenticationCode(validationCode);
+        return InteractionContext.ofUserWithSystemDefaults(user);
+    }
+
+    @Override
+    public final void logout() {
+        // no-op
+    }
+
+    protected boolean isValid(final AuthenticationRequestPassword request) {
+        return realm.lookupUserByName(request.getName())
+            .map(SimpleRealm.User::pass)
+            .map(pass->pass.equals(request.getPassword()))
+            .orElse(false);
+    }
+
+}
+
+
diff --git 
a/security/simple/src/main/java/org/apache/causeway/security/simple/authorization/SimpleAuthorizor.java
 
b/security/simple/src/main/java/org/apache/causeway/security/simple/authorization/SimpleAuthorizor.java
new file mode 100644
index 0000000000..7233ed6c4c
--- /dev/null
+++ 
b/security/simple/src/main/java/org/apache/causeway/security/simple/authorization/SimpleAuthorizor.java
@@ -0,0 +1,69 @@
+/*
+ *  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.security.simple.authorization;
+
+import java.util.List;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.stereotype.Service;
+
+import org.apache.causeway.applib.Identifier;
+import org.apache.causeway.applib.annotation.PriorityPrecedence;
+import org.apache.causeway.applib.services.iactnlayer.InteractionContext;
+import org.apache.causeway.applib.services.user.UserMemento;
+import org.apache.causeway.core.security.authorization.Authorizor;
+import org.apache.causeway.security.simple.CausewayModuleSecuritySimple;
+import org.apache.causeway.security.simple.realm.SimpleRealm;
+
+/**
+ * Simple in-memory {@link Authorizor} implementation.
+ *
+ * @since 2.x {@index}
+ */
+@Service
+@Named(CausewayModuleSecuritySimple.NAMESPACE + ".SimpleAuthorizor")
[email protected](PriorityPrecedence.LATE - 10) // ensure earlier 
than bypass
+@Qualifier("Simple")
+public class SimpleAuthorizor implements Authorizor {
+
+    @Inject protected SimpleRealm realm;
+
+    @Override
+    public boolean isVisible(final InteractionContext ctx, final Identifier 
identifier) {
+        return roles(ctx.getUser()).stream()
+            .anyMatch(role->role.grantsRead().test(identifier));
+    }
+
+    @Override
+    public boolean isUsable(final InteractionContext ctx, final Identifier 
identifier) {
+        return roles(ctx.getUser()).stream()
+                .anyMatch(role->role.grantsChange().test(identifier));
+    }
+
+    protected List<SimpleRealm.Role> roles(final UserMemento userMemento){
+        var roles = realm.lookupUserByName(userMemento.getName())
+                .map(SimpleRealm.User::roles)
+                .orElseGet(List::of);
+        return roles;
+    }
+
+}
diff --git 
a/security/simple/src/main/java/org/apache/causeway/security/simple/realm/SimpleRealm.java
 
b/security/simple/src/main/java/org/apache/causeway/security/simple/realm/SimpleRealm.java
new file mode 100644
index 0000000000..c8e8c4cab6
--- /dev/null
+++ 
b/security/simple/src/main/java/org/apache/causeway/security/simple/realm/SimpleRealm.java
@@ -0,0 +1,79 @@
+/*
+ *  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.security.simple.realm;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+import org.springframework.lang.Nullable;
+import org.springframework.stereotype.Component;
+
+import org.apache.causeway.applib.Identifier;
+import org.apache.causeway.commons.internal.base._Strings;
+
+import lombok.Value;
+import lombok.experimental.Accessors;
+
+@Component
+public class SimpleRealm {
+
+    @Value @Accessors(fluent=true)
+    public static class Role {
+        String name;
+        Predicate<Identifier> grantsRead;
+        Predicate<Identifier> grantsChange;
+    }
+    @Value @Accessors(fluent=true)
+    public static class User {
+        String name;
+        String pass; //TODO[CAUSEWAY-3698] use hash or predicate
+        List<Role> roles;
+    }
+    final Map<String, Role> roles = new HashMap<>();
+    final Map<String, User> users = new HashMap<>();
+    public SimpleRealm addRoleWithReadAndChange(final String name, final 
Predicate<Identifier> grants) {
+        roles.put(name, new Role(name, grants, grants));
+        return this;
+    }
+    public SimpleRealm addRoleWithReadOnly(final String name, final 
Predicate<Identifier> grants) {
+        roles.put(name, new Role(name, grants, id->false));
+        return this;
+    }
+    public SimpleRealm addUser(final String name, final String pass, final 
List<String> roleNames) {
+        users.put(name, new User(name, pass, 
roleNames.stream().map(roles::get).collect(Collectors.toList())));
+        return this;
+    }
+
+    public final Optional<Role> lookupRoleByName(@Nullable final String 
roleName) {
+        return _Strings.isNullOrEmpty(roleName)
+                ? Optional.empty()
+                : Optional.ofNullable(roles.get(roleName));
+    }
+
+    public final Optional<User> lookupUserByName(@Nullable final String 
userName) {
+        return _Strings.isNullOrEmpty(userName)
+                ? Optional.empty()
+                : Optional.ofNullable(users.get(userName));
+    }
+
+}

Reply via email to