solid-server 5.6.9-beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (170) hide show
  1. package/.acl +10 -0
  2. package/.github/workflows/ci.yml +47 -0
  3. package/.nvmrc +1 -0
  4. package/.snyk +35 -0
  5. package/.well-known/.acl +15 -0
  6. package/CHANGELOG.md +198 -0
  7. package/CONTRIBUTING.md +139 -0
  8. package/CONTRIBUTORS.md +36 -0
  9. package/Dockerfile +22 -0
  10. package/LICENSE.md +23 -0
  11. package/README.md +453 -0
  12. package/bin/lib/cli-utils.js +85 -0
  13. package/bin/lib/cli.js +39 -0
  14. package/bin/lib/init.js +94 -0
  15. package/bin/lib/invalidUsernames.js +148 -0
  16. package/bin/lib/migrateLegacyResources.js +69 -0
  17. package/bin/lib/options.js +399 -0
  18. package/bin/lib/start.js +148 -0
  19. package/bin/lib/updateIndex.js +56 -0
  20. package/bin/solid +3 -0
  21. package/bin/solid-test +12 -0
  22. package/bin/solid.js +3 -0
  23. package/common/css/solid.css +58 -0
  24. package/common/fonts/glyphicons-halflings-regular.eot +0 -0
  25. package/common/fonts/glyphicons-halflings-regular.svg +288 -0
  26. package/common/fonts/glyphicons-halflings-regular.ttf +0 -0
  27. package/common/fonts/glyphicons-halflings-regular.woff +0 -0
  28. package/common/fonts/glyphicons-halflings-regular.woff2 +0 -0
  29. package/common/img/.gitkeep +0 -0
  30. package/common/js/auth-buttons.js +65 -0
  31. package/common/js/solid.js +454 -0
  32. package/common/well-known/security.txt +2 -0
  33. package/config/defaults.js +25 -0
  34. package/config/usernames-blacklist.json +4 -0
  35. package/config.json-default +22 -0
  36. package/default-templates/emails/delete-account.js +49 -0
  37. package/default-templates/emails/invalid-username.js +30 -0
  38. package/default-templates/emails/reset-password.js +49 -0
  39. package/default-templates/emails/welcome.js +39 -0
  40. package/default-templates/new-account/.acl +26 -0
  41. package/default-templates/new-account/.meta +5 -0
  42. package/default-templates/new-account/.meta.acl +25 -0
  43. package/default-templates/new-account/.well-known/.acl +19 -0
  44. package/default-templates/new-account/favicon.ico +0 -0
  45. package/default-templates/new-account/favicon.ico.acl +26 -0
  46. package/default-templates/new-account/inbox/.acl +26 -0
  47. package/default-templates/new-account/private/.acl +10 -0
  48. package/default-templates/new-account/profile/.acl +19 -0
  49. package/default-templates/new-account/profile/card$.ttl +25 -0
  50. package/default-templates/new-account/public/.acl +19 -0
  51. package/default-templates/new-account/robots.txt +3 -0
  52. package/default-templates/new-account/robots.txt.acl +26 -0
  53. package/default-templates/new-account/settings/.acl +20 -0
  54. package/default-templates/new-account/settings/prefs.ttl +15 -0
  55. package/default-templates/new-account/settings/privateTypeIndex.ttl +4 -0
  56. package/default-templates/new-account/settings/publicTypeIndex.ttl +4 -0
  57. package/default-templates/new-account/settings/publicTypeIndex.ttl.acl +25 -0
  58. package/default-templates/new-account/settings/serverSide.ttl.acl +13 -0
  59. package/default-templates/new-account/settings/serverSide.ttl.inactive +12 -0
  60. package/default-templates/server/.acl +10 -0
  61. package/default-templates/server/.well-known/.acl +15 -0
  62. package/default-templates/server/favicon.ico +0 -0
  63. package/default-templates/server/favicon.ico.acl +15 -0
  64. package/default-templates/server/index.html +55 -0
  65. package/default-templates/server/robots.txt +3 -0
  66. package/default-templates/server/robots.txt.acl +15 -0
  67. package/default-views/account/account-deleted.hbs +17 -0
  68. package/default-views/account/delete-confirm.hbs +51 -0
  69. package/default-views/account/delete-link-sent.hbs +17 -0
  70. package/default-views/account/delete.hbs +51 -0
  71. package/default-views/account/invalid-username.hbs +22 -0
  72. package/default-views/account/register-disabled.hbs +6 -0
  73. package/default-views/account/register-form.hbs +132 -0
  74. package/default-views/account/register.hbs +24 -0
  75. package/default-views/auth/auth-hidden-fields.hbs +8 -0
  76. package/default-views/auth/change-password.hbs +58 -0
  77. package/default-views/auth/goodbye.hbs +23 -0
  78. package/default-views/auth/login-required.hbs +34 -0
  79. package/default-views/auth/login-tls.hbs +11 -0
  80. package/default-views/auth/login-username-password.hbs +28 -0
  81. package/default-views/auth/login.hbs +55 -0
  82. package/default-views/auth/no-permission.hbs +29 -0
  83. package/default-views/auth/password-changed.hbs +27 -0
  84. package/default-views/auth/reset-link-sent.hbs +21 -0
  85. package/default-views/auth/reset-password.hbs +52 -0
  86. package/default-views/auth/sharing.hbs +49 -0
  87. package/default-views/shared/create-account.hbs +8 -0
  88. package/default-views/shared/error.hbs +5 -0
  89. package/docs/how-to-delete-your-account.md +56 -0
  90. package/docs/login-and-grant-access-to-application.md +32 -0
  91. package/examples/custom-error-handling.js +31 -0
  92. package/examples/ldp-with-webid.js +12 -0
  93. package/examples/simple-express-app.js +20 -0
  94. package/examples/simple-ldp-server.js +8 -0
  95. package/favicon.ico +0 -0
  96. package/favicon.ico.acl +15 -0
  97. package/index.html +48 -0
  98. package/index.js +3 -0
  99. package/lib/acl-checker.js +274 -0
  100. package/lib/api/accounts/user-accounts.js +88 -0
  101. package/lib/api/authn/force-user.js +21 -0
  102. package/lib/api/authn/index.js +5 -0
  103. package/lib/api/authn/webid-oidc.js +202 -0
  104. package/lib/api/authn/webid-tls.js +69 -0
  105. package/lib/api/index.js +6 -0
  106. package/lib/capability-discovery.js +54 -0
  107. package/lib/common/fs-utils.js +43 -0
  108. package/lib/common/template-utils.js +50 -0
  109. package/lib/common/user-utils.js +28 -0
  110. package/lib/create-app.js +322 -0
  111. package/lib/create-server.js +107 -0
  112. package/lib/debug.js +17 -0
  113. package/lib/handlers/allow.js +82 -0
  114. package/lib/handlers/auth-proxy.js +63 -0
  115. package/lib/handlers/copy.js +39 -0
  116. package/lib/handlers/cors-proxy.js +95 -0
  117. package/lib/handlers/delete.js +23 -0
  118. package/lib/handlers/error-pages.js +212 -0
  119. package/lib/handlers/get.js +219 -0
  120. package/lib/handlers/index.js +42 -0
  121. package/lib/handlers/options.js +33 -0
  122. package/lib/handlers/patch/n3-patch-parser.js +49 -0
  123. package/lib/handlers/patch/sparql-update-parser.js +16 -0
  124. package/lib/handlers/patch.js +203 -0
  125. package/lib/handlers/post.js +99 -0
  126. package/lib/handlers/put.js +56 -0
  127. package/lib/handlers/restrict-to-top-domain.js +13 -0
  128. package/lib/header.js +136 -0
  129. package/lib/http-error.js +34 -0
  130. package/lib/ldp-container.js +161 -0
  131. package/lib/ldp-copy.js +73 -0
  132. package/lib/ldp-middleware.js +32 -0
  133. package/lib/ldp.js +620 -0
  134. package/lib/lock.js +10 -0
  135. package/lib/metadata.js +10 -0
  136. package/lib/models/account-manager.js +603 -0
  137. package/lib/models/account-template.js +152 -0
  138. package/lib/models/authenticator.js +333 -0
  139. package/lib/models/oidc-manager.js +53 -0
  140. package/lib/models/solid-host.js +131 -0
  141. package/lib/models/user-account.js +112 -0
  142. package/lib/models/webid-tls-certificate.js +184 -0
  143. package/lib/payment-pointer-discovery.js +83 -0
  144. package/lib/requests/add-cert-request.js +138 -0
  145. package/lib/requests/auth-request.js +234 -0
  146. package/lib/requests/create-account-request.js +468 -0
  147. package/lib/requests/delete-account-confirm-request.js +170 -0
  148. package/lib/requests/delete-account-request.js +144 -0
  149. package/lib/requests/login-request.js +205 -0
  150. package/lib/requests/password-change-request.js +201 -0
  151. package/lib/requests/password-reset-email-request.js +199 -0
  152. package/lib/requests/sharing-request.js +259 -0
  153. package/lib/resource-mapper.js +198 -0
  154. package/lib/server-config.js +167 -0
  155. package/lib/services/blacklist-service.js +33 -0
  156. package/lib/services/email-service.js +162 -0
  157. package/lib/services/token-service.js +47 -0
  158. package/lib/utils.js +254 -0
  159. package/lib/webid/index.js +13 -0
  160. package/lib/webid/lib/get.js +27 -0
  161. package/lib/webid/lib/parse.js +12 -0
  162. package/lib/webid/tls/index.js +185 -0
  163. package/package.json +172 -0
  164. package/renovate.json +5 -0
  165. package/robots.txt +3 -0
  166. package/robots.txt.acl +15 -0
  167. package/static/account-recovery.html +78 -0
  168. package/static/popup-redirect.html +1 -0
  169. package/static/signup.html +108 -0
  170. package/static/signup.html.acl +14 -0
