Skip to content

Commit

Permalink
[Major] Refactored REST request/response filters adding LogMessage on…
Browse files Browse the repository at this point in the history
… exception, setting CertificateThreadLocal
  • Loading branch information
eitch committed Jul 11, 2024
1 parent 60434b3 commit 17642c9
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 29 deletions.
3 changes: 2 additions & 1 deletion agent/src/main/resources/strolch-agent.properties
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ operationsLog.persist.failed=Failed to persist OperationsLog due to: {reason}
agent.query.failed.access.denied=User {user} may not perform query {query}
agent.search.failed=Search {search} failed due to: {reason}
agent.search.failed.access.denied=User {user} may not perform search {search}
agent.service.failed.access.denied=User {user} may not perform service {service}
agent.service.failed.access.denied=User {user} may not perform service {service}
web.rest.exception=REST Call failed due to: {exception}
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,20 @@
import jakarta.ws.rs.ext.Provider;
import li.strolch.exception.StrolchAccessDeniedException;
import li.strolch.exception.StrolchNotAuthenticatedException;
import li.strolch.handler.operationslog.OperationsLog;
import li.strolch.model.Locator;
import li.strolch.model.log.LogMessage;
import li.strolch.model.log.LogMessageState;
import li.strolch.model.log.LogSeverity;
import li.strolch.privilege.model.CertificateThreadLocal;
import li.strolch.rest.helper.ResponseUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.text.MessageFormat;
import java.util.ResourceBundle;

import static li.strolch.model.Tags.AGENT;

