4545
4646/**
4747 * An Access Control Resource type.
48+ *
49+ * <p>This is the root type for a resource that expresses access control policies.
4850 */
4951public class AccessControlResource extends RDFSource {
5052
5153 private static final Logger LOGGER = LoggerFactory .getLogger (AccessControlResource .class );
5254
5355 public static final URI SOLID_ACCESS_GRANT = URI .create ("http://www.w3.org/ns/solid/vc#SolidAccessGrant" );
5456
57+ /**
58+ * Definitions for different matcher types, for use with {@link #find}.
59+ */
60+ public enum MatcherType {
61+ AGENT (ACP .agent ), CLIENT (ACP .client ), ISSUER (ACP .issuer ), VC (ACP .vc );
62+
63+ private final URI predicate ;
64+
65+ MatcherType (final URI predicate ) {
66+ this .predicate = predicate ;
67+ }
68+
69+ /**
70+ * Return the matcher type as an IRI.
71+ *
72+ * @return the IRI for this predicate
73+ */
74+ public IRI asIRI () {
75+ return AccessControlResource .asIRI (asURI ());
76+ }
77+
78+ /**
79+ * Return the matcher type as a URI.
80+ *
81+ * @return the URI for this predicate
82+ */
83+ public URI asURI () {
84+ return predicate ;
85+ }
86+ }
87+
5588 /**
5689 * Create a new Access Control Resource.
5790 *
@@ -94,17 +127,26 @@ public Set<AccessControl> memberAccessControl() {
94127 public AccessControlResource expand (final SolidSyncClient client ) {
95128 // Copy the data from the existing ACR into a new dataset
96129 final var dataset = rdf .createDataset ();
130+ final var matcherType = asIRI (ACP .Matcher );
131+ final var accessControlType = asIRI (ACP .AccessControl );
97132 stream ().forEach (dataset ::add );
98133
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 ));
134+ try (final var cache = rdf .createDataset ()) {
135+ expandType (dataset , cache , asIRI (ACP .accessControl ), accessControlType ,
136+ uri -> populateCache (client , uri , cache ));
137+ expandType (dataset , cache , asIRI (ACP .memberAccessControl ), accessControlType ,
138+ uri -> populateCache (client , uri , cache ));
139+ expandType (dataset , cache , asIRI (ACP .apply ), asIRI (ACP .Policy ),
140+ uri -> populateCache (client , uri , cache ));
141+ expandType (dataset , cache , asIRI (ACP .allOf ), matcherType ,
142+ uri -> populateCache (client , uri , cache ));
143+ expandType (dataset , cache , asIRI (ACP .anyOf ), matcherType ,
144+ uri -> populateCache (client , uri , cache ));
145+ expandType (dataset , cache , asIRI (ACP .noneOf ), matcherType ,
146+ uri -> populateCache (client , uri , cache ));
147+ } catch (final Exception ex ) {
148+ LOGGER .atDebug ().setMessage ("Unable to close dataset: {}" ).addArgument (ex ::getMessage ).log ();
149+ }
108150
109151 return new AccessControlResource (getIdentifier (), dataset );
110152 }
@@ -120,67 +162,26 @@ public AccessControlResource expand(final SolidClient client) {
120162 final var dataset = rdf .createDataset ();
121163 stream ().forEach (dataset ::add );
122164
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 );
165+ final var matcherType = asIRI (ACP .Matcher );
166+ final var accessControlType = asIRI (ACP .AccessControl );
167+ try (final var cache = rdf .createDataset ()) {
168+ expandType (dataset , cache , asIRI (ACP .accessControl ), accessControlType ,
169+ uri -> populateCacheAsync (client , uri , cache ));
170+ expandType (dataset , cache , asIRI (ACP .memberAccessControl ), accessControlType ,
171+ uri -> populateCacheAsync (client , uri , cache ));
172+ expandType (dataset , cache , asIRI (ACP .apply ), asIRI (ACP .Policy ),
173+ uri -> populateCacheAsync (client , uri , cache ));
174+ expandType (dataset , cache , asIRI (ACP .allOf ), matcherType ,
175+ uri -> populateCacheAsync (client , uri , cache ));
176+ expandType (dataset , cache , asIRI (ACP .anyOf ), matcherType ,
177+ uri -> populateCacheAsync (client , uri , cache ));
178+ expandType (dataset , cache , asIRI (ACP .noneOf ), matcherType ,
179+ uri -> populateCacheAsync (client , uri , cache ));
155180 } 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 ();
181+ LOGGER .atDebug ().setMessage ("Unable to close dataset: {}" ).addArgument (ex ::getMessage ).log ();
161182 }
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- }
181183
182- static boolean isAccessControlResourceType (final Quad quad ) {
183- return asIRI (RDF .type ).equals (quad .getPredicate ()) && asIRI (ACP .AccessControlResource ).equals (quad .getObject ());
184+ return new AccessControlResource (getIdentifier (), dataset );
184185 }
185186
186187 /**
@@ -204,14 +205,6 @@ public void compact() {
204205 }
205206 }
206207
207- <T extends BlankNodeOrIRI > void removeUnusedStatements (final T resource ) {
208- if (!contains (null , null , null , resource )) {
209- for (final var quad : stream (null , resource , null , null ).toList ()) {
210- remove (quad );
211- }
212- }
213- }
214-
215208 /**
216209 * Merge two or more policies into a single policies with combined matchers.
217210 *
@@ -229,24 +222,6 @@ public Policy merge(final Policy... policies) {
229222 return policy ;
230223 }
231224
232- public enum MatcherType {
233- AGENT (ACP .agent ), CLIENT (ACP .client ), ISSUER (ACP .issuer ), VC (ACP .vc );
234-
235- private final URI predicate ;
236-
237- MatcherType (final URI predicate ) {
238- this .predicate = predicate ;
239- }
240-
241- public IRI asIRI () {
242- return AccessControlResource .asIRI (asURI ());
243- }
244-
245- public URI asURI () {
246- return predicate ;
247- }
248- }
249-
250225 /**
251226 * Find a policy, given a type, value and set of modes.
252227 *
@@ -268,22 +243,6 @@ public Set<Policy> find(final MatcherType type, final URI value, final Set<URI>
268243 .collect (Collectors .toSet ());
269244 }
270245
271- static class ACPNode extends WrapperIRI {
272- public ACPNode (final RDFTerm original , final Graph graph ) {
273- super (original , graph );
274- }
275-
276- public Set <AccessControl > memberAccessControl () {
277- return objects (asIRI (ACP .memberAccessControl ),
278- AccessControl ::asResource , ValueMappings .as (AccessControl .class ));
279- }
280-
281- public Set <AccessControl > accessControl () {
282- return objects (asIRI (ACP .accessControl ),
283- AccessControl ::asResource , ValueMappings .as (AccessControl .class ));
284- }
285- }
286-
287246 /**
288247 * Add policies to the access control resource.
289248 *
@@ -382,6 +341,22 @@ public Policy issuerPolicy(final URI issuer, final URI... access) {
382341 return simplePolicy (matcher -> matcher .issuer ().add (issuer ), access );
383342 }
384343
344+ static class ACPNode extends WrapperIRI {
345+ public ACPNode (final RDFTerm original , final Graph graph ) {
346+ super (original , graph );
347+ }
348+
349+ public Set <AccessControl > memberAccessControl () {
350+ return objects (asIRI (ACP .memberAccessControl ),
351+ AccessControl ::asResource , ValueMappings .as (AccessControl .class ));
352+ }
353+
354+ public Set <AccessControl > accessControl () {
355+ return objects (asIRI (ACP .accessControl ),
356+ AccessControl ::asResource , ValueMappings .as (AccessControl .class ));
357+ }
358+ }
359+
385360 Policy simplePolicy (final Consumer <Matcher > handler , final URI ... access ) {
386361 final var baseUri = getIdentifier ().getScheme () + ":" + getIdentifier ().getSchemeSpecificPart ();
387362 final var matcher = new Matcher (asIRI (baseUri + "#" + UUID .randomUUID ()), getGraph ());
@@ -395,11 +370,69 @@ Policy simplePolicy(final Consumer<Matcher> handler, final URI... access) {
395370 return policy ;
396371 }
397372
398- private static IRI asIRI (final URI uri ) {
373+ <T extends BlankNodeOrIRI > void removeUnusedStatements (final T resource ) {
374+ if (!contains (null , null , null , resource )) {
375+ for (final var quad : stream (null , resource , null , null ).toList ()) {
376+ remove (quad );
377+ }
378+ }
379+ }
380+
381+ void expandType (final Dataset dataset , final Dataset cache , final IRI predicate , final IRI type ,
382+ final Consumer <URI > handler ) {
383+ final var subjects = dataset .stream (null , null , predicate , null )
384+ .map (Quad ::getObject ).filter (IRI .class ::isInstance ).map (IRI .class ::cast )
385+ .filter (subject -> !dataset .contains (null , subject , asIRI (RDF .type ), type )).toList ();
386+
387+ for (final var subject : subjects ) {
388+ if (!cache .contains (null , subject , asIRI (RDF .type ), type )) {
389+ handler .accept (URI .create (subject .getIRIString ()));
390+ }
391+ cache .stream (null , subject , null , null ).forEach (dataset ::add );
392+ }
393+ }
394+
395+ void populateCache (final SolidSyncClient client , final URI uri , final Dataset cache ) {
396+ try (final var acr = client .read (uri , AccessControlResource .class )) {
397+ acr .stream ()
398+ .filter (quad -> !isAccessControlResourceType (quad ))
399+ .forEach (cache ::add );
400+ } catch (final Exception ex ) {
401+ LOGGER .atDebug ()
402+ .setMessage ("Unable to fetch access control resource from {}: {}" )
403+ .addArgument (uri )
404+ .addArgument (ex ::getMessage )
405+ .log ();
406+ }
407+ }
408+
409+ void populateCacheAsync (final SolidClient client , final URI uri , final Dataset cache ) {
410+ client .read (uri , AccessControlResource .class ).thenAccept (res -> {
411+ try (final var acr = res ) {
412+ acr .stream ()
413+ .filter (quad -> !isAccessControlResourceType (quad ))
414+ .forEach (cache ::add );
415+ }
416+ })
417+ .exceptionally (err -> {
418+ LOGGER .atDebug ()
419+ .setMessage ("Unable to fetch access control resource from {}: {}" )
420+ .addArgument (uri )
421+ .addArgument (err ::getMessage )
422+ .log ();
423+ return null ;
424+ }).toCompletableFuture ().join ();
425+ }
426+
427+ static boolean isAccessControlResourceType (final Quad quad ) {
428+ return asIRI (RDF .type ).equals (quad .getPredicate ()) && asIRI (ACP .AccessControlResource ).equals (quad .getObject ());
429+ }
430+
431+ static IRI asIRI (final URI uri ) {
399432 return asIRI (uri .toString ());
400433 }
401434
402- private static IRI asIRI (final String uri ) {
435+ static IRI asIRI (final String uri ) {
403436 return rdf .createIRI (uri );
404437 }
405438}
0 commit comments