Skip to content

Commit

Permalink
feat: ucp-enforcement stuff for demo
Browse files Browse the repository at this point in the history
  • Loading branch information
woutslabbinck committed Mar 8, 2024
1 parent beb9c14 commit 50c49f2
Show file tree
Hide file tree
Showing 8 changed files with 269 additions and 0 deletions.
48 changes: 48 additions & 0 deletions demo/demoEngine.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { ContainerUCRulesStorage, PolicyExecutor, UCRulesStorage, UconEnforcementDecision, UcpPatternEnforcement, UcpPlugin } from "@solidlab/ucp"
import { App, AppRunner, AppRunnerInput } from "@solid/community-server";
import * as Path from 'path';
import { EyeJsReasoner, readText } from "koreografeye";

type Demo = {
css: App,
ucpEngine: UconEnforcementDecision,
storage: UCRulesStorage
}

export async function initEngine(portNumber = 3123): Promise<Demo> {
const containerURL = `http://localhost:${portNumber}/`
// code to start css server somewhere
const css = await configSolidServer(portNumber)

// initiating
// load plugin(s)
const plugins = { "http://example.org/dataUsage": new UcpPlugin() }
// Initialise koreografeye policy executor
const policyExecutor = new PolicyExecutor(plugins)
// load N3 Rules from a directory
const rulesDirectory = Path.join(__dirname)
const n3Rules: string[] = [readText(Path.join(rulesDirectory, 'purpose-time.n3'))!]
// Initialise Usage Control Rule Storage
const uconRulesStorage = new ContainerUCRulesStorage(containerURL);
const ucpEngine = new UcpPatternEnforcement(uconRulesStorage, n3Rules, new EyeJsReasoner([
"--quiet",
"--nope",
"--pass"]), policyExecutor)
return {css, ucpEngine, storage: uconRulesStorage}
}


// utils
async function configSolidServer(port: number): Promise<App> {
const input: AppRunnerInput = {
config: Path.join(__dirname, "memory.json"),
variableBindings: {
'urn:solid-server:default:variable:port': port,
'urn:solid-server:default:variable:baseUrl': `http://localhost:${port}/`,
'urn:solid-server:default:variable:loggingLevel': 'warn',
}
}
const cssRunner = await new AppRunner().create(input)
return cssRunner
}

41 changes: 41 additions & 0 deletions demo/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { createContext, storeToString } from "@solidlab/ucp";
import { initEngine } from "./demoEngine";
import { demoPolicy } from "./policyCreation";

async function main(){
const {css, ucpEngine, storage} = await initEngine();

const target = "urn:wout:age"
const requestingParty = "https://pod.rubendedecker.be/profile/card#me"

const request = {
subject: requestingParty,
action: ["http://www.w3.org/ns/auth/acl#Read"],
resource: target,
owner: "https://pod.woutslabbinck.com/profile/card#me"
}
const policy = demoPolicy(target, requestingParty)
// start server
await css.start();


const noAccessModes = await ucpEngine.calculateAccessModes(request);
console.log("Access modes retrieved when no policy in storage", noAccessModes);

// Add following Policy to storage:
// Wout gives access to Ruben regarding Wout his age
// constraints: two weeks from now on + purpose= "age-verification"
await storage.addRule(policy.representation)


const accessModes = await ucpEngine.calculateAccessModes(request);
console.log("Access modes retrieved when policy in storage", accessModes);

// debug logs
// console.log(storeToString(createContext(request))); // Note: request -> which is also what is expected in the uma server at that stage
// console.log(storeToString(policy.representation)); // Note: log ODRL rule
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.");

}

