Skip to content

Commit 3136367

Browse files
chore: migrate UCP docs & tests (#23)
1 parent 3ace0e3 commit 3136367

28 files changed

+2128
-14
lines changed

packages/ucp/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
debug

packages/ucp/docs/crud_full.n3

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
@prefix odrl: <http://www.w3.org/ns/odrl/2/> .
2+
@prefix : <http://example.org/> .
3+
@prefix acl: <http://www.w3.org/ns/auth/acl#>.
4+
@prefix fno: <https://w3id.org/function/ontology#> .
5+
@prefix log: <http://www.w3.org/2000/10/swap/log#> .
6+
@prefix string: <http://www.w3.org/2000/10/swap/string#> .
7+
@prefix list: <http://www.w3.org/2000/10/swap/list#> .
8+
# Read ODRL rule
9+
{
10+
?permission a odrl:Permission;
11+
odrl:action ?action ;
12+
odrl:target ?targetResource ;
13+
odrl:assignee ?requestedParty;
14+
odrl:assigner ?resourceOwner .
15+
16+
?action list:in (odrl:use odrl:read) . # multiple options
17+
18+
?SCOPE log:notIncludes { ?permission odrl:constraint ?anything }. # No odrl:constraints may be present
19+
# context of a request
20+
?context
21+
:resourceOwner ?resourceOwner;
22+
:requestingParty ?requestedParty;
23+
:target ?targetResource;
24+
:requestPermission acl:Read.
25+
26+
:uuid5 log:uuid ?uuidStringdataUsagePolicyExecution.
27+
( "urn:uuid:" ?uuidStringdataUsagePolicyExecution) string:concatenation ?urnUuidStringdataUsagePolicyExecution.
28+
?dataUsagePolicyExecution log:uri ?urnUuidStringdataUsagePolicyExecution .
29+
} =>
30+
{
31+
?dataUsagePolicyExecution a fno:Execution;
32+
fno:executes <http://example.org/dataUsage> ;
33+
:accessModesAllowed acl:Read.
34+
}.
35+
36+
# Update ODRL Rule (odrl:modify: new asset is not created, not same as acl:write)
37+
{
38+
?permission a odrl:Permission;
39+
odrl:action ?action ;
40+
odrl:target ?targetResource ;
41+
odrl:assignee ?requestedParty;
42+
odrl:assigner ?resourceOwner .
43+
44+
?action list:in (odrl:use odrl:modify). # multiple options
45+
46+
?SCOPE log:notIncludes { ?permission odrl:constraint ?anything }. # No odrl:constraints may be present
47+
48+
# context of a request
49+
?context
50+
:resourceOwner ?resourceOwner;
51+
:requestingParty ?requestedParty;
52+
:target ?targetResource;
53+
:requestPermission acl:Write.
54+
55+
:uuid5 log:uuid ?uuidStringdataUsagePolicyExecution.
56+
( "urn:uuid:" ?uuidStringdataUsagePolicyExecution) string:concatenation ?urnUuidStringdataUsagePolicyExecution.
57+
?dataUsagePolicyExecution log:uri ?urnUuidStringdataUsagePolicyExecution .
58+
} =>
59+
{
60+
?dataUsagePolicyExecution a fno:Execution;
61+
fno:executes <http://example.org/dataUsage> ;
62+
:accessModesAllowed acl:Write.
63+
}.
64+
<http://example.org/1705937573496#usagePolicy> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/ns/odrl/2/Agreement> .
65+
<http://example.org/1705937573496#usagePolicy> <http://www.w3.org/ns/odrl/2/permission> <http://example.org/1705937573496#permission> .
66+
<http://example.org/1705937573496#permission> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/ns/odrl/2/Permission> .
67+
<http://example.org/1705937573496#permission> <http://www.w3.org/ns/odrl/2/action> <http://www.w3.org/ns/odrl/2/use> .
68+
<http://example.org/1705937573496#permission> <http://www.w3.org/ns/odrl/2/target> <http://localhost:3000/test.ttl> .
69+
<http://example.org/1705937573496#permission> <http://www.w3.org/ns/odrl/2/assignee> <https://woslabbi.pod.knows.idlab.ugent.be/profile/card#me> .
70+
<http://example.org/1705937573496#permission> <http://www.w3.org/ns/odrl/2/assigner> <https://pod.woutslabbinck.com/profile/card#me> .
71+
72+
# <http://example.org/1705937573496#permission> odrl:constraint <test>. # Note: uncommenting this rule leads to error
73+
<http://example.org/context> <http://example.org/resourceOwner> <https://pod.woutslabbinck.com/profile/card#me> .
74+
<http://example.org/context> <http://example.org/requestingParty> <https://woslabbi.pod.knows.idlab.ugent.be/profile/card#me> .
75+
<http://example.org/context> <http://example.org/target> <http://localhost:3000/test.ttl> .
76+
<http://example.org/context> <http://example.org/requestPermission> <http://www.w3.org/ns/auth/acl#Write> .
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
This tutorial is about how to install and use the [@solidlab/ucp](https://github.com/SolidLabResearch/user-managed-access/tree/main/packages/ucp) library utilities.
2+
3+
## Installing
4+
5+
As of today (2 Februari 2024), this package is not on npm.
6+
Which means installing via `npm install @solidlab/ucp` does **not** work yet.
7+
8+
However, it can still easily installed by adding `"@solidlab/ucp": "https://gitpkg.now.sh/SolidLabResearch/user-managed-access/packages/css?main"` to your dependencies in package.json, or executing the corresponding command of your favourite package manager, e.g. `yarn add @solidlab/ucp@"https://gitpkg.now.sh/SolidLabResearch/user-managed-access/packages/css?main"`.
9+
10+
## Using the library
11+
12+
First, a brief reminder of what the goal is of this library.
13+
What this library gives is a set of tools for creating **usage control decision** engines that are fully customizable.
14+
A **usage control decision** engine evaluates an *action request* against a set of *Usage Control Policies* to get a conclusion of *access grants*.
15+
16+
### Example
17+
18+
As an example, imagine that Ruben wants to know Wout his age.
19+
The age of Wout can be found in a *resource*, `urn:wout:age` (a unique identifier), and is safeguarded by Wout his *policy* that says Ruben has read access to the *resource*.
20+
oncretely, this policy could be modelled as a tuple (Requesting Party (RP), action, target resource, resource owner), which in this example would be (`urn:ruben`, `urn:modes:read`, `urn:wout:age`, `urn:wout`).
21+
The **usage control decision** engine, would then be able to interpret both the request and the policy to give as conclusion a grant: `urn:modes:read`.
22+
This means Ruben is able to know Wout his age and we know that the access is allowed conforming to the policies of Wout thanks to the engine.
23+
24+
Note: that this example does not contain any usage control yet, but is rather access control.
25+
When a policy has additional to the current tuple expression and enforcement for e.g. Ruben has to delete the data after 30 days and/or that he can only use it for the purpose of for example buying a gift for Wout his Birthday, then we are talking about Usage Control.
26+
Though, you could see the current use case as preventive usage control without purposes.
27+
28+
### High level Architecture
29+
30+
There are two functions implemented to calculate the grants, both are part of the interface `UconEnforcementDecision`.
31+
32+
1. `calculateAccessModes`: Calculate the *access grants* based on the set of *Usage Control Policies* , the *request* and how to *interpret the policies* (the algorithm).
33+
2. `calculateAndExplainAccessModes`: The same as `calculateAccessModes`, but also provides an **Explanation** of how the *access grants* are calculated.
34+
35+
Both functions have as input a `UconRequest`. This is an interface that formalizes the action request e.g. (RP, requested action, resource) (`urn:ruben`, `urn:modes:read`, `urn:wout:age`).
36+
37+
As output, `calculateAccessModes` has a list of strings (*access grants*) and `calculateAndExplainAccessModes` has an `Explanation` (which will be elaborated later).
38+
39+
### The first usage control decision engine
40+
41+
The implementation of the `UconEnforcementDecision` interface that this library provides is `UcpPatternEnforcement`.
42+
43+
The first instantiation of this interface is what is referred here as the first **usage control decision** engine.
44+
Before this instantiation is given, an explanation of how it works is given in [the following section](#usage-control-decision-engine).
45+
46+
#### Usage Control Decision engine
47+
48+
The (`UcpPatternEnforcement`) engine has three components and can be consulted with the methods described in [the high level architecture](#high-level-architecture).
49+
50+
The three components:
51+
52+
- A **[storage](https://github.com/SolidLabResearch/user-managed-access/tree/main/packages/ucp/src/plugins)** to the set of *Usage Control Policies*
53+
- A **storage** to the set of [Notation3](https://w3c.github.io/N3/spec/) (N3) [interpretation rules](https://github.com/SolidLabResearch/user-managed-access/tree/main/packages/ucp/rules)
54+
- A configured instance of **[Koreografeye](https://github.com/eyereasoner/Koreografeye)** consisting of:
55+
- An **N3 reasoner** ([eye-js](https://github.com/eyereasoner/eye-js))
56+
- A [Koreografeye policy/plugin executor](https://github.com/SolidLabResearch/user-managed-access/blob/main/packages/ucp/src/PolicyExecutor.ts) + [plugins](https://github.com/SolidLabResearch/user-managed-access/tree/main/packages/ucp/src/plugins)
57+
58+
When a method (e.g. `calculateAccessModes`) is then called with an *action request* the following steps are then executed:
59+
60+
1. The N3 Reasoner runs with as input:
61+
1. The set of *Usage Control Policies*
62+
2. The request (`UconRequest` serialized as RDF)
63+
3. The N3 *interpretation rules*
64+
2. The conclusion from the Reasoner is then extracted by the Koreografeye policy/plugin executor (configured with plugins)
65+
3. The result is then returned
66+
67+
This modular approach allows for fast prototyping of a formal Usage Control Policy language, which can then immediately be evaluated.
68+
69+
#### Instantiation
70+
71+
This first policy engine instantiation can perform an evaluation of policies modelled with [Open Digital Rights Language (ODRL)](https://www.w3.org/TR/odrl-model/).
72+
More specifically, with a subset that only can interpret `odrl:Permission`s with as **action** `odrl:modify`, `odrl:read` and `odrl:use` (which means both modify and read) against action requests and where there are **no Constraints**.
73+
74+
To initialise `UcpPatternEnforcement` as this engine, the following code is required:
75+
https://github.com/SolidLabResearch/user-managed-access/blob/1c0410b6f1c3f133843430f60d4f8c690273dff0/packages/ucp/docs/simple-engine.ts#L1-L18
76+
77+
78+
At this point, the engine is ready to be used. Which means that now you can use the `calculateAccessModes` function to request the **grants** for following *action request*: "Ruben wants to know the age of Wout".
79+
80+
https://github.com/SolidLabResearch/user-managed-access/blob/1c0410b6f1c3f133843430f60d4f8c690273dff0/packages/ucp/docs/simple-engine.ts#L20-L27
81+
82+
Unfortunately, this results into an empty list `[]`. Which means no **grants** are given and thus Ruben cannot know the age of Wout.
83+
84+
The reason for this is very simple, there is **no** *Usage Control Policy* in the storage.
85+
86+
This can however be resolved by simply adding such a policy to the **Usage Control Rule Storage**:
87+
88+
```ttl
89+
@prefix odrl: <http://www.w3.org/ns/odrl/2/> .
90+
@prefix : <http://example.org/usageControlRule> .
91+
92+
:permission
93+
a odrl:Permission ;
94+
odrl:action odrl:read ;
95+
odrl:target <urn:wout:age> ;
96+
odrl:assignee <https://pod.rubendedecker.be/profile/card#me> ;
97+
odrl:assigner <https://pod.woutslabbinck.com/profile/card#me> .
98+
```
99+
100+
To add this rule to the storage, the following code can be used:
101+
102+
https://github.com/SolidLabResearch/user-managed-access/blob/1c0410b6f1c3f133843430f60d4f8c690273dff0/packages/ucp/docs/simple-engine.ts#L30-L41
103+
104+
105+
From now on, when the access modes are calculated again, the following list of grants is received:
106+
107+
```sh
108+
[ 'http://www.w3.org/ns/auth/acl#Read' ]
109+
```
110+
111+
With current initialisation of the **UC Decision engine**, grants are modelled with the [Access Control List (ACL)](https://www.w3.org/ns/auth/acl) Ontology.
112+
The reason for this is that this component is going to be used in an Authorisation Server (AS) for a [Solid](https://solidproject.org/) Resource Server (RS) using the [User-Managed Access (UMA)](https://docs.kantarainitiative.org/uma/wg/rec-oauth-uma-grant-2.0.html) specification. In the [Solid Protocol](https://solidproject.org/TR/protocol), ACL is the ontology used for **access modes** both its authorization specifications: the [Web Access Control (WAC)](https://solidproject.org/TR/wac) specification and the [Access Control Policy (ACP)](https://solidproject.org/TR/acp) specification.
113+
114+
In the engine, this grant is calculated within the N3 interpretation rules, more specifically [here](https://github.com/SolidLabResearch/user-managed-access/blob/949917faa015195369e430f77200bf3273f79283/packages/ucp/rules/data-crud-rules.n3#L12-L38).
115+
116+
The full code sample for this example can be found in [appendix I](#appendix-I-Full-code-snippet)
117+
118+
### <!--Temporal Policy engine-->
119+
120+
<!--TODO:-->
121+
122+
### Engine with explanation
123+
124+
First an elaboration is given for what is meant by explanation.
125+
<!--Note: after implementation, similarities have been found with the idea of **ODRL Evaluator** from the [ODRL Formal Semantics Community Group](https://w3c.github.io/odrl/formal-semantics/).-->
126+
127+
#### Explanation
128+
129+
So what is the explanation exactly? It is an interface which contains four components:
130+
131+
132+
An `Explanation` consists of four components:
133+
134+
- `decision`: This is the same as the grants (array of access modes) from `calculateAccessModes`. It is the **result** of the evaluation
135+
- `request`: The input *action request*
136+
- `conclusions`: The conclusions of the reasoner. A conclusion itself consists of four parts: the **Rule Identifier**, the **Interpration N3 Rule Identifier**, the **grants** allowed (the actual conclusion) and the **timestamp** at which the conclusion was generated. A conclusion can be seen as the proof of following function: $interpretation(rule, context, timestamp) -> grants$
137+
- `algorithm`: Which algorithm is used to interpret the set of conclusions
138+
- Note: only the **union** operator is currently implemented. That is: $\forall c \in C. grant \in c \Rightarrow grant \in D$ </br>
139+
For all conclusions in `conclusions`, if a grant is in conclusion, then it is part of the list of grants in `decision`.
140+
141+
Having the **Explanation** after an evaluation thus allows for logging with provenance/proof of why a certain action was granted at a certain time.
142+
143+
#### Instantiation
144+
145+
To have a **usage control decision** engine that can give **Explanations**, it needs to be instantiated correctly. This can be done with following code:
146+
147+
https://github.com/SolidLabResearch/user-managed-access/blob/1c0410b6f1c3f133843430f60d4f8c690273dff0/packages/ucp/docs/log-engine.ts#L1-L18
148+
149+
Not that compared to the previous engines, a different **plugin** and a different **N3 interpretation rules** are used.
150+
These allow for retrieving a proper explanation during the calculation of the requests.
151+
152+
Another difference is the fact that know the method `calculateAndExplainAccessModes` has to be used.
153+
Luckily, this still uses a `UconRequest` as input:
154+
155+
https://github.com/SolidLabResearch/user-managed-access/blob/1c0410b6f1c3f133843430f60d4f8c690273dff0/packages/ucp/docs/log-engine.ts#L34-L41
156+
When a policy is added to the storage, the above code print out a Javascript Object, which is not very nice.
157+
158+
Luckily, it is possible to have a nice RDF serialization as output:
159+
160+
https://github.com/SolidLabResearch/user-managed-access/blob/1c0410b6f1c3f133843430f60d4f8c690273dff0/packages/ucp/docs/log-engine.ts#L46-L49
161+
162+
Or to be used as RDF in code with the N3 Store:
163+
164+
https://github.com/SolidLabResearch/user-managed-access/blob/1c0410b6f1c3f133843430f60d4f8c690273dff0/packages/ucp/docs/log-engine.ts#L43-L44
165+
166+
167+
## appendix I: Full code snippet
168+
169+
https://github.com/SolidLabResearch/user-managed-access/blob/1c0410b6f1c3f133843430f60d4f8c690273dff0/packages/ucp/docs/simple-engine.ts#L1-L52
170+
171+
172+
## appendix III: Full code snippet Explanation
173+
174+
https://github.com/SolidLabResearch/user-managed-access/blob/1c0410b6f1c3f133843430f60d4f8c690273dff0/packages/ucp/docs/log-engine.ts#L1-L51

packages/ucp/docs/log-engine.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { PolicyExecutor, UcpPatternEnforcement, UCPLogPlugin, MemoryUCRulesStorage, explanationToRdf, serializeFullExplanation, turtleStringToStore } from "@solidlab/ucp";
2+
import { EyeJsReasoner } from "koreografeye";
3+
4+
async function main() {
5+
// load plugin(s)
6+
const plugins = { "http://example.org/dataUsageLog": new UCPLogPlugin() }
7+
// instantiate koreografeye policy executor
8+
const policyExecutor = new PolicyExecutor(plugins)
9+
// ucon storage
10+
const uconRulesStorage = new MemoryUCRulesStorage()
11+
// load N3 Rules from a directory
12+
const response = await fetch('https://raw.githubusercontent.com/woutslabbinck/ucp-enforcement/main/rules/log-usage-rule.n3'); // loading from the github repo
13+
const n3Rules: string[] = [await response.text()]
14+
// instantiate the enforcer using the policy executor,
15+
const ucpEvaluator = new UcpPatternEnforcement(uconRulesStorage, n3Rules, new EyeJsReasoner([
16+
"--quiet",
17+
"--nope",
18+
"--pass"]), policyExecutor)
19+
20+
// add Usage Control Rule to Usage Control Rule Storage
21+
const ucr = `@prefix odrl: <http://www.w3.org/ns/odrl/2/> .
22+
@prefix : <http://example.org/usageControlRule> .
23+
24+
:permission
25+
a odrl:Permission ;
26+
odrl:action odrl:read ;
27+
odrl:target <urn:wout:age> ;
28+
odrl:assignee <https://pod.rubendedecker.be/profile/card#me> ;
29+
odrl:assigner <https://pod.woutslabbinck.com/profile/card#me> .
30+
`
31+
const policyStore = await turtleStringToStore(ucr);
32+
await uconRulesStorage.addRule(policyStore);
33+
34+
// calculate grants based on a request
35+
const explanation = await ucpEvaluator.calculateAndExplainAccessModes({
36+
subject: "https://pod.rubendedecker.be/profile/card#me",
37+
action: ["http://www.w3.org/ns/auth/acl#Read"],
38+
resource: "urn:wout:age",
39+
owner: "https://pod.woutslabbinck.com/profile/card#me"
40+
});
41+
console.log(explanation);
42+
43+
// use of explanationToRdf
44+
const explanationStore = explanationToRdf(explanation);
45+
46+
// use of serializeFullExplanation
47+
const uconRules = await uconRulesStorage.getStore();
48+
const serialized = serializeFullExplanation(explanation, uconRules, n3Rules.join('\n'));
49+
console.log(serialized);
50+
}
51+
main()

packages/ucp/docs/simple-engine.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { PolicyExecutor, UcpPatternEnforcement, UcpPlugin, MemoryUCRulesStorage, turtleStringToStore } from "@solidlab/ucp";
2+
import { EyeJsReasoner } from "koreografeye";
3+
4+
async function main() {
5+
// load plugin(s)
6+
const plugins = { "http://example.org/dataUsage": new UcpPlugin() }
7+
// Initialise koreografeye policy executor
8+
const policyExecutor = new PolicyExecutor(plugins)
9+
// Initialise Usage Control Rule Storage
10+
const uconRulesStorage = new MemoryUCRulesStorage();
11+
// load N3 Rules
12+
const response = await fetch('https://raw.githubusercontent.com/woutslabbinck/ucp-enforcement/main/rules/data-crud-rules.n3'); // loading from the github repo
13+
const n3Rules: string[] = [await response.text()]
14+
// instantiate the enforcer using the policy executor,
15+
const ucpEvaluator = new UcpPatternEnforcement(uconRulesStorage, n3Rules, new EyeJsReasoner([
16+
"--quiet",
17+
"--nope",
18+
"--pass"]), policyExecutor)
19+
20+
// calculate grants based on a request
21+
const noAccessModes = await ucpEvaluator.calculateAccessModes({
22+
subject: "https://pod.rubendedecker.be/profile/card#me",
23+
action: ["http://www.w3.org/ns/auth/acl#Read"],
24+
resource: "urn:wout:age",
25+
owner: "https://pod.woutslabbinck.com/profile/card#me"
26+
});
27+
console.log(noAccessModes);
28+
29+
// add Usage Control Rule to Usage Control Rule Storage
30+
const ucr = `@prefix odrl: <http://www.w3.org/ns/odrl/2/> .
31+
@prefix : <http://example.org/usageControlRule> .
32+
33+
:permission
34+
a odrl:Permission ;
35+
odrl:action odrl:read ;
36+
odrl:target <urn:wout:age> ;
37+
odrl:assignee <https://pod.rubendedecker.be/profile/card#me> ;
38+
odrl:assigner <https://pod.woutslabbinck.com/profile/card#me> .
39+
`
40+
const policyStore = await turtleStringToStore(ucr);
41+
await uconRulesStorage.addRule(policyStore);
42+
43+
// calculate grants based on a request
44+
const accessModes = await ucpEvaluator.calculateAccessModes({
45+
subject: "https://pod.rubendedecker.be/profile/card#me",
46+
action: ["http://www.w3.org/ns/auth/acl#Read"],
47+
resource: "urn:wout:age",
48+
owner: "https://pod.woutslabbinck.com/profile/card#me"
49+
});
50+
console.log(accessModes);
51+
}
52+
main()

0 commit comments

Comments
 (0)