Skip to content

Commit

Permalink
Merge pull request #771 from nscuro/mdc-request-metadata-enrichment
Browse files Browse the repository at this point in the history
  • Loading branch information
nscuro authored Feb 26, 2025
2 parents 2cefd10 + a62ea8d commit 93756a0
Show file tree
Hide file tree
Showing 2 changed files with 176 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* This file is part of Dependency-Track.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
* Copyright (c) OWASP Foundation. All Rights Reserved.
*/
package alpine.server.filters;

import org.glassfish.jersey.server.ContainerRequest;
import org.glassfish.jersey.server.ExtendedUriInfo;
import org.glassfish.jersey.uri.UriTemplate;
import org.slf4j.MDC;

import jakarta.annotation.Priority;
import jakarta.ws.rs.container.ContainerRequestContext;
import jakarta.ws.rs.container.ContainerRequestFilter;
import jakarta.ws.rs.container.ContainerResponseContext;
import jakarta.ws.rs.container.ContainerResponseFilter;
import jakarta.ws.rs.ext.Provider;
import java.io.IOException;
import java.util.StringJoiner;
import java.util.regex.Pattern;

/**
* @since 3.2.0
*/
@Provider
@Priority(2)
public class RequestMdcEnrichmentFilter implements ContainerRequestFilter, ContainerResponseFilter {

private static final Pattern TRIM_SLASHES_PATTERN = Pattern.compile("//+");

@Override
public void filter(final ContainerRequestContext requestContext) throws IOException {
MDC.put("requestMethod", requestContext.getMethod());
MDC.put("requestUri", getRequestUri(requestContext));
}

@Override
public void filter(final ContainerRequestContext requestContext, final ContainerResponseContext responseContext) throws IOException {
MDC.remove("requestMethod");
MDC.remove("requestUri");
}

private String getRequestUri(final ContainerRequestContext requestContext) {
if (!(requestContext instanceof final ContainerRequest containerRequest)) {
throw new IllegalStateException();
}

final ExtendedUriInfo uriInfo = containerRequest.getUriInfo();
if (uriInfo.getMatchedTemplates().isEmpty()) {
return null;
}

final var pathJoiner = new StringJoiner("/");
for (final UriTemplate uriTemplate : uriInfo.getMatchedTemplates().reversed()) {
pathJoiner.add(uriTemplate.getTemplate());
}

return TRIM_SLASHES_PATTERN.matcher(pathJoiner.toString()).replaceAll("/");
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*
* This file is part of Alpine.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
* Copyright (c) Steve Springett. All Rights Reserved.
*/
package alpine.server.filters;

import alpine.server.resources.AlpineResource;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.jupiter.api.Test;
import org.slf4j.MDC;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.client.Entity;
import jakarta.ws.rs.core.Application;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import java.util.Map;

import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;

public class RequestMdcEnrichmentFilterTest extends JerseyTest {

@Path("/")
public static class TestResource extends AlpineResource {

@GET
@Produces(MediaType.APPLICATION_JSON)
public Response get() {
return Response.ok(getRequestMetadata()).build();
}

@POST
@Path("/foo/{bar}/baz")
@Produces(MediaType.APPLICATION_JSON)
public Response post(@PathParam("bar") final String ignored) {
return Response.ok(getRequestMetadata()).build();
}

private Map<String, Object> getRequestMetadata() {
return Map.ofEntries(
Map.entry("requestMethod", MDC.get("requestMethod")),
Map.entry("requestUri", MDC.get("requestUri")));
}

}

@Override
protected Application configure() {
return new ResourceConfig(TestResource.class)
.register(RequestMdcEnrichmentFilter.class);
}

@Test
void shouldIncludeRequestMethodAndRootUri() {
final Response response = target("/")
.request()
.get();
assertThat(response.getStatus()).isEqualTo(200);
assertThatJson(response.readEntity(String.class)).isEqualTo(/* language=JSON */ """
{
"requestMethod": "GET",
"requestUri": "/"
}
""");
}

@Test
void shouldIncludeRequestMethodAndUriWithPathParamPlaceholders() {
final Response response = target("/foo/qux/baz")
.request()
.post(Entity.text(""));
assertThat(response.getStatus()).isEqualTo(200);
assertThatJson(response.readEntity(String.class)).isEqualTo(/* language=JSON */ """
{
"requestMethod": "POST",
"requestUri": "/foo/{bar}/baz"
}
""");
}

}

0 comments on commit 93756a0

Please sign in to comment.