🚧 Prefix changes

This commit is contained in:
Andreas Dinauer 2026-04-11 12:08:52 +02:00
parent 7a19f9f2dc
commit e3c584e96d
10 changed files with 118 additions and 144 deletions

View File

@ -1,4 +1,4 @@
package dev.dinauer;
package dev.dinauer.oidcproxy;
import io.smallrye.jwt.auth.principal.DefaultJWTParser;
import io.smallrye.jwt.auth.principal.ParseException;

View File

@ -0,0 +1,111 @@
package dev.dinauer.oidcproxy;
import dev.dinauer.oidcproxy.callback.CallbackService;
import dev.dinauer.oidcproxy.proxy.ForwardService;
import dev.dinauer.oidcproxy.proxy.ResponseHandler;
import dev.dinauer.oidcproxy.proxy.exception.ProxyHttpException;
import dev.dinauer.oidcproxy.proxy.exception.TokenNotFoundException;
import dev.dinauer.oidcproxy.startup.PathConverter;
import dev.dinauer.oidcproxy.startup.ProxyRoute;
import dev.dinauer.oidcproxy.startup.RouteService;
import io.quarkus.vertx.web.Route;
import io.smallrye.common.annotation.Blocking;
import io.vertx.core.http.HttpServerResponse;
import io.vertx.ext.web.RoutingContext;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.core.Context;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.http.HttpResponse;
import java.util.*;
@ApplicationScoped
public class Resource
{
private static final Logger LOG = LoggerFactory.getLogger(Resource.class);
@Inject
RouteService routeService;
@Inject
ForwardService forwardService;
@Inject
CallbackService callbackService;
@Inject
LogoutService logoutService;
@Route(path = "/auth/callback", order = 0)
@Blocking
public void callback(@Context RoutingContext context)
{
callbackService.get(context.response(), context.request());
}
@Route(path = "/auth/logout", order = 1)
@Blocking
public void logout(@Context HttpServerResponse response)
{
logoutService.logout(response);
}
@Route(path = "/api/*", order = 2)
@Blocking
public void proxy(@Context RoutingContext context)
{
String path = dropPrefix(context.request().path(), "/api");
Optional<ProxyRoute> match = routeService.match(path);
if (match.isPresent())
{
route(context, path, match.get());
}
else
{
LOG.error("No route found for request path '{}'", context.request().path());
ResponseHandler.notFound(context);
}
}
private void route(RoutingContext context, String path, ProxyRoute route)
{
List<String> requestSegments = PathConverter.toSegments(path);
LOG.info("Matched route with target '{}'", route.target());
try
{
String targetURI = route.target() + PathConverter.toPath(requestSegments);
HttpResponse<byte[]> response = forwardService.send(context, targetURI, route.strategy());
ResponseHandler.success(context, response);
}
catch (ProxyHttpException e)
{
LOG.error("Upstream returned error status {}.", e.getStatusCode(), e);
ResponseHandler.error(context, e.getStatusCode());
}
catch (TokenNotFoundException e)
{
LOG.error("Token not found.", e);
ResponseHandler.error(context, 401);
}
catch (InterruptedException e)
{
LOG.error("Proxy request was interrupted, returning 503.", e);
Thread.currentThread().interrupt();
ResponseHandler.error(context, 503);
}
catch (Exception e)
{
LOG.error("Error occurred on upstream.", e);
ResponseHandler.error(context, 502);
}
}
private String dropPrefix(String pathRaw, String prefixRaw)
{
List<String> path = PathConverter.toSegments(pathRaw);
List<String> prefix = PathConverter.toSegments(prefixRaw);
return PathConverter.toPath(new LinkedList<>(path.subList(prefix.size(), path.size())));
}
}

View File

@ -1,7 +1,6 @@
package dev.dinauer.oidcproxy.callback;
import dev.dinauer.JwtUtils;
import dev.dinauer.oidcproxy.AccessToken;
import dev.dinauer.oidcproxy.JwtUtils;
import dev.dinauer.oidcproxy.callback.model.TokenResponse;
import dev.dinauer.oidcproxy.session.SessionCache;
import io.vertx.core.http.Cookie;

View File

