From 98bb25448a9aafa602b5815e624a8e2aac31b69b Mon Sep 17 00:00:00 2001 From: Aaron Coburn Date: Tue, 13 Jun 2023 13:48:06 -0400 Subject: [PATCH] JCL-380: Add issuedAt getter to credentials --- .../client/accessgrant/AccessCredential.java | 37 ++++++++++++++++++- .../client/accessgrant/AccessGrantTest.java | 2 + .../client/accessgrant/AccessRequestTest.java | 2 + 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/access-grant/src/main/java/com/inrupt/client/accessgrant/AccessCredential.java b/access-grant/src/main/java/com/inrupt/client/accessgrant/AccessCredential.java index 67a85369a8c..8788797459c 100644 --- a/access-grant/src/main/java/com/inrupt/client/accessgrant/AccessCredential.java +++ b/access-grant/src/main/java/com/inrupt/client/accessgrant/AccessCredential.java @@ -52,6 +52,7 @@ public class AccessCredential { private final URI recipient; private final URI creator; private final Instant expiration; + private final Instant issuedAt; private final Status status; /** @@ -78,6 +79,7 @@ protected AccessCredential(final URI identifier, final String credential, this.types = metadata.getTypes(); this.status = metadata.getStatus(); this.expiration = metadata.getExpiration(); + this.issuedAt = metadata.getIssuedAt(); } /** @@ -116,6 +118,10 @@ public Instant getExpiration() { return expiration != null ? expiration : Instant.MAX; } + public Instant getIssuedAt() { + return issuedAt; + } + /** * Get the issuer of the access credential. * @@ -188,6 +194,7 @@ public static class CredentialMetadata { private final Set types; private final Status status; private final Instant expiration; + private final Instant issuedAt; /** * A collection of server-managed credential metadata. @@ -197,12 +204,30 @@ public static class CredentialMetadata { * @param types the credential types * @param expiration the credential expiration * @param status the credential status + * @deprecated as of Beta4, please use the 6-parameter constructor */ + @Deprecated public CredentialMetadata(final URI issuer, final URI creator, final Set types, final Instant expiration, final Status status) { + this(issuer, creator, types, Instant.now(), expiration, status); + } + + /** + * A collection of server-managed credential metadata. + * + * @param issuer the issuer + * @param creator the agent who created the credential + * @param types the credential types + * @param issuedAt the credential issuance date + * @param expiration the credential expiration date + * @param status the credential status + */ + public CredentialMetadata(final URI issuer, final URI creator, final Set types, + final Instant issuedAt, final Instant expiration, final Status status) { this.issuer = Objects.requireNonNull(issuer, "issuer may not be null!"); this.creator = Objects.requireNonNull(creator, "creator may not be null!"); this.types = Objects.requireNonNull(types, "types may not be null!"); + this.issuedAt = Objects.requireNonNull(issuedAt, "issuedAt may not be null!"); this.expiration = expiration; this.status = status; } @@ -251,6 +276,15 @@ public Instant getExpiration() { public Status getStatus() { return status; } + + /** + * Get the instant when the credential was issued. + * + * @return the time at which the access credentials was issued + */ + public Instant getIssuedAt() { + return issuedAt; + } } /** User-managed credential data. */ @@ -318,6 +352,7 @@ static CredentialMetadata extractMetadata(final Map data) { new IllegalArgumentException("Missing or invalid issuer field")); final Set types = asSet(data.get(TYPE)).orElseGet(Collections::emptySet); final Instant expiration = asInstant(data.get("expirationDate")).orElse(Instant.MAX); + final Instant issuedAt = asInstant(data.get("issuanceDate")).orElse(Instant.EPOCH); final Status status = asMap(data.get("credentialStatus")).flatMap(credentialStatus -> asSet(credentialStatus.get(TYPE)).filter(statusTypes -> statusTypes.contains(REVOCATION_LIST_2020_STATUS)).map(x -> @@ -329,7 +364,7 @@ static CredentialMetadata extractMetadata(final Map data) { final URI creator = asUri(subject.get("id")).orElseThrow(() -> new IllegalArgumentException("Missing or invalid credentialSubject.id field")); - return new CredentialMetadata(issuer, creator, types, expiration, status); + return new CredentialMetadata(issuer, creator, types, issuedAt, expiration, status); } static Map extractConsent(final Map data, final String property) { diff --git a/access-grant/src/test/java/com/inrupt/client/accessgrant/AccessGrantTest.java b/access-grant/src/test/java/com/inrupt/client/accessgrant/AccessGrantTest.java index a80b0b6a0b6..ad13b95cb4b 100644 --- a/access-grant/src/test/java/com/inrupt/client/accessgrant/AccessGrantTest.java +++ b/access-grant/src/test/java/com/inrupt/client/accessgrant/AccessGrantTest.java @@ -55,6 +55,7 @@ void testReadAccessGrant() throws IOException { expectedTypes.add("SolidAccessGrant"); assertEquals(expectedTypes, grant.getTypes()); 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"), grant.getIdentifier()); assertEquals(Collections.singleton("https://purpose.example/Purpose1"), grant.getPurpose()); @@ -88,6 +89,7 @@ void testReadAccessGrantSingletons() throws IOException { expectedTypes.add("SolidAccessGrant"); assertEquals(expectedTypes, grant.getTypes()); 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"), grant.getIdentifier()); assertEquals(Collections.singleton("https://purpose.example/Purpose1"), grant.getPurpose()); diff --git a/access-grant/src/test/java/com/inrupt/client/accessgrant/AccessRequestTest.java b/access-grant/src/test/java/com/inrupt/client/accessgrant/AccessRequestTest.java index 9e4cfa100ae..24f2d34be81 100644 --- a/access-grant/src/test/java/com/inrupt/client/accessgrant/AccessRequestTest.java +++ b/access-grant/src/test/java/com/inrupt/client/accessgrant/AccessRequestTest.java @@ -53,6 +53,7 @@ void testReadAccessRequest() throws IOException { expectedTypes.add("SolidAccessRequest"); assertEquals(expectedTypes, request.getTypes()); assertEquals(Instant.parse("2022-08-27T12:00:00Z"), request.getExpiration()); + assertEquals(Instant.parse("2022-08-25T20:34:05.153Z"), request.getIssuedAt()); assertEquals(URI.create("https://accessgrant.test/credential/d604c858-209a-4bb6-a7f8-2f52c9617cab"), request.getIdentifier()); assertEquals(Collections.singleton(URI.create("https://purpose.test/Purpose1")), request.getPurposes()); @@ -83,6 +84,7 @@ void testReadAccessRequestSingletons() throws IOException { expectedTypes.add("SolidAccessRequest"); assertEquals(expectedTypes, request.getTypes()); assertEquals(Instant.parse("2022-08-27T12:00:00Z"), request.getExpiration()); + assertEquals(Instant.parse("2022-08-25T20:34:05.153Z"), request.getIssuedAt()); assertEquals(URI.create("https://accessgrant.test/credential/d604c858-209a-4bb6-a7f8-2f52c9617cab"), request.getIdentifier()); assertEquals(Collections.singleton(URI.create("https://purpose.test/Purpose1")), request.getPurposes());