Skip to content

Commit 09aede5

Browse files
Merge pull request #6 from SolidLabResearch/feat/dynamic-ODRL
Feat/dynamic odrl
2 parents e3ae8d0 + f381f41 commit 09aede5

File tree

13 files changed

+518
-44
lines changed

13 files changed

+518
-44
lines changed

demo/dynamic-policy.ts

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import { Parser, Writer } from 'n3';
2+
import { materializePolicy, ODRLEngineMultipleSteps, ODRLEvaluator, resourceToOptimisedTurtle } from "../dist/index";
3+
4+
5+
// Variant on test case 036: Read request from Alice to resource X returns into yes (temporal lt) (Alice Request Read X).
6+
// https://github.com/SolidLabResearch/ODRL-Test-Suite/blob/main/data/test_cases/testcase-036-alice-read-x.ttl
7+
const policy = `
8+
@prefix odrl: <http://www.w3.org/ns/odrl/2/> .
9+
@prefix ex: <http://example.org/> .
10+
@prefix dct: <http://purl.org/dc/terms/> .
11+
@prefix odrluc: <https://w3id.org/force/odrlproposed#> .
12+
13+
<urn:uuid:32127a3f-5296-4cc6-b9d6-ef6c647a721d> a odrl:Set ;
14+
odrl:uid <urn:uuid:32127a3f-5296-4cc6-b9d6-ef6c647a721d> ;
15+
dct:description "ALICE may READ resource X when it is before 'ex:updateValue' (see sotw)." ;
16+
dct:source <https://github.com/SolidLabResearch/ODRL-Test-Suite/> ;
17+
odrl:permission <urn:uuid:d6ab4a38-68fb-418e-8af5-e77649a2187a> .
18+
19+
<urn:uuid:d6ab4a38-68fb-418e-8af5-e77649a2187a> a odrl:Permission ;
20+
odrl:assignee ex:alice ;
21+
odrl:action odrl:read ;
22+
odrl:target ex:x ;
23+
odrl:constraint <urn:uuid:constraint:86526f9b-57c2-4c94-b079-9762fec562f1> .
24+
25+
<urn:uuid:constraint:86526f9b-57c2-4c94-b079-9762fec562f1> odrl:leftOperand odrl:dateTime ;
26+
odrl:operator odrl:lt ;
27+
odrl:rightOperandReference ex:operandReference1 .
28+
29+
ex:operandReference1 a odrluc:OperandReference ;
30+
odrluc:reference ex:externalSource ;
31+
odrluc:path ex:updatedValue .
32+
`
33+
34+
// state of the world -> with external value indicating the time
35+
const sotw = `
36+
@prefix ex: <http://example.org/> .
37+
@prefix temp: <http://example.com/request/> .
38+
@prefix dct: <http://purl.org/dc/terms/> .
39+
40+
<urn:uuid:192620fa-06d9-447b-adbd-bd1ece4f9b12> a ex:Sotw ;
41+
ex:includes temp:currentTime .
42+
43+
temp:currentTime dct:issued "2017-02-12T11:20:10.999Z"^^<http://www.w3.org/2001/XMLSchema#dateTime> .
44+
45+
# external value that will be materialized in the policy
46+
ex:externalSource ex:updatedValue "2018-02-12T11:20:10.999Z"^^<http://www.w3.org/2001/XMLSchema#dateTime> .
47+
`
48+
49+
const request = `
50+
@prefix odrl: <http://www.w3.org/ns/odrl/2/> .
51+
@prefix ex: <http://example.org/> .
52+
@prefix dct: <http://purl.org/dc/terms/> .
53+
54+
<urn:uuid:1bafee59-006c-46a3-810c-5d176b4be364> a odrl:Request ;
55+
odrl:uid <urn:uuid:1bafee59-006c-46a3-810c-5d176b4be364> ;
56+
dct:description "Requesting Party ALICE requests to READ resource X." ;
57+
odrl:permission <urn:uuid:186be541-5857-4ce3-9f03-1a274f16bf59> .
58+
59+
<urn:uuid:186be541-5857-4ce3-9f03-1a274f16bf59> a odrl:Permission ;
60+
odrl:assignee ex:alice ;
61+
odrl:action odrl:read ;
62+
odrl:target ex:x .
63+
`
64+
65+
async function main() {
66+
const parser = new Parser()
67+
const writer = new Writer()
68+
69+
const odrlDynamicPolicyQuads = parser.parse(policy)
70+
71+
const odrlRequestQuads = parser.parse(request)
72+
const stateOfTheWorldQuads = parser.parse(sotw)
73+
74+
const instantiatedPolicyQuads = materializePolicy(odrlDynamicPolicyQuads, stateOfTheWorldQuads)
75+
76+
console.log("Instantiated Policy:")
77+
console.log(writer.quadsToString(instantiatedPolicyQuads));
78+
79+
80+
// reasoning over dynamic policy
81+
const evaluator = new ODRLEvaluator(new ODRLEngineMultipleSteps());
82+
const reasoningResult = await evaluator.evaluate(
83+
odrlDynamicPolicyQuads,
84+
odrlRequestQuads,
85+
stateOfTheWorldQuads)
86+
87+
const output = writer.quadsToString(reasoningResult);
88+
console.log("Compliance Report")
89+
console.log(output);
90+
91+
const prefixes = {
92+
'odrl': 'http://www.w3.org/ns/odrl/2/',
93+
'ex': 'http://example.org/',
94+
'temp': 'http://example.com/request/',
95+
'dct': 'http://purl.org/dc/terms/',
96+
'xsd': 'http://www.w3.org/2001/XMLSchema#',
97+
'foaf': 'http://xmlns.com/foaf/0.1/',
98+
'report': 'https://w3id.org/force/compliance-report#'
99+
}
100+
101+
// created report with N3
102+
// @ts-ignore
103+
console.log(resourceToOptimisedTurtle(reasoningResult, prefixes));
104+
105+
}
106+
main()
107+

