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.
- package/.acl +10 -0
- package/.github/workflows/ci.yml +47 -0
- package/.nvmrc +1 -0
- package/.snyk +35 -0
- package/.well-known/.acl +15 -0
- package/CHANGELOG.md +198 -0
- package/CONTRIBUTING.md +139 -0
- package/CONTRIBUTORS.md +36 -0
- package/Dockerfile +22 -0
- package/LICENSE.md +23 -0
- package/README.md +453 -0
- package/bin/lib/cli-utils.js +85 -0
- package/bin/lib/cli.js +39 -0
- package/bin/lib/init.js +94 -0
- package/bin/lib/invalidUsernames.js +148 -0
- package/bin/lib/migrateLegacyResources.js +69 -0
- package/bin/lib/options.js +399 -0
- package/bin/lib/start.js +148 -0
- package/bin/lib/updateIndex.js +56 -0
- package/bin/solid +3 -0
- package/bin/solid-test +12 -0
- package/bin/solid.js +3 -0
- package/common/css/solid.css +58 -0
- package/common/fonts/glyphicons-halflings-regular.eot +0 -0
- package/common/fonts/glyphicons-halflings-regular.svg +288 -0
- package/common/fonts/glyphicons-halflings-regular.ttf +0 -0
- package/common/fonts/glyphicons-halflings-regular.woff +0 -0
- package/common/fonts/glyphicons-halflings-regular.woff2 +0 -0
- package/common/img/.gitkeep +0 -0
- package/common/js/auth-buttons.js +65 -0
- package/common/js/solid.js +454 -0
- package/common/well-known/security.txt +2 -0
- package/config/defaults.js +25 -0
- package/config/usernames-blacklist.json +4 -0
- package/config.json-default +22 -0
- package/default-templates/emails/delete-account.js +49 -0
- package/default-templates/emails/invalid-username.js +30 -0
- package/default-templates/emails/reset-password.js +49 -0
- package/default-templates/emails/welcome.js +39 -0
- package/default-templates/new-account/.acl +26 -0
- package/default-templates/new-account/.meta +5 -0
- package/default-templates/new-account/.meta.acl +25 -0
- package/default-templates/new-account/.well-known/.acl +19 -0
- package/default-templates/new-account/favicon.ico +0 -0
- package/default-templates/new-account/favicon.ico.acl +26 -0
- package/default-templates/new-account/inbox/.acl +26 -0
- package/default-templates/new-account/private/.acl +10 -0
- package/default-templates/new-account/profile/.acl +19 -0
- package/default-templates/new-account/profile/card$.ttl +25 -0
- package/default-templates/new-account/public/.acl +19 -0
- package/default-templates/new-account/robots.txt +3 -0
- package/default-templates/new-account/robots.txt.acl +26 -0
- package/default-templates/new-account/settings/.acl +20 -0
- package/default-templates/new-account/settings/prefs.ttl +15 -0
- package/default-templates/new-account/settings/privateTypeIndex.ttl +4 -0
- package/default-templates/new-account/settings/publicTypeIndex.ttl +4 -0
- package/default-templates/new-account/settings/publicTypeIndex.ttl.acl +25 -0
- package/default-templates/new-account/settings/serverSide.ttl.acl +13 -0
- package/default-templates/new-account/settings/serverSide.ttl.inactive +12 -0
- package/default-templates/server/.acl +10 -0
- package/default-templates/server/.well-known/.acl +15 -0
- package/default-templates/server/favicon.ico +0 -0
- package/default-templates/server/favicon.ico.acl +15 -0
- package/default-templates/server/index.html +55 -0
- package/default-templates/server/robots.txt +3 -0
- package/default-templates/server/robots.txt.acl +15 -0
- package/default-views/account/account-deleted.hbs +17 -0
- package/default-views/account/delete-confirm.hbs +51 -0
- package/default-views/account/delete-link-sent.hbs +17 -0
- package/default-views/account/delete.hbs +51 -0
- package/default-views/account/invalid-username.hbs +22 -0
- package/default-views/account/register-disabled.hbs +6 -0
- package/default-views/account/register-form.hbs +132 -0
- package/default-views/account/register.hbs +24 -0
- package/default-views/auth/auth-hidden-fields.hbs +8 -0
- package/default-views/auth/change-password.hbs +58 -0
- package/default-views/auth/goodbye.hbs +23 -0
- package/default-views/auth/login-required.hbs +34 -0
- package/default-views/auth/login-tls.hbs +11 -0
- package/default-views/auth/login-username-password.hbs +28 -0
- package/default-views/auth/login.hbs +55 -0
- package/default-views/auth/no-permission.hbs +29 -0
- package/default-views/auth/password-changed.hbs +27 -0
- package/default-views/auth/reset-link-sent.hbs +21 -0
- package/default-views/auth/reset-password.hbs +52 -0
- package/default-views/auth/sharing.hbs +49 -0
- package/default-views/shared/create-account.hbs +8 -0
- package/default-views/shared/error.hbs +5 -0
- package/docs/how-to-delete-your-account.md +56 -0
- package/docs/login-and-grant-access-to-application.md +32 -0
- package/examples/custom-error-handling.js +31 -0
- package/examples/ldp-with-webid.js +12 -0
- package/examples/simple-express-app.js +20 -0
- package/examples/simple-ldp-server.js +8 -0
- package/favicon.ico +0 -0
- package/favicon.ico.acl +15 -0
- package/index.html +48 -0
- package/index.js +3 -0
- package/lib/acl-checker.js +274 -0
- package/lib/api/accounts/user-accounts.js +88 -0
- package/lib/api/authn/force-user.js +21 -0
- package/lib/api/authn/index.js +5 -0
- package/lib/api/authn/webid-oidc.js +202 -0
- package/lib/api/authn/webid-tls.js +69 -0
- package/lib/api/index.js +6 -0
- package/lib/capability-discovery.js +54 -0
- package/lib/common/fs-utils.js +43 -0
- package/lib/common/template-utils.js +50 -0
- package/lib/common/user-utils.js +28 -0
- package/lib/create-app.js +322 -0
- package/lib/create-server.js +107 -0
- package/lib/debug.js +17 -0
- package/lib/handlers/allow.js +82 -0
- package/lib/handlers/auth-proxy.js +63 -0
- package/lib/handlers/copy.js +39 -0
- package/lib/handlers/cors-proxy.js +95 -0
- package/lib/handlers/delete.js +23 -0
- package/lib/handlers/error-pages.js +212 -0
- package/lib/handlers/get.js +219 -0
- package/lib/handlers/index.js +42 -0
- package/lib/handlers/options.js +33 -0
- package/lib/handlers/patch/n3-patch-parser.js +49 -0
- package/lib/handlers/patch/sparql-update-parser.js +16 -0
- package/lib/handlers/patch.js +203 -0
- package/lib/handlers/post.js +99 -0
- package/lib/handlers/put.js +56 -0
- package/lib/handlers/restrict-to-top-domain.js +13 -0
- package/lib/header.js +136 -0
- package/lib/http-error.js +34 -0
- package/lib/ldp-container.js +161 -0
- package/lib/ldp-copy.js +73 -0
- package/lib/ldp-middleware.js +32 -0
- package/lib/ldp.js +620 -0
- package/lib/lock.js +10 -0
- package/lib/metadata.js +10 -0
- package/lib/models/account-manager.js +603 -0
- package/lib/models/account-template.js +152 -0
- package/lib/models/authenticator.js +333 -0
- package/lib/models/oidc-manager.js +53 -0
- package/lib/models/solid-host.js +131 -0
- package/lib/models/user-account.js +112 -0
- package/lib/models/webid-tls-certificate.js +184 -0
- package/lib/payment-pointer-discovery.js +83 -0
- package/lib/requests/add-cert-request.js +138 -0
- package/lib/requests/auth-request.js +234 -0
- package/lib/requests/create-account-request.js +468 -0
- package/lib/requests/delete-account-confirm-request.js +170 -0
- package/lib/requests/delete-account-request.js +144 -0
- package/lib/requests/login-request.js +205 -0
- package/lib/requests/password-change-request.js +201 -0
- package/lib/requests/password-reset-email-request.js +199 -0
- package/lib/requests/sharing-request.js +259 -0
- package/lib/resource-mapper.js +198 -0
- package/lib/server-config.js +167 -0
- package/lib/services/blacklist-service.js +33 -0
- package/lib/services/email-service.js +162 -0
- package/lib/services/token-service.js +47 -0
- package/lib/utils.js +254 -0
- package/lib/webid/index.js +13 -0
- package/lib/webid/lib/get.js +27 -0
- package/lib/webid/lib/parse.js +12 -0
- package/lib/webid/tls/index.js +185 -0
- package/package.json +172 -0
- package/renovate.json +5 -0
- package/robots.txt +3 -0
- package/robots.txt.acl +15 -0
- package/static/account-recovery.html +78 -0
- package/static/popup-redirect.html +1 -0
- package/static/signup.html +108 -0
- package/static/signup.html.acl +14 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
/**
|
|
3
|
+
* OIDC Relying Party API handler module.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const express = require('express')
|
|
7
|
+
const { routeResolvedFile } = require('../../utils')
|
|
8
|
+
const bodyParser = require('body-parser').urlencoded({ extended: false })
|
|
9
|
+
const OidcManager = require('../../models/oidc-manager')
|
|
10
|
+
const { LoginRequest } = require('../../requests/login-request')
|
|
11
|
+
const { SharingRequest } = require('../../requests/sharing-request')
|
|
12
|
+
|
|
13
|
+
const restrictToTopDomain = require('../../handlers/restrict-to-top-domain')
|
|
14
|
+
|
|
15
|
+
const PasswordResetEmailRequest = require('../../requests/password-reset-email-request')
|
|
16
|
+
const PasswordChangeRequest = require('../../requests/password-change-request')
|
|
17
|
+
|
|
18
|
+
const { AuthCallbackRequest } = require('@solid/oidc-auth-manager').handlers
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Sets up OIDC authentication for the given app.
|
|
22
|
+
*
|
|
23
|
+
* @param app {Object} Express.js app instance
|
|
24
|
+
* @param argv {Object} Config options hashmap
|
|
25
|
+
*/
|
|
26
|
+
function initialize (app, argv) {
|
|
27
|
+
const oidc = OidcManager.fromServerConfig(argv)
|
|
28
|
+
app.locals.oidc = oidc
|
|
29
|
+
oidc.initialize()
|
|
30
|
+
|
|
31
|
+
// Attach the OIDC API
|
|
32
|
+
app.use('/', middleware(oidc))
|
|
33
|
+
|
|
34
|
+
// Perform the actual authentication
|
|
35
|
+
app.use('/', async (req, res, next) => {
|
|
36
|
+
oidc.rs.authenticate({ tokenTypesSupported: argv.tokenTypesSupported })(req, res, (err) => {
|
|
37
|
+
// Error handling should be deferred to the ldp in case a user with a bad token is trying
|
|
38
|
+
// to access a public resource
|
|
39
|
+
if (err) {
|
|
40
|
+
req.authError = err
|
|
41
|
+
res.status(200)
|
|
42
|
+
}
|
|
43
|
+
next()
|
|
44
|
+
})
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
// Expose session.userId
|
|
48
|
+
app.use('/', (req, res, next) => {
|
|
49
|
+
oidc.webIdFromClaims(req.claims)
|
|
50
|
+
.then(webId => {
|
|
51
|
+
if (webId) {
|
|
52
|
+
req.session.userId = webId
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
next()
|
|
56
|
+
})
|
|
57
|
+
.catch(err => {
|
|
58
|
+
const error = new Error('Could not verify Web ID from token claims')
|
|
59
|
+
error.statusCode = 401
|
|
60
|
+
error.statusText = 'Invalid login'
|
|
61
|
+
error.cause = err
|
|
62
|
+
|
|
63
|
+
console.error(err)
|
|
64
|
+
|
|
65
|
+
next(error)
|
|
66
|
+
})
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Returns a router with OIDC Relying Party and Identity Provider middleware:
|
|
72
|
+
*
|
|
73
|
+
* @method middleware
|
|
74
|
+
*
|
|
75
|
+
* @param oidc {OidcManager}
|
|
76
|
+
*
|
|
77
|
+
* @return {Router} Express router
|
|
78
|
+
*/
|
|
79
|
+
function middleware (oidc) {
|
|
80
|
+
const router = express.Router('/')
|
|
81
|
+
|
|
82
|
+
// User-facing Authentication API
|
|
83
|
+
router.get(['/login', '/signin'], LoginRequest.get)
|
|
84
|
+
|
|
85
|
+
router.post('/login/password', bodyParser, LoginRequest.loginPassword)
|
|
86
|
+
|
|
87
|
+
router.post('/login/tls', bodyParser, LoginRequest.loginTls)
|
|
88
|
+
|
|
89
|
+
router.get('/sharing', SharingRequest.get)
|
|
90
|
+
router.post('/sharing', bodyParser, SharingRequest.share)
|
|
91
|
+
|
|
92
|
+
router.get('/account/password/reset', restrictToTopDomain, PasswordResetEmailRequest.get)
|
|
93
|
+
router.post('/account/password/reset', restrictToTopDomain, bodyParser, PasswordResetEmailRequest.post)
|
|
94
|
+
|
|
95
|
+
router.get('/account/password/change', restrictToTopDomain, PasswordChangeRequest.get)
|
|
96
|
+
router.post('/account/password/change', restrictToTopDomain, bodyParser, PasswordChangeRequest.post)
|
|
97
|
+
|
|
98
|
+
router.get('/.well-known/solid/logout/', (req, res) => res.redirect('/logout'))
|
|
99
|
+
|
|
100
|
+
router.get('/goodbye', (req, res) => { res.render('auth/goodbye') })
|
|
101
|
+
|
|
102
|
+
// The relying party callback is called at the end of the OIDC signin process
|
|
103
|
+
router.get('/api/oidc/rp/:issuer_id', AuthCallbackRequest.get)
|
|
104
|
+
|
|
105
|
+
// Static assets related to authentication
|
|
106
|
+
const authAssets = [
|
|
107
|
+
['/.well-known/solid/login/', '../static/popup-redirect.html', false],
|
|
108
|
+
['/common/', 'solid-auth-client/dist-popup/popup.html']
|
|
109
|
+
]
|
|
110
|
+
authAssets.map(args => routeResolvedFile(router, ...args))
|
|
111
|
+
|
|
112
|
+
// Initialize the OIDC Identity Provider routes/api
|
|
113
|
+
// router.get('/.well-known/openid-configuration', discover.bind(provider))
|
|
114
|
+
// router.get('/jwks', jwks.bind(provider))
|
|
115
|
+
// router.post('/register', register.bind(provider))
|
|
116
|
+
// router.get('/authorize', authorize.bind(provider))
|
|
117
|
+
// router.post('/authorize', authorize.bind(provider))
|
|
118
|
+
// router.post('/token', token.bind(provider))
|
|
119
|
+
// router.get('/userinfo', userinfo.bind(provider))
|
|
120
|
+
// router.get('/logout', logout.bind(provider))
|
|
121
|
+
const oidcProviderApi = require('oidc-op-express')(oidc.provider)
|
|
122
|
+
router.use('/', oidcProviderApi)
|
|
123
|
+
|
|
124
|
+
return router
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Sets the `WWW-Authenticate` response header for 401 error responses.
|
|
129
|
+
* Used by error-pages handler.
|
|
130
|
+
*
|
|
131
|
+
* @param req {IncomingRequest}
|
|
132
|
+
* @param res {ServerResponse}
|
|
133
|
+
* @param err {Error}
|
|
134
|
+
*/
|
|
135
|
+
function setAuthenticateHeader (req, res, err) {
|
|
136
|
+
const locals = req.app.locals
|
|
137
|
+
|
|
138
|
+
const errorParams = {
|
|
139
|
+
realm: locals.host.serverUri,
|
|
140
|
+
scope: 'openid webid',
|
|
141
|
+
error: err.error,
|
|
142
|
+
error_description: err.error_description,
|
|
143
|
+
error_uri: err.error_uri
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const challengeParams = Object.keys(errorParams)
|
|
147
|
+
.filter(key => !!errorParams[key])
|
|
148
|
+
.map(key => `${key}="${errorParams[key]}"`)
|
|
149
|
+
.join(', ')
|
|
150
|
+
|
|
151
|
+
res.set('WWW-Authenticate', 'Bearer ' + challengeParams)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Provides custom logic for error status code overrides.
|
|
156
|
+
*
|
|
157
|
+
* @param statusCode {number}
|
|
158
|
+
* @param req {IncomingRequest}
|
|
159
|
+
*
|
|
160
|
+
* @returns {number}
|
|
161
|
+
*/
|
|
162
|
+
function statusCodeOverride (statusCode, req) {
|
|
163
|
+
if (isEmptyToken(req)) {
|
|
164
|
+
return 400
|
|
165
|
+
} else {
|
|
166
|
+
return statusCode
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Tests whether the `Authorization:` header includes an empty or missing Bearer
|
|
172
|
+
* token.
|
|
173
|
+
*
|
|
174
|
+
* @param req {IncomingRequest}
|
|
175
|
+
*
|
|
176
|
+
* @returns {boolean}
|
|
177
|
+
*/
|
|
178
|
+
function isEmptyToken (req) {
|
|
179
|
+
const header = req.get('Authorization')
|
|
180
|
+
|
|
181
|
+
if (!header) { return false }
|
|
182
|
+
|
|
183
|
+
if (header.startsWith('Bearer')) {
|
|
184
|
+
const fragments = header.split(' ')
|
|
185
|
+
|
|
186
|
+
if (fragments.length === 1) {
|
|
187
|
+
return true
|
|
188
|
+
} else if (!fragments[1]) {
|
|
189
|
+
return true
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return false
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
module.exports = {
|
|
197
|
+
initialize,
|
|
198
|
+
isEmptyToken,
|
|
199
|
+
middleware,
|
|
200
|
+
setAuthenticateHeader,
|
|
201
|
+
statusCodeOverride
|
|
202
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
const webid = require('../../webid/tls')
|
|
2
|
+
const debug = require('../../debug').authentication
|
|
3
|
+
|
|
4
|
+
function initialize (app, argv) {
|
|
5
|
+
app.use('/', handler)
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function handler (req, res, next) {
|
|
9
|
+
// User already logged in? skip
|
|
10
|
+
if (req.session.userId) {
|
|
11
|
+
debug('User: ' + req.session.userId)
|
|
12
|
+
res.set('User', req.session.userId)
|
|
13
|
+
return next()
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// No certificate? skip
|
|
17
|
+
const certificate = getCertificateViaTLS(req)
|
|
18
|
+
if (!certificate) {
|
|
19
|
+
setEmptySession(req)
|
|
20
|
+
return next()
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Verify webid
|
|
24
|
+
webid.verify(certificate, function (err, result) {
|
|
25
|
+
if (err) {
|
|
26
|
+
debug('Error processing certificate: ' + err.message)
|
|
27
|
+
setEmptySession(req)
|
|
28
|
+
return next()
|
|
29
|
+
}
|
|
30
|
+
req.session.userId = result
|
|
31
|
+
debug('Identified user: ' + req.session.userId)
|
|
32
|
+
res.set('User', req.session.userId)
|
|
33
|
+
return next()
|
|
34
|
+
})
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Tries to obtain a client certificate retrieved through the TLS handshake
|
|
38
|
+
function getCertificateViaTLS (req) {
|
|
39
|
+
const certificate = req.connection.getPeerCertificate &&
|
|
40
|
+
req.connection.getPeerCertificate()
|
|
41
|
+
if (certificate && Object.keys(certificate).length > 0) {
|
|
42
|
+
return certificate
|
|
43
|
+
}
|
|
44
|
+
debug('No peer certificate received during TLS handshake.')
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function setEmptySession (req) {
|
|
48
|
+
req.session.userId = ''
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Sets the `WWW-Authenticate` response header for 401 error responses.
|
|
53
|
+
* Used by error-pages handler.
|
|
54
|
+
*
|
|
55
|
+
* @param req {IncomingRequest}
|
|
56
|
+
* @param res {ServerResponse}
|
|
57
|
+
*/
|
|
58
|
+
function setAuthenticateHeader (req, res) {
|
|
59
|
+
const locals = req.app.locals
|
|
60
|
+
|
|
61
|
+
res.set('WWW-Authenticate', `WebID-TLS realm="${locals.host.serverUri}"`)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
module.exports = {
|
|
65
|
+
initialize,
|
|
66
|
+
handler,
|
|
67
|
+
setAuthenticateHeader,
|
|
68
|
+
setEmptySession
|
|
69
|
+
}
|
package/lib/api/index.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
/**
|
|
3
|
+
* @module capability-discovery
|
|
4
|
+
*/
|
|
5
|
+
const express = require('express')
|
|
6
|
+
const { URL } = require('url')
|
|
7
|
+
|
|
8
|
+
module.exports = capabilityDiscovery
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Returns a set of routes to deal with server capability discovery
|
|
12
|
+
* @method capabilityDiscovery
|
|
13
|
+
* @return {Router} Express router
|
|
14
|
+
*/
|
|
15
|
+
function capabilityDiscovery () {
|
|
16
|
+
const router = express.Router('/')
|
|
17
|
+
|
|
18
|
+
// Advertise the server capability discover endpoint
|
|
19
|
+
router.get('/.well-known/solid', serviceCapabilityDocument())
|
|
20
|
+
return router
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Serves the service capability document (containing server root URL, including
|
|
25
|
+
* any base path the user specified in config, server API endpoints, etc).
|
|
26
|
+
* @method serviceCapabilityDocument
|
|
27
|
+
* @param req
|
|
28
|
+
* @param res
|
|
29
|
+
* @param next
|
|
30
|
+
*/
|
|
31
|
+
function serviceCapabilityDocument () {
|
|
32
|
+
return (req, res) => {
|
|
33
|
+
const ldp = req.app.locals.ldp
|
|
34
|
+
res.json({
|
|
35
|
+
// Add the server root url
|
|
36
|
+
root: ldp.resourceMapper.resolveUrl(req.hostname, req.path),
|
|
37
|
+
// Add the 'apps' urls section
|
|
38
|
+
apps: req.app.locals.appUrls,
|
|
39
|
+
api: {
|
|
40
|
+
accounts: {
|
|
41
|
+
// 'changePassword': '/api/account/changePassword',
|
|
42
|
+
// 'delete': '/api/accounts/delete',
|
|
43
|
+
|
|
44
|
+
// Create new user (see IdentityProvider.post() in identity-provider.js)
|
|
45
|
+
new: new URL('/api/accounts/new', ldp.serverUri),
|
|
46
|
+
recover: new URL('/api/accounts/recover', ldp.serverUri),
|
|
47
|
+
signin: ldp.resourceMapper.resolveUrl(req.hostname, '/login'),
|
|
48
|
+
signout: ldp.resourceMapper.resolveUrl(req.hostname, '/logout'),
|
|
49
|
+
validateToken: new URL('/api/accounts/validateToken', ldp.serverUri)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
})
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
module.exports.copyTemplateDir = copyTemplateDir
|
|
2
|
+
module.exports.processFile = processFile
|
|
3
|
+
module.exports.readFile = readFile
|
|
4
|
+
module.exports.writeFile = writeFile
|
|
5
|
+
|
|
6
|
+
const fs = require('fs-extra')
|
|
7
|
+
|
|
8
|
+
async function copyTemplateDir (templatePath, targetPath) {
|
|
9
|
+
return new Promise((resolve, reject) => {
|
|
10
|
+
fs.copy(templatePath, targetPath, (error) => {
|
|
11
|
+
if (error) { return reject(error) }
|
|
12
|
+
|
|
13
|
+
resolve()
|
|
14
|
+
})
|
|
15
|
+
})
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async function processFile (filePath, manipulateSourceFn) {
|
|
19
|
+
return new Promise((resolve, reject) => {
|
|
20
|
+
fs.readFile(filePath, 'utf8', (error, rawSource) => {
|
|
21
|
+
if (error) {
|
|
22
|
+
return reject(error)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const output = manipulateSourceFn(rawSource)
|
|
26
|
+
|
|
27
|
+
fs.writeFile(filePath, output, (error) => {
|
|
28
|
+
if (error) {
|
|
29
|
+
return reject(error)
|
|
30
|
+
}
|
|
31
|
+
resolve()
|
|
32
|
+
})
|
|
33
|
+
})
|
|
34
|
+
})
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function readFile (filePath, options = 'utf-8') {
|
|
38
|
+
return fs.readFileSync(filePath, options)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function writeFile (filePath, fileSource, options = 'utf-8') {
|
|
42
|
+
fs.writeFileSync(filePath, fileSource, options)
|
|
43
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
module.exports.compileTemplate = compileTemplate
|
|
2
|
+
module.exports.processHandlebarFile = processHandlebarFile
|
|
3
|
+
module.exports.writeTemplate = writeTemplate
|
|
4
|
+
|
|
5
|
+
const Handlebars = require('handlebars')
|
|
6
|
+
const debug = require('../debug').errors
|
|
7
|
+
const { processFile, readFile, writeFile } = require('./fs-utils')
|
|
8
|
+
|
|
9
|
+
async function compileTemplate (filePath) {
|
|
10
|
+
const indexTemplateSource = readFile(filePath)
|
|
11
|
+
return Handlebars.compile(indexTemplateSource)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Reads a file, processes it (performing template substitution), and saves
|
|
16
|
+
* back the processed result.
|
|
17
|
+
*
|
|
18
|
+
* @param filePath {string}
|
|
19
|
+
* @param substitutions {Object}
|
|
20
|
+
*
|
|
21
|
+
* @return {Promise}
|
|
22
|
+
*/
|
|
23
|
+
async function processHandlebarFile (filePath, substitutions) {
|
|
24
|
+
return processFile(filePath, (rawSource) => processHandlebarTemplate(rawSource, substitutions))
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Performs a Handlebars string template substitution, and returns the
|
|
29
|
+
* resulting string.
|
|
30
|
+
*
|
|
31
|
+
* @see https://www.npmjs.com/package/handlebars
|
|
32
|
+
*
|
|
33
|
+
* @param source {string} e.g. 'Hello, {{name}}'
|
|
34
|
+
*
|
|
35
|
+
* @return {string} Result, e.g. 'Hello, Alice'
|
|
36
|
+
*/
|
|
37
|
+
function processHandlebarTemplate (source, substitutions) {
|
|
38
|
+
try {
|
|
39
|
+
const template = Handlebars.compile(source)
|
|
40
|
+
return template(substitutions)
|
|
41
|
+
} catch (error) {
|
|
42
|
+
debug(`Error processing template: ${error}`)
|
|
43
|
+
return source
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function writeTemplate (filePath, template, substitutions) {
|
|
48
|
+
const source = template(substitutions)
|
|
49
|
+
writeFile(filePath, source)
|
|
50
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
const $rdf = require('rdflib')
|
|
2
|
+
|
|
3
|
+
const SOLID = $rdf.Namespace('http://www.w3.org/ns/solid/terms#')
|
|
4
|
+
const VCARD = $rdf.Namespace('http://www.w3.org/2006/vcard/ns#')
|
|
5
|
+
|
|
6
|
+
module.exports.getName = getName
|
|
7
|
+
module.exports.getWebId = getWebId
|
|
8
|
+
module.exports.isValidUsername = isValidUsername
|
|
9
|
+
|
|
10
|
+
async function getName (webId, fetchGraph) {
|
|
11
|
+
const graph = await fetchGraph(webId)
|
|
12
|
+
const nameNode = graph.any($rdf.sym(webId), VCARD('fn'))
|
|
13
|
+
return nameNode.value
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async function getWebId (accountDirectory, accountUrl, suffixMeta, fetchData) {
|
|
17
|
+
const metaFilePath = `${accountDirectory}/${suffixMeta}`
|
|
18
|
+
const metaFileUri = `${accountUrl}${suffixMeta}`
|
|
19
|
+
const metaData = await fetchData(metaFilePath)
|
|
20
|
+
const metaGraph = $rdf.graph()
|
|
21
|
+
$rdf.parse(metaData, metaGraph, metaFileUri, 'text/turtle')
|
|
22
|
+
const webIdNode = metaGraph.any(undefined, SOLID('account'), $rdf.sym(accountUrl))
|
|
23
|
+
return webIdNode.value
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function isValidUsername (username) {
|
|
27
|
+
return /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(username)
|
|
28
|
+
}
|