@ -18,12 +18,8 @@ import java.util.*;
@ApplicationScoped
public class ForwardService
{
private static final String AUTH_HEADER = "Authorization";
private static final HttpClient CLIENT = HttpClient.newHttpClient();
@Inject
SessionCache sessionCache;
@Inject
HeaderFilter headerFilter;
@ -44,19 +40,6 @@ public class ForwardService
throw new ProxyHttpException(response.statusCode());
}
private List<Map.Entry<String, String>> buildHeaders(List<Map.Entry<String, String>> request, Set<Cookie> cookies, String strategy) throws TokenNotFoundException
{
List<String> headerNames = request.stream().map(Map.Entry::getKey).toList();
if (!headerNames.contains(AUTH_HEADER) && "OIDC".equals(strategy) && cookies.size() == 1)
{
String session = cookies.iterator().next().getValue();
List<Map.Entry<String, String>> headers = new LinkedList<>(request);
headers.add(Map.entry(AUTH_HEADER, sessionCache.get(session)));
return headers;
}
return request;
}
private byte[] extractBody(RoutingContext context)
{
if (context.body().buffer() != null)

View File

@ -1,113 +0,0 @@
package dev.dinauer.oidcproxy.proxy;
import dev.dinauer.oidcproxy.LogoutService;
import dev.dinauer.oidcproxy.callback.CallbackService;
import dev.dinauer.oidcproxy.proxy.exception.ProxyHttpException;
import dev.dinauer.oidcproxy.proxy.exception.TokenNotFoundException;
import dev.dinauer.oidcproxy.session.SessionCache;
import dev.dinauer.oidcproxy.startup.PathConverter;
import dev.dinauer.oidcproxy.startup.ProxyRoute;
import dev.dinauer.oidcproxy.startup.RouteService;
import io.quarkus.vertx.web.Route;
import io.smallrye.common.annotation.Blocking;
import io.vertx.core.http.Cookie;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.core.http.HttpServerResponse;
import io.vertx.ext.web.RoutingContext;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.core.Context;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.http.HttpResponse;
import java.util.*;
@ApplicationScoped
public class ProxyResource
{
private static final Logger LOG = LoggerFactory.getLogger(ProxyResource.class);
@Inject
RouteService routeService;
@Inject
ForwardService forwardService;
@Inject
CallbackService callbackService;
@Inject
LogoutService logoutService;
@Route(path = "/auth/callback", order = 0)
@Blocking
public void callback(@Context RoutingContext context)
{
callbackService.get(context.response(), context.request());
}
@Route(path = "/auth/logout", order = 1)
@Blocking
public void logout(@Context HttpServerResponse response)
{
logoutService.logout(response);
}
@Route(path = "/*", order = 2)
@Blocking
public void proxy(@Context RoutingContext context)
{
HttpServerRequest request = context.request();
System.out.println(request.path());
List<String> requestSegments = PathConverter.toSegments(request.path());
Optional<ProxyRoute> routeOptional = routeService.match(requestSegments);
if (routeOptional.isPresent())
{
ProxyRoute route = routeOptional.get();
LOG.info("Matched route with target '{}'", route.target());
try
{
String targetURI = route.target() + PathConverter.toPath(requestSegments);
HttpResponse<byte[]> response = forwardService.send(context, targetURI, route.strategy());
ResponseHandler.success(context, response);
}
catch (ProxyHttpException e)
{
LOG.error("Upstream returned error status {}.", e.getStatusCode(), e);
ResponseHandler.error(context, e.getStatusCode());
}
catch (TokenNotFoundException e)
{
LOG.error("Token not found.", e);
ResponseHandler.error(context, 401);
}
catch (InterruptedException e)
{
LOG.error("Proxy request was interrupted, returning 503.", e);
Thread.currentThread().interrupt();
ResponseHandler.error(context, 503);
}
catch (Exception e)
{
LOG.error("Error occurred on upstream.", e);
ResponseHandler.error(context, 502);
}
}
else
{
LOG.error("No route found for request path '{}'", context.request().path());
ResponseHandler.notFound(context);
}
}
private List<String> dropPrefix(List<String> route, List<String> request)
{
for (int i = 0; i < route.size(); i++)
{
request.removeFirst();
}
return request;
}
}

View File

@ -1,4 +1,4 @@
package dev.dinauer.oidcproxy;
package dev.dinauer.oidcproxy.session;
import java.time.ZonedDateTime;

View File

@ -1,13 +1,11 @@
package dev.dinauer.oidcproxy.session;
import dev.dinauer.oidcproxy.AccessToken;
import dev.dinauer.oidcproxy.proxy.exception.TokenNotFoundException;
import io.quarkus.narayana.jta.QuarkusTransaction;
import io.quarkus.runtime.Startup;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.context.control.ActivateRequestContext;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import org.jboss.logging.Logger;
import java.security.MessageDigest;

View File

@ -1,6 +1,5 @@
package dev.dinauer.oidcproxy.session;
import dev.dinauer.oidcproxy.AccessToken;
import io.smallrye.jwt.auth.principal.DefaultJWTParser;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;

View File

@ -57,12 +57,12 @@ public class RouteService
this.routes = result;
}
public Optional<ProxyRoute> match(List<String> request)
public Optional<ProxyRoute> match(String request)
{
Pair<Integer, ProxyRoute> best = null;
for (ProxyRoute proxyRoute : this.routes)
{
Optional<Integer> match = SegmentMatcher.match(proxyRoute.segments(), request);
Optional<Integer> match = SegmentMatcher.match(proxyRoute.segments(), PathConverter.toSegments(request));
if (match.isPresent())
{
int matchLength = match.get();

View File

@ -1,7 +1,4 @@
routes:
- path: /api
- path: /
target: http://localhost:8081
strategy: OIDC
- path: /
target: http://example.com
strategy: NONE