package-lock.json

Lines changed: 66 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"clean": "rm -rf ./dist",
1818
"demo:test-engine": "ts-node demo/test-n3-engine.ts",
1919
"demo:test-evaluator": "ts-node demo/test-n3-evaluator.ts",
20+
"demo:dynamic-policy": "ts-node demo/dynamic-policy.ts",
2021
"prepare": "npm run build",
2122
"release": "npm run build && npm publish --access public",
2223
"test": "jest"
@@ -39,8 +40,8 @@
3940
"@types/jest": "^29.5.12",
4041
"@types/tmp": "^0.2.6",
4142
"jest": "^29.7.0",
42-
"ts-jest": "^29.2.5",
4343
"jest-rdf": "^2.0.0",
44+
"ts-jest": "^29.2.5",
4445
"ts-node": "^10.9.2",
4546
"typescript": "^5.4.5"
4647
},
@@ -50,9 +51,10 @@
5051
"eyereasoner": "^16.18.4",
5152
"n3": "^1.20.4",
5253
"rdf-isomorphic": "^1.3.1",
54+
"rdf-lens": "^1.3.5",
5355
"rdf-parse": "^3.0.0",
5456
"rdf-store-stream": "^2.0.1",
55-
"rdf-vocabulary": "^1.0.0",
57+
"rdf-vocabulary": "^1.0.1",
5658
"streamify-string": "^1.0.1",
5759
"tmp": "^0.2.3",
5860
"uuidv4": "^6.2.13"

