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 CHANGED
@@ -1 +1 @@
1
- 16.14.0
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
+ })
@@ -14,7 +14,7 @@
14
14
  </div>
15
15
 
16
16
  <div class="alert alert-success">
17
- <p>A Reset Password link has been sent to your email.</p>
17
+ <p>A Reset Password link has been sent to the associated email account.</p>
18
18
  </div>
19
19
  </div>
20
20
  </body>
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
- // prepare slug
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 POSTed may not contain : | or /')
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 already exists')
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.renderSuccess())
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
- throw new Error('Account not found for that username')
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
- renderSuccess () {
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.9",
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.9",
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",