PermissionsFilter.java

package de.dlr.shepard.filters;

import java.util.List;

import de.dlr.shepard.exceptions.ApiError;
import de.dlr.shepard.security.GracePeriodUtil;
import de.dlr.shepard.security.PermissionsUtil;
import de.dlr.shepard.util.AccessType;
import de.dlr.shepard.util.Constants;
import jakarta.annotation.Priority;
import jakarta.ws.rs.HttpMethod;
import jakarta.ws.rs.Priorities;
import jakarta.ws.rs.container.ContainerRequestContext;
import jakarta.ws.rs.container.ContainerRequestFilter;
import jakarta.ws.rs.core.PathSegment;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.Response.Status;
import jakarta.ws.rs.ext.Provider;
import lombok.extern.slf4j.Slf4j;

@Provider
@Slf4j
@Priority(Priorities.AUTHORIZATION)
public class PermissionsFilter implements ContainerRequestFilter {

	private static final int FIVE_MINUTES_IN_MILLIS = 5 * 60 * 1000;
	private static final List<String> writerMethods = List.of(HttpMethod.PUT, HttpMethod.POST, HttpMethod.DELETE);
	private static final List<String> readerMethods = List.of(HttpMethod.GET);

	private final GracePeriodUtil lastSeen;

	public PermissionsFilter() {
		lastSeen = new GracePeriodUtil(FIVE_MINUTES_IN_MILLIS);
	}

	@Override
	public void filter(ContainerRequestContext requestContext) {
		var principal = requestContext.getSecurityContext().getUserPrincipal();
		if (principal == null || principal.getName() == null) {
			log.warn("Unknown principal {}", principal);
			abort(requestContext, "User could not be read from the request context");
			return;
		}

		var lastSeenKey = principal.getName() + requestContext.getMethod() + requestContext.getUriInfo().getPath();
		if (lastSeen.elementIsKnown(lastSeenKey)) {
			return;
		}
		var permissionsUtil = getPermissionsUtil();
		var accessType = getAccessType(requestContext.getUriInfo().getPathSegments(), requestContext.getMethod());
		if (permissionsUtil.isAllowed(requestContext.getUriInfo().getPathSegments(), accessType, principal.getName())) {
			lastSeen.elementSeen(lastSeenKey);
			return;
		}

		abort(requestContext, "The requested action is forbidden by the permission policies");

	}

	private void abort(ContainerRequestContext requestContext, String reason) {
		requestContext.abortWith(Response.status(Status.FORBIDDEN)
				.entity(new ApiError(Status.FORBIDDEN.getStatusCode(), "AuthenticationException", reason)).build());
	}

	private AccessType getAccessType(List<PathSegment> pathSegments, String requestMethod) {
		if (pathSegments.stream().anyMatch(seg -> seg.getPath().equals(Constants.PERMISSIONS)))
			return AccessType.Manage;
		if (readerMethods.contains(requestMethod))
			return AccessType.Read;
		if (writerMethods.contains(requestMethod))
			return AccessType.Write;
		return AccessType.None;
	}

	protected PermissionsUtil getPermissionsUtil() {
		return new PermissionsUtil();
	}

}