Skip to content
Merged
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
5 changes: 2 additions & 3 deletions lib/acl-checker.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,8 @@ class ACLChecker {
if (!returnAcl) {
throw new HTTPError(500, `No ACL found for ${resource}, searched in \n- ${acls.join('\n- ')}`)
}
const groupUrls = returnAcl.graph
.statementsMatching(null, ACL('agentGroup'), null)
.map(node => node.object.value.split('#')[0])
const groupNodes = returnAcl.graph.statementsMatching(null, ACL('agentGroup'), null)
const groupUrls = groupNodes.map(node => node.object.value.split('#')[0])
await Promise.all(groupUrls.map(groupUrl => {
this.requests[groupUrl] = this.requests[groupUrl] || this.fetch(groupUrl, returnAcl.graph)
return this.requests[groupUrl]
Expand Down
16 changes: 15 additions & 1 deletion lib/handlers/allow.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,28 @@ function allow (mode, checkPermissionsForDirectory) {
trustedOrigins.push(ldp.serverUri)
}
// Obtain and store the ACL of the requested resource
req.acl = ACL.createFromLDPAndRequest(rootUrl + resourcePath, ldp, req)
const resourceUrl = rootUrl + resourcePath
req.acl = ACL.createFromLDPAndRequest(resourceUrl, ldp, req)

// Ensure the user has the required permission
const userId = req.session.userId
const isAllowed = await req.acl.can(userId, mode)
if (isAllowed) {
return next()
}
if (mode === 'Read' && (resourcePath === '' || resourcePath === '/')) {
// This is a hack to make NSS check the ACL for representation that is served for root (if any)
// See https://github.com/solid/node-solid-server/issues/1063 for more info
const representationUrl = await ldp.resourceMapper.getRepresentationUrlForResource(resourceUrl)
if (representationUrl.endsWith('index.html')) {
// We ONLY want to do this when the representation we return is a HTML file
req.acl = ACL.createFromLDPAndRequest(representationUrl, ldp, req)
const representationIsAllowed = await req.acl.can(userId, mode)
if (representationIsAllowed) {
return next()
}
}
}
const error = await req.acl.getError(userId, mode)
debug(`${mode} access denied to ${userId || '(none)'}: ${error.status} - ${error.message}`)
next(error)
Expand Down
19 changes: 19 additions & 0 deletions lib/resource-mapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,25 @@ class ResourceMapper {
return { path, contentType: contentType || this._defaultContentType }
}

async getRepresentationUrlForResource (resourceUrl) {
let fullPath = this.getFullPath(resourceUrl)
let isIndex = fullPath.endsWith('/')

// Append index filename if the URL ends with a '/'
if (isIndex) {
fullPath += this._indexFilename
}

// Read all files in the corresponding folder
const filename = fullPath.substr(fullPath.lastIndexOf('/') + 1)
const folder = fullPath.substr(0, fullPath.length - filename.length)
const files = await this._readdir(folder)

// Find a file with the same name (minus the dollar extension)
let match = (files.find(f => this._removeDollarExtension(f) === filename || (isIndex && f.startsWith(this._indexFilename + '.'))))
return `${resourceUrl}${match || ''}`
}

// Maps a given server file to a URL
async mapFileToUrl ({ path, hostname }) {
// Determine the URL by chopping off everything after the dollar sign
Expand Down
12 changes: 6 additions & 6 deletions test/integration/authentication-oidc-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ describe('Authentication API (OIDC)', () => {
describe('with that cookie and a non-matching origin', () => {
let response
before(done => {
alice.get('/')
alice.get('/private-for-owner.txt')
.set('Cookie', cookie)
.set('Origin', bobServerUri)
.end((err, res) => {
Expand Down Expand Up @@ -234,7 +234,7 @@ describe('Authentication API (OIDC)', () => {
describe('with that cookie and our origin', () => {
let response
before(done => {
alice.get('/')
alice.get('/private-for-owner.txt')
.set('Cookie', cookie)
.set('Origin', 'https://some.other.domain.com')
.end((err, res) => {
Expand All @@ -252,7 +252,7 @@ describe('Authentication API (OIDC)', () => {
describe('without that cookie but with our origin', () => {
let response
before(done => {
alice.get('/')
alice.get('/private-for-owner.txt')
.set('Origin', aliceServerUri)
.end((err, res) => {
response = res
Expand Down Expand Up @@ -324,7 +324,7 @@ describe('Authentication API (OIDC)', () => {
describe('without that cookie and a matching origin', () => {
let response
before(done => {
alice.get('/')
alice.get('/private-for-owner.txt')
.set('Origin', bobServerUri)
.end((err, res) => {
response = res
Expand All @@ -341,7 +341,7 @@ describe('Authentication API (OIDC)', () => {
describe('with that cookie and a non-matching origin', () => {
let response
before(done => {
alice.get('/')
alice.get('/private-for-owner.txt')
.set('Cookie', cookie)
.set('Origin', bobServerUri)
.end((err, res) => {
Expand All @@ -360,7 +360,7 @@ describe('Authentication API (OIDC)', () => {
let response
before(done => {
var malcookie = cookie.replace(/connect\.sid=(\S+)/, 'connect.sid=l33th4x0rzp0wn4g3;')
alice.get('/')
alice.get('/private-for-owner.txt')
.set('Cookie', malcookie)
.set('Origin', bobServerUri)
.end((err, res) => {
Expand Down
66 changes: 66 additions & 0 deletions test/integration/special-root-acl-handling.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
const assert = require('chai').assert
const request = require('request')
const path = require('path')
const { checkDnsSettings, cleanDir } = require('../utils')

const ldnode = require('../../index')

const port = 7777
const serverUri = `https://localhost:${port}`
const root = path.join(__dirname, '../resources/accounts-acl')
const dbPath = path.join(root, 'db')
const configPath = path.join(root, 'config')

function createOptions (path = '') {
return {
url: `https://nicola.localhost:${port}${path}`
}
}

describe('Special handling: Root ACL does not give READ access to root', () => {
let ldp, ldpHttpsServer

before(checkDnsSettings)

before(done => {
ldp = ldnode.createServer({
root,
serverUri,
dbPath,
port,
configPath,
sslKey: path.join(__dirname, '../keys/key.pem'),
sslCert: path.join(__dirname, '../keys/cert.pem'),
webid: true,
multiuser: true,
auth: 'oidc',
strictOrigin: true,
host: { serverUri }
})
ldpHttpsServer = ldp.listen(port, done)
})

after(() => {
if (ldpHttpsServer) ldpHttpsServer.close()
cleanDir(root)
})

describe('should still grant READ access to everyone because of index.html.acl', () => {
it('for root with /', function (done) {
var options = createOptions('/')
request.get(options, function (error, response, body) {
assert.equal(error, null)
assert.equal(response.statusCode, 200)
done()
})
})
it('for root without /', function (done) {
var options = createOptions()
request.get(options, function (error, response, body) {
assert.equal(error, null)
assert.equal(response.statusCode, 200)
done()
})
})
})
})
1 change: 1 addition & 0 deletions test/resources/accounts-acl/nicola.localhost/.acl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# This ACL does nothing by default
1 change: 1 addition & 0 deletions test/resources/accounts-acl/nicola.localhost/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<body>Everyone should get READ access for this file through <pre>index.html.acl</pre>.</body>
10 changes: 10 additions & 0 deletions test/resources/accounts-acl/nicola.localhost/index.html.acl
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# This file grants everyone READ access to ./index.html

@prefix acl: <http://www.w3.org/ns/auth/acl#>.
@prefix foaf: <http://xmlns.com/foaf/0.1/>.

<#public>
a acl:Authorization;
acl:agentClass foaf:Agent;
acl:accessTo <./index.html>;
acl:mode acl:Read.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
protected contents for owner
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<#Owner>
a <http://www.w3.org/ns/auth/acl#Authorization> ;
<http://www.w3.org/ns/auth/acl#accessTo> <./private-for-owner.txt>;
<http://www.w3.org/ns/auth/acl#agent> <https://localhost:7000/profile/card#me>;
<http://www.w3.org/ns/auth/acl#mode> <http://www.w3.org/ns/auth/acl#Read>, <http://www.w3.org/ns/auth/acl#Write>, <http://www.w3.org/ns/auth/acl#Control> .