Skip to content

Commit e659d95

Browse files
author
Psilo
committed
add user-access interceptor to httpServer
allows to intercept user-credentials for authentication-bound endpoints bump version
1 parent e57f032 commit e659d95

File tree

4 files changed

+66
-6
lines changed

4 files changed

+66
-6
lines changed

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
<modelVersion>4.0.0</modelVersion>
1919
<artifactId>http-server</artifactId>
20-
<version>0.1.2</version>
20+
<version>0.1.3</version>
2121
<name>HttpServer</name>
2222
<packaging>jar</packaging>
2323

src/main/java/info/unterrainer/commons/httpserver/HttpServer.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import java.util.concurrent.LinkedBlockingQueue;
1111
import java.util.concurrent.ThreadPoolExecutor;
1212
import java.util.concurrent.TimeUnit;
13+
import java.util.function.Consumer;
1314

1415
import org.eclipse.jetty.server.Server;
1516
import org.eclipse.jetty.server.ServerConnector;
@@ -26,6 +27,7 @@
2627
import info.unterrainer.commons.httpserver.handlers.HealthHandler;
2728
import info.unterrainer.commons.httpserver.handlers.PostmanCollectionHandler;
2829
import info.unterrainer.commons.httpserver.jsons.MessageJson;
30+
import info.unterrainer.commons.httpserver.jsons.UserDataJson;
2931
import info.unterrainer.commons.jreutils.ShutdownHook;
3032
import info.unterrainer.commons.rdbutils.entities.BasicJpa;
3133
import info.unterrainer.commons.serialization.JsonMapper;
@@ -36,6 +38,7 @@
3638
import io.javalin.http.Handler;
3739
import io.javalin.http.HandlerType;
3840
import lombok.Builder;
41+
import lombok.Getter;
3942
import lombok.extern.slf4j.Slf4j;
4043
import ma.glasnost.orika.MapperFactory;
4144
import ma.glasnost.orika.impl.DefaultMapperFactory;
@@ -53,6 +56,8 @@ public class HttpServer {
5356
private List<HandlerInstance> handlerInstances = new ArrayList<>();
5457
ExecutorService executorService;
5558
List<String> appVersionFqns;
59+
@Getter
60+
private Consumer<UserDataJson> userAccessInterceptor;
5661

5762
private HttpServer() {
5863
}
@@ -167,6 +172,11 @@ public void start() {
167172
javalin.addHandler(hi.handlerType(), hi.path(), hi.handler(), hi.roles());
168173
}
169174

175+
public HttpServer setUserAccessInterceptor(final Consumer<UserDataJson> userAccessInterceptor) {
176+
this.userAccessInterceptor = userAccessInterceptor;
177+
return this;
178+
}
179+
170180
public HttpServer get(final String path, final Handler handler, final Role... roles) {
171181
handlerInstances.add(HandlerInstance.builder()
172182
.path(path)

src/main/java/info/unterrainer/commons/httpserver/accessmanager/HttpAccessManager.java

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import java.net.http.HttpResponse.BodyHandlers;
1010
import java.security.PublicKey;
1111
import java.util.Set;
12+
import java.util.function.Consumer;
1213

1314
import org.eclipse.jetty.http.HttpHeader;
1415
import org.keycloak.TokenVerifier;
@@ -18,10 +19,12 @@
1819

1920
import com.fasterxml.jackson.databind.ObjectMapper;
2021

22+
import info.unterrainer.commons.httpserver.HttpServer;
2123
import info.unterrainer.commons.httpserver.enums.Attribute;
2224
import info.unterrainer.commons.httpserver.exceptions.ForbiddenException;
2325
import info.unterrainer.commons.httpserver.exceptions.GatewayTimeoutException;
2426
import info.unterrainer.commons.httpserver.exceptions.UnauthorizedException;
27+
import info.unterrainer.commons.httpserver.jsons.UserDataJson;
2528
import io.javalin.core.security.AccessManager;
2629
import io.javalin.core.security.Role;
2730
import io.javalin.http.Context;
@@ -49,7 +52,8 @@ public HttpAccessManager(final String host, final String realm) {
4952

5053
@Override
5154
public void manage(final Handler handler, final Context ctx, final Set<Role> permittedRoles) throws Exception {
52-
checkAccess(ctx, permittedRoles);
55+
checkAccess(ctx, permittedRoles,
56+
((HttpServer) ctx.attribute(Attribute.JAVALIN_SERVER)).getUserAccessInterceptor());
5357
handler.handle(ctx);
5458
}
5559

@@ -79,10 +83,13 @@ private void initPublicKey() {
7983
}
8084
return response.body();
8185
}).thenAccept(body -> {
82-
if (body == null)
86+
if (body == null) {
87+
log.warn("Received empty body.");
8388
return;
89+
}
8490
try {
8591
publicKey = objectMapper.readValue(body, PublishedRealmRepresentation.class).getPublicKey();
92+
log.info("Public key received.");
8693
} catch (IOException e) {
8794
log.error("Error parsing answer from keycloak.");
8895
throw new UncheckedIOException(e);
@@ -94,9 +101,10 @@ private void initPublicKey() {
94101
}
95102
}
96103

97-
private void checkAccess(final Context ctx, final Set<Role> permittedRoles) {
104+
private void checkAccess(final Context ctx, final Set<Role> permittedRoles,
105+
final Consumer<UserDataJson> userAccessInterceptor) {
98106
try {
99-
TokenVerifier<AccessToken> tokenVerifier = persistUserInfoInContext(ctx);
107+
TokenVerifier<AccessToken> tokenVerifier = persistUserInfoInContext(ctx, userAccessInterceptor);
100108

101109
if (permittedRoles.isEmpty() || permittedRoles.contains(DefaultRole.OPEN) && permittedRoles.size() == 1)
102110
return;
@@ -137,7 +145,8 @@ private boolean hasPermittedRole(final Context ctx, final Set<Role> permittedRol
137145
return false;
138146
}
139147

140-
private TokenVerifier<AccessToken> persistUserInfoInContext(final Context ctx) {
148+
private TokenVerifier<AccessToken> persistUserInfoInContext(final Context ctx,
149+
final Consumer<UserDataJson> userAccessInterceptor) {
141150
String authorizationHeader = ctx.header(HttpHeader.AUTHORIZATION.asString());
142151

143152
if (authorizationHeader == null || authorizationHeader.isBlank())
@@ -167,6 +176,19 @@ private TokenVerifier<AccessToken> persistUserInfoInContext(final Context ctx) {
167176
clientRoles = token.getResourceAccess().get(key).getRoles();
168177
ctx.attribute(Attribute.USER_CLIENT_ROLES, clientRoles);
169178

179+
userAccessInterceptor.accept(UserDataJson.builder()
180+
.userName(userName)
181+
.givenName(token.getGivenName())
182+
.client(token.getIssuedFor())
183+
.familyName(token.getFamilyName())
184+
.email(token.getEmail())
185+
.emailVerified(token.getEmailVerified())
186+
.realmRoles(token.getRealmAccess().getRoles())
187+
.clientRoles(clientRoles)
188+
.isActive(token.isActive())
189+
.isBearer(token.getType().equalsIgnoreCase("bearer"))
190+
.build());
191+
170192
if (!token.isActive()) {
171193
setTokenRejectionReason(ctx, "Token is inactive.");
172194
return null;
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package info.unterrainer.commons.httpserver.jsons;
2+
3+
import java.util.Set;
4+
5+
import lombok.Data;
6+
import lombok.NoArgsConstructor;
7+
import lombok.experimental.SuperBuilder;
8+
9+
@Data
10+
@NoArgsConstructor
11+
@SuperBuilder(toBuilder = true)
12+
public class UserDataJson {
13+
14+
private String userName;
15+
private String client;
16+
17+
private String givenName;
18+
private String familyName;
19+
20+
private String email;
21+
private boolean emailVerified;
22+
23+
private Set<String> realmRoles;
24+
private Set<String> clientRoles;
25+
26+
private boolean isActive;
27+
private boolean isBearer;
28+
}

0 commit comments

Comments
 (0)