diff --git a/lib/handlers/allow.js b/lib/handlers/allow.js index 5a6abff8c..5e2ccadd1 100644 --- a/lib/handlers/allow.js +++ b/lib/handlers/allow.js @@ -8,7 +8,7 @@ var debug = require('../debug.js').ACL function allow (mode) { return function allowHandler (req, res, next) { - var ldp = req.app.locals.ldp + var ldp = req.app.locals.ldp || {} if (!ldp.webid) { return next() } diff --git a/lib/handlers/auth-proxy.js b/lib/handlers/auth-proxy.js index e6a8e38ca..3c8a88eb4 100644 --- a/lib/handlers/auth-proxy.js +++ b/lib/handlers/auth-proxy.js @@ -2,13 +2,19 @@ // that sends a logged-in Solid user's details to a backend module.exports = addAuthProxyHandlers -const proxy = require('http-proxy-middleware') +const createProxy = require('http-proxy-middleware') const debug = require('../debug') +const allow = require('./allow') const PROXY_SETTINGS = { logLevel: 'silent', changeOrigin: true } +const REQUIRED_PERMISSIONS = { + get: ['Read'], + options: ['Read'], + use: ['Read', 'Write'] +} // Registers Auth Proxy handlers for each target function addAuthProxyHandlers (app, targets) { @@ -32,7 +38,11 @@ function addAuthProxyHandler (app, sourcePath, target) { }, PROXY_SETTINGS) // Activate the proxy - app.use(`${sourcePath}*`, proxy(settings)) + const proxy = createProxy(settings) + for (let action in REQUIRED_PERMISSIONS) { + const permissions = REQUIRED_PERMISSIONS[action] + app[action](`${sourcePath}*`, setOriginalUrl, ...permissions.map(allow), proxy) + } } // Adds a headers with authentication information @@ -45,3 +55,9 @@ function addAuthHeaders (proxyReq, req) { proxyReq.setHeader('Forwarded', `host=${headers.host}`) } } + +// Sets the original URL on the request (for the ACL handler) +function setOriginalUrl (req, res, next) { + res.locals.path = req.originalUrl + next() +} diff --git a/test/integration/auth-proxy-test.js b/test/integration/auth-proxy-test.js index 238696721..6aa53aa50 100644 --- a/test/integration/auth-proxy-test.js +++ b/test/integration/auth-proxy-test.js @@ -5,7 +5,6 @@ const request = require('supertest') const { expect } = require('chai') const rm = require('../utils').rm -const HOST = 'solid.org' const USER = 'https://ruben.verborgh.org/profile/#me' describe('Auth Proxy', () => { @@ -15,10 +14,12 @@ describe('Auth Proxy', () => { // Set up test back-end server nock('http://server-a.org').persist() .get(/./).reply(200, function () { return this.req.headers }) + .options(/./).reply(200) + .post(/./).reply(200) // Set up Solid server server = ldnode({ - root: path.join(__dirname, '../resources'), + root: path.join(__dirname, '../resources/auth-proxy'), authProxy: { '/server/a': 'http://server-a.org' }, @@ -36,18 +37,100 @@ describe('Auth Proxy', () => { describe('responding to /server/a', () => { let response - before(() => { - return request(server).get('/server/a') - .set('Host', HOST) + before(() => + request(server).get('/server/a/') .then(res => { response = res }) - }) + ) it('sets the User header on the proxy request', () => { expect(response.body).to.have.property('user', USER) }) + }) + + describe('responding to GET', () => { + describe('for a path with read permissions', () => { + let response + before(() => + request(server).get('/server/a/r') + .then(res => { response = res }) + ) + it('returns status code 200', () => { + expect(response.statusCode).to.equal(200) + }) + }) + + describe('for a path without read permissions', () => { + let response + before(() => + request(server).get('/server/a/wc') + .then(res => { response = res }) + ) + + it('returns status code 403', () => { + expect(response.statusCode).to.equal(403) + }) + }) + }) + + describe('responding to OPTIONS', () => { + describe('for a path with read permissions', () => { + let response + before(() => + request(server).options('/server/a/r') + .then(res => { response = res }) + ) + it('returns status code 200', () => { + expect(response.statusCode).to.equal(200) + }) + }) + + describe('for a path without read permissions', () => { + let response + before(() => + request(server).options('/server/a/wc') + .then(res => { response = res }) + ) + + it('returns status code 403', () => { + expect(response.statusCode).to.equal(403) + }) + }) + }) + + describe('responding to POST', () => { + describe('for a path with read and write permissions', () => { + let response + before(() => + request(server).post('/server/a/rw') + .then(res => { response = res }) + ) + it('returns status code 200', () => { + expect(response.statusCode).to.equal(200) + }) + }) + + describe('for a path without read permissions', () => { + let response + before(() => + request(server).post('/server/a/w') + .then(res => { response = res }) + ) + + it('returns status code 403', () => { + expect(response.statusCode).to.equal(403) + }) + }) + + describe('for a path without write permissions', () => { + let response + before(() => + request(server).post('/server/a/r') + .then(res => { response = res }) + ) - it('returns status code 200', () => { - expect(response).to.have.property('statusCode', 200) + it('returns status code 403', () => { + expect(response.statusCode).to.equal(403) + }) }) }) }) diff --git a/test/resources/auth-proxy/.acl b/test/resources/auth-proxy/.acl new file mode 100644 index 000000000..ef847823b --- /dev/null +++ b/test/resources/auth-proxy/.acl @@ -0,0 +1,42 @@ +@prefix acl: . +@prefix foaf: . + +# All permissions on /server/a +[ + a acl:Authorization; + acl:accessTo ; + acl:agent ; + acl:mode acl:Read, acl:Write, acl:Control +]. + +# Only Read permissions on /server/a/r +[ + a acl:Authorization; + acl:accessTo ; + acl:agent ; + acl:mode acl:Read +]. + +# No Read permissions on /server/a/wc +[ + a acl:Authorization; + acl:accessTo ; + acl:agent ; + acl:mode acl:Write, acl:Control +]. + +# Only Write permissions on /server/a/w +[ + a acl:Authorization; + acl:accessTo ; + acl:agent ; + acl:mode acl:Write +]. + +# Read-Write permissions on /server/a/rw +[ + a acl:Authorization; + acl:accessTo ; + acl:agent ; + acl:mode acl:Read, acl:Write +]. diff --git a/test/resources/auth-proxy/index.html b/test/resources/auth-proxy/index.html new file mode 100644 index 000000000..e69de29bb diff --git a/test/resources/auth-proxy/index.html.acl b/test/resources/auth-proxy/index.html.acl new file mode 100644 index 000000000..e69de29bb diff --git a/test/unit/auth-proxy-test.js b/test/unit/auth-proxy-test.js index fbd28d4b1..baf5a7b79 100644 --- a/test/unit/auth-proxy-test.js +++ b/test/unit/auth-proxy-test.js @@ -68,7 +68,7 @@ describe('Auth Proxy', () => { }) it('returns status code 200', () => { - expect(response).to.have.property('statusCode', 200) + expect(response.statusCode).to.equal(200) }) }) @@ -102,7 +102,7 @@ describe('Auth Proxy', () => { }) it('returns status code 200', () => { - expect(response).to.have.property('statusCode', 200) + expect(response.statusCode).to.equal(200) }) }) @@ -136,7 +136,7 @@ describe('Auth Proxy', () => { }) it('returns status code 200', () => { - expect(response).to.have.property('statusCode', 200) + expect(response.statusCode).to.equal(200) }) }) @@ -170,7 +170,7 @@ describe('Auth Proxy', () => { }) it('returns status code 200', () => { - expect(response).to.have.property('statusCode', 200) + expect(response.statusCode).to.equal(200) }) }) @@ -208,7 +208,7 @@ describe('Auth Proxy', () => { }) it('returns status code 200', () => { - expect(response).to.have.property('statusCode', 200) + expect(response.statusCode).to.equal(200) }) }) })