Skip to content

Commit e1c6286

Browse files
authored
Merge pull request #892 from solid/feature/link-toc
Enabling POD providers to enforce a Terms & Conditions Merging after @megoth has made a review of the security implications of snyks complaints.
2 parents 93d7482 + df438cc commit e1c6286

File tree

7 files changed

+149
-36
lines changed

7 files changed

+149
-36
lines changed

bin/lib/options.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,21 @@ module.exports = [
334334
name: 'server-logo',
335335
help: 'A logo that represents you, your brand, or your server (not required)',
336336
prompt: true
337+
},
338+
{
339+
name: 'enforceToc',
340+
help: 'Do you want to enforce Terms & Conditions for your service?',
341+
flag: true,
342+
prompt: true,
343+
default: true,
344+
when: answers => answers.multiuser
345+
},
346+
{
347+
name: 'tocUri',
348+
help: 'URI to your Terms & Conditions',
349+
prompt: true,
350+
validate: validUri,
351+
when: answers => answers.enforceToc
337352
}
338353
]
339354

config.json-default

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,7 @@
1414
"name": "",
1515
"description": "",
1616
"logo": ""
17-
}
17+
},
18+
"enforceToc": true,
19+
"tocUri": "https://your-toc"
1820
}

default-views/account/register-form.hbs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,17 @@
7171
</span>
7272
</div>
7373

