From 65b392ec36570a4d29110c5b603d0ebf58948f6d Mon Sep 17 00:00:00 2001 From: Sandor Molnar Date: Thu, 19 Oct 2023 20:00:46 +0200 Subject: [PATCH] CDPD-62595, KNOX-2970: Removing KnoxSSO cookie from the token state service upon logout (#806) Additionally, the Token Management UI displays the 'current' KnoxSSO cookie row in bold. Change-Id: I2c1f44d4fda67eeae5396dfb56a7116303a06fa3 --- .../jwt/filter/SSOCookieFederationFilter.java | 2 ++ .../service/knoxsso/KnoxSSOutMessages.java | 3 ++ .../service/knoxsso/WebSSOutResource.java | 29 +++++++++++++++++++ .../service/session/SessionInformation.java | 11 +++++++ .../service/session/SessionResource.java | 4 ++- .../services/security/token/TokenUtils.java | 1 + .../app/session.information.ts | 1 + .../app/token.management.component.html | 8 ++--- .../app/token.management.component.ts | 14 +++++++-- 9 files changed, 66 insertions(+), 7 deletions(-) diff --git a/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/SSOCookieFederationFilter.java b/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/SSOCookieFederationFilter.java index 81d6e1a97c..e329037de6 100644 --- a/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/SSOCookieFederationFilter.java +++ b/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/SSOCookieFederationFilter.java @@ -22,6 +22,7 @@ import org.apache.knox.gateway.i18n.messages.MessagesFactory; import org.apache.knox.gateway.provider.federation.jwt.JWTMessages; import org.apache.knox.gateway.security.PrimaryPrincipal; +import org.apache.knox.gateway.services.security.token.TokenUtils; import org.apache.knox.gateway.services.security.token.UnknownTokenException; import org.apache.knox.gateway.services.security.token.impl.JWT; import org.apache.knox.gateway.services.security.token.impl.JWTToken; @@ -165,6 +166,7 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha JWT token = new JWTToken(wireToken); if (validateToken(req, res, chain, token)) { Subject subject = createSubjectFromToken(token); + request.setAttribute(TokenUtils.ATTR_CURRENT_KNOXSSO_COOKIE_TOKEN_ID, token.getClaim(JWTToken.KNOX_ID_CLAIM)); continueWithEstablishedSecurityContext(subject, req, res, chain); // we found a valid cookie we don't need to keep checking anymore diff --git a/gateway-service-knoxssout/src/main/java/org/apache/knox/gateway/service/knoxsso/KnoxSSOutMessages.java b/gateway-service-knoxssout/src/main/java/org/apache/knox/gateway/service/knoxsso/KnoxSSOutMessages.java index e94dab8974..72ce31217d 100644 --- a/gateway-service-knoxssout/src/main/java/org/apache/knox/gateway/service/knoxsso/KnoxSSOutMessages.java +++ b/gateway-service-knoxssout/src/main/java/org/apache/knox/gateway/service/knoxsso/KnoxSSOutMessages.java @@ -28,4 +28,7 @@ public interface KnoxSSOutMessages { @Message(level = MessageLevel.WARN, text = "Could not find cookie with the name: {0} in the request to be removed from the concurrent session counter for user: {1}. ") void couldNotFindCookieWithTokenToRemove(String cookieName, String username); + + @Message( level = MessageLevel.INFO, text = "Knox Token service ({0}) revoked token {1} ({2}) (renewer={3})") + void revokedToken(String topologyName, String tokenDisplayText, String tokenId, String renewer); } \ No newline at end of file diff --git a/gateway-service-knoxssout/src/main/java/org/apache/knox/gateway/service/knoxsso/WebSSOutResource.java b/gateway-service-knoxssout/src/main/java/org/apache/knox/gateway/service/knoxsso/WebSSOutResource.java index 0ce4237602..72f2ffcc05 100644 --- a/gateway-service-knoxssout/src/main/java/org/apache/knox/gateway/service/knoxsso/WebSSOutResource.java +++ b/gateway-service-knoxssout/src/main/java/org/apache/knox/gateway/service/knoxsso/WebSSOutResource.java @@ -18,9 +18,15 @@ package org.apache.knox.gateway.service.knoxsso; import org.apache.knox.gateway.i18n.messages.MessagesFactory; +import org.apache.knox.gateway.security.SubjectUtils; import org.apache.knox.gateway.services.GatewayServices; import org.apache.knox.gateway.services.ServiceType; +import org.apache.knox.gateway.services.security.token.TokenStateService; +import org.apache.knox.gateway.services.security.token.TokenUtils; +import org.apache.knox.gateway.services.security.token.UnknownTokenException; +import org.apache.knox.gateway.services.security.token.impl.JWTToken; import org.apache.knox.gateway.session.control.ConcurrentSessionVerifier; +import org.apache.knox.gateway.util.Tokens; import org.apache.knox.gateway.util.Urls; import javax.annotation.PostConstruct; @@ -35,6 +41,7 @@ import javax.ws.rs.core.Context; import javax.ws.rs.core.Response; import java.net.MalformedURLException; +import java.text.ParseException; import java.util.Arrays; import java.util.Optional; @@ -118,6 +125,7 @@ private boolean removeAuthenticationToken(HttpServletResponse response) { if (gwServices != null) { ConcurrentSessionVerifier verifier = gwServices.getService(ServiceType.CONCURRENT_SESSION_VERIFIER); verifier.sessionEndedForUser(request.getUserPrincipal().getName(), ssoCookie.get().getValue()); + removeKnoxSsoCookie(ssoCookie.get(), gwServices); } } else { log.couldNotFindCookieWithTokenToRemove(cookieName, request.getUserPrincipal().getName()); @@ -125,6 +133,27 @@ private boolean removeAuthenticationToken(HttpServletResponse response) { return rc; } + private void removeKnoxSsoCookie(Cookie ssoCookie, GatewayServices gwServices) { + final TokenStateService tokenStateService = gwServices.getService(ServiceType.TOKEN_STATE_SERVICE); + if (tokenStateService!= null) { + try { + final JWTToken jwt = new JWTToken(ssoCookie.getValue()); + tokenStateService.revokeToken(jwt); + final String revoker = SubjectUtils.getCurrentEffectivePrincipalName(); + log.revokedToken(getTopologyName(), + Tokens.getTokenDisplayText(ssoCookie.getValue()), + Tokens.getTokenIDDisplayText(TokenUtils.getTokenId(jwt)), + revoker); + } catch (ParseException | UnknownTokenException e) { + // NOP: cookie maybe invalid or token management was disabled anyway + } + } + } + + private String getTopologyName() { + return (String) context.getAttribute("org.apache.knox.gateway.gateway.cluster"); + } + private Optional findCookie(String cookieName) { Cookie[] cookies = request.getCookies(); if (cookies != null) { diff --git a/gateway-service-session/src/main/java/org/apache/knox/gateway/service/session/SessionInformation.java b/gateway-service-session/src/main/java/org/apache/knox/gateway/service/session/SessionInformation.java index 9ebba46324..d572ebf5d3 100644 --- a/gateway-service-session/src/main/java/org/apache/knox/gateway/service/session/SessionInformation.java +++ b/gateway-service-session/src/main/java/org/apache/knox/gateway/service/session/SessionInformation.java @@ -37,6 +37,9 @@ public class SessionInformation { @XmlElement private boolean canSeeAllTokens; + @XmlElement + private String currentKnoxSsoCookieTokenId; + public String getUser() { return user; } @@ -77,4 +80,12 @@ public void setCanSeeAllTokens(boolean canSeeAllTokens) { this.canSeeAllTokens = canSeeAllTokens; } + public String getCurrentKnoxSsoCookieTokenId() { + return currentKnoxSsoCookieTokenId; + } + + public void setCurrentKnoxSsoCookieTokenId(String currentKnoxSsoCookieTokenId) { + this.currentKnoxSsoCookieTokenId = currentKnoxSsoCookieTokenId; + } + } diff --git a/gateway-service-session/src/main/java/org/apache/knox/gateway/service/session/SessionResource.java b/gateway-service-session/src/main/java/org/apache/knox/gateway/service/session/SessionResource.java index ad6b3bdf08..40dda8e816 100644 --- a/gateway-service-session/src/main/java/org/apache/knox/gateway/service/session/SessionResource.java +++ b/gateway-service-session/src/main/java/org/apache/knox/gateway/service/session/SessionResource.java @@ -31,6 +31,7 @@ import org.apache.knox.gateway.config.GatewayConfig; import org.apache.knox.gateway.i18n.messages.MessagesFactory; import org.apache.knox.gateway.security.SubjectUtils; +import org.apache.knox.gateway.services.security.token.TokenUtils; @Singleton @Path("session/api/v1/") @@ -58,7 +59,8 @@ public SessionInformation getSessionInformation() { sessionInfo.setLogoutPageUrl(getLogoutPageUrl(config)); sessionInfo.setGlobalLogoutPageUrl(getGlobalLogoutPageUrl(config)); } - sessionInfo.setCanSeeAllTokens(config == null ? false : config.canSeeAllTokens(user)); + sessionInfo.setCanSeeAllTokens(config != null ? config.canSeeAllTokens(user) : false); + sessionInfo.setCurrentKnoxSsoCookieTokenId((String) this.request.getAttribute(TokenUtils.ATTR_CURRENT_KNOXSSO_COOKIE_TOKEN_ID)); return sessionInfo; } diff --git a/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/TokenUtils.java b/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/TokenUtils.java index 4ba1defb02..e9620a4258 100644 --- a/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/TokenUtils.java +++ b/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/TokenUtils.java @@ -33,6 +33,7 @@ import java.util.LinkedHashMap; public class TokenUtils { + public static final String ATTR_CURRENT_KNOXSSO_COOKIE_TOKEN_ID = "currentKnoxSsoCookieTokenId"; public static final String SIGNING_HMAC_SECRET_ALIAS = "gateway.signing.hmac.secret"; private static final String DEFAULT_RSA_SIG_ALG = "RS256"; private static final String DEFAULT_HMAC_SIG_ALG = "HS256"; diff --git a/knox-token-management-ui/token-management/app/session.information.ts b/knox-token-management-ui/token-management/app/session.information.ts index 3d748deafb..7d64b026b5 100644 --- a/knox-token-management-ui/token-management/app/session.information.ts +++ b/knox-token-management-ui/token-management/app/session.information.ts @@ -21,4 +21,5 @@ export class SessionInformation { logoutPageUrl: string; globalLgoutPageUrl: string; canSeeAllTokens: boolean; + currentKnoxSsoCookieTokenId: string; } diff --git a/knox-token-management-ui/token-management/app/token.management.component.html b/knox-token-management-ui/token-management/app/token.management.component.html index cfdbc4a326..5bf0e7de65 100644 --- a/knox-token-management-ui/token-management/app/token.management.component.html +++ b/knox-token-management-ui/token-management/app/token.management.component.html @@ -59,7 +59,7 @@ Token ID - +
{{knoxToken.tokenId}}
{{knoxToken.tokenId}}
@@ -67,17 +67,17 @@ Issued - {{formatDateTime(knoxToken.issueTimeLong)}} + {{formatDateTime(knoxToken.issueTimeLong)}} Expires - {{formatDateTime(knoxToken.expirationLong)}} + {{formatDateTime(knoxToken.expirationLong)}} User Name - {{knoxToken.metadata.userName}} + {{knoxToken.metadata.userName}} diff --git a/knox-token-management-ui/token-management/app/token.management.component.ts b/knox-token-management-ui/token-management/app/token.management.component.ts index 6c73db0bc4..7efa44f6a2 100644 --- a/knox-token-management-ui/token-management/app/token.management.component.ts +++ b/knox-token-management-ui/token-management/app/token.management.component.ts @@ -36,6 +36,7 @@ export class TokenManagementComponent implements OnInit { userName: string; canSeeAllTokens: boolean; + currentKnoxSsoCookieTokenId: string; knoxTokens: MatTableDataSource = new MatTableDataSource(); selection = new SelectionModel(true, []); allKnoxTokens: KnoxToken[]; @@ -102,8 +103,9 @@ export class TokenManagementComponent implements OnInit { console.debug('TokenManagementComponent --> ngOnInit()'); this.tokenManagementService.getSessionInformation() .then(sessionInformation => { - this.canSeeAllTokens = sessionInformation.canSeeAllTokens; - this.setUserName(sessionInformation.user); + this.canSeeAllTokens = sessionInformation.canSeeAllTokens; + this.currentKnoxSsoCookieTokenId = sessionInformation.currentKnoxSsoCookieTokenId; + this.setUserName(sessionInformation.user); }); } @@ -268,4 +270,12 @@ export class TokenManagementComponent implements OnInit { return this.selection.selected.every(token => !token.metadata.knoxSsoCookie); } + getFontWeight(token: KnoxToken): string { + return this.isCurrentKnoxSsoCookietoken(token) ? 'bold' : 'normal'; + } + + private isCurrentKnoxSsoCookietoken(token: KnoxToken): boolean { + return this.isKnoxSsoCookie(token) && token.tokenId === this.currentKnoxSsoCookieTokenId; + } + }