diff --git a/src/main/java/org/openpodcastapi/opa/advice/GlobalExceptionHandler.java b/src/main/java/org/openpodcastapi/opa/advice/GlobalExceptionHandler.java index 6bf74aa..9f42bc1 100644 --- a/src/main/java/org/openpodcastapi/opa/advice/GlobalExceptionHandler.java +++ b/src/main/java/org/openpodcastapi/opa/advice/GlobalExceptionHandler.java @@ -44,7 +44,7 @@ public ResponseEntity handleValidationException(MethodA .map(fe -> new ValidationErrorResponse.FieldError(fe.getField(), fe.getDefaultMessage())) .toList(); - ValidationErrorResponse body = new ValidationErrorResponse( + var body = new ValidationErrorResponse( Instant.now(), HttpStatus.BAD_REQUEST.value(), errors diff --git a/src/main/java/org/openpodcastapi/opa/advice/GlobalModelAttributeAdvice.java b/src/main/java/org/openpodcastapi/opa/advice/GlobalModelAttributeAdvice.java index bc8b8ec..9ce94d8 100644 --- a/src/main/java/org/openpodcastapi/opa/advice/GlobalModelAttributeAdvice.java +++ b/src/main/java/org/openpodcastapi/opa/advice/GlobalModelAttributeAdvice.java @@ -16,14 +16,14 @@ public class GlobalModelAttributeAdvice { @ModelAttribute public void addAuthenticationFlag(Model model) { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - boolean isAuthenticated = authentication != null && authentication.isAuthenticated() + var isAuthenticated = authentication != null && authentication.isAuthenticated() && !"anonymousUser".equals(authentication.getPrincipal()); model.addAttribute("isAuthenticated", isAuthenticated); } @ModelAttribute public void addUserDetails(Principal principal, Model model) { - String username = principal != null ? principal.getName() : "Guest"; + var username = principal != null ? principal.getName() : "Guest"; model.addAttribute("username", username); } } \ No newline at end of file diff --git a/src/main/java/org/openpodcastapi/opa/auth/JwtAccessDeniedHandler.java b/src/main/java/org/openpodcastapi/opa/auth/JwtAccessDeniedHandler.java index b60352f..b4f01b1 100644 --- a/src/main/java/org/openpodcastapi/opa/auth/JwtAccessDeniedHandler.java +++ b/src/main/java/org/openpodcastapi/opa/auth/JwtAccessDeniedHandler.java @@ -27,7 +27,7 @@ public void handle(HttpServletRequest request, // Set content type to JSON response.setContentType("application/json"); - AuthDTO.ErrorMessageDTO message = new AuthDTO.ErrorMessageDTO("Forbidden", "You do not have permission to access this resource"); + final var message = new AuthDTO.ErrorMessageDTO("Forbidden", "You do not have permission to access this resource"); response.getWriter().write(objectMapper.writeValueAsString(message)); } diff --git a/src/main/java/org/openpodcastapi/opa/auth/JwtAuthenticationFilter.java b/src/main/java/org/openpodcastapi/opa/auth/JwtAuthenticationFilter.java index 555d489..97c7951 100644 --- a/src/main/java/org/openpodcastapi/opa/auth/JwtAuthenticationFilter.java +++ b/src/main/java/org/openpodcastapi/opa/auth/JwtAuthenticationFilter.java @@ -42,7 +42,7 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter { /// @throws EntityNotFoundException if no matching user is found private static UsernamePasswordAuthenticationToken getUsernamePasswordAuthenticationToken(UserEntity userEntity) throws EntityNotFoundException { // Create a new CustomUserDetails entity with the fetched user - CustomUserDetails userDetails = + final var userDetails = new CustomUserDetails(userEntity.getId(), userEntity.getUuid(), userEntity.getUsername(), @@ -68,8 +68,8 @@ private static UsernamePasswordAuthenticationToken getUsernamePasswordAuthentica protected void doFilterInternal(HttpServletRequest req, @Nonnull HttpServletResponse res, @Nonnull FilterChain chain) throws ServletException, IOException { - String header = req.getHeader(HttpHeaders.AUTHORIZATION); - SecretKey key = Keys.hmacShaKeyFor(jwtSecret.getBytes(StandardCharsets.UTF_8)); + final String header = req.getHeader(HttpHeaders.AUTHORIZATION); + final SecretKey key = Keys.hmacShaKeyFor(jwtSecret.getBytes(StandardCharsets.UTF_8)); // If the value is missing or is not a valid bearer token, filter the response if (header == null || !header.startsWith("Bearer ")) { @@ -78,25 +78,24 @@ protected void doFilterInternal(HttpServletRequest req, @Nonnull HttpServletResp } // Check that a valid Bearer token is in the headers - String token = header.substring(7); + final var token = header.substring(7); try { // Extract the claims from the JWT - Claims claims = Jwts.parser() + final Claims claims = Jwts.parser() .verifyWith(key) .build() .parseSignedClaims(token) .getPayload(); // Extract the user's UUID from the claims - String userUuid = claims.getSubject(); - UUID parsedUuid = UUID.fromString(userUuid); + final var parsedUuid = UUID.fromString(claims.getSubject()); // Fetch the matching user - UserEntity userEntity = repository.getUserByUuid(parsedUuid).orElseThrow(() -> new EntityNotFoundException("No matching user found")); + final var userEntity = repository.getUserByUuid(parsedUuid).orElseThrow(() -> new EntityNotFoundException("No matching user found")); // Create a user - UsernamePasswordAuthenticationToken authentication = getUsernamePasswordAuthenticationToken(userEntity); + final UsernamePasswordAuthenticationToken authentication = getUsernamePasswordAuthenticationToken(userEntity); SecurityContextHolder.getContext().setAuthentication(authentication); } catch (Exception e) { diff --git a/src/main/java/org/openpodcastapi/opa/controllers/api/AuthController.java b/src/main/java/org/openpodcastapi/opa/controllers/api/AuthController.java index e59eba8..5716e86 100644 --- a/src/main/java/org/openpodcastapi/opa/controllers/api/AuthController.java +++ b/src/main/java/org/openpodcastapi/opa/controllers/api/AuthController.java @@ -37,14 +37,14 @@ public ResponseEntity login(@RequestBody @NotNull SecurityContextHolder.getContext().setAuthentication(authentication); // Fetch the user record from the database - UserEntity userEntity = userRepository.findByUsername(loginRequest.username()).orElseThrow(() -> new EntityNotFoundException("No userEntity with username " + loginRequest.username() + " found")); + final var userEntity = userRepository.findByUsername(loginRequest.username()).orElseThrow(() -> new EntityNotFoundException("No userEntity with username " + loginRequest.username() + " found")); // Generate the access and refresh tokens for the user - String accessToken = tokenService.generateAccessToken(userEntity); - String refreshToken = tokenService.generateRefreshToken(userEntity); + final String accessToken = tokenService.generateAccessToken(userEntity); + final String refreshToken = tokenService.generateRefreshToken(userEntity); // Format the tokens and expiration time into a DTO - AuthDTO.LoginSuccessResponse response = new AuthDTO.LoginSuccessResponse(accessToken, refreshToken, String.valueOf(tokenService.getExpirationTime())); + final var response = new AuthDTO.LoginSuccessResponse(accessToken, refreshToken, String.valueOf(tokenService.getExpirationTime())); return ResponseEntity.ok(response); } @@ -52,16 +52,16 @@ public ResponseEntity login(@RequestBody @NotNull // === Refresh token endpoint === @PostMapping("/api/auth/refresh") public ResponseEntity getRefreshToken(@RequestBody @NotNull AuthDTO.RefreshTokenRequest refreshTokenRequest) { - UserEntity targetUserEntity = userRepository.findByUsername(refreshTokenRequest.username()).orElseThrow(() -> new EntityNotFoundException("No user with username " + refreshTokenRequest.username() + " found")); + final var targetUserEntity = userRepository.findByUsername(refreshTokenRequest.username()).orElseThrow(() -> new EntityNotFoundException("No user with username " + refreshTokenRequest.username() + " found")); // Validate the existing refresh token - UserEntity userEntity = tokenService.validateRefreshToken(refreshTokenRequest.refreshToken(), targetUserEntity); + final UserEntity userEntity = tokenService.validateRefreshToken(refreshTokenRequest.refreshToken(), targetUserEntity); // Generate new access token - String newAccessToken = tokenService.generateAccessToken(userEntity); + final String newAccessToken = tokenService.generateAccessToken(userEntity); // Format the token and expiration time into a DTO - AuthDTO.RefreshTokenResponse response = new AuthDTO.RefreshTokenResponse(newAccessToken, String.valueOf(tokenService.getExpirationTime())); + final var response = new AuthDTO.RefreshTokenResponse(newAccessToken, String.valueOf(tokenService.getExpirationTime())); return ResponseEntity.ok(response); } diff --git a/src/main/java/org/openpodcastapi/opa/security/TokenService.java b/src/main/java/org/openpodcastapi/opa/security/TokenService.java index 9d20198..c2ef523 100644 --- a/src/main/java/org/openpodcastapi/opa/security/TokenService.java +++ b/src/main/java/org/openpodcastapi/opa/security/TokenService.java @@ -93,7 +93,7 @@ public UserEntity validateRefreshToken(String rawToken, UserEntity userEntity) { token.getExpiresAt().isAfter(Instant.now())) { // Update the expiry date on the refresh token token.setExpiresAt(Instant.now().plusSeconds(refreshTokenDays * 24 * 3600)); - RefreshTokenEntity updatedToken = repository.save(token); + final RefreshTokenEntity updatedToken = repository.save(token); // Return the user to confirm the token is valid return updatedToken.getUser(); diff --git a/src/main/java/org/openpodcastapi/opa/subscription/SubscriptionRestController.java b/src/main/java/org/openpodcastapi/opa/subscription/SubscriptionRestController.java index 930ca74..74716ef 100644 --- a/src/main/java/org/openpodcastapi/opa/subscription/SubscriptionRestController.java +++ b/src/main/java/org/openpodcastapi/opa/subscription/SubscriptionRestController.java @@ -58,10 +58,10 @@ public ResponseEntity getAllSubscriptionsFo public ResponseEntity getSubscriptionByUuid(@PathVariable String uuid, @AuthenticationPrincipal CustomUserDetails user) throws EntityNotFoundException { // Attempt to validate the UUID value from the provided string // If the value is invalid, the GlobalExceptionHandler will throw a 400. - UUID uuidValue = UUID.fromString(uuid); + final var uuidValue = UUID.fromString(uuid); // Fetch the subscription, throw an EntityNotFoundException if this fails - SubscriptionDTO.UserSubscriptionDTO dto = service.getUserSubscriptionBySubscriptionUuid(uuidValue, user.id()); + final var dto = service.getUserSubscriptionBySubscriptionUuid(uuidValue, user.id()); // Return the mapped subscriptionEntity entry return new ResponseEntity<>(dto, HttpStatus.OK); @@ -79,9 +79,9 @@ public ResponseEntity getSubscriptionByUuid public ResponseEntity unsubscribeUserFromFeed(@PathVariable String uuid, @AuthenticationPrincipal CustomUserDetails user) { // Attempt to validate the UUID value from the provided string // If the value is invalid, the GlobalExceptionHandler will throw a 400. - UUID uuidValue = UUID.fromString(uuid); + final var uuidValue = UUID.fromString(uuid); - SubscriptionDTO.UserSubscriptionDTO dto = service.unsubscribeUserFromFeed(uuidValue, user.id()); + final var dto = service.unsubscribeUserFromFeed(uuidValue, user.id()); return new ResponseEntity<>(dto, HttpStatus.OK); } @@ -93,7 +93,7 @@ public ResponseEntity unsubscribeUserFromFe @PostMapping @PreAuthorize("hasRole('USER')") public ResponseEntity createUserSubscriptions(@RequestBody List request, @AuthenticationPrincipal CustomUserDetails user) { - SubscriptionDTO.BulkSubscriptionResponseDTO response = service.addSubscriptions(request, user.id()); + final var response = service.addSubscriptions(request, user.id()); if (response.success().isEmpty() && !response.failure().isEmpty()) { // If all requests failed, return a 400 error diff --git a/src/main/java/org/openpodcastapi/opa/subscription/SubscriptionService.java b/src/main/java/org/openpodcastapi/opa/subscription/SubscriptionService.java index 03dd115..73e6236 100644 --- a/src/main/java/org/openpodcastapi/opa/subscription/SubscriptionService.java +++ b/src/main/java/org/openpodcastapi/opa/subscription/SubscriptionService.java @@ -3,7 +3,6 @@ import jakarta.persistence.EntityNotFoundException; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; -import org.openpodcastapi.opa.user.UserEntity; import org.openpodcastapi.opa.user.UserRepository; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -29,7 +28,7 @@ public class SubscriptionService { /// @param dto the [SubscriptionDTO.SubscriptionCreateDTO] containing the subscription data /// @return the fetched or created [SubscriptionEntity] protected SubscriptionEntity fetchOrCreateSubscription(SubscriptionDTO.SubscriptionCreateDTO dto) { - UUID feedUuid = UUID.fromString(dto.uuid()); + final var feedUuid = UUID.fromString(dto.uuid()); return subscriptionRepository .findByUuid(feedUuid) .orElseGet(() -> { @@ -47,11 +46,11 @@ protected SubscriptionEntity fetchOrCreateSubscription(SubscriptionDTO.Subscript @Transactional(readOnly = true) public SubscriptionDTO.UserSubscriptionDTO getUserSubscriptionBySubscriptionUuid(UUID subscriptionUuid, Long userId) { log.debug("Fetching subscription {} for userEntity {}", subscriptionUuid, userId); - UserSubscriptionEntity subscription = userSubscriptionRepository.findByUserIdAndSubscriptionUuid(userId, subscriptionUuid) + final var userSubscription = userSubscriptionRepository.findByUserIdAndSubscriptionUuid(userId, subscriptionUuid) .orElseThrow(() -> new EntityNotFoundException("subscription not found for userEntity")); log.debug("Subscription {} for userEntity {} found", subscriptionUuid, userId); - return userSubscriptionMapper.toDto(subscription); + return userSubscriptionMapper.toDto(userSubscription); } /// Gets all subscriptions for the authenticated userEntity @@ -61,7 +60,8 @@ public SubscriptionDTO.UserSubscriptionDTO getUserSubscriptionBySubscriptionUuid @Transactional(readOnly = true) public Page getAllSubscriptionsForUser(Long userId, Pageable pageable) { log.debug("Fetching subscriptions for {}", userId); - return userSubscriptionRepository.findAllByUserId(userId, pageable) + return userSubscriptionRepository + .findAllByUserId(userId, pageable) .map(userSubscriptionMapper::toDto); } @@ -83,16 +83,17 @@ public Page getAllActiveSubscriptionsForUse /// @return a [SubscriptionDTO.UserSubscriptionDTO] representation of the subscription link /// @throws EntityNotFoundException if no matching user is found protected SubscriptionDTO.UserSubscriptionDTO persistUserSubscription(SubscriptionEntity subscriptionEntity, Long userId) { - UserEntity userEntity = userRepository.findById(userId).orElseThrow(() -> new EntityNotFoundException("userEntity not found")); + final var userEntity = userRepository.findById(userId).orElseThrow(() -> new EntityNotFoundException("user not found")); + log.debug("{}", userEntity); - UserSubscriptionEntity newSubscription = userSubscriptionRepository.findByUserIdAndSubscriptionUuid(userId, subscriptionEntity.getUuid()).orElseGet(() -> { + final var newSubscription = userSubscriptionRepository.findByUserIdAndSubscriptionUuid(userId, subscriptionEntity.getUuid()).orElseGet(() -> { log.debug("Creating new subscription for user {} and subscription {}", userId, subscriptionEntity.getUuid()); - UserSubscriptionEntity createdSubscription = new UserSubscriptionEntity(); - createdSubscription.setIsSubscribed(true); - createdSubscription.setUser(userEntity); - createdSubscription.setSubscription(subscriptionEntity); - return userSubscriptionRepository.save(createdSubscription); + final var createdSubscriptionEntity = new UserSubscriptionEntity(); + createdSubscriptionEntity.setIsSubscribed(true); + createdSubscriptionEntity.setUser(userEntity); + createdSubscriptionEntity.setSubscription(subscriptionEntity); + return userSubscriptionRepository.save(createdSubscriptionEntity); }); newSubscription.setIsSubscribed(true); @@ -111,19 +112,19 @@ public SubscriptionDTO.BulkSubscriptionResponseDTO addSubscriptions(List new EntityNotFoundException("no subscription found")); - subscription.setIsSubscribed(false); - return userSubscriptionMapper.toDto(userSubscriptionRepository.save(subscription)); + userSubscriptionEntity.setIsSubscribed(false); + return userSubscriptionMapper.toDto(userSubscriptionRepository.save(userSubscriptionEntity)); } } diff --git a/src/main/java/org/openpodcastapi/opa/user/UserRepository.java b/src/main/java/org/openpodcastapi/opa/user/UserRepository.java index 81eaff9..b56f70b 100644 --- a/src/main/java/org/openpodcastapi/opa/user/UserRepository.java +++ b/src/main/java/org/openpodcastapi/opa/user/UserRepository.java @@ -12,9 +12,7 @@ public interface UserRepository extends JpaRepository { Optional getUserByUsername(String username); - Boolean existsUserByUsername(String username); - - Boolean existsUserByEmail(String email); + Boolean existsUserByEmailOrUsername(String email, String username); Optional findByUsername(String username); } diff --git a/src/main/java/org/openpodcastapi/opa/user/UserRestController.java b/src/main/java/org/openpodcastapi/opa/user/UserRestController.java index 4325b07..c7fd577 100644 --- a/src/main/java/org/openpodcastapi/opa/user/UserRestController.java +++ b/src/main/java/org/openpodcastapi/opa/user/UserRestController.java @@ -1,7 +1,6 @@ package org.openpodcastapi.opa.user; import lombok.RequiredArgsConstructor; -import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -25,9 +24,9 @@ public class UserRestController { @ResponseStatus(HttpStatus.OK) @PreAuthorize("hasRole('ADMIN')") public ResponseEntity getAllUsers(Pageable pageable) { - Page users = service.getAllUsers(pageable); + final var paginatedUserResponse = service.getAllUsers(pageable); - return new ResponseEntity<>(UserDTO.UserPageDTO.fromPage(users), HttpStatus.OK); + return new ResponseEntity<>(UserDTO.UserPageDTO.fromPage(paginatedUserResponse), HttpStatus.OK); } /// Creates a new user in the system @@ -38,10 +37,10 @@ public ResponseEntity getAllUsers(Pageable pageable) { @ResponseStatus(HttpStatus.CREATED) public ResponseEntity createUser(@RequestBody @Validated UserDTO.CreateUserDTO request) { // Create and persist the user - UserDTO.UserResponseDTO dto = service.createAndPersistUser(request); + final var userResponseDTO = service.createAndPersistUser(request); // Return the user DTO with a `201` status. - return new ResponseEntity<>(dto, HttpStatus.CREATED); + return new ResponseEntity<>(userResponseDTO, HttpStatus.CREATED); } /// Fetch a specific user by UUID @@ -53,11 +52,11 @@ public ResponseEntity createUser(@RequestBody @Validate public ResponseEntity deleteUser(@PathVariable String uuid) { // Attempt to validate the UUID value from the provided string // If the value is invalid, the GlobalExceptionHandler will throw a 400. - UUID uuidValue = UUID.fromString(uuid); + final var uuidValue = UUID.fromString(uuid); - // Delete the user and return the status string - String status = service.deleteUser(uuidValue); + // Delete the user and return the message string + final var message = service.deleteUserAndReturnMessage(uuidValue); - return new ResponseEntity<>(status, HttpStatus.ACCEPTED); + return new ResponseEntity<>(message, HttpStatus.ACCEPTED); } } diff --git a/src/main/java/org/openpodcastapi/opa/user/UserService.java b/src/main/java/org/openpodcastapi/opa/user/UserService.java index c7f1059..d658f1c 100644 --- a/src/main/java/org/openpodcastapi/opa/user/UserService.java +++ b/src/main/java/org/openpodcastapi/opa/user/UserService.java @@ -29,28 +29,28 @@ public class UserService { @Transactional public UserDTO.UserResponseDTO createAndPersistUser(UserDTO.CreateUserDTO dto) throws DataIntegrityViolationException { // If the user already exists in the system, throw an exception and return a `400` response. - if (repository.existsUserByEmail(dto.email()) || repository.existsUserByUsername(dto.username())) { + if (repository.existsUserByEmailOrUsername(dto.email(), dto.username())) { throw new DataIntegrityViolationException("User already exists"); } // Create a new user with a hashed password and a default `USER` role. - UserEntity newUserEntity = mapper.toEntity(dto); + final var newUserEntity = mapper.toEntity(dto); newUserEntity.setPassword(passwordEncoder.encode(dto.password())); newUserEntity.getUserRoles().add(UserRoles.USER); // Save the user and return the DTO representation. - UserEntity persistedUserEntity = repository.save(newUserEntity); + final var persistedUserEntity = repository.save(newUserEntity); log.debug("persisted user {}", persistedUserEntity.getUuid()); return mapper.toDto(persistedUserEntity); } @Transactional(readOnly = true) public Page getAllUsers(Pageable pageable) { - Page users = repository.findAll(pageable); + final var paginatedUserDTO = repository.findAll(pageable); - log.debug("returning {} users", users.getTotalElements()); + log.debug("returning {} users", paginatedUserDTO.getTotalElements()); - return users.map(mapper::toDto); + return paginatedUserDTO.map(mapper::toDto); } /// Deletes a user from the database @@ -59,8 +59,8 @@ public Page getAllUsers(Pageable pageable) { /// @return a success message /// @throws EntityNotFoundException if no matching record is found @Transactional - public String deleteUser(UUID uuid) throws EntityNotFoundException { - UserEntity userEntity = repository.getUserByUuid(uuid).orElseThrow(() -> new EntityNotFoundException(USER_NOT_FOUND)); + public String deleteUserAndReturnMessage(UUID uuid) throws EntityNotFoundException { + final var userEntity = repository.getUserByUuid(uuid).orElseThrow(() -> new EntityNotFoundException(USER_NOT_FOUND)); repository.delete(userEntity); diff --git a/src/main/java/org/openpodcastapi/opa/util/AdminUserInitializer.java b/src/main/java/org/openpodcastapi/opa/util/AdminUserInitializer.java index 698443b..657e598 100644 --- a/src/main/java/org/openpodcastapi/opa/util/AdminUserInitializer.java +++ b/src/main/java/org/openpodcastapi/opa/util/AdminUserInitializer.java @@ -33,12 +33,12 @@ public class AdminUserInitializer implements ApplicationRunner { @Override public void run(ApplicationArguments args) { if (userRepository.getUserByUsername(username).isEmpty()) { - UserEntity admin = new UserEntity(); - admin.setUsername(username); - admin.setEmail(email); - admin.setPassword(encoder.encode(password)); - admin.setUserRoles(Set.of(UserRoles.ADMIN, UserRoles.USER)); - userRepository.save(admin); + final var adminUserEntity = new UserEntity(); + adminUserEntity.setUsername(username); + adminUserEntity.setEmail(email); + adminUserEntity.setPassword(encoder.encode(password)); + adminUserEntity.setUserRoles(Set.of(UserRoles.ADMIN, UserRoles.USER)); + userRepository.save(adminUserEntity); log.info("✅ Admin user created: {} / {}", username, password); }