Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ This example demonstrates how to parse a turtle string into an AclDoc object, th
```javascript
const SolidAclParser = require('SolicAclParser')

const webId = 'https://solid.example.org/profile/card#me'
const webId = 'https://pod.example.org/profile/card#me'
const aclUrl = 'https://pod.example.org/private/file.ext.acl'
const fileUrl = 'https://pod.example.org/private/file.ext'
const turtle = `
@prefix acl: <http://www.w3.org/ns/auth/acl#>.
@prefix foaf: <http://xmlns.com/foaf/0.1/>.

<#authorization2>
<#Read-0>
a acl:Authorization;
acl:agentClass foaf:Agent; # everyone
acl:mode acl:Read; # has Read-only access
Expand Down Expand Up @@ -52,12 +52,12 @@ Output turtle
@prefix acl: <http://www.w3.org/ns/auth/acl#>.
@prefix foaf: <http://xmlns.com/foaf/0.1/>.

<https://pod.example.org/private/file.ext.acl#authorization2> a acl:Authorization;
<#Read-0> a acl:Authorization;
acl:agentClass foaf:Agent;
acl:accessTo <https://pod.example.org/private/file.ext>;
acl:accessTo <./file.ext>;
acl:mode acl:Read.
<solid-acl-parser-rule-0> a acl:Authorization;
acl:agent <https://solid.example.org/profile/card#me>;
acl:accessTo <https://pod.example.org/private/file.ext>;
<#WriteControl-0> a acl:Authorization;
acl:agent </profile/card#me>;
acl:accessTo <./file.ext>;
acl:mode acl:Write, acl:Control.
```
14 changes: 7 additions & 7 deletions docs/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,14 @@ This example demonstrates how to parse a turtle string into an AclDoc object, th
*Tip: You can copy paste all complete example like this [here](./run.html ':ignore') to run it in CodePen*

```javascript
const webId = 'https://solid.example.org/profile/card#me'
const webId = 'https://pod.example.org/profile/card#me'
const aclUrl = 'https://pod.example.org/private/file.ext.acl'
const fileUrl = 'https://pod.example.org/private/file.ext'
const turtle = `
@prefix acl: <http://www.w3.org/ns/auth/acl#>.
@prefix foaf: <http://xmlns.com/foaf/0.1/>.

<#authorization2>
<#Read-0>
a acl:Authorization;
acl:agentClass foaf:Agent; # everyone
acl:mode acl:Read; # has Read-only access
Expand Down Expand Up @@ -74,13 +74,13 @@ Output turtle
@prefix acl: <http://www.w3.org/ns/auth/acl#>.
@prefix foaf: <http://xmlns.com/foaf/0.1/>.

<https://pod.example.org/private/file.ext.acl#authorization2> a acl:Authorization;
<#Read-0> a acl:Authorization;
acl:agentClass foaf:Agent;
acl:accessTo <https://pod.example.org/private/file.ext>;
acl:accessTo <./file.ext>;
acl:mode acl:Read.
<https://pod.example.org/private/file.ext#solid-acl-parser-rule-0> a acl:Authorization;
acl:agent <https://solid.example.org/profile/card#me>;
acl:accessTo <https://pod.example.org/private/file.ext>;
<#WriteControl-0> a acl:Authorization;
acl:agent </profile/card#me>;
acl:accessTo <./file.ext>;
acl:mode acl:Write, acl:Control.
```

