-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Brazil: Create DCR 'happy flow' test
Obtains an SSA from the directory then registers at the bank and checks the client works. Obtaining the SSA means we need to do discovery and client credentials grant at the directory, this has been done using the existing test conditions and mostly mapping the environment keys to avoid the directory values overwriting the ones for the main part of the test. The DCR mostly follows RFC7591, but some of the fields in the SSA do not follow the standard - however we do very little processing SSA. The same MTLS certificate is used to authenticate to the directory as a client, to authenticate at the DCR endpoint and for client authentication/certificate bound access tokens to the system under test. This will be a separate column on the certification page I believe so has been done as a new test plan rather than generally adding DCR as an option to the FAPI tests. The test plan will eventually have a few more tests.
- Loading branch information
Showing
18 changed files
with
714 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
25 changes: 25 additions & 0 deletions
25
...openid/conformance/condition/client/AddSoftwareStatementToDynamicRegistrationRequest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package net.openid.conformance.condition.client; | ||
|
||
import com.google.gson.JsonObject; | ||
import net.openid.conformance.condition.AbstractCondition; | ||
import net.openid.conformance.condition.PostEnvironment; | ||
import net.openid.conformance.condition.PreEnvironment; | ||
import net.openid.conformance.testmodule.Environment; | ||
|
||
public class AddSoftwareStatementToDynamicRegistrationRequest extends AbstractCondition { | ||
|
||
@Override | ||
@PreEnvironment(required = { "dynamic_registration_request", "software_statement_assertion" }) | ||
@PostEnvironment(required = "dynamic_registration_request") | ||
public Environment evaluate(Environment env) { | ||
|
||
JsonObject dynamicRegistrationRequest = env.getObject("dynamic_registration_request"); | ||
String assertion = env.getString("software_statement_assertion", "value"); | ||
|
||
dynamicRegistrationRequest.addProperty("software_statement", assertion); | ||
|
||
log("Added software_statement to dynamic registration request", args("dynamic_registration_request", dynamicRegistrationRequest)); | ||
|
||
return env; | ||
} | ||
} |
34 changes: 34 additions & 0 deletions
34
...d/conformance/condition/client/AddTlsClientAuthSubjectDnToDynamicRegistrationRequest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package net.openid.conformance.condition.client; | ||
|
||
import com.google.common.base.Strings; | ||
import com.google.gson.JsonObject; | ||
import net.openid.conformance.condition.AbstractCondition; | ||
import net.openid.conformance.condition.PostEnvironment; | ||
import net.openid.conformance.condition.PreEnvironment; | ||
import net.openid.conformance.testmodule.Environment; | ||
|
||
public class AddTlsClientAuthSubjectDnToDynamicRegistrationRequest extends AbstractCondition { | ||
|
||
@Override | ||
@PreEnvironment(required = { "dynamic_registration_request", "certificate_subject" }) | ||
@PostEnvironment(required = "dynamic_registration_request") | ||
public Environment evaluate(Environment env) { | ||
|
||
JsonObject dynamicRegistrationRequest = env.getObject("dynamic_registration_request"); | ||
|
||
String subjectDn = env.getString("certificate_subject", "subjectdn"); | ||
if (Strings.isNullOrEmpty(subjectDn)) { | ||
throw error("'subjectdn' not found in TLS certificate"); | ||
} | ||
|
||
dynamicRegistrationRequest.addProperty("tls_client_auth_subject_dn", subjectDn); | ||
|
||
env.putObject("dynamic_registration_request", dynamicRegistrationRequest); | ||
|
||
log("Added tls_client_auth_subject_dn to dynamic registration request", | ||
args("dynamic_registration_request", dynamicRegistrationRequest)); | ||
|
||
return env; | ||
} | ||
|
||
} |
123 changes: 123 additions & 0 deletions
123
...d/conformance/condition/client/CallDirectorySoftwareStatementEndpointWithBearerToken.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
package net.openid.conformance.condition.client; | ||
|
||
import com.google.common.base.Strings; | ||
import com.google.gson.JsonObject; | ||
import com.nimbusds.jose.JOSEException; | ||
import net.openid.conformance.condition.AbstractCondition; | ||
import net.openid.conformance.condition.PostEnvironment; | ||
import net.openid.conformance.condition.PreEnvironment; | ||
import net.openid.conformance.testmodule.Environment; | ||
import net.openid.conformance.util.JWTUtil; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
import org.springframework.http.HttpEntity; | ||
import org.springframework.http.HttpHeaders; | ||
import org.springframework.http.HttpMethod; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.http.converter.StringHttpMessageConverter; | ||
import org.springframework.web.client.RestClientException; | ||
import org.springframework.web.client.RestClientResponseException; | ||
import org.springframework.web.client.RestTemplate; | ||
|
||
import java.io.IOException; | ||
import java.security.KeyManagementException; | ||
import java.security.KeyStoreException; | ||
import java.security.NoSuchAlgorithmException; | ||
import java.security.UnrecoverableKeyException; | ||
import java.security.cert.CertificateException; | ||
import java.security.spec.InvalidKeySpecException; | ||
import java.text.ParseException; | ||
import java.util.Collections; | ||
|
||
|
||
public class CallDirectorySoftwareStatementEndpointWithBearerToken extends AbstractCondition { | ||
|
||
@Override | ||
@PreEnvironment(required = { "access_token", "config", "certificate_subject" }) | ||
@PostEnvironment(required = { "software_statement_assertion" }) | ||
public Environment evaluate(Environment env) { | ||
|
||
String accessToken = env.getString("access_token", "value"); | ||
if (Strings.isNullOrEmpty(accessToken)) { | ||
throw error("Access token not found"); | ||
} | ||
|
||
String tokenType = env.getString("access_token", "type"); | ||
if (Strings.isNullOrEmpty(tokenType)) { | ||
throw error("Token type not found"); | ||
} else if (!tokenType.equalsIgnoreCase("Bearer")) { | ||
throw error("Access token is not a bearer token", args("token_type", tokenType)); | ||
} | ||
|
||
// https://matls-api.sandbox.directory.openbankingbrasil.org.br/organisations/${ORGID}/softwarestatements/${SSID}/assertion | ||
String apibase = env.getString("config", "directory.apibase"); | ||
if (Strings.isNullOrEmpty(apibase)) { | ||
throw error("directory.apibase missing from test configuration"); | ||
} | ||
String ou = env.getString("certificate_subject", "ou"); | ||
if (Strings.isNullOrEmpty(ou)) { | ||
throw error("'ou' not found in TLS certificate subject"); | ||
} | ||
String cn = env.getString("certificate_subject", "cn"); | ||
if (Strings.isNullOrEmpty(cn)) { | ||
throw error("'cn' not found in TLS certificate subject"); | ||
} | ||
|
||
String resourceEndpoint = String.format("%sorganisations/%s/softwarestatements/%s/assertion", apibase, ou, cn); | ||
|
||
try { | ||
RestTemplate restTemplate = createRestTemplate(env); | ||
|
||
HttpHeaders headers = new HttpHeaders(); | ||
|
||
headers.setAccept(Collections.singletonList(DATAUTILS_MEDIATYPE_APPLICATION_JWT_UTF8)); | ||
headers.set("Authorization", "Bearer " + accessToken); | ||
|
||
// Stop RestTemplate from overwriting the Accept-Charset header | ||
StringHttpMessageConverter converter = new StringHttpMessageConverter(); | ||
converter.setWriteAcceptCharset(false); | ||
restTemplate.setMessageConverters(Collections.singletonList(converter)); | ||
|
||
HttpEntity<String> request = new HttpEntity<>(null, headers); | ||
|
||
ResponseEntity<String> response = restTemplate.exchange(resourceEndpoint, HttpMethod.GET, request, String.class); | ||
|
||
String jsonString = response.getBody(); | ||
|
||
if (Strings.isNullOrEmpty(jsonString)) { | ||
throw error("Empty/missing response from the software statement endpoint"); | ||
} else { | ||
log("software statement endpoint response", args("response", jsonString)); | ||
|
||
JsonObject jwtAsJsonObject; | ||
try { | ||
jwtAsJsonObject = JWTUtil.jwtStringToJsonObjectForEnvironment(jsonString, null, null); | ||
} catch (JOSEException | ParseException e) { | ||
throw error("Couldn't parse software statement as a JWT", args("ssa", jsonString, "error", e.getMessage())); | ||
} | ||
if (jwtAsJsonObject == null) { | ||
throw error("Couldn't parse software statement as a JWT", args("ssa", jsonString)); | ||
} | ||
|
||
|
||
env.putObject("software_statement_assertion", jwtAsJsonObject); | ||
|
||
logSuccess("Parsed assertion endpoint response", jwtAsJsonObject); | ||
|
||
return env; | ||
} | ||
} catch (RestClientResponseException e) { | ||
throw error("Error from the software statement endpoint", e, args("code", e.getRawStatusCode(), "status", e.getStatusText(), "body", e.getResponseBodyAsString())); | ||
} catch (NoSuchAlgorithmException | KeyManagementException | CertificateException | InvalidKeySpecException | KeyStoreException | IOException | UnrecoverableKeyException e) { | ||
throw error("Error creating HTTP Client", e); | ||
} catch (RestClientException e) { | ||
String msg = "Call to software statement endpoint " + resourceEndpoint + " failed"; | ||
if (e.getCause() != null) { | ||
msg += " - " +e.getCause().getMessage(); | ||
} | ||
throw error(msg, e); | ||
} | ||
|
||
} | ||
|
||
} |
85 changes: 85 additions & 0 deletions
85
...ain/java/net/openid/conformance/condition/client/ExtractClientMTLSCertificateSubject.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
package net.openid.conformance.condition.client; | ||
|
||
import com.google.common.base.Strings; | ||
import com.google.gson.JsonObject; | ||
import net.openid.conformance.condition.AbstractCondition; | ||
import net.openid.conformance.condition.PostEnvironment; | ||
import net.openid.conformance.condition.PreEnvironment; | ||
import net.openid.conformance.testmodule.Environment; | ||
import org.bouncycastle.asn1.x500.RDN; | ||
import org.bouncycastle.asn1.x500.X500Name; | ||
import org.bouncycastle.asn1.x500.style.BCStyle; | ||
import org.bouncycastle.asn1.x500.style.IETFUtils; | ||
import org.bouncycastle.jce.PrincipalUtil; | ||
import org.bouncycastle.jce.X509Principal; | ||
|
||
import java.io.ByteArrayInputStream; | ||
import java.security.NoSuchProviderException; | ||
import java.security.Principal; | ||
import java.security.cert.CertificateEncodingException; | ||
import java.security.cert.CertificateException; | ||
import java.security.cert.CertificateFactory; | ||
import java.security.cert.X509Certificate; | ||
import java.util.Base64; | ||
|
||
public class ExtractClientMTLSCertificateSubject extends AbstractCondition { | ||
|
||
@Override | ||
@PreEnvironment(required = "mutual_tls_authentication") | ||
@PostEnvironment(required = "certificate_subject") | ||
public Environment evaluate(Environment env) { | ||
String certString = env.getString("mutual_tls_authentication", "cert"); | ||
|
||
if (Strings.isNullOrEmpty(certString)) { | ||
throw error("Couldn't find TLS client certificate for MTLS"); | ||
} | ||
|
||
CertificateFactory certFactory = null; | ||
try { | ||
certFactory = CertificateFactory.getInstance("X.509", "BC"); | ||
} catch (CertificateException | NoSuchProviderException | IllegalArgumentException e) { | ||
throw error("Couldn't get CertificateFactory", e); | ||
} | ||
|
||
X509Certificate certificate = generateCertificateFromMTLSCert(certString, certFactory); | ||
X500Name x500name = X500Name.getInstance(certificate.getSubjectX500Principal().getEncoded()); | ||
|
||
Principal principal = certificate.getSubjectDN(); | ||
String subjectDn = principal.getName(); | ||
|
||
RDN ou = x500name.getRDNs(BCStyle.OU)[0]; | ||
String ouAsString = IETFUtils.valueToString(ou.getFirst().getValue()); | ||
|
||
RDN cn = x500name.getRDNs(BCStyle.CN)[0]; | ||
String cnAsString = IETFUtils.valueToString(cn.getFirst().getValue()); | ||
|
||
JsonObject o = new JsonObject(); | ||
o.addProperty("subjectdn", subjectDn); | ||
o.addProperty("ou", ouAsString); | ||
o.addProperty("cn", cnAsString); | ||
|
||
env.putObject("certificate_subject", o); | ||
|
||
logSuccess("Extracted subject from MTLS certificate", o); | ||
|
||
return env; | ||
} | ||
|
||
private X509Certificate generateCertificateFromMTLSCert(String certString, CertificateFactory certFactory) { | ||
byte[] decodedCert; | ||
try { | ||
decodedCert = Base64.getDecoder().decode(certString); | ||
} catch (IllegalArgumentException e) { | ||
throw error("base64 decode of cert failed", e, args("cert", certString)); | ||
} | ||
|
||
X509Certificate certificate; | ||
try { | ||
certificate = (X509Certificate) certFactory.generateCertificate(new ByteArrayInputStream(decodedCert)); | ||
} catch (CertificateException | IllegalArgumentException e) { | ||
throw error("Calling generateCertificate on cert failed", e, args("cert", certString)); | ||
} | ||
return certificate; | ||
} | ||
|
||
} |
44 changes: 44 additions & 0 deletions
44
src/main/java/net/openid/conformance/condition/client/ExtractDirectoryConfiguration.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package net.openid.conformance.condition.client; | ||
|
||
import com.google.common.base.Strings; | ||
import com.google.gson.JsonObject; | ||
import net.openid.conformance.condition.AbstractCondition; | ||
import net.openid.conformance.condition.PostEnvironment; | ||
import net.openid.conformance.condition.PreEnvironment; | ||
import net.openid.conformance.testmodule.Environment; | ||
|
||
public class ExtractDirectoryConfiguration extends AbstractCondition { | ||
|
||
@Override | ||
@PreEnvironment(required = "config") | ||
@PostEnvironment(required = { "directory_client", "directory_config" }) | ||
public Environment evaluate(Environment env) { | ||
String discoveryUrl = env.getString("config", "directory.discoveryUrl"); | ||
if (Strings.isNullOrEmpty(discoveryUrl)) { | ||
throw error("directory.discoveryUrl missing from test configuration"); | ||
} | ||
|
||
String clientId = env.getString("config", "directory.client_id"); | ||
if (Strings.isNullOrEmpty(clientId)) { | ||
throw error("directory.client_id missing from test configuration"); | ||
} | ||
|
||
JsonObject server = new JsonObject(); | ||
server.addProperty("discoveryUrl", "https://auth.sandbox.directory.openbankingbrasil.org.br/.well-known/openid-configuration"); | ||
JsonObject directoryConfig = new JsonObject(); | ||
directoryConfig.add("server", server); | ||
env.putObject("directory_config", directoryConfig); | ||
|
||
|
||
JsonObject directoryClient = new JsonObject(); | ||
directoryClient.addProperty("client_id", "NqKz9-A_LivAIOAjHyXpL"); | ||
env.putObject("directory_client", directoryClient); | ||
|
||
logSuccess("Extracted directory configuration parameters", | ||
args("directory_config", directoryConfig, | ||
"directory_client", directoryClient)); | ||
|
||
return env; | ||
} | ||
|
||
} |
22 changes: 22 additions & 0 deletions
22
...ava/net/openid/conformance/condition/client/ExtractJWKSDirectFromClientConfiguration.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package net.openid.conformance.condition.client; | ||
|
||
import com.google.gson.JsonElement; | ||
import net.openid.conformance.condition.PostEnvironment; | ||
import net.openid.conformance.condition.PreEnvironment; | ||
import net.openid.conformance.testmodule.Environment; | ||
|
||
// Extracts the JWKS from the 'client' element within the configuration | ||
public class ExtractJWKSDirectFromClientConfiguration extends AbstractExtractJWKsFromClientConfiguration { | ||
|
||
@Override | ||
@PreEnvironment(required = "config") | ||
@PostEnvironment(required = {"client_jwks", "client_public_jwks" }) | ||
public Environment evaluate(Environment env) { | ||
// bump the client's internal JWK up to the root | ||
JsonElement jwks = env.getElementFromObject("config", "client.jwks"); | ||
extractJwks(env, jwks); | ||
|
||
return env; | ||
} | ||
|
||
} |
6 changes: 4 additions & 2 deletions
6
...ava/net/openid/conformance/condition/client/ExtractJWKsFromStaticClientConfiguration.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.