Skip to content

Commit a745872

Browse files
authored
JCL-367: Distinguish between creator and recipient in access credential queries (#505)
* JCL-367: Distinguish between creator and recipient in access credential queries * Remove repeated strings
1 parent 6d9ca40 commit a745872

File tree

3 files changed

+69
-64
lines changed

3 files changed

+69
-64
lines changed

access-grant/src/main/java/com/inrupt/client/accessgrant/AccessGrantClient.java

Lines changed: 43 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -65,15 +65,14 @@
6565
* {@link Session} object, typically an OpenID-based session:
6666
*
6767
* <pre>{@code
68-
URI SOLID_ACCESS_GRANT = URI.create("http://www.w3.org/ns/solid/vc#SolidAccessGrant");
6968
URI issuer = URI.create("https://issuer.example");
7069
Session openid = OpenIdSession.ofIdToken(idToken);
7170
7271
AccessGrantClient client = new AccessGrantClient(issuer).session(session);
7372
7473
URI resource = URI.create("https://storage.example/data/resource");
7574
URI purpose = URI.create("https://purpose.example/1");
76-
client.query(null, resource, purpose, "Read", AccessGrant.class)
75+
client.query(resource, null, openid.getPrincipal().orElse(null), purpose, "Read", AccessGrant.class)
7776
.thenApply(grants -> AccessGrantSession.ofAccessGrant(openid, grants.toArray(new AccessGrant[0])))
7877
.thenApply(session -> SolidClient.getClient().session(session))
7978
.thenAccept(cl -> {
@@ -89,6 +88,7 @@ public class AccessGrantClient {
8988
private static final String VC_CONTEXT_URI = "https://www.w3.org/2018/credentials/v1";
9089
private static final String INRUPT_CONTEXT_URI = "https://schema.inrupt.com/credentials/v1.jsonld";
9190
private static final String VERIFIABLE_CREDENTIAL = "verifiableCredential";
91+
private static final String SOLID_VC_NAMESPACE = "http://www.w3.org/ns/solid/vc#";
9292
private static final String TYPE = "type";
9393
private static final String APPLICATION_JSON = "application/json";
9494
private static final String CONTENT_TYPE = "Content-Type";
@@ -102,9 +102,12 @@ public class AccessGrantClient {
102102
private static final String FOR_PURPOSE = "forPurpose";
103103
private static final String EXPIRATION_DATE = "expirationDate";
104104
private static final String CREDENTIAL = "credential";
105-
private static final URI ACCESS_GRANT = URI.create("http://www.w3.org/ns/solid/vc#SolidAccessGrant");
106-
private static final URI ACCESS_REQUEST = URI.create("http://www.w3.org/ns/solid/vc#SolidAccessRequest");
107-
private static final URI ACCESS_DENIAL = URI.create("http://www.w3.org/ns/solid/vc#SolidAccessDenial");
105+
private static final String SOLID_ACCESS_GRANT = "SolidAccessGrant";
106+
private static final String SOLID_ACCESS_REQUEST = "SolidAccessRequest";
107+
private static final String SOLID_ACCESS_DENIAL = "SolidAccessDenial";
108+
private static final URI FQ_ACCESS_GRANT = URI.create(SOLID_VC_NAMESPACE + SOLID_ACCESS_GRANT);
109+
private static final URI FQ_ACCESS_REQUEST = URI.create(SOLID_VC_NAMESPACE + SOLID_ACCESS_REQUEST);
110+
private static final URI FQ_ACCESS_DENIAL = URI.create(SOLID_VC_NAMESPACE + SOLID_ACCESS_DENIAL);
108111
private static final Set<String> ACCESS_GRANT_TYPES = getAccessGrantTypes();
109112
private static final Set<String> ACCESS_REQUEST_TYPES = getAccessRequestTypes();
110113
private static final Set<String> ACCESS_DENIAL_TYPES = getAccessDenialTypes();
@@ -301,9 +304,9 @@ public CompletionStage<AccessGrant> issue(final URI type, final URI agent, final
301304
}
302305
return v1Metadata().thenCompose(metadata -> {
303306
final Map<String, Object> data;
304-
if (ACCESS_GRANT.equals(type)) {
307+
if (FQ_ACCESS_GRANT.equals(type)) {
305308
data = buildAccessGrantv1(agent, resources, modes, expiration, uriPurposes);
306-
} else if (ACCESS_REQUEST.equals(type)) {
309+
} else if (FQ_ACCESS_REQUEST.equals(type)) {
307310
data = buildAccessRequestv1(agent, resources, modes, expiration, uriPurposes);
308311
} else {
309312
throw new AccessGrantException("Unsupported grant type: " + type);
@@ -377,35 +380,36 @@ public CompletionStage<AccessCredentialVerification> verify(final AccessCredenti
377380
* Perform an Access Grant query.
378381
*
379382
* @param <T> the AccessCredential type
380-
* @param agent the agent identifier, may be {@code null}
381383
* @param resource the resource identifier, may be {@code null}
384+
* @param creator the identifier for the agent who created the credential, may be {@code null}
385+
* @param recipient the identifier for the agent who is the recipient for the credential, may be {@code null}
382386
* @param purpose the access purpose, may be {@code null}
383387
* @param mode the access mode, may be {@code null}
384388
* @param clazz the AccessCredential type, either {@link AccessGrant} or {@link AccessRequest}
385-
* @return the next stage of completion, including the matched Access Grants
389+
* @return the next stage of completion, including the matched Access Credentials
386390
*/
387-
public <T extends AccessCredential> CompletionStage<List<T>> query(final URI agent, final URI resource,
388-
final URI purpose, final String mode, final Class<T> clazz) {
391+
public <T extends AccessCredential> CompletionStage<List<T>> query(final URI resource, final URI creator,
392+
final URI recipient, final URI purpose, final String mode, final Class<T> clazz) {
389393
Objects.requireNonNull(clazz, "The clazz parameter must not be null!");
390394

391395
final URI type;
392396
final Set<String> supportedTypes;
393397
if (AccessGrant.class.isAssignableFrom(clazz)) {
394-
type = URI.create("SolidAccessGrant");
398+
type = URI.create(SOLID_ACCESS_GRANT);
395399
supportedTypes = ACCESS_GRANT_TYPES;
396400
} else if (AccessRequest.class.isAssignableFrom(clazz)) {
397-
type = URI.create("SolidAccessRequest");
401+
type = URI.create(SOLID_ACCESS_REQUEST);
398402
supportedTypes = ACCESS_REQUEST_TYPES;
399403
} else if (AccessDenial.class.isAssignableFrom(clazz)) {
400-
type = URI.create("SolidAccessDenial");
404+
type = URI.create(SOLID_ACCESS_DENIAL);
401405
supportedTypes = ACCESS_DENIAL_TYPES;
402406
} else {
403407
throw new AccessGrantException("Unsupported type " + clazz + " in query request");
404408
}
405409

406410
return v1Metadata().thenCompose(metadata -> {
407411
final List<CompletableFuture<List<T>>> futures = buildQuery(config.getIssuer(), type,
408-
agent, resource, purpose, mode).stream()
412+
resource, creator, recipient, purpose, mode).stream()
409413
.map(data -> Request.newBuilder(metadata.queryEndpoint)
410414
.header(CONTENT_TYPE, APPLICATION_JSON)
411415
.POST(Request.BodyPublishers.ofByteArray(serialize(data))).build())
@@ -451,7 +455,7 @@ public CompletionStage<List<AccessGrant>> query(final URI type, final URI agent,
451455
Objects.requireNonNull(type, "The type parameter must not be null!");
452456
return v1Metadata().thenCompose(metadata -> {
453457
final List<CompletableFuture<List<AccessGrant>>> futures = buildQuery(config.getIssuer(), type,
454-
agent, resource, null, mode).stream()
458+
resource, null, agent, null, mode).stream()
455459
.map(data -> Request.newBuilder(metadata.queryEndpoint)
456460
.header(CONTENT_TYPE, APPLICATION_JSON)
457461
.POST(Request.BodyPublishers.ofByteArray(serialize(data))).build())
@@ -683,26 +687,26 @@ static Collection<Object> getCredentials(final Map<String, Object> data) {
683687
return Collections.emptyList();
684688
}
685689

686-
static List<Map<String, Object>> buildQuery(final URI issuer, final URI type, final URI agent, final URI resource,
687-
final URI purpose, final String mode) {
690+
static List<Map<String, Object>> buildQuery(final URI issuer, final URI type, final URI resource, final URI creator,
691+
final URI recipient, final URI purpose, final String mode) {
688692
final List<Map<String, Object>> queries = new ArrayList<>();
689-
buildQuery(queries, issuer, type, agent, resource, purpose, mode);
693+
buildQuery(queries, issuer, type, resource, creator, recipient, purpose, mode);
690694
return queries;
691695
}
692696

693-
static void buildQuery(final List<Map<String, Object>> queries, final URI issuer, final URI type, final URI agent,
694-
final URI resource, final URI purpose, final String mode) {
697+
static void buildQuery(final List<Map<String, Object>> queries, final URI issuer, final URI type,
698+
final URI resource, final URI creator, final URI recipient, final URI purpose, final String mode) {
695699
final Map<String, Object> credential = new HashMap<>();
696700
credential.put(CONTEXT, Arrays.asList(VC_CONTEXT_URI, INRUPT_CONTEXT_URI));
697701
credential.put("issuer", issuer);
698702
credential.put(TYPE, Arrays.asList(type));
699703

700704
final Map<String, Object> consent = new HashMap<>();
701-
if (agent != null) {
705+
if (recipient != null) {
702706
if (isAccessGrant(type) || isAccessDenial(type)) {
703-
consent.put(IS_PROVIDED_TO, agent);
707+
consent.put(IS_PROVIDED_TO, recipient);
704708
} else if (isAccessRequest(type)) {
705-
consent.put(IS_CONSENT_FOR_DATA_SUBJECT, agent);
709+
consent.put(IS_CONSENT_FOR_DATA_SUBJECT, recipient);
706710
}
707711
}
708712
if (resource != null) {
@@ -716,13 +720,18 @@ static void buildQuery(final List<Map<String, Object>> queries, final URI issuer
716720
}
717721

718722
final Map<String, Object> subject = new HashMap<>();
723+
if (creator != null) {
724+
subject.put("id", creator);
725+
}
719726
if (!consent.isEmpty()) {
720727
if (isAccessGrant(type) || isAccessDenial(type)) {
721728
subject.put(PROVIDED_CONSENT, consent);
722729
} else if (isAccessRequest(type)) {
723730
subject.put("hasConsent", consent);
724731
}
725732
credential.put(CREDENTIAL_SUBJECT, subject);
733+
} else if (!subject.isEmpty()) {
734+
credential.put(CREDENTIAL_SUBJECT, subject);
726735
}
727736

728737
final Map<String, Object> data = new HashMap<>();
@@ -733,7 +742,7 @@ static void buildQuery(final List<Map<String, Object>> queries, final URI issuer
733742
// Recurse
734743
final URI parent = getParent(resource);
735744
if (parent != null) {
736-
buildQuery(queries, issuer, type, agent, parent, purpose, mode);
745+
buildQuery(queries, issuer, type, parent, creator, recipient, purpose, mode);
737746
}
738747
}
739748

@@ -849,35 +858,35 @@ static boolean isSuccess(final int statusCode) {
849858

850859
static Set<String> getAccessRequestTypes() {
851860
final Set<String> types = new HashSet<>();
852-
types.add("SolidAccessRequest");
853-
types.add(ACCESS_REQUEST.toString());
861+
types.add(SOLID_ACCESS_REQUEST);
862+
types.add(FQ_ACCESS_REQUEST.toString());
854863
return Collections.unmodifiableSet(types);
855864
}
856865

857866
static Set<String> getAccessGrantTypes() {
858867
final Set<String> types = new HashSet<>();
859-
types.add("SolidAccessGrant");
860-
types.add(ACCESS_GRANT.toString());
868+
types.add(SOLID_ACCESS_GRANT);
869+
types.add(FQ_ACCESS_GRANT.toString());
861870
return Collections.unmodifiableSet(types);
862871
}
863872

864873
static Set<String> getAccessDenialTypes() {
865874
final Set<String> types = new HashSet<>();
866-
types.add("SolidAccessDenial");
867-
types.add(ACCESS_DENIAL.toString());
875+
types.add(SOLID_ACCESS_DENIAL);
876+
types.add(FQ_ACCESS_DENIAL.toString());
868877
return Collections.unmodifiableSet(types);
869878
}
870879

871880
static boolean isAccessGrant(final URI type) {
872-
return "SolidAccessGrant".equals(type.toString()) || ACCESS_GRANT.equals(type);
881+
return SOLID_ACCESS_GRANT.equals(type.toString()) || FQ_ACCESS_GRANT.equals(type);
873882
}
874883

875884
static boolean isAccessRequest(final URI type) {
876-
return "SolidAccessRequest".equals(type.toString()) || ACCESS_REQUEST.equals(type);
885+
return SOLID_ACCESS_REQUEST.equals(type.toString()) || FQ_ACCESS_REQUEST.equals(type);
877886
}
878887

879888
static boolean isAccessDenial(final URI type) {
880-
return "SolidAccessDenial".equals(type.toString()) || ACCESS_DENIAL.equals(type);
889+
return SOLID_ACCESS_DENIAL.equals(type.toString()) || FQ_ACCESS_DENIAL.equals(type);
881890
}
882891

883892
/**

access-grant/src/test/java/com/inrupt/client/accessgrant/AccessGrantClientTest.java

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -508,9 +508,8 @@ void testQueryGrant() {
508508
final String token = generateIdToken(claims);
509509
final AccessGrantClient client = agClient.session(OpenIdSession.ofIdToken(token));
510510

511-
final List<AccessGrant> grants = client.query(null,
512-
URI.create("https://storage.example/e973cc3d-5c28-4a10-98c5-e40079289358/a/b/c"), null, "Read",
513-
AccessGrant.class)
511+
final URI resource = URI.create("https://storage.example/e973cc3d-5c28-4a10-98c5-e40079289358/a/b/c");
512+
final List<AccessGrant> grants = client.query(resource, null, null, null, "Read", AccessGrant.class)
514513
.toCompletableFuture().join();
515514
assertEquals(1, grants.size());
516515
}
@@ -525,9 +524,8 @@ void testQueryGrantAgent() {
525524
final String token = generateIdToken(claims);
526525
final AccessGrantClient client = agClient.session(OpenIdSession.ofIdToken(token));
527526

528-
final List<AccessGrant> grants = client.query(URI.create("https://id.test/user"),
529-
null, null, "Read", AccessGrant.class)
530-
.toCompletableFuture().join();
527+
final List<AccessGrant> grants = client.query(null, null, URI.create("https://id.test/user"),
528+
null, "Read", AccessGrant.class).toCompletableFuture().join();
531529
assertEquals(1, grants.size());
532530
}
533531

@@ -541,9 +539,8 @@ void testQueryRequestAgent() {
541539
final String token = generateIdToken(claims);
542540
final AccessGrantClient client = agClient.session(OpenIdSession.ofIdToken(token));
543541

544-
final List<AccessRequest> requests = client.query(URI.create("https://id.test/user"),
545-
null, null, "Read", AccessRequest.class)
546-
.toCompletableFuture().join();
542+
final List<AccessRequest> requests = client.query(null, null, URI.create("https://id.test/user"),
543+
null, "Read", AccessRequest.class).toCompletableFuture().join();
547544
assertEquals(1, requests.size());
548545
}
549546

@@ -557,10 +554,9 @@ void testQueryRequest() {
557554
final String token = generateIdToken(claims);
558555
final AccessGrantClient client = agClient.session(OpenIdSession.ofIdToken(token));
559556

560-
final List<AccessRequest> requests = client.query(null,
561-
URI.create("https://storage.example/f1759e6d-4dda-4401-be61-d90d070a5474/a/b/c"), null, "Read",
562-
AccessRequest.class)
563-
.toCompletableFuture().join();
557+
final URI resource = URI.create("https://storage.example/f1759e6d-4dda-4401-be61-d90d070a5474/a/b/c");
558+
final List<AccessRequest> requests = client.query(resource, null, null, null, "Read", AccessRequest.class)
559+
.toCompletableFuture().join();
564560
assertEquals(1, requests.size());
565561
}
566562

@@ -574,10 +570,9 @@ void testQueryDenial() {
574570
final String token = generateIdToken(claims);
575571
final AccessGrantClient client = agClient.session(OpenIdSession.ofIdToken(token));
576572

577-
final List<AccessDenial> grants = client.query(null,
578-
URI.create("https://storage.example/ef9c4b90-0459-408d-bfa9-1c61d46e1eaf/e/f/g"), null, "Read",
579-
AccessDenial.class)
580-
.toCompletableFuture().join();
573+
final URI resource = URI.create("https://storage.example/ef9c4b90-0459-408d-bfa9-1c61d46e1eaf/e/f/g");
574+
final List<AccessDenial> grants = client.query(resource, null, null, null, "Read", AccessDenial.class)
575+
.toCompletableFuture().join();
581576
assertEquals(1, grants.size());
582577
}
583578

@@ -592,7 +587,8 @@ void testQueryInvalidType() {
592587
final AccessGrantClient client = agClient.session(OpenIdSession.ofIdToken(token));
593588

594589
final URI uri = URI.create("https://storage.example/f1759e6d-4dda-4401-be61-d90d070a5474/a/b/c");
595-
assertThrows(AccessGrantException.class, () -> client.query(null, uri, null, "Read", AccessCredential.class));
590+
assertThrows(AccessGrantException.class, () ->
591+
client.query(uri, null, null, null, "Read", AccessCredential.class));
596592
}
597593

598594

integration/base/src/main/java/com/inrupt/client/integration/base/AccessGrantScenarios.java

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -323,17 +323,17 @@ void accessGrantQueryByRequestorTest(final Session session) {
323323
final AccessGrantClient accessGrantClient = new AccessGrantClient(URI.create(VC_PROVIDER)).session(session);
324324

325325
//query for all grants issued by the user
326-
final List<AccessRequest> grants = accessGrantClient.query(URI.create(webidUrl),
327-
sharedResource, PURPOSE1, GRANT_MODE_READ, AccessRequest.class)
326+
final List<AccessRequest> grants = accessGrantClient.query(sharedResource, null, URI.create(webidUrl),
327+
PURPOSE1, GRANT_MODE_READ, AccessRequest.class)
328328
.toCompletableFuture().join();
329329
// result is 4 because we retrieve the grants for each path
330330
// sharedTextFileURI =
331331
// http://localhost:57577/private/accessgrant-test-2c82772f-7c0a-4e39-9466-abf9756b59c7/sharedFile.txt
332332
assertEquals(1, grants.size());
333333

334334
//query for all grants issued by a random user
335-
final List<AccessRequest> randomGrants = accessGrantClient.query(URI.create("https://someuser.test"),
336-
sharedResource, PURPOSE1, GRANT_MODE_READ, AccessRequest.class)
335+
final List<AccessRequest> randomGrants = accessGrantClient.query(sharedResource, null,
336+
URI.create("https://someuser.test"), PURPOSE1, GRANT_MODE_READ, AccessRequest.class)
337337
.toCompletableFuture().join();
338338
assertEquals(0, randomGrants.size());
339339
}
@@ -347,14 +347,14 @@ void accessGrantQueryByResourceTest(final Session session) {
347347
final AccessGrantClient accessGrantClient = new AccessGrantClient(URI.create(VC_PROVIDER)).session(session);
348348

349349
//query for all grants of a dedicated resource
350-
final List<AccessRequest> requests = accessGrantClient.query(URI.create(webidUrl),
351-
sharedResource, PURPOSE1, GRANT_MODE_READ, AccessRequest.class)
350+
final List<AccessRequest> requests = accessGrantClient.query(sharedResource, null, URI.create(webidUrl),
351+
PURPOSE1, GRANT_MODE_READ, AccessRequest.class)
352352
.toCompletableFuture().join();
353353
assertEquals(1, requests.size());
354354

355355
//query for all grants of a random resource
356-
final List<AccessRequest> randomGrants = accessGrantClient.query(URI.create(webidUrl),
357-
URI.create("https://somerandom.test"), PURPOSE1, GRANT_MODE_READ, AccessRequest.class)
356+
final List<AccessRequest> randomGrants = accessGrantClient.query(URI.create("https://somerandom.test"),
357+
null, URI.create(webidUrl), PURPOSE1, GRANT_MODE_READ, AccessRequest.class)
358358
.toCompletableFuture().join();
359359
assertEquals(0, randomGrants.size());
360360
}
@@ -368,14 +368,14 @@ void accessGrantQueryByPurposeTest(final Session session) {
368368
final AccessGrantClient accessGrantClient = new AccessGrantClient(URI.create(VC_PROVIDER)).session(session);
369369

370370
//query for all grants with a dedicated purpose
371-
final List<AccessRequest> requests = accessGrantClient.query(URI.create(webidUrl),
372-
sharedResource, PURPOSE1, GRANT_MODE_READ, AccessRequest.class)
371+
final List<AccessRequest> requests = accessGrantClient.query(sharedResource, null, URI.create(webidUrl),
372+
PURPOSE1, GRANT_MODE_READ, AccessRequest.class)
373373
.toCompletableFuture().join();
374374
assertEquals(1, requests.size());
375375

376376
//query for all grants of dedicated purpose combinations
377-
final List<AccessGrant> randomGrants = accessGrantClient.query(URI.create(webidUrl),
378-
sharedResource, PURPOSE1, GRANT_MODE_WRITE, AccessGrant.class)
377+
final List<AccessGrant> randomGrants = accessGrantClient.query(sharedResource, null, URI.create(webidUrl),
378+
PURPOSE1, GRANT_MODE_WRITE, AccessGrant.class)
379379
.toCompletableFuture().join();
380380
assertEquals(0, randomGrants.size()); //our grant is actually a Read
381381
}

0 commit comments

Comments
 (0)