woutslabbinck / ucp-enforcement

A playground for calculating access mode grants based on usage control rules and usage control rule interpreters
2 stars 0 forks source link
access-control usage-control user-managed-access

(Preventive) Usage Control Decision/Enforcement playground

A playground environment to calculate Access Modes based on ODRL Rules, an UMA Client Request and Koreografeye (with accompanying N3 Rules and plugin(s)). This class will be used in the UMA Authorisation Server that is being developed to work with the Community Solid Server.

The UMA client request contains the following information

In main.ts is an example of how it works given.

Running that script is done as follows:

npx ts-node main.ts

More information on how this library works can be found in the tutorial.

How does it work

There are a couple of components:

This means that in an Authorization Server, this component could be used as a decision component.

const ucpDecide: UconEnforcementDecision = ...
const accessModes = await ucpDecide.calculateAccessModes({
    subject: "https://woslabbi.pod.knows.idlab.ugent.be/profile/card#me",
    action: ["http://www.w3.org/ns/auth/acl#Read"],
    resource: "http://localhost:3000/test.ttl",
    owner: "http://localhost:3000/alice/profile/card#me"
});
console.log(accessModes);
> [ 'read' ]

Example: CRUD policy engine

In crud_engine.ts, a UconEnforcementDecision component is initialised. This means that CRUD action requests can be evaluated against a set of ucon rules and an ucon rules interpreter.

The ucon rule interpretation is a combination of N3 rules (data-crud-rules.n3), a N3 Reasoner (eye-js), a plugin executor (PolicyExecutor) and a plugin (UcpPlugin). These components all work together as described in Koreografeye.

The set of ucon rules can be dynamically generated and added through the ucon rules store through the functions createPolicy and createTemporalPolicy (see crudUtil.ts).

Initialisation

import { EyeJsReasoner, readText } from "koreografeye";
import { UcpPlugin } from "./src/plugins/UCPPlugin";
import { ContainerUCRulesStorage } from "./src/storage/ContainerUCRulesStorage";
import { PolicyExecutor } from "./src/PolicyExecutor";

// load plugin
const plugins = { "http://example.org/dataUsage": new UcpPlugin() }
// instantiate koreografeye policy executor
const policyExecutor = new PolicyExecutor(plugins)
// ucon storage
const uconRulesStorage = new ContainerUCRulesStorage(uconRulesContainer)
// load N3 Rules from a directory | TODO: utils are needed
const n3Rules: string[] = [readText('./rules/data-crud-rules.n3')!]
// instantiate the enforcer using the policy executor,
const ucpPatternEnforcement = new UcpPatternEnforcement(uconRulesStorage, n3Rules, new EyeJsReasoner([
        "--quiet",
        "--nope",
        "--pass"]), policyExecutor)

The code in crud_engine.ts evaluates 12 UconRequests using an instance of UconEnforcementDecision against a variety of ucon rule sets to verify the engine is working as intended.

When an evaluation fails, e.g. through changing the code, an N3 file will be created in the debug directory. This file will include the request (transformed to RDF and serialized to n-triples), the ucon rule(s) and the N3 interpretation rules. The contents of the file can then be used with the eye reasoner (either local or online).

An example of such file is crud_full, the conclusion of which can be locally tested with following command:

eye --quiet --nope --pass-only-new crud_full.n3 

With as conclusion:

@prefix fno: <https://w3id.org/function/ontology#>.
@prefix : <http://example.org/>.
@prefix acl: <http://www.w3.org/ns/auth/acl#>.

<urn:uuid:b5e1c037-8f35-41f7-a0dc-58ed0efe229e> a fno:Execution.
<urn:uuid:b5e1c037-8f35-41f7-a0dc-58ed0efe229e> fno:executes :dataUsage.
<urn:uuid:b5e1c037-8f35-41f7-a0dc-58ed0efe229e> :accessModesAllowed acl:Write.

As an extra, the same evaluation is also tested for a crud engine that also supports temporal policies. The only difference in that engine is that a second set of N3 interpretation rules are added to interpret temporal ucon rules.

Example: explanation after decision

In log_engine.ts, a UconEnforcementDecision component is initialised. Here, calculateAndExplainAccessModes is used to not only decide the access modes calculated, but also to add the explanation to why these access modes would be granted.

For this, another plugin (UCPLogPlugin) and rule interpretation (log-usage-rule.n3) is required.

The engine is initialised as follows:

import { EyeJsReasoner, readText } from "koreografeye";
import { UcpPlugin } from "./src/plugins/UCPPlugin";
import { ContainerUCRulesStorage } from "./src/storage/ContainerUCRulesStorage";
import { PolicyExecutor } from "./src/PolicyExecutor";

// load plugin
const plugins = { "http://example.org/dataUsageLog": new UCPLogPlugin() }
// instantiate koreografeye policy executor
const policyExecutor = new PolicyExecutor(plugins)
// ucon storage
const uconRulesStorage = new ContainerUCRulesStorage(uconRulesContainer)
// load N3 Rules from a directory | TODO: utils are needed
const n3Rules: string[] = [readText('./rules/log-usage-rule.n3')!]
// instantiate the enforcer using the policy executor,
const ucpPatternEnforcement = new UcpPatternEnforcement(uconRulesStorage, n3Rules, new EyeJsReasoner([
  "--quiet",
  "--nope",
  "--pass"]), policyExecutor)

When evaluating a UCONRequest, an Explanation is retrieved.

const explanation = await ucpDecide.calculateAndExplainAccessModes({
    subject: "https://woslabbi.pod.knows.idlab.ugent.be/profile/card#me",
    action: ["http://www.w3.org/ns/auth/acl#Read"],
    resource: "http://localhost:3000/test.ttl",
    owner: "http://localhost:3000/alice/profile/card#me"
});

An Explanation consists of four components:

Having the Explanation after an evaluation thus allows for logging with provenance/proof of why a certain action was granted at a certain time.

Some utility functions are added such that an explanation can be serialized to RDF:

import { Store } from "n3";

// use of explanationToRdf
const explanationStore: Store = explanationToRdf(explanation);

// use of serializeFullExplanation
const uconRules = await uconRulesStorage.getStore();
const serialized: string = serializeFullExplanation(explanation, uconRules, n3Rules.join('\n'));