feat: role operations

This commit is contained in:
Arthur Wambst 2025-08-23 06:25:55 +02:00
parent 391c43a569
commit e290b9314c
No known key found for this signature in database
9 changed files with 201 additions and 44 deletions

View File

@ -1,6 +1,5 @@
package fr.la_banquise.backend.data.model;
import com.fasterxml.jackson.annotation.JsonBackReference;
import com.github.dockerjava.api.model.Container;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;

View File

@ -0,0 +1,20 @@
package fr.la_banquise.backend.data.model;
public enum RolesAsso {
ROOT("ROOT"), // ROOT should always be the first
MODO("MODO"),
PINGOUIN("PINGOUIN"),
JI("JI"),
NONE("NONE");
private final String roleName;
RolesAsso(String roleName) { this.roleName = roleName; }
public String getRoleName() { return roleName; }
@Override
public String toString() {
return roleName;
}
}

View File

@ -1,12 +1,17 @@
package fr.la_banquise.backend.data.model;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import io.quarkus.security.jpa.Password;
import io.quarkus.security.jpa.Roles;
import io.quarkus.security.jpa.RolesValue;
import io.quarkus.security.jpa.UserDefinition;
import io.quarkus.security.jpa.Username;
import jakarta.persistence.CascadeType;
import jakarta.persistence.CollectionTable;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
@ -14,7 +19,11 @@ import jakarta.persistence.ManyToMany;
import jakarta.persistence.OneToMany;
import jakarta.persistence.SequenceGenerator;
import jakarta.persistence.Table;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
@ -38,7 +47,11 @@ public class User {
public Long id;
@Username public String name;
@Password public String password;
@Roles public String role;
@Enumerated(EnumType.STRING)
@ElementCollection(fetch = FetchType.EAGER)
@CollectionTable(name = "user_roles")
public Set<RolesAsso> role;
//@JsonManagedReference
@OneToMany(mappedBy = "owner", cascade = CascadeType.ALL)
@ -50,11 +63,24 @@ public class User {
@ManyToMany(mappedBy = "respos", cascade = CascadeType.ALL)
public List<Ji> jiRespo;
public User(String name, String password, String role,
// Méthode pour Quarkus Security - conversion simple
@RolesValue
@Roles
public Set<String> getRoles() {
return role.stream()
.filter(r -> r != RolesAsso.NONE)
.map(Enum::name)
.collect(Collectors.toSet());
}
public User(String name, String password, RolesAsso role,
List<Instance> instances) {
this.name = name;
this.password = password;
this.role = role;
if (role == RolesAsso.NONE)
this.role = new HashSet<>();
else
this.role = new HashSet<>(Arrays.asList(role));
this.instances = instances;
}
}

View File

@ -1,8 +1,12 @@
package fr.la_banquise.backend.rest;
import fr.la_banquise.backend.data.model.RolesAsso;
import fr.la_banquise.backend.data.model.User;
import fr.la_banquise.backend.rest.request.BulkUserDelRequest;
import fr.la_banquise.backend.rest.request.BulkUserPostRequest;
import fr.la_banquise.backend.rest.request.UserRequest;
import fr.la_banquise.backend.rest.response.BulkUserDelResponse;
import fr.la_banquise.backend.rest.response.BulkUserPostResponse;
import fr.la_banquise.backend.rest.response.LoggedUserResponse;
import fr.la_banquise.backend.services.UserService;
import io.quarkus.security.Authenticated;
@ -18,11 +22,13 @@ import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import java.util.HashMap;
import java.util.Map;
/**
* UserEndpoints
*/
@Path("/api/users")
@Path("/api/user")
@Produces(MediaType.APPLICATION_JSON)
public class UserEndpoints {
@ -40,46 +46,67 @@ public class UserEndpoints {
}
@GET
@RolesAllowed("root")
public Response getAllUsers() {
return Response.ok(userService.getAllUsers()).build();
}
@GET
@RolesAllowed("root")
@RolesAllowed("ROOT")
@Path("{id}")
public Response getUser(@PathParam("id") Long id) {
return Response.ok(userService.getUser(id)).build();
}
@GET
@RolesAllowed("ROOT")
@Path("{id}/roles")
public Response getRoles(@PathParam("id") Long userId) {
try {
User user = userService.getUser(userId);
return Response.ok(user.role).build();
} catch (Exception e) {
return Response.status(404)
.entity(Map.of("User or Role not found", e))
.build();
}
}
@POST
@RolesAllowed("root")
@RolesAllowed("ROOT")
@Path("{id}/roles")
public Response addRole(@PathParam("id") Long userId,
@QueryParam("role") String role) {
try {
User user = userService.getUser(userId);
user.role.add(userService.fromString(role));
return Response.ok(user.role).build();
} catch (Exception e) {
return Response.status(404)
.entity(Map.of("User or Role not found", e))
.build();
}
}
@DELETE
@RolesAllowed("ROOT")
@Path("{id}/roles")
public Response removeRole(@PathParam("id") Long userId,
@QueryParam("role") String role) {
try {
User user = userService.getUser(userId);
user.role.remove(userService.fromString(role));
return Response.ok(user.role).build();
} catch (Exception e) {
return Response.status(404)
.entity(Map.of("User or Role not found", e))
.build();
}
}
@POST
@RolesAllowed("ROOT")
public Response createUser(UserRequest user) {
return Response.ok(userService.createUser(user)).build();
}
@POST
@RolesAllowed("root") // TODO: respos JI doivent aussi pouvoir faire ca
@Path("/bulk")
// INFO: if response is empty => required associated jiId was not found in
// existing JIs
public Response createUsersBulk(BulkUserPostRequest users) {
userService.createUsers(
users); // TODO: adapter en fonction de la reponse
return Response.ok().build();
}
@DELETE
@RolesAllowed("root")
@Path("/bulk")
public Response deleteUserBulk(BulkUserDelRequest users) {
userService.deleteUsers(
users); // TODO: adapter en focntion de la reponse
return Response.ok().build();
}
@DELETE
@RolesAllowed("root")
@RolesAllowed("ROOT")
public Response deleteUser(@QueryParam("id") Long id) {
userService.deleteUser(id);
return Response.ok().build();

View File

@ -0,0 +1,74 @@
package fr.la_banquise.backend.rest;
import fr.la_banquise.backend.data.model.RolesAsso;
import fr.la_banquise.backend.data.model.User;
import fr.la_banquise.backend.rest.request.BulkUserDelRequest;
import fr.la_banquise.backend.rest.request.BulkUserPostRequest;
import fr.la_banquise.backend.rest.request.UserRequest;
import fr.la_banquise.backend.rest.response.BulkUserDelResponse;
import fr.la_banquise.backend.rest.response.BulkUserPostResponse;
import fr.la_banquise.backend.rest.response.LoggedUserResponse;
import fr.la_banquise.backend.services.UserService;
import io.quarkus.security.Authenticated;
import io.quarkus.security.identity.SecurityIdentity;
import jakarta.annotation.security.RolesAllowed;
import jakarta.inject.Inject;
import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import java.util.HashMap;
import java.util.Map;
/**
* UserEndpoints
*/
@Path("/api/users")
@Produces(MediaType.APPLICATION_JSON)
public class UsersEndpoints {
@Inject SecurityIdentity identity;
@Inject UserService userService;
@GET
@RolesAllowed("ROOT")
public Response getAllUsers() {
return Response.ok(userService.getAllUsers()).build();
}
@POST
@RolesAllowed("ROOT") // TODO: respos JI doivent aussi pouvoir faire ca
// INFO: if response is empty => required associated jiId was not found in
// existing JIs
public Response createUsersBulk(BulkUserPostRequest users) {
BulkUserPostResponse response = userService.createUsers(
users);
if (response.success_names.size() == users.users.size())
return Response.ok().build();
return Response.status(202)
.entity(Map.of("These users were already created : ",
response.already_created))
.build();
}
@DELETE
@RolesAllowed("ROOT")
public Response deleteUserBulk(BulkUserDelRequest users) {
BulkUserDelResponse response = userService.deleteUsers(users);
if (response.success_names.size() == users.usernames.size())
return Response.ok().build();
Map<String, String> retour = new HashMap<String, String>();
for (int id = 0; id < response.failed_names.size(); id++) {
retour.put(response.failed_names.get(id),
response.failed_reasons.get(id));
}
return Response.status(202).entity(retour).build();
}
}

View File

@ -1,15 +1,13 @@
/**package fr.la_banquise.backend.services;
/*package fr.la_banquise.backend.services;
import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.command.CreateContainerResponse;
import com.github.dockerjava.api.command.InspectContainerResponse;
// import com.github.dockerjava.api.model.Container;
import com.github.dockerjava.api.model.ContainerPort;
import com.github.dockerjava.api.model.ExposedPort;
import com.github.dockerjava.api.model.HostConfig;
import com.github.dockerjava.api.model.PortBinding;
import com.github.dockerjava.api.model.Ports;
import fr.la_banquise.backend.data.model.Container;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import java.util.HashMap;
@ -47,7 +45,7 @@ public class ContainerService {
return container.getId();
}
public Container createContainer() {
/*public Container createContainer() {
Container container =
dockerClient
.createContainerCmd("nginx:latest")

View File

@ -27,7 +27,7 @@ public class InstanceService {
return instanceRepository.findAll().list();
}
public List<Instance> getAllInstances(String username) {
public List<Instance> getInstancesByOwner(String username) {
User user = userRepository.findByName(username);
return instanceRepository.find("owner", user).list();
}
@ -37,8 +37,8 @@ public class InstanceService {
}
@Transactional
public Instance createInstance(String name, String ssh, String pwd,
Long port, String username, Long sujetId) {
public Instance createInstance(String name, Long port, String username,
Long sujetId) {
User user = userRepository.findByName(username);
Sujet sujet = sujetRepository.findById(sujetId);
Instance instance = new Instance(name, port, user, sujet);

View File

@ -1,5 +1,6 @@
package fr.la_banquise.backend.services;
import fr.la_banquise.backend.data.model.RolesAsso;
import fr.la_banquise.backend.data.model.User;
import fr.la_banquise.backend.data.repository.JiRepository;
import fr.la_banquise.backend.data.repository.UserRepository;
@ -34,7 +35,7 @@ public class UserService {
public User createUser(UserRequest request) {
User user =
new User(request.name, BcryptUtil.bcryptHash(request.password),
"pingouin", new ArrayList<>());
RolesAsso.NONE, new ArrayList<>());
userRepository.persist(user);
return user;
}
@ -93,5 +94,16 @@ public class UserService {
}
return response;
}
public RolesAsso fromString(String role_str) {
return switch (role_str) {
case "ROOT" -> RolesAsso.ROOT;
case "MODO" -> RolesAsso.MODO;
case "PINGOUIN" -> RolesAsso.PINGOUIN;
case "JI" -> RolesAsso.JI;
default -> throw new Error("Wrong role str");
};
}
}

View File

@ -1,2 +1,3 @@
-- Ce fichier est exécuté automatiquement en mode dev
INSERT INTO penguin (name, password, role) VALUES ('root', '$2a$10$lzKAv4aj6s0jtneg0Ikx/eEBb6p.6N6yo7ZF.myqYxEA9MWbMwvNu', 'root');
INSERT INTO penguin (name, password) VALUES ('root', '$2a$10$lzKAv4aj6s0jtneg0Ikx/eEBb6p.6N6yo7ZF.myqYxEA9MWbMwvNu');
INSERT INTO user_roles (User_id, role) VALUES (1, 'ROOT');