Expand Down
32 changes: 28 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "solid-acl-parser",
"version": "0.1.0",
"version": "0.2.0",
"keywords": [
"solid",
"acl",
Expand Down Expand Up @@ -44,8 +44,10 @@
"dependencies": {
"@babel/runtime-corejs3": "^7.7.4",
"@types/n3": "^1.1.1",
"@types/url-parse": "^1.4.3",
"core-js": "^3.4.5",
"n3": "^1.3.3"
"n3": "^1.3.4",
"url-parse": "^1.4.7"
},
"standard": {
"parser": "@typescript-eslint/parser",
Expand Down
40 changes: 29 additions & 11 deletions src/AclDoc.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import Agents, { AgentsCastable } from './Agents'
import Permissions, { PermissionsCastable, permissionString } from './Permissions'
import Permissions, { PermissionsCastable, permissionString, permissionLinks } from './Permissions'
import AclRule from './AclRule'
import { iterableEquals } from './utils'
import { Quad } from 'n3';
import { Quad } from 'n3'

/**
* @module AclDoc
Expand All @@ -26,7 +26,7 @@ class AclDoc {
public readonly strict: boolean
public rules: Record<string, AclRule>
public otherQuads: Quad[]

constructor ({ accessTo, strict = true }: AclDocOptions) {
this.accessTo = accessTo
this.strict = strict
Expand All @@ -47,8 +47,9 @@ class AclDoc {
* // addRule uses AclRule.from which means we could use following too
* doc.addRule([READ, WRITE], 'https://my.web.id/#me')
*/
addRule (firstVal: AclRule|PermissionsCastable, agents?: Agents, { subjectId = this._getNewSubjectId() }: AddRuleOptions = {}) {
addRule (firstVal: AclRule|PermissionsCastable, agents?: Agents, { subjectId }: AddRuleOptions = {}) {
const rule = this._ruleFromArgs(firstVal, agents)
subjectId = this._normalizedSubectId(subjectId || this._getNewSubjectId(rule))
this.rules[subjectId] = rule

return this
Expand All @@ -61,7 +62,7 @@ class AclDoc {
addDefaultRule (firstVal: AclRule|PermissionsCastable, agents?: Agents, options: AddRuleOptions = {}) {
const rule = this._ruleFromArgs(firstVal, agents)
rule.default = this.accessTo

return this.addRule(rule, undefined, options)
}

Expand Down Expand Up @@ -101,7 +102,7 @@ class AclDoc {
* @description Get the rule with this subject id
*/
getRuleBySubjectId (subjectId: string): AclRule|undefined {
return this.rules[subjectId]
return this.rules[this._normalizedSubectId(subjectId)]
}

/**
Expand Down Expand Up @@ -141,7 +142,7 @@ class AclDoc {
delete this.rules[subjectId]

for (const newRule of newRules) {
const newSubjectId = this._getNewSubjectId(subjectId)
const newSubjectId = this._getNewSubjectId(newRule, subjectId)
this.rules[newSubjectId] = newRule
}
}
Expand Down Expand Up @@ -281,19 +282,36 @@ class AclDoc {
* @description Get an unused subject id
* @param {string} [base] - The newly generated id will begin with this base id
*/
_getNewSubjectId (base = this._defaultSubjectId) {
_getNewSubjectId (rule: AclRule, base = this._defaultSubjectIdForRule(rule)) {
const digitMatches = base.match(/[\d]*$/) || ['0']
let index = Number(digitMatches[0]) // Last positive number; 0 if not ending with number
base = base.replace(/[\d]*$/, '')

while (this.rules.hasOwnProperty(base + index)) {
while (this._containsSubjectId(base + index)) {
index++
}
return base + index
}

get _defaultSubjectId () {
return this.accessTo + '#solid-acl-parser-rule-'
_containsSubjectId (subjectId: string) {
return this.rules.hasOwnProperty(this._normalizedSubectId(subjectId))
}

_normalizedSubectId (subjectId: string) {
if (!subjectId.startsWith(this.accessTo)) {
return subjectId
}
return subjectId.includes('#') ? subjectId.substr(subjectId.lastIndexOf('#')) : subjectId
}

_defaultSubjectIdForRule (rule: AclRule) {
let id = '#'
id += Object.entries(permissionLinks)
.filter(([name, permission]) => rule.permissions.has(permission))
.map(([name]) => name[0] + name.substr(1).toLowerCase())
.join('')
if (rule.default || rule.defaultForNew) { id += 'Default' }
return id + '-'
}
}

Expand Down
26 changes: 15 additions & 11 deletions src/AclParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import * as N3 from 'n3'
import AclDoc from './AclDoc'
import prefixes from './prefixes'
import AclRule from './AclRule'
import { parseTurtle } from './utils'
import { permissionString } from './Permissions';
import { parseTurtle, makeRelativeIfPossible } from './utils'
import { permissionString } from './Permissions'

/**
* @module AclParser
Expand Down Expand Up @@ -57,13 +57,14 @@ const types = {
class AclParser {
private readonly parser: N3.N3Parser
private readonly accessTo: string

private readonly aclUrl: string

constructor ({ fileUrl, aclUrl }: AclParserOptions) {
this.parser = new N3.Parser({ baseIRI: aclUrl })
this.accessTo = fileUrl
this.aclUrl = aclUrl
}


async turtleToAclDoc (aclTurtle: string) {
const data = await parseTurtle(this.parser, aclTurtle)
const doc = new AclDoc({ accessTo: this.accessTo })
Expand Down Expand Up @@ -180,25 +181,27 @@ class AclParser {
_ruleToQuads (subjectId: string, rule: AclRule) {
const { DataFactory: { quad, namedNode } } = N3
const quads = []
const relative = (url: string) => makeRelativeIfPossible(this.aclUrl, url)

quads.push(quad(
namedNode(subjectId),
namedNode(predicates.type),
namedNode(types.authorization)
))

// Agents
for (const webId of rule.agents.webIds) {
quads.push(quad(
namedNode(subjectId),
namedNode(predicates.agent),
namedNode(webId)
namedNode(relative(webId))
))
}
for (const group of rule.agents.groups) {
quads.push(quad(
namedNode(subjectId),
namedNode(predicates.agentGroup),
namedNode(group)
namedNode(relative(group))
))
}
if (rule.agents.public) {
Expand All @@ -215,29 +218,30 @@ class AclParser {
namedNode(agentClasses.authenticated)
))
}
// accessTo

// Targets
if (rule.accessTo) {
quads.push(quad(
namedNode(subjectId),
namedNode(predicates.accessTo),
namedNode(rule.accessTo)
namedNode(relative(rule.accessTo))
))
}
// Provides default permissions for contained items?
if (typeof rule.default !== 'undefined') {
quads.push(quad(
namedNode(subjectId),
namedNode(predicates.default),
namedNode(rule.default)
namedNode(relative(rule.default))
))
}
if (typeof rule.defaultForNew !== 'undefined') {
quads.push(quad(
namedNode(subjectId),
namedNode(predicates.defaultForNew),
namedNode(rule.defaultForNew)
namedNode(relative(rule.defaultForNew))
))
}

// Permissions
for (const permission of rule.permissions) {
quads.push(quad(
Expand Down
Loading