|
21 | 21 | package com.inrupt.client.acp; |
22 | 22 |
|
23 | 23 | import com.inrupt.client.RDFSource; |
| 24 | +import com.inrupt.client.solid.SolidClient; |
| 25 | +import com.inrupt.client.solid.SolidSyncClient; |
24 | 26 | import com.inrupt.client.vocabulary.ACP; |
25 | 27 | import com.inrupt.client.vocabulary.RDF; |
26 | 28 | import com.inrupt.rdf.wrapping.commons.ValueMappings; |
|
38 | 40 | import org.apache.commons.rdf.api.IRI; |
39 | 41 | import org.apache.commons.rdf.api.Quad; |
40 | 42 | import org.apache.commons.rdf.api.RDFTerm; |
| 43 | +import org.slf4j.Logger; |
| 44 | +import org.slf4j.LoggerFactory; |
41 | 45 |
|
42 | 46 | /** |
43 | 47 | * An Access Control Resource type. |
44 | 48 | */ |
45 | 49 | public class AccessControlResource extends RDFSource { |
46 | 50 |
|
| 51 | + private static final Logger LOGGER = LoggerFactory.getLogger(AccessControlResource.class); |
| 52 | + |
47 | 53 | public static final URI SOLID_ACCESS_GRANT = URI.create("http://www.w3.org/ns/solid/vc#SolidAccessGrant"); |
48 | 54 |
|
49 | 55 | /** |
@@ -79,6 +85,104 @@ public Set<AccessControl> memberAccessControl() { |
79 | 85 | return new ACPNode(asIRI(getIdentifier()), getGraph()).memberAccessControl(); |
80 | 86 | } |
81 | 87 |
|
| 88 | + /** |
| 89 | + * Expand the internal data using a synchronous client. |
| 90 | + * |
| 91 | + * @param client the solid client |
| 92 | + * @return an expanded access control resource |
| 93 | + */ |
| 94 | + public AccessControlResource expand(final SolidSyncClient client) { |
| 95 | + // Copy the data from the existing ACR into a new dataset |
| 96 | + final var dataset = rdf.createDataset(); |
| 97 | + stream().forEach(dataset::add); |
| 98 | + |
| 99 | + final var cache = rdf.createDataset(); |
| 100 | + expandType(dataset, cache, asIRI(ACP.accessControl), asIRI(ACP.AccessControl), |
| 101 | + uri -> populateCache(client, uri, cache)); |
| 102 | + expandType(dataset, cache, asIRI(ACP.memberAccessControl), asIRI(ACP.AccessControl), |
| 103 | + uri -> populateCache(client, uri, cache)); |
| 104 | + expandType(dataset, cache, asIRI(ACP.apply), asIRI(ACP.Policy), uri -> populateCache(client, uri, cache)); |
| 105 | + expandType(dataset, cache, asIRI(ACP.allOf), asIRI(ACP.Matcher), uri -> populateCache(client, uri, cache)); |
| 106 | + expandType(dataset, cache, asIRI(ACP.anyOf), asIRI(ACP.Matcher), uri -> populateCache(client, uri, cache)); |
| 107 | + expandType(dataset, cache, asIRI(ACP.noneOf), asIRI(ACP.Matcher), uri -> populateCache(client, uri, cache)); |
| 108 | + |
| 109 | + return new AccessControlResource(getIdentifier(), dataset); |
| 110 | + } |
| 111 | + |
| 112 | + /** |
| 113 | + * Expand the internal data using an asynchronous client. |
| 114 | + * |
| 115 | + * @param client the solid client |
| 116 | + * @return an expanded access control resource |
| 117 | + */ |
| 118 | + public AccessControlResource expand(final SolidClient client) { |
| 119 | + // Copy the data from the existing ACR into a new dataset |
| 120 | + final var dataset = rdf.createDataset(); |
| 121 | + stream().forEach(dataset::add); |
| 122 | + |
| 123 | + final var cache = rdf.createDataset(); |
| 124 | + expandType(dataset, cache, asIRI(ACP.accessControl), asIRI(ACP.AccessControl), |
| 125 | + uri -> populateCache(client, uri, cache)); |
| 126 | + expandType(dataset, cache, asIRI(ACP.memberAccessControl), asIRI(ACP.AccessControl), |
| 127 | + uri -> populateCache(client, uri, cache)); |
| 128 | + expandType(dataset, cache, asIRI(ACP.apply), asIRI(ACP.Policy), uri -> populateCache(client, uri, cache)); |
| 129 | + expandType(dataset, cache, asIRI(ACP.allOf), asIRI(ACP.Matcher), uri -> populateCache(client, uri, cache)); |
| 130 | + expandType(dataset, cache, asIRI(ACP.anyOf), asIRI(ACP.Matcher), uri -> populateCache(client, uri, cache)); |
| 131 | + expandType(dataset, cache, asIRI(ACP.noneOf), asIRI(ACP.Matcher), uri -> populateCache(client, uri, cache)); |
| 132 | + |
| 133 | + return new AccessControlResource(getIdentifier(), dataset); |
| 134 | + } |
| 135 | + |
| 136 | + void expandType(final Dataset dataset, final Dataset cache, final IRI predicate, final IRI type, |
| 137 | + final Consumer<URI> handler) { |
| 138 | + final var subjects = dataset.stream(null, null, predicate, null) |
| 139 | + .map(Quad::getObject).filter(IRI.class::isInstance).map(IRI.class::cast) |
| 140 | + .filter(subject -> !dataset.contains(null, subject, asIRI(RDF.type), type)).toList(); |
| 141 | + |
| 142 | + for (final var subject : subjects) { |
| 143 | + if (!cache.contains(null, subject, asIRI(RDF.type), type)) { |
| 144 | + handler.accept(URI.create(subject.getIRIString())); |
| 145 | + } |
| 146 | + cache.stream(null, subject, null, null).forEach(dataset::add); |
| 147 | + } |
| 148 | + } |
| 149 | + |
| 150 | + void populateCache(final SolidSyncClient client, final URI uri, final Dataset cache) { |
| 151 | + try (final var acr = client.read(uri, AccessControlResource.class)) { |
| 152 | + acr.stream() |
| 153 | + .filter(quad -> !isAccessControlResourceType(quad)) |
| 154 | + .forEach(cache::add); |
| 155 | + } catch (final Exception ex) { |
| 156 | + LOGGER.atDebug() |
| 157 | + .setMessage("Unable to fetch access control resource from {}: {}") |
| 158 | + .addArgument(uri) |
| 159 | + .addArgument(ex::getMessage) |
| 160 | + .log(); |
| 161 | + } |
| 162 | + } |
| 163 | + |
| 164 | + void populateCache(final SolidClient client, final URI uri, final Dataset cache) { |
| 165 | + client.read(uri, AccessControlResource.class).thenAccept(res -> { |
| 166 | + try (final var acr = res) { |
| 167 | + acr.stream() |
| 168 | + .filter(quad -> !isAccessControlResourceType(quad)) |
| 169 | + .forEach(cache::add); |
| 170 | + } |
| 171 | + }) |
| 172 | + .exceptionally(err -> { |
| 173 | + LOGGER.atDebug() |
| 174 | + .setMessage("Unable to fetch access control resource from {}: {}") |
| 175 | + .addArgument(uri) |
| 176 | + .addArgument(err::getMessage) |
| 177 | + .log(); |
| 178 | + return null; |
| 179 | + }).toCompletableFuture().join(); |
| 180 | + } |
| 181 | + |
| 182 | + static boolean isAccessControlResourceType(final Quad quad) { |
| 183 | + return asIRI(RDF.type).equals(quad.getPredicate()) && asIRI(ACP.AccessControlResource).equals(quad.getObject()); |
| 184 | + } |
| 185 | + |
82 | 186 | /** |
83 | 187 | * Compact the internal data. |
84 | 188 | */ |
@@ -135,7 +239,7 @@ public enum MatcherType { |
135 | 239 | } |
136 | 240 |
|
137 | 241 | public IRI asIRI() { |
138 | | - return AccessControlResource.asIRI(predicate); |
| 242 | + return AccessControlResource.asIRI(asURI()); |
139 | 243 | } |
140 | 244 |
|
141 | 245 | public URI asURI() { |
|
0 commit comments