diff --git a/src/main/java/com/example/gestionstagesbackend/Config/SecurityConfig.java b/src/main/java/com/example/gestionstagesbackend/Config/SecurityConfig.java deleted file mode 100644 index 781796f43f3be8fea9cca37c8dbf7bfdcd555268..0000000000000000000000000000000000000000 --- a/src/main/java/com/example/gestionstagesbackend/Config/SecurityConfig.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.example.gestionstagesbackend.Config; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer; -import org.springframework.security.config.http.SessionCreationPolicy; -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.security.web.SecurityFilterChain; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; -import org.springframework.security.authentication.AuthenticationManager; - -@Configuration -@EnableWebSecurity -@EnableMethodSecurity(prePostEnabled = true) // Enables @PreAuthorize -public class SecurityConfig { - - @Bean - public PasswordEncoder passwordEncoder() { - return new BCryptPasswordEncoder(); - } - - @Bean - public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception { - return authenticationConfiguration.getAuthenticationManager(); - } - - @Bean - public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { - http - .csrf(csrf -> csrf.disable()) - .authorizeHttpRequests(auth -> auth - .requestMatchers("/api/auth/**").permitAll() // Public routes - .requestMatchers("/api/students/**").hasAnyRole("SUPERVISEUR", "ADMIN") - .requestMatchers("/api/stages/**").hasAnyRole("ENTREPRISE", "ADMIN") - .requestMatchers("/api/enterprises/**").hasRole("ADMIN") - .requestMatchers("/api/candidacies/**").hasAnyRole("ETUDIANT", "ADMIN") - .anyRequest().authenticated() - ) - .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)); - - return http.build(); - } -} diff --git a/src/main/java/com/example/gestionstagesbackend/GestionStagesBackendApplication.java b/src/main/java/com/example/gestionstagesbackend/GestionStagesBackendApplication.java index 23df20b096ad025ec89048496cd3cfcc8ff8f351..d7f85ea654c3aa128fe83d7e8000a78c4e539a7e 100644 --- a/src/main/java/com/example/gestionstagesbackend/GestionStagesBackendApplication.java +++ b/src/main/java/com/example/gestionstagesbackend/GestionStagesBackendApplication.java @@ -12,8 +12,5 @@ public class GestionStagesBackendApplication { SpringApplication.run(GestionStagesBackendApplication.class, args); } - @Bean - public BCryptPasswordEncoder passwordEncoder() { - return new BCryptPasswordEncoder(); - } + } diff --git a/src/main/java/com/example/gestionstagesbackend/config/SecurityConfig.java b/src/main/java/com/example/gestionstagesbackend/config/SecurityConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..26b1c008940b29a7683676caefa7993a959c9689 --- /dev/null +++ b/src/main/java/com/example/gestionstagesbackend/config/SecurityConfig.java @@ -0,0 +1,80 @@ +package com.example.gestionstagesbackend.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsConfigurationSource; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +@Configuration +public class SecurityConfig { + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + + @Bean + public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception { + return authenticationConfiguration.getAuthenticationManager(); + } + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http + .cors(cors -> cors.configurationSource(corsConfigurationSource())) // Enable CORS + .csrf(csrf -> csrf.disable()) // Disable CSRF for APIs + .authorizeHttpRequests(auth -> auth + // Allow everyone to access login, register, and logout + .requestMatchers("/api/auth/login", "/api/auth/logout", "/api/auth/register").permitAll() + + // Allow OPTIONS requests for CORS preflight + .requestMatchers("/**").permitAll() + + // Role-based access + .requestMatchers("/api/students").hasRole("ETUDIANT") + .requestMatchers("/api/students/**").hasRole("ETUDIANT") + .requestMatchers("/api/stages").hasRole("ETUDIANT") + .requestMatchers("/api/stages/**").hasRole("ENTREPRISE") + .requestMatchers("/api/students").hasRole("SUPERVISEUR") + .requestMatchers("/api/students/**").hasRole("SUPERVISEUR") + .requestMatchers("/api/stages").hasRole("SUPERVISEUR") + .requestMatchers("/api/stages/**").hasRole("SUPERVISEUR") + .requestMatchers("/api/enterprises/add").hasRole("ENTREPRISE") + .requestMatchers("/api/enterprises/update").hasRole("ENTREPRISE") + .requestMatchers("/api/enterprises/delete").hasRole("ENTREPRISE") + .requestMatchers("/**").hasRole("ADMIN") + + // Any other request requires authentication + .anyRequest().authenticated() + ) + .formLogin() + .and() + .httpBasic(); + + return http.build(); + } + + @Bean + public CorsConfigurationSource corsConfigurationSource() { + CorsConfiguration configuration = new CorsConfiguration(); + configuration.setAllowedOrigins(Collections.singletonList("http://localhost:3000")); // Frontend URL + configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS")); // Allow these HTTP methods + configuration.setAllowedHeaders(Arrays.asList("Authorization", "Content-Type")); // Allow these headers + configuration.setAllowCredentials(true); // Allow cookies/auth headers + + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", configuration); + return source; + } +} diff --git a/src/main/java/com/example/gestionstagesbackend/controllers/AdminController.java b/src/main/java/com/example/gestionstagesbackend/controllers/AdminController.java new file mode 100644 index 0000000000000000000000000000000000000000..4603fb94f95eede3f8290355ca6207d233fb437c --- /dev/null +++ b/src/main/java/com/example/gestionstagesbackend/controllers/AdminController.java @@ -0,0 +1,17 @@ +package com.example.gestionstagesbackend.controllers; + +import org.springframework.web.bind.annotation.CrossOrigin; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@CrossOrigin(origins = "http://localhost:3000", allowCredentials = "true") +@RequestMapping("/admin") +public class AdminController { + + @GetMapping("/dashboard") + public String adminDashboard() { + return "Welcome, Admin!"; + } +} diff --git a/src/main/java/com/example/gestionstagesbackend/controllers/AuthController.java b/src/main/java/com/example/gestionstagesbackend/controllers/AuthController.java index 19f6cd703458d1fc5c700b7a17675972634e4fbb..04135e830b0f128e3c2eaf5dd77a8d80dca26623 100644 --- a/src/main/java/com/example/gestionstagesbackend/controllers/AuthController.java +++ b/src/main/java/com/example/gestionstagesbackend/controllers/AuthController.java @@ -2,6 +2,9 @@ package com.example.gestionstagesbackend.controllers; import com.example.gestionstagesbackend.model.User; import com.example.gestionstagesbackend.services.UserService; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; import org.springframework.http.ResponseEntity; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; @@ -11,11 +14,13 @@ import org.springframework.web.bind.annotation.*; import java.util.Collections; import java.util.Map; +import java.util.Optional; @RestController @RequestMapping("/api/auth") -@CrossOrigin(origins = "*") +@CrossOrigin(origins = "http://localhost:3000", allowCredentials = "true") public class AuthController { + private final UserService userService; private final AuthenticationManager authenticationManager; @@ -24,31 +29,77 @@ public class AuthController { this.authenticationManager = authenticationManager; } + /** REGISTER NEW USER **/ @PostMapping("/register") public ResponseEntity<?> registerUser(@RequestBody User user) { if (userService.findByUsername(user.getUsername()).isPresent()) { + System.out.println("❌ Registration failed: Username " + user.getUsername() + " is already taken."); return ResponseEntity.badRequest().body(Map.of("message", "Username is already taken")); } + // Assign default role if none is provided if (user.getRoles() == null || user.getRoles().isEmpty()) { user.setRoles(Collections.singleton("ROLE_ETUDIANT")); // Default role } + // Save user with hashed password User newUser = userService.registerUser(user); - return ResponseEntity.ok(newUser); + System.out.println("✅ User registered successfully: " + newUser.getUsername()); + return ResponseEntity.ok("User registered successfully"); } - + /** LOGIN USER (No JWT, Just Plain Text Response) **/ @PostMapping("/login") public ResponseEntity<?> loginUser(@RequestBody Map<String, String> loginRequest) { String username = loginRequest.get("username"); String password = loginRequest.get("password"); - Authentication authentication = authenticationManager.authenticate( - new UsernamePasswordAuthenticationToken(username, password) - ); + System.out.println("🔍 Attempting login for: " + username); + + if (username == null || password == null) { + System.out.println("❌ Username or password is missing in the request."); + return ResponseEntity.status(400).body("Username and password are required"); + } + + Optional<User> user = userService.findByUsername(username); + if (user.isEmpty()) { + System.out.println("❌ User not found!"); + return ResponseEntity.status(401).body("Invalid credentials"); + } + + System.out.println("✅ User found: " + username); + + try { + Authentication authentication = authenticationManager.authenticate( + new UsernamePasswordAuthenticationToken(username, password) + ); - SecurityContextHolder.getContext().setAuthentication(authentication); - return ResponseEntity.ok(Map.of("message", "Login successful")); + // Update security context on successful authentication + SecurityContextHolder.getContext().setAuthentication(authentication); + System.out.println("✅ Login successful for user: " + username); + return ResponseEntity.ok("Login successful"); + } catch (Exception e) { + System.out.println("❌ Authentication failed for user " + username + ": " + e.getMessage()); + return ResponseEntity.status(401).body("Invalid username or password"); + } + + } + /** LOGOUT USER **/ + @PostMapping("/logout") + public ResponseEntity<?> logoutUser(HttpServletRequest request, HttpServletResponse response) { + System.out.println("🔓 Logging out user: " + SecurityContextHolder.getContext().getAuthentication()); + + // Invalidate session + HttpSession session = request.getSession(false); + if (session != null) { + session.invalidate(); + } + + // Clear authentication + SecurityContextHolder.clearContext(); + + System.out.println("✅ Logout successful."); + return ResponseEntity.ok("Logout successful"); } + } diff --git a/src/main/java/com/example/gestionstagesbackend/controllers/CandidacyController.java b/src/main/java/com/example/gestionstagesbackend/controllers/CandidacyController.java index 94a57462885201636c36f4f010db134089b4b862..99034d74077f7cb2118b5fc80d40ff3caa1416c5 100644 --- a/src/main/java/com/example/gestionstagesbackend/controllers/CandidacyController.java +++ b/src/main/java/com/example/gestionstagesbackend/controllers/CandidacyController.java @@ -9,6 +9,7 @@ import com.example.gestionstagesbackend.services.StudentService; import com.example.gestionstagesbackend.services.StageService; import com.example.gestionstagesbackend.services.UnivSupervisorService; import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; import java.util.List; @@ -16,7 +17,7 @@ import java.util.Optional; @RestController @RequestMapping("/api/candidacies") -@CrossOrigin(origins = "*") +@CrossOrigin(origins = "http://localhost:3000", allowCredentials = "true") public class CandidacyController { private final CandidacyService candidacyService; private final StudentService studentService; @@ -31,7 +32,8 @@ public class CandidacyController { this.univSupervisorService = univSupervisorService; } - @PostMapping + //@PreAuthorize("hasRole('ETUDIANT') or hasRole('ADMIN')") + @PostMapping("/add") public ResponseEntity<?> createCandidacy(@RequestBody Candidacy candidacy) { if (candidacy.getStudent() == null || candidacy.getStudent().getId() == null) { return ResponseEntity.badRequest().body("Student ID is required"); @@ -69,11 +71,13 @@ public class CandidacyController { return ResponseEntity.ok(savedCandidacy); } + //@PreAuthorize("hasRole('ETUDIANT') or hasRole('ADMIN')") @GetMapping public ResponseEntity<List<Candidacy>> getAllCandidacies() { return ResponseEntity.ok(candidacyService.getAllCandidacies()); } + //@PreAuthorize("hasRole('ETUDIANT') or hasRole('ADMIN')") @GetMapping("/{id}") public ResponseEntity<Candidacy> getCandidacyById(@PathVariable Long id) { Optional<Candidacy> optionalCandidacy = candidacyService.getCandidacyById(id); @@ -87,6 +91,7 @@ public class CandidacyController { } } + //@PreAuthorize("hasRole('ETUDIANT') or hasRole('ADMIN')") @DeleteMapping("/{id}") public ResponseEntity<Void> deleteCandidacy(@PathVariable Long id) { if (!candidacyService.existsById(id)) { @@ -96,6 +101,7 @@ public class CandidacyController { return ResponseEntity.noContent().build(); } + //@PreAuthorize("hasRole('ETUDIANT') or hasRole('ADMIN')") @PutMapping("/{id}") public ResponseEntity<?> updateCandidacy(@PathVariable Long id, @RequestBody Candidacy updatedCandidacy) { Optional<Candidacy> existingCandidacy = candidacyService.getCandidacyById(id); diff --git a/src/main/java/com/example/gestionstagesbackend/controllers/EnterpriseController.java b/src/main/java/com/example/gestionstagesbackend/controllers/EnterpriseController.java index 4ac9c60f30f4b742c52167c60afef09bb3177092..abd889e4632739d5661223d83a8755e5ca1225b8 100644 --- a/src/main/java/com/example/gestionstagesbackend/controllers/EnterpriseController.java +++ b/src/main/java/com/example/gestionstagesbackend/controllers/EnterpriseController.java @@ -10,7 +10,7 @@ import java.util.Optional; @RestController @RequestMapping("/api/enterprises") -@CrossOrigin(origins = "*") +@CrossOrigin(origins = "http://localhost:3000", allowCredentials = "true") public class EnterpriseController { private final EnterpriseService enterpriseService; @@ -19,7 +19,7 @@ public class EnterpriseController { this.enterpriseService = enterpriseService; } - @PostMapping + @PostMapping("/add") public ResponseEntity<Enterprise> createEnterprise(@RequestBody Enterprise enterprise) { System.out.println("Received POST request: " + enterprise.getName() + ", " + enterprise.getAddress()); @@ -44,14 +44,14 @@ public class EnterpriseController { .orElse(ResponseEntity.notFound().build()); } - @DeleteMapping("/{id}") + @DeleteMapping("/delete/{id}") public ResponseEntity<Void> deleteEnterprise(@PathVariable Long id) { System.out.println("Deleting enterprise with ID: " + id); enterpriseService.deleteEnterprise(id); return ResponseEntity.noContent().build(); } - @PutMapping("/{id}") + @PutMapping("/update/{id}") public ResponseEntity<?> updateEnterprise(@PathVariable Long id, @RequestBody Enterprise updatedEnterprise) { Optional<Enterprise> optionalEnterprise = enterpriseService.getEnterpriseById(id); diff --git a/src/main/java/com/example/gestionstagesbackend/controllers/StageController.java b/src/main/java/com/example/gestionstagesbackend/controllers/StageController.java index a3155a8629c3c5589d28369737bf0c740118e015..cb4ef17445feb80ed84d8ff11ff1f2dbf6990c63 100644 --- a/src/main/java/com/example/gestionstagesbackend/controllers/StageController.java +++ b/src/main/java/com/example/gestionstagesbackend/controllers/StageController.java @@ -5,6 +5,7 @@ import com.example.gestionstagesbackend.model.Enterprise; import com.example.gestionstagesbackend.services.StageService; import com.example.gestionstagesbackend.services.EnterpriseService; import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; import java.util.List; @@ -12,7 +13,7 @@ import java.util.Optional; @RestController @RequestMapping("/api/stages") -@CrossOrigin(origins = "*") +@CrossOrigin(origins = "http://localhost:3000", allowCredentials = "true") public class StageController { private final StageService stageService; private final EnterpriseService enterpriseService; @@ -22,7 +23,8 @@ public class StageController { this.enterpriseService = enterpriseService; } - @PostMapping + //@PreAuthorize("hasRole('ROLE_ENTREPRISE') or hasRole('ROLE_ADMIN')") + @PostMapping("/add") public ResponseEntity<?> createStage(@RequestBody Stage stage) { if (stage.getEnterprise() == null || stage.getEnterprise().getId() == null) { return ResponseEntity.badRequest().body("Enterprise ID is required"); @@ -43,8 +45,7 @@ public class StageController { return fullStage.map(ResponseEntity::ok).orElse(ResponseEntity.ok(savedStage)); } - - + //@PreAuthorize("hasRole('ROLE_SUPERVISEUR') or hasRole('ROLE_ETUDIANT') or hasRole('ROLE_ADMIN')") @GetMapping public ResponseEntity<List<Stage>> getAllStages() { return ResponseEntity.ok(stageService.getAllStages()); @@ -63,7 +64,8 @@ public class StageController { } - @PutMapping("/{id}") + //@PreAuthorize("hasRole('ROLE_ENTREPRISE') or hasRole('ROLE_ADMIN')") + @PutMapping("/update/{id}") public ResponseEntity<?> updateStage(@PathVariable Long id, @RequestBody Stage updatedStage) { Optional<Stage> optionalStage = stageService.getStageById(id); @@ -86,7 +88,8 @@ public class StageController { return ResponseEntity.notFound().build(); } } - @DeleteMapping("/{id}") + //@PreAuthorize("hasRole('ROLE_ENTREPRISE') or hasRole('ROLE_ADMIN')") + @DeleteMapping("/delete/{id}") public ResponseEntity<?> deleteStage(@PathVariable Long id) { Optional<Stage> optionalStage = stageService.getStageById(id); diff --git a/src/main/java/com/example/gestionstagesbackend/controllers/StudentController.java b/src/main/java/com/example/gestionstagesbackend/controllers/StudentController.java index f07a8ce0fc206eea0981b8ea953d8bcf39a51276..45f172ddfd7884e47312c7dcc42f51df2d6fba81 100644 --- a/src/main/java/com/example/gestionstagesbackend/controllers/StudentController.java +++ b/src/main/java/com/example/gestionstagesbackend/controllers/StudentController.java @@ -5,6 +5,7 @@ import com.example.gestionstagesbackend.model.Student; import com.example.gestionstagesbackend.services.CandidacyService; import com.example.gestionstagesbackend.services.StudentService; import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; import java.util.List; @@ -12,7 +13,7 @@ import java.util.Optional; @RestController @RequestMapping("/api/students") -@CrossOrigin(origins = "*") +@CrossOrigin(origins = "http://localhost:3000", allowCredentials = "true") public class StudentController { private final StudentService studentService; private final CandidacyService candidacyService; @@ -22,17 +23,20 @@ public class StudentController { this.candidacyService = candidacyService; } - @PostMapping + //@PreAuthorize("hasRole('ROLE_SUPERVISEUR') or hasRole('ROLE_ADMIN')") + @PostMapping("/add") public ResponseEntity<Student> createStudent(@RequestBody Student student) { Student savedStudent = studentService.saveStudent(student); return ResponseEntity.ok(savedStudent); } + //@PreAuthorize("hasRole('ROLE_SUPERVISEUR') or hasRole('ROLE_ETUDIANT') or hasRole('ROLE_ADMIN')") @GetMapping public ResponseEntity<List<Student>> getAllStudents() { return ResponseEntity.ok(studentService.getAllStudents()); } + //@PreAuthorize("hasRole('ROLE_SUPERVISEUR') or hasRole('ROLE_ETUDIANT') or hasRole('ROLE_ADMIN')") @GetMapping("/{id}") public ResponseEntity<Student> getStudentById(@PathVariable Long id) { return studentService.getStudentById(id) @@ -47,8 +51,8 @@ public class StudentController { return ResponseEntity.ok(candidacies); } - // ✅ PUT - Update student by ID - @PutMapping("/{id}") + //@PreAuthorize("hasRole('ROLE_SUPERVISEUR') or hasRole('ROLE_ADMIN')") + @PutMapping("/update/{id}") public ResponseEntity<Student> updateStudent(@PathVariable Long id, @RequestBody Student updatedStudent) { Optional<Student> optionalStudent = studentService.getStudentById(id); @@ -65,7 +69,7 @@ public class StudentController { } // ✅ DELETE - Remove student by ID - @DeleteMapping("/{id}") + @DeleteMapping("/delete/{id}") public ResponseEntity<Void> deleteStudent(@PathVariable Long id) { studentService.deleteStudent(id); return ResponseEntity.noContent().build(); diff --git a/src/main/java/com/example/gestionstagesbackend/controllers/UnivSupervisorController.java b/src/main/java/com/example/gestionstagesbackend/controllers/UnivSupervisorController.java index 186c6e071ba0027787e0641c172142708c9dc5bf..2973a23c9cc632261f82b22289f5e3977d3b100e 100644 --- a/src/main/java/com/example/gestionstagesbackend/controllers/UnivSupervisorController.java +++ b/src/main/java/com/example/gestionstagesbackend/controllers/UnivSupervisorController.java @@ -10,7 +10,7 @@ import java.util.Optional; @RestController @RequestMapping("/api/univsupervisors") -@CrossOrigin(origins = "*") +@CrossOrigin(origins = "http://localhost:3000", allowCredentials = "true") public class UnivSupervisorController { private final UnivSupervisorService univSupervisorService; @@ -18,7 +18,7 @@ public class UnivSupervisorController { this.univSupervisorService = univSupervisorService; } - @PostMapping + @PostMapping("/add") public ResponseEntity<UnivSupervisor> createUnivSupervisor(@RequestBody UnivSupervisor univSupervisor) { UnivSupervisor savedUnivSupervisor = univSupervisorService.saveUnivSupervisor(univSupervisor); return ResponseEntity.ok(savedUnivSupervisor); @@ -37,7 +37,7 @@ public class UnivSupervisorController { } // ✅ PUT - Update UnivSupervisor by ID - @PutMapping("/{id}") + @PutMapping("/update/{id}") public ResponseEntity<UnivSupervisor> updateUnivSupervisor(@PathVariable Long id, @RequestBody UnivSupervisor updatedSupervisor) { Optional<UnivSupervisor> optionalSupervisor = univSupervisorService.getUnivSupervisorById(id); @@ -53,7 +53,7 @@ public class UnivSupervisorController { } // ✅ DELETE - Remove UnivSupervisor by ID - @DeleteMapping("/{id}") + @DeleteMapping("/delete/{id}") public ResponseEntity<Void> deleteUnivSupervisor(@PathVariable Long id) { univSupervisorService.deleteUnivSupervisor(id); return ResponseEntity.noContent().build(); diff --git a/src/main/java/com/example/gestionstagesbackend/model/User.java b/src/main/java/com/example/gestionstagesbackend/model/User.java index 085a5920831fccf6ea38e8f3a4abf8d30f107284..14ba5954dd4d0f3377d728c48cbc7e27cb947eee 100644 --- a/src/main/java/com/example/gestionstagesbackend/model/User.java +++ b/src/main/java/com/example/gestionstagesbackend/model/User.java @@ -1,40 +1,101 @@ package com.example.gestionstagesbackend.model; +import com.fasterxml.jackson.annotation.JsonIgnore; import jakarta.persistence.*; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + +import java.util.Collection; import java.util.Set; +import java.util.stream.Collectors; @Entity @Table(name = "users") -public class User { +public class User implements UserDetails { // ✅ Implements UserDetails @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + @Column(unique = true, nullable = false) private String username; - private String email; + + @JsonIgnore + @Column(nullable = false) private String password; @ElementCollection(fetch = FetchType.EAGER) - private Set<String> roles; // ["ROLE_ETUDIANT", "ROLE_ENTREPRISE", "ROLE_SUPERVISEUR", "ROLE_ADMIN"] + @CollectionTable(name = "user_roles", joinColumns = @JoinColumn(name = "user_id")) + @Column(name = "role") + private Set<String> roles; // ✅ Roles stored in a separate table public User() {} - public User(String username, String email, String password, Set<String> roles) { + public User(String username, String password, Set<String> roles) { this.username = username; - this.email = email; this.password = password; this.roles = roles; } - public Long getId() { return id; } - public String getUsername() { return username; } - public String getEmail() { return email; } - public String getPassword() { return password; } - public Set<String> getRoles() { return roles; } + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } - public void setId(Long id) { this.id = id; } - public void setUsername(String username) { this.username = username; } - public void setEmail(String email) { this.email = email; } - public void setPassword(String password) { this.password = password; } - public void setRoles(Set<String> roles) { this.roles = roles; } + public void setPassword(String password) { + this.password = password; + } + + public Set<String> getRoles() { + return roles; + } + + public void setRoles(Set<String> roles) { + this.roles = roles; + } + + // ✅ Implementing UserDetails methods + @Override + public Collection<? extends GrantedAuthority> getAuthorities() { + return roles.stream().map(role -> (GrantedAuthority) () -> role).collect(Collectors.toSet()); + } + + @Override + public boolean isAccountNonExpired() { + return true; + } + + @Override + public boolean isAccountNonLocked() { + return true; + } + + @Override + public boolean isCredentialsNonExpired() { + return true; + } + + @Override + public boolean isEnabled() { + return true; + } + + @Override + public String toString() { + return "User{id=" + id + ", username='" + username + "', roles=" + roles + "}"; + } } diff --git a/src/main/java/com/example/gestionstagesbackend/repositories/UserRepository.java b/src/main/java/com/example/gestionstagesbackend/repositories/UserRepository.java index 80e7e38735f8a8ace296d41e9e47c87fab54e0af..ce9ec7d19ff099102c6b65986cfb5bc0b383732e 100644 --- a/src/main/java/com/example/gestionstagesbackend/repositories/UserRepository.java +++ b/src/main/java/com/example/gestionstagesbackend/repositories/UserRepository.java @@ -2,10 +2,9 @@ package com.example.gestionstagesbackend.repositories; import com.example.gestionstagesbackend.model.User; import org.springframework.data.jpa.repository.JpaRepository; + import java.util.Optional; public interface UserRepository extends JpaRepository<User, Long> { Optional<User> findByUsername(String username); - boolean existsByUsername(String username); - boolean existsByEmail(String email); } diff --git a/src/main/java/com/example/gestionstagesbackend/services/UserService.java b/src/main/java/com/example/gestionstagesbackend/services/UserService.java index 8617474750f9ee9b2595e93d7ccff5191c12258b..394867ce5a73d2d12007585876558dadefe0c9b6 100644 --- a/src/main/java/com/example/gestionstagesbackend/services/UserService.java +++ b/src/main/java/com/example/gestionstagesbackend/services/UserService.java @@ -2,12 +2,18 @@ package com.example.gestionstagesbackend.services; import com.example.gestionstagesbackend.model.User; import com.example.gestionstagesbackend.repositories.UserRepository; +import jakarta.annotation.PostConstruct; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; + import java.util.Optional; +import java.util.Set; @Service -public class UserService { +public class UserService implements UserDetailsService { private final UserRepository userRepository; private final PasswordEncoder passwordEncoder; @@ -16,12 +22,52 @@ public class UserService { this.passwordEncoder = passwordEncoder; } + public boolean checkPassword(String rawPassword, String encodedPassword) { + return passwordEncoder.matches(rawPassword, encodedPassword); + } + public User registerUser(User user) { - user.setPassword(passwordEncoder.encode(user.getPassword())); // Hash password + if (user.getPassword() == null || user.getPassword().trim().isEmpty()) { + throw new IllegalArgumentException("Password cannot be null or empty"); + } + user.setPassword(passwordEncoder.encode(user.getPassword())); // Hash password before saving return userRepository.save(user); } + public Optional<User> findByUsername(String username) { return userRepository.findByUsername(username); } + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + User user = userRepository.findByUsername(username) + .orElseThrow(() -> new UsernameNotFoundException("User not found: " + username)); + + // Convert to UserDetails for Spring Security + return org.springframework.security.core.userdetails.User + .withUsername(user.getUsername()) + .password(user.getPassword()) + .authorities(user.getRoles().toArray(new String[0])) // Convert roles to authorities + .build(); + } + + @PostConstruct + public void createDefaultUsers() { + createUserIfNotExists("admin", "admin123", Set.of("ROLE_ADMIN")); + createUserIfNotExists("etudiant", "etudiant123", Set.of("ROLE_ETUDIANT")); + createUserIfNotExists("entreprise", "entreprise123", Set.of("ROLE_ENTREPRISE")); + createUserIfNotExists("superviseur", "superviseur123", Set.of("ROLE_SUPERVISEUR")); + } + + private void createUserIfNotExists(String username, String password, Set<String> roles) { + if (userRepository.findByUsername(username).isEmpty()) { + User user = new User(); + user.setUsername(username); + user.setPassword(passwordEncoder.encode(password)); + user.setRoles(roles); + userRepository.save(user); + System.out.println("✅ Created user: " + username + " with roles " + roles); + } + } }