Skip to content

Commit ea528d2

Browse files
authored
2.x Adapt to optimized IAS server API (#1361)
1 parent 4b3e42a commit ea528d2

File tree

9 files changed

+156
-125
lines changed

9 files changed

+156
-125
lines changed

java-security-it/src/test/java/com/sap/cloud/security/test/integration/XsuaaIntegrationTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ public void xsuaaTokenValidationFails_withIasCombiningValidator() {
8484
ValidationResult result = tokenValidator.validate(token);
8585
assertThat(result.isValid()).isFalse();
8686
assertThat(result.getErrorDescription()).startsWith(
87-
"Issuer is not trusted because issuer 'http://auth.com' doesn't match any of these domains '[myauth.com]' of the identity provider");
87+
"Issuer http://auth.com was not a trusted domain or a subdomain of the trusted domains [myauth.com].");
8888
}
8989

9090
@Test@Ignore("to be fixed")

java-security-it/src/test/java/com/sap/cloud/security/test/performance/SpringSecurityPerformanceIT.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ private OAuth2ServiceConfigurationBuilder createXsuaaConfigurationBuilder() {
8080

8181
private OAuth2ServiceConfigurationBuilder createIasConfigurationBuilder() {
8282
return OAuth2ServiceConfigurationBuilder.forService(IAS)
83-
.withDomains(SecurityTest.DEFAULT_DOMAIN)
83+
.withDomains(securityIasTest.getWireMockServer().baseUrl())
8484
.withClientId(SecurityTest.DEFAULT_CLIENT_ID);
8585
}
8686
}

java-security-test/src/main/java/com/sap/cloud/security/test/SecurityTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import javax.servlet.Servlet;
4141
import java.io.IOException;
4242
import java.net.URI;
43+
import java.net.URL;
4344
import java.nio.charset.StandardCharsets;
4445
import java.security.NoSuchAlgorithmException;
4546
import java.security.interfaces.RSAPublicKey;
@@ -169,7 +170,7 @@ public OAuth2ServiceConfigurationBuilder getOAuth2ServiceConfigurationBuilderFro
169170
String configurationResourceName) {
170171
return VcapServicesParser.fromFile(configurationResourceName)
171172
.getConfigurationBuilder()
172-
.withDomains(URI.create(issuerUrl).getHost())
173+
.withDomains(issuerUrl)
173174
.withUrl(issuerUrl);
174175
}
175176

