Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,7 @@ public static class CredentialData {
private final Set<URI> purposes;
private final Set<URI> resources;
private final URI recipient;
private final URI accessRequest;

/**
* Create a collection of user-managed credential data.
Expand All @@ -293,10 +294,25 @@ public static class CredentialData {
*/
public CredentialData(final Set<URI> resources, final Set<String> modes,
final Set<URI> purposes, final URI recipient) {
this(resources, modes, purposes, recipient, null);
}

/**
* Create a collection of user-managed credential data.
*
* @param resources the resources referenced by the credential
* @param modes the access modes defined by this credential
* @param purposes the purposes associated with this credential
* @param recipient the recipient for this credential, may be {@code null}
* @param accessRequest the access request identifier, may be {@code null}
*/
public CredentialData(final Set<URI> resources, final Set<String> modes,
final Set<URI> purposes, final URI recipient, final URI accessRequest) {
this.modes = Objects.requireNonNull(modes, "modes may not be null!");
this.purposes = Objects.requireNonNull(purposes, "purposes may not be null!");
this.resources = Objects.requireNonNull(resources, "resources may not be null!");
this.recipient = recipient;
this.accessRequest = accessRequest;
}

/**
Expand Down Expand Up @@ -334,6 +350,15 @@ public Set<URI> getResources() {
public URI getRecipient() {
return recipient;
}

/**
* Get the access request identifier associated with this credential.
*
* @return the access request identifier, may be {@code null}
*/
public URI getAccessRequest() {
return accessRequest;
}
}