src/evaluator/DynamicConstraint.ts

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { Quad, Term } from "@rdfjs/types";
2+
import { Literal, NamedNode, Store } from 'n3';
3+
import { BasicLensM, CBDLens, Cont, pred, ShaclPath } from "rdf-lens";
4+
import { ODRL, ODRLUC, RDF } from "../util/Vocabularies";
5+
6+
// use RDF lens instead of clownface for SHACL Property Paths: https://ceur-ws.org/Vol-3759/paper13.pdf
7+
export const pathLens = pred(ODRLUC.terms.path)
8+
.one()
9+
.then(ShaclPath);
10+
11+
// Utility function to extract the SHACL property path of the Dynamic ODRL Policy using `odrluc:path`
12+
export function getPath(id: Term, quads: Quad[]): BasicLensM<Cont, Term> {
13+
return pathLens.execute({ id, quads }).mapAll((x) => x.id);
14+
}
15+
16+
// Utility function to extract the value from an external resource using a SHACL Property path
17+
export function usePath(id: Term, quads: Quad[], lens: BasicLensM<Cont, Term>): Term[] {
18+
return lens.execute({ id, quads })
19+
}
20+
21+
// implements dynamic policy constraint algorithm
22+
// algorithm to fetch value using SHACL path from sotw in policy
23+
// will only materialize when RightOperandReference class is present
24+
export function materializePolicy(dynamicPolicy: Quad[], stateOfTheWorld: Quad[]): Quad[] {
25+
const odrlPolicyStore = new Store(dynamicPolicy)
26+
const odrlDynamicPolicyStore = new Store(dynamicPolicy)
27+
28+
// get all constraints containing references
29+
const operandReferenceNodes = odrlDynamicPolicyStore.getSubjects(RDF.terms.type, ODRLUC.terms.OperandReference, null);
30+
31+
for (const operandReferenceNode of operandReferenceNodes) {
32+
// ODRL constraint that has that given rightOperandReference
33+
const constraintNodes = odrlDynamicPolicyStore.getSubjects(ODRL.terms.rightOperandReference, operandReferenceNode, null)
34+
35+
// external resource
36+
const externalResource = odrlDynamicPolicyStore.getObjects(operandReferenceNode, ODRLUC.terms.reference, null)[0]
37+
if (!externalResource) {
38+
throw Error("There is no source present in the Dynamic Policy Constraint.")
39+
}
40+
41+
// SHACL Property path
42+
const lens = getPath(operandReferenceNode, dynamicPolicy);
43+
44+
// extract SHACL Property Path using RDF-Lens
45+
const getValue = usePath(externalResource, stateOfTheWorld, lens);
46+
const instantiatedValue = getValue[0]; // according to the algorithm (see paper), there should be only one term
47+
48+
if (!(instantiatedValue instanceof NamedNode || instantiatedValue instanceof Literal)) {
49+
// Now instantiatedValue is a valid value
50+
throw Error("Instantiated value is not a proper value.");
51+
}
52+
for (const constraintNode of constraintNodes) {
53+
// remove rightOperandRefrence triple from constraint
54+
odrlPolicyStore.removeQuad(constraintNode, ODRL.terms.rightOperandReference, operandReferenceNode)
55+
// add materialized rightOperand
56+
odrlPolicyStore.addQuad(constraintNode, ODRL.terms.rightOperand, instantiatedValue)
57+
}
58+
59+
// remove OperandReference triples from Policy store
60+
const operandReferenceQuads = CBDLens.execute({id:operandReferenceNode, quads: dynamicPolicy})
61+
odrlPolicyStore.removeQuads(operandReferenceQuads)
62+
}
63+
return odrlPolicyStore.getQuads(null, null, null, null)
64+
}
65+
66+

src/evaluator/Evaluate.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { Quad } from '@rdfjs/types';
22
import { ODRLEngine, Engine} from './Engine';
33
import { RDFValidator, TripleTermValidator, SHACLValidator } from './Validate';
4+
import { materializePolicy } from './DynamicConstraint';
45

56
export interface Evaluator {
67
/**
@@ -64,9 +65,11 @@ export class ODRLEvaluator implements Evaluator {
6465

6566
// if there are compact policies -> they must be expanded (also reocmmended by ODRL § 2.7.1 Compact Policy)
6667

68+
// handle dynamic policies
69+
const instantiatedPolicies = materializePolicy(policy, state);
6770
// evaluate
6871
// the evaluation will result into a conformance report
69-
const evaluation = await this.engine.evaluate([...policy, ...request, ...state])
72+
const evaluation = await this.engine.evaluate([...instantiatedPolicies, ...request, ...state])
7073

7174
// TODO: think about when the report can be empty
7275
// does it always mean there is not enough information?

src/index.browser.ts

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1 @@
1-
export * from './evaluator/Engine'
2-
export * from './evaluator/Evaluate'
3-
export * from './evaluator/Validate'
4-
5-
export * from './reasoner/EyeJsReasoner'
6-
// export * from './reasoner/EyeReasoner'
7-
export * from './reasoner/Reasoner'
8-
9-
export * from './util/Notation3Util'
10-
// export * from './util/RDFUtil'
11-
export * from './rules/Rules'
1+
export * from "./index.core"

src/index.core.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
export * from './evaluator/Engine'
2+
export * from './evaluator/Evaluate'
3+
export * from './evaluator/Validate'
4+
export * from './evaluator/DynamicConstraint'
5+
6+
export * from './reasoner/EyeJsReasoner'
7+
export * from './reasoner/Reasoner'
8+
9+
export * from './util/Notation3Util'
10+
export * from './util/Vocabularies'
11+
12+
export * from './rules/Rules'

src/index.ts

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,4 @@
1-
export * from './evaluator/Engine'
2-
export * from './evaluator/Evaluate'
3-
export * from './evaluator/Validate'
4-
5-
export * from './reasoner/EyeJsReasoner'
1+
export * from "./index.core"
62
export * from './reasoner/EyeReasoner'
7-
export * from './reasoner/Reasoner'
83

9-
export * from './util/Notation3Util'
104
export * from './util/RDFUtil'

src/reasoner/EyeJsReasoner.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { n3reasoner } from "eyereasoner/dist";
1+
import { n3reasoner } from "eyereasoner";
22
import { Reasoner } from "./Reasoner";
33

44
export class EyeJsReasoner extends Reasoner {

0 commit comments

Comments
 (0)