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..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 @@ -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,62 @@ public DBInitialization(UserRepository userRepository, RecordRepository recordRe } @PostConstruct - public void initializeDatabase() { - // Default user - if (!userRepository.existsById("demo")) { +public void initializeDatabase() { + + + if (!userRepository.existsById("demo")) { + User user = new User(); + user.setEmail("demo@sample.app"); + user.setId("demo"); + user.setPassword(defaultPassword); + user.encodePassword(); + userRepository.save(user); + } + + if (!userRepository.existsById("admin")) { + Admin admin = new Admin(); + admin.setEmail("admin@sample.app"); + admin.setId("admin"); + admin.setPassword(defaultPassword); + admin.encodePassword(); + userRepository.save(admin); + } + + if (!userRepository.existsById("creator")) { + Creator creator = new Creator(); + creator.setEmail("seed-creator@sample.app"); + creator.setId("creator"); + 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); + } + + // ====================== + // 🧪 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/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 b2c3a4f8..64114139 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 @@ -40,11 +40,12 @@ protected SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exce .requestMatchers(HttpMethod.GET, "/admins/{username}").hasRole("ADMIN") .requestMatchers(HttpMethod.POST, "/admins/*/suspend").hasRole("ADMIN") .requestMatchers(HttpMethod.POST, "/admins/*").denyAll() - // Creators - .requestMatchers(HttpMethod.GET, "/creators").permitAll() - .requestMatchers(HttpMethod.POST, "/creators").permitAll() + //Creators + .requestMatchers(HttpMethod.GET, "/creators").hasRole("ADMIN") + .requestMatchers(HttpMethod.POST, "/creators").permitAll() .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 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..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,14 +2,24 @@ 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; +import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*; -@RepositoryRestController + +@RestController public class CustomCreatorController { private CreatorRepository creatorRepository; @@ -17,25 +27,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,8 +37,83 @@ public ResponseEntity suspendCreator (@PathVariable String username) { creatorRepository.save(creator); return ResponseEntity.ok(creator); - + + } + + @PostMapping("/creators") + 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 saved = creatorRepository.save(creator); + + return ResponseEntity.status(HttpStatus.CREATED).body(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(); + } + + @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) { + return updateProfileForCreator(auth.getName(), updated); + } + + @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); + } + + private Profile updateProfileForCreator(String username, Profile updated) { + Creator creator = creatorRepository.findById(username) + .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 31b34555..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; @@ -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()) @@ -64,4 +64,5 @@ public ResponseEntity> getPublicProfiles() { .toList(); return ResponseEntity.ok(publicProfiles); } + } 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