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.
This commit is contained in:
parent
a0bf4ee541
commit
3914ca5194
|
@ -0,0 +1,30 @@
|
|||
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.gcube</groupId>
|
||||
<artifactId>keycloak-d4science-spi-parent</artifactId>
|
||||
<version>0.2.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>delete-account</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.gcube.common</groupId>
|
||||
<artifactId>event-publisher-library</artifactId>
|
||||
<version>[1.0.0-SNAPSHOT, 2.0.0-SNAPSHOT)</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.gcube</groupId>
|
||||
<artifactId>event-listener-provider</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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() {
|
||||
}
|
||||
|
||||
}
|
|
@ -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() {
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"themes": [
|
||||
{
|
||||
"name": "delete-account",
|
||||
"types": [
|
||||
"account"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
org.gcube.keycloak.account.DeleteAccountRealmResourceProviderFactory
|
|
@ -0,0 +1,91 @@
|
|||
<#import "template.ftl" as layout>
|
||||
<@layout.mainLayout active='account' bodyClass='user'; section>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-10">
|
||||
<h2>${msg("editAccountHtmlTitle")}</h2>
|
||||
</div>
|
||||
<div class="col-md-2 subtitle">
|
||||
<span class="subtitle"><span class="required">*</span> ${msg("requiredFields")}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form action="${url.accountUrl}" class="form-horizontal" method="post">
|
||||
|
||||
<input type="hidden" id="stateChecker" name="stateChecker" value="${stateChecker}">
|
||||
|
||||
<#if !realm.registrationEmailAsUsername>
|
||||
<div class="form-group ${messagesPerField.printIfExists('username','has-error')}">
|
||||
<div class="col-sm-2 col-md-2">
|
||||
<label for="username" class="control-label">${msg("username")}</label> <#if realm.editUsernameAllowed><span class="required">*</span></#if>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-10 col-md-10">
|
||||
<input type="text" class="form-control" id="username" name="username" <#if !realm.editUsernameAllowed>disabled="disabled"</#if> value="${(account.username!'')}"/>
|
||||
</div>
|
||||
</div>
|
||||
</#if>
|
||||
|
||||
<div class="form-group ${messagesPerField.printIfExists('email','has-error')}">
|
||||
<div class="col-sm-2 col-md-2">
|
||||
<label for="email" class="control-label">${msg("email")}</label> <span class="required">*</span>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-10 col-md-10">
|
||||
<input type="text" class="form-control" id="email" name="email" autofocus value="${(account.email!'')}"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group ${messagesPerField.printIfExists('firstName','has-error')}">
|
||||
<div class="col-sm-2 col-md-2">
|
||||
<label for="firstName" class="control-label">${msg("firstName")}</label> <span class="required">*</span>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-10 col-md-10">
|
||||
<input type="text" class="form-control" id="firstName" name="firstName" value="${(account.firstName!'')}"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group ${messagesPerField.printIfExists('lastName','has-error')}">
|
||||
<div class="col-sm-2 col-md-2">
|
||||
<label for="lastName" class="control-label">${msg("lastName")}</label> <span class="required">*</span>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-10 col-md-10">
|
||||
<input type="text" class="form-control" id="lastName" name="lastName" value="${(account.lastName!'')}"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div id="kc-form-buttons" class="col-md-offset-2 col-md-10 submit">
|
||||
<div class="">
|
||||
<#if url.referrerURI??><a href="${url.referrerURI}">${kcSanitize(msg("backToApplication")?no_esc)}</a></#if>
|
||||
<button type="submit" class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonLargeClass!}" name="submitAction" value="Save">${msg("doSave")}</button>
|
||||
<button type="submit" class="${properties.kcButtonClass!} ${properties.kcButtonDefaultClass!} ${properties.kcButtonLargeClass!}" name="submitAction" value="Cancel">${msg("doCancel")}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-10">
|
||||
<h2>${msg("deleteAccountHtmlTitle")}</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<#assign deleteUrl = url.accountUrl?replace("^(.*)(/account/?)(\\?(.*))?$", "$1/delete-account/delete?$4", 'r') />
|
||||
<form action="${deleteUrl}" class="form-horizontal" method="post">
|
||||
|
||||
<img src="${url.resourcesPath}/img/delete-user.png" style="max-width:200px;" >
|
||||
<input type="hidden" name="stateChecker" value="${stateChecker}">
|
||||
|
||||
<div class="form-group">
|
||||
<div id="kc-form-buttons" class="col-md-offset-2 col-md-10 submit">
|
||||
<div class="">
|
||||
<button type="submit" class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonLargeClass!}" name="submitAction" value="${msg("deleteAccountSubmitButton")}">${msg("deleteAccountSubmitButton")}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</@layout.mainLayout>
|
|
@ -0,0 +1,2 @@
|
|||
deleteAccountHtmlTitle=Delete Personal Account
|
||||
deleteAccountSubmitButton=Delete Account
|
Binary file not shown.
After Width: | Height: | Size: 3.3 KiB |
|
@ -0,0 +1 @@
|
|||
parent=keycloak
|
|
@ -42,6 +42,11 @@
|
|||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.gcube</groupId>
|
||||
<artifactId>delete-account</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.gcube</groupId>
|
||||
<artifactId>event-listener-provider</artifactId>
|
||||
|
@ -88,6 +93,13 @@
|
|||
<bundleFileName>avatar-realm-resource.jar</bundleFileName>
|
||||
<bundleDir>/</bundleDir>
|
||||
</jarModule>
|
||||
<jarModule>
|
||||
<groupId>org.gcube</groupId>
|
||||
<artifactId>delete-account</artifactId>
|
||||
<includeInApplicationXml>true</includeInApplicationXml>
|
||||
<bundleFileName>delete-account.jar</bundleFileName>
|
||||
<bundleDir>/</bundleDir>
|
||||
</jarModule>
|
||||
<jarModule>
|
||||
<groupId>org.gcube</groupId>
|
||||
<artifactId>event-listener-provider</artifactId>
|
||||
|
|
|
@ -36,6 +36,19 @@
|
|||
<module name="org.slf4j" />
|
||||
</dependencies>
|
||||
</sub-deployment>
|
||||
<sub-deployment name="delete-account.jar">
|
||||
<dependencies>
|
||||
<module name="javax.servlet.api" />
|
||||
<module name="javax.ws.rs.api" />
|
||||
<module name="org.keycloak.keycloak-core" />
|
||||
<module name="org.keycloak.keycloak-server-spi" />
|
||||
<module name="org.keycloak.keycloak-server-spi-private" />
|
||||
<module name="org.keycloak.keycloak-services" />
|
||||
<module name="org.jboss.logging" />
|
||||
<module name="org.jboss.resteasy.resteasy-jaxrs" />
|
||||
<module name="org.slf4j" />
|
||||
</dependencies>
|
||||
</sub-deployment>
|
||||
<sub-deployment name="event-listener-provider.jar">
|
||||
<dependencies>
|
||||
<module name="org.keycloak.keycloak-core" />
|
||||
|
|
Loading…
Reference in New Issue