Skip to content

Commit b444753

Browse files
feat: ucp changes for demo (#37)
Co-authored-by: woutslabbinck <wout.slabbinck@ugent.be>
1 parent da9ad0d commit b444753

7 files changed

Lines changed: 265 additions & 0 deletions

File tree

demo/demoEngine.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { ContainerUCRulesStorage, PolicyExecutor, UCRulesStorage, UconEnforcementDecision, UcpPatternEnforcement, UcpPlugin } from "@solidlab/ucp"
2+
import { App, AppRunner, AppRunnerInput } from "@solid/community-server";
3+
import * as Path from 'path';
4+
import { EyeJsReasoner, readText } from "koreografeye";
5+
6+
type Demo = {
7+
css: App,
8+
ucpEngine: UconEnforcementDecision,
9+
storage: UCRulesStorage
10+
}
11+
12+
export async function initEngine(portNumber = 3123): Promise<Demo> {
13+
const containerURL = `http://localhost:${portNumber}/`
14+
// code to start css server somewhere
15+
const css = await configSolidServer(portNumber)
16+
17+
// initiating
18+
// load plugin(s)
19+
const plugins = { "http://example.org/dataUsage": new UcpPlugin() }
20+
// Initialise koreografeye policy executor
21+
const policyExecutor = new PolicyExecutor(plugins)
22+
// load N3 Rules from a directory
23+
const rulesDirectory = Path.join(__dirname)
24+
const n3Rules: string[] = [readText(Path.join(rulesDirectory, 'purpose-time.n3'))!]
25+
// Initialise Usage Control Rule Storage
26+
const uconRulesStorage = new ContainerUCRulesStorage(containerURL);
27+
const ucpEngine = new UcpPatternEnforcement(uconRulesStorage, n3Rules, new EyeJsReasoner([
28+
"--quiet",
29+
"--nope",
30+
"--pass"]), policyExecutor)
31+
return {css, ucpEngine, storage: uconRulesStorage}
32+
}
33+
34+
35+
// utils
36+
async function configSolidServer(port: number): Promise<App> {
37+
const input: AppRunnerInput = {
38+
config: Path.join(__dirname, "memory.json"),
39+
variableBindings: {
40+
'urn:solid-server:default:variable:port': port,
41+
'urn:solid-server:default:variable:baseUrl': `http://localhost:${port}/`,
42+
'urn:solid-server:default:variable:loggingLevel': 'warn',
43+
}
44+
}
45+
const cssRunner = await new AppRunner().create(input)
46+
return cssRunner
47+
}
48+

demo/main.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { createContext, storeToString } from "@solidlab/ucp";
2+
import { initEngine } from "./demoEngine";
3+
import { demoPolicy } from "./policyCreation";
4+
5+
async function main(){
6+
const {css, ucpEngine, storage} = await initEngine();
7+
8+
const target = "urn:wout:age"
9+
const requestingParty = "https://pod.rubendedecker.be/profile/card#me"
10+
11+
const request = {
12+
subject: requestingParty,
13+
action: ["http://www.w3.org/ns/auth/acl#Read"],
14+
resource: target,
15+
owner: "https://pod.woutslabbinck.com/profile/card#me"
16+
}
17+
const policy = demoPolicy(target, requestingParty)
18+
// start server
19+
await css.start();
20+
21+
22+
const noAccessModes = await ucpEngine.calculateAccessModes(request);
23+
console.log("Access modes retrieved when no policy in storage", noAccessModes);
24+
25+
// Add following Policy to storage:
26+
// Wout gives access to Ruben regarding Wout his age
27+
// constraints: two weeks from now on + purpose= "age-verification"
28+
await storage.addRule(policy.representation)
29+
30+
31+
const accessModes = await ucpEngine.calculateAccessModes(request);
32+
console.log("Access modes retrieved when policy in storage", accessModes);
33+
34+
// debug logs
35+
// console.log(storeToString(createContext(request))); // Note: request -> which is also what is expected in the uma server at that stage
36+
// console.log(storeToString(policy.representation)); // Note: log ODRL rule
37+
console.log("Right now 'storage' is used to PUT the demo policy to 'http://localhost:3123/'. A normal HTTP request can also be used to do that.");
38+
39+
}
40+
41+
main()

demo/memory.json

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
{
2+
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^7.0.0/components/context.jsonld",
3+
"import": [
4+
"css:config/app/init/initialize-root.json",
5+
"css:config/app/main/default.json",
6+
"css:config/app/variables/default.json",
7+
"css:config/http/handler/default.json",
8+
"css:config/http/middleware/default.json",
9+
"css:config/http/notifications/all.json",
10+
"css:config/http/server-factory/http.json",
11+
"css:config/http/static/default.json",
12+
"css:config/identity/access/public.json",
13+
"css:config/identity/email/default.json",
14+
"css:config/identity/handler/disabled.json",
15+
"css:config/identity/oidc/disabled.json",
16+
"css:config/identity/ownership/token.json",
17+
"css:config/identity/pod/static.json",
18+
"css:config/ldp/authentication/dpop-bearer.json",
19+
"css:config/ldp/authorization/webacl.json",
20+
"css:config/ldp/handler/default.json",
21+
"css:config/ldp/metadata-parser/default.json",
22+
"css:config/ldp/metadata-writer/default.json",
23+
"css:config/ldp/modes/default.json",
24+
"css:config/storage/backend/memory.json",
25+
"css:config/storage/key-value/resource-store.json",
26+
"css:config/storage/location/root.json",
27+
"css:config/storage/middleware/default.json",
28+
"css:config/util/auxiliary/acl.json",
29+
"css:config/util/identifiers/suffix.json",
30+
"css:config/util/index/default.json",
31+
"css:config/util/logging/winston.json",
32+
"css:config/util/representation-conversion/default.json",
33+
"css:config/util/resource-locker/memory.json",
34+
"css:config/util/variables/default.json"
35+
],
36+
"@graph": []
37+
}

demo/policyCreation.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/**
2+
* create ODRL policy with three constraints -> basically a function that prints that as output
3+
- constraints
4+
- start time
5+
- end time
6+
- purpose: string -> "age-verification"
7+
- target: SHACL shape?
8+
*/
9+
10+
import { SimplePolicy, UCPPolicy, basicPolicy, storeToString } from '@solidlab/ucp'
11+
12+
export const agePurpose = "age-verification"
13+
/**
14+
* Create demo ODRL policy:
15+
*
16+
* Read access for requestingparty to target under constraints (temporal + purpose)
17+
* @param targetIRI - an IRI representing the target -> the resource
18+
* @param requestingPartyIRI - an IRI representing the entity requesting access
19+
* @param constraints
20+
*/
21+
export function demoPolicy(targetIRI, requestingPartyIRI, constraints?: { startDate?: Date, endDate?: Date, purpose?: string }): SimplePolicy {
22+
const startDate = constraints?.startDate ?? new Date()
23+
const endDate = constraints?.endDate ?? new Date(startDate.valueOf()+ 86_400 * 14 * 1000)
24+
const purpose = constraints?.purpose ?? agePurpose
25+
26+
const policy: UCPPolicy = {
27+
rules: [{
28+
requestingParty: requestingPartyIRI,
29+
action: "http://www.w3.org/ns/odrl/2/read", // msut be odrl
30+
resource: targetIRI,
31+
owner: "https://pod.woutslabbinck.com/profile/card#me", // can lead to bugs, depending on whether we use owner or not
32+
constraints: [
33+
{
34+
type: "temporal",
35+
operator: "http://www.w3.org/ns/odrl/2/gt",
36+
value: startDate
37+
},
38+
{
39+
type: "temporal",
40+
operator: "http://www.w3.org/ns/odrl/2/lt",
41+
value: endDate
42+
},
43+
{
44+
type: "purpose",
45+
operator: "http://www.w3.org/ns/odrl/2/eq",
46+
value: purpose
47+
},
48+
]
49+
}]
50+
}
51+
52+
const policyObject = basicPolicy(policy);
53+
// console.log(storeToString(policyObject.representation));
54+
return policyObject
55+
}
56+
57+
// example: Wout gives access to Ruben regarding Wout his age
58+
// demoPolicy("urn:wout:age", "https://pod.rubendedecker.be/profile/card#me")

demo/purpose-time.n3

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
2+
@prefix odrl: <http://www.w3.org/ns/odrl/2/> .
3+
@prefix : <http://example.org/> .
4+
@prefix acl: <http://www.w3.org/ns/auth/acl#>.
5+
@prefix fno: <https://w3id.org/function/ontology#> .
6+
@prefix log: <http://www.w3.org/2000/10/swap/log#> .
7+
@prefix string: <http://www.w3.org/2000/10/swap/string#> .
8+
@prefix list: <http://www.w3.org/2000/10/swap/list#> .
9+
@prefix time: <http://www.w3.org/2000/10/swap/time#> .
10+
@prefix math: <http://www.w3.org/2000/10/swap/math#> .
11+
# Create ODRL Rule: doesn't exist (`odrl:write` and `odrl:append` are deprecated)
12+
{ :currentTime :is ?currentTime } <= { "" time:localTime ?currentTime }.
13+
14+
# Read ODRL rule
15+
{
16+
?permission a odrl:Permission;
17+
odrl:action ?action ;
18+
odrl:target ?targetResource ;
19+
odrl:assignee ?requestedParty;
20+
odrl:assigner ?resourceOwner .
21+
22+
?action list:in (odrl:use odrl:read) . # multiple options
23+
24+
# context of a request
25+
?context
26+
:resourceOwner ?resourceOwner;
27+
:requestingParty ?requestedParty;
28+
:target ?targetResource;
29+
:requestPermission acl:Read.
30+
31+
:uuid5 log:uuid ?uuidStringdataUsagePolicyExecution.
32+
( "urn:uuid:" ?uuidStringdataUsagePolicyExecution) string:concatenation ?urnUuidStringdataUsagePolicyExecution.
33+
?dataUsagePolicyExecution log:uri ?urnUuidStringdataUsagePolicyExecution .
34+
35+
# Constraint checking
36+
# number of constraints must be two (temporal needs lower and upper bound)
37+
(?template {?permission odrl:constraint _:s} ?L) log:collectAllIn ?SCOPE.
38+
?L list:length 3 .
39+
40+
:currentTime :is ?currentTime .
41+
42+
# lower bound
43+
?permission odrl:constraint ?lowerBoundIRI .
44+
?lowerBoundIRI odrl:leftOperand odrl:dateTime ;
45+
odrl:operator odrl:gt ;
46+
odrl:rightOperand ?lowerBound .
47+
48+
# greater bound
49+
?permission odrl:constraint ?upperBoundIRI .
50+
?upperBoundIRI odrl:leftOperand odrl:dateTime ;
51+
odrl:operator odrl:lt ;
52+
odrl:rightOperand ?upperBound .
53+
54+
# ?lowerBound < ?currentTime < ?upperBound
55+
?currentTime math:greaterThan ?lowerBound .
56+
?currentTime math:lessThan ?upperBound .
57+
58+
# purpose constraint
59+
?permission odrl:constraint ?purposeConstraint .
60+
?purposeConstraint odrl:leftOperand odrl:purpose ;
61+
odrl:operator odrl:eq ;
62+
odrl:rightOperand ?purposeValue
63+
# Note: nothing is done with the purpose right now TODO: needs checking
64+
} =>
65+
{
66+
?dataUsagePolicyExecution a fno:Execution;
67+
fno:executes <http://example.org/dataUsage> ;
68+
:accessModesAllowed acl:Read.
69+
?dataUsagePolicyExecution <http://purl.org/dc/terms/issued> ?currentTime .
70+
71+
}.
72+

packages/ucp/src/policy/ODRL.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,14 @@ export function createConstraintQuads(constraint: UCPConstraint, ruleIRI?: strin
8383
quads.push(quad(namedNode(ruleIRI), ODRL.terms.constraint, namedNode(constraintIRI)))
8484
}
8585
break;
86+
case "purpose":
87+
quads.push(quad(namedNode(constraintIRI), ODRL.terms.leftOperand, ODRL.terms.purpose));
88+
quads.push(quad(namedNode(constraintIRI), ODRL.terms.operator, namedNode(constraint.operator)));
89+
quads.push(quad(namedNode(constraintIRI), ODRL.terms.rightOperand, literal(constraint.value)));
90+
if (ruleIRI) {
91+
quads.push(quad(namedNode(ruleIRI), ODRL.terms.constraint, namedNode(constraintIRI)))
92+
}
93+
break;
8694
default:
8795
console.log("Can not create constraint as the type is not understood:", constraint.type);
8896
break;

packages/ucp/src/util/Vocabularies.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ export const ODRL = createVocabulary(
122122
'operator',
123123
'permission',
124124
'dateTime',
125+
'purpose',
125126
'leftOperand',
126127
'rightOperand'
127128
)

0 commit comments

Comments
 (0)