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")}
+
+
+
+
+
+
+
+
${msg("deleteAccountHtmlTitle")}
+
+
+
+ <#assign deleteUrl = url.accountUrl?replace("^(.*)(/account/?)(\\?(.*))?$", "$1/delete-account/delete?$4", 'r') />
+
+
+@layout.mainLayout>
\ 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 0000000..d6e73ed
Binary files /dev/null and b/delete-account/src/main/resources/theme/delete-account/account/resources/img/delete-user.png differ
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