solid-server 5.7.6 → 5.7.8
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/.github/workflows/ci.yml +2 -2
- package/README.md +2 -0
- package/lib/acl-checker.js +8 -1
- package/lib/handlers/allow.js +4 -3
- package/lib/handlers/patch.js +1 -3
- package/lib/header.js +1 -1
- package/lib/ldp.js +25 -8
- package/package.json +14 -14
package/.github/workflows/ci.yml
CHANGED
|
@@ -17,7 +17,7 @@ jobs:
|
|
|
17
17
|
|
|
18
18
|
strategy:
|
|
19
19
|
matrix:
|
|
20
|
-
node-version: [
|
|
20
|
+
node-version: [16.x, 18.x]
|
|
21
21
|
os: [ubuntu-latest]
|
|
22
22
|
|
|
23
23
|
steps:
|
|
@@ -97,4 +97,4 @@ jobs:
|
|
|
97
97
|
build-args: SOLID_SERVER_VERSION=${{ steps.tagName.outputs.version }}
|
|
98
98
|
push: true
|
|
99
99
|
tags: ${{ steps.meta.outputs.tags }}
|
|
100
|
-
labels: ${{ steps.meta.outputs.labels }}
|
|
100
|
+
labels: ${{ steps.meta.outputs.labels }}
|
package/README.md
CHANGED
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
|
|
10
10
|
`solid-server` lets you run a Solid server on top of the file-system. You can use it as a [command-line tool](https://github.com/solid/node-solid-server/blob/master/README.md#command-line-usage) (easy) or as a [library](https://github.com/solid/node-solid-server/blob/master/README.md#library-usage) (advanced).
|
|
11
11
|
|
|
12
|
+
The [solid test suite](https://github.com/nodeSolidServer/node-solid-server/blob/main/test/surface/run-solid-test-suite.sh) runs as part of GitHub Actions on this repository, ensuring that this server is always (to the best of our knowledge) fully spec compliant.
|
|
13
|
+
|
|
12
14
|
## Solid Features supported
|
|
13
15
|
- [x] [Linked Data Platform](http://www.w3.org/TR/ldp/)
|
|
14
16
|
- [x] [Web Access Control](http://www.w3.org/wiki/WebAccessControl)
|
package/lib/acl-checker.js
CHANGED
|
@@ -28,7 +28,14 @@ class ACLChecker {
|
|
|
28
28
|
constructor (resource, options = {}) {
|
|
29
29
|
this.resource = resource
|
|
30
30
|
this.resourceUrl = new URL(resource)
|
|
31
|
-
this.agentOrigin =
|
|
31
|
+
this.agentOrigin = null
|
|
32
|
+
try {
|
|
33
|
+
if (options.strictOrigin && options.agentOrigin) {
|
|
34
|
+
this.agentOrigin = rdf.sym(options.agentOrigin)
|
|
35
|
+
}
|
|
36
|
+
} catch (e) {
|
|
37
|
+
// noop
|
|
38
|
+
}
|
|
32
39
|
this.fetch = options.fetch
|
|
33
40
|
this.fetchGraph = options.fetchGraph
|
|
34
41
|
this.trustedOrigins = options.strictOrigin && options.trustedOrigins ? options.trustedOrigins.map(trustedOrigin => rdf.sym(trustedOrigin)) : null
|
package/lib/handlers/allow.js
CHANGED
|
@@ -72,9 +72,10 @@ function allow (mode) {
|
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
// check user is owner.
|
|
76
|
-
|
|
77
|
-
|
|
75
|
+
// check if user is owner. Check isOwner from /.meta
|
|
76
|
+
try {
|
|
77
|
+
if (resourceUrl.endsWith('.acl') && (await ldp.isOwner(userId, req.hostname))) return next()
|
|
78
|
+
} catch (err) {}
|
|
78
79
|
const error = req.authError || await req.acl.getError(userId, mode)
|
|
79
80
|
debug(`${mode} access denied to ${userId || '(none)'}: ${error.status} - ${error.message}`)
|
|
80
81
|
next(error)
|
package/lib/handlers/patch.js
CHANGED
|
@@ -143,8 +143,6 @@ async function checkPermission (request, patchObject, resourceExists) {
|
|
|
143
143
|
// Now that we know the details of the patch,
|
|
144
144
|
// we might need to perform additional checks.
|
|
145
145
|
let modes = []
|
|
146
|
-
// acl:default Write is required for create
|
|
147
|
-
if (!resourceExists) modes = ['Write']
|
|
148
146
|
const { acl, session: { userId } } = request
|
|
149
147
|
// Read access is required for DELETE and WHERE.
|
|
150
148
|
// If we would allows users without read access,
|
|
@@ -164,7 +162,7 @@ async function checkPermission (request, patchObject, resourceExists) {
|
|
|
164
162
|
if (!allAllowed) {
|
|
165
163
|
// check owner with Control
|
|
166
164
|
const ldp = request.app.locals.ldp
|
|
167
|
-
if (request.path.endsWith('.acl') &&
|
|
165
|
+
if (request.path.endsWith('.acl') && await ldp.isOwner(userId, request.hostname)) return Promise.resolve(patchObject)
|
|
168
166
|
|
|
169
167
|
const errors = await Promise.all(modes.map(mode => acl.getError(userId, mode)))
|
|
170
168
|
const error = errors.filter(error => !!error)
|
package/lib/header.js
CHANGED
|
@@ -128,7 +128,7 @@ async function addPermissions (req, res, next) {
|
|
|
128
128
|
getPermissionsFor(acl, null, req),
|
|
129
129
|
getPermissionsFor(acl, session.userId, req)
|
|
130
130
|
])
|
|
131
|
-
if (resource.endsWith('.acl') && userPerms === '' &&
|
|
131
|
+
if (resource.endsWith('.acl') && userPerms === '' && await ldp.isOwner(session.userId, req.hostname)) userPerms = 'control'
|
|
132
132
|
debug.ACL(`Permissions on ${resource} for ${session.userId || '(none)'}: ${userPerms}`)
|
|
133
133
|
debug.ACL(`Permissions on ${resource} for public: ${publicPerms}`)
|
|
134
134
|
res.set('WAC-Allow', `user="${userPerms}",public="${publicPerms}"`)
|
package/lib/ldp.js
CHANGED
|
@@ -243,7 +243,14 @@ class LDP {
|
|
|
243
243
|
'PUT request requires a content-type via the Content-Type header')
|
|
244
244
|
}
|
|
245
245
|
|
|
246
|
+
// reject resource with percent-encoded $ extension
|
|
247
|
+
const dollarExtensionRegex = /%(?:24)\.[^%(?:24)]*$/ // /\$\.[^$]*$/
|
|
248
|
+
if ((url.url || url).match(dollarExtensionRegex)) {
|
|
249
|
+
throw error(400, 'Resource with a $.ext is not allowed by the server')
|
|
250
|
+
}
|
|
251
|
+
|
|
246
252
|
// check if a folder or file with same name exists
|
|
253
|
+
// const urlItem = url.url || url
|
|
247
254
|
await this.checkItemName(url)
|
|
248
255
|
|
|
249
256
|
// First check if we are above quota
|
|
@@ -350,17 +357,27 @@ class LDP {
|
|
|
350
357
|
|
|
351
358
|
async checkItemName (url) {
|
|
352
359
|
let testName
|
|
353
|
-
const
|
|
360
|
+
const { hostname, pathname } = this.resourceMapper._parseUrl(url) // (url.url || url)
|
|
361
|
+
let itemUrl = this.resourceMapper.resolveUrl(hostname, pathname)
|
|
354
362
|
const container = itemUrl.endsWith('/')
|
|
355
363
|
try {
|
|
356
364
|
const testUrl = container ? itemUrl.slice(0, -1) : itemUrl + '/'
|
|
357
365
|
const { path: testPath } = await this.resourceMapper.mapUrlToFile({ url: testUrl })
|
|
358
|
-
// testName = fs.lstatSync(testPath).isDirectory()
|
|
359
366
|
testName = container ? fs.lstatSync(testPath).isFile() : fs.lstatSync(testPath).isDirectory()
|
|
360
|
-
} catch (err) {
|
|
361
|
-
|
|
367
|
+
} catch (err) {
|
|
368
|
+
testName = false
|
|
369
|
+
|
|
370
|
+
// item does not exist, check one level up the tree
|
|
371
|
+
if (itemUrl.endsWith('/')) itemUrl = itemUrl.substring(0, itemUrl.length - 1)
|
|
372
|
+
itemUrl = itemUrl.substring(0, itemUrl.lastIndexOf('/') + 1)
|
|
373
|
+
const { pathname } = this.resourceMapper._parseUrl(itemUrl) // (url.url || url)
|
|
374
|
+
// check not at root
|
|
375
|
+
if (pathname !== '/') {
|
|
376
|
+
await this.checkItemName(itemUrl)
|
|
377
|
+
}
|
|
378
|
+
}
|
|
362
379
|
if (testName) {
|
|
363
|
-
throw error(
|
|
380
|
+
throw error(404, 'Container and resource cannot have the same name in URI')
|
|
364
381
|
}
|
|
365
382
|
}
|
|
366
383
|
|
|
@@ -437,7 +454,7 @@ class LDP {
|
|
|
437
454
|
// this is a hack to replace solid:owner, using solid:account in /.meta to avoid NSS migration
|
|
438
455
|
// this /.meta has no functionality in actual NSS
|
|
439
456
|
// comment https://github.com/solid/node-solid-server/pull/1604#discussion_r652903546
|
|
440
|
-
async
|
|
457
|
+
async isOwner (webId, hostname) {
|
|
441
458
|
// const ldp = req.app.locals.ldp
|
|
442
459
|
const rootUrl = this.resourceMapper.resolveUrl(hostname)
|
|
443
460
|
let graph
|
|
@@ -445,8 +462,8 @@ class LDP {
|
|
|
445
462
|
// TODO check for permission ?? Owner is a MUST
|
|
446
463
|
graph = await this.getGraph(rootUrl + '/.meta')
|
|
447
464
|
const SOLID = $rdf.Namespace('http://www.w3.org/ns/solid/terms#')
|
|
448
|
-
const owner = await graph.
|
|
449
|
-
return owner.
|
|
465
|
+
const owner = await graph.statementsMatching($rdf.sym(webId), SOLID('account'), $rdf.sym(rootUrl + '/'))
|
|
466
|
+
return owner.length
|
|
450
467
|
} catch (error) {
|
|
451
468
|
throw new Error(`Failed to get owner from ${rootUrl}/.meta, got ` + error)
|
|
452
469
|
}
|
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.8",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Tim Berners-Lee",
|
|
7
7
|
"email": "timbl@w3.org"
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"cached-path-relative": "^1.1.0",
|
|
70
70
|
"camelize": "^1.0.1",
|
|
71
71
|
"cheerio": "^1.0.0-rc.12",
|
|
72
|
-
"colorette": "^2.0.
|
|
72
|
+
"colorette": "^2.0.20",
|
|
73
73
|
"commander": "^8.3.0",
|
|
74
74
|
"cors": "^2.8.5",
|
|
75
75
|
"debug": "^4.3.4",
|
|
@@ -82,27 +82,27 @@
|
|
|
82
82
|
"get-folder-size": "^2.0.1",
|
|
83
83
|
"glob": "^7.2.3",
|
|
84
84
|
"global-tunnel-ng": "^2.7.1",
|
|
85
|
-
"handlebars": "^4.7.
|
|
85
|
+
"handlebars": "^4.7.8",
|
|
86
86
|
"http-proxy-middleware": "^2.0.6",
|
|
87
|
-
"inquirer": "^8.2.
|
|
87
|
+
"inquirer": "^8.2.6",
|
|
88
88
|
"into-stream": "^6.0.0",
|
|
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.9",
|
|
93
93
|
"mime-types": "^2.1.35",
|
|
94
94
|
"negotiator": "^0.6.3",
|
|
95
|
-
"node-fetch": "^2.
|
|
95
|
+
"node-fetch": "^2.7.0",
|
|
96
96
|
"node-forge": "^1.3.1",
|
|
97
97
|
"node-mailer": "^0.1.1",
|
|
98
|
-
"nodemailer": "^6.9.
|
|
98
|
+
"nodemailer": "^6.9.7",
|
|
99
99
|
"oidc-op-express": "^0.0.3",
|
|
100
100
|
"owasp-password-strength-test": "^1.3.0",
|
|
101
101
|
"recursive-readdir": "^2.2.3",
|
|
102
102
|
"request": "^2.88.2",
|
|
103
103
|
"rimraf": "^3.0.2",
|
|
104
104
|
"solid-auth-client": "^2.5.6",
|
|
105
|
-
"solid-namespace": "^0.5.
|
|
105
|
+
"solid-namespace": "^0.5.3",
|
|
106
106
|
"solid-ws": "^0.4.3",
|
|
107
107
|
"text-encoder-lite": "^2.0.0",
|
|
108
108
|
"the-big-username-blacklist": "^1.5.2",
|
|
@@ -110,26 +110,26 @@
|
|
|
110
110
|
"urijs": "^1.19.11",
|
|
111
111
|
"uuid": "^8.3.2",
|
|
112
112
|
"valid-url": "^1.0.9",
|
|
113
|
-
"validator": "^13.
|
|
113
|
+
"validator": "^13.11.0",
|
|
114
114
|
"vhost": "^3.0.2"
|
|
115
115
|
},
|
|
116
116
|
"devDependencies": {
|
|
117
|
-
"@solid/solid-auth-oidc": "
|
|
118
|
-
"chai": "^4.3.
|
|
117
|
+
"@solid/solid-auth-oidc": "0.3.0",
|
|
118
|
+
"chai": "^4.3.10",
|
|
119
119
|
"chai-as-promised": "7.1.1",
|
|
120
120
|
"cross-env": "7.0.3",
|
|
121
121
|
"dirty-chai": "2.0.1",
|
|
122
122
|
"eslint": "^7.32.0",
|
|
123
123
|
"localstorage-memory": "1.0.3",
|
|
124
124
|
"mocha": "^9.2.2",
|
|
125
|
-
"nock": "^13.
|
|
126
|
-
"node-mocks-http": "1.
|
|
125
|
+
"nock": "^13.4.0",
|
|
126
|
+
"node-mocks-http": "^1.14.0",
|
|
127
127
|
"nyc": "15.1.0",
|
|
128
128
|
"pre-commit": "1.2.2",
|
|
129
129
|
"randombytes": "2.1.0",
|
|
130
130
|
"sinon": "12.0.1",
|
|
131
131
|
"sinon-chai": "3.7.0",
|
|
132
|
-
"snyk": "^1.
|
|
132
|
+
"snyk": "^1.1264.0",
|
|
133
133
|
"standard": "16.0.4",
|
|
134
134
|
"supertest": "^6.3.3",
|
|
135
135
|
"turtle-validator": "1.1.1",
|