Skip to content

Commit 3c3f87e

Browse files
committed
Added getTrustedModesForOrigin and added optional param for accessDenied
Makes use of the proposed feature app:trustedApp
1 parent df7eb09 commit 3c3f87e

File tree

4 files changed

+108
-9
lines changed

4 files changed

+108
-9
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
"postversion": "git push --follow-tags",
1616
"prepublish": "npm test && npm run build",
1717
"standard": "standard *.js src/*.js",
18-
"tape": "tape test/unit/check-access-test.js test/unit/access-denied-test.js test/unit/configure-logger-test.js",
18+
"tape": "tape test/unit/check-access-test.js test/unit/access-denied-test.js test/unit/configure-logger-test.js test/unit/get-trusted-modes-for-origin-test.js",
1919
"test": "npm run standard && npm run tape"
2020
},
2121
"repository": {

src/acl-check.js

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ function publisherTrustedApp (kb, doc, aclDoc, modesRequired, origin, docAuths)
2020
// modesRequired.every(mode => appAuths.some(auth => kb.holds(auth, ACL('mode'), mode, aclDoc)))
2121
}
2222

23-
function accessDenied (kb, doc, directory, aclDoc, agent, modesRequired, origin, trustedOrigins) {
23+
function accessDenied (kb, doc, directory, aclDoc, agent, modesRequired, origin, trustedOrigins, originTrustedModes = []) {
2424
log(`accessDenied: checking access to ${doc} by ${agent} and origin ${origin}`)
25-
let modeURIorReasons = modesAllowed(kb, doc, directory, aclDoc, agent, origin, trustedOrigins)
25+
const modeURIorReasons = modesAllowed(kb, doc, directory, aclDoc, agent, origin, trustedOrigins, originTrustedModes)
2626
let ok = false
2727
log('accessDenied: modeURIorReasons: ' + JSON.stringify(Array.from(modeURIorReasons)))
2828
modesRequired.forEach(mode => {
@@ -44,6 +44,36 @@ function accessDenied (kb, doc, directory, aclDoc, agent, modesRequired, origin,
4444
return ok
4545
}
4646

47+
async function getTrustedModesForOrigin (kb, agent, origin) {
48+
if (!kb || !origin) {
49+
return Promise.resolve({})
50+
}
51+
const queryString = `
52+
SELECT ?mode WHERE {
53+
${agent} ${ACL('trustedApp')} ?trustedOrigin.
54+
?trustedOrigin ${ACL('origin')} ${origin};
55+
${ACL('mode')} ?mode .
56+
}`
57+
const results = await query(queryString, kb)
58+
return results.map(result => result['?mode'])
59+
}
60+
61+
async function query (queryString, store) {
62+
return new Promise((resolve, reject) => {
63+
try {
64+
const query = $rdf.SPARQLToQuery(queryString, true, store)
65+
const results = []
66+
store.query(query, (result) => {
67+
results.push(result)
68+
}, null, () => {
69+
resolve(results)
70+
})
71+
} catch (err) {
72+
reject(err)
73+
}
74+
})
75+
}
76+
4777
/* Function checkAccess
4878
** @param kb A quadstore
4979
** @param doc the resource (A named node) or directory for which ACL applies
@@ -52,7 +82,7 @@ function checkAccess (kb, doc, directory, aclDoc, agent, modesRequired, origin,
5282
return !accessDenied(kb, doc, directory, aclDoc, agent, modesRequired, origin, trustedOrigins)
5383
}
5484

55-
function modesAllowed (kb, doc, directory, aclDoc, agent, origin, trustedOrigins) {
85+
function modesAllowed (kb, doc, directory, aclDoc, agent, origin, trustedOrigins, originTrustedModes = []) {
5686
var auths
5787
if (!directory) { // Normal case, ACL for a file
5888
auths = kb.each(null, ACL('accessTo'), doc, aclDoc)
@@ -62,7 +92,7 @@ function modesAllowed (kb, doc, directory, aclDoc, agent, origin, trustedOrigins
6292
auths = auths.concat(kb.each(null, ACL('defaultForNew'), directory, null)) // Deprecated but keep for ages
6393
log(` ${auths.length} default authentications about ${directory} in ${aclDoc}`)
6494
}
65-
if (origin && trustedOrigins && trustedOriginsIncludeOrigin(trustedOrigins, origin)) {
95+
if (origin && trustedOrigins && nodesIncludeNode(trustedOrigins, origin)) {
6696
log('Origin ' + origin + ' is trusted')
6797
origin = null // stop worrying about origin
6898
log(` modesAllowed: Origin ${origin} is trusted.`)
@@ -108,6 +138,10 @@ function modesAllowed (kb, doc, directory, aclDoc, agent, origin, trustedOrigins
108138
log(' Origin check not needed: no origin.')
109139
return false
110140
}
141+
if (originTrustedModes && originTrustedModes.length > 0) {
142+
log(` Origin might have access (${originTrustedModes.join(', ')})`)
143+
return false
144+
}
111145
if (originOK(auth, origin)) {
112146
log(' Origin check succeeded.')
113147
return false
@@ -125,6 +159,9 @@ function modesAllowed (kb, doc, directory, aclDoc, agent, origin, trustedOrigins
125159
modeURIorReasons.add(agentAndAppStatus)
126160
} else {
127161
let modes = kb.each(auth, ACL('mode'), null, aclDoc)
162+
if (originTrustedModes && originTrustedModes.length > 0) {
163+
modes = modes.filter(mode => nodesIncludeNode(originTrustedModes, mode))
164+
}
128165
modes.forEach(mode => {
129166
log(' Mode allowed: ' + mode)
130167
modeURIorReasons.add(mode.uri)
@@ -134,9 +171,8 @@ function modesAllowed (kb, doc, directory, aclDoc, agent, origin, trustedOrigins
134171
return modeURIorReasons
135172
}
136173

137-
function trustedOriginsIncludeOrigin (trustedOrigins, origin) {
138-
return trustedOrigins.filter(
139-
trustedOrigin => trustedOrigin.termType === origin.termType && trustedOrigin.value === origin.value).length > 0
174+
function nodesIncludeNode (nodes, node) {
175+
return nodes.some(trustedOrigin => trustedOrigin.termType === node.termType && trustedOrigin.value === node.value)
140176
}
141177

142178
function configureLogger (logger) {
@@ -147,9 +183,10 @@ function log (...msgs) {
147183
return (_logger || console.log).apply(_logger, msgs)
148184
}
149185

186+
module.exports.accessDenied = accessDenied
150187
module.exports.checkAccess = checkAccess
151188
module.exports.configureLogger = configureLogger
189+
module.exports.getTrustedModesForOrigin = getTrustedModesForOrigin
152190
module.exports.log = log
153-
module.exports.accessDenied = accessDenied
154191
module.exports.modesAllowed = modesAllowed
155192
module.exports.publisherTrustedApp = publisherTrustedApp

test/unit/access-denied-test.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const $rdf = require('rdflib')
66

77
const ACL = $rdf.Namespace('http://www.w3.org/ns/auth/acl#')
88
const FOAF = $rdf.Namespace('http://xmlns.com/foaf/0.1/')
9+
const ALICE = $rdf.Namespace('https://alice.example.com/')
910

1011
const prefixes = `@prefix acl: <http://www.w3.org/ns/auth/acl#> .
1112
@prefix foaf: <http://xmlns.com/foaf/0.1/>.
@@ -403,3 +404,34 @@ test('aclCheck accessDenied() test - With trustedOrigins', t => {
403404
t.end()
404405
})
405406

407+
test('aclCheck accessDenied() test - with use of acl:trustedApp', t => {
408+
const resource = ALICE('docs/file1')
409+
const aclDoc = ALICE('docs/.acl')
410+
const aclUrl = aclDoc.uri
411+
412+
const origin = $rdf.sym('https://apps.example.com')
413+
const aclStore = $rdf.graph()
414+
// grants read, write and control access to Alice
415+
const ACLtext = `${prefixes}
416+
<#auth> a acl:Authorization;
417+
acl:mode acl:Read, acl:Write, acl:Control;
418+
acl:agent alice:me;
419+
acl:accessTo ${resource} .
420+
`
421+
$rdf.parse(ACLtext, aclStore, aclUrl, 'text/turtle')
422+
423+
const agent = alice
424+
const directory = null
425+
const trustedOrigins = []
426+
const originTrustedModes = [ACL('Read'), ACL('Write')]
427+
428+
const readWriteModeRequired = [ACL('Read'), ACL('Write')]
429+
const readWriteModeResult = aclLogic.accessDenied(aclStore, resource, directory, aclDoc, agent, readWriteModeRequired, origin, trustedOrigins, originTrustedModes)
430+
t.ok(!readWriteModeResult, 'Should get access to modes when origin is listed as trusted app')
431+
432+
const controlModeRequired = [ACL('Control')]
433+
const controlModeResult = aclLogic.accessDenied(aclStore, resource, directory, aclDoc, agent, controlModeRequired, origin, trustedOrigins, originTrustedModes)
434+
t.ok(controlModeResult, 'All Required Access Modes Not Granted', 'Correct reason')
435+
436+
t.end()
437+
})
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
'use strict'
2+
3+
const test = require('tape')
4+
const aclLogic = require('../../src/acl-check')
5+
const $rdf = require('rdflib')
6+
7+
const ACL = $rdf.Namespace('http://www.w3.org/ns/auth/acl#')
8+
const ALICE = $rdf.Namespace('https://alice.example.com/')
9+
const alice = ALICE('#me')
10+
11+
const prefixes = `
12+
@prefix acl: ${ACL()} .
13+
@prefix alice: ${ALICE('#')} .
14+
`
15+
16+
test('aclCheck getTrustedModesForOirign() test', t => {
17+
const origin = $rdf.sym('https://apps.example.com')
18+
const agent = alice
19+
const agentStore = $rdf.graph()
20+
const agentText = `${prefixes}
21+
${agent} acl:trustedApp [ acl:origin ${origin};
22+
acl:mode acl:Read, acl:Write].
23+
`
24+
$rdf.parse(agentText, agentStore, agent.uri, 'text/turtle')
25+
26+
aclLogic.getTrustedModesForOrigin(agentStore, agent, origin).then(result => {
27+
t.deepEqual(result, [ACL('Read'), ACL('Write')], 'Should get a list of modes')
28+
t.end()
29+
})
30+
})

0 commit comments

Comments
 (0)