{"id":20666409,"url":"https://github.com/woutslabbinck/ucp-enforcement","last_synced_at":"2026-04-24T13:02:45.514Z","repository":{"id":203322450,"uuid":"707295103","full_name":"woutslabbinck/ucp-enforcement","owner":"woutslabbinck","description":"A playground for calculating access mode grants based on usage control rules and usage control rule interpreters","archived":false,"fork":false,"pushed_at":"2024-02-13T10:01:08.000Z","size":1837,"stargazers_count":2,"open_issues_count":3,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-10T12:55:56.865Z","etag":null,"topics":["access-control","usage-control","user-managed-access"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/woutslabbinck.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-10-19T15:49:34.000Z","updated_at":"2024-01-29T19:38:48.000Z","dependencies_parsed_at":null,"dependency_job_id":"9211e3f5-b999-49ed-8765-78d6740ac249","html_url":"https://github.com/woutslabbinck/ucp-enforcement","commit_stats":null,"previous_names":["woutslabbinck/ucp-enforcement"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/woutslabbinck/ucp-enforcement","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/woutslabbinck%2Fucp-enforcement","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/woutslabbinck%2Fucp-enforcement/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/woutslabbinck%2Fucp-enforcement/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/woutslabbinck%2Fucp-enforcement/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/woutslabbinck","download_url":"https://codeload.github.com/woutslabbinck/ucp-enforcement/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/woutslabbinck%2Fucp-enforcement/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32224413,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-24T10:26:35.452Z","status":"ssl_error","status_checked_at":"2026-04-24T10:25:27.643Z","response_time":64,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["access-control","usage-control","user-managed-access"],"created_at":"2024-11-16T19:37:23.815Z","updated_at":"2026-04-24T13:02:45.480Z","avatar_url":"https://github.com/woutslabbinck.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# (Preventive) Usage Control Decision/Enforcement playground\n\nA playground environment to calculate Access Modes based on ODRL Rules, an [UMA Client Request](https://docs.kantarainitiative.org/uma/wg/rec-oauth-uma-grant-2.0.html#rfc.section.3.3.1) and Koreografeye (with accompanying N3 Rules and plugin(s)).\nThis class will be used in the [UMA Authorisation Server](https://github.com/woutslabbinck/UMA/tree/main/packages/uma) that is being developed to work with the [Community Solid Server](https://github.com/CommunitySolidServer/CommunitySolidServer).\n\nThe UMA client request contains the following information\n* Resource Owner\n* Resource (the target resource)\n* Requesting Party\n* Requested Access Mode (decided by the Resource Server)\n\nIn [`main.ts`](./main.ts) is an example of how it works given.\n\nRunning that script is done as follows:\n\n```sh\nnpx ts-node main.ts\n```\n\nMore information on how this library works can be found in the [**tutorial**](./docs/getting-started.md).\n\n## How does it work\n\u003c!-- TODO: make architecture drawing of the components --\u003e\n\nThere are a couple of components:\n* Usage Control Rules storage: A storage containing instantiated concrete policies\n* N3 Rules storage: A storage containing a set of N3 rules where each such rule matches a concrete type of UCP to the context. \nThe result of which are a set of instructions for a **Koreografeye Plugin** .\n* A Plugin storage: A storage containing a set Koreografeye Plugins. (note, they are a bit different than the koreografeye policies as they can return `any` instead of just `boolean`)\n* An N3 Reasoner\n* A Plugin Executor\n* A UCP decision component: Which currently uses all of the above to **reason** over which **access modes** (actions) are granted based on a request.\n\nThis means that in an Authorization Server, this component could be used as a decision component.\n```typescript\nconst ucpDecide: UconEnforcementDecision = ...\nconst accessModes = await ucpDecide.calculateAccessModes({\n    subject: \"https://woslabbi.pod.knows.idlab.ugent.be/profile/card#me\",\n    action: [\"http://www.w3.org/ns/auth/acl#Read\"],\n    resource: \"http://localhost:3000/test.ttl\",\n    owner: \"http://localhost:3000/alice/profile/card#me\"\n});\nconsole.log(accessModes);\n```\n```sh\n\u003e [ 'read' ]\n```\n\n### Example: CRUD policy engine\n\nIn [crud_engine.ts](./crud_engine.ts), a `UconEnforcementDecision` component is initialised.\nThis means that CRUD action requests can be evaluated against a set of ucon rules and an ucon rules interpreter.\n\nThe **ucon rule interpretation** is a combination of N3 rules ([data-crud-rules.n3](./rules/data-crud-rules.n3)), a N3 Reasoner ([eye-js](https://github.com/eyereasoner/eye-js)), a plugin executor ([`PolicyExecutor`](./src/PolicyExecutor.ts)) and a plugin ([`UcpPlugin`](src/plugins/UCPPlugin.ts)). These components all work together as described in [Koreografeye](https://github.com/eyereasoner/Koreografeye).\n\nThe **set of ucon rules** can be dynamically generated and added through the ucon rules store through the functions `createPolicy` and `createTemporalPolicy` (see [crudUtil.ts](./crudUtil.ts)).\n\n#### Initialisation\n\n```ts\nimport { EyeJsReasoner, readText } from \"koreografeye\";\nimport { UcpPlugin } from \"./src/plugins/UCPPlugin\";\nimport { ContainerUCRulesStorage } from \"./src/storage/ContainerUCRulesStorage\";\nimport { PolicyExecutor } from \"./src/PolicyExecutor\";\n\n// load plugin\nconst plugins = { \"http://example.org/dataUsage\": new UcpPlugin() }\n// instantiate koreografeye policy executor\nconst policyExecutor = new PolicyExecutor(plugins)\n// ucon storage\nconst uconRulesStorage = new ContainerUCRulesStorage(uconRulesContainer)\n// load N3 Rules from a directory | TODO: utils are needed\nconst n3Rules: string[] = [readText('./rules/data-crud-rules.n3')!]\n// instantiate the enforcer using the policy executor,\nconst ucpPatternEnforcement = new UcpPatternEnforcement(uconRulesStorage, n3Rules, new EyeJsReasoner([\n        \"--quiet\",\n        \"--nope\",\n        \"--pass\"]), policyExecutor)\n```\n\n\n\nThe code in `crud_engine.ts` evaluates 12[ `UconRequest`](./src/request.ts)s using an instance of [`UconEnforcementDecision`](./src/UcpPatternEnforcement.ts) against a variety of ucon rule sets to verify the engine is working as intended.\n\nWhen an evaluation fails, e.g. through changing the code, an N3 file will be created in the `debug` directory.\nThis file will include the request (transformed to RDF and serialized to n-triples), the ucon rule(s) and the N3 interpretation rules.\nThe contents of the file can then be used with the eye reasoner (either local or [online](https://editor.notation3.org/)).\n\nAn example of such file is [crud_full](./crud_full.n3), the conclusion of which can be locally tested with following command: \n\n```sh\neye --quiet --nope --pass-only-new crud_full.n3 \n```\n\nWith as conclusion:\n\n```ttl\n@prefix fno: \u003chttps://w3id.org/function/ontology#\u003e.\n@prefix : \u003chttp://example.org/\u003e.\n@prefix acl: \u003chttp://www.w3.org/ns/auth/acl#\u003e.\n\n\u003curn:uuid:b5e1c037-8f35-41f7-a0dc-58ed0efe229e\u003e a fno:Execution.\n\u003curn:uuid:b5e1c037-8f35-41f7-a0dc-58ed0efe229e\u003e fno:executes :dataUsage.\n\u003curn:uuid:b5e1c037-8f35-41f7-a0dc-58ed0efe229e\u003e :accessModesAllowed acl:Write.\n```\n\nAs 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](./rules/data-crud-temporal.n3) are added to interpret temporal ucon rules.\n\n### Example: explanation after decision\n\nIn [log_engine.ts](./log_engine.ts), a `UconEnforcementDecision` component is initialised. \nHere, `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.\n\nFor this, another plugin ([UCPLogPlugin](./src/plugins/UCPLogPlugin.ts)) and rule interpretation ([log-usage-rule.n3](./rules/log-usage-rule.n3)) is required.\n\nThe engine is initialised as follows:\n\n```js\nimport { EyeJsReasoner, readText } from \"koreografeye\";\nimport { UcpPlugin } from \"./src/plugins/UCPPlugin\";\nimport { ContainerUCRulesStorage } from \"./src/storage/ContainerUCRulesStorage\";\nimport { PolicyExecutor } from \"./src/PolicyExecutor\";\n\n// load plugin\nconst plugins = { \"http://example.org/dataUsageLog\": new UCPLogPlugin() }\n// instantiate koreografeye policy executor\nconst policyExecutor = new PolicyExecutor(plugins)\n// ucon storage\nconst uconRulesStorage = new ContainerUCRulesStorage(uconRulesContainer)\n// load N3 Rules from a directory | TODO: utils are needed\nconst n3Rules: string[] = [readText('./rules/log-usage-rule.n3')!]\n// instantiate the enforcer using the policy executor,\nconst ucpPatternEnforcement = new UcpPatternEnforcement(uconRulesStorage, n3Rules, new EyeJsReasoner([\n  \"--quiet\",\n  \"--nope\",\n  \"--pass\"]), policyExecutor)\n```\nWhen evaluating a UCONRequest, an **Explanation** is retrieved.\n\n```ts\nconst explanation = await ucpDecide.calculateAndExplainAccessModes({\n    subject: \"https://woslabbi.pod.knows.idlab.ugent.be/profile/card#me\",\n    action: [\"http://www.w3.org/ns/auth/acl#Read\"],\n    resource: \"http://localhost:3000/test.ttl\",\n    owner: \"http://localhost:3000/alice/profile/card#me\"\n});\n```\n\nAn **Explanation** consists of four components:\n\n- **decision**: This is the same as the grants (array of access modes) from `calculateAccessModes`. It is the **result** of the evaluation\n- **request**: The input request\n- **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) -\u003e grants$\n- **algorithm**: Which algorithm is used to interpret the set of conclusions\n  - Note: only the **union** operator is currently implemented. That is: $\\forall c \\in C. grant \\in c \\Rightarrow grant \\in D$ \u003c/br\u003e\n    For all conclusions in **Conclusions**, if a grant is in conclusion, then it is part of the list of grants in **Decision**.\n\nHaving the **Explanation** after an evaluation thus allows for logging with provenance/proof of why a certain action was granted at a certain time.\n\nSome utility functions are added such that an explanation can be serialized to RDF:\n\n- `explanationToRdf`: transforms the Javascript object Explanation to an [N3 Store](https://github.com/rdfjs/N3.js?tab=readme-ov-file#storing) containing the explanation information\n- `serializeFullExplanation`: Serializes the explanation (which contains the request), ucon policies and N3 interpretation to [Notation3](https://w3c.github.io/N3/spec/).\n\n```ts\nimport { Store } from \"n3\";\n\n// use of explanationToRdf\nconst explanationStore: Store = explanationToRdf(explanation);\n\n// use of serializeFullExplanation\nconst uconRules = await uconRulesStorage.getStore();\nconst serialized: string = serializeFullExplanation(explanation, uconRules, n3Rules.join('\\n'));\n\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwoutslabbinck%2Fucp-enforcement","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwoutslabbinck%2Fucp-enforcement","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwoutslabbinck%2Fucp-enforcement/lists"}