Skip to content

Commit e7e7911

Browse files
committed
Expose the user's permissions through a header.
Closes #246.
1 parent fb7235d commit e7e7911

File tree

10 files changed

+108
-1
lines changed

10 files changed

+108
-1
lines changed

lib/header.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ module.exports.addLink = addLink
22
module.exports.addLinks = addLinks
33
module.exports.parseMetadataFromHeader = parseMetadataFromHeader
44
module.exports.linksHandler = linksHandler
5+
module.exports.addPermissions = addPermissions
56

67
var li = require('li')
78
var path = require('path')
@@ -11,6 +12,8 @@ var debug = require('./debug.js')
1112
var utils = require('./utils.js')
1213
var error = require('./http-error')
1314

15+
const PERMISSIONS = ['Read', 'Write', 'Append', 'Control']
16+
1417
function addLink (res, value, rel) {
1518
var oldLink = res.get('Link')
1619
if (oldLink === undefined) {
@@ -95,3 +98,28 @@ function parseMetadataFromHeader (linkHeader) {
9598
}
9699
return fileMetadata
97100
}
101+
102+
// Adds a header that describes the user's permissions
103+
function addPermissions (req, res, next) {
104+
const { acl, session, originalUrl } = req
105+
if (!acl) return next()
106+
107+
// Turn permissions for the public and the user into a header
108+
const resource = utils.uriAbs(req) + path
109+
Promise.all([
110+
getPermissionsFor(acl, null, resource),
111+
getPermissionsFor(acl, session.userId, resource)
112+
])
113+
.then(([publicPerms, userPerms]) => {
114+
res.set('WAC-Allow', `user="${userPerms}",public="${publicPerms}"`)
115+
})
116+
.then(next, next)
117+
}
118+
119+
// Gets the permissions string for the given user and resource
120+
function getPermissionsFor (acl, userId, resource) {
121+
return Promise.all(PERMISSIONS.map(perm =>
122+
new Promise(resolve => acl.can(userId, perm, resource, e => resolve(!e)))
123+
))
124+
.then(perms => PERMISSIONS.filter((p, i) => perms[i]).join(';').toLowerCase())
125+
}

lib/ldp-middleware.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ function LdpMiddleware (corsSettings) {
2222
}
2323

2424
router.copy('/*', allow('Write'), copy)
25-
router.get('/*', index, allow('Read'), get)
25+
router.get('/*', index, allow('Read'), header.addPermissions, get)
2626
router.post('/*', allow('Append'), post)
2727
router.patch('/*', allow('Write'), patch)
2828
router.put('/*', allow('Write'), put)

test/integration/header-test.js

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
const { expect } = require('chai')
2+
const path = require('path')
3+
const ldnode = require('../../index')
4+
const supertest = require('supertest')
5+
6+
const serverOptions = {
7+
root: path.join(__dirname, '../resources/headers'),
8+
idp: false,
9+
webid: true,
10+
sslKey: path.join(__dirname, '../keys/key.pem'),
11+
sslCert: path.join(__dirname, '../keys/cert.pem'),
12+
forceUser: 'https://ruben.verborgh.org/profile/#me'
13+
}
14+
15+
describe('Header handler', () => {
16+
let request
17+
18+
before(() => {
19+
const server = ldnode.createServer(serverOptions)
20+
request = supertest(server)
21+
})
22+
23+
describe('WAC-Allow', () => {
24+
describeHeaderTest('read/append for the public', {
25+
resource: '/public-ra',
26+
headers: { 'WAC-Allow': 'user="read;append",public="read;append"' }
27+
})
28+
29+
describeHeaderTest('read/write for the user, read for the public', {
30+
resource: '/user-rw-public-r',
31+
headers: { 'WAC-Allow': 'user="read;write;append",public="read"' }
32+
})
33+
34+
describeHeaderTest('read/write/append/control for the user, nothing for the public', {
35+
resource: '/user-rwac-public-0',
36+
headers: { 'WAC-Allow': 'user="read;write;append;control",public=""' }
37+
})
38+
})
39+
40+
function describeHeaderTest (label, { resource, headers }) {
41+
describe(`a resource that is ${label}`, () => {
42+
let response
43+
before(() => request.get(resource).then(res => { response = res }))
44+
45+
for (const header in headers) {
46+
const value = headers[header]
47+
it(`has a ${header} header of ${value}`, () => {
48+
expect(response.headers).to.have.property(header.toLowerCase(), value)
49+
})
50+
}
51+
})
52+
}
53+
})

test/resources/headers/index.html

Whitespace-only changes.

test/resources/headers/public-ra

Whitespace-only changes.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
@prefix acl: <http://www.w3.org/ns/auth/acl#>.
2+
@prefix foaf: <http://xmlns.com/foaf/0.1/>.
3+
4+
<#public> a acl:Authorization;
5+
acl:accessTo <./public-ra>;
6+
acl:agentClass foaf:Agent;
7+
acl:mode acl:Read, acl:Append.

test/resources/headers/user-rw-public-r

Whitespace-only changes.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
@prefix acl: <http://www.w3.org/ns/auth/acl#>.
2+
@prefix foaf: <http://xmlns.com/foaf/0.1/>.
3+
4+
<#owner> a acl:Authorization;
5+
acl:accessTo <./user-rw-public-r>;
6+
acl:agent <https://ruben.verborgh.org/profile/#me>;
7+
acl:mode acl:Read, acl:Write.
8+
9+
<#public> a acl:Authorization;
10+
acl:accessTo <./user-rw-public-r>;
11+
acl:agentClass foaf:Agent;
12+
acl:mode acl:Read.

test/resources/headers/user-rwac-public-0

Whitespace-only changes.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
@prefix acl: <http://www.w3.org/ns/auth/acl#>.
2+
@prefix foaf: <http://xmlns.com/foaf/0.1/>.
3+
4+
<#owner> a acl:Authorization;
5+
acl:accessTo <./user-rwac-public-0>;
6+
acl:agent <https://ruben.verborgh.org/profile/#me>;
7+
acl:mode acl:Read, acl:Write, acl:Append, acl:Delete, acl:Control.

0 commit comments

Comments
 (0)