From 0f1c54fd4bc53d1f6f0fae01df015e98a787084c Mon Sep 17 00:00:00 2001 From: Miquel Baron Date: Thu, 16 Apr 2026 16:47:41 +0200 Subject: [PATCH 1/5] Fixes --- .../softarch/demo/controller/CustomProfileController.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/cat/udl/eps/softarch/demo/controller/CustomProfileController.java b/src/main/java/cat/udl/eps/softarch/demo/controller/CustomProfileController.java index 31b34555..e3b188e1 100644 --- a/src/main/java/cat/udl/eps/softarch/demo/controller/CustomProfileController.java +++ b/src/main/java/cat/udl/eps/softarch/demo/controller/CustomProfileController.java @@ -25,7 +25,7 @@ public CustomProfileController(ProfileRepository profileRepository) { } // PUT /profiles/id - @PutMapping("/products/{id}") + @PutMapping("/profiles/{id}") @PreAuthorize("@profileSecurity.isOwner(principal.username, #id)") @ResponseBody public ResponseEntity updateProfile(@PathVariable Long id, @@ -45,7 +45,7 @@ public ResponseEntity updateProfile(@PathVariable Long id, return ResponseEntity.ok(profile); } - @GetMapping("/products/{id}") + @GetMapping("/profiles/{id}") @PreAuthorize("@profileSecurity.isOwner(principal.username, #id) or @profileSecurity.isPublic(#id)") @ResponseBody public ResponseEntity getProfile(@PathVariable Long id) { @@ -55,7 +55,7 @@ public ResponseEntity getProfile(@PathVariable Long id) { } - @GetMapping("/products/") + @GetMapping("/profiles/") @ResponseBody public ResponseEntity> getPublicProfiles() { List publicProfiles = ((List) profileRepository.findAll()) From 0c48430f352183465ba072059e0f66a2ea1e6bf0 Mon Sep 17 00:00:00 2001 From: Miquel Baron Date: Thu, 16 Apr 2026 17:33:46 +0200 Subject: [PATCH 2/5] Fixes --- .../demo/config/DBInitialization.java | 93 ++++++++++++++----- .../controller/CustomCreatorController.java | 70 +++++++++----- .../controller/CustomProfileController.java | 5 +- 3 files changed, 120 insertions(+), 48 deletions(-) diff --git a/src/main/java/cat/udl/eps/softarch/demo/config/DBInitialization.java b/src/main/java/cat/udl/eps/softarch/demo/config/DBInitialization.java index 383e83ff..28af7bb8 100644 --- a/src/main/java/cat/udl/eps/softarch/demo/config/DBInitialization.java +++ b/src/main/java/cat/udl/eps/softarch/demo/config/DBInitialization.java @@ -6,7 +6,9 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; import jakarta.annotation.PostConstruct; - +import cat.udl.eps.softarch.demo.domain.Admin; +import cat.udl.eps.softarch.demo.domain.Creator; +import cat.udl.eps.softarch.demo.domain.Profile; import java.time.ZonedDateTime; import java.util.Arrays; @@ -27,33 +29,74 @@ public DBInitialization(UserRepository userRepository, RecordRepository recordRe } @PostConstruct - public void initializeDatabase() { - // Default user - if (!userRepository.existsById("demo")) { +public void initializeDatabase() { + + // ====================== + // πŸ‘€ DEFAULT USER (ya tienes) + // ====================== + if (!userRepository.existsById("demo")) { + User user = new User(); + user.setEmail("demo@sample.app"); + user.setId("demo"); + user.setPassword(defaultPassword); + user.encodePassword(); + userRepository.save(user); + } + + // ====================== + // πŸ‘‘ ADMIN + // ====================== + if (!userRepository.existsById("admin")) { + Admin admin = new Admin(); + admin.setId("admin"); + admin.setEmail("admin@sample.app"); + admin.setPassword(defaultPassword); + admin.encodePassword(); + + userRepository.save(admin); + } + + // ====================== + // 🎬 CREATOR con PROFILE + // ====================== + if (!userRepository.existsById("creator")) { + + Creator creator = new Creator(); + creator.setId("creator"); + creator.setEmail("creator@sample.app"); + creator.setPassword(defaultPassword); + creator.encodePassword(); + + Profile profile = new Profile(); + profile.setDescription(""); + profile.setVisibility(Profile.Visibility.PRIVATE); + + creator.setProfile(profile); + + userRepository.save(creator); + } + + // ====================== + // πŸ§ͺ TEST DATA (tu lΓ³gica) + // ====================== + if (Arrays.asList(activeProfiles.split(",")).contains("test")) { + + if (!userRepository.existsById("test")) { User user = new User(); - user.setEmail("demo@sample.app"); - user.setId("demo"); + user.setEmail("test@sample.app"); + user.setId("test"); user.setPassword(defaultPassword); user.encodePassword(); - userRepository.save(user); - } - if (Arrays.asList(activeProfiles.split(",")).contains("test")) { - // Testing instances - if (!userRepository.existsById("test")) { - User user = new User(); - user.setEmail("test@sample.app"); - user.setId("test"); - user.setPassword(defaultPassword); - user.encodePassword(); - user = userRepository.save(user); - cat.udl.eps.softarch.demo.domain.Record record = new Record(); - record.setName("My test record"); - record.setDescription("A record used for testing purposes, nothing more, nothing less..."); - record.setCreated(ZonedDateTime.now()); - record.setModified(record.getCreated()); - record.setOwnedBy(user); - recordRepository.save(record); - } + user = userRepository.save(user); + + Record record = new Record(); + record.setName("My test record"); + record.setDescription("A record used for testing purposes..."); + record.setCreated(ZonedDateTime.now()); + record.setModified(record.getCreated()); + record.setOwnedBy(user); + recordRepository.save(record); } } } +} diff --git a/src/main/java/cat/udl/eps/softarch/demo/controller/CustomCreatorController.java b/src/main/java/cat/udl/eps/softarch/demo/controller/CustomCreatorController.java index 38872dd1..f348fd0b 100644 --- a/src/main/java/cat/udl/eps/softarch/demo/controller/CustomCreatorController.java +++ b/src/main/java/cat/udl/eps/softarch/demo/controller/CustomCreatorController.java @@ -8,8 +8,11 @@ import org.springframework.web.bind.annotation.*; import org.springframework.web.server.ResponseStatusException; import org.springframework.http.HttpStatus; +import org.springframework.security.core.Authentication; +import cat.udl.eps.softarch.demo.domain.Profile; +import org.springframework.web.bind.annotation.RestController; -@RepositoryRestController +@RestController public class CustomCreatorController { private CreatorRepository creatorRepository; @@ -17,25 +20,6 @@ public CustomCreatorController(CreatorRepository creatorRepository){ this.creatorRepository = creatorRepository; } - // PUT creators/username - @PutMapping("/creators/{username}/profile") - @PreAuthorize("@CreatorSecurity.isOwner(principal.username, #username)") - public ResponseEntity updateCreator(@PathVariable String username, - @RequestBody Creator updated) { - - Creator creator = creatorRepository.findById(username) - .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); - - if (updated.getProfile() != null) { - creator.setProfile(updated.getProfile()); - } - if(updated.getEmail() !=null){ - creator.setEmail(updated.getEmail()); - } - - creatorRepository.save(creator); - return ResponseEntity.ok(creator); - } @PreAuthorize("hasRole('ADMIN')") @PostMapping("/creators/{username}/suspend") public ResponseEntity suspendCreator (@PathVariable String username) { @@ -46,9 +30,53 @@ public ResponseEntity suspendCreator (@PathVariable String username) { creatorRepository.save(creator); return ResponseEntity.ok(creator); - + + } + + @PostMapping("/creators") +public ResponseEntity createCreator(@RequestBody Creator creator) { + + Profile profile = new Profile(); + profile.setDescription(""); + profile.setVisibility(Profile.Visibility.PRIVATE); + + creator.setProfile(profile); + + Creator saved = creatorRepository.save(creator); + + return ResponseEntity.ok(saved); +} + @GetMapping("/me/profile") + @PreAuthorize("hasRole('CREATOR')") + public Profile getMyProfile(Authentication auth) { + + Creator creator = creatorRepository.findById(auth.getName()) + .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); + + return creator.getProfile(); + } + + @PutMapping("/me/profile") + @PreAuthorize("hasRole('CREATOR')") + public Profile updateMyProfile(@RequestBody Profile updated, Authentication auth) { + + Creator creator = creatorRepository.findById(auth.getName()) + .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); + + Profile profile = creator.getProfile(); + + if (profile == null) { + throw new ResponseStatusException(HttpStatus.NOT_FOUND); } + + profile.setDescription(updated.getDescription()); + profile.setVisibility(updated.getVisibility()); + + creatorRepository.save(creator); + + return profile; +} diff --git a/src/main/java/cat/udl/eps/softarch/demo/controller/CustomProfileController.java b/src/main/java/cat/udl/eps/softarch/demo/controller/CustomProfileController.java index e3b188e1..cefeee3d 100644 --- a/src/main/java/cat/udl/eps/softarch/demo/controller/CustomProfileController.java +++ b/src/main/java/cat/udl/eps/softarch/demo/controller/CustomProfileController.java @@ -12,10 +12,10 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.server.ResponseStatusException; - +import org.springframework.web.bind.annotation.RestController; import java.util.List; -@RepositoryRestController("customProfileController") +@RestController public class CustomProfileController { private final ProfileRepository profileRepository; @@ -64,4 +64,5 @@ public ResponseEntity> getPublicProfiles() { .toList(); return ResponseEntity.ok(publicProfiles); } + } From 34f2877b6f4883b30bcdde9c35782bab6c767dca Mon Sep 17 00:00:00 2001 From: Miquel Baron Date: Thu, 16 Apr 2026 17:43:11 +0200 Subject: [PATCH 3/5] Fix in password encode --- .../eps/softarch/demo/controller/CustomCreatorController.java | 2 +- .../java/cat/udl/eps/softarch/demo/steps/RegisterStepDefs.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/cat/udl/eps/softarch/demo/controller/CustomCreatorController.java b/src/main/java/cat/udl/eps/softarch/demo/controller/CustomCreatorController.java index f348fd0b..a322640f 100644 --- a/src/main/java/cat/udl/eps/softarch/demo/controller/CustomCreatorController.java +++ b/src/main/java/cat/udl/eps/softarch/demo/controller/CustomCreatorController.java @@ -35,7 +35,7 @@ public ResponseEntity suspendCreator (@PathVariable String username) { @PostMapping("/creators") public ResponseEntity createCreator(@RequestBody Creator creator) { - + creator.encodePassword(); Profile profile = new Profile(); profile.setDescription(""); profile.setVisibility(Profile.Visibility.PRIVATE); diff --git a/src/test/java/cat/udl/eps/softarch/demo/steps/RegisterStepDefs.java b/src/test/java/cat/udl/eps/softarch/demo/steps/RegisterStepDefs.java index e997adc0..cc4b4921 100644 --- a/src/test/java/cat/udl/eps/softarch/demo/steps/RegisterStepDefs.java +++ b/src/test/java/cat/udl/eps/softarch/demo/steps/RegisterStepDefs.java @@ -151,4 +151,4 @@ public void thereIsStillOnlyOneCreatorWithUsername(String username) { } assertEquals(1, count, "There should still be only one creator with username \"" + username + "\""); } -} +} \ No newline at end of file From a3ee88cedb1bc02c4039bcecf7a654aa49cd0e84 Mon Sep 17 00:00:00 2001 From: Miquel Baron Date: Thu, 16 Apr 2026 18:02:40 +0200 Subject: [PATCH 4/5] Fixes creator controller --- .../softarch/demo/config/WebSecurityConfig.java | 10 +++++----- .../controller/CustomCreatorController.java | 17 +++++++++++++++++ 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/main/java/cat/udl/eps/softarch/demo/config/WebSecurityConfig.java b/src/main/java/cat/udl/eps/softarch/demo/config/WebSecurityConfig.java index 6d914d7b..d1e3b75c 100644 --- a/src/main/java/cat/udl/eps/softarch/demo/config/WebSecurityConfig.java +++ b/src/main/java/cat/udl/eps/softarch/demo/config/WebSecurityConfig.java @@ -41,11 +41,11 @@ protected SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exce .requestMatchers(HttpMethod.POST, "/admins/*/suspend").hasRole("ADMIN") .requestMatchers(HttpMethod.POST, "/admins/*").denyAll() //Creators - .requestMatchers(HttpMethod.GET, "/creators").permitAll() - .requestMatchers(HttpMethod.POST, "/creators").permitAll() - .requestMatchers(HttpMethod.GET, "/creators/{username}").permitAll() - .requestMatchers(HttpMethod.PUT, "/creators/{username}").hasRole("ADMIN") - .requestMatchers(HttpMethod.POST, "/creators/*").hasRole("ADMIN") + .requestMatchers(HttpMethod.GET, "/creators").hasRole("ADMIN") + .requestMatchers(HttpMethod.POST, "/creators").permitAll() + .requestMatchers(HttpMethod.GET, "/creators/{username}").authenticated() + .requestMatchers(HttpMethod.PUT, "/creators/{username}").hasRole("ADMIN") + .requestMatchers(HttpMethod.POST, "/creators/*").hasRole("ADMIN") //Projects .requestMatchers(HttpMethod.POST, "/projects").authenticated() .requestMatchers(HttpMethod.PUT, "/projects/*").authenticated() diff --git a/src/main/java/cat/udl/eps/softarch/demo/controller/CustomCreatorController.java b/src/main/java/cat/udl/eps/softarch/demo/controller/CustomCreatorController.java index a322640f..fda8cc46 100644 --- a/src/main/java/cat/udl/eps/softarch/demo/controller/CustomCreatorController.java +++ b/src/main/java/cat/udl/eps/softarch/demo/controller/CustomCreatorController.java @@ -11,6 +11,11 @@ import org.springframework.security.core.Authentication; import cat.udl.eps.softarch.demo.domain.Profile; import org.springframework.web.bind.annotation.RestController; +import java.util.ArrayList; +import java.util.List; +import org.springframework.hateoas.CollectionModel; +import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*; + @RestController public class CustomCreatorController { @@ -57,6 +62,18 @@ public Profile getMyProfile(Authentication auth) { return creator.getProfile(); } + @GetMapping("/creators") + @PreAuthorize("hasRole('ADMIN')") + public CollectionModel getCreators() { + + List creators = new ArrayList<>(); + creatorRepository.findAll().forEach(creators::add); + + return CollectionModel.of(creators, + linkTo(methodOn(CustomCreatorController.class).getCreators()).withSelfRel() + ); + } + @PutMapping("/me/profile") @PreAuthorize("hasRole('CREATOR')") public Profile updateMyProfile(@RequestBody Profile updated, Authentication auth) { From 798c05bb2ee370e4712bca962b392f22c1083235 Mon Sep 17 00:00:00 2001 From: Miquel Baron Date: Thu, 16 Apr 2026 18:37:55 +0200 Subject: [PATCH 5/5] Fixes --- .../demo/config/DBInitialization.java | 20 ++---- .../demo/config/RestApiExceptionHandler.java | 22 +++++++ .../demo/config/WebSecurityConfig.java | 7 ++- .../controller/CustomCreatorController.java | 63 ++++++++++++------- 4 files changed, 72 insertions(+), 40 deletions(-) create mode 100644 src/main/java/cat/udl/eps/softarch/demo/config/RestApiExceptionHandler.java diff --git a/src/main/java/cat/udl/eps/softarch/demo/config/DBInitialization.java b/src/main/java/cat/udl/eps/softarch/demo/config/DBInitialization.java index 28af7bb8..08d7a85e 100644 --- a/src/main/java/cat/udl/eps/softarch/demo/config/DBInitialization.java +++ b/src/main/java/cat/udl/eps/softarch/demo/config/DBInitialization.java @@ -31,9 +31,7 @@ public DBInitialization(UserRepository userRepository, RecordRepository recordRe @PostConstruct public void initializeDatabase() { - // ====================== - // πŸ‘€ DEFAULT USER (ya tienes) - // ====================== + if (!userRepository.existsById("demo")) { User user = new User(); user.setEmail("demo@sample.app"); @@ -43,36 +41,26 @@ public void initializeDatabase() { userRepository.save(user); } - // ====================== - // πŸ‘‘ ADMIN - // ====================== if (!userRepository.existsById("admin")) { Admin admin = new Admin(); - admin.setId("admin"); admin.setEmail("admin@sample.app"); + admin.setId("admin"); admin.setPassword(defaultPassword); admin.encodePassword(); - userRepository.save(admin); } - // ====================== - // 🎬 CREATOR con PROFILE - // ====================== if (!userRepository.existsById("creator")) { - Creator creator = new Creator(); + creator.setEmail("seed-creator@sample.app"); creator.setId("creator"); - creator.setEmail("creator@sample.app"); creator.setPassword(defaultPassword); creator.encodePassword(); - + creator.setEnabled(true); Profile profile = new Profile(); profile.setDescription(""); profile.setVisibility(Profile.Visibility.PRIVATE); - creator.setProfile(profile); - userRepository.save(creator); } diff --git a/src/main/java/cat/udl/eps/softarch/demo/config/RestApiExceptionHandler.java b/src/main/java/cat/udl/eps/softarch/demo/config/RestApiExceptionHandler.java new file mode 100644 index 00000000..020866e8 --- /dev/null +++ b/src/main/java/cat/udl/eps/softarch/demo/config/RestApiExceptionHandler.java @@ -0,0 +1,22 @@ +package cat.udl.eps.softarch.demo.config; + +import java.util.List; +import java.util.Map; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +@RestControllerAdvice +public class RestApiExceptionHandler { + + @ExceptionHandler(MethodArgumentNotValidException.class) + public ResponseEntity>>> handleMethodArgumentNotValid( + MethodArgumentNotValidException ex) { + List> errors = ex.getBindingResult().getFieldErrors().stream() + .map(fe -> Map.of("message", fe.getDefaultMessage() != null ? fe.getDefaultMessage() : "")) + .toList(); + return ResponseEntity.badRequest().body(Map.of("errors", errors)); + } +} diff --git a/src/main/java/cat/udl/eps/softarch/demo/config/WebSecurityConfig.java b/src/main/java/cat/udl/eps/softarch/demo/config/WebSecurityConfig.java index d1e3b75c..483ca022 100644 --- a/src/main/java/cat/udl/eps/softarch/demo/config/WebSecurityConfig.java +++ b/src/main/java/cat/udl/eps/softarch/demo/config/WebSecurityConfig.java @@ -43,9 +43,10 @@ protected SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exce //Creators .requestMatchers(HttpMethod.GET, "/creators").hasRole("ADMIN") .requestMatchers(HttpMethod.POST, "/creators").permitAll() - .requestMatchers(HttpMethod.GET, "/creators/{username}").authenticated() - .requestMatchers(HttpMethod.PUT, "/creators/{username}").hasRole("ADMIN") - .requestMatchers(HttpMethod.POST, "/creators/*").hasRole("ADMIN") + .requestMatchers(HttpMethod.GET, "/creators/{username}").permitAll() + .requestMatchers(HttpMethod.PUT, "/creators/{username}").hasRole("ADMIN") + .requestMatchers(HttpMethod.PUT, "/creators/*/profile").authenticated() + .requestMatchers(HttpMethod.POST, "/creators/*").hasRole("ADMIN") //Projects .requestMatchers(HttpMethod.POST, "/projects").authenticated() .requestMatchers(HttpMethod.PUT, "/projects/*").authenticated() diff --git a/src/main/java/cat/udl/eps/softarch/demo/controller/CustomCreatorController.java b/src/main/java/cat/udl/eps/softarch/demo/controller/CustomCreatorController.java index fda8cc46..740adfb8 100644 --- a/src/main/java/cat/udl/eps/softarch/demo/controller/CustomCreatorController.java +++ b/src/main/java/cat/udl/eps/softarch/demo/controller/CustomCreatorController.java @@ -2,15 +2,17 @@ import cat.udl.eps.softarch.demo.domain.Creator; import cat.udl.eps.softarch.demo.repository.CreatorRepository; -import org.springframework.data.rest.webmvc.RepositoryRestController; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; import org.springframework.web.server.ResponseStatusException; import org.springframework.http.HttpStatus; import org.springframework.security.core.Authentication; +import org.springframework.security.authentication.AnonymousAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; import cat.udl.eps.softarch.demo.domain.Profile; import org.springframework.web.bind.annotation.RestController; +import jakarta.validation.Valid; import java.util.ArrayList; import java.util.List; import org.springframework.hateoas.CollectionModel; @@ -39,18 +41,28 @@ public ResponseEntity suspendCreator (@PathVariable String username) { } @PostMapping("/creators") -public ResponseEntity createCreator(@RequestBody Creator creator) { - creator.encodePassword(); - Profile profile = new Profile(); - profile.setDescription(""); - profile.setVisibility(Profile.Visibility.PRIVATE); + public ResponseEntity createCreator(@Valid @RequestBody Creator creator) { + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + if (auth != null && !(auth instanceof AnonymousAuthenticationToken)) { + throw new ResponseStatusException(HttpStatus.UNAUTHORIZED); + } + if (creator.getUsername() != null && creatorRepository.existsById(creator.getUsername())) { + throw new ResponseStatusException(HttpStatus.CONFLICT, "Username already exists"); + } + if (creator.getEmail() != null && creatorRepository.countByEmail(creator.getEmail()) > 0) { + throw new ResponseStatusException(HttpStatus.CONFLICT, "Email already registered"); + } + creator.encodePassword(); + Profile profile = new Profile(); + profile.setDescription(""); + profile.setVisibility(Profile.Visibility.PRIVATE); - creator.setProfile(profile); + creator.setProfile(profile); - Creator saved = creatorRepository.save(creator); + Creator saved = creatorRepository.save(creator); - return ResponseEntity.ok(saved); -} + return ResponseEntity.status(HttpStatus.CREATED).body(saved); + } @GetMapping("/me/profile") @PreAuthorize("hasRole('CREATOR')") @@ -77,23 +89,32 @@ public CollectionModel getCreators() { @PutMapping("/me/profile") @PreAuthorize("hasRole('CREATOR')") public Profile updateMyProfile(@RequestBody Profile updated, Authentication auth) { + return updateProfileForCreator(auth.getName(), updated); + } - Creator creator = creatorRepository.findById(auth.getName()) - .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); + @PutMapping("/creators/{username}/profile") + @PreAuthorize("hasRole('CREATOR') and authentication.name.equals(#username)") + public Profile updateCreatorProfile(@PathVariable String username, @RequestBody Profile updated) { + return updateProfileForCreator(username, updated); + } - Profile profile = creator.getProfile(); + private Profile updateProfileForCreator(String username, Profile updated) { + Creator creator = creatorRepository.findById(username) + .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); - if (profile == null) { - throw new ResponseStatusException(HttpStatus.NOT_FOUND); - } + Profile profile = creator.getProfile(); + + if (profile == null) { + throw new ResponseStatusException(HttpStatus.NOT_FOUND); + } - profile.setDescription(updated.getDescription()); - profile.setVisibility(updated.getVisibility()); + profile.setDescription(updated.getDescription()); + profile.setVisibility(updated.getVisibility()); - creatorRepository.save(creator); + creatorRepository.save(creator); - return profile; -} + return profile; + }