feat: add keycloak account creation and club groupe set
This commit is contained in:
@@ -16,6 +16,7 @@ import java.util.Map;
|
||||
public class ClubEntity {
|
||||
private long id;
|
||||
private String name;
|
||||
private String clubId;
|
||||
private String country;
|
||||
private String shieldURL;
|
||||
private Map<Contact, String> contact;
|
||||
@@ -35,6 +36,7 @@ public class ClubEntity {
|
||||
return ClubEntity.builder()
|
||||
.id(model.getId())
|
||||
.name(model.getName())
|
||||
.clubId(model.getClubId())
|
||||
.country(model.getCountry())
|
||||
.shieldURL(model.getShieldURL())
|
||||
.contact(model.getContact())
|
||||
@@ -49,7 +51,7 @@ public class ClubEntity {
|
||||
}
|
||||
|
||||
public ClubModel toModel () {
|
||||
return new ClubModel(this.id, this.name, this.country, this.shieldURL, this.contact, this.training_location,
|
||||
return new ClubModel(this.id, this.clubId, this.name, this.country, this.shieldURL, this.contact, this.training_location,
|
||||
this.training_day_time, this.contact_intern, this.RNA, this.SIRET, this.no_affiliation,
|
||||
this.international);
|
||||
}
|
||||
|
||||
@@ -32,4 +32,11 @@ public class ClubService {
|
||||
public Uni<List<ClubModel>> getAll() {
|
||||
return repository.listAll();
|
||||
}
|
||||
|
||||
public Uni<?> setClubId(Long id, String id1) {
|
||||
return repository.findById(id).chain(clubModel -> {
|
||||
clubModel.setClubId(id1);
|
||||
return Panache.withTransaction(() -> repository.persist(clubModel));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
package fr.titionfire.ffsaf.domain.service;
|
||||
|
||||
import fr.titionfire.ffsaf.data.model.ClubModel;
|
||||
import fr.titionfire.ffsaf.data.model.MembreModel;
|
||||
import fr.titionfire.ffsaf.utils.KeycloakException;
|
||||
import io.smallrye.mutiny.Uni;
|
||||
import io.smallrye.mutiny.unchecked.Unchecked;
|
||||
import io.vertx.mutiny.core.Vertx;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.admin.client.Keycloak;
|
||||
import org.keycloak.admin.client.resource.UserResource;
|
||||
import org.keycloak.representations.idm.GroupRepresentation;
|
||||
import org.keycloak.representations.idm.RoleRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
|
||||
import java.text.Normalizer;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@ApplicationScoped
|
||||
public class KeycloakService {
|
||||
private static final Logger LOGGER = Logger.getLogger(KeycloakService.class);
|
||||
|
||||
@Inject
|
||||
Keycloak keycloak;
|
||||
|
||||
@Inject
|
||||
ClubService clubService;
|
||||
|
||||
@Inject
|
||||
MembreService membreService;
|
||||
|
||||
@ConfigProperty(name = "keycloak.realm")
|
||||
String realm;
|
||||
|
||||
@Inject
|
||||
Vertx vertx;
|
||||
|
||||
public Uni<String> getGroupFromClub(ClubModel club) {
|
||||
if (club.getClubId() == null) {
|
||||
LOGGER.infof("Creation of club group %d-%s...", club.getId(), club.getName());
|
||||
return vertx.getOrCreateContext().executeBlocking(() -> {
|
||||
GroupRepresentation clubGroup = keycloak.realm(realm).groups().groups().stream().filter(g -> g.getName().equals("club"))
|
||||
.findAny().orElseThrow(() -> new KeycloakException("Fail to fetch group %s".formatted("club")));
|
||||
|
||||
GroupRepresentation groupRepresentation = new GroupRepresentation();
|
||||
groupRepresentation.setName(club.getId() + "-" + club.getName());
|
||||
|
||||
try (Response response = keycloak.realm(realm).groups().group(clubGroup.getId()).subGroup(groupRepresentation)) {
|
||||
if (!response.getStatusInfo().equals(Response.Status.CREATED) && !response.getStatusInfo().equals(Response.Status.CONFLICT))
|
||||
throw new KeycloakException("Fail to set group parent for club: %s (reason=%s)".formatted(club.getName(),
|
||||
response.getStatusInfo().getReasonPhrase()));
|
||||
}
|
||||
|
||||
return keycloak.realm(realm).groups().group(clubGroup.getId()).toRepresentation().getSubGroups().stream()
|
||||
.filter(g -> g.getName().startsWith(club.getId() + "-")).findAny().map(GroupRepresentation::getId)
|
||||
.orElseThrow(() -> new KeycloakException("Fail to fetch group %s*".formatted(club.getId() + "-")));
|
||||
}
|
||||
).call(id -> clubService.setClubId(club.getId(), id));
|
||||
}
|
||||
return Uni.createFrom().item(club::getClubId);
|
||||
}
|
||||
|
||||
public Uni<String> getUserFromMember(MembreModel membreModel) {
|
||||
if (membreModel.getUserId() == null) {
|
||||
return Uni.createFrom().failure(new NullPointerException("No keycloak user linked to the user id=" + membreModel.getId()));
|
||||
}
|
||||
return Uni.createFrom().item(membreModel::getUserId);
|
||||
}
|
||||
|
||||
public Uni<String> setClubGroupMembre(MembreModel membreModel, ClubModel club) {
|
||||
return getGroupFromClub(club).chain(
|
||||
clubId -> getUserFromMember(membreModel).chain(userId -> vertx.getOrCreateContext().executeBlocking(() -> {
|
||||
UserResource user = keycloak.realm(realm).users().get(userId);
|
||||
user.groups().stream().filter(g -> g.getPath().startsWith("/club")).forEach(g -> user.leaveGroup(g.getId()));
|
||||
user.joinGroup(clubId);
|
||||
LOGGER.infof("Set club \"%s\" to user %s (%s)", club.getName(), userId, user.toRepresentation().getUsername());
|
||||
return "OK";
|
||||
})));
|
||||
}
|
||||
|
||||
public Uni<UserCompteState> fetchCompte(String id) {
|
||||
return vertx.getOrCreateContext().executeBlocking(() -> {
|
||||
UserResource user = keycloak.realm(realm).users().get(id);
|
||||
UserRepresentation user2 = user.toRepresentation();
|
||||
return new UserCompteState(user2.isEnabled(), user2.getUsername(), user2.isEmailVerified(),
|
||||
user.roles().realmLevel().listEffective().stream().map(RoleRepresentation::getName).toList(),
|
||||
user.groups().stream().map(GroupRepresentation::getName).toList());
|
||||
});
|
||||
}
|
||||
|
||||
public Uni<String> initCompte(long id) {
|
||||
return membreService.getById(id).invoke(Unchecked.consumer(membreModel -> {
|
||||
if (membreModel.getUserId() != null)
|
||||
throw new KeycloakException("User already linked to the user id=" + id);
|
||||
if (membreModel.getEmail() == null)
|
||||
throw new KeycloakException("User email is null");
|
||||
if (membreModel.getFname() == null || membreModel.getLname() == null)
|
||||
throw new KeycloakException("User name is null");
|
||||
})).chain(membreModel -> creatUser(membreModel).chain(user -> {
|
||||
LOGGER.infof("Set user id %s to membre %s", user.getId(), membreModel.getId());
|
||||
return membreService.setUserId(membreModel.getId(), user.getId());
|
||||
}))
|
||||
.map(__ -> "OK");
|
||||
}
|
||||
|
||||
private Uni<UserRepresentation> creatUser(MembreModel membreModel) {
|
||||
String login = makeLogin(membreModel);
|
||||
LOGGER.infof("Creation of user %s...", login);
|
||||
return vertx.getOrCreateContext().executeBlocking(() -> {
|
||||
UserRepresentation user = new UserRepresentation();
|
||||
user.setUsername(login);
|
||||
user.setFirstName(membreModel.getFname());
|
||||
user.setLastName(membreModel.getLname());
|
||||
user.setEnabled(true);
|
||||
|
||||
//user.setRequiredActions(List.of(UserModel.RequiredAction.VERIFY_EMAIL.name(),
|
||||
// UserModel.RequiredAction.UPDATE_PASSWORD.name()));
|
||||
|
||||
try (Response response = keycloak.realm(realm).users().create(user)) {
|
||||
if (!response.getStatusInfo().equals(Response.Status.CREATED) && !response.getStatusInfo().equals(Response.Status.CONFLICT))
|
||||
throw new KeycloakException("Fail to creat user %s (reason=%s)".formatted(login, response.getStatusInfo().getReasonPhrase()));
|
||||
}
|
||||
|
||||
return getUser(login).orElseThrow(() -> new KeycloakException("Fail to fetch user %s".formatted(login)));
|
||||
}).call(user -> membreService.setUserId(membreModel.getId(), user.getId()));
|
||||
}
|
||||
|
||||
private Optional<UserRepresentation> getUser(String username) {
|
||||
List<UserRepresentation> users = keycloak.realm(realm).users().searchByUsername(username, true);
|
||||
|
||||
if (users.isEmpty())
|
||||
return Optional.empty();
|
||||
else
|
||||
return Optional.of(users.get(0));
|
||||
}
|
||||
|
||||
private String makeLogin(MembreModel model) {
|
||||
return Normalizer.normalize((model.getFname().toLowerCase() + "." + model.getLname().toLowerCase()).replace(' ', '_'), Normalizer.Form.NFD)
|
||||
.replaceAll("\\p{M}", "");
|
||||
|
||||
}
|
||||
|
||||
public record UserCompteState(Boolean enabled, String login, Boolean emailVerified, List<String> realmRoles,
|
||||
List<String> groups) {
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,8 @@ public class MembreService {
|
||||
ClubRepository clubRepository;
|
||||
@Inject
|
||||
ServerCustom serverCustom;
|
||||
@Inject
|
||||
KeycloakService keycloakService;
|
||||
|
||||
public SimpleCombModel find(int licence, String np) throws Throwable {
|
||||
return VertxContextSupport.subscribeAndAwait(() -> Panache.withTransaction(() ->
|
||||
@@ -65,6 +67,15 @@ public class MembreService {
|
||||
return Panache.withTransaction(() -> repository.persist(m));
|
||||
})
|
||||
.invoke(membreModel -> SReqComb.sendIfNeed(serverCustom.clients, SimpleCombModel.fromModel(membreModel)))
|
||||
.call(membreModel -> (membreModel.getUserId() != null) ?
|
||||
keycloakService.setClubGroupMembre(membreModel, membreModel.getClub()) : Uni.createFrom().nullItem())
|
||||
.map(__ -> "OK");
|
||||
}
|
||||
|
||||
public Uni<?> setUserId(Long id, String id1) {
|
||||
return repository.findById(id).chain(membreModel -> {
|
||||
membreModel.setUserId(id1);
|
||||
return Panache.withTransaction(() -> repository.persist(membreModel));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user