main()
37 changes: 37 additions & 0 deletions demo/memory.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^7.0.0/components/context.jsonld",
"import": [
"css:config/app/init/initialize-root.json",
"css:config/app/main/default.json",
"css:config/app/variables/default.json",
"css:config/http/handler/default.json",
"css:config/http/middleware/default.json",
"css:config/http/notifications/all.json",
"css:config/http/server-factory/http.json",
"css:config/http/static/default.json",
"css:config/identity/access/public.json",
"css:config/identity/email/default.json",
"css:config/identity/handler/disabled.json",
"css:config/identity/oidc/disabled.json",
"css:config/identity/ownership/token.json",
"css:config/identity/pod/static.json",
"css:config/ldp/authentication/dpop-bearer.json",
"css:config/ldp/authorization/webacl.json",
"css:config/ldp/handler/default.json",
"css:config/ldp/metadata-parser/default.json",
"css:config/ldp/metadata-writer/default.json",
"css:config/ldp/modes/default.json",
"css:config/storage/backend/memory.json",
"css:config/storage/key-value/resource-store.json",
"css:config/storage/location/root.json",
"css:config/storage/middleware/default.json",
"css:config/util/auxiliary/acl.json",
"css:config/util/identifiers/suffix.json",
"css:config/util/index/default.json",
"css:config/util/logging/winston.json",
"css:config/util/representation-conversion/default.json",
"css:config/util/resource-locker/memory.json",
"css:config/util/variables/default.json"
],
"@graph": []
}
58 changes: 58 additions & 0 deletions demo/policyCreation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/**
* create ODRL policy with three constraints -> basically a function that prints that as output
- constraints
- start time
- end time
- purpose: string -> "age-verification"
- target: SHACL shape?
*/

import { SimplePolicy, UCPPolicy, basicPolicy, storeToString } from '@solidlab/ucp'

export const agePurpose = "age-verification"
/**
* Create demo ODRL policy:
*
* Read access for requestingparty to target under constraints (temporal + purpose)
* @param targetIRI - an IRI representing the target -> the resource
* @param requestingPartyIRI - an IRI representing the entity requesting access
* @param constraints
*/
export function demoPolicy(targetIRI, requestingPartyIRI, constraints?: { startDate?: Date, endDate?: Date, purpose?: string }): SimplePolicy {
const startDate = constraints?.startDate ?? new Date()
const endDate = constraints?.endDate ?? new Date(startDate.valueOf()+ 86_400 * 14 * 1000)
const purpose = constraints?.purpose ?? agePurpose

const policy: UCPPolicy = {
rules: [{
requestingParty: requestingPartyIRI,
action: "http://www.w3.org/ns/odrl/2/read", // msut be odrl
resource: targetIRI,
owner: "https://pod.woutslabbinck.com/profile/card#me", // can lead to bugs, depending on whether we use owner or not
constraints: [
{
type: "temporal",
operator: "http://www.w3.org/ns/odrl/2/gt",
value: startDate
},
{
type: "temporal",
operator: "http://www.w3.org/ns/odrl/2/lt",
value: endDate
},
{
type: "purpose",
operator: "http://www.w3.org/ns/odrl/2/eq",
value: purpose
},
]
}]
}

const policyObject = basicPolicy(policy);
// console.log(storeToString(policyObject.representation));
return policyObject
}

// example: Wout gives access to Ruben regarding Wout his age
// demoPolicy("urn:wout:age", "https://pod.rubendedecker.be/profile/card#me")
72 changes: 72 additions & 0 deletions demo/purpose-time.n3
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix odrl: <http://www.w3.org/ns/odrl/2/> .
@prefix : <http://example.org/> .
@prefix acl: <http://www.w3.org/ns/auth/acl#>.
@prefix fno: <https://w3id.org/function/ontology#> .
@prefix log: <http://www.w3.org/2000/10/swap/log#> .
@prefix string: <http://www.w3.org/2000/10/swap/string#> .
@prefix list: <http://www.w3.org/2000/10/swap/list#> .
@prefix time: <http://www.w3.org/2000/10/swap/time#> .
@prefix math: <http://www.w3.org/2000/10/swap/math#> .
# Create ODRL Rule: doesn't exist (`odrl:write` and `odrl:append` are deprecated)
{ :currentTime :is ?currentTime } <= { "" time:localTime ?currentTime }.