@Provider
public class StrolchRestfulExceptionMapper implements ExceptionMapper<Exception> {
Expand All @@ -39,6 +48,24 @@ public Response toResponse(Exception ex) {

logger.error(MessageFormat.format("Handling exception {0}", ex.getClass()), ex);

RestfulStrolchComponent instance = RestfulStrolchComponent.getInstance();
if (instance.hasComponent(OperationsLog.class)) {
try {
String username = CertificateThreadLocal.hasCert() ? CertificateThreadLocal.getCert().getUsername() :
"anonymous";
String realm = instance.getAgent().getRealmNames().iterator().next();
OperationsLog operationsLog = instance.getComponent(OperationsLog.class);
operationsLog.addMessage(new LogMessage(realm, username,
Locator.valueOf(AGENT, RestfulStrolchComponent.class.getSimpleName(),
ex.getClass().getSimpleName()), LogSeverity.Exception, LogMessageState.Information,
ResourceBundle.getBundle("strolch-agent"), "web.rest.exception")
.withException(ex)
.value("exception", ex));
} catch (Exception e) {
logger.error("Failed to add log message for exception!", e);
}
}

return switch (ex) {
case NotFoundException ignored -> ResponseUtil.toResponse(Status.NOT_FOUND, ex);
case StrolchAccessDeniedException e -> ResponseUtil.toResponse(Status.FORBIDDEN, e.getI18n());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import li.strolch.exception.StrolchAccessDeniedException;
import li.strolch.exception.StrolchNotAuthenticatedException;
import li.strolch.privilege.model.Certificate;
import li.strolch.privilege.model.CertificateThreadLocal;
import li.strolch.privilege.model.Usage;
import li.strolch.rest.RestfulStrolchComponent;
import li.strolch.rest.StrolchRestfulConstants;
Expand All @@ -35,10 +36,7 @@
import org.slf4j.LoggerFactory;

import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.*;

import static li.strolch.rest.StrolchRestfulConstants.*;
import static li.strolch.utils.helper.StringHelper.*;
Expand Down Expand Up @@ -116,12 +114,15 @@ public void filter(ContainerRequestContext requestContext) {

try {

Optional<Certificate> certificate;
if (isUnsecuredPath(requestContext)) {
setCertificateIfAvailable(requestContext, remoteIp);
certificate = setCertificateIfAvailable(requestContext, remoteIp);
} else {
validateSession(requestContext, remoteIp);
certificate = validateSession(requestContext, remoteIp);
}

certificate.ifPresent(CertificateThreadLocal::setCert);

} catch (StrolchNotAuthenticatedException e) {
logger.error(e.getMessage());
requestContext.abortWith(Response
Expand All @@ -146,30 +147,28 @@ public void filter(ContainerRequestContext requestContext) {
}
}

protected void setCertificateIfAvailable(ContainerRequestContext requestContext, String remoteIp) {
protected Optional<Certificate> setCertificateIfAvailable(ContainerRequestContext requestContext, String remoteIp) {
StrolchSessionHandler sessionHandler = getSessionHandler();

String sessionId = trimOrEmpty(requestContext.getHeaderString(HttpHeaders.AUTHORIZATION));
if (isNotEmpty(sessionId)) {
if (sessionHandler.isSessionKnown(sessionId)) {
validateCertificate(requestContext, sessionId, remoteIp);
return validateCertificate(requestContext, sessionId, remoteIp);
} else {
logger.error("Session {} by authorization header does not exist anymore, ignoring!", sessionId);
return Optional.empty();
}

return;
}

sessionId = getSessionIdFromCookie(requestContext);
if (isEmpty(sessionId)) {
return;
}
if (isEmpty(sessionId))
return Optional.empty();

if (sessionHandler.isSessionKnown(sessionId)) {
validateCertificate(requestContext, sessionId, remoteIp);
} else {
logger.error("Session {} by cookie does not exist anymore, ignoring!", sessionId);
}
if (sessionHandler.isSessionKnown(sessionId))
return validateCertificate(requestContext, sessionId, remoteIp);

logger.debug("Session {} by cookie does not exist anymore, ignoring!", sessionId);
return Optional.empty();
}

/**
Expand All @@ -187,7 +186,7 @@ protected void setCertificateIfAvailable(ContainerRequestContext requestContext,
* @return the certificate for the validated session, or null, of the request is aborted to no missing or invalid
* authorization token
*/
protected Certificate validateSession(ContainerRequestContext requestContext, String remoteIp) {
protected Optional<Certificate> validateSession(ContainerRequestContext requestContext, String remoteIp) {
String authorization = trimOrEmpty(requestContext.getHeaderString(HttpHeaders.AUTHORIZATION));
if (authorization.isEmpty())
return validateCookie(requestContext, remoteIp);
Expand All @@ -208,7 +207,7 @@ protected String getSessionIdFromCookie(ContainerRequestContext requestContext)
return sessionId.trim();
}

protected Certificate validateCookie(ContainerRequestContext requestContext, String remoteIp) {
protected Optional<Certificate> validateCookie(ContainerRequestContext requestContext, String remoteIp) {
String sessionId = getSessionIdFromCookie(requestContext);
if (isEmpty(sessionId)) {
logger.error("No Authorization header or cookie on request to URL {}",
Expand All @@ -218,13 +217,13 @@ protected Certificate validateCookie(ContainerRequestContext requestContext, Str
.header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN)
.entity("Missing Authorization!")
.build());
return null;
return Optional.empty();
}

return validateCertificate(requestContext, sessionId, remoteIp);
}

protected Certificate authenticateBasic(ContainerRequestContext requestContext, String authorization,
protected Optional<Certificate> authenticateBasic(ContainerRequestContext requestContext, String authorization,
String remoteIp) {

if (!getRestful().isBasicAuthEnabled()) {
Expand All @@ -234,7 +233,7 @@ protected Certificate authenticateBasic(ContainerRequestContext requestContext,
.header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN)
.entity("Basic Auth not available")
.build());
return null;
return Optional.empty();
}

String basicAuth = authorization.substring("Basic ".length());
Expand All @@ -246,7 +245,7 @@ protected Certificate authenticateBasic(ContainerRequestContext requestContext,
.header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN)
.entity("Invalid Basic Authorization!")
.build());
return null;
return Optional.empty();
}

logger.info("Performing basic auth for user {}...", parts[0]);
Expand All @@ -257,12 +256,22 @@ protected Certificate authenticateBasic(ContainerRequestContext requestContext,
requestContext.setProperty(STROLCH_CERTIFICATE, certificate);
requestContext.setProperty(STROLCH_REQUEST_SOURCE, remoteIp);

return certificate;
return Optional.ofNullable(certificate);
}

protected Certificate validateCertificate(ContainerRequestContext requestContext, String sessionId,
protected Optional<Certificate> validateCertificate(ContainerRequestContext requestContext, String sessionId,
String remoteIp) {
StrolchSessionHandler sessionHandler = getSessionHandler();
if (!sessionHandler.isSessionKnown(sessionId)) {
logger.debug("Ignoring unknown session!");
requestContext.abortWith(Response
.status(Response.Status.UNAUTHORIZED)
.header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN)
.entity("User is not authenticated!")
.build());
return Optional.empty();
}

Certificate certificate = sessionHandler.validate(sessionId, remoteIp);

if (certificate.getUsage() == Usage.SET_PASSWORD) {
Expand All @@ -275,12 +284,12 @@ protected Certificate validateCertificate(ContainerRequestContext requestContext
.header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN)
.entity("Can only set password!")
.build());
return null;
return Optional.empty();
}
}

requestContext.setProperty(STROLCH_CERTIFICATE, certificate);
requestContext.setProperty(STROLCH_REQUEST_SOURCE, remoteIp);
return certificate;
return Optional.of(certificate);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import jakarta.ws.rs.ext.Provider;

import li.strolch.privilege.model.Certificate;
import li.strolch.privilege.model.CertificateThreadLocal;
import li.strolch.rest.RestfulStrolchComponent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -40,6 +41,9 @@ public class AuthenticationResponseFilter implements ContainerResponseFilter {
@Override
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) {

if (CertificateThreadLocal.hasCert())
CertificateThreadLocal.removeCert();

Certificate cert = (Certificate) requestContext.getProperty(STROLCH_CERTIFICATE);
if (cert == null)
return;
Expand Down

0 comments on commit 17642c9

Please sign in to comment.