From 3914ca51949d84fc921f72e8eb07a558f6844c13 Mon Sep 17 00:00:00 2001 From: Mauro Mugnaini Date: Mon, 16 Nov 2020 23:35:08 +0100 Subject: [PATCH] Added `delete-account` sub-module waiting for the official support in KC upcoming version. Theme resources are included in the module and are to moved in the specific theme project/repository when tested in DEV. --- delete-account/pom.xml | 30 ++++++ .../keycloak/account/DeleteAccountEvent.java | 38 ++++++++ .../DeleteAccountRealmResourceProvider.java | 27 ++++++ ...teAccountRealmResourceProviderFactory.java | 41 ++++++++ .../account/DeleteAccountResource.java | 66 +++++++++++++ .../resources/META-INF/keycloak-themes.json | 10 ++ ...ices.resource.RealmResourceProviderFactory | 1 + .../theme/delete-account/account/account.ftl | 91 ++++++++++++++++++ .../account/messages/messages_en.properties | 2 + .../account/resources/img/delete-user.png | Bin 0 -> 3414 bytes .../delete-account/account/theme.properties | 1 + keycloak-d4science-bundle/pom.xml | 12 +++ .../META-INF/jboss-deployment-structure.xml | 13 +++ pom.xml | 1 + 14 files changed, 333 insertions(+) create mode 100644 delete-account/pom.xml create mode 100644 delete-account/src/main/java/org/gcube/keycloak/account/DeleteAccountEvent.java create mode 100644 delete-account/src/main/java/org/gcube/keycloak/account/DeleteAccountRealmResourceProvider.java create mode 100644 delete-account/src/main/java/org/gcube/keycloak/account/DeleteAccountRealmResourceProviderFactory.java create mode 100644 delete-account/src/main/java/org/gcube/keycloak/account/DeleteAccountResource.java create mode 100644 delete-account/src/main/resources/META-INF/keycloak-themes.json create mode 100644 delete-account/src/main/resources/META-INF/services/org.keycloak.services.resource.RealmResourceProviderFactory create mode 100644 delete-account/src/main/resources/theme/delete-account/account/account.ftl create mode 100644 delete-account/src/main/resources/theme/delete-account/account/messages/messages_en.properties create mode 100644 delete-account/src/main/resources/theme/delete-account/account/resources/img/delete-user.png create mode 100644 delete-account/src/main/resources/theme/delete-account/account/theme.properties diff --git a/delete-account/pom.xml b/delete-account/pom.xml new file mode 100644 index 0000000..35a50d5 --- /dev/null +++ b/delete-account/pom.xml @@ -0,0 +1,30 @@ + + + 4.0.0 + + + org.gcube + keycloak-d4science-spi-parent + 0.2.0-SNAPSHOT + + + delete-account + jar + + + + org.gcube.common + event-publisher-library + [1.0.0-SNAPSHOT, 2.0.0-SNAPSHOT) + + + org.gcube + event-listener-provider + ${project.version} + provided + + + + \ No newline at end of file diff --git a/delete-account/src/main/java/org/gcube/keycloak/account/DeleteAccountEvent.java b/delete-account/src/main/java/org/gcube/keycloak/account/DeleteAccountEvent.java new file mode 100644 index 0000000..7bdc294 --- /dev/null +++ b/delete-account/src/main/java/org/gcube/keycloak/account/DeleteAccountEvent.java @@ -0,0 +1,38 @@ +package org.gcube.keycloak.account; + +import org.gcube.event.publisher.Event; +import org.gcube.keycloak.event.KeycloakEvent; +import org.keycloak.models.RealmModel; +import org.keycloak.models.UserModel; + +public class DeleteAccountEvent extends Event { + + private static final long serialVersionUID = -4519708230339210727L; + + public static final String NAME = "delete-user-account"; + + public static final String REALM = "realm"; + public static final String USER_ID = "userid"; + + public DeleteAccountEvent(UserModel userModel, RealmModel realmModel) { + super(NAME, KeycloakEvent.TYPE, KeycloakEvent.HOST_NAME); + setUserId(userModel.getId()); + setRealm(realmModel.getName()); + } + + public void setUserId(String userid) { + set(USER_ID, userid); + } + + public String getUserId() { + return (String) get(USER_ID); + } + public void setRealm(String realm) { + set(KeycloakEvent.REALM, realm); + } + + public String getRealm() { + return (String) get(KeycloakEvent.REALM); + } + +} diff --git a/delete-account/src/main/java/org/gcube/keycloak/account/DeleteAccountRealmResourceProvider.java b/delete-account/src/main/java/org/gcube/keycloak/account/DeleteAccountRealmResourceProvider.java new file mode 100644 index 0000000..ba4903c --- /dev/null +++ b/delete-account/src/main/java/org/gcube/keycloak/account/DeleteAccountRealmResourceProvider.java @@ -0,0 +1,27 @@ +package org.gcube.keycloak.account; + +import org.jboss.logging.Logger; +import org.keycloak.models.KeycloakSession; +import org.keycloak.services.resource.RealmResourceProvider; + +public class DeleteAccountRealmResourceProvider implements RealmResourceProvider { + + protected static final Logger logger = Logger.getLogger(DeleteAccountRealmResourceProvider.class); + + private KeycloakSession session; + + public DeleteAccountRealmResourceProvider(KeycloakSession session) { + logger.info("Created new DeleteAccountRealmResourceProvider object"); + this.session = session; + } + + @Override + public Object getResource() { + return new DeleteAccountResource(session); + } + + @Override + public void close() { + } + +} diff --git a/delete-account/src/main/java/org/gcube/keycloak/account/DeleteAccountRealmResourceProviderFactory.java b/delete-account/src/main/java/org/gcube/keycloak/account/DeleteAccountRealmResourceProviderFactory.java new file mode 100644 index 0000000..fb9c858 --- /dev/null +++ b/delete-account/src/main/java/org/gcube/keycloak/account/DeleteAccountRealmResourceProviderFactory.java @@ -0,0 +1,41 @@ +package org.gcube.keycloak.account; + +import org.jboss.logging.Logger; +import org.keycloak.Config.Scope; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.services.resource.RealmResourceProviderFactory; + +public class DeleteAccountRealmResourceProviderFactory implements RealmResourceProviderFactory { + + protected static final Logger logger = Logger.getLogger(DeleteAccountRealmResourceProviderFactory.class); + + public static final String ID = "delete-account"; + + public DeleteAccountRealmResourceProviderFactory() { + logger.info("Created new DeleteAccountRealmResourceProviderFactory object"); + } + + @Override + public String getId() { + return ID; + } + + @Override + public DeleteAccountRealmResourceProvider create(KeycloakSession session) { + return new DeleteAccountRealmResourceProvider(session); + } + + @Override + public void init(Scope config) { + } + + @Override + public void postInit(KeycloakSessionFactory factory) { + } + + @Override + public void close() { + } + +} diff --git a/delete-account/src/main/java/org/gcube/keycloak/account/DeleteAccountResource.java b/delete-account/src/main/java/org/gcube/keycloak/account/DeleteAccountResource.java new file mode 100644 index 0000000..79c517b --- /dev/null +++ b/delete-account/src/main/java/org/gcube/keycloak/account/DeleteAccountResource.java @@ -0,0 +1,66 @@ +package org.gcube.keycloak.account; + +import java.net.URI; + +import javax.ws.rs.ForbiddenException; +import javax.ws.rs.FormParam; +import javax.ws.rs.NotAuthorizedException; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.core.Response; + +import org.gcube.keycloak.event.OrchestratorEventPublisherProviderFactory; +import org.jboss.logging.Logger; +import org.jboss.resteasy.annotations.cache.NoCache; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.services.managers.AppAuthManager; +import org.keycloak.services.managers.AuthenticationManager; +import org.keycloak.services.resources.RealmsResource; + +public class DeleteAccountResource { + + protected static final Logger logger = Logger.getLogger(DeleteAccountResource.class); + + public static final String STATE_CHECKER_ATTRIBUTE = "state_checker"; + public static final String STATE_CHECKER_PARAMETER = "stateChecker"; + + private final KeycloakSession session; + private final AuthenticationManager.AuthResult auth; + + public DeleteAccountResource(KeycloakSession session) { + logger.info("Created new DeleteAccountResource object"); + this.session = session; + auth = new AppAuthManager().authenticateIdentityCookie(session, session.getContext().getRealm()); + } + + @NoCache + @POST() + @Path("delete") + public Response performDeleteAccount(@FormParam(STATE_CHECKER_PARAMETER) String stateChecker) { + if (auth == null) { + logger.debug("Invoked DELETE without authorization"); + throw new NotAuthorizedException("Cookie"); + } + String requiredStateChecker = session.getAttribute(STATE_CHECKER_ATTRIBUTE, String.class); + if (!requiredStateChecker.equals(stateChecker)) { + throw new ForbiddenException("State"); + } + logger.info("Invoked perform delete account"); + RealmModel realm = auth.getSession().getRealm(); + logger.debug("Sending delete account event to the orchestrator"); + new OrchestratorEventPublisherProviderFactory().create(session) + .publish(new DeleteAccountEvent(auth.getUser(), realm)); + + logger.debug("Forcing logout from all active sessions"); + session.sessions().removeUserSessions(realm); + + + URI auccountLoginUri = session.getContext().getUri().getBaseUriBuilder().path(RealmsResource.class) + .path("{realm}/account").build(realm.getName()); + + logger.debugf("Finally redirecting to the account form login: %s", auccountLoginUri); + return Response.status(302).location(auccountLoginUri).build(); + } + +} diff --git a/delete-account/src/main/resources/META-INF/keycloak-themes.json b/delete-account/src/main/resources/META-INF/keycloak-themes.json new file mode 100644 index 0000000..9014155 --- /dev/null +++ b/delete-account/src/main/resources/META-INF/keycloak-themes.json @@ -0,0 +1,10 @@ +{ + "themes": [ + { + "name": "delete-account", + "types": [ + "account" + ] + } + ] +} \ No newline at end of file diff --git a/delete-account/src/main/resources/META-INF/services/org.keycloak.services.resource.RealmResourceProviderFactory b/delete-account/src/main/resources/META-INF/services/org.keycloak.services.resource.RealmResourceProviderFactory new file mode 100644 index 0000000..5709301 --- /dev/null +++ b/delete-account/src/main/resources/META-INF/services/org.keycloak.services.resource.RealmResourceProviderFactory @@ -0,0 +1 @@ +org.gcube.keycloak.account.DeleteAccountRealmResourceProviderFactory \ No newline at end of file diff --git a/delete-account/src/main/resources/theme/delete-account/account/account.ftl b/delete-account/src/main/resources/theme/delete-account/account/account.ftl new file mode 100644 index 0000000..cb30171 --- /dev/null +++ b/delete-account/src/main/resources/theme/delete-account/account/account.ftl @@ -0,0 +1,91 @@ +<#import "template.ftl" as layout> +<@layout.mainLayout active='account' bodyClass='user'; section> + +
+
+

