Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Append put new document #1746

Closed
wants to merge 3 commits into from
Closed
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
42 changes: 38 additions & 4 deletions lib/handlers/put.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ async function handler (req, res, next) {

const contentType = req.get('content-type')
// check for valid rdf content for auxiliary resource and /profile/card
// in future we may check that /profile/card is a minimal valid WebID card
// TODO check that /profile/card is a minimal valid WebID card
if (isAuxiliary(req) || req.originalUrl === '/profile/card') {
if (contentType === 'text/turtle') {
return bodyParser.text({ type: () => true })(req, res, () => putValidRdf(req, res, next))
Expand All @@ -21,17 +21,51 @@ async function handler (req, res, next) {
return putStream(req, res, next)
}

// Verifies whether the user is allowed to perform Append PUT on the target
async function checkPermission (request, resourceExists) {
// If no ACL object was passed down, assume permissions are okay.
if (!request.acl) return Promise.resolve()
// At this point, we already assume append access,
// we might need to perform additional checks.
let modes = []
// acl:default Write is required for PUT when Resource Exists
if (resourceExists) modes = ['Write']
// if (resourceExists && request.originalUrl.endsWith('.acl')) modes = ['Control']
const { acl, session: { userId } } = request

const allowed = await Promise.all(modes.map(mode => acl.can(userId, mode, request.method, resourceExists)))
const allAllowed = allowed.reduce((memo, allowed) => memo && allowed, true)
if (!allAllowed) {
// check owner with Control
// const ldp = request.app.locals.ldp
// if (request.path.endsWith('.acl') && userId === await ldp.getOwner(request.hostname)) return Promise.resolve()

const errors = await Promise.all(modes.map(mode => acl.getError(userId, mode)))
const error = errors.filter(error => !!error)
.reduce((prevErr, err) => prevErr.status > err.status ? prevErr : err, { status: 0 })
return Promise.reject(error)
}
return Promise.resolve()
}

// TODO could be renamed as putResource (it now covers container and non-container)
async function putStream (req, res, next, stream = req) {
const ldp = req.app.locals.ldp
// try {
// Obtain details of the target resource
let resourceExists = true
try {
// First check if the file already exists
await ldp.resourceMapper.mapUrlToFile({ url: req })
} catch (err) {
resourceExists = false
}
try {
debug('test ' + req.get('content-type') + getContentType(req.headers))
if (!req.originalUrl.endsWith('.acl')) await checkPermission(req, resourceExists)
await ldp.put(req, stream, getContentType(req.headers))
debug('succeded putting the file/folder')
res.sendStatus(201)
return next()
} catch (err) {
debug('error putting the file/folder:' + err.message)
err.message = 'Can\'t write file/folder: ' + err.message
return next(err)
}
Expand Down
2 changes: 1 addition & 1 deletion lib/ldp-middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ function LdpMiddleware (corsSettings) {
router.get('/*', index, allow('Read'), header.addPermissions, get)
router.post('/*', allow('Append'), post)
router.patch('/*', allow('Append'), patch)
router.put('/*', allow('Write'), put)
router.put('/*', allow('Append'), put)
router.delete('/*', allow('Write'), del)

return router
Expand Down
45 changes: 39 additions & 6 deletions test/integration/acl-oidc-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ describe('ACL with WebID+OIDC over HTTP', function () {
done()
})
})
it('user1 as solid:owner should let edit the .acl', function (done) {
it('user1 as solid:owner should let edit the .acl', function (done) { // alain
const options = createOptions('/empty-acl/.acl', 'user1', 'text/turtle')
options.body = ''
request.put(options, function (error, response, body) {
Expand Down Expand Up @@ -209,7 +209,7 @@ describe('ACL with WebID+OIDC over HTTP', function () {
done()
})
})
it('Should not create empty acl file', function (done) {
it('Should not create empty acl file', function (done) { // alain
const options = createOptions('/write-acl/empty-acl/another-empty-folder/.acl', 'user1', 'text/turtle')
options.body = ''
request.put(options, function (error, response, body) {
Expand Down Expand Up @@ -273,7 +273,7 @@ describe('ACL with WebID+OIDC over HTTP', function () {
})

describe('no-control', function () {
it('user1 as owner should edit acl file', function (done) {
it('user1 as owner should edit acl file', function (done) { // alain
const options = createOptions('/no-control/.acl', 'user1', 'text/turtle')
options.body = '<#0>' +
'\n a <http://www.w3.org/ns/auth/acl#Authorization>;' +
Expand Down Expand Up @@ -571,6 +571,27 @@ describe('ACL with WebID+OIDC over HTTP', function () {
done()
})
})
it('user1 should be able to PUT (which CREATEs) (non existent resource)', function (done) {
const options = createOptions('/append-inherited/test1.ttl', 'user1')
options.body = '<a> <b> <c> .\n'
options.headers['content-type'] = 'text/turtle'
request.put(options, function (error, response, body) {
assert.equal(error, null)
assert.equal(response.statusCode, 201)
done()
})
})
it('user2 should not be able to PUT with Append (existing resource)', function (done) {
const options = createOptions('/append-inherited/test1.ttl', 'user2')
options.body = '<a> <b> <c> .\n'
options.headers['content-type'] = 'text/turtle'
request.put(options, function (error, response, body) {
assert.equal(error, null)
assert.equal(response.statusCode, 403)
assert.include(response.statusMessage, 'User Unauthorized')
done()
})
})
it('user1 should be able to access test file', function (done) {
const options = createOptions('/append-acl/abc.ttl', 'user1')
request.head(options, function (error, response, body) {
Expand Down Expand Up @@ -599,6 +620,16 @@ describe('ACL with WebID+OIDC over HTTP', function () {
done()
})
})
it('user2 should be able to PUT to (which CREATEs) a non existent resource', function (done) { // alain
const options = createOptions('/append-inherited/new1.ttl', 'user1')
options.body = '<a> <b> <c> .\n'
options.headers['content-type'] = 'text/turtle'
request.put(options, function (error, response, body) {
assert.equal(error, null)
assert.equal(response.statusCode, 201)
done()
})
})
it('user2 should not be able to access test file\'s ACL file', function (done) {
const options = createOptions('/append-acl/abc.ttl.acl', 'user2', 'text/turtle')
request.head(options, function (error, response, body) {
Expand Down Expand Up @@ -627,13 +658,13 @@ describe('ACL with WebID+OIDC over HTTP', function () {
done()
})
})
it('user2 (with append permission) cannot use PUT to append', function (done) {
it('user2 (with append permission) cannot use PUT on an existing resource', function (done) {
const options = createOptions('/append-acl/abc.ttl', 'user2', 'text/turtle')
options.body = '<d> <e> <f> .\n'
request.put(options, function (error, response, body) {
assert.equal(error, null)
assert.equal(response.statusCode, 403)
assert.equal(response.statusMessage, 'User Unauthorized')
assert.include(response.statusMessage, 'Can\'t write file/folder: User Unauthorized')
done()
})
})
Expand All @@ -652,13 +683,15 @@ describe('ACL with WebID+OIDC over HTTP', function () {
request.put(options, function (error, response, body) {
assert.equal(error, null)
assert.equal(response.statusCode, 401)
assert.equal(response.statusMessage, 'Unauthenticated')
assert.include(response.statusMessage, 'Can\'t write file/folder: Unauthenticated')
done()
})
})
after(function () {
rm('/accounts-acl/tim.localhost/append-inherited/test.ttl')
rm('/accounts-acl/tim.localhost/append-inherited/test1.ttl')
rm('/accounts-acl/tim.localhost/append-inherited/new.ttl')
rm('/accounts-acl/tim.localhost/append-inherited/new1.ttl')
})
})

Expand Down