test(sprint-2): add integration tests for Auth + Compliance controllers
- AuthControllerIntegrationTest: 7 tests (login, refresh, error cases) - ComplianceControllerIntegrationTest: 5 tests (quota, auth, 404) - Fix Boot 4.0 @EntityScan relocation (boot.persistence.autoconfigure) - Fix BCrypt 72-byte limit for refresh tokens (use SHA-256 instead) - Configure H2 test DB with NON_KEYWORDS for reserved words (month/year)
This commit is contained in:
@@ -2,6 +2,7 @@ package de.cannamanage.api;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.persistence.autoconfigure.EntityScan;
|
||||
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||
|
||||
/**
|
||||
@@ -11,10 +12,11 @@ import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||
* Multi-module scanning:
|
||||
* - scanBasePackages: component scanning (controllers, services)
|
||||
* - EnableJpaRepositories: Spring Data JPA repository interfaces
|
||||
* - Entity scanning configured via spring.jpa properties
|
||||
* - EntityScan: JPA entity detection across modules (Boot 4.0 relocated package)
|
||||
*/
|
||||
@SpringBootApplication(scanBasePackages = "de.cannamanage")
|
||||
@EnableJpaRepositories(basePackages = "de.cannamanage.service.repository")
|
||||
@EntityScan(basePackages = "de.cannamanage.domain.entity")
|
||||
public class CannaManageApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
@@ -12,7 +12,11 @@ import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.time.Instant;
|
||||
import java.util.HexFormat;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
@@ -48,8 +52,8 @@ public class AuthService {
|
||||
user.getId(), user.getTenantId(), roleName, user.getEmail());
|
||||
String refreshToken = jwtService.generateRefreshToken(user.getId(), user.getTenantId());
|
||||
|
||||
// Store hashed refresh token for revocation
|
||||
user.setRefreshTokenHash(passwordEncoder.encode(refreshToken));
|
||||
// Store SHA-256 hashed refresh token for revocation (BCrypt can't handle >72 bytes)
|
||||
user.setRefreshTokenHash(sha256(refreshToken));
|
||||
user.setLastLogin(Instant.now());
|
||||
userRepository.save(user);
|
||||
|
||||
@@ -76,7 +80,7 @@ public class AuthService {
|
||||
|
||||
// Verify the refresh token matches stored hash (revocation check)
|
||||
if (user.getRefreshTokenHash() == null ||
|
||||
!passwordEncoder.matches(token, user.getRefreshTokenHash())) {
|
||||
!sha256(token).equals(user.getRefreshTokenHash())) {
|
||||
throw new AuthenticationException("Refresh token has been revoked");
|
||||
}
|
||||
|
||||
@@ -86,12 +90,27 @@ public class AuthService {
|
||||
user.getId(), user.getTenantId(), roleName, user.getEmail());
|
||||
String newRefreshToken = jwtService.generateRefreshToken(user.getId(), user.getTenantId());
|
||||
|
||||
user.setRefreshTokenHash(passwordEncoder.encode(newRefreshToken));
|
||||
user.setRefreshTokenHash(sha256(newRefreshToken));
|
||||
userRepository.save(user);
|
||||
|
||||
return new LoginResponse(newAccessToken, newRefreshToken, 3600L, roleName);
|
||||
}
|
||||
|
||||
/**
|
||||
* SHA-256 hash for refresh token storage.
|
||||
* JWTs exceed BCrypt's 72-byte limit (enforced in Spring Security 7+).
|
||||
* SHA-256 is appropriate here: refresh tokens are already high-entropy random strings.
|
||||
*/
|
||||
private String sha256(String input) {
|
||||
try {
|
||||
MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
||||
byte[] hash = digest.digest(input.getBytes(StandardCharsets.UTF_8));
|
||||
return HexFormat.of().formatHex(hash);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException("SHA-256 not available", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom authentication exception — caught by GlobalExceptionHandler.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user