java-security-test/src/test/java/com/sap/cloud/security/test/SecurityTestRuleTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ public void getJwtGeneratorFromFile_setsTestingDefaults() throws IOException {
144144
public void setKeys_invalidPath_throwsException() {
145145
assertThatThrownBy(() -> SecurityTestRule.getInstance(XSUAA)
146146
.setKeys("doesNotExist", "doesNotExist"))
147-
.isInstanceOf(RuntimeException.class);
147+
.isInstanceOf(RuntimeException.class);
148148
}
149149

150150
@Test
Lines changed: 36 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,27 @@
11
/**
22
* SPDX-FileCopyrightText: 2018-2023 SAP SE or an SAP affiliate company and Cloud Security Client Java contributors
3-
*<p>
3+
* <p>
44
* SPDX-License-Identifier: Apache-2.0
55
*/
66
package com.sap.cloud.security.token.validation.validators;
77

8-
import static com.sap.cloud.security.token.validation.ValidationResults.createInvalid;
9-
import static com.sap.cloud.security.token.validation.ValidationResults.createValid;
10-
import static com.sap.cloud.security.xsuaa.Assertions.assertNotEmpty;
11-
12-
import java.net.URI;
13-
import java.net.URISyntaxException;
14-
import java.util.List;
15-
16-
import com.sap.cloud.security.config.Service;
17-
import org.slf4j.Logger;
18-
import org.slf4j.LoggerFactory;
19-
208
import com.sap.cloud.security.config.OAuth2ServiceConfiguration;
9+
import com.sap.cloud.security.json.JsonParsingException;
2110
import com.sap.cloud.security.token.Token;
2211
import com.sap.cloud.security.token.validation.ValidationResult;
2312
import com.sap.cloud.security.token.validation.Validator;
13+
import org.slf4j.Logger;
14+
import org.slf4j.LoggerFactory;
15+
16+
import java.net.MalformedURLException;
17+
import java.net.URL;
18+
import java.util.List;
19+
import java.util.Objects;
20+
import java.util.regex.Pattern;
21+
22+
import static com.sap.cloud.security.token.validation.ValidationResults.createInvalid;
23+
import static com.sap.cloud.security.token.validation.ValidationResults.createValid;
24+
import static com.sap.cloud.security.xsuaa.Assertions.assertNotEmpty;
2425

2526
/**
2627
* Validates that the jwt token is issued by a trust worthy identity provider.
@@ -37,6 +38,7 @@
3738
* These checks are a prerequisite for using the `JwtSignatureValidator`.
3839
*/
3940
class JwtIssuerValidator implements Validator<Token> {
41+
protected static final String HTTPS_SCHEME = "https://";
4042
private final List<String> domains;
4143
protected final Logger logger = LoggerFactory.getLogger(getClass());
4244

@@ -55,55 +57,34 @@ class JwtIssuerValidator implements Validator<Token> {
5557

5658
@Override
5759
public ValidationResult validate(Token token) {
58-
String issuer = token.getIssuer();
59-
if (token.getService().equals(Service.IAS) && !issuer.startsWith("http")) {
60-
issuer = "https://" + issuer;
61-
}
62-
ValidationResult validationResult = validateUrl(issuer);
63-
if (validationResult.isErroneous()) {
64-
return validationResult;
60+
String issuer;
61+
62+
try {
63+
issuer = token.getIssuer();
64+
} catch(JsonParsingException e) {
65+
return createInvalid("Issuer validation can not be performed because token issuer claim was not a String value.");
6566
}
66-
return matchesTokenIssuerUrl(issuer);
67-
}
6867

69-
private ValidationResult matchesTokenIssuerUrl(String issuer) {
70-
URI issuerUri = URI.create(issuer);
71-
if (issuerUri.getQuery() == null && issuerUri.getFragment() == null && issuerUri.getHost() != null) {
72-
for (String d : domains) {
73-
if (issuerUri.getHost().endsWith(d)) {
74-
return createValid();
75-
}
76-
}
68+
if (issuer == null || issuer.trim().isEmpty()) {
69+
return createInvalid("Issuer validation can not be performed because token does not contain an issuer claim.");
7770
}
78-
return createInvalid(
79-
"Issuer is not trusted because issuer '{}' doesn't match any of these domains '{}' of the identity provider.",
80-
issuer, domains);
81-
}
8271

83-
private ValidationResult validateUrl(String issuer) {
84-
URI issuerUri;
72+
String issuerUrl = issuer.startsWith(HTTPS_SCHEME) ? issuer : HTTPS_SCHEME + issuer;
8573
try {
86-
if (issuer == null || issuer.trim().isEmpty()) {
87-
return createInvalid(
88-
"Issuer validation can not be performed because Jwt token does not contain an issuer claim.");
89-
}
90-
if (!issuer.startsWith("http")) {
91-
return createInvalid(
92-
"Issuer is not trusted because issuer '{}' does not provide a valid URI (missing http scheme). Please contact your Identity Provider Administrator.",
93-
issuer);
94-
}
95-
issuerUri = new URI(issuer);
96-
if (issuerUri.getQuery() == null && issuerUri.getFragment() == null && issuerUri.getHost() != null) {
74+
new URL(issuerUrl);
75+
} catch (MalformedURLException e) {
76+
return createInvalid("Issuer validation can not be performed because token issuer is not a valid URL suitable for https.");
77+
}
78+
79+
String issuerDomain = issuerUrl.substring(HTTPS_SCHEME.length());
80+
for(String d : domains) {
81+
// a string that ends with .<trustedDomain> and contains 1-63 letters, digits or '-' before that for the subdomain
82+
String validSubdomainPattern = String.format("^[a-zA-Z0-9-]{1,63}\\.%s$", Pattern.quote(d));
83+
if(Objects.equals(d, issuerDomain) || issuerDomain.matches(validSubdomainPattern)) {
9784
return createValid();
9885
}
99-
} catch (URISyntaxException e) {
100-
logger.error(
101-
"Error: issuer claim '{}' does not provide a valid URI: {}. Please contact your Identity Provider Administrator.",
102-
issuer, e.getMessage(), e);
10386
}
104-
return createInvalid(
105-
"Issuer is not trusted because issuer does not provide a valid URI. Please contact your Identity Provider Administrator.",
106-
issuer);
107-
}
10887

88+
return createInvalid("Issuer {} was not a trusted domain or a subdomain of the trusted domains {}.", issuer, domains);
89+
}
10990
}

java-security/src/main/java/com/sap/cloud/security/token/validation/validators/SapIdJwtSignatureValidator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ private URI getJwksUri(Token token) throws OAuth2ServiceException {
6868
}
6969

7070
if (isTenantIdCheckEnabled && !domain.equals("" + configuration.getUrl()) && token.getAppTid() == null) {
71-
throw new IllegalArgumentException("OIDC token must provide a valid " + TokenClaims.SAP_GLOBAL_APP_TID + " header when issuer has a different domain than the url from the service credentials.");
71+
throw new IllegalArgumentException("OIDC token must provide the " + TokenClaims.SAP_GLOBAL_APP_TID + " claim for tenant validation when issuer is not the same as the url from the service credentials.");
7272
}
7373

7474

0 commit comments

Comments
 (0)