Skip to content

Commit 6ec2a1e

Browse files
fix: Modulith caches improvements with infinispan (#4548)
Signed-off-by: Richard Salac <richard.salac@broadcom.com>
1 parent bc34c71 commit 6ec2a1e

64 files changed

Lines changed: 743 additions & 400 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

api-catalog-services/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ dependencies {
9191
testImplementation libs.spring.boot.starter.test
9292
testImplementation libs.spring.mock.mvc
9393
testImplementation(testFixtures(project(":apiml-common")))
94+
testImplementation(testFixtures(project(":apiml-security-common")))
9495
testImplementation libs.reactor.test
9596

9697
compileOnly libs.lombok

api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/config/SecurityConfiguration.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -348,15 +348,15 @@ WebFilter oidcAuthenticationFilter(
348348
Optional.ofNullable(exchange.getRequest().getHeaders().getFirst(HEADER_OIDC_TOKEN))
349349
.map(token -> {
350350
try {
351-
return Map.entry(token, gatewaySecurity.verifyOidc(token));
351+
var tokenAuthentication = gatewaySecurity.verifyOidc(token);
352+
tokenAuthentication.setAuthenticated(true);
353+
return tokenAuthentication;
352354
} catch (Exception e) {
353355
log.debug("Cannot verify OIDC token: {}", token, e);
354356
return null;
355357
}
356358
})
357-
.map(pair -> ReactiveSecurityContextHolder.withAuthentication(
358-
createAuthenticated(pair.getValue().getUserId(), pair.getKey(), TokenAuthentication.Type.OIDC)
359-
))
359+
.map(ReactiveSecurityContextHolder::withAuthentication)
360360
.orElse(context)
361361
);
362362
}

api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/security/ApiCatalogLogoutSuccessHandlerTest.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import org.springframework.web.server.WebFilterChain;
2222
import org.zowe.apiml.security.common.config.AuthConfigurationProperties;
2323
import org.zowe.apiml.security.common.token.TokenAuthentication;
24+
import org.zowe.apiml.security.common.util.JWTTestUtils;
2425
import reactor.test.StepVerifier;
2526

2627
import static org.junit.jupiter.api.Assertions.*;
@@ -31,8 +32,9 @@ class ApiCatalogLogoutSuccessHandlerTest {
3132

3233
@Test
3334
void testOnLogoutSuccess() {
35+
var token = JWTTestUtils.createDummyAPIMLToken("user");
3436
var request = MockServerHttpRequest.get("/logout")
35-
.header(HttpHeaders.AUTHORIZATION, "Bearer token123")
37+
.header(HttpHeaders.AUTHORIZATION, "Bearer %s".formatted(token))
3638
.build();
3739
var exchange = MockServerWebExchange.from(request);
3840
WebFilterChain mockChain = mock(WebFilterChain.class);
@@ -43,7 +45,7 @@ void testOnLogoutSuccess() {
4345

4446
StepVerifier.create(apiCatalogLogoutSuccessHandler.onLogoutSuccess(
4547
webFilterExchange,
46-
new TokenAuthentication("TEST_TOKEN_STRING")
48+
new TokenAuthentication(token)
4749
))
4850
.verifyComplete();
4951

api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/staticapi/StaticDefinitionGeneratorTest.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.springframework.security.core.context.SecurityContextImpl;
2323
import org.springframework.test.util.ReflectionTestUtils;
2424
import org.zowe.apiml.security.common.token.TokenAuthentication;
25+
import org.zowe.apiml.security.common.util.JWTTestUtils;
2526

2627
import java.io.IOException;
2728
import java.nio.file.FileAlreadyExistsException;
@@ -44,7 +45,7 @@ class WhenStaticDefinitionGenerationResponse {
4445

4546
@BeforeEach
4647
void setUp() {
47-
TokenAuthentication authentication = new TokenAuthentication("token");
48+
TokenAuthentication authentication = new TokenAuthentication(JWTTestUtils.createDummyAPIMLToken("user"));
4849
authentication.setAuthenticated(true);
4950
SecurityContextHolder.setContext(new SecurityContextImpl(authentication));
5051
ReflectionTestUtils.setField(staticDefinitionGenerator, "staticApiDefinitionsDirectories", configFileLocation);
@@ -115,7 +116,7 @@ class WhenStaticDefinitionOverrideResponse {
115116

116117
@BeforeEach
117118
void setUp() {
118-
TokenAuthentication authentication = new TokenAuthentication("token");
119+
TokenAuthentication authentication = new TokenAuthentication(JWTTestUtils.createDummyAPIMLToken("user"));
119120
authentication.setAuthenticated(true);
120121
SecurityContextHolder.setContext(new SecurityContextImpl(authentication));
121122
ReflectionTestUtils.setField(staticDefinitionGenerator, "staticApiDefinitionsDirectories", "../config/local/api-defs");

apiml-security-common/src/main/java/org/zowe/apiml/security/common/content/AbstractSecureContentFilter.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,12 @@ protected boolean shouldNotFilter(HttpServletRequest request) {
7676
*/
7777
@Override
7878
protected void doFilterInternal(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull FilterChain filterChain) throws ServletException, IOException {
79-
Optional<AbstractAuthenticationToken> authenticationToken = extractContent(request);
79+
var authenticationToken = Optional.<AbstractAuthenticationToken>empty();
80+
try {
81+
authenticationToken = extractContent(request);
82+
} catch (AuthenticationException authenticationException) {
83+
failureHandler.onAuthenticationFailure(request, response, authenticationException);
84+
}
8085

8186
if (authenticationToken.isPresent()) {
8287
Authentication authentication = null;

apiml-security-common/src/main/java/org/zowe/apiml/security/common/token/QueryResponse.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
import org.zowe.apiml.cache.EntryExpiration;
1919
import org.zowe.apiml.util.UrlUtils;
2020

21+
import java.io.Serial;
22+
import java.io.Serializable;
2123
import java.util.Date;
2224
import java.util.List;
2325

@@ -27,7 +29,10 @@
2729
@Data
2830
@AllArgsConstructor
2931
@NoArgsConstructor
30-
public class QueryResponse implements EntryExpiration {
32+
public class QueryResponse implements EntryExpiration, Serializable {
33+
34+
@Serial
35+
private static final long serialVersionUID = 4282686067850298800L;
3136

3237
private String domain;
3338
private String userId;

apiml-security-common/src/main/java/org/zowe/apiml/security/common/token/TokenAuthentication.java

Lines changed: 109 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -10,84 +10,155 @@
1010

1111
package org.zowe.apiml.security.common.token;
1212

13+
import com.nimbusds.jwt.*;
1314
import lombok.EqualsAndHashCode;
1415
import lombok.Getter;
16+
import lombok.extern.slf4j.Slf4j;
17+
import org.apache.commons.lang3.StringUtils;
1518
import org.springframework.security.authentication.AbstractAuthenticationToken;
19+
import org.zowe.apiml.security.common.util.JwtUtils;
1620
import org.zowe.apiml.security.common.login.LoginFilter;
1721

18-
import java.util.Collections;
19-
import java.util.Optional;
22+
import java.io.Serial;
23+
import java.text.ParseException;
24+
import java.util.*;
2025

2126
/**
2227
* This object is added to security context after successful authentication.
2328
* Contains username and valid JWT token.
2429
*/
25-
@EqualsAndHashCode(callSuper = false)
30+
@EqualsAndHashCode(callSuper = false, onlyExplicitlyIncluded = true)
31+
@Slf4j
2632
public class TokenAuthentication extends AbstractAuthenticationToken {
2733

28-
private static final long serialVersionUID = 9187160928171618141L;
34+
@Serial
35+
private static final long serialVersionUID = 82346593850419807L;
2936

30-
private final String username;
31-
private final String token;
37+
private static final String DOMAIN_CLAIM_NAME = "dom";
38+
private static final String SCOPES = "scopes";
39+
40+
@Getter
41+
private final JWT jwt;
42+
private final JWTClaimsSet claims;
43+
@Getter
44+
private final QueryResponse queryResponse;
3245
@Getter
33-
private Type type;
46+
private final Type type;
3447

35-
public TokenAuthentication(String token) {
36-
this(token, (Type) null);
48+
public enum Type {
49+
JWT,
50+
OIDC
3751
}
3852

39-
public TokenAuthentication(String token, Type type) {
40-
this(null, token, type);
53+
public TokenAuthentication(String tokenString, Type type) {
54+
super(Collections.emptyList());
55+
56+
try {
57+
this.jwt = JWTParser.parse(tokenString);
58+
this.claims = jwt.getJWTClaimsSet();
59+
this.queryResponse = parseQueryResponse(claims);
60+
this.type = type;
61+
} catch (ParseException ex) {
62+
throw JwtUtils.handleJwtParserException(ex);
63+
}
4164
}
4265

43-
public TokenAuthentication(String username, String token) {
44-
this(username, token, (Type) null);
66+
public TokenAuthentication(String tokenString) {
67+
this(tokenString, Type.JWT);
4568
}
4669

47-
public TokenAuthentication(String username, String token, Type type) {
48-
super(Collections.emptyList());
49-
this.username = username;
50-
this.token = token;
51-
this.type = type;
70+
public TokenAuthentication(String userId, String tokenString, Type type) {
71+
this(tokenString, type);
72+
checkUserId(userId);
73+
}
74+
75+
public static TokenAuthentication createAuthenticated(String tokenString, Type type) {
76+
var tokenAuthentication = new TokenAuthentication(tokenString, type);
77+
tokenAuthentication.setAuthenticated(true);
78+
return tokenAuthentication;
79+
}
80+
81+
public static TokenAuthentication createAuthenticated(String userId, String token, Type type) {
82+
var tokenAuthentication = new TokenAuthentication(userId, token, type);
83+
tokenAuthentication.setAuthenticated(true);
84+
return tokenAuthentication;
85+
}
86+
87+
@SuppressWarnings("squid:S3655")
88+
public static TokenAuthentication createAuthenticatedFromHeader(String token, String authHeader) {
89+
var loginRequest = LoginFilter.getCredentialFromAuthorizationHeader(Optional.of(authHeader));
90+
return createAuthenticated(loginRequest.get().getUsername(), token, Type.JWT);
91+
}
92+
93+
public boolean isExpired() {
94+
return queryResponse.isExpired();
95+
}
96+
97+
public Date getExpiration() {
98+
return queryResponse.getExpiration();
99+
}
100+
101+
public QueryResponse.Source getSource() {
102+
return queryResponse.getSource();
103+
}
104+
105+
public String getClaimAsString(String claimName) throws ParseException {
106+
return claims.getClaimAsString(claimName);
52107
}
53108

54109
/**
55110
* @return the token that prove the username is correct
56111
*/
57112
@Override
113+
@EqualsAndHashCode.Include
58114
public String getCredentials() {
59-
return token;
115+
return jwt.getParsedString();
60116
}
61117

62118
/**
63119
* @return the username being authenticated
64120
*/
65121
@Override
66122
public String getPrincipal() {
67-
return username;
123+
return queryResponse.getUserId();
68124
}
69125

70-
/**
71-
* Creates the TokenAuthentication with fulfilled username (principal), token and marked as authenticated.
72-
* @param username Username, who is authenticated
73-
* @param token Token, which authenticate the user
74-
* @return TokenAuthentication marked as authenticated with username, token
75-
*/
76-
public static TokenAuthentication createAuthenticated(String username, String token, Type type) {
77-
final TokenAuthentication out = new TokenAuthentication(username, token, type);
78-
out.setAuthenticated(true);
79-
return out;
126+
@Override
127+
public void setAuthenticated(boolean authenticated) {
128+
if (authenticated && isExpired()) {
129+
throw new TokenExpireException(
130+
"Unable to set authentication as true because the token ...%s expired on %s"
131+
.formatted(StringUtils.right(jwt.getParsedString(), 15), getExpiration()));
132+
}
133+
super.setAuthenticated(authenticated);
80134
}
81135

82-
@SuppressWarnings("squid:S3655")
83-
public static TokenAuthentication createAuthenticatedFromHeader(String token, String authHeader) {
84-
var loginRequest = LoginFilter.getCredentialFromAuthorizationHeader(Optional.of(authHeader));
85-
return createAuthenticated(loginRequest.get().getUsername(), token, Type.JWT);
136+
private QueryResponse parseQueryResponse(JWTClaimsSet claims) {
137+
Object scopesObject = claims.getClaim(SCOPES);
138+
List<String> scopes = Collections.emptyList();
139+
if (scopesObject instanceof List<?>) {
140+
scopes = (List<String>) scopesObject;
141+
}
142+
try {
143+
return new QueryResponse(
144+
claims.getClaimAsString(DOMAIN_CLAIM_NAME),
145+
claims.getSubject(),
146+
claims.getIssueTime(),
147+
claims.getExpirationTime(),
148+
claims.getIssuer(),
149+
scopes,
150+
QueryResponse.Source.valueByIssuer(claims.getIssuer())
151+
);
152+
} catch (ParseException e) {
153+
throw new TokenNotValidException(e.getMessage(), e);
154+
}
86155
}
87156

88-
public enum Type {
89-
JWT,
90-
OIDC
157+
private void checkUserId(String userId) {
158+
var principal = getPrincipal();
159+
if (userId == null || !userId.equalsIgnoreCase(principal)) {
160+
log.debug("Username '{}' does not match the one in token '{}' or is null", userId, principal);
161+
throw new TokenNotValidException("Token is not valid for provided username");
162+
}
91163
}
92-
93164
}

apiml-security-common/src/main/java/org/zowe/apiml/security/common/util/JwtUtils.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,10 +103,6 @@ public RuntimeException handleJwtParserException(Exception exception) {
103103
return new TokenNotValidException("An internal error occurred while validating the token therefore the token is no longer valid.", exception);
104104
}
105105

106-
boolean verifyJwtSignatureWithJwk() {
107-
return false;
108-
}
109-
110106
/**
111107
* Extracts value of a field from an OIDC token. The value is extracted from a custom path which supports nested objects.
112108
* @param token to extract the field from

apiml-security-common/src/test/java/org/zowe/apiml/gateway/security/login/SuccessfulAccessTokenHandlerTest.java

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
import org.zowe.apiml.security.common.token.TokenAuthentication;
2424

2525
import jakarta.servlet.http.HttpServletResponse;
26+
import org.zowe.apiml.security.common.util.JWTTestUtils;
27+
2628
import java.io.IOException;
2729
import java.io.PrintWriter;
2830
import java.util.HashSet;
@@ -36,7 +38,8 @@
3638
class SuccessfulAccessTokenHandlerTest {
3739

3840
private static final String USERNAME = "user";
39-
private final TokenAuthentication dummyAuth = new TokenAuthentication(USERNAME, "TEST_TOKEN_STRING");
41+
public static final String JWT_TOKEN = JWTTestUtils.createDummyAPIMLToken(USERNAME);
42+
private final TokenAuthentication dummyAuth = new TokenAuthentication(JWT_TOKEN);
4043
private SuccessfulAccessTokenHandler underTest;
4144
private AccessTokenProvider accessTokenProvider;
4245
private MockHttpServletRequest httpServletRequest;
@@ -71,23 +74,23 @@ void setup() {
7174
class WhenCallingOnAuthentication {
7275
@Test
7376
void thenReturn200() throws IOException {
74-
when(accessTokenProvider.getToken(any(), anyInt(), any())).thenReturn("jwtToken");
77+
when(accessTokenProvider.getToken(any(), anyInt(), any())).thenReturn(JWT_TOKEN);
7578
executeLoginHandler();
7679

7780
assertEquals(HttpStatus.OK.value(), httpServletResponse.getStatus());
7881
}
7982

8083
@Test
8184
void givenNullExpiration_thenReturn200() throws IOException {
82-
when(accessTokenProvider.getToken(any(), anyInt(), any())).thenReturn("jwtToken");
85+
when(accessTokenProvider.getToken(any(), anyInt(), any())).thenReturn(JWT_TOKEN);
8386
executeLoginHandler();
8487

8588
assertEquals(HttpStatus.OK.value(), httpServletResponse.getStatus());
8689
}
8790

8891
@Test
8992
void givenResponseNotCommitted_thenThrowIOException() throws IOException {
90-
when(accessTokenProvider.getToken(any(), anyInt(), any())).thenReturn("jwtToken");
93+
when(accessTokenProvider.getToken(any(), anyInt(), any())).thenReturn(JWT_TOKEN);
9194
HttpServletResponse servletResponse = mock(HttpServletResponse.class);
9295
PrintWriter mockWriter = mock(PrintWriter.class);
9396
when(servletResponse.getWriter()).thenReturn(mockWriter);
@@ -112,7 +115,7 @@ void verifyCommons() {
112115

113116
@Test
114117
void whenProperInputs_thenRauditxIsGenerated() throws IOException {
115-
doReturn("token").when(accessTokenProvider).getToken(anyString(), anyInt(), any());
118+
doReturn(JWT_TOKEN).when(accessTokenProvider).getToken(anyString(), anyInt(), any());
116119

117120
underTest.onAuthenticationSuccess(httpServletRequest, httpServletResponse, dummyAuth);
118121

apiml-security-common/src/test/java/org/zowe/apiml/security/common/auth/saf/SafResourceAccessEndpointTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.springframework.web.client.RestTemplate;
2626
import org.zowe.apiml.security.common.config.AuthConfigurationProperties;
2727
import org.zowe.apiml.security.common.token.TokenAuthentication;
28+
import org.zowe.apiml.security.common.util.JWTTestUtils;
2829

2930
import static org.junit.jupiter.api.Assertions.*;
3031
import static org.mockito.ArgumentMatchers.*;
@@ -41,7 +42,7 @@ class SafResourceAccessEndpointTest {
4142
private static final String UNSUPPORTED_CLASS = "testClass";
4243
private static final String RESOURCE = "resourceTest";
4344
private static final String LEVEL = "READ";
44-
private static final Authentication authentication = new TokenAuthentication(USER_ID, "token");
45+
private static final Authentication authentication = new TokenAuthentication(JWTTestUtils.createDummyAPIMLToken(USER_ID));
4546

4647
@Mock
4748
private RestTemplate restTemplate;

0 commit comments

Comments
 (0)