refactor: remove Phase 1 auth endpoints, switch to Logto OIDC
Auth is now handled by Logto. Removed AuthController, AuthService, and related DTOs. Integration tests use Spring Security JWT mocks. Ed25519 JwtService retained for machine token signing. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,54 +0,0 @@
|
||||
package net.siegeln.cameleer.saas.auth;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.validation.Valid;
|
||||
import net.siegeln.cameleer.saas.auth.dto.AuthResponse;
|
||||
import net.siegeln.cameleer.saas.auth.dto.LoginRequest;
|
||||
import net.siegeln.cameleer.saas.auth.dto.RegisterRequest;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/auth")
|
||||
public class AuthController {
|
||||
|
||||
private final AuthService authService;
|
||||
|
||||
public AuthController(AuthService authService) {
|
||||
this.authService = authService;
|
||||
}
|
||||
|
||||
@PostMapping("/register")
|
||||
public ResponseEntity<AuthResponse> register(@Valid @RequestBody RegisterRequest request,
|
||||
HttpServletRequest httpRequest) {
|
||||
try {
|
||||
var response = authService.register(request, extractClientIp(httpRequest));
|
||||
return ResponseEntity.status(HttpStatus.CREATED).body(response);
|
||||
} catch (IllegalArgumentException e) {
|
||||
return ResponseEntity.status(HttpStatus.CONFLICT).build();
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping("/login")
|
||||
public ResponseEntity<AuthResponse> login(@Valid @RequestBody LoginRequest request,
|
||||
HttpServletRequest httpRequest) {
|
||||
try {
|
||||
var response = authService.login(request, extractClientIp(httpRequest));
|
||||
return ResponseEntity.ok(response);
|
||||
} catch (IllegalArgumentException e) {
|
||||
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
|
||||
}
|
||||
}
|
||||
|
||||
private String extractClientIp(HttpServletRequest request) {
|
||||
String xForwardedFor = request.getHeader("X-Forwarded-For");
|
||||
if (xForwardedFor != null && !xForwardedFor.isEmpty()) {
|
||||
return xForwardedFor.split(",")[0].trim();
|
||||
}
|
||||
return request.getRemoteAddr();
|
||||
}
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
package net.siegeln.cameleer.saas.auth;
|
||||
|
||||
import net.siegeln.cameleer.saas.audit.AuditAction;
|
||||
import net.siegeln.cameleer.saas.audit.AuditService;
|
||||
import net.siegeln.cameleer.saas.auth.dto.AuthResponse;
|
||||
import net.siegeln.cameleer.saas.auth.dto.LoginRequest;
|
||||
import net.siegeln.cameleer.saas.auth.dto.RegisterRequest;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class AuthService {
|
||||
|
||||
private final UserRepository userRepository;
|
||||
private final RoleRepository roleRepository;
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
private final JwtService jwtService;
|
||||
private final AuditService auditService;
|
||||
|
||||
public AuthService(UserRepository userRepository,
|
||||
RoleRepository roleRepository,
|
||||
PasswordEncoder passwordEncoder,
|
||||
JwtService jwtService,
|
||||
AuditService auditService) {
|
||||
this.userRepository = userRepository;
|
||||
this.roleRepository = roleRepository;
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
this.jwtService = jwtService;
|
||||
this.auditService = auditService;
|
||||
}
|
||||
|
||||
public AuthResponse register(RegisterRequest request, String sourceIp) {
|
||||
if (userRepository.existsByEmail(request.email())) {
|
||||
throw new IllegalArgumentException("Email already registered");
|
||||
}
|
||||
|
||||
var user = new UserEntity();
|
||||
user.setEmail(request.email());
|
||||
user.setName(request.name());
|
||||
user.setPassword(passwordEncoder.encode(request.password()));
|
||||
|
||||
roleRepository.findByName("OWNER").ifPresent(role -> user.getRoles().add(role));
|
||||
|
||||
var saved = userRepository.save(user);
|
||||
var token = jwtService.generateToken(saved);
|
||||
|
||||
auditService.log(
|
||||
saved.getId(), saved.getEmail(), null,
|
||||
AuditAction.AUTH_REGISTER, null,
|
||||
null, sourceIp,
|
||||
"SUCCESS", null
|
||||
);
|
||||
|
||||
return new AuthResponse(token, saved.getEmail(), saved.getName());
|
||||
}
|
||||
|
||||
public AuthResponse login(LoginRequest request, String sourceIp) {
|
||||
var user = userRepository.findByEmail(request.email())
|
||||
.orElseThrow(() -> new IllegalArgumentException("Invalid credentials"));
|
||||
|
||||
if (!passwordEncoder.matches(request.password(), user.getPassword())) {
|
||||
auditService.log(
|
||||
user.getId(), user.getEmail(), null,
|
||||
AuditAction.AUTH_LOGIN_FAILED, null,
|
||||
null, sourceIp,
|
||||
"FAILURE", null
|
||||
);
|
||||
throw new IllegalArgumentException("Invalid credentials");
|
||||
}
|
||||
|
||||
var token = jwtService.generateToken(user);
|
||||
|
||||
auditService.log(
|
||||
user.getId(), user.getEmail(), null,
|
||||
AuditAction.AUTH_LOGIN, null,
|
||||
null, sourceIp,
|
||||
"SUCCESS", null
|
||||
);
|
||||
|
||||
return new AuthResponse(token, user.getEmail(), user.getName());
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
package net.siegeln.cameleer.saas.auth.dto;
|
||||
|
||||
public record AuthResponse(
|
||||
String token,
|
||||
String email,
|
||||
String name
|
||||
) {
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package net.siegeln.cameleer.saas.auth.dto;
|
||||
|
||||
import jakarta.validation.constraints.Email;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
|
||||
public record LoginRequest(
|
||||
@NotBlank @Email String email,
|
||||
@NotBlank String password
|
||||
) {
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package net.siegeln.cameleer.saas.auth.dto;
|
||||
|
||||
import jakarta.validation.constraints.Email;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Size;
|
||||
|
||||
public record RegisterRequest(
|
||||
@NotBlank @Email String email,
|
||||
@NotBlank String name,
|
||||
@NotBlank @Size(min = 8, max = 128) String password
|
||||
) {
|
||||
}
|
||||
@@ -47,7 +47,6 @@ public class SecurityConfig {
|
||||
.csrf(csrf -> csrf.disable())
|
||||
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
||||
.authorizeHttpRequests(auth -> auth
|
||||
.requestMatchers("/api/auth/**").permitAll()
|
||||
.requestMatchers("/actuator/health").permitAll()
|
||||
.requestMatchers("/auth/verify").permitAll()
|
||||
.anyRequest().authenticated()
|
||||
|
||||
@@ -32,8 +32,14 @@ public class LicenseController {
|
||||
var tenant = tenantService.getById(tenantId).orElse(null);
|
||||
if (tenant == null) return ResponseEntity.notFound().build();
|
||||
|
||||
UUID actorId = authentication.getCredentials() instanceof UUID uid
|
||||
? uid : UUID.fromString(authentication.getCredentials().toString());
|
||||
// Extract actor ID from JWT subject (Logto OIDC: sub may be a non-UUID string)
|
||||
String sub = authentication.getName();
|
||||
UUID actorId;
|
||||
try {
|
||||
actorId = UUID.fromString(sub);
|
||||
} catch (IllegalArgumentException e) {
|
||||
actorId = UUID.nameUUIDFromBytes(sub.getBytes());
|
||||
}
|
||||
|
||||
var license = licenseService.generateLicense(tenant, Duration.ofDays(365), actorId);
|
||||
return ResponseEntity.status(HttpStatus.CREATED).body(toResponse(license));
|
||||
|
||||
@@ -29,9 +29,14 @@ public class TenantController {
|
||||
public ResponseEntity<TenantResponse> create(@Valid @RequestBody CreateTenantRequest request,
|
||||
Authentication authentication) {
|
||||
try {
|
||||
// Extract actor ID from authentication credentials (Phase 1: userId stored as credentials)
|
||||
UUID actorId = authentication.getCredentials() instanceof UUID uid
|
||||
? uid : UUID.fromString(authentication.getCredentials().toString());
|
||||
// Extract actor ID from JWT subject (Logto OIDC: sub may be a non-UUID string)
|
||||
String sub = authentication.getName();
|
||||
UUID actorId;
|
||||
try {
|
||||
actorId = UUID.fromString(sub);
|
||||
} catch (IllegalArgumentException e) {
|
||||
actorId = UUID.nameUUIDFromBytes(sub.getBytes());
|
||||
}
|
||||
|
||||
var entity = tenantService.create(request, actorId);
|
||||
return ResponseEntity.status(HttpStatus.CREATED).body(toResponse(entity));
|
||||
|
||||
@@ -1,126 +0,0 @@
|
||||
package net.siegeln.cameleer.saas.auth;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import net.siegeln.cameleer.saas.TestcontainersConfig;
|
||||
import net.siegeln.cameleer.saas.TestSecurityConfig;
|
||||
import net.siegeln.cameleer.saas.auth.dto.LoginRequest;
|
||||
import net.siegeln.cameleer.saas.auth.dto.RegisterRequest;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
@SpringBootTest
|
||||
@AutoConfigureMockMvc
|
||||
@Import({TestcontainersConfig.class, TestSecurityConfig.class})
|
||||
@ActiveProfiles("test")
|
||||
class AuthControllerTest {
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
@Test
|
||||
void register_returns201WithToken() throws Exception {
|
||||
var request = new RegisterRequest("newuser@example.com", "New User", "password123");
|
||||
|
||||
mockMvc.perform(post("/api/auth/register")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(objectMapper.writeValueAsString(request)))
|
||||
.andExpect(status().isCreated())
|
||||
.andExpect(jsonPath("$.token").isNotEmpty())
|
||||
.andExpect(jsonPath("$.email").value("newuser@example.com"))
|
||||
.andExpect(jsonPath("$.name").value("New User"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void register_returns409ForDuplicateEmail() throws Exception {
|
||||
var request = new RegisterRequest("duplicate@example.com", "User One", "password123");
|
||||
|
||||
// First registration
|
||||
mockMvc.perform(post("/api/auth/register")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(objectMapper.writeValueAsString(request)))
|
||||
.andExpect(status().isCreated());
|
||||
|
||||
// Duplicate registration
|
||||
mockMvc.perform(post("/api/auth/register")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(objectMapper.writeValueAsString(request)))
|
||||
.andExpect(status().isConflict());
|
||||
}
|
||||
|
||||
@Test
|
||||
void login_returns200WithToken() throws Exception {
|
||||
var registerRequest = new RegisterRequest("loginuser@example.com", "Login User", "password123");
|
||||
|
||||
mockMvc.perform(post("/api/auth/register")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(objectMapper.writeValueAsString(registerRequest)))
|
||||
.andExpect(status().isCreated());
|
||||
|
||||
var loginRequest = new LoginRequest("loginuser@example.com", "password123");
|
||||
|
||||
mockMvc.perform(post("/api/auth/login")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(objectMapper.writeValueAsString(loginRequest)))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.token").isNotEmpty())
|
||||
.andExpect(jsonPath("$.email").value("loginuser@example.com"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void login_returns401ForBadPassword() throws Exception {
|
||||
var registerRequest = new RegisterRequest("badpass@example.com", "Bad Pass", "password123");
|
||||
|
||||
mockMvc.perform(post("/api/auth/register")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(objectMapper.writeValueAsString(registerRequest)))
|
||||
.andExpect(status().isCreated());
|
||||
|
||||
var loginRequest = new LoginRequest("badpass@example.com", "wrong-password");
|
||||
|
||||
mockMvc.perform(post("/api/auth/login")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(objectMapper.writeValueAsString(loginRequest)))
|
||||
.andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
@Test
|
||||
void protectedEndpoint_returns401WithoutToken() throws Exception {
|
||||
mockMvc.perform(get("/api/health/secured"))
|
||||
.andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
@Test
|
||||
void protectedEndpoint_returns200WithValidToken() throws Exception {
|
||||
// Register to get a token
|
||||
var registerRequest = new RegisterRequest("secured@example.com", "Secured User", "password123");
|
||||
|
||||
var result = mockMvc.perform(post("/api/auth/register")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(objectMapper.writeValueAsString(registerRequest)))
|
||||
.andExpect(status().isCreated())
|
||||
.andReturn();
|
||||
|
||||
var responseBody = objectMapper.readTree(result.getResponse().getContentAsString());
|
||||
String token = responseBody.get("token").asText();
|
||||
|
||||
// Access protected endpoint with token
|
||||
mockMvc.perform(get("/api/health/secured")
|
||||
.header("Authorization", "Bearer " + token))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.status").value("authenticated"));
|
||||
}
|
||||
}
|
||||
@@ -1,171 +0,0 @@
|
||||
package net.siegeln.cameleer.saas.auth;
|
||||
|
||||
import net.siegeln.cameleer.saas.audit.AuditAction;
|
||||
import net.siegeln.cameleer.saas.audit.AuditService;
|
||||
import net.siegeln.cameleer.saas.auth.dto.LoginRequest;
|
||||
import net.siegeln.cameleer.saas.auth.dto.RegisterRequest;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class AuthServiceTest {
|
||||
|
||||
@Mock
|
||||
private UserRepository userRepository;
|
||||
@Mock
|
||||
private RoleRepository roleRepository;
|
||||
@Mock
|
||||
private PasswordEncoder passwordEncoder;
|
||||
@Mock
|
||||
private JwtService jwtService;
|
||||
@Mock
|
||||
private AuditService auditService;
|
||||
|
||||
private AuthService authService;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
authService = new AuthService(userRepository, roleRepository,
|
||||
passwordEncoder, jwtService, auditService);
|
||||
}
|
||||
|
||||
@Test
|
||||
void register_createsUserAndReturnsToken() {
|
||||
var request = new RegisterRequest("user@example.com", "Test User", "password123");
|
||||
var ownerRole = new RoleEntity();
|
||||
ownerRole.setName("OWNER");
|
||||
|
||||
when(userRepository.existsByEmail("user@example.com")).thenReturn(false);
|
||||
when(passwordEncoder.encode("password123")).thenReturn("encoded-password");
|
||||
when(roleRepository.findByName("OWNER")).thenReturn(Optional.of(ownerRole));
|
||||
when(userRepository.save(any(UserEntity.class))).thenAnswer(invocation -> {
|
||||
UserEntity user = invocation.getArgument(0);
|
||||
// simulate ID assignment by persistence
|
||||
try {
|
||||
var idField = UserEntity.class.getDeclaredField("id");
|
||||
idField.setAccessible(true);
|
||||
idField.set(user, java.util.UUID.randomUUID());
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return user;
|
||||
});
|
||||
when(jwtService.generateToken(any(UserEntity.class))).thenReturn("test-jwt-token");
|
||||
|
||||
var response = authService.register(request, "127.0.0.1");
|
||||
|
||||
assertNotNull(response);
|
||||
assertEquals("test-jwt-token", response.token());
|
||||
assertEquals("user@example.com", response.email());
|
||||
assertEquals("Test User", response.name());
|
||||
|
||||
// Verify audit was logged
|
||||
verify(auditService).log(
|
||||
any(), eq("user@example.com"), eq(null),
|
||||
eq(AuditAction.AUTH_REGISTER), eq(null),
|
||||
eq(null), eq("127.0.0.1"),
|
||||
eq("SUCCESS"), eq(null)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void register_rejectsDuplicateEmail() {
|
||||
var request = new RegisterRequest("existing@example.com", "Test User", "password123");
|
||||
when(userRepository.existsByEmail("existing@example.com")).thenReturn(true);
|
||||
|
||||
var exception = assertThrows(IllegalArgumentException.class,
|
||||
() -> authService.register(request, "127.0.0.1"));
|
||||
|
||||
assertEquals("Email already registered", exception.getMessage());
|
||||
verify(userRepository, never()).save(any());
|
||||
verify(auditService, never()).log(any(), any(), any(), any(), any(), any(), any(), any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void login_returnsTokenForValidCredentials() {
|
||||
var request = new LoginRequest("user@example.com", "password123");
|
||||
var user = createUserWithId("user@example.com", "encoded-password");
|
||||
|
||||
when(userRepository.findByEmail("user@example.com")).thenReturn(Optional.of(user));
|
||||
when(passwordEncoder.matches("password123", "encoded-password")).thenReturn(true);
|
||||
when(jwtService.generateToken(user)).thenReturn("login-jwt-token");
|
||||
|
||||
var response = authService.login(request, "192.168.1.1");
|
||||
|
||||
assertNotNull(response);
|
||||
assertEquals("login-jwt-token", response.token());
|
||||
assertEquals("user@example.com", response.email());
|
||||
|
||||
verify(auditService).log(
|
||||
any(), eq("user@example.com"), eq(null),
|
||||
eq(AuditAction.AUTH_LOGIN), eq(null),
|
||||
eq(null), eq("192.168.1.1"),
|
||||
eq("SUCCESS"), eq(null)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void login_rejectsInvalidPassword() {
|
||||
var request = new LoginRequest("user@example.com", "wrong-password");
|
||||
var user = createUserWithId("user@example.com", "encoded-password");
|
||||
|
||||
when(userRepository.findByEmail("user@example.com")).thenReturn(Optional.of(user));
|
||||
when(passwordEncoder.matches("wrong-password", "encoded-password")).thenReturn(false);
|
||||
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> authService.login(request, "192.168.1.1"));
|
||||
|
||||
// Verify AUTH_LOGIN_FAILED audit was logged
|
||||
verify(auditService).log(
|
||||
any(), eq("user@example.com"), eq(null),
|
||||
eq(AuditAction.AUTH_LOGIN_FAILED), eq(null),
|
||||
eq(null), eq("192.168.1.1"),
|
||||
eq("FAILURE"), eq(null)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void login_rejectsUnknownEmail() {
|
||||
var request = new LoginRequest("unknown@example.com", "password123");
|
||||
|
||||
when(userRepository.findByEmail("unknown@example.com")).thenReturn(Optional.empty());
|
||||
|
||||
var exception = assertThrows(IllegalArgumentException.class,
|
||||
() -> authService.login(request, "192.168.1.1"));
|
||||
|
||||
assertEquals("Invalid credentials", exception.getMessage());
|
||||
verify(auditService, never()).log(any(), any(), any(), any(), any(), any(), any(), any(), any());
|
||||
}
|
||||
|
||||
private UserEntity createUserWithId(String email, String password) {
|
||||
var user = new UserEntity();
|
||||
user.setEmail(email);
|
||||
user.setName("Test User");
|
||||
user.setPassword(password);
|
||||
try {
|
||||
var idField = UserEntity.class.getDeclaredField("id");
|
||||
idField.setAccessible(true);
|
||||
idField.set(user, UUID.randomUUID());
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return user;
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import org.springframework.http.MediaType;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.jwt;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
@@ -30,25 +31,12 @@ class LicenseControllerTest {
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
private String getAuthToken() throws Exception {
|
||||
var registerRequest = new net.siegeln.cameleer.saas.auth.dto.RegisterRequest(
|
||||
"license-test-" + System.nanoTime() + "@example.com", "Test User", "password123");
|
||||
|
||||
var result = mockMvc.perform(post("/api/auth/register")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(objectMapper.writeValueAsString(registerRequest)))
|
||||
.andExpect(status().isCreated())
|
||||
.andReturn();
|
||||
|
||||
return objectMapper.readTree(result.getResponse().getContentAsString()).get("token").asText();
|
||||
}
|
||||
|
||||
private String createTenantAndGetId(String token) throws Exception {
|
||||
private String createTenantAndGetId() throws Exception {
|
||||
String slug = "license-tenant-" + System.nanoTime();
|
||||
var request = new CreateTenantRequest("License Test Org", slug, "MID");
|
||||
|
||||
var result = mockMvc.perform(post("/api/tenants")
|
||||
.header("Authorization", "Bearer " + token)
|
||||
.with(jwt().jwt(j -> j.claim("sub", "test-user")))
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(objectMapper.writeValueAsString(request)))
|
||||
.andExpect(status().isCreated())
|
||||
@@ -59,11 +47,10 @@ class LicenseControllerTest {
|
||||
|
||||
@Test
|
||||
void generateLicense_returns201WithToken() throws Exception {
|
||||
String token = getAuthToken();
|
||||
String tenantId = createTenantAndGetId(token);
|
||||
String tenantId = createTenantAndGetId();
|
||||
|
||||
mockMvc.perform(post("/api/tenants/" + tenantId + "/license")
|
||||
.header("Authorization", "Bearer " + token))
|
||||
.with(jwt().jwt(j -> j.claim("sub", "test-user"))))
|
||||
.andExpect(status().isCreated())
|
||||
.andExpect(jsonPath("$.token").isNotEmpty())
|
||||
.andExpect(jsonPath("$.tier").value("MID"))
|
||||
@@ -72,26 +59,24 @@ class LicenseControllerTest {
|
||||
|
||||
@Test
|
||||
void getActiveLicense_returnsLicense() throws Exception {
|
||||
String token = getAuthToken();
|
||||
String tenantId = createTenantAndGetId(token);
|
||||
String tenantId = createTenantAndGetId();
|
||||
|
||||
mockMvc.perform(post("/api/tenants/" + tenantId + "/license")
|
||||
.header("Authorization", "Bearer " + token))
|
||||
.with(jwt().jwt(j -> j.claim("sub", "test-user"))))
|
||||
.andExpect(status().isCreated());
|
||||
|
||||
mockMvc.perform(get("/api/tenants/" + tenantId + "/license")
|
||||
.header("Authorization", "Bearer " + token))
|
||||
.with(jwt().jwt(j -> j.claim("sub", "test-user"))))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.tier").value("MID"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getActiveLicense_returns404WhenNone() throws Exception {
|
||||
String token = getAuthToken();
|
||||
String tenantId = createTenantAndGetId(token);
|
||||
String tenantId = createTenantAndGetId();
|
||||
|
||||
mockMvc.perform(get("/api/tenants/" + tenantId + "/license")
|
||||
.header("Authorization", "Bearer " + token))
|
||||
.with(jwt().jwt(j -> j.claim("sub", "test-user"))))
|
||||
.andExpect(status().isNotFound());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import org.springframework.http.MediaType;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.jwt;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
@@ -30,26 +31,14 @@ class TenantControllerTest {
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
private String getAuthToken() throws Exception {
|
||||
var registerRequest = new net.siegeln.cameleer.saas.auth.dto.RegisterRequest(
|
||||
"tenant-test-" + System.nanoTime() + "@example.com", "Test User", "password123");
|
||||
|
||||
var result = mockMvc.perform(post("/api/auth/register")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(objectMapper.writeValueAsString(registerRequest)))
|
||||
.andExpect(status().isCreated())
|
||||
.andReturn();
|
||||
|
||||
return objectMapper.readTree(result.getResponse().getContentAsString()).get("token").asText();
|
||||
}
|
||||
|
||||
@Test
|
||||
void createTenant_returns201() throws Exception {
|
||||
String token = getAuthToken();
|
||||
var request = new CreateTenantRequest("Test Org", "test-org-" + System.nanoTime(), "LOW");
|
||||
|
||||
mockMvc.perform(post("/api/tenants")
|
||||
.header("Authorization", "Bearer " + token)
|
||||
.with(jwt().jwt(j -> j
|
||||
.claim("sub", "test-user")
|
||||
.claim("organization_id", "test-org")))
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(objectMapper.writeValueAsString(request)))
|
||||
.andExpect(status().isCreated())
|
||||
@@ -60,18 +49,17 @@ class TenantControllerTest {
|
||||
|
||||
@Test
|
||||
void createTenant_returns409ForDuplicateSlug() throws Exception {
|
||||
String token = getAuthToken();
|
||||
String slug = "duplicate-slug-" + System.nanoTime();
|
||||
var request = new CreateTenantRequest("First", slug, null);
|
||||
|
||||
mockMvc.perform(post("/api/tenants")
|
||||
.header("Authorization", "Bearer " + token)
|
||||
.with(jwt().jwt(j -> j.claim("sub", "test-user")))
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(objectMapper.writeValueAsString(request)))
|
||||
.andExpect(status().isCreated());
|
||||
|
||||
mockMvc.perform(post("/api/tenants")
|
||||
.header("Authorization", "Bearer " + token)
|
||||
.with(jwt().jwt(j -> j.claim("sub", "test-user")))
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(objectMapper.writeValueAsString(request)))
|
||||
.andExpect(status().isConflict());
|
||||
@@ -89,12 +77,11 @@ class TenantControllerTest {
|
||||
|
||||
@Test
|
||||
void getTenant_returnsTenantById() throws Exception {
|
||||
String token = getAuthToken();
|
||||
String slug = "get-test-" + System.nanoTime();
|
||||
var request = new CreateTenantRequest("Get Test", slug, null);
|
||||
|
||||
var createResult = mockMvc.perform(post("/api/tenants")
|
||||
.header("Authorization", "Bearer " + token)
|
||||
.with(jwt().jwt(j -> j.claim("sub", "test-user")))
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(objectMapper.writeValueAsString(request)))
|
||||
.andExpect(status().isCreated())
|
||||
@@ -103,7 +90,7 @@ class TenantControllerTest {
|
||||
String id = objectMapper.readTree(createResult.getResponse().getContentAsString()).get("id").asText();
|
||||
|
||||
mockMvc.perform(get("/api/tenants/" + id)
|
||||
.header("Authorization", "Bearer " + token))
|
||||
.with(jwt().jwt(j -> j.claim("sub", "test-user"))))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.slug").value(slug));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user