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,322 @@
1
+ module.exports = createApp
2
+
3
+ const express = require('express')
4
+ const session = require('express-session')
5
+ const handlebars = require('express-handlebars')
6
+ const uuid = require('uuid')
7
+ const cors = require('cors')
8
+ const LDP = require('./ldp')
9
+ const LdpMiddleware = require('./ldp-middleware')
10
+ const corsProxy = require('./handlers/cors-proxy')
11
+ const authProxy = require('./handlers/auth-proxy')
12
+ const SolidHost = require('./models/solid-host')
13
+ const AccountManager = require('./models/account-manager')
14
+ const vhost = require('vhost')
15
+ const EmailService = require('./services/email-service')
16
+ const TokenService = require('./services/token-service')
17
+ const capabilityDiscovery = require('./capability-discovery')
18
+ const paymentPointerDiscovery = require('./payment-pointer-discovery')
19
+ const API = require('./api')
20
+ const errorPages = require('./handlers/error-pages')
21
+ const config = require('./server-config')
22
+ const defaults = require('../config/defaults')
23
+ const options = require('./handlers/options')
24
+ const debug = require('./debug')
25
+ const path = require('path')
26
+ const { routeResolvedFile } = require('./utils')
27
+ const ResourceMapper = require('./resource-mapper')
28
+ const aclCheck = require('@solid/acl-check')
29
+ const { version } = require('../package.json')
30
+
31
+ const corsSettings = cors({
32
+ methods: [
33
+ 'OPTIONS', 'HEAD', 'GET', 'PATCH', 'POST', 'PUT', 'DELETE'
34
+ ],
35
+ exposedHeaders: 'Authorization, User, Location, Link, Vary, Last-Modified, ETag, Accept-Patch, Accept-Post, Updates-Via, Allow, WAC-Allow, Content-Length, WWW-Authenticate, MS-Author-Via, X-Powered-By',
36
+ credentials: true,
37
+ maxAge: 1728000,
38
+ origin: true,
39
+ preflightContinue: true
40
+ })
41
+
42
+ function createApp (argv = {}) {
43
+ // Override default configs (defaults) with passed-in params (argv)
44
+ argv = Object.assign({}, defaults, argv)
45
+
46
+ argv.host = SolidHost.from(argv)
47
+
48
+ argv.resourceMapper = new ResourceMapper({
49
+ rootUrl: argv.serverUri,
50
+ rootPath: path.resolve(argv.root || process.cwd()),
51
+ includeHost: argv.multiuser,
52
+ defaultContentType: argv.defaultContentType
53
+ })
54
+
55
+ const configPath = config.initConfigDir(argv)
56
+ argv.templates = config.initTemplateDirs(configPath)
57
+
58
+ config.printDebugInfo(argv)
59
+
60
+ const ldp = new LDP(argv)
61
+
62
+ const app = express()
63
+
64
+ initAppLocals(app, argv, ldp)
65
+ initHeaders(app)
66
+ initViews(app, configPath)
67
+ initLoggers()
68
+
69
+ // Serve the public 'common' directory (for shared CSS files, etc)
70
+ app.use('/common', express.static(path.join(__dirname, '../common')))
71
+ app.use('/', express.static(path.dirname(require.resolve('mashlib/dist/databrowser.html')), { index: false }))
72
+ routeResolvedFile(app, '/common/js/', 'solid-auth-client/dist-lib/solid-auth-client.bundle.js')
73
+ routeResolvedFile(app, '/common/js/', 'solid-auth-client/dist-lib/solid-auth-client.bundle.js.map')
74
+ app.use('/.well-known', express.static(path.join(__dirname, '../common/well-known')))
75
+
76
+ // Serve bootstrap from it's node_module directory
77
+ routeResolvedFile(app, '/common/css/', 'bootstrap/dist/css/bootstrap.min.css')
78
+ routeResolvedFile(app, '/common/css/', 'bootstrap/dist/css/bootstrap.min.css.map')
79
+ routeResolvedFile(app, '/common/fonts/', 'bootstrap/dist/fonts/glyphicons-halflings-regular.eot')
80
+ routeResolvedFile(app, '/common/fonts/', 'bootstrap/dist/fonts/glyphicons-halflings-regular.svg')
81
+ routeResolvedFile(app, '/common/fonts/', 'bootstrap/dist/fonts/glyphicons-halflings-regular.ttf')
82
+ routeResolvedFile(app, '/common/fonts/', 'bootstrap/dist/fonts/glyphicons-halflings-regular.woff')
83
+ routeResolvedFile(app, '/common/fonts/', 'bootstrap/dist/fonts/glyphicons-halflings-regular.woff2')
84
+
85
+ // Serve OWASP password checker from it's node_module directory
86
+ routeResolvedFile(app, '/common/js/', 'owasp-password-strength-test/owasp-password-strength-test.js')
87
+ // Serve the TextEncoder polyfill
88
+ routeResolvedFile(app, '/common/js/', 'text-encoder-lite/text-encoder-lite.min.js')
89
+
90
+ // Add CORS proxy
91
+ if (argv.proxy) {
92
+ console.warn('The proxy configuration option has been renamed to corsProxy.')
93
+ argv.corsProxy = argv.corsProxy || argv.proxy
94
+ delete argv.proxy
95
+ }
96
+ if (argv.corsProxy) {
97
+ corsProxy(app, argv.corsProxy)
98
+ }
99
+
100
+ // Options handler
101
+ app.options('/*', options)
102
+
103
+ // Set up API
104
+ if (argv.apiApps) {
105
+ app.use('/api/apps', express.static(argv.apiApps))
106
+ }
107
+
108
+ // Authenticate the user
109
+ if (argv.webid) {
110
+ initWebId(argv, app, ldp)
111
+ }
112
+ // Add Auth proxy (requires authentication)
113
+ if (argv.authProxy) {
114
+ authProxy(app, argv.authProxy)
115
+ }
116
+
117
+ // Attach the LDP middleware
118
+ app.use('/', LdpMiddleware(corsSettings))
119
+
120
+ // Errors
121
+ app.use(errorPages.handler)
122
+
123
+ return app
124
+ }
125
+
126
+ /**
127
+ * Initializes `app.locals` parameters for downstream use (typically by route
128
+ * handlers).
129
+ *
130
+ * @param app {Function} Express.js app instance
131
+ * @param argv {Object} Config options hashmap
132
+ * @param ldp {LDP}
133
+ */
134
+ function initAppLocals (app, argv, ldp) {
135
+ app.locals.ldp = ldp
136
+ app.locals.appUrls = argv.apps // used for service capability discovery
137
+ app.locals.host = argv.host
138
+ app.locals.authMethod = argv.auth
139
+ app.locals.localAuth = argv.localAuth
140
+ app.locals.tokenService = new TokenService()
141
+ app.locals.enforceToc = argv.enforceToc
142
+ app.locals.tocUri = argv.tocUri
143
+ app.locals.disablePasswordChecks = argv.disablePasswordChecks
144
+
145
+ if (argv.email && argv.email.host) {
146
+ app.locals.emailService = new EmailService(argv.templates.email, argv.email)
147
+ }
148
+ }
149
+
150
+ /**
151
+ * Sets up headers common to all Solid requests (CORS-related, Allow, etc).
152
+ *
153
+ * @param app {Function} Express.js app instance
154
+ */
155
+ function initHeaders (app) {
156
+ app.use(corsSettings)
157
+
158
+ app.use((req, res, next) => {
159
+ res.set('X-Powered-By', 'solid-server/' + version)
160
+
161
+ // Cors lib adds Vary: Origin automatically, but inreliably
162
+ res.set('Vary', 'Accept, Authorization, Origin')
163
+
164
+ // Set default Allow methods
165
+ res.set('Allow', 'OPTIONS, HEAD, GET, PATCH, POST, PUT, DELETE')
166
+ next()
167
+ })
168
+
169
+ app.use('/', capabilityDiscovery())
170
+ app.use('/', paymentPointerDiscovery())
171
+ }
172
+
173
+ /**
174
+ * Sets up the express rendering engine and views directory.
175
+ *
176
+ * @param app {Function} Express.js app
177
+ * @param configPath {string}
178
+ */
179
+ function initViews (app, configPath) {
180
+ const viewsPath = config.initDefaultViews(configPath)
181
+
182
+ app.set('views', viewsPath)
183
+ app.engine('.hbs', handlebars({
184
+ extname: '.hbs',
185
+ partialsDir: viewsPath,
186
+ defaultLayout: null
187
+ }))
188
+ app.set('view engine', '.hbs')
189
+ }
190
+
191
+ /**
192
+ * Sets up WebID-related functionality (account creation and authentication)
193
+ *
194
+ * @param argv {Object}
195
+ * @param app {Function}
196
+ * @param ldp {LDP}
197
+ */
198
+ function initWebId (argv, app, ldp) {
199
+ config.ensureWelcomePage(argv)
200
+
201
+ // Store the user's session key in a cookie
202
+ // (for same-domain browsing by people only)
203
+ const useSecureCookies = !!argv.sslKey // use secure cookies when over HTTPS
204
+ const sessionHandler = session(sessionSettings(useSecureCookies, argv.host))
205
+ app.use(sessionHandler)
206
+ // Reject cookies from third-party applications.
207
+ // Otherwise, when a user is logged in to their Solid server,
208
+ // any third-party application could perform authenticated requests
209
+ // without permission by including the credentials set by the Solid server.
210
+ app.use((req, res, next) => {
211
+ const origin = req.get('origin')
212
+ const trustedOrigins = ldp.getTrustedOrigins(req)
213
+ const userId = req.session.userId
214
+ // Exception: allow logout requests from all third-party apps
215
+ // such that OIDC client can log out via cookie auth
216
+ // TODO: remove this exception when OIDC clients
217
+ // use Bearer token to authenticate instead of cookie
218
+ // (https://github.com/solid/node-solid-server/pull/835#issuecomment-426429003)
219
+ //
220
+ // Authentication cookies are an optimization:
221
+ // instead of going through the process of
222
+ // fully validating authentication on every request,
223
+ // we go through this process once,
224
+ // and store its successful result in a cookie
225
+ // that will be reused upon the next request.
226
+ // However, that cookie can then be sent by any server,
227
+ // even servers that have not gone through the proper authentication mechanism.
228
+ // However, if trusted origins are enabled,
229
+ // then any origin is allowed to take the shortcut route,
230
+ // since malicious origins will be banned at the ACL checking phase.
231
+ // https://github.com/solid/node-solid-server/issues/1117
232
+ if (!argv.strictOrigin && !argv.host.allowsSessionFor(userId, origin, trustedOrigins) && !isLogoutRequest(req)) {
233
+ debug.authentication(`Rejecting session for ${userId} from ${origin}`)
234
+ // Destroy session data
235
+ delete req.session.userId
236
+ // Ensure this modified session is not saved
237
+ req.session.save = (done) => done()
238
+ }
239
+ if (isLogoutRequest(req)) {
240
+ delete req.session.userId
241
+ }
242
+ next()
243
+ })
244
+
245
+ const accountManager = AccountManager.from({
246
+ authMethod: argv.auth,
247
+ emailService: app.locals.emailService,
248
+ tokenService: app.locals.tokenService,
249
+ host: argv.host,
250
+ accountTemplatePath: argv.templates.account,
251
+ store: ldp,
252
+ multiuser: argv.multiuser
253
+ })
254
+ app.locals.accountManager = accountManager
255
+
256
+ // Account Management API (create account, new cert)
257
+ app.use('/', API.accounts.middleware(accountManager))
258
+
259
+ // Set up authentication-related API endpoints and app.locals
260
+ initAuthentication(app, argv)
261
+
262
+ if (argv.multiuser) {
263
+ app.use(vhost('*', LdpMiddleware(corsSettings)))
264
+ }
265
+ }
266
+
267
+ function initLoggers () {
268
+ aclCheck.configureLogger(debug.ACL)
269
+ }
270
+
271
+ /**
272
+ * Determines whether the given request is a logout request
273
+ */
274
+ function isLogoutRequest (req) {
275
+ // TODO: this is a hack that hard-codes OIDC paths,
276
+ // this code should live in the OIDC module
277
+ return req.path === '/logout' || req.path === '/goodbye'
278
+ }
279
+
280
+ /**
281
+ * Sets up authentication-related routes and handlers for the app.
282
+ *
283
+ * @param app {Object} Express.js app instance
284
+ * @param argv {Object} Config options hashmap
285
+ */
286
+ function initAuthentication (app, argv) {
287
+ const auth = argv.forceUser ? 'forceUser' : argv.auth
288
+ if (!(auth in API.authn)) {
289
+ throw new Error(`Unsupported authentication scheme: ${auth}`)
290
+ }
291
+ API.authn[auth].initialize(app, argv)
292
+ }
293
+
294
+ /**
295
+ * Returns a settings object for Express.js sessions.
296
+ *
297
+ * @param secureCookies {boolean}
298
+ * @param host {SolidHost}
299
+ *
300
+ * @return {Object} `express-session` settings object
301
+ */
302
+ function sessionSettings (secureCookies, host) {
303
+ const sessionSettings = {
304
+ name: 'nssidp.sid',
305
+ secret: uuid.v1(),
306
+ saveUninitialized: false,
307
+ resave: false,
308
+ rolling: true,
309
+ cookie: {
310
+ maxAge: 24 * 60 * 60 * 1000
311
+ }
312
+ }
313
+ // Cookies should set to be secure if https is on
314
+ if (secureCookies) {
315
+ sessionSettings.cookie.secure = true
316
+ }
317
+
318
+ // Determine the cookie domain
319
+ sessionSettings.cookie.domain = host.cookieDomain
320
+
321
+ return sessionSettings
322
+ }
@@ -0,0 +1,107 @@
1
+ module.exports = createServer
2
+
3
+ const express = require('express')
4
+ const fs = require('fs')
5
+ const https = require('https')
6
+ const http = require('http')
7
+ const SolidWs = require('solid-ws')
8
+ const debug = require('./debug')
9
+ const createApp = require('./create-app')
10
+ const globalTunnel = require('global-tunnel-ng')
11
+
12
+ function createServer (argv, app) {
13
+ argv = argv || {}
14
+ app = app || express()
15
+ const ldpApp = createApp(argv)
16
+ const ldp = ldpApp.locals.ldp || {}
17
+ let mount = argv.mount || '/'
18
+ // Removing ending '/'
19
+ if (mount.length > 1 &&
20
+ mount[mount.length - 1] === '/') {
21
+ mount = mount.slice(0, -1)
22
+ }
23
+ app.use(mount, ldpApp)
24
+ debug.settings('Base URL (--mount): ' + mount)
25
+
26
+ if (argv.idp) {
27
+ console.warn('The idp configuration option has been renamed to multiuser.')
28
+ argv.multiuser = argv.idp
29
+ delete argv.idp
30
+ }
31
+
32
+ if (argv.httpProxy) {
33
+ globalTunnel.initialize(argv.httpProxy)
34
+ }
35
+
36
+ let server
37
+ const needsTLS = argv.sslKey || argv.sslCert
38
+ if (!needsTLS) {
39
+ server = http.createServer(app)
40
+ } else {
41
+ debug.settings('SSL Private Key path: ' + argv.sslKey)
42
+ debug.settings('SSL Certificate path: ' + argv.sslCert)
43
+
44
+ if (!argv.sslCert && !argv.sslKey) {
45
+ throw new Error('Missing SSL cert and SSL key to enable WebIDs')
46
+ }
47
+
48
+ if (!argv.sslKey && argv.sslCert) {
49
+ throw new Error('Missing path for SSL key')
50
+ }
51
+
52
+ if (!argv.sslCert && argv.sslKey) {
53
+ throw new Error('Missing path for SSL cert')
54
+ }
55
+
56
+ let key
57
+ try {
58
+ key = fs.readFileSync(argv.sslKey)
59
+ } catch (e) {
60
+ throw new Error('Can\'t find SSL key in ' + argv.sslKey)
61
+ }
62
+
63
+ let cert
64
+ try {
65
+ cert = fs.readFileSync(argv.sslCert)
66
+ } catch (e) {
67
+ throw new Error('Can\'t find SSL cert in ' + argv.sslCert)
68
+ }
69
+
70
+ const credentials = Object.assign({
71
+ key: key,
72
+ cert: cert
73
+ }, argv)
74
+
75
+ if (ldp.webid && ldp.auth === 'tls') {
76
+ credentials.requestCert = true
77
+ }
78
+
79
+ server = https.createServer(credentials, app)
80
+ }
81
+
82
+ // Look for port or list of ports to redirect to argv.port
83
+ if ('redirectHttpFrom' in argv) {
84
+ const redirectHttpFroms = argv.redirectHttpFrom.constructor === Array
85
+ ? argv.redirectHttpFrom
86
+ : [argv.redirectHttpFrom]
87
+ const portStr = argv.port === 443 ? '' : ':' + argv.port
88
+ redirectHttpFroms.forEach(redirectHttpFrom => {
89
+ debug.settings('will redirect from port ' + redirectHttpFrom + ' to port ' + argv.port)
90
+ const redirectingServer = express()
91
+ redirectingServer.get('*', function (req, res) {
92
+ const host = req.headers.host.split(':') // ignore port
93
+ debug.server(host, '=> https://' + host + portStr + req.url)
94
+ res.redirect('https://' + host + portStr + req.url)
95
+ })
96
+ redirectingServer.listen(redirectHttpFrom)
97
+ })
98
+ }
99
+
100
+ // Setup Express app
101
+ if (ldp.live) {
102
+ const solidWs = SolidWs(server, ldpApp)
103
+ ldpApp.locals.ldp.live = solidWs.publish.bind(solidWs)
104
+ }
105
+
106
+ return server
107
+ }
package/lib/debug.js ADDED
@@ -0,0 +1,17 @@
1
+ const debug = require('debug')
2
+
3
+ exports.handlers = debug('solid:handlers')
4
+ exports.errors = debug('solid:errors')
5
+ exports.ACL = debug('solid:ACL')
6
+ exports.cache = debug('solid:cache')
7
+ exports.parse = debug('solid:parse')
8
+ exports.metadata = debug('solid:metadata')
9
+ exports.authentication = debug('solid:authentication')
10
+ exports.settings = debug('solid:settings')
11
+ exports.server = debug('solid:server')
12
+ exports.subscription = debug('solid:subscription')
13
+ exports.container = debug('solid:container')
14
+ exports.accounts = debug('solid:accounts')
15
+ exports.email = debug('solid:email')
16
+ exports.ldp = debug('solid:ldp')
17
+ exports.fs = debug('solid:fs')
@@ -0,0 +1,82 @@
1
+ module.exports = allow
2
+
3
+ // const path = require('path')
4
+ const ACL = require('../acl-checker')
5
+ const debug = require('../debug.js').ACL
6
+ // const error = require('../http-error')
7
+
8
+ function allow (mode) {
9
+ return async function allowHandler (req, res, next) {
10
+ const ldp = req.app.locals.ldp || {}
11
+ if (!ldp.webid) {
12
+ return next()
13
+ }
14
+
15
+ // Set up URL to filesystem mapping
16
+ const rootUrl = ldp.resourceMapper.resolveUrl(req.hostname)
17
+
18
+ // Determine the actual path of the request
19
+ // (This is used as an ugly hack to check the ACL status of other resources.)
20
+ let resourcePath = res && res.locals && res.locals.path
21
+ ? res.locals.path
22
+ : req.path
23
+
24
+ // Check whether the resource exists
25
+ let stat
26
+ try {
27
+ const ret = await ldp.exists(req.hostname, resourcePath)
28
+ stat = ret.stream
29
+ } catch (err) {
30
+ stat = null
31
+ }
32
+
33
+ // Ensure directories always end in a slash
34
+ if (!resourcePath.endsWith('/') && stat && stat.isDirectory()) {
35
+ resourcePath += '/'
36
+ }
37
+
38
+ const trustedOrigins = [ldp.resourceMapper.resolveUrl(req.hostname)].concat(ldp.trustedOrigins)
39
+ if (ldp.multiuser) {
40
+ trustedOrigins.push(ldp.serverUri)
41
+ }
42
+ // Obtain and store the ACL of the requested resource
43
+ const resourceUrl = rootUrl + resourcePath
44
+ // Ensure the user has the required permission
45
+ const userId = req.session.userId
46
+ try {
47
+ req.acl = ACL.createFromLDPAndRequest(resourceUrl, ldp, req)
48
+
49
+ // if (resourceUrl.endsWith('.acl')) mode = 'Control'
50
+ const isAllowed = await req.acl.can(userId, mode, req.method, stat)
51
+ if (isAllowed) {
52
+ return next()
53
+ }
54
+ } catch (error) { next(error) }
55
+ if (mode === 'Read' && (resourcePath === '' || resourcePath === '/')) {
56
+ // This is a hack to make NSS check the ACL for representation that is served for root (if any)
57
+ // See https://github.com/solid/node-solid-server/issues/1063 for more info
58
+ const representationUrl = `${rootUrl}/index.html`
59
+ let representationPath
60
+ try {
61
+ representationPath = await ldp.resourceMapper.mapUrlToFile({ url: representationUrl })
62
+ } catch (err) {
63
+ }
64
+
65
+ // We ONLY want to do this when the HTML representation exists
66
+ if (representationPath) {
67
+ req.acl = ACL.createFromLDPAndRequest(representationUrl, ldp, req)
68
+ const representationIsAllowed = await req.acl.can(userId, mode)
69
+ if (representationIsAllowed) {
70
+ return next()
71
+ }
72
+ }
73
+ }
74
+
75
+ // check user is owner. Find owner from /.meta
76
+ if (resourceUrl.endsWith('.acl') && userId === await ldp.getOwner(req.hostname)) return next()
77
+
78
+ const error = req.authError || await req.acl.getError(userId, mode)
79
+ debug(`${mode} access denied to ${userId || '(none)'}: ${error.status} - ${error.message}`)
80
+ next(error)
81
+ }
82
+ }
@@ -0,0 +1,63 @@
1
+ // An authentication proxy is a reverse proxy
2
+ // that sends a logged-in Solid user's details to a backend
3
+ module.exports = addAuthProxyHandlers
4
+
5
+ const { createProxyMiddleware } = require('http-proxy-middleware')
6
+ const debug = require('../debug')
7
+ const allow = require('./allow')
8
+
9
+ const PROXY_SETTINGS = {
10
+ logLevel: 'silent',
11
+ changeOrigin: true
12
+ }
13
+ const REQUIRED_PERMISSIONS = {
14
+ get: ['Read'],
15
+ options: ['Read'],
16
+ use: ['Read', 'Write']
17
+ }
18
+
19
+ // Registers Auth Proxy handlers for each target
20
+ function addAuthProxyHandlers (app, targets) {
21
+ for (const sourcePath in targets) {
22
+ addAuthProxyHandler(app, sourcePath, targets[sourcePath])
23
+ }
24
+ }
25
+
26
+ // Registers an Auth Proxy handler for the given target
27
+ function addAuthProxyHandler (app, sourcePath, target) {
28
+ debug.settings(`Add auth proxy from ${sourcePath} to ${target}`)
29
+
30
+ // Proxy to the target, removing the source path
31
+ // (e.g., /my/proxy/path resolves to http://my.proxy/path)
32
+ const sourcePathLength = sourcePath.length
33
+ const settings = Object.assign({
34
+ target,
35
+ onProxyReq: addAuthHeaders,
36
+ onProxyReqWs: addAuthHeaders,
37
+ pathRewrite: path => path.substr(sourcePathLength)
38
+ }, PROXY_SETTINGS)
39
+
40
+ // Activate the proxy
41
+ const proxy = createProxyMiddleware(settings)
42
+ for (const action in REQUIRED_PERMISSIONS) {
43
+ const permissions = REQUIRED_PERMISSIONS[action]
44
+ app[action](`${sourcePath}*`, setOriginalUrl, ...permissions.map(allow), proxy)
45
+ }
46
+ }
47
+
48
+ // Adds a headers with authentication information
49
+ function addAuthHeaders (proxyReq, req) {
50
+ const { session = {}, headers = {} } = req
51
+ if (session.userId) {
52
+ proxyReq.setHeader('User', session.userId)
53
+ }
54
+ if (headers.host) {
55
+ proxyReq.setHeader('Forwarded', `host=${headers.host}`)
56
+ }
57
+ }
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
+ }
@@ -0,0 +1,39 @@
1
+ /* eslint-disable node/no-deprecated-api */
2
+
3
+ module.exports = handler
4
+
5
+ const debug = require('../debug')
6
+ const error = require('../http-error')
7
+ const ldpCopy = require('../ldp-copy')
8
+ const url = require('url')
9
+
10
+ /**
11
+ * Handles HTTP COPY requests to import a given resource (specified in the
12
+ * `Source:` header) to a destination (specified in request path).
13
+ * For the moment, you can copy from public resources only (no auth delegation
14
+ * is implemented), and is mainly intended for use with
15
+ * "Save an external resource to Solid" type apps.
16
+ * @method handler
17
+ */
18
+ async function handler (req, res, next) {
19
+ const copyFrom = req.header('Source')
20
+ if (!copyFrom) {
21
+ return next(error(400, 'Source header required'))
22
+ }
23
+ const fromExternal = !!url.parse(copyFrom).hostname
24
+ const ldp = req.app.locals.ldp
25
+ const serverRoot = ldp.resourceMapper.resolveUrl(req.hostname)
26
+ const copyFromUrl = fromExternal ? copyFrom : serverRoot + copyFrom
27
+ const copyToUrl = res.locals.path || req.path
28
+ try {
29
+ await ldpCopy(ldp.resourceMapper, copyToUrl, copyFromUrl)
30
+ } catch (err) {
31
+ const statusCode = err.statusCode || 500
32
+ const errorMessage = err.statusMessage || err.message
33
+ debug.handlers('Error with COPY request:' + errorMessage)
34
+ return next(error(statusCode, errorMessage))
35
+ }
36
+ res.set('Location', copyToUrl)
37
+ res.sendStatus(201)
38
+ next()
39
+ }