Lot of things

Refactored Role enum to be the same as Keycloak roles
Managed CORS errors in backend
Edited Keycloak config to avoid CORS error
Edited frontend API to avoid CORS errors
Changed Activite creation management
Added debug print in Login (should be removed);
This commit is contained in:
Alexis Leboeuf
2026-01-08 11:26:16 +01:00
parent fc98b7aef9
commit 9a2d1ae5e6
12 changed files with 45 additions and 12 deletions

6
back_end/package-lock.json generated Normal file
View File

@@ -0,0 +1,6 @@
{
"name": "back_end",
"lockfileVersion": 3,
"requires": true,
"packages": {}
}

View File

@@ -6,11 +6,15 @@ import java.util.Map;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
@Configuration @Configuration
@EnableWebSecurity @EnableWebSecurity
@@ -19,9 +23,10 @@ public class WebSecurityConfig {
@Bean @Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http http
.cors(cors -> cors.configurationSource(corsConfigurationSource()))
.csrf(csrf -> csrf.disable()) .csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth .authorizeHttpRequests(auth -> auth
.requestMatchers("/", "/public", "/coach/**").permitAll() // allow coach endpoints .requestMatchers(HttpMethod.OPTIONS, "/", "/public", "/coach/**").permitAll() // allow coach endpoints
.requestMatchers("/admin/**").hasRole("admin") .requestMatchers("/admin/**").hasRole("admin")
.requestMatchers("/user/**").hasRole("user") .requestMatchers("/user/**").hasRole("user")
.anyRequest().authenticated()) .anyRequest().authenticated())
@@ -29,6 +34,7 @@ public class WebSecurityConfig {
.jwt(jwt -> jwt.jwtAuthenticationConverter(jwtToken -> { .jwt(jwt -> jwt.jwtAuthenticationConverter(jwtToken -> {
Map<String, Collection<String>> realmAccess = jwtToken.getClaim("realm_access"); Map<String, Collection<String>> realmAccess = jwtToken.getClaim("realm_access");
Collection<String> roles = realmAccess.get("roles"); Collection<String> roles = realmAccess.get("roles");
System.out.println("ROLES FROM TOKEN " + roles);
List<SimpleGrantedAuthority> authorities = roles.stream() List<SimpleGrantedAuthority> authorities = roles.stream()
.map(role -> new SimpleGrantedAuthority("ROLE_" + role)) .map(role -> new SimpleGrantedAuthority("ROLE_" + role))
.toList(); .toList();
@@ -37,4 +43,18 @@ public class WebSecurityConfig {
return http.build(); return http.build();
} }
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowedOrigins(List.of("http://localhost:3000"));
config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS"));
config.setAllowCredentials(true);
config.setAllowedHeaders(List.of("Authorization", "Content-Type"));
UrlBasedCorsConfigurationSource source =
new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return source;
}
} }

View File

@@ -14,7 +14,7 @@ import jakarta.persistence.Entity;
public class Admin extends User{ public class Admin extends User{
public Admin(String id_keycloak, String name, String prenom){ public Admin(String id_keycloak, String name, String prenom){
super(name, id_keycloak, prenom, Role.ADMIN ); super(name, id_keycloak, prenom, Role.admin );
} }
@Override @Override

View File

@@ -28,7 +28,7 @@ public class Athlete extends User{
private List<Session> sessions = new ArrayList<>(); // plusieurs sessions sont possibles private List<Session> sessions = new ArrayList<>(); // plusieurs sessions sont possibles
public Athlete(String name, String id_keycloak, String prenom){ public Athlete(String name, String id_keycloak, String prenom){
super(name, id_keycloak, prenom, Role.ATHLETE); super(name, id_keycloak, prenom, Role.athlete);
} }
@Override @Override

View File

@@ -20,7 +20,7 @@ public class Coach extends User{
private List<Session> sessions = new ArrayList<>(); // Un coach peut avoir plusieurs sessions private List<Session> sessions = new ArrayList<>(); // Un coach peut avoir plusieurs sessions
public Coach(String name, String id_keycloak, String prenom){ public Coach(String name, String id_keycloak, String prenom){
super(name, id_keycloak, prenom, Role.COACH ); super(name, id_keycloak, prenom, Role.coach );
} }
@Override @Override

View File

@@ -1,7 +1,7 @@
package hackathon.FrisbYEE.jpa.metier; package hackathon.FrisbYEE.jpa.metier;
public enum Role { public enum Role {
ADMIN, admin,
COACH, coach,
ATHLETE athlete
} }

View File

@@ -58,7 +58,7 @@ public class Session {
} }
public void setCoach(Coach coach) { public void setCoach(Coach coach) {
if (coach.getRole() != Role.COACH) { if (coach.getRole() != Role.coach) {
throw new IllegalArgumentException("L'utilisateur n'est pas un coach"); throw new IllegalArgumentException("L'utilisateur n'est pas un coach");
} }
this.coach = coach; this.coach = coach;
@@ -66,7 +66,7 @@ public class Session {
public void setAthletes(List<Athlete> athletes) { public void setAthletes(List<Athlete> athletes) {
for (Athlete athlete : athletes) { for (Athlete athlete : athletes) {
if (athlete.getRole() != Role.ATHLETE) { if (athlete.getRole() != Role.athlete) {
throw new IllegalArgumentException("L'utilisateur n'est pas un athlète"); throw new IllegalArgumentException("L'utilisateur n'est pas un athlète");
} }
} }

View File

@@ -20,6 +20,7 @@ import org.springframework.web.bind.annotation.*;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@CrossOrigin(origins = "http://localhost:3000") @CrossOrigin(origins = "http://localhost:3000")
@Controller @Controller
@RequestMapping("/activite") @RequestMapping("/activite")
@@ -49,6 +50,7 @@ public class ActiviteResource {
public ResponseEntity<String> create(@RequestBody ActiviteDTO dto) { public ResponseEntity<String> create(@RequestBody ActiviteDTO dto) {
try { try {
System.out.println("ROLE TEST " + hackathon.FrisbYEE.jpa.metier.Role.coach);
Session session = sessionDAO.findById(dto.getSessionId()).get(); Session session = sessionDAO.findById(dto.getSessionId()).get();
Activite activite = mapToEntity(dto); Activite activite = mapToEntity(dto);
activite.setSession(session); activite.setSession(session);

View File

@@ -1,6 +1,7 @@
{ {
"realm": "Frisbyee_realm", "realm": "Frisbyee_realm",
"resource": "Frisbyee_client", "resource": "Frisbyee_client",
"clientId": "Frisbyee_client",
"auth-server-url": "http://localhost:8080", "auth-server-url": "http://localhost:8080",
"public-client": true "public-client": true
} }

View File

@@ -6,6 +6,7 @@ const api = axios.create({
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
withCredentials: true,
}); });
api.interceptors.request.use((config) => { api.interceptors.request.use((config) => {

View File

@@ -1,7 +1,7 @@
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import { Session, User, Coach, Activite, Groupe } from "../classes"; import { Session, User, Coach, Activite, Groupe } from "../classes";
import { useLocalData } from "../context/useLocalData"; import { useLocalData } from "../context/useLocalData";
import { sessionService } from "../api"; import { activiteService, sessionService } from "../api";
export const CreateSession = () => { export const CreateSession = () => {
const {user} = useLocalData() const {user} = useLocalData()
@@ -24,7 +24,7 @@ export const CreateSession = () => {
newActivite.duree= activiteDuree; newActivite.duree= activiteDuree;
newActivite.data= new Map<string,string>(); newActivite.data= new Map<string,string>();
try{ try{
await sessionService.create(newActivite); await activiteService.create(newActivite);
console.log("Session créée"); console.log("Session créée");
setActivities([...activities, newActivite]); setActivities([...activities, newActivite]);

View File

@@ -16,7 +16,7 @@ export const Login =() =>{
function handleLogin(): void { function handleLogin(): void {
keycloak.login() keycloak.login();
//TODO setUser //TODO setUser
} }
@@ -39,6 +39,9 @@ export const Login =() =>{
<div> <div>
User nom : { user.nom} User nom : { user.nom}
</div> </div>
<div>
User role : { user.role}
</div>
</div> </div>
} }