Skip to content

Commit d9355b8

Browse files
committed
Require write permissions for unsafe proxy methods.
Closes #595.
1 parent fa4d1d2 commit d9355b8

File tree

3 files changed

+146
-16
lines changed

3 files changed

+146
-16
lines changed

lib/handlers/auth-proxy.js

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,19 @@
22
// that sends a logged-in Solid user's details to a backend
33
module.exports = addAuthProxyHandlers
44

5-
const proxy = require('http-proxy-middleware')
5+
const createProxy = require('http-proxy-middleware')
66
const debug = require('../debug')
77
const allow = require('./allow')
88

99
const PROXY_SETTINGS = {
1010
logLevel: 'silent',
1111
changeOrigin: true
1212
}
13+
const REQUIRED_PERMISSIONS = {
14+
get: ['Read'],
15+
options: ['Read'],
16+
use: ['Read', 'Write']
17+
}
1318

1419
// Registers Auth Proxy handlers for each target
1520
function addAuthProxyHandlers (app, targets) {
@@ -33,7 +38,11 @@ function addAuthProxyHandler (app, sourcePath, target) {
3338
}, PROXY_SETTINGS)
3439

3540
// Activate the proxy
36-
app.use(`${sourcePath}*`, allow('Read'), proxy(settings))
41+
const proxy = createProxy(settings)
42+
for (let action in REQUIRED_PERMISSIONS) {
43+
const permissions = REQUIRED_PERMISSIONS[action]
44+
app[action](`${sourcePath}*`, setOriginalUrl, ...permissions.map(allow), proxy)
45+
}
3746
}
3847

3948
// Adds a headers with authentication information
@@ -46,3 +55,9 @@ function addAuthHeaders (proxyReq, req) {
4655
proxyReq.setHeader('Forwarded', `host=${headers.host}`)
4756
}
4857
}
58+
59+
// Sets the original URL on the request (for the ACL handler)
60+
function setOriginalUrl (req, res, next) {
61+
res.locals.path = req.originalUrl
62+
next()
63+
}

test/integration/auth-proxy-test.js

Lines changed: 90 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ const request = require('supertest')
55
const { expect } = require('chai')
66
const rm = require('../utils').rm
77

8-
const HOST = 'solid.org'
98
const USER = 'https://ruben.verborgh.org/profile/#me'
109

1110
describe('Auth Proxy', () => {
@@ -15,6 +14,8 @@ describe('Auth Proxy', () => {
1514
// Set up test back-end server
1615
nock('http://server-a.org').persist()
1716
.get(/./).reply(200, function () { return this.req.headers })
17+
.options(/./).reply(200)
18+
.post(/./).reply(200)
1819

1920
// Set up Solid server
2021
server = ldnode({
@@ -36,18 +37,100 @@ describe('Auth Proxy', () => {
3637

3738
describe('responding to /server/a', () => {
3839
let response
39-
before(() => {
40-
return request(server).get('/server/a')
41-
.set('Host', HOST)
40+
before(() =>
41+
request(server).get('/server/a/')
4242
.then(res => { response = res })
43-
})
43+
)
4444

4545
it('sets the User header on the proxy request', () => {
4646
expect(response.body).to.have.property('user', USER)
4747
})
48+
})
49+
50+
describe('responding to GET', () => {
51+
describe('for a path with read permissions', () => {
52+
let response
53+
before(() =>
54+
request(server).get('/server/a/r')
55+
.then(res => { response = res })
56+
)
57+
it('returns status code 200', () => {
58+
expect(response).to.have.property('statusCode', 200)
59+
})
60+
})
61+
62+
describe('for a path without read permissions', () => {
63+
let response
64+
before(() =>
65+
request(server).get('/server/a/wc')
66+
.then(res => { response = res })
67+
)
68+
69+
it('returns status code 403', () => {
70+
expect(response).to.have.property('statusCode', 403)
71+
})
72+
})
73+
})
74+
75+
describe('responding to OPTIONS', () => {
76+
describe('for a path with read permissions', () => {
77+
let response
78+
before(() =>
79+
request(server).options('/server/a/r')
80+
.then(res => { response = res })
81+
)
82+
it('returns status code 200', () => {
83+
expect(response).to.have.property('statusCode', 200)
84+
})
85+
})
86+
87+
describe('for a path without read permissions', () => {
88+
let response
89+
before(() =>
90+
request(server).options('/server/a/wc')
91+
.then(res => { response = res })
92+
)
93+
94+
it('returns status code 403', () => {
95+
expect(response).to.have.property('statusCode', 403)
96+
})
97+
})
98+
})
99+
100+
describe('responding to POST', () => {
101+
describe('for a path with read and write permissions', () => {
102+
let response
103+
before(() =>
104+
request(server).post('/server/a/rw')
105+
.then(res => { response = res })
106+
)
107+
it('returns status code 200', () => {
108+
expect(response).to.have.property('statusCode', 200)
109+
})
110+
})
111+
112+
describe('for a path without read permissions', () => {
113+
let response
114+
before(() =>
115+
request(server).post('/server/a/w')
116+
.then(res => { response = res })
117+
)
118+
119+
it('returns status code 403', () => {
120+
expect(response).to.have.property('statusCode', 403)
121+
})
122+
})
123+
124+
describe('for a path without write permissions', () => {
125+
let response
126+
before(() =>
127+
request(server).post('/server/a/r')
128+
.then(res => { response = res })
129+
)
48130

49-
it('returns status code 200', () => {
50-
expect(response).to.have.property('statusCode', 200)
131+
it('returns status code 403', () => {
132+
expect(response).to.have.property('statusCode', 403)
133+
})
51134
})
52135
})
53136
})

test/resources/auth-proxy/.acl

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,42 @@
11
@prefix acl: <http://www.w3.org/ns/auth/acl#>.
22
@prefix foaf: <http://xmlns.com/foaf/0.1/>.
33

4-
# All permissions on the root
5-
<#owner>
6-
a acl:Authorization;
7-
acl:agent <https://ruben.verborgh.org/profile/#me>;
8-
acl:accessTo <./>;
9-
acl:defaultForNew <./>;
10-
acl:mode acl:Read, acl:Write, acl:Control.
4+
# All permissions on /server/a
5+
[
6+
a acl:Authorization;
7+
acl:accessTo </server/a/>;
8+
acl:agent <https://ruben.verborgh.org/profile/#me>;
9+
acl:mode acl:Read, acl:Write, acl:Control
10+
].
11+
12+
# Only Read permissions on /server/a/r
13+
[
14+
a acl:Authorization;
15+
acl:accessTo </server/a/r>;
16+
acl:agent <https://ruben.verborgh.org/profile/#me>;
17+
acl:mode acl:Read
18+
].
19+
20+
# No Read permissions on /server/a/wc
21+
[
22+
a acl:Authorization;
23+
acl:accessTo </server/a/wc>;
24+
acl:agent <https://ruben.verborgh.org/profile/#me>;
25+
acl:mode acl:Write, acl:Control
26+
].
27+
28+
# Only Write permissions on /server/a/w
29+
[
30+
a acl:Authorization;
31+
acl:accessTo </server/a/w>;
32+
acl:agent <https://ruben.verborgh.org/profile/#me>;
33+
acl:mode acl:Write
34+
].
35+
36+
# Read-Write permissions on /server/a/rw
37+
[
38+
a acl:Authorization;
39+
acl:accessTo </server/a/rw>;
40+
acl:agent <https://ruben.verborgh.org/profile/#me>;
41+
acl:mode acl:Read, acl:Write
42+
].

0 commit comments

Comments
 (0)