Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public ResponseEntity<ValidationErrorResponse> 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand All @@ -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 ")) {
Expand All @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,31 +37,31 @@ public ResponseEntity<AuthDTO.LoginSuccessResponse> 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);
}

// === Refresh token endpoint ===
@PostMapping("/api/auth/refresh")
public ResponseEntity<AuthDTO.RefreshTokenResponse> 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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,10 @@ public ResponseEntity<SubscriptionDTO.SubscriptionPageDTO> getAllSubscriptionsFo
public ResponseEntity<SubscriptionDTO.UserSubscriptionDTO> 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);
Expand All @@ -79,9 +79,9 @@ public ResponseEntity<SubscriptionDTO.UserSubscriptionDTO> getSubscriptionByUuid
public ResponseEntity<SubscriptionDTO.UserSubscriptionDTO> 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);
}
Expand All @@ -93,7 +93,7 @@ public ResponseEntity<SubscriptionDTO.UserSubscriptionDTO> unsubscribeUserFromFe
@PostMapping
@PreAuthorize("hasRole('USER')")
public ResponseEntity<SubscriptionDTO.BulkSubscriptionResponseDTO> createUserSubscriptions(@RequestBody List<SubscriptionDTO.SubscriptionCreateDTO> 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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(() -> {
Expand All @@ -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
Expand All @@ -61,7 +60,8 @@ public SubscriptionDTO.UserSubscriptionDTO getUserSubscriptionBySubscriptionUuid
@Transactional(readOnly = true)
public Page<SubscriptionDTO.UserSubscriptionDTO> getAllSubscriptionsForUser(Long userId, Pageable pageable) {
log.debug("Fetching subscriptions for {}", userId);
return userSubscriptionRepository.findAllByUserId(userId, pageable)
return userSubscriptionRepository
.findAllByUserId(userId, pageable)
.map(userSubscriptionMapper::toDto);
}

Expand All @@ -83,16 +83,17 @@ public Page<SubscriptionDTO.UserSubscriptionDTO> 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);
Expand All @@ -111,19 +112,19 @@ public SubscriptionDTO.BulkSubscriptionResponseDTO addSubscriptions(List<Subscri

log.info("{}", requests);

for (SubscriptionDTO.SubscriptionCreateDTO dto : requests) {
for (var subscriptionObject : requests) {
try {
// Fetch or create the subscription object to subscribe the user to
SubscriptionEntity subscriptionEntity = this.fetchOrCreateSubscription(dto);
final var subscriptionEntity = this.fetchOrCreateSubscription(subscriptionObject);
log.debug("{}", subscriptionEntity);
// If all is successful, persist the new UserSubscriptionEntity and add a UserSubscriptionDTO to the successes list
successes.add(persistUserSubscription(subscriptionEntity, userId));
} catch (IllegalArgumentException _) {
// If the UUID of the feed is invalid, add a new failure to the failures list
failures.add(new SubscriptionDTO.SubscriptionFailureDTO(dto.uuid(), dto.feedUrl(), "invalid UUID format"));
failures.add(new SubscriptionDTO.SubscriptionFailureDTO(subscriptionObject.uuid(), subscriptionObject.feedUrl(), "invalid UUID format"));
} catch (Exception e) {
// If another failure is encountered, add it to the failures list
failures.add(new SubscriptionDTO.SubscriptionFailureDTO(dto.uuid(), dto.feedUrl(), e.getMessage()));
failures.add(new SubscriptionDTO.SubscriptionFailureDTO(subscriptionObject.uuid(), subscriptionObject.feedUrl(), e.getMessage()));
}
}

Expand All @@ -138,10 +139,10 @@ public SubscriptionDTO.BulkSubscriptionResponseDTO addSubscriptions(List<Subscri
/// @return a [SubscriptionDTO.UserSubscriptionDTO] containing the updated object
@Transactional
public SubscriptionDTO.UserSubscriptionDTO unsubscribeUserFromFeed(UUID feedUUID, Long userId) {
UserSubscriptionEntity subscription = userSubscriptionRepository.findByUserIdAndSubscriptionUuid(userId, feedUUID)
final var userSubscriptionEntity = userSubscriptionRepository.findByUserIdAndSubscriptionUuid(userId, feedUUID)
.orElseThrow(() -> new EntityNotFoundException("no subscription found"));

subscription.setIsSubscribed(false);
return userSubscriptionMapper.toDto(userSubscriptionRepository.save(subscription));
userSubscriptionEntity.setIsSubscribed(false);
return userSubscriptionMapper.toDto(userSubscriptionRepository.save(userSubscriptionEntity));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,7 @@ public interface UserRepository extends JpaRepository<UserEntity, Long> {

Optional<UserEntity> getUserByUsername(String username);

Boolean existsUserByUsername(String username);

Boolean existsUserByEmail(String email);
Boolean existsUserByEmailOrUsername(String email, String username);

Optional<UserEntity> findByUsername(String username);
}
Loading