@@ -0,0 +1,234 @@
1
+ 'use strict'
2
+ /* eslint-disable node/no-deprecated-api */
3
+
4
+ const url = require('url')
5
+ const debug = require('./../debug').authentication
6
+
7
+ const IDToken = require('@solid/oidc-op/src/IDToken')
8
+
9
+ /**
10
+ * Hidden form fields from the login page that must be passed through to the
11
+ * Authentication request.
12
+ *
13
+ * @type {Array<string>}
14
+ */
15
+ const AUTH_QUERY_PARAMS = ['response_type', 'display', 'scope',
16
+ 'client_id', 'redirect_uri', 'state', 'nonce', 'request']
17
+
18
+ /**
19
+ * Base authentication request (used for login and password reset workflows).
20
+ */
21
+ class AuthRequest {
22
+ /**
23
+ * @constructor
24
+ * @param [options.response] {ServerResponse} middleware `res` object
25
+ * @param [options.session] {Session} req.session
26
+ * @param [options.userStore] {UserStore}
27
+ * @param [options.accountManager] {AccountManager}
28
+ * @param [options.returnToUrl] {string}
29
+ * @param [options.authQueryParams] {Object} Key/value hashmap of parsed query
30
+ * parameters that will be passed through to the /authorize endpoint.
31
+ * @param [options.enforceToc] {boolean} Whether or not to enforce the service provider's T&C
32
+ * @param [options.tocUri] {string} URI to the service provider's T&C
33
+ */
34
+ constructor (options) {
35
+ this.response = options.response
36
+ this.session = options.session || {}
37
+ this.userStore = options.userStore
38
+ this.accountManager = options.accountManager
39
+ this.returnToUrl = options.returnToUrl
40
+ this.authQueryParams = options.authQueryParams || {}
41
+ this.localAuth = options.localAuth
42
+ this.enforceToc = options.enforceToc
43
+ this.tocUri = options.tocUri
44
+ }
45
+
46
+ /**
47
+ * Extracts a given parameter from the request - either from a GET query param,
48
+ * a POST body param, or an express registered `/:param`.
49
+ * Usage:
50
+ *
51
+ * ```
52
+ * AuthRequest.parseParameter(req, 'client_id')
53
+ * // -> 'client123'
54
+ * ```
55
+ *
56
+ * @param req {IncomingRequest}
57
+ * @param parameter {string} Parameter key
58
+ *
59
+ * @return {string|null}
60
+ */
61
+ static parseParameter (req, parameter) {
62
+ const query = req.query || {}
63
+ const body = req.body || {}
64
+ const params = req.params || {}
65
+
66
+ return query[parameter] || body[parameter] || params[parameter] || null
67
+ }
68
+
69
+ /**
70
+ * Extracts the options in common to most auth-related requests.
71
+ *
72
+ * @param req
73
+ * @param res
74
+ *
75
+ * @return {Object}
76
+ */
77
+ static requestOptions (req, res) {
78
+ let userStore, accountManager, localAuth
79
+
80
+ if (req.app && req.app.locals) {
81
+ const locals = req.app.locals
82
+
83
+ if (locals.oidc) {
84
+ userStore = locals.oidc.users
85
+ }
86
+
87
+ accountManager = locals.accountManager
88
+
89
+ localAuth = locals.localAuth
90
+ }
91
+
92
+ const authQueryParams = AuthRequest.extractAuthParams(req)
93
+ const returnToUrl = AuthRequest.parseParameter(req, 'returnToUrl')
94
+ const acceptToc = AuthRequest.parseParameter(req, 'acceptToc') === 'true'
95
+
96
+ const options = {
97
+ response: res,
98
+ session: req.session,
99
+ userStore,
100
+ accountManager,
101
+ returnToUrl,
102
+ authQueryParams,
103
+ localAuth,
104
+ acceptToc
105
+ }
106
+
107
+ return options
108
+ }
109
+
110
+ /**
111
+ * Initializes query params required by Oauth2/OIDC type work flow from the
112
+ * request body.
113
+ * Only authorized params are loaded, all others are discarded.
114
+ *
115
+ * @param req {IncomingRequest}
116
+ *
117
+ * @return {Object}
118
+ */
119
+ static extractAuthParams (req) {
120
+ let params
121
+ if (req.method === 'POST') {
122
+ params = req.body
123
+ } else {
124
+ params = req.query
125
+ }
126
+
127
+ if (!params) { return {} }
128
+
129
+ const extracted = {}
130
+
131
+ const paramKeys = AUTH_QUERY_PARAMS
132
+ let value
133
+
134
+ for (const p of paramKeys) {
135
+ value = params[p]
136
+ // value = value === 'undefined' ? undefined : value
137
+ extracted[p] = value
138
+ }
139
+
140
+ // Special case because solid-auth-client does not include redirect in params
141
+ if (!extracted.redirect_uri && params.request) {
142
+ extracted.redirect_uri = IDToken.decode(params.request).payload.redirect_uri
143
+ }
144
+
145
+ return extracted
146
+ }
147
+
148
+ /**
149
+ * Calls the appropriate form to display to the user.
150
+ * Serves as an error handler for this request workflow.
151
+ *
152
+ * @param error {Error}
153
+ */
154
+ error (error, body) {
155
+ error.statusCode = error.statusCode || 400
156
+
157
+ this.renderForm(error, body)
158
+ }
159
+
160
+ /**
161
+ * Initializes a session (for subsequent authentication/authorization) with
162
+ * a given user's credentials.
163
+ *
164
+ * @param userAccount {UserAccount}
165
+ */
166
+ initUserSession (userAccount) {
167
+ const session = this.session
168
+
169
+ debug('Initializing user session with webId: ', userAccount.webId)
170
+
171
+ session.userId = userAccount.webId
172
+ session.subject = {
173
+ _id: userAccount.webId
174
+ }
175
+
176
+ return userAccount
177
+ }
178
+
179
+ /**
180
+ * Returns this installation's /authorize url. Used for redirecting post-login
181
+ * and post-signup.
182
+ *
183
+ * @return {string}
184
+ */
185
+ authorizeUrl () {
186
+ const host = this.accountManager.host
187
+ const authUrl = host.authEndpoint
188
+
189
+ authUrl.query = this.authQueryParams
190
+
191
+ return url.format(authUrl)
192
+ }
193
+
194
+ /**
195
+ * Returns this installation's /register url. Used for redirecting post-signup.
196
+ *
197
+ * @return {string}
198
+ */
199
+ registerUrl () {
200
+ const host = this.accountManager.host
201
+ const signupUrl = url.parse(url.resolve(host.serverUri, '/register'))
202
+
203
+ signupUrl.query = this.authQueryParams
204
+
205
+ return url.format(signupUrl)
206
+ }
207
+
208
+ /**
209
+ * Returns this installation's /login url.
210
+ *
211
+ * @return {string}
212
+ */
213
+ loginUrl () {
214
+ const host = this.accountManager.host
215
+ const signupUrl = url.parse(url.resolve(host.serverUri, '/login'))
216
+
217
+ signupUrl.query = this.authQueryParams
218
+
219
+ return url.format(signupUrl)
220
+ }
221
+
222
+ sharingUrl () {
223
+ const host = this.accountManager.host
224
+ const sharingUrl = url.parse(url.resolve(host.serverUri, '/sharing'))
225
+
226
+ sharingUrl.query = this.authQueryParams
227
+
228
+ return url.format(sharingUrl)
229
+ }
230
+ }
231
+
232
+ AuthRequest.AUTH_QUERY_PARAMS = AUTH_QUERY_PARAMS
233
+
234
+ module.exports = AuthRequest
@@ -0,0 +1,468 @@
1
+ 'use strict'
2
+
3
+ const AuthRequest = require('./auth-request')
4
+ const WebIdTlsCertificate = require('../models/webid-tls-certificate')
5
+ const debug = require('../debug').accounts
6
+ const blacklistService = require('../services/blacklist-service')
7
+ const { isValidUsername } = require('../common/user-utils')
8
+
9
+ /**
10
+ * Represents a 'create new user account' http request (either a POST to the
11
+ * `/accounts/api/new` endpoint, or a GET to `/register`).
12
+ *
13
+ * Intended just for browser-based requests; to create new user accounts from
14
+ * a command line, use the `AccountManager` class directly.
15
+ *
16
+ * This is an abstract class, subclasses are created (for example
17
+ * `CreateOidcAccountRequest`) depending on which Authentication mode the server
18
+ * is running in.
19
+ *
20
+ * @class CreateAccountRequest
21
+ */
22
+ class CreateAccountRequest extends AuthRequest {
23
+ /**
24
+ * @param [options={}] {Object}
25
+ * @param [options.accountManager] {AccountManager}
26
+ * @param [options.userAccount] {UserAccount}
27
+ * @param [options.session] {Session} e.g. req.session
28
+ * @param [options.response] {HttpResponse}
29
+ * @param [options.returnToUrl] {string} If present, redirect the agent to
30
+ * this url on successful account creation
31
+ * @param [options.enforceToc] {boolean} Whether or not to enforce the service provider's T&C
32
+ * @param [options.tocUri] {string} URI to the service provider's T&C
33
+ * @param [options.acceptToc] {boolean} Whether or not user has accepted T&C
34
+ */
35
+ constructor (options) {
36
+ super(options)
37
+
38
+ this.username = options.username
39
+ this.userAccount = options.userAccount
40
+ this.acceptToc = options.acceptToc
41
+ this.disablePasswordChecks = options.disablePasswordChecks
42
+ }
43
+
44
+ /**
45
+ * Factory method, creates an appropriate CreateAccountRequest subclass from
46
+ * an HTTP request (browser form submit), depending on the authn method.
47
+ *
48
+ * @param req
49
+ * @param res
50
+ *
51
+ * @throws {Error} If required parameters are missing (via
52
+ * `userAccountFrom()`), or it encounters an unsupported authentication
53
+ * scheme.
54
+ *
55
+ * @return {CreateOidcAccountRequest|CreateTlsAccountRequest}
56
+ */
57
+ static fromParams (req, res) {
58
+ const options = AuthRequest.requestOptions(req, res)
59
+
60
+ const locals = req.app.locals
61
+ const authMethod = locals.authMethod
62
+ const accountManager = locals.accountManager
63
+
64
+ const body = req.body || {}
65
+
66
+ if (body.username) {
67
+ options.username = body.username.toLowerCase()
68
+ options.userAccount = accountManager.userAccountFrom(body)
69
+ }
70
+
71
+ options.enforceToc = locals.enforceToc
72
+ options.tocUri = locals.tocUri
73
+ options.disablePasswordChecks = locals.disablePasswordChecks
74
+
75
+ switch (authMethod) {
76
+ case 'oidc':
77
+ options.password = body.password
78
+ return new CreateOidcAccountRequest(options)
79
+ case 'tls':
80
+ options.spkac = body.spkac
81
+ return new CreateTlsAccountRequest(options)
82
+ default:
83
+ throw new TypeError('Unsupported authentication scheme')
84
+ }
85
+ }
86
+
87
+ static async post (req, res) {
88
+ const request = CreateAccountRequest.fromParams(req, res)
89
+
90
+ try {
91
+ request.validate()
92
+ await request.createAccount()
93
+ } catch (error) {
94
+ request.error(error, req.body)
95
+ }
96
+ }
97
+
98
+ static get (req, res) {
99
+ const request = CreateAccountRequest.fromParams(req, res)
100
+
101
+ return Promise.resolve()
102
+ .then(() => request.renderForm())
103
+ .catch(error => request.error(error))
104
+ }
105
+
106
+ /**
107
+ * Renders the Register form
108
+ */
109
+ renderForm (error, data = {}) {
110
+ const authMethod = this.accountManager.authMethod
111
+
112
+ const params = Object.assign({}, this.authQueryParams, {
113
+ enforceToc: this.enforceToc,
114
+ loginUrl: this.loginUrl(),
115
+ multiuser: this.accountManager.multiuser,
116
+ registerDisabled: authMethod === 'tls',
117
+ returnToUrl: this.returnToUrl,
118
+ tocUri: this.tocUri,
119
+ disablePasswordChecks: this.disablePasswordChecks,
120
+ username: data.username,
121
+ name: data.name,
122
+ email: data.email,
123
+ acceptToc: data.acceptToc
124
+ })
125
+
126
+ if (error) {
127
+ params.error = error.message
128
+ this.response.status(error.statusCode)
129
+ }
130
+
131
+ this.response.render('account/register', params)
132
+ }
133
+
134
+ /**
135
+ * Creates an account for a given user (from a POST to `/api/accounts/new`)
136
+ *
137
+ * @throws {Error} If errors were encountering while validating the username.
138
+ *
139
+ * @return {Promise<UserAccount>} Resolves with newly created account instance
140
+ */
141
+ async createAccount () {
142
+ const userAccount = this.userAccount
143
+ const accountManager = this.accountManager
144
+
145
+ if (userAccount.externalWebId) {
146
+ const error = new Error('Linked users not currently supported, sorry (external WebID without TLS?)')
147
+ error.statusCode = 400
148
+ throw error
149
+ }
150
+ this.cancelIfUsernameInvalid(userAccount)
151
+ this.cancelIfBlacklistedUsername(userAccount)
152
+ await this.cancelIfAccountExists(userAccount)
153
+ await this.createAccountStorage(userAccount)
154
+ await this.saveCredentialsFor(userAccount)
155
+ await this.sendResponse(userAccount)
156
+
157
+ // 'return' not used deliberately, no need to block and wait for email
158
+ if (userAccount && userAccount.email) {
159
+ debug('Sending Welcome email')
160
+ accountManager.sendWelcomeEmail(userAccount)
161
+ }
162
+
163
+ return userAccount
164
+ }
165
+
166
+ /**
167
+ * Rejects with an error if an account already exists, otherwise simply
168
+ * resolves with the account.
169
+ *
170
+ * @param userAccount {UserAccount} Instance of the account to be created
171
+ *
172
+ * @return {Promise<UserAccount>} Chainable
173
+ */
174
+ cancelIfAccountExists (userAccount) {
175
+ const accountManager = this.accountManager
176
+
177
+ return accountManager.accountExists(userAccount.username)
178
+ .then(exists => {
179
+ if (exists) {
180
+ debug(`Canceling account creation, ${userAccount.webId} already exists`)
181
+ const error = new Error('Account already exists')
182
+ error.status = 400
183
+ throw error
184
+ }
185
+ // Account does not exist, proceed
186
+ return userAccount
187
+ })
188
+ }
189
+
190
+ /**
191
+ * Creates the root storage folder, initializes default containers and
192
+ * resources for the new account.
193
+ *
194
+ * @param userAccount {UserAccount} Instance of the account to be created
195
+ *
196
+ * @throws {Error} If errors were encountering while creating new account
197
+ * resources.
198
+ *
199
+ * @return {Promise<UserAccount>} Chainable
200
+ */
201
+ createAccountStorage (userAccount) {
202
+ return this.accountManager.createAccountFor(userAccount)
203
+ .catch(error => {
204
+ error.message = 'Error creating account storage: ' + error.message
205
+ throw error
206
+ })
207
+ .then(() => {
208
+ debug('Account storage resources created')
209
+ return userAccount
210
+ })
211
+ }
212
+
213
+ /**
214
+ * Check if a username is a valid slug.
215
+ *
216
+ * @param userAccount {UserAccount} Instance of the account to be created
217
+ *
218
+ * @throws {Error} If errors were encountering while validating the
219
+ * username.
220
+ *
221
+ * @return {UserAccount} Chainable
222
+ */
223
+ cancelIfUsernameInvalid (userAccount) {
224
+ if (!userAccount.username || !isValidUsername(userAccount.username)) {
225
+ debug('Invalid username ' + userAccount.username)
226
+ const error = new Error('Invalid username (contains invalid characters)')
227
+ error.status = 400
228
+ throw error
229
+ }
230
+
231
+ return userAccount
232
+ }
233
+
234
+ /**
235
+ * Check if a username is a valid slug.
236
+ *
237
+ * @param userAccount {UserAccount} Instance of the account to be created
238
+ *
239
+ * @throws {Error} If username is blacklisted
240
+ *
241
+ * @return {UserAccount} Chainable
242
+ */
243
+ cancelIfBlacklistedUsername (userAccount) {
244
+ const validUsername = blacklistService.validate(userAccount.username)
245
+ if (!validUsername) {
246
+ debug('Invalid username ' + userAccount.username)
247
+ const error = new Error('Invalid username (username is blacklisted)')
248
+ error.status = 400
249
+ throw error
250
+ }
251
+
252
+ return userAccount
253
+ }
254
+ }
255
+
256
+ /**
257
+ * Models a Create Account request for a server using WebID-OIDC (OpenID Connect)
258
+ * as a primary authentication mode. Handles saving user credentials to the
259
+ * `UserStore`, etc.
260
+ *
261
+ * @class CreateOidcAccountRequest
262
+ * @extends CreateAccountRequest
263
+ */
264
+ class CreateOidcAccountRequest extends CreateAccountRequest {
265
+ /**
266
+ * @constructor
267
+ *
268
+ * @param [options={}] {Object} See `CreateAccountRequest` constructor docstring
269
+ * @param [options.password] {string} Password, as entered by the user at signup
270
+ * @param [options.acceptToc] {boolean} Whether or not user has accepted T&C
271
+ */
272
+ constructor (options) {
273
+ super(options)
274
+
275
+ this.password = options.password
276
+ }
277
+
278
+ /**
279
+ * Validates the Login request (makes sure required parameters are present),
280
+ * and throws an error if not.
281
+ *
282
+ * @throws {Error} If missing required params
283
+ */
284
+ validate () {
285
+ let error
286
+
287
+ if (!this.username) {
288
+ error = new Error('Username required')
289
+ error.statusCode = 400
290
+ throw error
291
+ }
292
+
293
+ if (!this.password) {
294
+ error = new Error('Password required')
295
+ error.statusCode = 400
296
+ throw error
297
+ }
298
+
299
+ if (this.enforceToc && !this.acceptToc) {
300
+ error = new Error('Accepting Terms & Conditions is required for this service')
301
+ error.statusCode = 400
302
+ throw error
303
+ }
304
+ }
305
+
306
+ /**
307
+ * Generate salted password hash, etc.
308
+ *
309
+ * @param userAccount {UserAccount}
310
+ *
311
+ * @return {Promise<null|Graph>}
312
+ */
313
+ saveCredentialsFor (userAccount) {
314
+ return this.userStore.createUser(userAccount, this.password)
315
+ .then(() => {
316
+ debug('User credentials stored')
317
+ return userAccount
318
+ })
319
+ }
320
+
321
+ /**
322
+ * Generate the response for the account creation
323
+ *
324
+ * @param userAccount {UserAccount}
325
+ *
326
+ * @return {UserAccount}
327
+ */
328
+ sendResponse (userAccount) {
329
+ const redirectUrl = this.returnToUrl || userAccount.podUri
330
+ this.response.redirect(redirectUrl)
331
+
332
+ return userAccount
333
+ }
334
+ }
335
+
336
+ /**
337
+ * Models a Create Account request for a server using WebID-TLS as primary
338
+ * authentication mode. Handles generating and saving a TLS certificate, etc.
339
+ *
340
+ * @class CreateTlsAccountRequest
341
+ * @extends CreateAccountRequest
342
+ */
343
+ class CreateTlsAccountRequest extends CreateAccountRequest {
344
+ /**
345
+ * @constructor
346
+ *
347
+ * @param [options={}] {Object} See `CreateAccountRequest` constructor docstring
348
+ * @param [options.spkac] {string}
349
+ * @param [options.acceptToc] {boolean} Whether or not user has accepted T&C
350
+ */
351
+ constructor (options) {
352
+ super(options)
353
+
354
+ this.spkac = options.spkac
355
+ this.certificate = null
356
+ }
357
+
358
+ /**
359
+ * Validates the Signup request (makes sure required parameters are present),
360
+ * and throws an error if not.
361
+ *
362
+ * @throws {Error} If missing required params
363
+ */
364
+ validate () {
365
+ let error
366
+
367
+ if (!this.username) {
368
+ error = new Error('Username required')
369
+ error.statusCode = 400
370
+ throw error
371
+ }
372
+
373
+ if (this.enforceToc && !this.acceptToc) {
374
+ error = new Error('Accepting Terms & Conditions is required for this service')
375
+ error.statusCode = 400
376
+ throw error
377
+ }
378
+ }
379
+
380
+ /**
381
+ * Generates a new X.509v3 RSA certificate (if `spkac` was passed in) and
382
+ * adds it to the user account. Used for storage in an agent's WebID
383
+ * Profile, for WebID-TLS authentication.
384
+ *
385
+ * @param userAccount {UserAccount}
386
+ * @param userAccount.webId {string} An agent's WebID URI
387
+ *
388
+ * @throws {Error} HTTP 400 error if errors were encountering during
389
+ * certificate generation.
390
+ *
391
+ * @return {Promise<UserAccount>} Chainable
392
+ */
393
+ generateTlsCertificate (userAccount) {
394
+ if (!this.spkac) {
395
+ debug('Missing spkac param, not generating cert during account creation')
396
+ return Promise.resolve(userAccount)
397
+ }
398
+
399
+ return Promise.resolve()
400
+ .then(() => {
401
+ const host = this.accountManager.host
402
+ return WebIdTlsCertificate.fromSpkacPost(this.spkac, userAccount, host)
403
+ .generateCertificate()
404
+ })
405
+ .catch(err => {
406
+ err.status = 400
407
+ err.message = 'Error generating a certificate: ' + err.message
408
+ throw err
409
+ })
410
+ .then(certificate => {
411
+ debug('Generated a WebID-TLS certificate as part of account creation')
412
+ this.certificate = certificate
413
+ return userAccount
414
+ })
415
+ }
416
+
417
+ /**
418
+ * Generates a WebID-TLS certificate and saves it to the user's profile
419
+ * graph.
420
+ *
421
+ * @param userAccount {UserAccount}
422
+ *
423
+ * @return {Promise<UserAccount>} Chainable
424
+ */
425
+ saveCredentialsFor (userAccount) {
426
+ return this.generateTlsCertificate(userAccount)
427
+ .then(userAccount => {
428
+ if (this.certificate) {
429
+ return this.accountManager
430
+ .addCertKeyToProfile(this.certificate, userAccount)
431
+ .then(() => {
432
+ debug('Saved generated WebID-TLS certificate to profile')
433
+ })
434
+ } else {
435
+ debug('No certificate generated, no need to save to profile')
436
+ }
437
+ })
438
+ .then(() => {
439
+ return userAccount
440
+ })
441
+ }
442
+
443
+ /**
444
+ * Writes the generated TLS certificate to the http Response object.
445
+ *
446
+ * @param userAccount {UserAccount}
447
+ *
448
+ * @return {UserAccount} Chainable
449
+ */
450
+ sendResponse (userAccount) {
451
+ const res = this.response
452
+ res.set('User', userAccount.webId)
453
+ res.status(200)
454
+
455
+ if (this.certificate) {
456
+ res.set('Content-Type', 'application/x-x509-user-cert')
457
+ res.send(this.certificate.toDER())
458
+ } else {
459
+ res.end()
460
+ }
461
+
462
+ return userAccount
463
+ }
464
+ }
465
+
466
+ module.exports = CreateAccountRequest
467
+ module.exports.CreateAccountRequest = CreateAccountRequest
468
+ module.exports.CreateTlsAccountRequest = CreateTlsAccountRequest