# Read ODRL rule
{
?permission a odrl:Permission;
odrl:action ?action ;
odrl:target ?targetResource ;
odrl:assignee ?requestedParty;
odrl:assigner ?resourceOwner .

?action list:in (odrl:use odrl:read) . # multiple options

# context of a request
?context
:resourceOwner ?resourceOwner;
:requestingParty ?requestedParty;
:target ?targetResource;
:requestPermission acl:Read.

:uuid5 log:uuid ?uuidStringdataUsagePolicyExecution.
( "urn:uuid:" ?uuidStringdataUsagePolicyExecution) string:concatenation ?urnUuidStringdataUsagePolicyExecution.
?dataUsagePolicyExecution log:uri ?urnUuidStringdataUsagePolicyExecution .

# Constraint checking
# number of constraints must be two (temporal needs lower and upper bound)
(?template {?permission odrl:constraint _:s} ?L) log:collectAllIn ?SCOPE.
?L list:length 3 .

:currentTime :is ?currentTime .

# lower bound
?permission odrl:constraint ?lowerBoundIRI .
?lowerBoundIRI odrl:leftOperand odrl:dateTime ;
odrl:operator odrl:gt ;
odrl:rightOperand ?lowerBound .

# greater bound
?permission odrl:constraint ?upperBoundIRI .
?upperBoundIRI odrl:leftOperand odrl:dateTime ;
odrl:operator odrl:lt ;
odrl:rightOperand ?upperBound .

# ?lowerBound < ?currentTime < ?upperBound
?currentTime math:greaterThan ?lowerBound .
?currentTime math:lessThan ?upperBound .

# purpose constraint
?permission odrl:constraint ?purposeConstraint .
?purposeConstraint odrl:leftOperand odrl:purpose ;
odrl:operator odrl:eq ;
odrl:rightOperand ?purposeValue
# Note: nothing is done with the purpose right now TODO: needs checking
} =>
{
?dataUsagePolicyExecution a fno:Execution;
fno:executes <http://example.org/dataUsage> ;
:accessModesAllowed acl:Read.
?dataUsagePolicyExecution <http://purl.org/dc/terms/issued> ?currentTime .

}.

4 changes: 4 additions & 0 deletions packages/ucp/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ export * from './storage/DirectoryUCRulesStorage'
export * from './storage/MemoryUCRulesStorage'
export * from './storage/UCRulesStorage'

// policy
export * from './policy/ODRL'
export * from './policy/UsageControlPolicy'

// util
export * from './util/Conversion'
export * from './util/Constants'
Expand Down
8 changes: 8 additions & 0 deletions packages/ucp/src/policy/ODRL.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,14 @@ export function createConstraintQuads(constraint: UCPConstraint, ruleIRI?: strin
quads.push(quad(namedNode(ruleIRI), ODRL.terms.constraint, namedNode(constraintIRI)))
}
break;
case "purpose":
quads.push(quad(namedNode(constraintIRI), ODRL.terms.leftOperand, ODRL.terms.purpose));
quads.push(quad(namedNode(constraintIRI), ODRL.terms.operator, namedNode(constraint.operator)));
quads.push(quad(namedNode(constraintIRI), ODRL.terms.rightOperand, literal(constraint.value)));
if (ruleIRI) {
quads.push(quad(namedNode(ruleIRI), ODRL.terms.constraint, namedNode(constraintIRI)))
}
break;
default:
console.log("Can not create constraint as the type is not understood:", constraint.type);
break;
Expand Down
1 change: 1 addition & 0 deletions packages/ucp/src/util/Vocabularies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ export const ODRL = createVocabulary(
'operator',
'permission',
'dateTime',
'purpose',
'leftOperand',
'rightOperand'
)
Expand Down

0 comments on commit 50c49f2

Please sign in to comment.