Skip to content

Commit ab979bd

Browse files
megothrubensworks
authored andcommitted
Logic in patch-handler required use of throw
1 parent 82ad56f commit ab979bd

File tree

4 files changed

+39
-64
lines changed

4 files changed

+39
-64
lines changed

lib/acl-checker.js

Lines changed: 9 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -47,20 +47,13 @@ class ACLChecker {
4747
// aclCheck.checkAccess(acl.graph, this.resource)
4848

4949
// Check the resource's permissions
50-
<<<<<<< 2740f8873bfe7d7edcf0c2c31f927a106dc0abc7
51-
this.acl = this.acl || await this.getNearestACL().catch(err => {
52-
throw new HTTPError(500, `Found no ACL file:\n${err}`)
50+
const acl = await this.getNearestACL().catch(err => {
51+
this.messagesCached[cacheKey].push(new HTTPError(err.status || 500, err.message || err))
5352
})
54-
=======
55-
const acl = await this.getNearestACL()
56-
.catch(err => {
57-
this.messagesCached[cacheKey].push(new HTTPError(500, err))
58-
})
5953
if (!acl) {
6054
this.aclCached[cacheKey] = Promise.resolve(false)
6155
return this.aclCached[cacheKey]
6256
}
63-
>>>>>>> Trying another approach to acl.can
6457
// console.log('TEST', this.acl)
6558
const resource = rdf.sym(this.resource)
6659
// const directory = acl.isContainer ? this.resource : null
@@ -75,24 +68,17 @@ class ACLChecker {
7568
const agentOrigin = this.agentOrigin ? rdf.sym(this.agentOrigin) : null
7669
const trustedOrigins = this.trustedOrigins ? this.trustedOrigins.map(trustedOrigin => rdf.sym(trustedOrigin)) : null
7770
const accessDenied = aclCheck.accessDenied(acl.graph, resource, directory, aclFile, agent, modes, agentOrigin, trustedOrigins)
78-
console.log('BAR', accessDenied)
7971
if (accessDenied && user) {
80-
<<<<<<< 2740f8873bfe7d7edcf0c2c31f927a106dc0abc7
81-
throw new HTTPError(403, accessDenied)
82-
} else if (accessDenied) {
83-
throw new HTTPError(401, 'Unauthenticated')
84-
=======
85-
this.messagesCached[cacheKey].push(new HTTPError(403, `Access to ${this.resource} denied for ${user}: ${accessDenied}`))
72+
this.messagesCached[cacheKey].push(new HTTPError(403, `No permission: Access to ${this.resource} denied for ${user}: ${accessDenied}`))
8673
} else if (accessDenied) {
8774
this.messagesCached[cacheKey].push(new HTTPError(401, `Access to ${this.resource} requires authorization: ${accessDenied}`))
88-
>>>>>>> Trying another approach to acl.can
8975
}
9076
console.log('ACCESS ALLOWED', !accessDenied, user, '\n\n')
9177
this.aclCached[cacheKey] = Promise.resolve(!accessDenied)
92-
return this.aclCached
78+
return this.aclCached[cacheKey]
9379
}
9480

95-
async getError (mode, user) {
81+
async getError (user, mode) {
9682
const cacheKey = `${mode}-${user}`
9783
this.aclCached[cacheKey] = this.aclCached[cacheKey] || this.can(user, mode)
9884
const isAllowed = await this.aclCached[cacheKey]
@@ -123,26 +109,6 @@ class ACLChecker {
123109
// let directory = null
124110
// Create a cascade of reject handlers (one for each possible ACL)
125111
const possibleACLs = this.getPossibleACLs()
126-
<<<<<<< 2740f8873bfe7d7edcf0c2c31f927a106dc0abc7
127-
const nearestACL = possibleACLs.reduce((prevACL, acl) => {
128-
return prevACL.catch(() => new Promise((resolve, reject) => {
129-
this.fetch(acl, (err, graph) => {
130-
if (err && err.code !== 'ENOENT') {
131-
isContainer = true
132-
reject(err)
133-
} else {
134-
if (resource.endsWith('/' + this.suffix)) {
135-
isContainer = true
136-
}
137-
const relative = resource.replace(acl.replace(/[^/]+$/, ''), './')
138-
debug(`Using ACL ${acl} for ${relative}`)
139-
resolve({ acl, graph, isContainer })
140-
}
141-
})
142-
}))
143-
}, Promise.reject())
144-
return nearestACL.catch(e => { throw new Error(`No ACL resource found, searched in \n- ${possibleACLs.join('\n- ')}`) })
145-
=======
146112
const acls = [...possibleACLs]
147113
let returnAcl = null
148114
while (possibleACLs.length > 0 && !returnAcl) {
@@ -153,18 +119,18 @@ class ACLChecker {
153119
debug(`Using ACL ${acl} for ${relative}`)
154120
returnAcl = { acl, graph, isContainer }
155121
} catch (err) {
156-
if (err && err.code === 'ENOENT') {
122+
if (err && (err.code === 'ENOENT' || err.status === 404)) {
157123
isContainer = true
158-
return
124+
continue
159125
} else if (err) {
160-
console.error('ERROR IN getNearestACL', err)
126+
console.error('ERROR IN getNearestACL', err.code, err)
161127
debug(err)
162128
throw err
163129
}
164130
}
165131
}
166132
if (!returnAcl) {
167-
throw new Error(`No ACL found for ${resource}, searched in \n- ${acls.join('\n- ')}`)
133+
throw new HTTPError(403, `No ACL found for ${resource}, searched in \n- ${acls.join('\n- ')}`)
168134
}
169135
return returnAcl
170136
// const nearestACL = possibleACLs.reduce((prevACL, acl) => {
@@ -182,7 +148,6 @@ class ACLChecker {
182148
// }))
183149
// }, Promise.reject())
184150
// return nearestACL.catch(e => { throw new Error(`No ACL resource found, searched in \n- ${possibleACLs.join('\n- ')}`) })
185-
>>>>>>> Trying another approach to acl.can
186151
}
187152

188153
// Gets all possible ACL paths that apply to the resource

lib/handlers/allow.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ function allow (mode) {
5555
if (isAllowed) {
5656
return next()
5757
}
58-
const error = await req.acl.getError(mode, userId)
58+
const error = await req.acl.getError(userId, mode)
5959
console.log('ERROR', error)
6060
debug(`${mode} access denied to ${userId || '(none)'}: ${error.status} - ${error.message}`)
6161
res.status(error.status).send(error.message)

lib/handlers/patch.js

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -74,19 +74,20 @@ async function patchHandler (req, res, next) {
7474
function handler (req, res, next) {
7575
readEntity(req, res, () => patchHandler(req, res, next))
7676
}
77+
7778
const readEntity = bodyParser.text({ type: () => true })
7879

7980
// Reads the RDF graph in the given resource
8081
function readGraph (resource) {
8182
// Read the resource's file
8283
return new Promise((resolve, reject) =>
83-
fs.readFile(resource.path, {encoding: 'utf8'}, function (err, fileContents) {
84+
fs.readFile(resource.path, { encoding: 'utf8' }, function (err, fileContents) {
8485
if (err) {
8586
// If the file does not exist, assume empty contents
8687
// (it will be created after a successful patch)
8788
if (err.code === 'ENOENT') {
8889
fileContents = ''
89-
// Fail on all other errors
90+
// Fail on all other errors
9091
} else {
9192
return reject(error(500, `Original file read error: ${err}`))
9293
}
@@ -109,27 +110,38 @@ function readGraph (resource) {
109110
})
110111
}
111112

113+
112114
// Verifies whether the user is allowed to perform the patch on the target
113-
function checkPermission (request, patchObject) {
115+
async function checkPermission (request, patchObject) {
114116
// If no ACL object was passed down, assume permissions are okay.
115117
if (!request.acl) return Promise.resolve(patchObject)
116118
// At this point, we already assume append access,
117119
// as this can be checked upfront before parsing the patch.
118120
// Now that we know the details of the patch,
119121
// we might need to perform additional checks.
120-
let checks = []
122+
let modes = []
121123
const { acl, session: { userId } } = request
122124
// Read access is required for DELETE and WHERE.
123125
// If we would allows users without read access,
124126
// they could use DELETE or WHERE to trigger 200 or 409,
125127
// and thereby guess the existence of certain triples.
126128
// DELETE additionally requires write access.
127129
if (patchObject.delete) {
128-
checks = [acl.can(userId, 'Read'), acl.can(userId, 'Write')]
130+
modes = ['Read', 'Write']
131+
// checks = [acl.can(userId, 'Read'), acl.can(userId, 'Write')]
129132
} else if (patchObject.where) {
130-
checks = [acl.can(userId, 'Read')]
133+
modes = ['Read']
134+
// checks = [acl.can(userId, 'Read')]
135+
}
136+
const allowed = await Promise.all(modes.map(mode => acl.can(userId, mode)))
137+
const allAllowed = allowed.reduce((memo, allowed) => memo && allowed, true)
138+
if (!allAllowed) {
139+
const errors = await Promise.all(modes.map(mode => acl.getError(userId, mode)))
140+
const error = errors.filter(error => !!error)
141+
.reduce((prevErr, err) => prevErr.status > err.status ? prevErr : err, { status: 0 })
142+
throw error
131143
}
132-
return Promise.all(checks).then(() => patchObject)
144+
return Promise.resolve(patchObject)
133145
}
134146

135147
// Applies the patch to the RDF graph
@@ -161,7 +173,7 @@ function writeGraph (graph, resource, root, serverUri) {
161173
'User has exceeded their storage quota'))
162174
}
163175

164-
fs.writeFile(resource.path, serialized, {encoding: 'utf8'}, function (err) {
176+
fs.writeFile(resource.path, serialized, { encoding: 'utf8' }, function (err) {
165177
if (err) {
166178
return reject(error(500, `Failed to write file after patch: ${err}`))
167179
}

lib/header.js

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -110,27 +110,25 @@ function parseMetadataFromHeader (linkHeader) {
110110
}
111111

112112
// Adds a header that describes the user's permissions
113-
function addPermissions (req, res, next) {
113+
async function addPermissions (req, res, next) {
114114
const { acl, session } = req
115115
if (!acl) return next()
116116

117117
// Turn permissions for the public and the user into a header
118118
const resource = req.app.locals.ldp.resourceMapper.resolveUrl(req.hostname, req.path)
119-
Promise.all([
119+
const [publicPerms, userPerms] = await Promise.all([
120120
getPermissionsFor(acl, null, req),
121121
getPermissionsFor(acl, session.userId, req)
122122
])
123-
.then(([publicPerms, userPerms]) => {
124-
debug.ACL(`Permissions on ${resource} for ${session.userId || '(none)'}: ${userPerms}`)
125-
debug.ACL(`Permissions on ${resource} for public: ${publicPerms}`)
126-
res.set('WAC-Allow', `user="${userPerms}",public="${publicPerms}"`)
127-
})
128-
.then(next, next)
123+
debug.ACL(`Permissions on ${resource} for ${session.userId || '(none)'}: ${userPerms}`)
124+
debug.ACL(`Permissions on ${resource} for public: ${publicPerms}`)
125+
res.set('WAC-Allow', `user="${userPerms}",public="${publicPerms}"`)
126+
next()
129127
}
130128

131129
// Gets the permissions string for the given user and resource
132-
function getPermissionsFor (acl, user, req) {
130+
async function getPermissionsFor (acl, user, req) {
133131
const accesses = MODES.map(mode => acl.can(user, mode))
134-
return Promise.all(accesses)
135-
.then(allowed => PERMISSIONS.filter((_, i) => allowed[i]).join(' '))
132+
const allowed = await Promise.all(accesses)
133+
return PERMISSIONS.filter((mode, i) => allowed[i]).join(' ')
136134
}

0 commit comments

Comments
 (0)