Skip to content

Commit

Permalink
chore: add latest JSON-LD context to cache (#167)
Browse files Browse the repository at this point in the history
  • Loading branch information
paullatzelsperger committed Nov 7, 2023
1 parent e8bee8b commit 084a28d
Show file tree
Hide file tree
Showing 14 changed files with 162 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,14 @@
import org.eclipse.edc.web.jersey.jsonld.ObjectMapperProvider;
import org.eclipse.edc.web.spi.WebService;

import java.net.URISyntaxException;

import static org.eclipse.edc.identityhub.spi.model.IdentityHubConstants.IATP_CONTEXT_URL;
import static org.eclipse.edc.identityhub.spi.model.IdentityHubConstants.PRESENTATION_EXCHANGE_URL;
import static org.eclipse.edc.spi.CoreConstants.JSON_LD;

@Extension(value = "Presentation API Extension")
public class PresentationApiExtension implements ServiceExtension {

public static final String RESOLUTION_SCOPE = "resolution-scope";
public static final String RESOLUTION_CONTEXT = "resolution";
public static final String PRESENTATION_EXCHANGE_V_1_JSON = "presentation-exchange.v1.json";
public static final String PRESENTATION_QUERY_V_08_JSON = "presentation-query.v08.json";

@Inject
private TypeTransformerRegistry typeTransformer;

Expand Down Expand Up @@ -77,8 +72,6 @@ public void initialize(ServiceExtensionContext context) {
validatorRegistry.register(PresentationQuery.PRESENTATION_QUERY_TYPE_PROPERTY, new PresentationQueryValidator());


// Setup API
cacheContextDocuments(getClass().getClassLoader());
var controller = new PresentationApiController(validatorRegistry, typeTransformer, credentialResolver, accessTokenVerifier, presentationGenerator, context.getMonitor());

var jsonLdMapper = typeManager.getMapper(JSON_LD);
Expand All @@ -91,12 +84,5 @@ public void initialize(ServiceExtensionContext context) {
typeTransformer.register(new JsonValueToGenericTypeTransformer(jsonLdMapper));
}

private void cacheContextDocuments(ClassLoader classLoader) {
try {
jsonLd.registerCachedDocument(PRESENTATION_EXCHANGE_URL, classLoader.getResource(PRESENTATION_EXCHANGE_V_1_JSON).toURI());
jsonLd.registerCachedDocument(IATP_CONTEXT_URL, classLoader.getResource(PRESENTATION_QUERY_V_08_JSON).toURI());
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ record PresentationQuerySchema(
String type,
@Schema(name = "scope", requiredMode = NOT_REQUIRED)
List<String> scope,
@Schema(name = "presentation_definition", requiredMode = NOT_REQUIRED)
@Schema(name = "presentationDefinition", requiredMode = NOT_REQUIRED)
PresentationDefinitionSchema presentationDefinitionSchema
) {

Expand All @@ -60,7 +60,7 @@ record PresentationQuerySchema(
"https://identity.foundation/presentation-exchange/submission/v1"
],
"@type": "Query",
"presentation_definition": null,
"presentationDefinition": null,
"scope": [
"org.eclipse.edc.vc.type:SomeCredential_0.3.5:write,
"org.eclipse.edc.vc.type:SomeOtherCredential:read,
Expand All @@ -83,7 +83,7 @@ record PresentationDefinitionSchema() {
private static final String EXAMPLE = """
{
"comment": "taken from https://identity.foundation/presentation-exchange/spec/v2.0.0/#presentation-definition"
"presentation_definition": {
"presentationDefinition": {
"id": "first simple example",
"input_descriptors": [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,14 @@ public interface PresentationApi {
responses = {
@ApiResponse(responseCode = "200", description = "The query was successfully processed, the response contains the VerifiablePresentation",
content = @Content(schema = @Schema(implementation = PresentationResponse.class), mediaType = "application/ld+json")),
@ApiResponse(responseCode = "400", description = "Request body was malformed, for example when both scope and presentation_definition are given",
@ApiResponse(responseCode = "400", description = "Request body was malformed, for example when both scope and presentationDefinition are given",
content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiErrorDetailSchema.class)), mediaType = "application/json")),
@ApiResponse(responseCode = "401", description = "No Authorization header was given.",
content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiErrorDetailSchema.class)), mediaType = "application/json")),
@ApiResponse(responseCode = "403", description = "The given authentication token could not be validated. This can happen, when the request body " +
"calls for a broader query scope than the granted scope in the auth token",
content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiErrorDetailSchema.class)), mediaType = "application/json")),
@ApiResponse(responseCode = "501", description = "When the request contained a presentation_definition object, but the implementation does not support it.",
@ApiResponse(responseCode = "501", description = "When the request contained a presentationDefinition object, but the implementation does not support it.",
content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiErrorDetailSchema.class)), mediaType = "application/json"))
}
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

/**
* Validates, that a JsonObject representing a {@link PresentationQuery} contains <em>either</em> a {@code scope} property,
* <em>or</em> a {@code presentation_definition} query.
* <em>or</em> a {@code presentationDefinition} query.
*/
public class PresentationQueryValidator implements Validator<JsonObject> {
@Override
Expand All @@ -35,11 +35,11 @@ public ValidationResult validate(JsonObject input) {
var presentationDef = input.get(PresentationQuery.PRESENTATION_QUERY_DEFINITION_PROPERTY);

if (scope == null && presentationDef == null) {
return failure(violation("Must contain either a 'scope' or a 'presentation_definition' property.", null));
return failure(violation("Must contain either a 'scope' or a 'presentationDefinition' property.", null));
}

if (scope != null && presentationDef != null) {
return failure(violation("Must contain either a 'scope' or a 'presentation_definition', not both.", null));
return failure(violation("Must contain either a 'scope' or a 'presentationDefinition', not both.", null));
}

return success();
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ void validate_withPresentationDefinition_success() throws JsonProcessingExceptio
@Test
void validate_withNone_fails() {
var jo = createObjectBuilder().build();
assertThat(validator.validate(jo)).isFailed().detail().contains("Must contain either a 'scope' or a 'presentation_definition' property.");
assertThat(validator.validate(jo)).isFailed().detail().contains("Must contain either a 'scope' or a 'presentationDefinition' property.");
}

@Test
Expand All @@ -84,7 +84,7 @@ void validate_withBoth_fails() throws JsonProcessingException {
.add(PresentationQuery.PRESENTATION_QUERY_DEFINITION_PROPERTY, createPresentationDefArray(presDef))
.build();

assertThat(validator.validate(jo)).isFailed().detail().contains("Must contain either a 'scope' or a 'presentation_definition', not both.");
assertThat(validator.validate(jo)).isFailed().detail().contains("Must contain either a 'scope' or a 'presentationDefinition', not both.");
}

private JsonArray createScopeArray() {
Expand Down
1 change: 1 addition & 0 deletions core/identity-hub-core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ plugins {

dependencies {
api(project(":spi:identity-hub-spi"))
implementation(libs.edc.spi.jsonld)
implementation(libs.edc.iatp.service) // JWT validator
implementation(libs.edc.core.crypto) // JWT verifier
implementation(libs.nimbus.jwt)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import org.eclipse.edc.identityhub.token.verification.AccessTokenVerifierImpl;
import org.eclipse.edc.identitytrust.validation.JwtValidator;
import org.eclipse.edc.identitytrust.verification.JwtVerifier;
import org.eclipse.edc.jsonld.spi.JsonLd;
import org.eclipse.edc.runtime.metamodel.annotation.Extension;
import org.eclipse.edc.runtime.metamodel.annotation.Inject;
import org.eclipse.edc.runtime.metamodel.annotation.Provider;
Expand All @@ -29,6 +30,11 @@
import org.eclipse.edc.spi.system.ServiceExtensionContext;
import org.eclipse.edc.verification.jwt.SelfIssuedIdTokenVerifier;

import java.net.URISyntaxException;

import static org.eclipse.edc.identityhub.spi.model.IdentityHubConstants.IATP_CONTEXT_URL;
import static org.eclipse.edc.identityhub.spi.model.IdentityHubConstants.PRESENTATION_EXCHANGE_URL;

/**
* This extension provides some core services for the IdentityHub, such as:
* <ul>
Expand All @@ -42,15 +48,25 @@ public class CoreServicesExtension implements ServiceExtension {

@Setting(value = "Configure this IdentityHub's DID", required = true)
public static final String OWN_DID_PROPERTY = "edc.ih.iam.id";
public static final String PRESENTATION_EXCHANGE_V_1_JSON = "presentation-exchange.v1.json";
public static final String PRESENTATION_QUERY_V_08_JSON = "presentation-query.v08.json";
private JwtVerifier jwtVerifier;
private JwtValidator jwtValidator;

@Inject
private DidResolverRegistry didResolverRegistry;

@Inject
private PublicKeyWrapper identityHubPublicKey;

@Inject
private JsonLd jsonLd;

@Override
public void initialize(ServiceExtensionContext context) {
// Setup API
cacheContextDocuments(getClass().getClassLoader());
}

@Provider
public AccessTokenVerifier createAccessTokenVerifier(ServiceExtensionContext context) {
return new AccessTokenVerifierImpl(getJwtVerifier(), getJwtValidator(), getOwnDid(context), identityHubPublicKey);
Expand All @@ -75,4 +91,13 @@ public JwtVerifier getJwtVerifier() {
private String getOwnDid(ServiceExtensionContext context) {
return context.getConfig().getString(OWN_DID_PROPERTY);
}

private void cacheContextDocuments(ClassLoader classLoader) {
try {
jsonLd.registerCachedDocument(PRESENTATION_EXCHANGE_URL, classLoader.getResource(PRESENTATION_EXCHANGE_V_1_JSON).toURI());
jsonLd.registerCachedDocument(IATP_CONTEXT_URL, classLoader.getResource(PRESENTATION_QUERY_V_08_JSON).toURI());
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}
}
116 changes: 116 additions & 0 deletions core/identity-hub-core/src/main/resources/presentation-query.v08.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
{
"@context": {
"@version": 1.1,
"@protected": true,
"iatp": "https://w3id.org/tractusx-trust/v0.8/",
"cred": "https://www.w3.org/2018/credentials/",
"xsd": "http://www.w3.org/2001/XMLSchema/",
"CredentialContainer": {
"@id": "iatp:CredentialContainer",
"@context": {
"payload": {
"@id": "iatp:payload",
"@type": "xsd:string"
}
}
},
"CredentialMessage": {
"@id": "iatp:CredentialMessage",
"@context": {
"credentials": "iatp:credentials"
}
},
"CredentialObject": {
"@id": "iatp:CredentialObject",
"@context": {
"credentialType": {
"@id": "iatp:credentialType",
"@container": "@set"
},
"format": "iatp:format",
"offerReason": {
"@id": "iatp:offerReason",
"@type": "xsd:string"
},
"bindingMethods": {
"@id": "iatp:bindingMethods",
"@type": "xsd:string",
"@container": "@set"
},
"cryptographicSuites": {
"@id": "iatp:cryptographicSuites",
"@type": "xsd:string",
"@container": "@set"
},
"issuancePolicy": "iatp:issuancePolicy"
}
},
"CredentialOfferMessage": {
"@id": "iatp:CredentialOfferMessage",
"@context": {
"credentialIssuer": "cred:issuer",
"credentials": "iatp:credentials"
}
},
"CredentialRequestMessage": {
"@id": "iatp:CredentialRequestMessage",
"@context": {
"format": "iatp:format",
"type": "@type"
}
},
"CredentialService": "iatp:CredentialService",
"CredentialStatus": {
"@id": "iatp:CredentialStatus",
"@context": {
"requestId": {
"@id": "iatp:requestId",
"@type": "@id"
},
"status": {
"@id": "iatp:status",
"@type": "xsd:string"
}
}
},
"IssuerMetadata": {
"@id": "iatp:IssuerMetadata",
"@context": {
"credentialIssuer": "cred:issuer",
"credentialsSupported": {
"@id": "iatp:credentialsSupported",
"@container": "@set"
}
}
},
"PresentationQueryMessage": {
"@id": "iatp:PresentationQueryMessage",
"@context": {
"presentationDefinition": "iatp:presentationDefinition",
"scope": "iatp:scope"
}
},
"credentials": {
"@id": "iatp:credentials",
"@container": "@set"
},
"credentialSubject": {
"@id": "iatp:credentialSubject",
"@type": "cred:credentialSubject"
},
"format": {
"@id": "iatp:format",
"@type": "xsd:string"
},
"presentationDefinition": {
"@id": "iatp:presentationDefinition",
"@type": "@json"
},
"scope": {
"@id": "iatp:scope",
"@type": "xsd:string",
"@container": "@set"
},
"type": "@type"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ void transform_withPresentationDefinition() throws JsonProcessingException {
"https://w3id.org/tractusx-trust/v0.8"
],
"@type": "Query",
"presentation_definition": {
"presentationDefinition": {
"id": "first simple example",
"input_descriptors": [
{
Expand Down Expand Up @@ -135,7 +135,7 @@ void transform_withScopesAndPresDef() throws JsonProcessingException {
],
"@type": "Query",
"scope": ["test-scope1"],
"presentation_definition": {
"presentationDefinition": {
"id": "first simple example",
"input_descriptors": [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
"@id": "https://w3id.org/tractusx-trust/v0.8/scope",
"@container": "@set"
},
"presentation_definition":{
"@id": "https://w3id.org/tractusx-trust/v0.8/presentation_definition",
"presentationDefinition": {
"@id": "https://w3id.org/tractusx-trust/v0.8/presentationDefinition",
"@type": "@json"
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public class ResolutionApiEndToEndTest {
private static final CredentialQueryResolver CREDENTIAL_QUERY_RESOLVER = mock();
private static final PresentationGenerator PRESENTATION_GENERATOR = mock();
private static final AccessTokenVerifier ACCESS_TOKEN_VERIFIER = mock();

@RegisterExtension
static EdcRuntimeExtension runtime;

Expand Down Expand Up @@ -121,7 +121,7 @@ void query_withPresentationDefinition_shouldReturn503() {
"https://w3id.org/tractusx-trust/v0.8"
],
"@type": "Query",
"presentation_definition":{
"presentationDefinition":{
}
}
""";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
*/
public class PresentationQuery {
public static final String PRESENTATION_QUERY_SCOPE_PROPERTY = IATP_PREFIX + "scope";
public static final String PRESENTATION_QUERY_DEFINITION_PROPERTY = IATP_PREFIX + "presentation_definition";
public static final String PRESENTATION_QUERY_DEFINITION_PROPERTY = IATP_PREFIX + "presentationDefinition";
public static final String PRESENTATION_QUERY_TYPE_PROPERTY = IATP_PREFIX + "Query";
private List<String> scopes = new ArrayList<>();
private PresentationDefinition presentationDefinition;
Expand Down

0 comments on commit 084a28d

Please sign in to comment.