diff --git a/pom.xml b/pom.xml index c406a692e..9dabab446 100644 --- a/pom.xml +++ b/pom.xml @@ -16,7 +16,7 @@ 1.12.2 5.11.2 - 9.2.0 + 9.4.11 0.5.10 ${project.version} diff --git a/src/main/java/com/uid2/admin/auth/AdminAuthMiddleware.java b/src/main/java/com/uid2/admin/auth/AdminAuthMiddleware.java index a5460644f..ead01877d 100644 --- a/src/main/java/com/uid2/admin/auth/AdminAuthMiddleware.java +++ b/src/main/java/com/uid2/admin/auth/AdminAuthMiddleware.java @@ -9,6 +9,8 @@ import io.vertx.ext.web.RoutingContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.uid2.shared.audit.AuditParams; +import com.uid2.shared.audit.Audit; import java.util.*; @@ -17,6 +19,7 @@ public class AdminAuthMiddleware { private final AuthProvider authProvider; private final String environment; private final boolean isAuthDisabled; + private final Audit audit; final Map> roleToOktaGroups = new EnumMap<>(Role.class); public AdminAuthMiddleware(AuthProvider authProvider, JsonObject config) { @@ -26,6 +29,7 @@ public AdminAuthMiddleware(AuthProvider authProvider, JsonObject config) { roleToOktaGroups.put(Role.MAINTAINER, parseOktaGroups(config.getString(AdminConst.ROLE_OKTA_GROUP_MAP_MAINTAINER))); roleToOktaGroups.put(Role.PRIVILEGED, parseOktaGroups(config.getString(AdminConst.ROLE_OKTA_GROUP_MAP_PRIVILEGED))); roleToOktaGroups.put(Role.SUPER_USER, parseOktaGroups(config.getString(AdminConst.ROLE_OKTA_GROUP_MAP_SUPER_USER))); + this.audit = new Audit(AdminAuthMiddleware.class.getPackage().getName()); } private List parseOktaGroups(final String oktaGroups) { @@ -40,15 +44,29 @@ private List parseOktaGroups(final String oktaGroups) { return allOktaGroups; } - public Handler handle(Handler handler, Role... roles) { + public Handler handle(Handler handler, AuditParams params, Role... roles) { if (isAuthDisabled) return handler; if (roles == null || roles.length == 0) { throw new IllegalArgumentException("must specify at least one role"); } - AdminAuthHandler adminAuthHandler = new AdminAuthHandler(handler, authProvider, Set.of(roles), environment, roleToOktaGroups); + Handler loggedHandler = logAndHandle(handler, params); + AdminAuthHandler adminAuthHandler = new AdminAuthHandler(loggedHandler, authProvider, Set.of(roles), + environment, roleToOktaGroups); return adminAuthHandler::handle; } + public Handler handle(Handler handler, Role... roles) { + return this.handle(handler, new AuditParams(), roles); + } + + + private Handler logAndHandle(Handler handler, AuditParams params) { + return ctx -> { + ctx.addBodyEndHandler(v -> this.audit.log(ctx, params)); + handler.handle(ctx); + }; + } + private static class AdminAuthHandler { private final String environment; private final Handler innerHandler; @@ -133,6 +151,10 @@ private void validateAccessToken(RoutingContext rc, String accessToken) { return; } List scopes = (List) jwt.getClaims().get("scp"); + JsonObject serviceAccountDetails = new JsonObject(); + serviceAccountDetails.put("scope", scopes); + serviceAccountDetails.put("client_id", jwt.getClaims().get("client_id")); + rc.put("userDetails", serviceAccountDetails); if(isAuthorizedService(scopes)) { innerHandler.handle(rc); } else { @@ -154,6 +176,11 @@ private void validateIdToken(RoutingContext rc, String idToken) { return; } List groups = (List) jwt.getClaims().get("groups"); + JsonObject userDetails = new JsonObject(); + userDetails.put("groups", groups); + userDetails.put("email", jwt.getClaims().get("email")); + userDetails.put("sub", jwt.getClaims().get("sub")); + rc.put("userDetails", userDetails); if(isAuthorizedUser(groups)) { innerHandler.handle(rc); } else { diff --git a/src/test/java/com/uid2/admin/auth/AdminAuthMiddlewareTest.java b/src/test/java/com/uid2/admin/auth/AdminAuthMiddlewareTest.java index 48a5509ca..46c68f2bb 100644 --- a/src/test/java/com/uid2/admin/auth/AdminAuthMiddlewareTest.java +++ b/src/test/java/com/uid2/admin/auth/AdminAuthMiddlewareTest.java @@ -9,6 +9,7 @@ import io.vertx.core.Handler; import io.vertx.core.http.HttpServerRequest; import io.vertx.core.http.HttpServerResponse; +import io.vertx.core.json.JsonArray; import io.vertx.core.json.JsonObject; import io.vertx.ext.auth.User; import io.vertx.ext.web.RoutingContext; @@ -22,10 +23,11 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import java.util.List; -import java.util.Map; +import java.util.*; +import java.util.stream.Collectors; import java.util.stream.Stream; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; @@ -67,6 +69,20 @@ public void setup() { when(rc.response()).thenReturn(response); when(rc.session()).thenReturn(session); + Map contextData = new HashMap<>(); + + when(rc.put(anyString(), any())).thenAnswer(invocation -> { + String key = invocation.getArgument(0); + Object value = invocation.getArgument(1); + contextData.put(key, value); + return rc; // Return rc for chaining + }); + + when(rc.get(anyString())).thenAnswer(invocation -> { + String key = invocation.getArgument(0); + return contextData.get(key); + }); + when(response.setStatusCode(anyInt())).thenReturn(response); when(response.putHeader(anyString(), anyString())).thenReturn(response); } @@ -151,7 +167,7 @@ public void testIdToken_GoodTokenUnauthorized() throws JwtVerificationException handler.handle(rc); verify(idTokenVerifier).decode(eq("testIdToken"), any()); - verify(jwt, times(3)).getClaims(); + verify(jwt, times(5)).getClaims(); verifyUnauthorized(false); } @@ -173,7 +189,7 @@ public void testIdToken_GoodTokenRealRoleUnauthorized(List userOktaGroup handler.handle(rc); verify(idTokenVerifier).decode(eq("testIdToken"), any()); - verify(jwt, times(3)).getClaims(); + verify(jwt, times(5)).getClaims(); verifyUnauthorized(false); } @@ -197,9 +213,13 @@ public void testIdToken_GoodTokenAuthorized(List userOktaGroups, Role... Handler handler = adminAuthMiddleware.handle(innerHandler, endpointRoles); handler.handle(rc); - + JsonObject userDetails = rc.get("userDetails"); + Set groups = userDetails.getJsonArray("groups").stream() + .map(Object::toString) + .collect(Collectors.toSet()); + assertEquals(new HashSet<>(userOktaGroups), groups); verify(idTokenVerifier).decode(eq("testIdToken"), any()); - verify(jwt, times(3)).getClaims(); + verify(jwt, times(5)).getClaims(); verify(innerHandler).handle(eq(rc)); } @@ -251,7 +271,7 @@ public void testAccessToken_GoodTokenUnauthorized(String customOktaScope, Role.. handler.handle(rc); verify(accessTokenVerifier).decode(eq("testAccessToken")); - verify(jwt, times(3)).getClaims(); + verify(jwt, times(4)).getClaims(); verifyUnauthorized(false); } @@ -272,9 +292,14 @@ public void testAccessToken_GoodTokenAuthorized(OktaCustomScope scope, Role allo Handler handler = adminAuthMiddleware.handle(innerHandler, allowedRole); handler.handle(rc); + JsonObject userDetails = rc.get("userDetails"); + Set scopes = userDetails.getJsonArray("scope").stream() + .map(Object::toString) + .collect(Collectors.toSet()); + assertEquals(Set.of(scope.getName()), scopes); verify(accessTokenVerifier).decode(eq("testAccessToken")); - verify(jwt, times(3)).getClaims(); + verify(jwt, times(4)).getClaims(); verify(innerHandler).handle(eq(rc)); } }