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,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
+ }
@@ -0,0 +1,6 @@
1
+ 'use strict'
2
+
3
+ module.exports = {
4
+ authn: require('./authn'),
5
+ accounts: require('./accounts/user-accounts')
6
+ }
@@ -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
+ }