${msg("editAccountHtmlTitle")}

+
+
+ * ${msg("requiredFields")} +
+
+ +
+ + + + <#if !realm.registrationEmailAsUsername> +
+
+ <#if realm.editUsernameAllowed>* +
+ +
+ disabled="disabled" value="${(account.username!'')}"/> +
+
+ + +
+
+ * +
+ +
+ +
+
+ +
+
+ * +
+ +
+ +
+
+ +
+
+ * +
+ +
+ +
+
+ +
+
+
+ <#if url.referrerURI??>${kcSanitize(msg("backToApplication")?no_esc)} + + +
+
+
+
+ +
+
+

${msg("deleteAccountHtmlTitle")}

+
+
+ + <#assign deleteUrl = url.accountUrl?replace("^(.*)(/account/?)(\\?(.*))?$", "$1/delete-account/delete?$4", 'r') /> +
+ + + + +
+
+
+ +
+
+
+
+ + \ No newline at end of file diff --git a/delete-account/src/main/resources/theme/delete-account/account/messages/messages_en.properties b/delete-account/src/main/resources/theme/delete-account/account/messages/messages_en.properties new file mode 100644 index 0000000..7579466 --- /dev/null +++ b/delete-account/src/main/resources/theme/delete-account/account/messages/messages_en.properties @@ -0,0 +1,2 @@ +deleteAccountHtmlTitle=Delete Personal Account +deleteAccountSubmitButton=Delete Account \ No newline at end of file diff --git a/delete-account/src/main/resources/theme/delete-account/account/resources/img/delete-user.png b/delete-account/src/main/resources/theme/delete-account/account/resources/img/delete-user.png new file mode 100644 index 0000000000000000000000000000000000000000..d6e73edd1123747b2f7a0dc514432cfb99260e40 GIT binary patch literal 3414 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 literal 0 HcmV?d00001 diff --git a/delete-account/src/main/resources/theme/delete-account/account/theme.properties b/delete-account/src/main/resources/theme/delete-account/account/theme.properties new file mode 100644 index 0000000..512d633 --- /dev/null +++ b/delete-account/src/main/resources/theme/delete-account/account/theme.properties @@ -0,0 +1 @@ +parent=keycloak \ No newline at end of file diff --git a/keycloak-d4science-bundle/pom.xml b/keycloak-d4science-bundle/pom.xml index ad0111c..199a24d 100644 --- a/keycloak-d4science-bundle/pom.xml +++ b/keycloak-d4science-bundle/pom.xml @@ -42,6 +42,11 @@ ${project.version} provided + + org.gcube + delete-account + ${project.version} + org.gcube event-listener-provider @@ -88,6 +93,13 @@ avatar-realm-resource.jar / + + org.gcube + delete-account + true + delete-account.jar + / + org.gcube event-listener-provider diff --git a/keycloak-d4science-bundle/src/main/application/META-INF/jboss-deployment-structure.xml b/keycloak-d4science-bundle/src/main/application/META-INF/jboss-deployment-structure.xml index 97d242d..2a34643 100644 --- a/keycloak-d4science-bundle/src/main/application/META-INF/jboss-deployment-structure.xml +++ b/keycloak-d4science-bundle/src/main/application/META-INF/jboss-deployment-structure.xml @@ -36,6 +36,19 @@ + + + + + + + + + + + + + diff --git a/pom.xml b/pom.xml index e539645..03e20ce 100644 --- a/pom.xml +++ b/pom.xml @@ -19,6 +19,7 @@ avatar-storage avatar-realm-resource avatar-importer + delete-account event-listener-provider identity-provider-mapper ldap-storage-mapper