🚧 Prefix changes
This commit is contained in:
parent
7a19f9f2dc
commit
e3c584e96d
@ -1,4 +1,4 @@
|
|||||||
package dev.dinauer;
|
package dev.dinauer.oidcproxy;
|
||||||
|
|
||||||
import io.smallrye.jwt.auth.principal.DefaultJWTParser;
|
import io.smallrye.jwt.auth.principal.DefaultJWTParser;
|
||||||
import io.smallrye.jwt.auth.principal.ParseException;
|
import io.smallrye.jwt.auth.principal.ParseException;
|
||||||
111
src/main/java/dev/dinauer/oidcproxy/Resource.java
Normal file
111
src/main/java/dev/dinauer/oidcproxy/Resource.java
Normal 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())));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,7 +1,6 @@
|
|||||||
package dev.dinauer.oidcproxy.callback;
|
package dev.dinauer.oidcproxy.callback;
|
||||||
|
|
||||||
import dev.dinauer.JwtUtils;
|
import dev.dinauer.oidcproxy.JwtUtils;
|
||||||
import dev.dinauer.oidcproxy.AccessToken;
|
|
||||||
import dev.dinauer.oidcproxy.callback.model.TokenResponse;
|
import dev.dinauer.oidcproxy.callback.model.TokenResponse;
|
||||||
import dev.dinauer.oidcproxy.session.SessionCache;
|
import dev.dinauer.oidcproxy.session.SessionCache;
|
||||||
import io.vertx.core.http.Cookie;
|
import io.vertx.core.http.Cookie;
|
||||||
|
|||||||
@ -18,12 +18,8 @@ import java.util.*;
|
|||||||
@ApplicationScoped
|
@ApplicationScoped
|
||||||
public class ForwardService
|
public class ForwardService
|
||||||
{
|
{
|
||||||
private static final String AUTH_HEADER = "Authorization";
|
|
||||||
private static final HttpClient CLIENT = HttpClient.newHttpClient();
|
private static final HttpClient CLIENT = HttpClient.newHttpClient();
|
||||||
|
|
||||||
@Inject
|
|
||||||
SessionCache sessionCache;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
HeaderFilter headerFilter;
|
HeaderFilter headerFilter;
|
||||||
|
|
||||||
@ -44,19 +40,6 @@ public class ForwardService
|
|||||||
throw new ProxyHttpException(response.statusCode());
|
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)
|
private byte[] extractBody(RoutingContext context)
|
||||||
{
|
{
|
||||||
if (context.body().buffer() != null)
|
if (context.body().buffer() != null)
|
||||||
|
|||||||
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package dev.dinauer.oidcproxy;
|
package dev.dinauer.oidcproxy.session;
|
||||||
|
|
||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
|
|
||||||
@ -1,13 +1,11 @@
|
|||||||
package dev.dinauer.oidcproxy.session;
|
package dev.dinauer.oidcproxy.session;
|
||||||
|
|
||||||
import dev.dinauer.oidcproxy.AccessToken;
|
|
||||||
import dev.dinauer.oidcproxy.proxy.exception.TokenNotFoundException;
|
import dev.dinauer.oidcproxy.proxy.exception.TokenNotFoundException;
|
||||||
import io.quarkus.narayana.jta.QuarkusTransaction;
|
import io.quarkus.narayana.jta.QuarkusTransaction;
|
||||||
import io.quarkus.runtime.Startup;
|
import io.quarkus.runtime.Startup;
|
||||||
import jakarta.enterprise.context.ApplicationScoped;
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
import jakarta.enterprise.context.control.ActivateRequestContext;
|
import jakarta.enterprise.context.control.ActivateRequestContext;
|
||||||
import jakarta.inject.Inject;
|
import jakarta.inject.Inject;
|
||||||
import jakarta.transaction.Transactional;
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
package dev.dinauer.oidcproxy.session;
|
package dev.dinauer.oidcproxy.session;
|
||||||
|
|
||||||
import dev.dinauer.oidcproxy.AccessToken;
|
|
||||||
import io.smallrye.jwt.auth.principal.DefaultJWTParser;
|
import io.smallrye.jwt.auth.principal.DefaultJWTParser;
|
||||||
import jakarta.enterprise.context.ApplicationScoped;
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
import jakarta.inject.Inject;
|
import jakarta.inject.Inject;
|
||||||
|
|||||||
@ -57,12 +57,12 @@ public class RouteService
|
|||||||
this.routes = result;
|
this.routes = result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<ProxyRoute> match(List<String> request)
|
public Optional<ProxyRoute> match(String request)
|
||||||
{
|
{
|
||||||
Pair<Integer, ProxyRoute> best = null;
|
Pair<Integer, ProxyRoute> best = null;
|
||||||
for (ProxyRoute proxyRoute : this.routes)
|
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())
|
if (match.isPresent())
|
||||||
{
|
{
|
||||||
int matchLength = match.get();
|
int matchLength = match.get();
|
||||||
|
|||||||
@ -1,7 +1,4 @@
|
|||||||
routes:
|
routes:
|
||||||
- path: /api
|
- path: /
|
||||||
target: http://localhost:8081
|
target: http://localhost:8081
|
||||||
strategy: OIDC
|
strategy: OIDC
|
||||||
- path: /
|
|
||||||
target: http://example.com
|
|
||||||
strategy: NONE
|
|
||||||
Loading…
x
Reference in New Issue
Block a user