WorkspaceAccessWebMvcConfiguration.java
package com.taxonomy.workspace.controller;
import com.taxonomy.workspace.service.WorkspaceAccessService;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.io.IOException;
import java.security.Principal;
import java.util.Map;
/**
* Applies ownership checks to workspace metadata reads before controller data is
* materialized. Shared workspaces remain visible, while guessed private IDs are
* deliberately indistinguishable from missing IDs.
*/
@Configuration(proxyBeanMethods = false)
class WorkspaceAccessWebMvcConfiguration implements WebMvcConfigurer {
private final WorkspaceAccessService workspaceAccessService;
WorkspaceAccessWebMvcConfiguration(WorkspaceAccessService workspaceAccessService) {
this.workspaceAccessService = workspaceAccessService;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new WorkspaceInfoAccessInterceptor(workspaceAccessService))
.addPathPatterns("/api/workspace/*/info");
}
static final class WorkspaceInfoAccessInterceptor implements HandlerInterceptor {
private final WorkspaceAccessService workspaceAccessService;
WorkspaceInfoAccessInterceptor(WorkspaceAccessService workspaceAccessService) {
this.workspaceAccessService = workspaceAccessService;
}
@Override
public boolean preHandle(
HttpServletRequest request,
HttpServletResponse response,
Object handler) throws IOException {
if (!(handler instanceof HandlerMethod handlerMethod)
|| handlerMethod.getBeanType() != WorkspaceController.class
|| !"getWorkspaceInfo".equals(handlerMethod.getMethod().getName())) {
return true;
}
Principal principal = request.getUserPrincipal();
if (principal == null) {
// The SecurityFilterChain owns the unauthenticated response.
return true;
}
String workspaceId = pathVariable(request, "id");
if (workspaceAccessService.canReadWorkspaceMetadata(
workspaceId, principal.getName())) {
return true;
}
response.sendError(HttpStatus.NOT_FOUND.value());
return false;
}
private static String pathVariable(HttpServletRequest request, String name) {
Object value = request.getAttribute(
HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
if (!(value instanceof Map<?, ?> variables)) {
return null;
}
Object pathValue = variables.get(name);
return pathValue instanceof String text ? text : null;
}
}
}