This is an automated email from the ASF dual-hosted git repository. ahuber pushed a commit to branch jpa3-SNAPSHOT in repository https://gitbox.apache.org/repos/asf/causeway-app-helloworld.git
commit 8067a41e4da743d2df7569e9dd07b0b5f47f0bab Author: Andi Huber <[email protected]> AuthorDate: Wed Mar 20 15:18:53 2024 +0100 use SimpleAuth as Shiro replacement --- pom.xml | 11 +-- .../modules/hello/dom/hwo/HelloWorldObject.java | 28 +++--- .../modules/hello/dom/hwo/HelloWorldObjects.java | 6 +- src/main/java/domainapp/webapp/AppManifest.java | 3 +- src/main/java/domainapp/webapp/HelloWorldApp.java | 51 +++++++++- src/main/java/domainapp/webapp/SimpleAuth.java | 106 +++++++++++++++++++++ src/main/resources/shiro.ini | 49 ---------- 7 files changed, 179 insertions(+), 75 deletions(-) diff --git a/pom.xml b/pom.xml index cad7745..51818b9 100644 --- a/pom.xml +++ b/pom.xml @@ -7,20 +7,19 @@ <parent> <groupId>org.apache.causeway.app</groupId> <artifactId>causeway-app-starter-parent</artifactId> - <version>2.0.0-SNAPSHOT</version> + <version>3.0.0-SNAPSHOT</version> <relativePath/> </parent> <artifactId>helloworld-jpa</artifactId> <groupId>org.apache.causeway.starters</groupId> - <version>2.0.0-SNAPSHOT</version> <name>HelloWorld (JPA)</name> <packaging>jar</packaging> <properties> - <java.version>11</java.version> + <java.version>21</java.version> <!-- https://stackoverflow.com/questions/38983934/cannot-get-maven-project-version-property-in-a-spring-application-with-value/38983935#38983935 --> <resource.delimiter>^</resource.delimiter> </properties> @@ -88,7 +87,7 @@ <dependency> <groupId>org.apache.causeway.security</groupId> - <artifactId>causeway-security-shiro</artifactId> + <artifactId>causeway-security-bypass</artifactId> </dependency> <dependency> @@ -115,7 +114,7 @@ <groupId>org.springframework</groupId> <artifactId>spring-instrument</artifactId> </dependency> - + </dependencies> <profiles> @@ -133,7 +132,7 @@ <artifactId>jib-maven-plugin</artifactId> <configuration> <from> - <image>adoptopenjdk/openjdk11:x86_64-alpine-jre-11.0.10_9</image> + <image>openjdk:21-jdk-bookworm</image> </from> <container> <jvmFlags> diff --git a/src/main/java/domainapp/modules/hello/dom/hwo/HelloWorldObject.java b/src/main/java/domainapp/modules/hello/dom/hwo/HelloWorldObject.java index b93ae56..2d96194 100644 --- a/src/main/java/domainapp/modules/hello/dom/hwo/HelloWorldObject.java +++ b/src/main/java/domainapp/modules/hello/dom/hwo/HelloWorldObject.java @@ -2,18 +2,18 @@ package domainapp.modules.hello.dom.hwo; import java.util.Comparator; -import javax.inject.Inject; -import javax.inject.Named; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.EntityListeners; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Table; -import javax.persistence.Transient; -import javax.persistence.UniqueConstraint; -import javax.persistence.Version; +import jakarta.inject.Inject; +import jakarta.inject.Named; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EntityListeners; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import jakarta.persistence.Transient; +import jakarta.persistence.UniqueConstraint; +import jakarta.persistence.Version; import org.apache.causeway.applib.annotation.Action; import org.apache.causeway.applib.annotation.ActionLayout; @@ -73,7 +73,7 @@ public class HelloWorldObject implements Comparable<HelloWorldObject> { public String getName() { return name; } - public void setName(String name) { + public void setName(final String name) { this.name = name; } @@ -86,7 +86,7 @@ public class HelloWorldObject implements Comparable<HelloWorldObject> { public String getNotes() { return notes; } - public void setNotes(String notes) { + public void setNotes(final String notes) { this.notes = notes; } diff --git a/src/main/java/domainapp/modules/hello/dom/hwo/HelloWorldObjects.java b/src/main/java/domainapp/modules/hello/dom/hwo/HelloWorldObjects.java index b155532..8e2bc7e 100644 --- a/src/main/java/domainapp/modules/hello/dom/hwo/HelloWorldObjects.java +++ b/src/main/java/domainapp/modules/hello/dom/hwo/HelloWorldObjects.java @@ -2,9 +2,9 @@ package domainapp.modules.hello.dom.hwo; import java.util.List; -import javax.annotation.Priority; -import javax.inject.Inject; -import javax.inject.Named; +import jakarta.annotation.Priority; +import jakarta.inject.Inject; +import jakarta.inject.Named; import org.apache.causeway.applib.annotation.Action; import org.apache.causeway.applib.annotation.ActionLayout; diff --git a/src/main/java/domainapp/webapp/AppManifest.java b/src/main/java/domainapp/webapp/AppManifest.java index 1ad5341..0e5fb3a 100644 --- a/src/main/java/domainapp/webapp/AppManifest.java +++ b/src/main/java/domainapp/webapp/AppManifest.java @@ -10,7 +10,6 @@ import org.apache.causeway.applib.CausewayModuleApplibMixins; import org.apache.causeway.core.config.presets.CausewayPresets; import org.apache.causeway.core.runtimeservices.CausewayModuleCoreRuntimeServices; import org.apache.causeway.persistence.jpa.eclipselink.CausewayModulePersistenceJpaEclipselink; -import org.apache.causeway.security.shiro.CausewayModuleSecurityShiro; import org.apache.causeway.testing.h2console.ui.CausewayModuleTestingH2ConsoleUi; import org.apache.causeway.viewer.restfulobjects.jaxrsresteasy.CausewayModuleViewerRestfulObjectsJaxrsResteasy; import org.apache.causeway.viewer.wicket.applib.CausewayModuleViewerWicketApplibMixins; @@ -24,7 +23,7 @@ import domainapp.modules.hello.HelloWorldModule; CausewayModuleApplibChangeAndExecutionLoggers.class, CausewayModuleCoreRuntimeServices.class, - CausewayModuleSecurityShiro.class, + //CausewayModuleSecurityXxx.class, CausewayModulePersistenceJpaEclipselink.class, CausewayModuleViewerRestfulObjectsJaxrsResteasy.class, CausewayModuleViewerWicketApplibMixins.class, diff --git a/src/main/java/domainapp/webapp/HelloWorldApp.java b/src/main/java/domainapp/webapp/HelloWorldApp.java index 9ad441f..a67f5fa 100644 --- a/src/main/java/domainapp/webapp/HelloWorldApp.java +++ b/src/main/java/domainapp/webapp/HelloWorldApp.java @@ -1,19 +1,68 @@ package domainapp.webapp; +import java.util.List; + import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.apache.causeway.core.config.presets.CausewayPresets; +import domainapp.webapp.HelloWorldApp.SimpleAuthSetup; +import domainapp.webapp.SimpleAuth.Realm; + @SpringBootApplication @Import({ + SimpleAuthSetup.class, + SimpleAuth.class, AppManifest.class, }) public class HelloWorldApp extends SpringBootServletInitializer { - public static void main(String[] args) { + @Configuration + static class SimpleAuthSetup { + + /* migrated from shiro.ini + [users] + sven = pass, admin_role + dick = pass, hello_role, default_role + bob = pass, hello_role, default_role, fixtures_role + joe = pass, hello_role, default_role + + [roles] + hello_role = *:HelloWorldObjects:*:*,\ + *:HelloWorldObject:*:* + admin_role = * + default_role = causeway.applib,\ + causeway.security + fixtures_role = causeway.testing.fixtures + features_role = causeway.feat + metamodel_role = causeway.metamodel + h2_role = causeway.ext.h2Console + jdo_role = causeway.persistence.jdo + swagger_role = causeway.viewer.restfulobjects + conf_role = causeway.conf + sudo_role = causeway.sudo + */ + @Bean + public SimpleAuth.Realm simpleAuthRealm() { + return new Realm() + .addRole("hello_role", id->id.getFullIdentityString().contains("HelloWorldObject")) + .addRole("admin_role", id->true) + .addRole("default_role", id->id.getFullIdentityString().startsWith("causeway.applib") + || id.getFullIdentityString().startsWith("causeway.security")) + .addRole("fixtures_role", id->id.getFullIdentityString().startsWith("causeway..testing.fixtures")) + .addUser("sven", "pass", List.of("admin_role")) + .addUser("dick", "pass", List.of("hello_role", "default_role")) + .addUser("bob", "pass", List.of("hello_role", "default_role", "fixtures_role")) + .addUser("joe", "pass", List.of("hello_role", "default_role")); + } + } + + public static void main(final String[] args) { CausewayPresets.prototyping(); // or run with use -DPROTOTYPING=true SpringApplication.run(new Class[] { HelloWorldApp.class }, args); } diff --git a/src/main/java/domainapp/webapp/SimpleAuth.java b/src/main/java/domainapp/webapp/SimpleAuth.java new file mode 100644 index 0000000..dd52a4a --- /dev/null +++ b/src/main/java/domainapp/webapp/SimpleAuth.java @@ -0,0 +1,106 @@ +package domainapp.webapp; + +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.Stream; + +import jakarta.inject.Inject; + +import org.springframework.stereotype.Component; + +import org.apache.causeway.applib.Identifier; +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.core.security.authorization.Authorizor; + +@Component +public class SimpleAuth implements Authenticator, Authorizor { + + static class Realm { + static record Role(String name, Predicate<Identifier> grantsRead, Predicate<Identifier> grantsChange) { + } + static record User(String name, String pass, List<Role> roles) { + } + final Map<String, Role> roles = new HashMap<>(); + final Map<String, User> users = new HashMap<>(); + public Realm addRole(final String name, final Predicate<Identifier> grants) { + roles.put(name, new Role(name, grants, grants)); + return this; + } + public Realm addUser(final String name, final String pass, final List<String> roleNames) { + users.put(name, new User(name, pass, roleNames.stream().map(roles::get).toList())); + return this; + } + } + + @Inject Realm realm; + + /** + * Default implementation returns a validated {@link InteractionContext}; can be overridden + * if required. + */ + @Override + public final InteractionContext authenticate( + final AuthenticationRequest request, + final String validationCode) { + + if(request instanceof AuthenticationRequestPassword pwdRequest) { + if (!isValid(pwdRequest)) { + return null; + } + } + + var user = UserMemento.ofNameAndRoleNames(request.getName(), Stream.concat( + request.streamRoles(), + realm.users.get(request.getName()) + .roles() + .stream() + .map(Realm.Role::name)) + ) + .withAuthenticationCode(validationCode); + return InteractionContext.ofUserWithSystemDefaults(user); + } + + @Override + public final void logout() { + // no-op + } + + @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)); + } + + @Override + public final boolean canAuthenticate(final Class<? extends AuthenticationRequest> authenticationRequestClass) { + return AuthenticationRequestPassword.class.isAssignableFrom(authenticationRequestClass); + } + + protected boolean isValid(final AuthenticationRequestPassword request) { + return Optional.ofNullable(realm.users.get(request.getName())) + .map(Realm.User::pass) + .map(pass->pass.equals(request.getPassword())) + .orElse(false); + } + + protected List<Realm.Role> roles(final UserMemento userMemento){ + var roles = Optional.ofNullable(realm.users.get(userMemento.getName())) + .map(Realm.User::roles) + .orElseGet(List::of); + return roles; + } + +} diff --git a/src/main/resources/shiro.ini b/src/main/resources/shiro.ini deleted file mode 100644 index 30faa89..0000000 --- a/src/main/resources/shiro.ini +++ /dev/null @@ -1,49 +0,0 @@ -[main] - -# to use .ini file -securityManager.realms = $iniRealm - - - -# ----------------------------------------------------------------------------- -# Users and their assigned roles -# -# Each line conforms to the format defined in the -# org.apache.shiro.realm.text.TextConfigurationRealm#setUserDefinitions JavaDoc -# ----------------------------------------------------------------------------- - -[users] -# user = password, role1, role2, role3, ... - - -sven = pass, admin_role -dick = pass, hello_role, default_role -bob = pass, hello_role, default_role, fixtures_role -joe = pass, hello_role, default_role - - - -# ----------------------------------------------------------------------------- -# Roles with assigned permissions -# -# Each line conforms to the format defined in the -# org.apache.shiro.realm.text.TextConfigurationRealm#setRoleDefinitions JavaDoc -# ----------------------------------------------------------------------------- - -[roles] -# role = perm1, perm2, perm3, ... -# perm in format: logicalTypeNamespace:logicalTypeSimpleName:memberName:r,w - -hello_role = *:HelloWorldObjects:*:*,\ - *:HelloWorldObject:*:* -admin_role = * -default_role = causeway.applib,\ - causeway.security -fixtures_role = causeway.testing.fixtures -features_role = causeway.feat -metamodel_role = causeway.metamodel -h2_role = causeway.ext.h2Console -jdo_role = causeway.persistence.jdo -swagger_role = causeway.viewer.restfulobjects -conf_role = causeway.conf -sudo_role = causeway.sudo
