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,112 @@
1
+ 'use strict'
2
+ /* eslint-disable node/no-deprecated-api */
3
+
4
+ const url = require('url')
5
+
6
+ /**
7
+ * Represents a Solid user account (created as a result of Signup, etc).
8
+ */
9
+ class UserAccount {
10
+ /**
11
+ * @constructor
12
+ * @param [options={}] {Object}
13
+ * @param [options.username] {string}
14
+ * @param [options.webId] {string}
15
+ * @param [options.name] {string}
16
+ * @param [options.email] {string}
17
+ * @param [options.externalWebId] {string}
18
+ * @param [options.localAccountId] {string}
19
+ */
20
+ constructor (options = {}) {
21
+ this.username = options.username
22
+ this.webId = options.webId
23
+ this.name = options.name
24
+ this.email = options.email
25
+ this.externalWebId = options.externalWebId
26
+ this.localAccountId = options.localAccountId
27
+ }
28
+
29
+ /**
30
+ * Factory method, returns an instance of `UserAccount`.
31
+ *
32
+ * @param [options={}] {Object} See `contructor()` docstring.
33
+ *
34
+ * @return {UserAccount}
35
+ */
36
+ static from (options = {}) {
37
+ return new UserAccount(options)
38
+ }
39
+
40
+ /**
41
+ * Returns the display name for the account.
42
+ *
43
+ * @return {string}
44
+ */
45
+ get displayName () {
46
+ return this.name || this.username || this.email || 'Solid account'
47
+ }
48
+
49
+ /**
50
+ * Returns the id key for the user account (for use with the user store, for
51
+ * example), consisting of the WebID URI minus the protocol and slashes.
52
+ * Usage:
53
+ *
54
+ * ```
55
+ * userAccount.webId = 'https://alice.example.com/profile/card#me'
56
+ * userAccount.id // -> 'alice.example.com/profile/card#me'
57
+ * ```
58
+ *
59
+ * @return {string}
60
+ */
61
+ get id () {
62
+ if (!this.webId) { return null }
63
+
64
+ const parsed = url.parse(this.webId)
65
+ let id = parsed.host + parsed.pathname
66
+ if (parsed.hash) {
67
+ id += parsed.hash
68
+ }
69
+ return id
70
+ }
71
+
72
+ get accountUri () {
73
+ if (!this.webId) { return null }
74
+
75
+ const parsed = url.parse(this.webId)
76
+
77
+ return parsed.protocol + '//' + parsed.host
78
+ }
79
+
80
+ /**
81
+ * Returns the Uri to the account's Pod
82
+ *
83
+ * @return {string}
84
+ */
85
+ get podUri () {
86
+ const webIdUrl = url.parse(this.webId)
87
+ const podUrl = `${webIdUrl.protocol}//${webIdUrl.host}`
88
+ return url.format(podUrl)
89
+ }
90
+
91
+ /**
92
+ * Returns the URI of the WebID Profile for this account.
93
+ * Usage:
94
+ *
95
+ * ```
96
+ * // userAccount.webId === 'https://alice.example.com/profile/card#me'
97
+ *
98
+ * userAccount.profileUri // -> 'https://alice.example.com/profile/card'
99
+ * ```
100
+ *
101
+ * @return {string|null}
102
+ */
103
+ get profileUri () {
104
+ if (!this.webId) { return null }
105
+
106
+ const parsed = url.parse(this.webId)
107
+ // Note that the hash fragment gets dropped
108
+ return parsed.protocol + '//' + parsed.host + parsed.pathname
109
+ }
110
+ }
111
+
112
+ module.exports = UserAccount
@@ -0,0 +1,184 @@
1
+ 'use strict'
2
+ /* eslint-disable node/no-deprecated-api */
3
+
4
+ const webidTls = require('../webid')('tls')
5
+ const forge = require('node-forge')
6
+ const utils = require('../utils')
7
+
8
+ /**
9
+ * Models a WebID-TLS crypto certificate, as generated at signup from a browser's
10
+ * `<keygen>` element.
11
+ *
12
+ * @class WebIdTlsCertificate
13
+ */
14
+ class WebIdTlsCertificate {
15
+ /**
16
+ * @param [options={}] {Object}
17
+ * @param [options.spkac] {Buffer<string>}
18
+ * @param [options.date] {Date}
19
+ * @param [options.webId] {string}
20
+ * @param [options.commonName] {string} Certificate name
21
+ * @param [options.issuerName] {string}
22
+ */
23
+ constructor (options = {}) {
24
+ this.spkac = options.spkac
25
+ this.date = options.date || new Date()
26
+ this.webId = options.webId
27
+ this.commonName = options.commonName
28
+ this.issuer = { commonName: options.issuerName }
29
+
30
+ this.certificate = null // gets initialized in `generateCertificate()`
31
+ }
32
+
33
+ /**
34
+ * Factory method, used to construct a certificate instance from a browser-
35
+ * based signup.
36
+ *
37
+ * @param spkac {string} Signed Public Key and Challenge from a browser's
38
+ * `<keygen>` element.
39
+ * @param userAccount {UserAccount}
40
+ * @param host {SolidHost}
41
+ *
42
+ * @throws {TypeError} If no `spkac` param provided (http 400)
43
+ *
44
+ * @return {WebIdTlsCertificate}
45
+ */
46
+ static fromSpkacPost (spkac, userAccount, host) {
47
+ if (!spkac) {
48
+ const error = new TypeError('Missing spkac parameter')
49
+ error.status = 400
50
+ throw error
51
+ }
52
+
53
+ const date = new Date()
54
+ const commonName = `${userAccount.displayName} [on ${host.serverUri}, created ${date}]`
55
+
56
+ const options = {
57
+ spkac: WebIdTlsCertificate.prepPublicKey(spkac),
58
+ webId: userAccount.webId,
59
+ date,
60
+ commonName,
61
+ issuerName: host.serverUri
62
+ }
63
+
64
+ return new WebIdTlsCertificate(options)
65
+ }
66
+
67
+ /**
68
+ * Formats a `<keygen>`-generated public key and converts it into a Buffer,
69
+ * to use in TLS certificate generation.
70
+ *
71
+ * @param spkac {string} Signed Public Key and Challenge from a browser's
72
+ * `<keygen>` element.
73
+ *
74
+ * @return {Buffer<string>|null} UTF-8 string buffer of the public key, if one
75
+ * was passed in.
76
+ */
77
+ static prepPublicKey (spkac) {
78
+ if (!spkac) { return null }
79
+
80
+ spkac = utils.stripLineEndings(spkac)
81
+ spkac = new Buffer(spkac, 'utf-8')
82
+ return spkac
83
+ }
84
+
85
+ /**
86
+ * Generates an X509Certificate from the passed-in `spkac` value.
87
+ *
88
+ * @throws {Error} See `webid` module's `generate()` function.
89
+ *
90
+ * @return {Promise<WebIdTlsCertificate>} Resolves to self (chainable), with
91
+ * the `.certificate` property initialized.
92
+ */
93
+ generateCertificate () {
94
+ const certOptions = {
95
+ spkac: this.spkac,
96
+ agent: this.webId,
97
+ commonName: this.commonName,
98
+ issuer: this.issuer
99
+ }
100
+
101
+ return new Promise((resolve, reject) => {
102
+ webidTls.generate(certOptions, (err, certificate) => {
103
+ if (err) {
104
+ reject(err)
105
+ } else {
106
+ this.certificate = certificate
107
+ resolve(this)
108
+ }
109
+ })
110
+ })
111
+ }
112
+
113
+ /**
114
+ * Returns the URI (with hash fragment) for this certificate's public key,
115
+ * to be used as a subject of RDF triples in a user's WebID Profile.
116
+ *
117
+ * @throws {TypeError} HTTP 400 error if no `webId` has been set.
118
+ *
119
+ * @return {string}
120
+ */
121
+ get keyUri () {
122
+ if (!this.webId) {
123
+ const error = new TypeError('Cannot construct key URI, WebID is missing')
124
+ error.status = 400
125
+ throw error
126
+ }
127
+
128
+ const profileUri = this.webId.split('#')[0]
129
+ return profileUri + '#key-' + this.date.getTime()
130
+ }
131
+
132
+ /**
133
+ * Returns the public key exponent (for adding to a user's WebID Profile)
134
+ *
135
+ * @throws {TypeError} HTTP 400 error if no certificate has been generated.
136
+ *
137
+ * @return {string}
138
+ */
139
+ get exponent () {
140
+ if (!this.certificate) {
141
+ const error = new TypeError('Cannot return exponent, certificate has not been generated')
142
+ error.status = 400
143
+ throw error
144
+ }
145
+
146
+ return this.certificate.publicKey.e.toString()
147
+ }
148
+
149
+ /**
150
+ * Returns the public key modulus (for adding to a user's WebID Profile)
151
+ *
152
+ * @throws {TypeError} HTTP 400 error if no certificate has been generated.
153
+ *
154
+ * @return {string}
155
+ */
156
+ get modulus () {
157
+ if (!this.certificate) {
158
+ const error = new TypeError('Cannot return modulus, certificate has not been generated')
159
+ error.status = 400
160
+ throw error
161
+ }
162
+
163
+ return this.certificate.publicKey.n.toString(16).toUpperCase()
164
+ }
165
+
166
+ /**
167
+ * Converts the generated cert to DER format and returns it.
168
+ *
169
+ * @return {X509Certificate|null} In DER format
170
+ */
171
+ toDER () {
172
+ if (!this.certificate) {
173
+ return null
174
+ }
175
+
176
+ const certificateAsn = forge.pki.certificateToAsn1(this.certificate)
177
+ // Convert to DER
178
+ const certificateDer = forge.asn1.toDer(certificateAsn).getBytes()
179
+ // new Buffer(der, 'binary')
180
+ return certificateDer
181
+ }
182
+ }
183
+
184
+ module.exports = WebIdTlsCertificate
@@ -0,0 +1,83 @@
1
+ 'use strict'
2
+ /**
3
+ * @module payment-pointer-discovery
4
+ */
5
+ const express = require('express')
6
+ const { promisify } = require('util')
7
+ const fs = require('fs')
8
+ const rdf = require('rdflib')
9
+
10
+ module.exports = paymentPointerDiscovery
11
+
12
+ const PROFILE_PATH = '/profile/card'
13
+
14
+ /**
15
+ * Returns a set of routes to deal with server payment pointer discovery
16
+ * @method paymentPointerDiscovery
17
+ * @return {Router} Express router
18
+ */
19
+ function paymentPointerDiscovery () {
20
+ const router = express.Router('/')
21
+
22
+ // Advertise the server payment pointer discover endpoint
23
+ router.get('/.well-known/pay', paymentPointerDocument())
24
+ return router
25
+ }
26
+
27
+ /**
28
+ * Serves the service payment pointer document (containing server root URL, including
29
+ * any base path the user specified in config, server API endpoints, etc).
30
+ * @method paymentPointerDocument
31
+ * @param req
32
+ * @param res
33
+ * @param next
34
+ */
35
+ function paymentPointerDocument () {
36
+ return async (req, res) => {
37
+ try {
38
+ const ldp = req.app.locals.ldp
39
+ const url = ldp.resourceMapper.resolveUrl(req.hostname, PROFILE_PATH)
40
+ const contentType = 'text/turtle'
41
+ const createIfNotExists = false
42
+ const { path } = await ldp.resourceMapper.mapUrlToFile({ url, contentType, createIfNotExists })
43
+ let body
44
+ try {
45
+ // Read the file from disk
46
+ body = await promisify(fs.readFile)(path, { encoding: 'utf8' })
47
+ } catch (e) {
48
+ if (e.message.startsWith('ENOENT: no such file or directory,')) {
49
+ res.json({
50
+ error: `Please create ${PROFILE_PATH} on your pod`
51
+ })
52
+ }
53
+ }
54
+ const webid = rdf.Namespace(`${url}#`)('me')
55
+ const pp = rdf.Namespace('http://paymentpointers.org/ns#')('PaymentPointer')
56
+ let paymentPointer
57
+ try {
58
+ const graph = rdf.graph()
59
+ // Parse the file as Turtle
60
+ rdf.parse(body, graph, url, contentType)
61
+ paymentPointer = graph.any(webid, pp)
62
+ } catch (e) {
63
+ console.error(e)
64
+ res.json({
65
+ error: `Please make sure ${PROFILE_PATH} contains valid Turtle`
66
+ })
67
+ }
68
+ if (paymentPointer === null) {
69
+ res.json({ fail: 'Add triple', subject: `<${webid.value}>`, predicate: `<${pp.value}>`, object: '$alice.example' })
70
+ }
71
+ if (paymentPointer.value.startsWith('$')) {
72
+ let suffix = ''
73
+ if (paymentPointer.value.indexOf('/') === -1) {
74
+ suffix = '/.well-known/pay'
75
+ }
76
+ paymentPointer.value = `https://${paymentPointer.value.substring(1)}${suffix}`
77
+ }
78
+ res.redirect(paymentPointer.value)
79
+ } catch (e) {
80
+ res.json({ fail: e.message })
81
+ }
82
+ }
83
+ }
@@ -0,0 +1,138 @@
1
+ 'use strict'
2
+
3
+ const WebIdTlsCertificate = require('../models/webid-tls-certificate')
4
+ const debug = require('./../debug').accounts
5
+
6
+ /**
7
+ * Represents an 'add new certificate to account' request
8
+ * (a POST to `/api/accounts/cert` endpoint).
9
+ *
10
+ * Note: The account has to exist, and the user must be already logged in,
11
+ * for this to succeed.
12
+ */
13
+ class AddCertificateRequest {
14
+ /**
15
+ * @param [options={}] {Object}
16
+ * @param [options.accountManager] {AccountManager}
17
+ * @param [options.userAccount] {UserAccount}
18
+ * @param [options.certificate] {WebIdTlsCertificate}
19
+ * @param [options.response] {HttpResponse}
20
+ */
21
+ constructor (options) {
22
+ this.accountManager = options.accountManager
23
+ this.userAccount = options.userAccount
24
+ this.certificate = options.certificate
25
+ this.response = options.response
26
+ }
27
+
28
+ /**
29
+ * Handles the HTTP request (from an Express route handler).
30
+ *
31
+ * @param req
32
+ * @param res
33
+ * @param accountManager {AccountManager}
34
+ *
35
+ * @throws {TypeError}
36
+ * @throws {Error} HTTP 401 if the user is not logged in (`req.session.userId`
37
+ * does not match the intended account to which the cert is being added).
38
+ *
39
+ * @return {Promise}
40
+ */
41
+ static handle (req, res, accountManager) {
42
+ let request
43
+ try {
44
+ request = AddCertificateRequest.fromParams(req, res, accountManager)
45
+ } catch (error) {
46
+ return Promise.reject(error)
47
+ }
48
+
49
+ return AddCertificateRequest.addCertificate(request)
50
+ }
51
+
52
+ /**
53
+ * Factory method, returns an initialized instance of `AddCertificateRequest`.
54
+ *
55
+ * @param req
56
+ * @param res
57
+ * @param accountManager {AccountManager}
58
+ *
59
+ * @throws {TypeError} If required parameters missing
60
+ * @throws {Error} HTTP 401 if the user is not logged in (`req.session.userId`
61
+ * does not match the intended account to which the cert is being added).
62
+ *
63
+ * @return {AddCertificateRequest}
64
+ */
65
+ static fromParams (req, res, accountManager) {
66
+ const userAccount = accountManager.userAccountFrom(req.body)
67
+ const certificate = WebIdTlsCertificate.fromSpkacPost(
68
+ req.body.spkac,
69
+ userAccount,
70
+ accountManager.host)
71
+
72
+ debug(`Adding a new certificate for ${userAccount.webId}`)
73
+
74
+ if (req.session.userId !== userAccount.webId) {
75
+ debug(`Cannot add new certificate: signed in user is "${req.session.userId}"`)
76
+ const error = new Error("You are not logged in, so you can't create a certificate")
77
+ error.status = 401
78
+ throw error
79
+ }
80
+
81
+ const options = {
82
+ accountManager,
83
+ userAccount,
84
+ certificate,
85
+ response: res
86
+ }
87
+
88
+ return new AddCertificateRequest(options)
89
+ }
90
+
91
+ /**
92
+ * Generates a new certificate for a given user account, and adds it to that
93
+ * account's WebID Profile graph.
94
+ *
95
+ * @param request {AddCertificateRequest}
96
+ *
97
+ * @throws {Error} HTTP 400 if there were errors during certificate generation
98
+ *
99
+ * @returns {Promise}
100
+ */
101
+ static addCertificate (request) {
102
+ const { certificate, userAccount, accountManager } = request
103
+
104
+ return certificate.generateCertificate()
105
+ .catch(err => {
106
+ err.status = 400
107
+ err.message = 'Error generating a certificate: ' + err.message
108
+ throw err
109
+ })
110
+ .then(() => {
111
+ return accountManager.addCertKeyToProfile(certificate, userAccount)
112
+ })
113
+ .catch(err => {
114
+ err.status = 400
115
+ err.message = 'Error adding certificate to profile: ' + err.message
116
+ throw err
117
+ })
118
+ .then(() => {
119
+ request.sendResponse(certificate)
120
+ })
121
+ }
122
+
123
+ /**
124
+ * Sends the generated certificate in the response object.
125
+ *
126
+ * @param certificate {WebIdTlsCertificate}
127
+ */
128
+ sendResponse (certificate) {
129
+ const { response, userAccount } = this
130
+ response.set('User', userAccount.webId)
131
+ response.status(200)
132
+
133
+ response.set('Content-Type', 'application/x-x509-user-cert')
134
+ response.send(certificate.toDER())
135
+ }
136
+ }
137
+
138
+ module.exports = AddCertificateRequest