com.inrupt.client.accessgrant.AccessGrantClient Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of inrupt-client-accessgrant Show documentation
Show all versions of inrupt-client-accessgrant Show documentation
Access Grant support for the Inrupt Client Libraries.
/*
* Copyright Inrupt Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
* Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.inrupt.client.accessgrant;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.inrupt.client.Client;
import com.inrupt.client.ClientCache;
import com.inrupt.client.ClientProvider;
import com.inrupt.client.Headers;
import com.inrupt.client.Request;
import com.inrupt.client.Response;
import com.inrupt.client.auth.Session;
import com.inrupt.client.spi.JsonService;
import com.inrupt.client.spi.ServiceProvider;
import com.inrupt.client.util.URIBuilder;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.lang.reflect.Type;
import java.net.URI;
import java.time.Duration;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A client for interacting with and managing Access Grant Resources.
*
* This client will require a suitable
* {@link Session} object, typically an OpenID-based session:
*
*
{@code
URI issuer = URI.create("https://issuer.example");
Session openid = OpenIdSession.ofIdToken(idToken);
AccessGrantClient client = new AccessGrantClient(issuer).session(session);
URI resource = URI.create("https://storage.example/data/resource");
URI purpose = URI.create("https://purpose.example/1");
CredentialFilter filter = CredentialFilter.newBuilder()
.status(CredentialStatus.ACTIVE)
.resource(resource)
.purpose(purpose)
.build(AccessGrant.class);
client.query(filter)
.thenApply(results -> AccessGrantSession.ofAccessGrant(openid,
results.getItems().toArray(new AccessGrant[0])))
.thenApply(session -> SolidClient.getClient().session(session))
.thenAccept(cl -> {
// Do something with the Access Grant-scoped client
});
* }
*/
public class AccessGrantClient {
private static final Logger LOGGER = LoggerFactory.getLogger(AccessGrantClient.class);
private static final String CONTEXT = "@context";
private static final String VC_CONTEXT_URI = "https://www.w3.org/2018/credentials/v1";
private static final String INRUPT_CONTEXT_URI = "https://schema.inrupt.com/credentials/v1.jsonld";
private static final String VERIFIABLE_CREDENTIAL = "verifiableCredential";
private static final String SOLID_VC_NAMESPACE = "http://www.w3.org/ns/solid/vc#";
private static final String SOLID_VC_QN = "vc:";
private static final String TYPE = "type";
private static final String APPLICATION_JSON = "application/json";
private static final String CONTENT_TYPE = "Content-Type";
private static final String CREDENTIAL_SUBJECT = "credentialSubject";
private static final String IS_PROVIDED_TO = "isProvidedTo";
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";
private static final String EXPIRATION_DATE = "expirationDate";
private static final String ISSUANCE_DATE = "issuanceDate";
private static final String CREDENTIAL = "credential";
private static final String SOLID_ACCESS_GRANT = "SolidAccessGrant";
private static final String SOLID_ACCESS_REQUEST = "SolidAccessRequest";
private static final String SOLID_ACCESS_DENIAL = "SolidAccessDenial";
private static final String VERIFIABLE_PRESENTATION = "VerifiablePresentation";
private static final String FIRST = "first";
private static final String LAST = "last";
private static final String PREV = "prev";
private static final String NEXT = "next";
private static final URI FQ_ACCESS_GRANT = URI.create(SOLID_VC_NAMESPACE + SOLID_ACCESS_GRANT);
private static final URI FQ_ACCESS_REQUEST = URI.create(SOLID_VC_NAMESPACE + SOLID_ACCESS_REQUEST);
private static final URI FQ_ACCESS_DENIAL = URI.create(SOLID_VC_NAMESPACE + SOLID_ACCESS_DENIAL);
private static final URI QN_ACCESS_GRANT = URI.create(SOLID_VC_QN + SOLID_ACCESS_GRANT);
private static final URI QN_ACCESS_REQUEST = URI.create(SOLID_VC_QN + SOLID_ACCESS_REQUEST);
private static final URI QN_ACCESS_DENIAL = URI.create(SOLID_VC_QN + SOLID_ACCESS_DENIAL);
private static final Set ACCESS_GRANT_TYPES = getAccessGrantTypes();
private static final Set ACCESS_REQUEST_TYPES = getAccessRequestTypes();
private static final Set ACCESS_DENIAL_TYPES = getAccessDenialTypes();
private static final Type JSON_TYPE_REF = new HashMap(){}.getClass().getGenericSuperclass();
private static final Set LINK_REL_VALUES = getLinkPagingRelValues();
private final Client client;
private final ClientCache metadataCache;
private final JsonService jsonService;
private final AccessGrantConfiguration config;
/**
* Create an access grant client.
*
* @param issuer the issuer
*/
public AccessGrantClient(final URI issuer) {
this(ClientProvider.getClient(), issuer);
}
/**
* Create an access grant client.
*
* @param client the client
* @param issuer the issuer
*/
public AccessGrantClient(final Client client, final URI issuer) {
this(client, issuer, ServiceProvider.getCacheBuilder().build(100, Duration.ofMinutes(60)));
}
/**
* Create an access grant client.
*
* @param client the client
* @param issuer the issuer
* @param metadataCache the metadata cache
*/
public AccessGrantClient(final Client client, final URI issuer, final ClientCache metadataCache) {
this(client, metadataCache, new AccessGrantConfiguration(issuer));
}
/**
* Create an access grant client.
*
* @param client the client
* @param metadataCache the metadata cache
* @param config the access grant configuration
*/
// This ctor may be made public at a later point
private AccessGrantClient(final Client client, final ClientCache metadataCache,
final AccessGrantConfiguration config) {
this.client = Objects.requireNonNull(client, "client may not be null!");
this.config = Objects.requireNonNull(config, "config may not be null!");
this.metadataCache = Objects.requireNonNull(metadataCache, "metadataCache may not be null!");
this.jsonService = ServiceProvider.getJsonService();
LOGGER.debug("Initializing Access Grant client with issuer: {}", config.getIssuer());
}
/**
* Scope an access grant client to a particular session.
*
* @param session the session
* @return the scoped access grant client
*/
public AccessGrantClient session(final Session session) {
Objects.requireNonNull(session, "Session may not be null!");
return new AccessGrantClient(client.session(session), metadataCache, config);
}
/**
* Issue an access request.
*
* @param request the parameters for the access request
* @return the next stage of completion containing the resulting access request
*/
public CompletionStage requestAccess(final AccessRequest.RequestParameters request) {
return requestAccess(request.getRecipient(), request.getResources(),
request.getModes(), request.getPurposes(), request.getExpiration(), request.getIssuedAt());
}
/**
* Issue an access request.
*
* @param recipient the agent controlling access to the resources
* @param resources the resources to which this credential applies
* @param modes the access modes for this credential
* @param purposes the purposes of this credential
* @param expiration the expiration time of this credential
* @return the next stage of completion containing the resulting access request
*/
public CompletionStage requestAccess(final URI recipient, final Set resources,
final Set modes, final Set purposes, final Instant expiration) {
return requestAccess(recipient, resources, modes, purposes, expiration, null);
}
private CompletionStage requestAccess(final URI recipient, final Set resources,
final Set modes, final Set purposes, final Instant expiration, final Instant issuance) {
Objects.requireNonNull(resources, "Resources may not be null!");
Objects.requireNonNull(modes, "Access modes may not be null!");
return v1Metadata().thenCompose(metadata -> {
final Map data = buildAccessRequestv1(recipient, resources, modes, purposes, expiration,
issuance);
final Request req = Request.newBuilder(metadata.issueEndpoint)
.header(CONTENT_TYPE, APPLICATION_JSON)
.POST(Request.BodyPublishers.ofByteArray(serialize(data))).build();
return client.send(req, Response.BodyHandlers.ofInputStream())
.thenApply(res -> {
try (final InputStream input = res.body()) {
final int status = res.statusCode();
if (isSuccess(status)) {
return processVerifiableCredential(input, ACCESS_REQUEST_TYPES, AccessRequest.class);
}
throw new AccessGrantException("Unable to issue Access Request: HTTP error " + status,
status);
} catch (final IOException ex) {
throw new AccessGrantException(
"Unexpected I/O exception while processing Access Request", ex);
}
});
});
}
/**
* Issue an access grant based on an access request.
*
* @param request the access request
* @return the next stage of completion containing the issued access grant
*/
public CompletionStage grantAccess(final AccessRequest request) {
Objects.requireNonNull(request, "Request may not be null!");
return v1Metadata().thenCompose(metadata -> {
final Map data = buildAccessGrantv1(request.getCreator(), request.getResources(),
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();
return client.send(req, Response.BodyHandlers.ofInputStream())
.thenApply(res -> {
try (final InputStream input = res.body()) {
final int status = res.statusCode();
if (isSuccess(status)) {
return processVerifiableCredential(input, ACCESS_GRANT_TYPES, AccessGrant.class);
}
throw new AccessGrantException("Unable to issue Access Grant: HTTP error " + status,
status);
} catch (final IOException ex) {
throw new AccessGrantException(
"Unexpected I/O exception while processing Access Grant", ex);
}
});
});
}
/**
* Issue an access denial receipt based on an access request.
*
* @param request the access request
* @return the next stage of completion containing the issued access denial
*/
public CompletionStage denyAccess(final AccessRequest request) {
Objects.requireNonNull(request, "Request may not be null!");
return v1Metadata().thenCompose(metadata -> {
final Map data = buildAccessDenialv1(request.getCreator(), request.getResources(),
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();
return client.send(req, Response.BodyHandlers.ofInputStream())
.thenApply(res -> {
try (final InputStream input = res.body()) {
final int status = res.statusCode();
if (isSuccess(status)) {
return processVerifiableCredential(input, ACCESS_DENIAL_TYPES, AccessDenial.class);
}
throw new AccessGrantException("Unable to issue Access Denial: HTTP error " + status,
status);
} catch (final IOException ex) {
throw new AccessGrantException(
"Unexpected I/O exception while processing Access Denial", ex);
}
});
});
}
/**
* Verify an access grant or request.
*
* @param credential the credential to verify
* @return the next stage of completion containing the verification result in the form of a
* {@link AccessCredentialVerification}
*/
public CompletionStage verify(final AccessCredential credential) {
return v1Metadata().thenCompose(metadata -> {
final Map presentation = new HashMap<>();
try (final InputStream is = new ByteArrayInputStream(credential.serialize().getBytes(UTF_8))) {
final Map data = jsonService.fromJson(is, JSON_TYPE_REF);
Utils.getCredentialsFromPresentation(data, credential.getTypes()).stream().findFirst()
.ifPresent(c -> presentation.put(VERIFIABLE_CREDENTIAL, c));
} catch (final IOException ex) {
throw new AccessGrantException("Unable to serialize credential", ex);
}
final Request req = Request.newBuilder(metadata.verifyEndpoint)
.header(CONTENT_TYPE, APPLICATION_JSON)
.POST(Request.BodyPublishers.ofByteArray(serialize(presentation))).build();
return client.send(req, Response.BodyHandlers.ofInputStream())
.thenApply(res -> {
try (final InputStream input = res.body()) {
final int status = res.statusCode();
if (isSuccess(status)) {
return jsonService.fromJson(input,
AccessCredentialVerification.class);
}
throw new AccessGrantException("Unable to perform Access Grant verify: HTTP error " + status,
status);
} catch (final IOException ex) {
throw new AccessGrantException(
"Unexpected I/O exception while verifying Access Grant", ex);
}
});
});
}
/**
* Perform an Access Credentials query and return a page of access credentials.
*
* @param the credential type
* @param filter the query filter
* @return the page of query results
*/
public CompletionStage> query(final CredentialFilter filter) {
final Class clazz = Objects.requireNonNull(filter, "filter may not be null!").getCredentialType();
final Set supportedTypes;
if (AccessGrant.class.isAssignableFrom(clazz)) {
supportedTypes = ACCESS_GRANT_TYPES;
} else if (AccessRequest.class.isAssignableFrom(clazz)) {
supportedTypes = ACCESS_REQUEST_TYPES;
} else if (AccessDenial.class.isAssignableFrom(clazz)) {
supportedTypes = ACCESS_DENIAL_TYPES;
} else {
throw new AccessGrantException("Unsupported type " + clazz + " in query request");
}
return v1Metadata().thenCompose(metadata -> {
if (metadata.queryEndpoint == null) {
throw new AccessGrantException("Server does not support CredentialFilter-based queries");
}
final Request req = Request.newBuilder(filter.asURI(metadata.queryEndpoint)).GET().build();
return client.send(req, Response.BodyHandlers.ofInputStream()).thenApply(response -> {
try (final InputStream input = response.body()) {
if (isSuccess(response.statusCode())) {
final Map> links = processFilterResponseHeaders(response.headers(),
filter);
final List items = processFilterResponseBody(input, supportedTypes, clazz);
return new CredentialResult<>(items, links.get(FIRST), links.get(PREV),
links.get(NEXT), links.get(LAST));
} else {
throw new AccessGrantException("Error querying access grant: HTTP response " +
response.statusCode());
}
} catch (final IOException ex) {
throw new AccessGrantException(
"Unexpected I/O exception while processing Access Grant query", ex);
}
});
});
}
Map> processFilterResponseHeaders(final Headers headers,
final CredentialFilter filter) {
final Map> links = new HashMap<>();
final List linkHeaders = headers.allValues("Link");
if (!linkHeaders.isEmpty()) {
Headers.Link.parse(linkHeaders.toArray(new String[0]))
.forEach(link -> {
final String rel = link.getParameter("rel");
final URI uri = link.getUri();
if (LINK_REL_VALUES.contains(rel) && uri != null) {
final String page = Utils.getQueryParam(uri, "page");
links.put(rel, CredentialFilter.newBuilder(filter).page(page)
.build(filter.getCredentialType()));
}
});
}
return links;
}
List processFilterResponseBody(final InputStream input,
final Set validTypes, final Class clazz) throws IOException {
final List page = new ArrayList<>();
final Map data = jsonService.fromJson(input, JSON_TYPE_REF);
Utils.asList(data.get("items")).ifPresent(items -> {
for (final Object item : items) {
Utils.asMap(item).ifPresent(credential ->
Utils.asSet(credential.get(TYPE)).ifPresent(types -> {
types.retainAll(validTypes);
if (!types.isEmpty()) {
final Map presentation = new HashMap<>();
presentation.put(CONTEXT, Arrays.asList(VC_CONTEXT_URI));
presentation.put(TYPE, Arrays.asList(VERIFIABLE_PRESENTATION));
presentation.put(VERIFIABLE_CREDENTIAL, Arrays.asList(credential));
final T c = cast(presentation, clazz);
if (c != null) {
page.add(c);
}
}
}));
}
});
return page;
}
@SuppressWarnings("unchecked")
T cast(final Map data, final Class clazz) {
if (AccessGrant.class.isAssignableFrom(clazz)) {
return (T) AccessGrant.of(new String(serialize(data), UTF_8));
} else if (AccessRequest.class.isAssignableFrom(clazz)) {
return (T) AccessRequest.of(new String(serialize(data), UTF_8));
} else if (AccessDenial.class.isAssignableFrom(clazz)) {
return (T) AccessDenial.of(new String(serialize(data), UTF_8));
}
return null;
}
/**
* Perform an Access Credentials query and returns 0 to N matching access credentials.
*
* @param the AccessCredential type
* @param resource the resource identifier, may be {@code null}
* @param creator the identifier for the agent who created the credential, may be {@code null}
* @param recipient the identifier for the agent who is the recipient for the credential, may be {@code null}
* @param purpose the access purpose, may be {@code null}
* @param mode the access mode, may be {@code null}
* @param clazz the AccessCredential type, either {@link AccessGrant} or {@link AccessRequest}
* @return the next stage of completion, including the matched Access Credentials
* @deprecated As of 1.3, replaced by {@link #query(CredentialFilter)}
*/
@Deprecated
public CompletionStage> query(final URI resource, final URI creator,
final URI recipient, final URI purpose, final String mode, final Class clazz) {
final Set modes = mode != null ? Collections.singleton(mode) : Collections.emptySet();
final Set purposes = purpose != null ? Collections.singleton(purpose) : Collections.emptySet();
return query(resource, creator, recipient, purposes, modes, clazz);
}
/**
* Perform an Access Credentials query and returns 0 to N matching access credentials.
*
* @param the AccessCredential type
* @param query the access credential query, never {@code null}
* @return the next stage of completion, including the matched Access Credentials
* @deprecated As of 1.3, replaced by {@link #query(CredentialFilter)}
*/
@Deprecated
public CompletionStage> query(final AccessCredentialQuery query) {
Objects.requireNonNull(query, "The query may not be null!");
return query(query.getResource(), query.getCreator(), query.getRecipient(), query.getPurposes(),
query.getModes(), query.getAccessCredentialType());
}
private CompletionStage> query(final URI resource, final URI creator,
final URI recipient, final Set purposes, final Set modes, final Class clazz) {
Objects.requireNonNull(clazz, "The clazz parameter must not be null!");
final URI type;
final Set supportedTypes;
if (AccessGrant.class.isAssignableFrom(clazz)) {
type = URI.create(SOLID_ACCESS_GRANT);
supportedTypes = ACCESS_GRANT_TYPES;
} else if (AccessRequest.class.isAssignableFrom(clazz)) {
type = URI.create(SOLID_ACCESS_REQUEST);
supportedTypes = ACCESS_REQUEST_TYPES;
} else if (AccessDenial.class.isAssignableFrom(clazz)) {
type = URI.create(SOLID_ACCESS_DENIAL);
supportedTypes = ACCESS_DENIAL_TYPES;
} else {
throw new AccessGrantException("Unsupported type " + clazz + " in query request");
}
return v1Metadata().thenApply(metadata -> {
if (metadata.deriveEndpoint == null) {
throw new AccessGrantException("Server does not support queries via AccessCredentialQuery objects");
}
final List responses = new ArrayList<>();
for (final Map data :
buildQuery(config.getIssuer(), type, resource, creator, recipient, purposes, modes)) {
final Request req = Request.newBuilder(metadata.deriveEndpoint)
.header(CONTENT_TYPE, APPLICATION_JSON)
.POST(Request.BodyPublishers.ofByteArray(serialize(data))).build();
final Response response = client.send(req, Response.BodyHandlers.ofInputStream())
.toCompletableFuture().join();
try (final InputStream input = response.body()) {
final int status = response.statusCode();
if (isSuccess(status)) {
responses.addAll(processQueryResponse(input, supportedTypes, clazz));
}
} catch (final IOException ex) {
throw new AccessGrantException(
"Unexpected I/O exception while processing Access Grant query", ex);
}
}
return responses;
});
}
/**
* Revoke an access credential.
*
* @param credential the access credential
* @return the next stage of completion
*/
public CompletionStage revoke(final AccessCredential credential) {
return v1Metadata().thenCompose(metadata -> {
final Status status = credential.getStatus().orElseThrow(() ->
new AccessGrantException("Unable to revoke Access Credential: no credentialStatus data"));
final Map credentialStatus = new HashMap<>();
credentialStatus.put(TYPE, status.getType());
credentialStatus.put("status", "1");
final Map data = new HashMap<>();
data.put("credentialId", credential.getIdentifier());
data.put("credentialStatus", Arrays.asList(credentialStatus));
final Request req = Request.newBuilder(metadata.statusEndpoint)
.header(CONTENT_TYPE, APPLICATION_JSON)
.POST(Request.BodyPublishers.ofByteArray(serialize(data)))
.build();
return client.send(req, Response.BodyHandlers.discarding())
.thenAccept(res -> {
final int code = res.statusCode();
if (!isSuccess(code)) {
throw new AccessGrantException("Unable to revoke Access Grant: " +
credential.getIdentifier(), code);
}
});
});
}
/**
* Fetch an access credential by identifier. Return at most one access credential or throws an exception.
*
* @param the credential type
* @param identifier the access credential identifier
* @param clazz the credential type, such as {@link AccessGrant} or {@link AccessRequest}
* @return the next stage of completion, containing the access credential
*/
public CompletionStage fetch(final URI identifier, final Class clazz) {
final Request req = Request.newBuilder(identifier)
.header("Accept", "application/ld+json,application/json").build();
return client.send(req, Response.BodyHandlers.ofInputStream())
.thenApply(res -> {
try (final InputStream input = res.body()) {
final int httpStatus = res.statusCode();
if (isSuccess(httpStatus)) {
if (AccessGrant.class.isAssignableFrom(clazz)) {
return (T) processVerifiableCredential(input, ACCESS_GRANT_TYPES, clazz);
} else if (AccessRequest.class.isAssignableFrom(clazz)) {
return (T) processVerifiableCredential(input, ACCESS_REQUEST_TYPES, clazz);
} else if (AccessDenial.class.isAssignableFrom(clazz)) {
return (T) processVerifiableCredential(input, ACCESS_DENIAL_TYPES, clazz);
}
throw new AccessGrantException("Unable to fetch credential as " + clazz);
}
throw new AccessGrantException(
"Unable to fetch the Access Grant: HTTP Error " + httpStatus, httpStatus);
} catch (final IOException ex) {
throw new AccessGrantException(
"Unexpected I/O exception while fetching the Access Grant metadata resource.", ex);
}
});
}
T processVerifiableCredential(final InputStream input, final Set validTypes,
final Class clazz) throws IOException {
final Map data = jsonService.fromJson(input, JSON_TYPE_REF);
final Set types = Utils.asSet(data.get(TYPE)).orElseThrow(() ->
new AccessGrantException("Invalid Access Grant: no 'type' field"));
types.retainAll(validTypes);
if (!types.isEmpty()) {
final Map presentation = new HashMap<>();
presentation.put(CONTEXT, Arrays.asList(VC_CONTEXT_URI));
presentation.put(TYPE, Arrays.asList(VERIFIABLE_PRESENTATION));
presentation.put(VERIFIABLE_CREDENTIAL, Arrays.asList(data));
final T credential = cast(presentation, clazz);
if (credential != null) {
return credential;
}
}
throw new AccessGrantException("Invalid Access Grant: missing supported type");
}
List processQueryResponse(final InputStream input, final Set validTypes,
final Class clazz) throws IOException {
final Map data = jsonService.fromJson(input, JSON_TYPE_REF);
final List grants = new ArrayList<>();
for (final Object item : getCredentials(data)) {
Utils.asMap(item).ifPresent(credential ->
Utils.asSet(credential.get(TYPE)).ifPresent(types -> {
types.retainAll(validTypes);
if (!types.isEmpty()) {
final Map presentation = new HashMap<>();
presentation.put(CONTEXT, Arrays.asList(VC_CONTEXT_URI));
presentation.put(TYPE, Arrays.asList(VERIFIABLE_PRESENTATION));
presentation.put(VERIFIABLE_CREDENTIAL, Arrays.asList(credential));
final T c = cast(presentation, clazz);
if (c != null) {
grants.add(c);
}
}
}));
}
return grants;
}
CompletionStage v1Metadata() {
final URI uri = URIBuilder.newBuilder(config.getIssuer()).path(".well-known/vc-configuration").build();
final Metadata cached = metadataCache.get(uri);
if (cached != null) {
return CompletableFuture.completedFuture(cached);
}
final Request req = Request.newBuilder(uri).header("Accept", APPLICATION_JSON).build();
return client.send(req, Response.BodyHandlers.ofInputStream())
.thenApply(res -> {
try (final InputStream input = res.body()) {
final int httpStatus = res.statusCode();
if (isSuccess(httpStatus)) {
final Map data = jsonService.fromJson(input, JSON_TYPE_REF);
return data;
}
throw new AccessGrantException(
"Unable to fetch the Access Grant metadata: HTTP Error " + httpStatus, httpStatus);
} catch (final IOException ex) {
throw new AccessGrantException(
"Unexpected I/O exception while fetching the Access Grant metadata resource.", ex);
}
})
.thenApply(metadata -> {
final Metadata m = new Metadata();
m.deriveEndpoint = asUri(metadata.get("derivationService"));
m.queryEndpoint = asUri(metadata.get("queryService"));
m.issueEndpoint = asUri(metadata.get("issuerService"));
m.verifyEndpoint = asUri(metadata.get("verifierService"));
m.statusEndpoint = asUri(metadata.get("statusService"));
metadataCache.put(uri, m);
return m;
});
}
byte[] serialize(final Map data) {
try (final ByteArrayOutputStream output = new ByteArrayOutputStream()) {
jsonService.toJson(data, output);
return output.toByteArray();
} catch (final IOException ex) {
throw new UncheckedIOException("Unable to serialize data as JSON", ex);
}
}
static Collection
© 2015 - 2024 Weber Informatics LLC | Privacy Policy