keycloak registration done

This commit is contained in:
Jottyfan
2022-12-09 22:23:54 +01:00
parent b6658655be
commit 29e8ff7798
10 changed files with 248 additions and 53 deletions

View File

@@ -25,19 +25,19 @@ import org.springframework.stereotype.Repository;
public class KeycloakRepository {
private final static Logger LOGGER = LogManager.getLogger(KeycloakRepository.class);
@Value("${keycloak.resource:biblecamp}")
@Value("${keycloak.resource}")
private String keycloakClientId;
@Value("${keycloak.auth-server-url}")
private String keycloakUrl;
@Value("${keycloak.realm:ow}")
@Value("${keycloak.realm}")
private String keycloakRealm;
@Value("${ow.keycloak.admin.name:admin")
@Value("${ow.keycloak.admin.name}")
private String keycloakAdminName;
@Value("${ow.keycloak.admin.password:password")
@Value("${ow.keycloak.admin.password}")
private String keycloakAdminPassword;
/**
@@ -54,28 +54,32 @@ public class KeycloakRepository {
/**
* register the login in keycloak
*
* @param forename the forename
* @param surname the surname
* @param login the username
* @param password the password
* @param email the email
* @return true or false
*/
public boolean register(String login, String password, String email) {
UserRepresentation user = getUserRepresentation(login, password, email);
UsersResource resource = getUsersResource(keycloakUrl, keycloakRealm, keycloakAdminName, keycloakAdminPassword, keycloakClientId);
boolean result = register(resource, user);
sendVerificationLink(login, resource);
return result;
public boolean register(String forename, String surname, String login, String password, String email) {
UserRepresentation user = getUserRepresentation(forename, surname, login, password, email);
UsersResource resource = getUsersResource(keycloakUrl, keycloakRealm, keycloakAdminName, keycloakAdminPassword,
keycloakClientId);
return register(resource, user);
}
/**
* generate a user representation
*
* @param login the login
* @param forename the forename
* @param surname the surname
* @param login the login
* @param password the password
* @param email the email
* @param email the email
* @return the user representation
*/
protected UserRepresentation getUserRepresentation(String login, String password, String email) {
protected UserRepresentation getUserRepresentation(String forename, String surname, String login, String password,
String email) {
CredentialRepresentation passwordCredentials = new CredentialRepresentation();
passwordCredentials.setTemporary(false);
passwordCredentials.setType(CredentialRepresentation.PASSWORD);
@@ -83,6 +87,8 @@ public class KeycloakRepository {
UserRepresentation user = new UserRepresentation();
user.setUsername(login);
user.setFirstName(forename);
user.setLastName(surname);
user.setEmail(email);
user.setCredentials(Collections.singletonList(passwordCredentials));
user.setEnabled(true);
@@ -94,37 +100,42 @@ public class KeycloakRepository {
* register a user in keycloak
*
* @param resource the resource
* @param user the user
* @param user the user
* @return true or false
*/
protected boolean register(UsersResource resource, UserRepresentation user) {
Response response = resource.create(user);
LOGGER.info("created new keycloak user {}", user.getUsername());
return Status.OK.equals(response.getStatusInfo());
Boolean success = Status.CREATED.equals(response.getStatusInfo());
if (success) {
LOGGER.info("created new keycloak user {}", user.getUsername());
} else {
LOGGER.error("error on creating keycloak user {}: {}", user.getUsername(), response.getStatus());
}
return success;
}
/**
* generate a keycloak object with the special arguments
*
* @param url the url
* @param realm the realm
* @param admin the admin of the realm
* @param url the url
* @param realm the realm
* @param admin the admin of the realm
* @param password the password
* @param clientId the client ID
* @return the keycloak object
*/
public KeycloakBuilder getKeycloak(String url, String realm, String admin, String password, String clientId) {
return KeycloakBuilder.builder().serverUrl(url).realm(realm).grantType(OAuth2Constants.PASSWORD)
.username(admin).password(password).clientId(clientId)
return KeycloakBuilder.builder().serverUrl(url).realm(realm).grantType(OAuth2Constants.PASSWORD).username(admin)
.password(password).clientId(clientId)
.resteasyClient(new ResteasyClientBuilderImpl().connectionPoolSize(10).build());
}
/**
* generate a users resource object
*
* @param url the url
* @param realm the realm
* @param admin the admin of the realm
* @param url the url
* @param realm the realm
* @param admin the admin of the realm
* @param password the password
* @param clientId the client ID
* @return the users resource
@@ -138,8 +149,9 @@ public class KeycloakRepository {
*
* @param userId the ID of the user
*/
public void sendVerificationLink(String userId, UsersResource usersResource) {
usersResource.get(userId).sendVerifyEmail();
public void sendVerificationLink(String userId) {
getUsersResource(keycloakUrl, keycloakRealm, keycloakAdminName, keycloakAdminPassword, keycloakClientId).get(userId)
.sendVerifyEmail();
}
}

View File

@@ -37,20 +37,32 @@ public class RegistrationController extends CommonController {
bean.setFkCamp(fkCamp);
bean.setRegisterInKeycloak(true); // we want people to register generally
model.addAttribute("bean", bean);
return "/registration";
return "/registration/registration";
}
@PostMapping("/registration/register")
public String register(@Valid @ModelAttribute RegistrationBean bean, final BindingResult bindingResult, Model model) {
public String register(@Valid @ModelAttribute("bean") RegistrationBean bean, final BindingResult bindingResult, Model model) {
super.setupSession(model, request);
if (bindingResult.hasErrors()) {
super.setupSession(model, request);
CampBean campBean = service.getCamp(bean.getFkCamp());
model.addAttribute("camp", campBean);
model.addAttribute("bean", bean);
return "/registration";
return "/registration/registration";
}
Boolean result = service.register(bean);
// TODO: give the user a message about success or error and, if registered in keycloak, a note about how to login
return index(bean.getFkCamp(), model);
return result ? "/registration/success" : "/error";
}
@GetMapping("/registration/cancel/{id}")
public String cancellation(@PathVariable Integer id, final Model model) {
super.setupSession(model, request);
model.addAttribute("bean", service.getBooking(id));
return "/registration/cancellation";
}
@GetMapping("/registration/remove/{id}")
public String remove(@PathVariable Integer id, final Model model) {
super.setupSession(model, request);
service.removeBooking(id);
return "redirect:/dashboard";
}
}

View File

@@ -2,6 +2,7 @@ package de.jottyfan.camporganizer.module.registration;
import static de.jottyfan.camporganizer.db.jooq.Tables.T_CAMP;
import static de.jottyfan.camporganizer.db.jooq.Tables.T_PERSON;
import static de.jottyfan.camporganizer.db.jooq.Tables.T_PERSONDOCUMENT;
import static de.jottyfan.camporganizer.db.jooq.Tables.T_PROFILE;
import java.time.LocalDate;
@@ -13,9 +14,11 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jasypt.util.password.StrongPasswordEncryptor;
import org.jooq.DSLContext;
import org.jooq.DeleteConditionStep;
import org.jooq.InsertResultStep;
import org.jooq.InsertValuesStep12;
import org.jooq.InsertValuesStep13;
import org.jooq.Record7;
import org.jooq.SelectConditionStep;
import org.jooq.exception.DataAccessException;
import org.jooq.impl.DSL;
@@ -27,7 +30,9 @@ import de.jottyfan.camporganizer.db.jooq.enums.EnumCamprole;
import de.jottyfan.camporganizer.db.jooq.enums.EnumSex;
import de.jottyfan.camporganizer.db.jooq.tables.records.TCampRecord;
import de.jottyfan.camporganizer.db.jooq.tables.records.TPersonRecord;
import de.jottyfan.camporganizer.db.jooq.tables.records.TPersondocumentRecord;
import de.jottyfan.camporganizer.db.jooq.tables.records.TProfileRecord;
import de.jottyfan.camporganizer.module.common.BookingBean;
import de.jottyfan.camporganizer.module.common.LambdaResultWrapper;
/**
@@ -163,4 +168,61 @@ public class RegistrationGateway {
});
return lrw.getCounter() > 0;
}
/**
* get the booking from the database
*
* @param id the id of the booking
* @return the booking bean or null
*/
public BookingBean getBooking(Integer id) {
SelectConditionStep<Record7<Integer, String, String, EnumCamprole, String, LocalDateTime, LocalDateTime>> sql = jooq
// @formatter:off
.select(T_PERSON.PK,
T_PERSON.FORENAME,
T_PERSON.SURNAME,
T_PERSON.CAMPROLE,
T_CAMP.NAME,
T_CAMP.ARRIVE,
T_CAMP.DEPART)
.from(T_PERSON)
.leftJoin(T_CAMP).on(T_CAMP.PK.eq(T_PERSON.FK_CAMP))
.where(T_PERSON.PK.eq(id));
// @formatter:on
LOGGER.debug(sql.toString());
Record7<Integer, String, String, EnumCamprole, String, LocalDateTime, LocalDateTime> r = sql.fetchOne();
if (r != null) {
BookingBean bean = new BookingBean();
bean.setPk(r.get(T_PERSON.PK));
bean.setForename(r.get(T_PERSON.FORENAME));
bean.setSurname(r.get(T_PERSON.SURNAME));
bean.setCampName(r.get(T_CAMP.NAME));
bean.setArrive(r.get(T_CAMP.ARRIVE));
bean.setDepart(r.get(T_CAMP.DEPART));
bean.setCamprole(r.get(T_PERSON.CAMPROLE));
return bean;
}
return null;
}
/**
* remove the booking and all of its dependencies
*
* @param id the pk of t_person
* @return number of affected database rows, should be 1
*/
public Integer removeBooking(Integer id) {
LambdaResultWrapper lrw = new LambdaResultWrapper();
jooq.transaction(t -> {
DeleteConditionStep<TPersondocumentRecord> sql1 = DSL.using(t).deleteFrom(T_PERSONDOCUMENT)
.where(T_PERSONDOCUMENT.FK_PERSON.eq(id));
LOGGER.debug(sql1.toString());
sql1.execute();
DeleteConditionStep<TPersonRecord> sql2 = DSL.using(t).deleteFrom(T_PERSON).where(T_PERSON.PK.eq(id));
LOGGER.debug(sql2.toString());
lrw.add(sql2.execute());
});
return lrw.getCounter();
}
}

View File

@@ -1,8 +1,12 @@
package de.jottyfan.camporganizer.module.registration;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import de.jottyfan.camporganizer.module.common.BookingBean;
/**
*
* @author jotty
@@ -10,6 +14,7 @@ import org.springframework.stereotype.Service;
*/
@Service
public class RegistrationService {
private final static Logger LOGGER = LogManager.getLogger(RegistrationService.class);
@Autowired
private RegistrationGateway gateway;
@@ -36,8 +41,34 @@ public class RegistrationService {
public Boolean register(RegistrationBean bean) {
Boolean result = gateway.register(bean);
if (result && bean.getRegisterInKeycloak()) {
keycloak.register(bean.getLogin(), bean.getPassword(), bean.getEmail());
keycloak.register(bean.getForename(), bean.getSurname(), bean.getLogin(), bean.getPassword(), bean.getEmail());
if (bean.getEmail() != null && !bean.getEmail().isBlank()) {
try {
keycloak.sendVerificationLink(bean.getLogin());
} catch (Exception e) {
LOGGER.error(e.getMessage(), e);
}
}
}
return result;
}
/**
* get the booking bean
*
* @param id the id of the registration (t_person.pk)
* @return the booking bean or null
*/
public BookingBean getBooking(Integer id) {
return gateway.getBooking(id);
}
/**
* remove the booking and all of its dependencies
*
* @param id the id of the booking (t_person.pk)
*/
public Boolean removeBooking(Integer id) {
return gateway.removeBooking(id) > 0;
}
}