Skip to content

Commit 0bbb3d3

Browse files
committed
Merge branch 'appendPutNewDocument' into issue#1743
2 parents e17beb8 + 47100fc commit 0bbb3d3

File tree

3 files changed

+78
-11
lines changed

3 files changed

+78
-11
lines changed

lib/handlers/put.js

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ async function handler (req, res, next) {
1919
return next(e)
2020
}
2121
// check for valid rdf content for auxiliary resource and /profile/card
22-
// in future we may check that /profile/card is a minimal valid WebID card
22+
// TODO check that /profile/card is a minimal valid WebID card
2323
if (isAuxiliary(req) || req.originalUrl === '/profile/card') {
2424
if (contentType === 'text/turtle') {
2525
return bodyParser.text({ type: () => true })(req, res, () => putValidRdf(req, res, next))
@@ -28,17 +28,51 @@ async function handler (req, res, next) {
2828
return putStream(req, res, next)
2929
}
3030

31+
// Verifies whether the user is allowed to perform Append PUT on the target
32+
async function checkPermission (request, resourceExists) {
33+
// If no ACL object was passed down, assume permissions are okay.
34+
if (!request.acl) return Promise.resolve()
35+
// At this point, we already assume append access,
36+
// we might need to perform additional checks.
37+
let modes = []
38+
// acl:default Write is required for PUT when Resource Exists
39+
if (resourceExists) modes = ['Write']
40+
// if (resourceExists && request.originalUrl.endsWith('.acl')) modes = ['Control']
41+
const { acl, session: { userId } } = request
42+
43+
const allowed = await Promise.all(modes.map(mode => acl.can(userId, mode, request.method, resourceExists)))
44+
const allAllowed = allowed.reduce((memo, allowed) => memo && allowed, true)
45+
if (!allAllowed) {
46+
// check owner with Control
47+
// const ldp = request.app.locals.ldp
48+
// if (request.path.endsWith('.acl') && userId === await ldp.getOwner(request.hostname)) return Promise.resolve()
49+
50+
const errors = await Promise.all(modes.map(mode => acl.getError(userId, mode)))
51+
const error = errors.filter(error => !!error)
52+
.reduce((prevErr, err) => prevErr.status > err.status ? prevErr : err, { status: 0 })
53+
return Promise.reject(error)
54+
}
55+
return Promise.resolve()
56+
}
57+
3158
// TODO could be renamed as putResource (it now covers container and non-container)
3259
async function putStream (req, res, next, stream = req) {
3360
const ldp = req.app.locals.ldp
61+
// try {
62+
// Obtain details of the target resource
63+
let resourceExists = true
64+
try {
65+
// First check if the file already exists
66+
await ldp.resourceMapper.mapUrlToFile({ url: req })
67+
} catch (err) {
68+
resourceExists = false
69+
}
3470
try {
35-
debug('test ' + req.get('content-type') + getContentType(req.headers))
71+
if (!req.originalUrl.endsWith('.acl')) await checkPermission(req, resourceExists)
3672
await ldp.put(req, stream, getContentType(req.headers))
37-
debug('succeded putting the file/folder')
3873
res.sendStatus(201)
3974
return next()
4075
} catch (err) {
41-
debug('error putting the file/folder:' + err.message)
4276
err.message = 'Can\'t write file/folder: ' + err.message
4377
return next(err)
4478
}

lib/ldp-middleware.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ function LdpMiddleware (corsSettings) {
2525
router.get('/*', index, allow('Read'), header.addPermissions, get)
2626
router.post('/*', allow('Append'), post)
2727
router.patch('/*', allow('Append'), patch)
28-
router.put('/*', allow('Write'), put)
28+
router.put('/*', allow('Append'), put)
2929
router.delete('/*', allow('Write'), del)
3030

3131
return router

test/integration/acl-oidc-test.js

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ describe('ACL with WebID+OIDC over HTTP', function () {
138138
done()
139139
})
140140
})
141-
it('user1 as solid:owner should let edit the .acl', function (done) {
141+
it('user1 as solid:owner should let edit the .acl', function (done) { // alain
142142
const options = createOptions('/empty-acl/.acl', 'user1', 'text/turtle')
143143
options.body = ''
144144
request.put(options, function (error, response, body) {
@@ -209,7 +209,7 @@ describe('ACL with WebID+OIDC over HTTP', function () {
209209
done()
210210
})
211211
})
212-
it('Should not create empty acl file', function (done) {
212+
it('Should not create empty acl file', function (done) { // alain
213213
const options = createOptions('/write-acl/empty-acl/another-empty-folder/.acl', 'user1', 'text/turtle')
214214
options.body = ''
215215
request.put(options, function (error, response, body) {
@@ -273,7 +273,7 @@ describe('ACL with WebID+OIDC over HTTP', function () {
273273
})
274274

275275
describe('no-control', function () {
276-
it('user1 as owner should edit acl file', function (done) {
276+
it('user1 as owner should edit acl file', function (done) { // alain
277277
const options = createOptions('/no-control/.acl', 'user1', 'text/turtle')
278278
options.body = '<#0>' +
279279
'\n a <http://www.w3.org/ns/auth/acl#Authorization>;' +
@@ -571,6 +571,27 @@ describe('ACL with WebID+OIDC over HTTP', function () {
571571
done()
572572
})
573573
})
574+
it('user1 should be able to PUT (which CREATEs) (non existent resource)', function (done) {
575+
const options = createOptions('/append-inherited/test1.ttl', 'user1')
576+
options.body = '<a> <b> <c> .\n'
577+
options.headers['content-type'] = 'text/turtle'
578+
request.put(options, function (error, response, body) {
579+
assert.equal(error, null)
580+
assert.equal(response.statusCode, 201)
581+
done()
582+
})
583+
})
584+
it('user2 should not be able to PUT with Append (existing resource)', function (done) {
585+
const options = createOptions('/append-inherited/test1.ttl', 'user2')
586+
options.body = '<a> <b> <c> .\n'
587+
options.headers['content-type'] = 'text/turtle'
588+
request.put(options, function (error, response, body) {
589+
assert.equal(error, null)
590+
assert.equal(response.statusCode, 403)
591+
assert.include(response.statusMessage, 'User Unauthorized')
592+
done()
593+
})
594+
})
574595
it('user1 should be able to access test file', function (done) {
575596
const options = createOptions('/append-acl/abc.ttl', 'user1')
576597
request.head(options, function (error, response, body) {
@@ -599,6 +620,16 @@ describe('ACL with WebID+OIDC over HTTP', function () {
599620
done()
600621
})
601622
})
623+
it('user2 should be able to PUT to (which CREATEs) a non existent resource', function (done) { // alain
624+
const options = createOptions('/append-inherited/new1.ttl', 'user1')
625+
options.body = '<a> <b> <c> .\n'
626+
options.headers['content-type'] = 'text/turtle'
627+
request.put(options, function (error, response, body) {
628+
assert.equal(error, null)
629+
assert.equal(response.statusCode, 201)
630+
done()
631+
})
632+
})
602633
it('user2 should not be able to access test file\'s ACL file', function (done) {
603634
const options = createOptions('/append-acl/abc.ttl.acl', 'user2', 'text/turtle')
604635
request.head(options, function (error, response, body) {
@@ -627,13 +658,13 @@ describe('ACL with WebID+OIDC over HTTP', function () {
627658
done()
628659
})
629660
})
630-
it('user2 (with append permission) cannot use PUT to append', function (done) {
661+
it('user2 (with append permission) cannot use PUT on an existing resource', function (done) {
631662
const options = createOptions('/append-acl/abc.ttl', 'user2', 'text/turtle')
632663
options.body = '<d> <e> <f> .\n'
633664
request.put(options, function (error, response, body) {
634665
assert.equal(error, null)
635666
assert.equal(response.statusCode, 403)
636-
assert.equal(response.statusMessage, 'User Unauthorized')
667+
assert.include(response.statusMessage, 'Can\'t write file/folder: User Unauthorized')
637668
done()
638669
})
639670
})
@@ -652,13 +683,15 @@ describe('ACL with WebID+OIDC over HTTP', function () {
652683
request.put(options, function (error, response, body) {
653684
assert.equal(error, null)
654685
assert.equal(response.statusCode, 401)
655-
assert.equal(response.statusMessage, 'Unauthenticated')
686+
assert.include(response.statusMessage, 'Can\'t write file/folder: Unauthenticated')
656687
done()
657688
})
658689
})
659690
after(function () {
660691
rm('/accounts-acl/tim.localhost/append-inherited/test.ttl')
692+
rm('/accounts-acl/tim.localhost/append-inherited/test1.ttl')
661693
rm('/accounts-acl/tim.localhost/append-inherited/new.ttl')
694+
rm('/accounts-acl/tim.localhost/append-inherited/new1.ttl')
662695
})
663696
})
664697

0 commit comments

Comments
 (0)