74+
{{#if enforceToc}}
75+
{{#if tocUri}}
76+
<div class="checkbox">
77+
<label>
78+
<input type="checkbox" name="acceptToc">
79+
I agree to the <a href="{{tocUri}}" target="_blank">Terms &amp; Conditions</a> of this service
80+
</label>
81+
</div>
82+
{{/if}}
83+
{{/if}}
84+
7485

7586
<button type="submit" class="btn btn-primary" id="register">Register</button>
7687

lib/create-app.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,8 @@ function initAppLocals (app, argv, ldp) {
123123
app.locals.authMethod = argv.auth
124124
app.locals.localAuth = argv.localAuth
125125
app.locals.tokenService = new TokenService()
126+
app.locals.enforceToc = argv.enforceToc
127+
app.locals.tocUri = argv.tocUri
126128

127129
if (argv.email && argv.email.host) {
128130
app.locals.emailService = new EmailService(argv.templates.email, argv.email)

lib/requests/auth-request.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ class AuthRequest {
2525
* @param [options.returnToUrl] {string}
2626
* @param [options.authQueryParams] {Object} Key/value hashmap of parsed query
2727
* parameters that will be passed through to the /authorize endpoint.
28+
* @param [options.enforceToc] {boolean} Whether or not to enforce the service provider's T&C
29+
* @param [options.tocUri] {string} URI to the service provider's T&C
2830
*/
2931
constructor (options) {
3032
this.response = options.response
@@ -34,6 +36,8 @@ class AuthRequest {
3436
this.returnToUrl = options.returnToUrl
3537
this.authQueryParams = options.authQueryParams || {}
3638
this.localAuth = options.localAuth
39+
this.enforceToc = options.enforceToc
40+
this.tocUri = options.tocUri
3741
}
3842

3943
/**
@@ -84,6 +88,7 @@ class AuthRequest {
8488

8589
let authQueryParams = AuthRequest.extractAuthParams(req)
8690
let returnToUrl = AuthRequest.parseParameter(req, 'returnToUrl')
91+
const acceptToc = AuthRequest.parseParameter(req, 'acceptToc') === 'true'
8792

8893
let options = {
8994
response: res,
@@ -92,7 +97,8 @@ class AuthRequest {
9297
accountManager,
9398
returnToUrl,
9499
authQueryParams,
95-
localAuth
100+
localAuth,
101+
acceptToc
96102
}
97103

98104
return options

lib/requests/create-account-request.js

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,16 @@ class CreateAccountRequest extends AuthRequest {
2727
* @param [options.response] {HttpResponse}
2828
* @param [options.returnToUrl] {string} If present, redirect the agent to
2929
* this url on successful account creation
30+
* @param [options.enforceToc] {boolean} Whether or not to enforce the service provider's T&C
31+
* @param [options.tocUri] {string} URI to the service provider's T&C
32+
* @param [options.acceptToc] {boolean} Whether or not user has accepted T&C
3033
*/
3134
constructor (options) {
3235
super(options)
3336

3437
this.username = options.username
3538
this.userAccount = options.userAccount
39+
this.acceptToc = options.acceptToc
3640
}
3741

3842
/**
@@ -46,7 +50,7 @@ class CreateAccountRequest extends AuthRequest {
4650
* `userAccountFrom()`), or it encounters an unsupported authentication
4751
* scheme.
4852
*
49-
* @return {CreateAccountRequest|CreateTlsAccountRequest}
53+
* @return {CreateOidcAccountRequest|CreateTlsAccountRequest}
5054
*/
5155
static fromParams (req, res) {
5256
let options = AuthRequest.requestOptions(req, res)
@@ -62,6 +66,9 @@ class CreateAccountRequest extends AuthRequest {
6266
options.userAccount = accountManager.userAccountFrom(body)
6367
}
6468

69+
options.enforceToc = locals.enforceToc
70+
options.tocUri = locals.tocUri
71+
6572
switch (authMethod) {
6673
case 'oidc':
6774
options.password = body.password
@@ -74,13 +81,15 @@ class CreateAccountRequest extends AuthRequest {
7481
}
7582
}
7683

77-
static post (req, res) {
84+
static async post (req, res) {
7885
let request = CreateAccountRequest.fromParams(req, res)
7986

80-
return Promise.resolve()
81-
.then(() => request.validate())
82-
.then(() => request.createAccount())
83-
.catch(error => request.error(error))
87+
try {
88+
request.validate()
89+
await request.createAccount()
90+
} catch (error) {
91+
request.error(error)
92+
}
8493
}
8594

8695
static get (req, res) {
@@ -97,13 +106,14 @@ class CreateAccountRequest extends AuthRequest {
97106
renderForm (error) {
98107
let authMethod = this.accountManager.authMethod
99108

100-
let params = Object.assign({}, this.authQueryParams,
101-
{
102-
returnToUrl: this.returnToUrl,
103-
loginUrl: this.loginUrl(),
104-
registerDisabled: authMethod === 'tls',
105-
multiuser: this.accountManager.multiuser
106-
})
109+
let params = Object.assign({}, this.authQueryParams, {
110+
enforceToc: this.enforceToc,
111+
loginUrl: this.loginUrl(),
112+
multiuser: this.accountManager.multiuser,
113+
registerDisabled: authMethod === 'tls',
114+
returnToUrl: this.returnToUrl,
115+
tocUri: this.tocUri
116+
})
107117

108118
if (error) {
109119
params.error = error.message
@@ -244,6 +254,7 @@ class CreateOidcAccountRequest extends CreateAccountRequest {
244254
*
245255
* @param [options={}] {Object} See `CreateAccountRequest` constructor docstring
246256
* @param [options.password] {string} Password, as entered by the user at signup
257+
* @param [options.acceptToc] {boolean} Whether or not user has accepted T&C
247258
*/
248259
constructor (options) {
249260
super(options)
@@ -271,6 +282,12 @@ class CreateOidcAccountRequest extends CreateAccountRequest {
271282
error.statusCode = 400
272283
throw error
273284
}
285+
286+
if (this.enforceToc && !this.acceptToc) {
287+
error = new Error('Accepting Terms & Conditions is required for this service')
288+
error.statusCode = 400
289+
throw error
290+
}
274291
}
275292

276293
/**
@@ -310,6 +327,7 @@ class CreateTlsAccountRequest extends CreateAccountRequest {
310327
*
311328
* @param [options={}] {Object} See `CreateAccountRequest` constructor docstring
312329
* @param [options.spkac] {string}
330+
* @param [options.acceptToc] {boolean} Whether or not user has accepted T&C
313331
*/
314332
constructor (options) {
315333
super(options)
@@ -319,7 +337,7 @@ class CreateTlsAccountRequest extends CreateAccountRequest {
319337
}
320338

321339
/**
322-
* Validates the Login request (makes sure required parameters are present),
340+
* Validates the Signup request (makes sure required parameters are present),
323341
* and throws an error if not.
324342
*
325343
* @throws {Error} If missing required params
@@ -332,6 +350,12 @@ class CreateTlsAccountRequest extends CreateAccountRequest {
332350
error.statusCode = 400
333351
throw error
334352
}
353+
354+
if (this.enforceToc && !this.acceptToc) {
355+
error = new Error('Accepting Terms & Conditions is required for this service')
356+
error.statusCode = 400
357+
throw error
358+
}
335359
}
336360

337361
/**

test/integration/account-creation-oidc-test.js

Lines changed: 73 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,17 @@ const path = require('path')
88
const fs = require('fs-extra')
99

1010
describe('AccountManager (OIDC account creation tests)', function () {
11-
var serverUri = 'https://localhost:3457'
12-
var host = 'localhost:3457'
13-
var ldpHttpsServer
14-
let rootPath = path.join(__dirname, '../resources/accounts/')
15-
let configPath = path.join(__dirname, '../resources/config')
16-
let dbPath = path.join(__dirname, '../resources/accounts/db')
11+
const port = 3457
12+
const serverUri = `https://localhost:${port}`
13+
const host = `localhost:${port}`
14+
const root = path.join(__dirname, '../resources/accounts/')
15+
const configPath = path.join(__dirname, '../resources/config')
16+
const dbPath = path.join(__dirname, '../resources/accounts/db')
17+
18+
let ldpHttpsServer
1719

1820
var ldp = ldnode.createServer({
19-
root: rootPath,
21+
root,
2022
configPath,
2123
sslKey: path.join(__dirname, '../keys/key.pem'),
2224
sslCert: path.join(__dirname, '../keys/cert.pem'),
@@ -25,19 +27,20 @@ describe('AccountManager (OIDC account creation tests)', function () {
2527
multiuser: true,
2628
strictOrigin: true,
2729
dbPath,
28-
serverUri
30+
serverUri,
31+
enforceToc: true
2932
})
3033

3134
before(checkDnsSettings)
3235

3336
before(function (done) {
34-
ldpHttpsServer = ldp.listen(3457, done)
37+
ldpHttpsServer = ldp.listen(port, done)
3538
})
3639

3740
after(function () {
3841
if (ldpHttpsServer) ldpHttpsServer.close()
3942
fs.removeSync(path.join(dbPath, 'oidc/users/users'))
40-
cleanDir(path.join(rootPath, 'localhost'))
43+
cleanDir(path.join(root, 'localhost'))
4144
})
4245

4346
var server = supertest(serverUri)
@@ -93,45 +96,52 @@ describe('AccountManager (OIDC account creation tests)', function () {
9396
it('should not create a WebID if it already exists', function (done) {
9497
var subdomain = supertest('https://nicola.' + host)
9598
subdomain.post('/api/accounts/new')
96-
.send('username=nicola&password=12345')
99+
.send('username=nicola&password=12345&acceptToc=true')
97100
.expect(302)
98101
.end((err, res) => {
99102
if (err) {
100103
return done(err)
101104
}
102105
subdomain.post('/api/accounts/new')
103-
.send('username=nicola&password=12345')
106+
.send('username=nicola&password=12345&acceptToc=true')
104107
.expect(400)
105108
.end((err) => {
106109
done(err)
107110
})
108111
})
109112
})
110113

114+
it('should not create WebID if T&C is not accepted', (done) => {
115+
let subdomain = supertest('https://nicola.' + host)
116+
subdomain.post('/api/accounts/new')
117+
.send('username=nicola&password=12345&acceptToc=false')
118+
.expect(400, done)
119+
})
120+
111121
it('should create the default folders', function (done) {
112122
var subdomain = supertest('https://nicola.' + host)
113123
subdomain.post('/api/accounts/new')
114-
.send('username=nicola&password=12345')
124+
.send('username=nicola&password=12345&acceptToc=true')
115125
.expect(302)
116126
.end(function (err) {
117127
if (err) {
118128
return done(err)
119129
}
120130
var domain = host.split(':')[0]
121131
var card = read(path.join('accounts/nicola.' + domain,
122-
'profile/card'))
132+
'profile/card'))
123133
var cardAcl = read(path.join('accounts/nicola.' + domain,
124-
'profile/card.acl'))
134+
'profile/card.acl'))
125135
var prefs = read(path.join('accounts/nicola.' + domain,
126-
'settings/prefs.ttl'))
136+
'settings/prefs.ttl'))
127137
var inboxAcl = read(path.join('accounts/nicola.' + domain,
128-
'inbox/.acl'))
138+
'inbox/.acl'))
129139
var rootMeta = read(path.join('accounts/nicola.' + domain, '.meta'))
130140
var rootMetaAcl = read(path.join('accounts/nicola.' + domain,
131-
'.meta.acl'))
141+
'.meta.acl'))
132142

133143
if (domain && card && cardAcl && prefs && inboxAcl && rootMeta &&
134-
rootMetaAcl) {
144+
rootMetaAcl) {
135145
done()
136146
} else {
137147
done(new Error('failed to create default files'))
@@ -142,7 +152,7 @@ describe('AccountManager (OIDC account creation tests)', function () {
142152
it('should link WebID to the root account', function (done) {
143153
var subdomain = supertest('https://nicola.' + host)
144154
subdomain.post('/api/accounts/new')
145-
.send('username=nicola&password=12345')
155+
.send('username=nicola&password=12345&acceptToc=true')
146156
.expect(302)
147157
.end(function (err) {
148158
if (err) {
@@ -236,3 +246,46 @@ describe('Single User signup page', () => {
236246
.end(done)
237247
})
238248
})
249+
250+
describe('Signup page where Terms & Conditions are not being enforced', () => {
251+
const port = 3457
252+
const host = `localhost:${port}`
253+
const root = path.join(__dirname, '../resources/accounts/')
254+
const configPath = path.join(__dirname, '../resources/config')
255+
const dbPath = path.join(__dirname, '../resources/accounts/db')
256+
const ldp = ldnode.createServer({
257+
port,
258+
root,
259+
configPath,
260+
sslKey: path.join(__dirname, '../keys/key.pem'),
261+
sslCert: path.join(__dirname, '../keys/cert.pem'),
262+
auth: 'oidc',
263+
webid: true,
264+
multiuser: true,
265+
strictOrigin: true,
266+
enforceToc: false
267+
})
268+
let ldpHttpsServer
269+
270+
before(function (done) {
271+
ldpHttpsServer = ldp.listen(port, done)
272+
})
273+
274+
after(function () {
275+
if (ldpHttpsServer) ldpHttpsServer.close()
276+
fs.removeSync(path.join(dbPath, 'oidc/users/users'))
277+
cleanDir(path.join(root, 'localhost'))
278+
rm('accounts/nicola.localhost')
279+
})
280+
281+
beforeEach(function () {
282+
rm('accounts/nicola.localhost')
283+
})
284+
285+
it('should not enforce T&C upon creating account', function (done) {
286+
var subdomain = supertest('https://nicola.' + host)
287+
subdomain.post('/api/accounts/new')
288+
.send('username=nicola&password=12345')
289+
.expect(302, done)
290+
})
291+
})

0 commit comments

Comments
 (0)