solid-server 5.7.9 → 5.7.11
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/.nvmrc +1 -1
- package/common/js/index-buttons.js +2 -2
- package/default-views/auth/reset-link-sent.hbs +1 -1
- package/lib/ldp.js +29 -16
- package/lib/models/account-manager.js +1 -3
- package/lib/requests/create-account-request.js +1 -1
- package/lib/requests/password-reset-email-request.js +6 -3
- package/lib/requests/sharing-request.js +3 -1
- package/lib/utils.js +1 -1
- package/package.json +10 -2
package/.nvmrc
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
v18.19.0
|
|
@@ -30,7 +30,7 @@ document.addEventListener('DOMContentLoaded', async function() {
|
|
|
30
30
|
// HIDE LOGIN BUTTON, ADD REGISTER BUTTON
|
|
31
31
|
else {
|
|
32
32
|
let loginArea = document.getElementById('loginStatusArea');
|
|
33
|
-
let html = `<input type="button" onclick="window.location.href='/register'" value="Register to get a Pod" class="register-button">`
|
|
33
|
+
let html = `<input type="button" onclick="window.location.href='/register'" value="Register to get a Pod" class="register-button" style="padding: 1em; border-radius:0.2em; font-size: 100%;margin: 0.75em 0 0.75em 0.5em !important; padding: 0.5em !important;background-color: #efe;">`
|
|
34
34
|
let span = document.createElement("span")
|
|
35
35
|
span.innerHTML = html
|
|
36
36
|
loginArea.appendChild(span);
|
|
@@ -41,4 +41,4 @@ document.addEventListener('DOMContentLoaded', async function() {
|
|
|
41
41
|
signUpButton.style.display = "none";
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
|
-
})
|
|
44
|
+
})
|
package/lib/ldp.js
CHANGED
|
@@ -103,7 +103,6 @@ class LDP {
|
|
|
103
103
|
|
|
104
104
|
async listContainer (container, reqUri, containerData, hostname) {
|
|
105
105
|
const resourceGraph = $rdf.graph()
|
|
106
|
-
|
|
107
106
|
try {
|
|
108
107
|
$rdf.parse(containerData, resourceGraph, reqUri, 'text/turtle')
|
|
109
108
|
} catch (err) {
|
|
@@ -145,18 +144,26 @@ class LDP {
|
|
|
145
144
|
|
|
146
145
|
const ldp = this
|
|
147
146
|
debug.handlers('POST -- On parent: ' + containerPath)
|
|
148
|
-
|
|
147
|
+
if (container) {
|
|
148
|
+
// Containers should not receive an extension
|
|
149
|
+
extension = ''
|
|
150
|
+
}
|
|
151
|
+
// pepare slug
|
|
149
152
|
if (slug) {
|
|
150
|
-
if (this.isAuxResource(slug, extension)) throw error(403, 'POST is not allowed for auxiliary resources')
|
|
151
153
|
slug = decodeURIComponent(slug)
|
|
154
|
+
|
|
155
|
+
if (container) {
|
|
156
|
+
// the name of a container cannot be a valid auxiliary resource document
|
|
157
|
+
while (this._containsInvalidSuffixes(slug + '/')) {
|
|
158
|
+
const idx = slug.lastIndexOf('.')
|
|
159
|
+
slug = slug.substr(0, idx)
|
|
160
|
+
}
|
|
161
|
+
} else if (this.isAuxResource(slug, extension)) throw error(403, 'POST to auxiliary resources is not allowed')
|
|
162
|
+
|
|
152
163
|
if (slug.match(/\/|\||:/)) {
|
|
153
|
-
throw error(400, 'The name of new file
|
|
164
|
+
throw error(400, 'The name of a POSTed new file may not contain ":" (colon), "|" (pipe), or "/" (slash)')
|
|
154
165
|
}
|
|
155
166
|
}
|
|
156
|
-
// Containers should not receive an extension
|
|
157
|
-
if (container) {
|
|
158
|
-
extension = ''
|
|
159
|
-
}
|
|
160
167
|
|
|
161
168
|
// always return a valid URL.
|
|
162
169
|
const resourceUrl = await ldp.getAvailableUrl(hostname, containerPath, { slug, extension, container })
|
|
@@ -212,19 +219,16 @@ class LDP {
|
|
|
212
219
|
|
|
213
220
|
async put (url, stream, contentType) {
|
|
214
221
|
const container = (url.url || url).endsWith('/')
|
|
215
|
-
|
|
216
222
|
// PUT without content type is forbidden, unless PUTting container
|
|
217
223
|
if (!contentType && !container) {
|
|
218
224
|
throw error(400,
|
|
219
225
|
'PUT request requires a content-type via the Content-Type header')
|
|
220
226
|
}
|
|
221
|
-
|
|
222
227
|
// reject resource with percent-encoded $ extension
|
|
223
228
|
const dollarExtensionRegex = /%(?:24)\.[^%(?:24)]*$/ // /\$\.[^$]*$/
|
|
224
229
|
if ((url.url || url).match(dollarExtensionRegex)) {
|
|
225
230
|
throw error(400, 'Resource with a $.ext is not allowed by the server')
|
|
226
231
|
}
|
|
227
|
-
|
|
228
232
|
// First check if we are above quota
|
|
229
233
|
let isOverQuota
|
|
230
234
|
// Someone had a reason to make url actually a req sometimes but not
|
|
@@ -238,7 +242,6 @@ class LDP {
|
|
|
238
242
|
if (isOverQuota) {
|
|
239
243
|
throw error(413, 'User has exceeded their storage quota')
|
|
240
244
|
}
|
|
241
|
-
|
|
242
245
|
// Set url using folder/.meta. This is Hack to find folder path
|
|
243
246
|
if (container) {
|
|
244
247
|
if (typeof url !== 'string') {
|
|
@@ -248,22 +251,18 @@ class LDP {
|
|
|
248
251
|
}
|
|
249
252
|
contentType = 'text/turtle'
|
|
250
253
|
}
|
|
251
|
-
|
|
252
254
|
const { path } = await this.resourceMapper.mapUrlToFile({ url, contentType, createIfNotExists: true })
|
|
253
255
|
// debug.handlers(container + ' item ' + (url.url || url) + ' ' + contentType + ' ' + path)
|
|
254
256
|
// check if file exists, and in that case that it has the same extension
|
|
255
257
|
if (!container) { await this.checkFileExtension(url, path) }
|
|
256
|
-
|
|
257
258
|
// Create the enclosing directory, if necessary, do not create pubsub if PUT create container
|
|
258
259
|
await this.createDirectory(path, hostname, !container)
|
|
259
|
-
|
|
260
260
|
// clear cache
|
|
261
261
|
if (path.endsWith(this.suffixAcl)) {
|
|
262
262
|
const { url: aclUrl } = await this.resourceMapper.mapFileToUrl({ path, hostname })
|
|
263
263
|
clearAclCache(aclUrl)
|
|
264
264
|
// clearAclCache()
|
|
265
265
|
}
|
|
266
|
-
|
|
267
266
|
// Directory created, now write the file
|
|
268
267
|
if (container) return
|
|
269
268
|
return withLock(path, () => new Promise((resolve, reject) => {
|
|
@@ -327,11 +326,25 @@ class LDP {
|
|
|
327
326
|
} catch (err) { }
|
|
328
327
|
}
|
|
329
328
|
|
|
329
|
+
/**
|
|
330
|
+
* This function is used to make sure a resource or container which contains
|
|
331
|
+
* reserved suffixes for auxiliary documents cannot be created.
|
|
332
|
+
* @param {string} path - the uri to check for invalid suffixes
|
|
333
|
+
* @returns {boolean} true is fail - if the path contains reserved suffixes
|
|
334
|
+
*/
|
|
335
|
+
_containsInvalidSuffixes (path) {
|
|
336
|
+
return AUXILIARY_RESOURCES.some(suffix => path.endsWith(suffix + '/'))
|
|
337
|
+
}
|
|
338
|
+
|
|
330
339
|
// check whether a document (or container) has the same name as another document (or container)
|
|
331
340
|
async checkItemName (url) {
|
|
332
341
|
let testName, testPath
|
|
333
342
|
const { hostname, pathname } = this.resourceMapper._parseUrl(url) // (url.url || url)
|
|
334
343
|
let itemUrl = this.resourceMapper.resolveUrl(hostname, pathname)
|
|
344
|
+
// make sure the resource being created does not attempt invalid resource creation
|
|
345
|
+
if (this._containsInvalidSuffixes(itemUrl)) {
|
|
346
|
+
throw error(400, `${itemUrl} contained reserved suffixes in path`)
|
|
347
|
+
}
|
|
335
348
|
const container = itemUrl.endsWith('/')
|
|
336
349
|
try {
|
|
337
350
|
const testUrl = container ? itemUrl.slice(0, -1) : itemUrl + '/'
|
|
@@ -89,12 +89,10 @@ class AccountManager {
|
|
|
89
89
|
try {
|
|
90
90
|
accountUri = this.accountUriFor(accountName)
|
|
91
91
|
accountUri = url.parse(accountUri).hostname
|
|
92
|
-
|
|
93
92
|
cardPath = url.resolve('/', this.pathCard)
|
|
94
93
|
} catch (err) {
|
|
95
94
|
return Promise.reject(err)
|
|
96
95
|
}
|
|
97
|
-
|
|
98
96
|
return this.accountUriExists(accountUri, cardPath)
|
|
99
97
|
}
|
|
100
98
|
|
|
@@ -537,7 +535,7 @@ class AccountManager {
|
|
|
537
535
|
throw new Error('Email service is not set up')
|
|
538
536
|
}
|
|
539
537
|
|
|
540
|
-
if (!userAccount.email) {
|
|
538
|
+
if (userAccount && !userAccount.email) {
|
|
541
539
|
throw new Error('Account recovery email has not been provided')
|
|
542
540
|
}
|
|
543
541
|
}
|
|
@@ -178,7 +178,7 @@ class CreateAccountRequest extends AuthRequest {
|
|
|
178
178
|
.then(exists => {
|
|
179
179
|
if (exists) {
|
|
180
180
|
debug(`Canceling account creation, ${userAccount.webId} already exists`)
|
|
181
|
-
const error = new Error('Account
|
|
181
|
+
const error = new Error('Account creation failed')
|
|
182
182
|
error.status = 400
|
|
183
183
|
throw error
|
|
184
184
|
}
|
|
@@ -94,7 +94,7 @@ class PasswordResetEmailRequest extends AuthRequest {
|
|
|
94
94
|
.then(() => request.validate())
|
|
95
95
|
.then(() => request.loadUser())
|
|
96
96
|
.then(userAccount => request.sendResetLink(userAccount))
|
|
97
|
-
.then(() => request.
|
|
97
|
+
.then(() => request.resetLinkMessage())
|
|
98
98
|
.catch(error => request.error(error))
|
|
99
99
|
}
|
|
100
100
|
|
|
@@ -123,7 +123,10 @@ class PasswordResetEmailRequest extends AuthRequest {
|
|
|
123
123
|
return this.accountManager.accountExists(username)
|
|
124
124
|
.then(exists => {
|
|
125
125
|
if (!exists) {
|
|
126
|
-
|
|
126
|
+
// For security reasons, avoid leaking error information
|
|
127
|
+
// See: https://github.com/nodeSolidServer/node-solid-server/issues/1770
|
|
128
|
+
this.accountManager.verifyEmailDependencies()
|
|
129
|
+
return this.resetLinkMessage()
|
|
127
130
|
}
|
|
128
131
|
|
|
129
132
|
const userData = { username }
|
|
@@ -191,7 +194,7 @@ class PasswordResetEmailRequest extends AuthRequest {
|
|
|
191
194
|
/**
|
|
192
195
|
* Displays the 'your reset link has been sent' success message view
|
|
193
196
|
*/
|
|
194
|
-
|
|
197
|
+
resetLinkMessage () {
|
|
195
198
|
this.response.render('auth/reset-link-sent')
|
|
196
199
|
}
|
|
197
200
|
}
|
|
@@ -64,10 +64,11 @@ class SharingRequest extends AuthRequest {
|
|
|
64
64
|
* @param req {IncomingRequest}
|
|
65
65
|
* @param res {ServerResponse}
|
|
66
66
|
*/
|
|
67
|
-
static async get (req, res) {
|
|
67
|
+
static async get (req, res, next) {
|
|
68
68
|
const request = SharingRequest.fromParams(req, res)
|
|
69
69
|
|
|
70
70
|
const appUrl = request.getAppUrl()
|
|
71
|
+
if (!appUrl) return next()
|
|
71
72
|
const appOrigin = appUrl.origin
|
|
72
73
|
const serverUrl = new url.URL(req.app.locals.ldp.serverUri)
|
|
73
74
|
|
|
@@ -153,6 +154,7 @@ class SharingRequest extends AuthRequest {
|
|
|
153
154
|
}
|
|
154
155
|
|
|
155
156
|
getAppUrl () {
|
|
157
|
+
if (!this.authQueryParams.redirect_uri) return
|
|
156
158
|
return new url.URL(this.authQueryParams.redirect_uri)
|
|
157
159
|
}
|
|
158
160
|
|
package/lib/utils.js
CHANGED
|
@@ -201,7 +201,7 @@ async function getQuota (root, serverUri) {
|
|
|
201
201
|
return Infinity
|
|
202
202
|
}
|
|
203
203
|
const graph = $rdf.graph()
|
|
204
|
-
const storageUri = serverUri + '/'
|
|
204
|
+
const storageUri = serverUri.endsWith('/') ? serverUri : serverUri + '/'
|
|
205
205
|
try {
|
|
206
206
|
$rdf.parse(prefs, graph, storageUri, 'text/turtle')
|
|
207
207
|
} catch (error) {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "solid-server",
|
|
3
3
|
"description": "Solid server on top of the file-system",
|
|
4
|
-
"version": "5.7.
|
|
4
|
+
"version": "5.7.11",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Tim Berners-Lee",
|
|
7
7
|
"email": "timbl@w3.org"
|
|
@@ -89,7 +89,7 @@
|
|
|
89
89
|
"ip-range-check": "0.2.0",
|
|
90
90
|
"is-ip": "^3.1.0",
|
|
91
91
|
"li": "^1.3.0",
|
|
92
|
-
"mashlib": "^1.8.
|
|
92
|
+
"mashlib": "^1.8.11",
|
|
93
93
|
"mime-types": "^2.1.35",
|
|
94
94
|
"negotiator": "^0.6.3",
|
|
95
95
|
"node-fetch": "^2.7.0",
|
|
@@ -146,6 +146,14 @@
|
|
|
146
146
|
"validate": "node ./test/validate-turtle.js",
|
|
147
147
|
"nyc": "cross-env NODE_TLS_REJECT_UNAUTHORIZED=0 nyc --reporter=text-summary mocha --recursive test/integration/ test/unit/",
|
|
148
148
|
"mocha": "cross-env NODE_TLS_REJECT_UNAUTHORIZED=0 mocha --recursive test/integration/ test/unit/",
|
|
149
|
+
"mocha-integration": "cross-env NODE_TLS_REJECT_UNAUTHORIZED=0 mocha --recursive test/integration/http-test.js",
|
|
150
|
+
"mocha-account-creation-oidc": "cross-env NODE_TLS_REJECT_UNAUTHORIZED=0 mocha --recursive test/integration/account-creation-oidc-test.js",
|
|
151
|
+
"mocha-account-manager": "cross-env NODE_TLS_REJECT_UNAUTHORIZED=0 mocha --recursive test/integration/account-manager-test.js",
|
|
152
|
+
"mocha-account-template": "cross-env NODE_TLS_REJECT_UNAUTHORIZED=0 mocha --recursive test/integration/account-template-test.js",
|
|
153
|
+
"mocha-acl-oidc": "cross-env NODE_TLS_REJECT_UNAUTHORIZED=0 mocha --recursive test/integration/acl-oidc-test.js",
|
|
154
|
+
"mocha-authentication-oidc": "cross-env NODE_TLS_REJECT_UNAUTHORIZED=0 mocha --recursive test/integration/authentication-oidc-test.js",
|
|
155
|
+
"mocha-header": "cross-env NODE_TLS_REJECT_UNAUTHORIZED=0 mocha --recursive test/integration/header-test.js",
|
|
156
|
+
"mocha-ldp": "cross-env NODE_TLS_REJECT_UNAUTHORIZED=0 mocha --recursive test/integration/ldp-test.js",
|
|
149
157
|
"prepublishOnly": "npm test",
|
|
150
158
|
"postpublish": "git push --follow-tags",
|
|
151
159
|
"test": "npm run standard && npm run validate && npm run nyc",
|