test: add 25 tests for vendor + portal services and controllers
Some checks failed
CI / build (push) Failing after 1m16s
CI / docker (push) Has been skipped

VendorTenantServiceTest (8): create/provision, suspend, delete, renew
VendorTenantControllerTest (7): CRUD, auth, conflict handling
TenantPortalServiceTest (5): dashboard, license, settings
TenantPortalControllerTest (5): dashboard, license, settings, auth

Fix TenantIsolationInterceptor bugs found by tests:
- org_id resolution now runs before portal path check
- path matching uses URI minus context path (not getServletPath)
- portal path returns 403 sendError instead of empty 200

Total: 50 tests, 0 failures.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-04-09 23:08:47 +02:00
parent faac0048c3
commit 17fbe73e60
5 changed files with 749 additions and 8 deletions

View File

@@ -34,19 +34,19 @@ public class TenantIsolationInterceptor implements HandlerInterceptor {
var authentication = SecurityContextHolder.getContext().getAuthentication();
if (!(authentication instanceof JwtAuthenticationToken jwtAuth)) return true;
String path = request.getRequestURI();
// Strip context-path prefix to get the application-relative path.
// getServletPath() returns empty string in MockMvc, so use getRequestURI() minus contextPath.
String contextPath = request.getContextPath();
String uri = request.getRequestURI();
String path = (contextPath != null && !contextPath.isEmpty() && uri.startsWith(contextPath))
? uri.substring(contextPath.length()) : uri;
// Vendor endpoints: platform:admin already enforced by Spring Security
if (path.startsWith("/platform/api/vendor/")) {
if (path.startsWith("/api/vendor/")) {
return true;
}
// Tenant portal endpoints: tenant resolved from JWT org context (no path variable)
if (path.startsWith("/platform/api/tenant/")) {
return TenantContext.getTenantId() != null;
}
// 1. Resolve: JWT organization_id -> TenantContext
// 1. Resolve: JWT organization_id -> TenantContext (applies to all non-vendor paths)
Jwt jwt = jwtAuth.getToken();
String orgId = jwt.getClaimAsString("organization_id");
if (orgId != null) {
@@ -54,6 +54,15 @@ public class TenantIsolationInterceptor implements HandlerInterceptor {
.ifPresent(tenant -> TenantContext.setTenantId(tenant.getId()));
}
// Tenant portal endpoints: tenant resolved from JWT org context (no path variable)
if (path.startsWith("/api/tenant/")) {
if (TenantContext.getTenantId() == null) {
response.sendError(HttpServletResponse.SC_FORBIDDEN, "No organization context");
return false;
}
return true;
}
// 2. Validate: read path variables from Spring's HandlerMapping
@SuppressWarnings("unchecked")
Map<String, String> pathVars = (Map<String, String>) request.getAttribute(