static CredentialMetadata extractMetadata(final Map<String, Object> data) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ public class AccessDenial extends AccessCredential {
private static final Set<String> supportedTypes = getSupportedTypes();
private static final JsonService jsonService = ServiceProvider.getJsonService();

private final URI accessRequest;

/**
* Read a verifiable presentation as an AccessDenial.
*
Expand All @@ -60,6 +62,16 @@ public class AccessDenial extends AccessCredential {
protected AccessDenial(final URI identifier, final String credential, final CredentialData data,
final CredentialMetadata metadata) {
super(identifier, credential, data, metadata);
this.accessRequest = data.getAccessRequest();
}

/**
* Get the corresponding access request identifier.
*
* @return the access request identifier, may be {@code null}
*/
public URI getAccessRequest() {
return accessRequest;
}

/**
Expand Down Expand Up @@ -127,12 +139,14 @@ static AccessDenial parse(final String serialization) throws IOException {
final Optional<URI> other = asUri(consent.get("isProvidedTo"));

final URI recipient = person.orElseGet(() -> controller.orElseGet(() -> other.orElse(null)));
final URI accessRequest = asUri(consent.get("request")).orElse(null);
final Set<String> modes = asSet(consent.get("mode")).orElseGet(Collections::emptySet);
final Set<URI> resources = asSet(consent.get("forPersonalData")).orElseGet(Collections::emptySet)
.stream().map(URI::create).collect(Collectors.toSet());
final Set<URI> purposes = asSet(consent.get("forPurpose")).orElseGet(Collections::emptySet)
.stream().flatMap(AccessCredential::filterUris).collect(Collectors.toSet());
final CredentialData credentialData = new CredentialData(resources, modes, purposes, recipient);
final CredentialData credentialData = new CredentialData(resources, modes, purposes, recipient,
accessRequest);

return new AccessDenial(identifier, serialization, credentialData, credentialMetadata);
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ public class AccessGrant extends AccessCredential {
private static final Set<String> supportedTypes = getSupportedTypes();
private static final JsonService jsonService = ServiceProvider.getJsonService();

private final URI accessRequest;

/**
* Read a verifiable presentation as an AccessGrant.
*
Expand All @@ -60,6 +62,16 @@ public class AccessGrant extends AccessCredential {
protected AccessGrant(final URI identifier, final String credential, final CredentialData data,
final CredentialMetadata metadata) {
super(identifier, credential, data, metadata);
this.accessRequest = data.getAccessRequest();
}

/**
* Get the corresponding access request identifier.
*
* @return the access request identifier, may be {@code null}
*/
public URI getAccessRequest() {
return accessRequest;
}

/**
Expand Down Expand Up @@ -126,12 +138,14 @@ static AccessGrant parse(final String serialization) throws IOException {
final Optional<URI> other = asUri(consent.get("isProvidedTo"));

final URI recipient = person.orElseGet(() -> controller.orElseGet(() -> other.orElse(null)));
final URI accessRequest = asUri(consent.get("request")).orElse(null);
final Set<String> modes = asSet(consent.get("mode")).orElseGet(Collections::emptySet);
final Set<URI> resources = asSet(consent.get("forPersonalData")).orElseGet(Collections::emptySet)
.stream().map(URI::create).collect(Collectors.toSet());
final Set<URI> purposes = asSet(consent.get("forPurpose")).orElseGet(Collections::emptySet)
.stream().flatMap(AccessCredential::filterUris).collect(Collectors.toSet());
final CredentialData credentialData = new CredentialData(resources, modes, purposes, recipient);
final CredentialData credentialData = new CredentialData(resources, modes, purposes, recipient,
accessRequest);

return new AccessGrant(identifier, serialization, credentialData, credentialMetadata);
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ public class AccessGrantClient {
private static final String IS_CONSENT_FOR_DATA_SUBJECT = "isConsentForDataSubject";
private static final String FOR_PERSONAL_DATA = "forPersonalData";
private static final String HAS_STATUS = "hasStatus";
private static final String REQUEST = "request";
private static final String MODE = "mode";
private static final String PROVIDED_CONSENT = "providedConsent";
private static final String FOR_PURPOSE = "forPurpose";
Expand Down Expand Up @@ -260,7 +261,8 @@ public CompletionStage<AccessGrant> grantAccess(final AccessRequest request) {
Objects.requireNonNull(request, "Request may not be null!");
return v1Metadata().thenCompose(metadata -> {
final Map<String, Object> data = buildAccessGrantv1(request.getCreator(), request.getResources(),
request.getModes(), request.getPurposes(), request.getExpiration(), request.getIssuedAt());
request.getModes(), request.getPurposes(), request.getExpiration(), request.getIssuedAt(),
request.getIdentifier());
final Request req = Request.newBuilder(metadata.issueEndpoint)
.header(CONTENT_TYPE, APPLICATION_JSON)
.POST(Request.BodyPublishers.ofByteArray(serialize(data))).build();
Expand Down Expand Up @@ -292,7 +294,8 @@ public CompletionStage<AccessDenial> denyAccess(final AccessRequest request) {
Objects.requireNonNull(request, "Request may not be null!");
return v1Metadata().thenCompose(metadata -> {
final Map<String, Object> data = buildAccessDenialv1(request.getCreator(), request.getResources(),
request.getModes(), request.getPurposes(), request.getExpiration(), request.getIssuedAt());
request.getModes(), request.getPurposes(), request.getExpiration(), request.getIssuedAt(),
request.getIdentifier());
final Request req = Request.newBuilder(metadata.issueEndpoint)
.header(CONTENT_TYPE, APPLICATION_JSON)
.POST(Request.BodyPublishers.ofByteArray(serialize(data))).build();
Expand Down Expand Up @@ -799,13 +802,14 @@ static URI asUri(final Object value) {
}

static Map<String, Object> buildAccessDenialv1(final URI agent, final Set<URI> resources, final Set<String> modes,
final Set<URI> purposes, final Instant expiration, final Instant issuance) {
final Set<URI> purposes, final Instant expiration, final Instant issuance, final URI accessRequest) {
Objects.requireNonNull(agent, "Access denial agent may not be null!");
final Map<String, Object> consent = new HashMap<>();
consent.put(MODE, modes);
consent.put(HAS_STATUS, "https://w3id.org/GConsent#ConsentStatusRefused");
consent.put(FOR_PERSONAL_DATA, resources);
consent.put(IS_PROVIDED_TO, agent);
consent.put(REQUEST, accessRequest);
if (!purposes.isEmpty()) {
consent.put(FOR_PURPOSE, purposes);
}
Expand All @@ -829,13 +833,14 @@ static Map<String, Object> buildAccessDenialv1(final URI agent, final Set<URI> r
}

static Map<String, Object> buildAccessGrantv1(final URI agent, final Set<URI> resources, final Set<String> modes,
final Set<URI> purposes, final Instant expiration, final Instant issuance) {
final Set<URI> purposes, final Instant expiration, final Instant issuance, final URI accessRequest) {
Objects.requireNonNull(agent, "Access grant agent may not be null!");
final Map<String, Object> consent = new HashMap<>();
consent.put(MODE, modes);
consent.put(HAS_STATUS, "https://w3id.org/GConsent#ConsentStatusExplicitlyGiven");
consent.put(FOR_PERSONAL_DATA, resources);
consent.put(IS_PROVIDED_TO, agent);
consent.put(REQUEST, accessRequest);
if (!purposes.isEmpty()) {
consent.put(FOR_PURPOSE, purposes);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ void testReadAccessDenial() throws IOException {
expectedTypes.add("VerifiableCredential");
expectedTypes.add("SolidAccessDenial");
assertEquals(expectedTypes, denial.getTypes());
assertEquals(URI.create("https://accessrequest.test/5678"), denial.getAccessRequest());
assertEquals(Instant.parse("2022-08-27T12:00:00Z"), denial.getExpiration());
assertEquals(Instant.parse("2022-08-25T20:34:05.153Z"), denial.getIssuedAt());
assertEquals(URI.create("https://accessgrant.test/credential/fc2dbcd9-81d4-4fa4-8fd4-239e16dd83ab"),
Expand Down Expand Up @@ -80,6 +81,7 @@ void testReadAccessDenialQualifiedName() throws IOException {
final Set<String> expectedTypes = new HashSet<>();
expectedTypes.add("VerifiableCredential");
expectedTypes.add("vc:SolidAccessDenial");
assertNull(denial.getAccessRequest());
assertEquals(expectedTypes, denial.getTypes());
assertEquals(Instant.parse("2022-08-27T12:00:00Z"), denial.getExpiration());
assertEquals(Instant.parse("2022-08-25T20:34:05.153Z"), denial.getIssuedAt());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ void testReadAccessGrant() throws IOException {
expectedTypes.add("VerifiableCredential");
expectedTypes.add("SolidAccessGrant");
assertEquals(expectedTypes, grant.getTypes());
assertNull(grant.getAccessRequest());
assertEquals(Instant.parse("2022-08-27T12:00:00Z"), grant.getExpiration());
assertEquals(Instant.parse("2022-08-25T20:34:05.153Z"), grant.getIssuedAt());
assertEquals(URI.create("https://accessgrant.example/credential/5c6060ad-2f16-4bc1-b022-dffb46bff626"),
Expand Down Expand Up @@ -83,6 +84,7 @@ void testReadAccessGrantQualifiedName() throws IOException {
expectedTypes.add("VerifiableCredential");
expectedTypes.add("vc:SolidAccessGrant");
assertEquals(expectedTypes, grant.getTypes());
assertEquals(URI.create("https://accessrequest.example/1234"), grant.getAccessRequest());
assertEquals(Instant.parse("2022-08-27T12:00:00Z"), grant.getExpiration());
assertEquals(Instant.parse("2022-08-25T20:34:05.153Z"), grant.getIssuedAt());
assertEquals(URI.create("https://accessgrant.example/credential/5c6060ad-2f16-4bc1-b022-dffb46bff626"),
Expand Down
3 changes: 2 additions & 1 deletion access-grant/src/test/resources/access_denial1.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"https://www.w3.org/2018/credentials/v1",
"https://w3id.org/security/suites/ed25519-2020/v1",
"https://w3id.org/vc-revocation-list-2020/v1",
"https://schema.inrupt.com/credentials/v1.jsonld"],
"https://schema.inrupt.com/credentials/v2.jsonld"],
"id":"https://accessgrant.test/credential/fc2dbcd9-81d4-4fa4-8fd4-239e16dd83ab",
"type":["VerifiableCredential","SolidAccessDenial"],
"issuer":"https://accessgrant.test",
Expand All @@ -21,6 +21,7 @@
"id":"https://id.test/grantor",
"providedConsent":{
"mode":["Read"],
"request": "https://accessrequest.test/5678",
"hasStatus":"https://w3id.org/GConsent#ConsentStatusRefused",
"isProvidedTo":"https://id.test/grantee",
"forPurpose":["https://purpose.test/Purpose1"],
Expand Down
3 changes: 2 additions & 1 deletion access-grant/src/test/resources/access_grant4.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"https://www.w3.org/2018/credentials/v1",
"https://w3id.org/security/suites/ed25519-2020/v1",
"https://w3id.org/vc-revocation-list-2020/v1",
"https://schema.inrupt.com/credentials/v1.jsonld"],
"https://schema.inrupt.com/credentials/v2.jsonld"],
"id":"https://accessgrant.example/credential/5c6060ad-2f16-4bc1-b022-dffb46bff626",
"type":["VerifiableCredential","vc:SolidAccessGrant"],
"issuer":"https://accessgrant.example",
Expand All @@ -20,6 +20,7 @@
"credentialSubject":{
"id":"https://id.example/grantor",
"providedConsent":{
"request":"https://accessrequest.example/1234",
"mode":["Read"],
"hasStatus":"https://w3id.org/GConsent#ConsentStatusExplicitlyGiven",
"isProvidedTo":"https://id.example/grantee",
Expand Down