javascript-solid-server 0.0.13 → 0.0.16
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/.claude/settings.local.json +27 -1
- package/CTH.md +222 -0
- package/README.md +92 -3
- package/bin/jss.js +11 -1
- package/cth-config/application.yaml +2 -0
- package/cth-config/jss.ttl +6 -0
- package/cth-config/test-subjects.ttl +14 -0
- package/cth.env +19 -0
- package/package.json +1 -1
- package/scripts/test-cth-compat.js +3 -2
- package/src/auth/middleware.js +17 -7
- package/src/auth/token.js +44 -1
- package/src/config.js +7 -0
- package/src/handlers/container.js +49 -16
- package/src/handlers/resource.js +99 -32
- package/src/idp/accounts.js +11 -2
- package/src/idp/credentials.js +38 -38
- package/src/idp/index.js +112 -21
- package/src/idp/interactions.js +123 -11
- package/src/idp/provider.js +68 -2
- package/src/rdf/turtle.js +15 -2
- package/src/server.js +24 -0
- package/src/utils/url.js +52 -0
- package/src/wac/parser.js +43 -1
- package/test/idp.test.js +17 -14
- package/test/ldp.test.js +10 -5
- package/test-data-idp-accounts/.idp/accounts/3c1cd503-1d7f-4ba0-a3af-ebedf519594d.json +9 -0
- package/test-data-idp-accounts/.idp/accounts/_email_index.json +3 -0
- package/test-data-idp-accounts/.idp/accounts/_webid_index.json +3 -0
- package/test-data-idp-accounts/.idp/keys/jwks.json +22 -0
- package/test-dpop-flow.js +148 -0
- package/test-subjects.ttl +21 -0
- package/data/alice/.acl +0 -50
- package/data/alice/inbox/.acl +0 -50
- package/data/alice/index.html +0 -80
- package/data/alice/private/.acl +0 -32
- package/data/alice/public/test.json +0 -1
- package/data/alice/settings/.acl +0 -32
- package/data/alice/settings/prefs +0 -17
- package/data/alice/settings/privateTypeIndex +0 -7
- package/data/alice/settings/publicTypeIndex +0 -7
- package/data/bob/.acl +0 -50
- package/data/bob/inbox/.acl +0 -50
- package/data/bob/index.html +0 -80
- package/data/bob/private/.acl +0 -32
- package/data/bob/settings/.acl +0 -32
- package/data/bob/settings/prefs +0 -17
- package/data/bob/settings/privateTypeIndex +0 -7
- package/data/bob/settings/publicTypeIndex +0 -7
package/test/idp.test.js
CHANGED
|
@@ -43,7 +43,8 @@ describe('Identity Provider', () => {
|
|
|
43
43
|
assert.strictEqual(res.status, 200);
|
|
44
44
|
|
|
45
45
|
const config = await res.json();
|
|
46
|
-
|
|
46
|
+
// Issuer has trailing slash for CTH compatibility
|
|
47
|
+
assert.strictEqual(config.issuer, BASE_URL + '/');
|
|
47
48
|
assert.ok(config.authorization_endpoint);
|
|
48
49
|
assert.ok(config.token_endpoint);
|
|
49
50
|
assert.ok(config.jwks_uri);
|
|
@@ -259,17 +260,16 @@ describe('Identity Provider - Accounts', () => {
|
|
|
259
260
|
|
|
260
261
|
describe('Identity Provider - Credentials Endpoint', () => {
|
|
261
262
|
let server;
|
|
262
|
-
|
|
263
|
+
// Use same data dir as other tests (DATA_ROOT is cached at module load)
|
|
264
|
+
const CREDS_DATA_DIR = './data';
|
|
263
265
|
const CREDS_PORT = 3101;
|
|
264
266
|
const CREDS_URL = `http://${TEST_HOST}:${CREDS_PORT}`;
|
|
265
267
|
|
|
266
268
|
before(async () => {
|
|
267
|
-
await fs.
|
|
268
|
-
await fs.ensureDir(CREDS_DATA_DIR);
|
|
269
|
+
await fs.emptyDir(CREDS_DATA_DIR);
|
|
269
270
|
|
|
270
271
|
server = createServer({
|
|
271
272
|
logger: false,
|
|
272
|
-
root: CREDS_DATA_DIR,
|
|
273
273
|
idp: true,
|
|
274
274
|
idpIssuer: CREDS_URL,
|
|
275
275
|
});
|
|
@@ -277,7 +277,7 @@ describe('Identity Provider - Credentials Endpoint', () => {
|
|
|
277
277
|
await server.listen({ port: CREDS_PORT, host: TEST_HOST });
|
|
278
278
|
|
|
279
279
|
// Create a test user
|
|
280
|
-
await fetch(`${CREDS_URL}/.pods`, {
|
|
280
|
+
const res = await fetch(`${CREDS_URL}/.pods`, {
|
|
281
281
|
method: 'POST',
|
|
282
282
|
headers: { 'Content-Type': 'application/json' },
|
|
283
283
|
body: JSON.stringify({
|
|
@@ -286,11 +286,14 @@ describe('Identity Provider - Credentials Endpoint', () => {
|
|
|
286
286
|
password: 'testpassword123',
|
|
287
287
|
}),
|
|
288
288
|
});
|
|
289
|
+
if (!res.ok) {
|
|
290
|
+
throw new Error(`Failed to create test user: ${res.status} ${await res.text()}`);
|
|
291
|
+
}
|
|
289
292
|
});
|
|
290
293
|
|
|
291
294
|
after(async () => {
|
|
292
295
|
await server.close();
|
|
293
|
-
await fs.
|
|
296
|
+
await fs.emptyDir(CREDS_DATA_DIR);
|
|
294
297
|
});
|
|
295
298
|
|
|
296
299
|
describe('GET /idp/credentials', () => {
|
|
@@ -366,7 +369,7 @@ describe('Identity Provider - Credentials Endpoint', () => {
|
|
|
366
369
|
assert.ok(body.webid.includes('credtest'), 'should have webid');
|
|
367
370
|
});
|
|
368
371
|
|
|
369
|
-
it('should return
|
|
372
|
+
it('should return JWT token with webid claim', async () => {
|
|
370
373
|
const res = await fetch(`${CREDS_URL}/idp/credentials`, {
|
|
371
374
|
method: 'POST',
|
|
372
375
|
headers: { 'Content-Type': 'application/json' },
|
|
@@ -378,15 +381,15 @@ describe('Identity Provider - Credentials Endpoint', () => {
|
|
|
378
381
|
|
|
379
382
|
const body = await res.json();
|
|
380
383
|
|
|
381
|
-
//
|
|
384
|
+
// JWT tokens have format: header.payload.signature
|
|
382
385
|
const parts = body.access_token.split('.');
|
|
383
|
-
assert.strictEqual(parts.length,
|
|
386
|
+
assert.strictEqual(parts.length, 3, 'JWT token has 3 parts');
|
|
384
387
|
|
|
385
|
-
// Decode the payload
|
|
386
|
-
const payload = JSON.parse(Buffer.from(parts[
|
|
388
|
+
// Decode the payload (second part)
|
|
389
|
+
const payload = JSON.parse(Buffer.from(parts[1], 'base64url').toString());
|
|
387
390
|
|
|
388
|
-
assert.ok(payload.
|
|
389
|
-
assert.ok(payload.
|
|
391
|
+
assert.ok(payload.webid, 'token should have webid claim');
|
|
392
|
+
assert.ok(payload.webid.includes('credtest'), 'webid should reference user');
|
|
390
393
|
assert.ok(payload.exp > payload.iat, 'should have valid expiry');
|
|
391
394
|
});
|
|
392
395
|
|
package/test/ldp.test.js
CHANGED
|
@@ -29,7 +29,8 @@ describe('LDP CRUD Operations', () => {
|
|
|
29
29
|
|
|
30
30
|
describe('GET', () => {
|
|
31
31
|
it('should return 404 for non-existent resource', async () => {
|
|
32
|
-
|
|
32
|
+
// Must use /public/ path for unauthenticated access
|
|
33
|
+
const res = await request('/ldptest/public/nonexistent.json');
|
|
33
34
|
assertStatus(res, 404);
|
|
34
35
|
});
|
|
35
36
|
|
|
@@ -149,14 +150,18 @@ describe('LDP CRUD Operations', () => {
|
|
|
149
150
|
assertStatus(parent, 200);
|
|
150
151
|
});
|
|
151
152
|
|
|
152
|
-
it('should
|
|
153
|
-
|
|
153
|
+
it('should create container with PUT to path ending in slash', async () => {
|
|
154
|
+
// Solid spec: PUT to path with trailing / creates container
|
|
155
|
+
const res = await request('/ldptest/public/new-container/', {
|
|
154
156
|
method: 'PUT',
|
|
155
|
-
body: 'cannot put to container',
|
|
156
157
|
auth: 'ldptest'
|
|
157
158
|
});
|
|
158
159
|
|
|
159
|
-
assertStatus(res,
|
|
160
|
+
assertStatus(res, 201);
|
|
161
|
+
|
|
162
|
+
// Verify it's a container
|
|
163
|
+
const verify = await request('/ldptest/public/new-container/');
|
|
164
|
+
assertHeaderContains(verify, 'Link', 'Container');
|
|
160
165
|
});
|
|
161
166
|
});
|
|
162
167
|
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "3c1cd503-1d7f-4ba0-a3af-ebedf519594d",
|
|
3
|
+
"email": "credtest@example.com",
|
|
4
|
+
"passwordHash": "$2b$10$h3cRwsgAo/4wEOyip6ckyOf6J.reHOzJzyZrM.LDfQdIWa3e/MkAu",
|
|
5
|
+
"webId": "http://localhost:3101/credtest/#me",
|
|
6
|
+
"podName": "credtest",
|
|
7
|
+
"createdAt": "2025-12-27T12:48:00.226Z",
|
|
8
|
+
"lastLogin": "2025-12-27T12:48:00.567Z"
|
|
9
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"jwks": {
|
|
3
|
+
"keys": [
|
|
4
|
+
{
|
|
5
|
+
"kty": "EC",
|
|
6
|
+
"x": "NzQ-skj7cwbmI4Q_-vlQzoPYoQLWq_u34ln95VMcpnU",
|
|
7
|
+
"y": "H6gMaardV77boCWD4OTix1DsxdY-clIBw7I5xvvDDe8",
|
|
8
|
+
"crv": "P-256",
|
|
9
|
+
"d": "WrlpdJo_JidHFldBjU4Q6Wv_ULhAk1j-DTU6YlHxDAQ",
|
|
10
|
+
"kid": "4e655460-7c94-479c-9ed8-923aa8bfd77f",
|
|
11
|
+
"use": "sig",
|
|
12
|
+
"alg": "ES256",
|
|
13
|
+
"iat": 1766839680
|
|
14
|
+
}
|
|
15
|
+
]
|
|
16
|
+
},
|
|
17
|
+
"cookieKeys": [
|
|
18
|
+
"CQXN03oU_rUqugGhwZgoiI7eZMgKJaPE_kyrT9lmTu4",
|
|
19
|
+
"jsJGLtKzYy-RJPZaAMZDpUcfY4-EtdqNOFS-Uiowk9w"
|
|
20
|
+
],
|
|
21
|
+
"createdAt": "2025-12-27T12:48:00.136Z"
|
|
22
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import * as jose from 'jose';
|
|
2
|
+
import crypto from 'crypto';
|
|
3
|
+
|
|
4
|
+
const BASE = 'http://localhost:4000';
|
|
5
|
+
|
|
6
|
+
// Create DPoP proof
|
|
7
|
+
async function createDpopProof(privateKey, publicJwk, method, url, ath = null) {
|
|
8
|
+
const payload = {
|
|
9
|
+
jti: crypto.randomUUID(),
|
|
10
|
+
htm: method,
|
|
11
|
+
htu: url,
|
|
12
|
+
iat: Math.floor(Date.now() / 1000),
|
|
13
|
+
};
|
|
14
|
+
if (ath) payload.ath = ath;
|
|
15
|
+
|
|
16
|
+
return new jose.SignJWT(payload)
|
|
17
|
+
.setProtectedHeader({ alg: 'ES256', typ: 'dpop+jwt', jwk: publicJwk })
|
|
18
|
+
.sign(privateKey);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async function main() {
|
|
22
|
+
console.log('=== Testing DPoP Auth Flow ===\n');
|
|
23
|
+
|
|
24
|
+
// 1. Generate key pair
|
|
25
|
+
const { privateKey, publicKey } = await jose.generateKeyPair('ES256');
|
|
26
|
+
const publicJwk = await jose.exportJWK(publicKey);
|
|
27
|
+
const jkt = await jose.calculateJwkThumbprint(publicJwk, 'sha256');
|
|
28
|
+
console.log('1. Generated DPoP key pair, thumbprint:', jkt.substring(0, 20) + '...\n');
|
|
29
|
+
|
|
30
|
+
// 2. Register client dynamically
|
|
31
|
+
console.log('2. Registering client...');
|
|
32
|
+
const regRes = await fetch(`${BASE}/idp/reg`, {
|
|
33
|
+
method: 'POST',
|
|
34
|
+
headers: { 'Content-Type': 'application/json' },
|
|
35
|
+
body: JSON.stringify({
|
|
36
|
+
redirect_uris: ['https://tester'],
|
|
37
|
+
token_endpoint_auth_method: 'none',
|
|
38
|
+
grant_types: ['authorization_code'],
|
|
39
|
+
response_types: ['code'],
|
|
40
|
+
}),
|
|
41
|
+
});
|
|
42
|
+
const client = await regRes.json();
|
|
43
|
+
console.log(' Client ID:', client.client_id, '\n');
|
|
44
|
+
|
|
45
|
+
// 3. Generate PKCE
|
|
46
|
+
const codeVerifier = crypto.randomBytes(32).toString('base64url');
|
|
47
|
+
const codeChallenge = crypto.createHash('sha256').update(codeVerifier).digest('base64url');
|
|
48
|
+
console.log('3. Generated PKCE challenge\n');
|
|
49
|
+
|
|
50
|
+
// 4. Authorization request - WITH dpop_jkt parameter
|
|
51
|
+
console.log('4. Starting authorization (with dpop_jkt)...');
|
|
52
|
+
const authUrl = new URL(`${BASE}/idp/auth`);
|
|
53
|
+
authUrl.searchParams.set('client_id', client.client_id);
|
|
54
|
+
authUrl.searchParams.set('redirect_uri', 'https://tester');
|
|
55
|
+
authUrl.searchParams.set('response_type', 'code');
|
|
56
|
+
authUrl.searchParams.set('scope', 'openid');
|
|
57
|
+
authUrl.searchParams.set('code_challenge', codeChallenge);
|
|
58
|
+
authUrl.searchParams.set('code_challenge_method', 'S256');
|
|
59
|
+
authUrl.searchParams.set('dpop_jkt', jkt); // KEY: Include dpop_jkt!
|
|
60
|
+
|
|
61
|
+
const authRes = await fetch(authUrl, { redirect: 'manual' });
|
|
62
|
+
const interactionUrl = authRes.headers.get('location');
|
|
63
|
+
console.log(' Redirected to:', interactionUrl ? interactionUrl.substring(0, 50) + '...' : 'none');
|
|
64
|
+
console.log(' Status:', authRes.status, '\n');
|
|
65
|
+
|
|
66
|
+
// 5. Get interaction session cookie
|
|
67
|
+
const rawCookies = authRes.headers.get('set-cookie') || '';
|
|
68
|
+
// Extract just name=value from each Set-Cookie, ignore attributes
|
|
69
|
+
const cookieValues = rawCookies.split(/, (?=[^;]+=[^;]+)/).map(c => c.split(';')[0]).join('; ');
|
|
70
|
+
console.log('5. Got cookies:', cookieValues ? cookieValues.substring(0, 80) + '...' : 'none\n');
|
|
71
|
+
|
|
72
|
+
// 6. Login
|
|
73
|
+
console.log('6. Logging in...');
|
|
74
|
+
const uid = interactionUrl ? interactionUrl.match(/interaction\/([^/?]+)/)?.[1] : null;
|
|
75
|
+
if (!uid) {
|
|
76
|
+
console.log(' ERROR: No interaction UID found');
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const loginRes = await fetch(`${BASE}/idp/interaction/${uid}`, {
|
|
80
|
+
method: 'POST',
|
|
81
|
+
headers: {
|
|
82
|
+
'Content-Type': 'application/json',
|
|
83
|
+
Cookie: cookieValues,
|
|
84
|
+
},
|
|
85
|
+
body: JSON.stringify({ email: 'alice@example.com', password: 'alicepassword123' }),
|
|
86
|
+
});
|
|
87
|
+
let loginBody;
|
|
88
|
+
const loginText = await loginRes.text();
|
|
89
|
+
try {
|
|
90
|
+
loginBody = JSON.parse(loginText);
|
|
91
|
+
} catch (e) {
|
|
92
|
+
console.log(' Login response (text):', loginText.substring(0, 200));
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
console.log(' Login response:', loginRes.status, loginBody.location ? loginBody.location.substring(0, 50) : '');
|
|
96
|
+
|
|
97
|
+
// 7. Follow auth resume
|
|
98
|
+
console.log('\n7. Following auth resume...');
|
|
99
|
+
const resumeUrl = loginBody.location;
|
|
100
|
+
if (!resumeUrl) {
|
|
101
|
+
console.log(' ERROR: No resume URL');
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
const fullResumeUrl = resumeUrl.startsWith('http') ? resumeUrl : `${BASE}${resumeUrl}`;
|
|
105
|
+
const resumeRes = await fetch(fullResumeUrl, {
|
|
106
|
+
redirect: 'manual',
|
|
107
|
+
headers: { Cookie: cookieValues },
|
|
108
|
+
});
|
|
109
|
+
const callbackUrl = resumeRes.headers.get('location');
|
|
110
|
+
console.log(' Resume status:', resumeRes.status);
|
|
111
|
+
console.log(' Callback URL:', callbackUrl ? callbackUrl.substring(0, 80) + '...' : 'none');
|
|
112
|
+
|
|
113
|
+
// 8. Extract code
|
|
114
|
+
const codeMatch = callbackUrl ? callbackUrl.match(/code=([^&]+)/) : null;
|
|
115
|
+
const code = codeMatch ? codeMatch[1] : null;
|
|
116
|
+
if (!code) {
|
|
117
|
+
console.log(' ERROR: No code in callback');
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
console.log(' Code:', code.substring(0, 20) + '...\n');
|
|
121
|
+
|
|
122
|
+
// 9. Token exchange with DPoP
|
|
123
|
+
console.log('8. Exchanging code for token (with DPoP)...');
|
|
124
|
+
const dpopProof = await createDpopProof(privateKey, publicJwk, 'POST', `${BASE}/idp/token`);
|
|
125
|
+
const tokenRes = await fetch(`${BASE}/idp/token`, {
|
|
126
|
+
method: 'POST',
|
|
127
|
+
headers: {
|
|
128
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
129
|
+
DPoP: dpopProof,
|
|
130
|
+
},
|
|
131
|
+
body: new URLSearchParams({
|
|
132
|
+
grant_type: 'authorization_code',
|
|
133
|
+
code: code,
|
|
134
|
+
redirect_uri: 'https://tester',
|
|
135
|
+
client_id: client.client_id,
|
|
136
|
+
code_verifier: codeVerifier,
|
|
137
|
+
}).toString(),
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
console.log(' Token response status:', tokenRes.status);
|
|
141
|
+
const tokenBody = await tokenRes.text();
|
|
142
|
+
console.log(' Token response:', tokenBody.substring(0, 300));
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
main().catch(err => {
|
|
146
|
+
console.error('Error:', err.message);
|
|
147
|
+
console.error(err.stack);
|
|
148
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
@base <https://github.com/solid/conformance-test-harness/> .
|
|
2
|
+
@prefix solid-test: <https://github.com/solid/conformance-test-harness/vocab#> .
|
|
3
|
+
@prefix doap: <http://usefulinc.com/ns/doap#> .
|
|
4
|
+
@prefix earl: <http://www.w3.org/ns/earl#> .
|
|
5
|
+
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
|
|
6
|
+
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
|
|
7
|
+
|
|
8
|
+
<jss>
|
|
9
|
+
a earl:Software, earl:TestSubject ;
|
|
10
|
+
doap:name "JavaScript Solid Server"@en ;
|
|
11
|
+
doap:release <jss#test-subject-release> ;
|
|
12
|
+
doap:developer <https://github.com/JavaScriptSolidServer> ;
|
|
13
|
+
doap:homepage <https://github.com/JavaScriptSolidServer/JavaScriptSolidServer> ;
|
|
14
|
+
doap:description "A minimal, fast, JSON-LD native Solid server."@en ;
|
|
15
|
+
doap:programming-language "JavaScript"@en ;
|
|
16
|
+
solid-test:skip "acp", "wac", "wac-allow-public" ;
|
|
17
|
+
rdfs:comment "JSON-LD first Solid server with built-in IdP"@en .
|
|
18
|
+
|
|
19
|
+
<jss#test-subject-release>
|
|
20
|
+
doap:revision "0.0.14"@en ;
|
|
21
|
+
doap:created "2025-12-27"^^xsd:date .
|
package/data/alice/.acl
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"@context": {
|
|
3
|
-
"acl": "http://www.w3.org/ns/auth/acl#",
|
|
4
|
-
"foaf": "http://xmlns.com/foaf/0.1/"
|
|
5
|
-
},
|
|
6
|
-
"@graph": [
|
|
7
|
-
{
|
|
8
|
-
"@id": "#owner",
|
|
9
|
-
"@type": "acl:Authorization",
|
|
10
|
-
"acl:agent": {
|
|
11
|
-
"@id": "http://localhost:3457/alice/#me"
|
|
12
|
-
},
|
|
13
|
-
"acl:accessTo": {
|
|
14
|
-
"@id": "http://localhost:3457/alice/"
|
|
15
|
-
},
|
|
16
|
-
"acl:mode": [
|
|
17
|
-
{
|
|
18
|
-
"@id": "acl:Read"
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
"@id": "acl:Write"
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
"@id": "acl:Control"
|
|
25
|
-
}
|
|
26
|
-
],
|
|
27
|
-
"acl:default": {
|
|
28
|
-
"@id": "http://localhost:3457/alice/"
|
|
29
|
-
}
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
"@id": "#public",
|
|
33
|
-
"@type": "acl:Authorization",
|
|
34
|
-
"acl:agentClass": {
|
|
35
|
-
"@id": "foaf:Agent"
|
|
36
|
-
},
|
|
37
|
-
"acl:accessTo": {
|
|
38
|
-
"@id": "http://localhost:3457/alice/"
|
|
39
|
-
},
|
|
40
|
-
"acl:mode": [
|
|
41
|
-
{
|
|
42
|
-
"@id": "acl:Read"
|
|
43
|
-
}
|
|
44
|
-
],
|
|
45
|
-
"acl:default": {
|
|
46
|
-
"@id": "http://localhost:3457/alice/"
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
]
|
|
50
|
-
}
|
package/data/alice/inbox/.acl
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"@context": {
|
|
3
|
-
"acl": "http://www.w3.org/ns/auth/acl#",
|
|
4
|
-
"foaf": "http://xmlns.com/foaf/0.1/"
|
|
5
|
-
},
|
|
6
|
-
"@graph": [
|
|
7
|
-
{
|
|
8
|
-
"@id": "#owner",
|
|
9
|
-
"@type": "acl:Authorization",
|
|
10
|
-
"acl:agent": {
|
|
11
|
-
"@id": "http://localhost:3457/alice/#me"
|
|
12
|
-
},
|
|
13
|
-
"acl:accessTo": {
|
|
14
|
-
"@id": "http://localhost:3457/alice/inbox/"
|
|
15
|
-
},
|
|
16
|
-
"acl:default": {
|
|
17
|
-
"@id": "http://localhost:3457/alice/inbox/"
|
|
18
|
-
},
|
|
19
|
-
"acl:mode": [
|
|
20
|
-
{
|
|
21
|
-
"@id": "acl:Read"
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
"@id": "acl:Write"
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
"@id": "acl:Control"
|
|
28
|
-
}
|
|
29
|
-
]
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
"@id": "#public",
|
|
33
|
-
"@type": "acl:Authorization",
|
|
34
|
-
"acl:agentClass": {
|
|
35
|
-
"@id": "foaf:Agent"
|
|
36
|
-
},
|
|
37
|
-
"acl:accessTo": {
|
|
38
|
-
"@id": "http://localhost:3457/alice/inbox/"
|
|
39
|
-
},
|
|
40
|
-
"acl:default": {
|
|
41
|
-
"@id": "http://localhost:3457/alice/inbox/"
|
|
42
|
-
},
|
|
43
|
-
"acl:mode": [
|
|
44
|
-
{
|
|
45
|
-
"@id": "acl:Append"
|
|
46
|
-
}
|
|
47
|
-
]
|
|
48
|
-
}
|
|
49
|
-
]
|
|
50
|
-
}
|
package/data/alice/index.html
DELETED
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
-
<title>alice's Profile</title>
|
|
7
|
-
<script type="application/ld+json">
|
|
8
|
-
{
|
|
9
|
-
"@context": {
|
|
10
|
-
"foaf": "http://xmlns.com/foaf/0.1/",
|
|
11
|
-
"solid": "http://www.w3.org/ns/solid/terms#",
|
|
12
|
-
"schema": "http://schema.org/",
|
|
13
|
-
"pim": "http://www.w3.org/ns/pim/space#",
|
|
14
|
-
"ldp": "http://www.w3.org/ns/ldp#",
|
|
15
|
-
"inbox": {
|
|
16
|
-
"@id": "ldp:inbox",
|
|
17
|
-
"@type": "@id"
|
|
18
|
-
},
|
|
19
|
-
"storage": {
|
|
20
|
-
"@id": "pim:storage",
|
|
21
|
-
"@type": "@id"
|
|
22
|
-
},
|
|
23
|
-
"oidcIssuer": {
|
|
24
|
-
"@id": "solid:oidcIssuer",
|
|
25
|
-
"@type": "@id"
|
|
26
|
-
},
|
|
27
|
-
"preferencesFile": {
|
|
28
|
-
"@id": "pim:preferencesFile",
|
|
29
|
-
"@type": "@id"
|
|
30
|
-
}
|
|
31
|
-
},
|
|
32
|
-
"@graph": [
|
|
33
|
-
{
|
|
34
|
-
"@id": "http://localhost:3457/alice/",
|
|
35
|
-
"@type": "foaf:PersonalProfileDocument",
|
|
36
|
-
"foaf:maker": {
|
|
37
|
-
"@id": "http://localhost:3457/alice/#me"
|
|
38
|
-
},
|
|
39
|
-
"foaf:primaryTopic": {
|
|
40
|
-
"@id": "http://localhost:3457/alice/#me"
|
|
41
|
-
}
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
"@id": "http://localhost:3457/alice/#me",
|
|
45
|
-
"@type": [
|
|
46
|
-
"foaf:Person",
|
|
47
|
-
"schema:Person"
|
|
48
|
-
],
|
|
49
|
-
"foaf:name": "alice",
|
|
50
|
-
"inbox": "http://localhost:3457/alice/inbox/",
|
|
51
|
-
"storage": "http://localhost:3457/alice/",
|
|
52
|
-
"oidcIssuer": "http://localhost:3457",
|
|
53
|
-
"preferencesFile": "http://localhost:3457/alice/settings/prefs"
|
|
54
|
-
}
|
|
55
|
-
]
|
|
56
|
-
}
|
|
57
|
-
</script>
|
|
58
|
-
<style>
|
|
59
|
-
body { font-family: system-ui, sans-serif; max-width: 600px; margin: 2rem auto; padding: 0 1rem; }
|
|
60
|
-
h1 { color: #333; }
|
|
61
|
-
.card { background: #f5f5f5; padding: 1.5rem; border-radius: 8px; }
|
|
62
|
-
dt { font-weight: bold; margin-top: 1rem; }
|
|
63
|
-
dd { margin-left: 0; color: #666; }
|
|
64
|
-
a { color: #7c4dff; }
|
|
65
|
-
</style>
|
|
66
|
-
</head>
|
|
67
|
-
<body>
|
|
68
|
-
<div class="card">
|
|
69
|
-
<h1>alice</h1>
|
|
70
|
-
<dl>
|
|
71
|
-
<dt>WebID</dt>
|
|
72
|
-
<dd><a href="http://localhost:3457/alice/#me">http://localhost:3457/alice/#me</a></dd>
|
|
73
|
-
<dt>Storage</dt>
|
|
74
|
-
<dd><a href="http://localhost:3457/alice/">http://localhost:3457/alice/</a></dd>
|
|
75
|
-
<dt>Inbox</dt>
|
|
76
|
-
<dd><a href="http://localhost:3457/alice/inbox/">http://localhost:3457/alice/inbox/</a></dd>
|
|
77
|
-
</dl>
|
|
78
|
-
</div>
|
|
79
|
-
</body>
|
|
80
|
-
</html>
|
package/data/alice/private/.acl
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"@context": {
|
|
3
|
-
"acl": "http://www.w3.org/ns/auth/acl#",
|
|
4
|
-
"foaf": "http://xmlns.com/foaf/0.1/"
|
|
5
|
-
},
|
|
6
|
-
"@graph": [
|
|
7
|
-
{
|
|
8
|
-
"@id": "#owner",
|
|
9
|
-
"@type": "acl:Authorization",
|
|
10
|
-
"acl:agent": {
|
|
11
|
-
"@id": "http://localhost:3457/alice/#me"
|
|
12
|
-
},
|
|
13
|
-
"acl:accessTo": {
|
|
14
|
-
"@id": "http://localhost:3457/alice/private/"
|
|
15
|
-
},
|
|
16
|
-
"acl:mode": [
|
|
17
|
-
{
|
|
18
|
-
"@id": "acl:Read"
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
"@id": "acl:Write"
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
"@id": "acl:Control"
|
|
25
|
-
}
|
|
26
|
-
],
|
|
27
|
-
"acl:default": {
|
|
28
|
-
"@id": "http://localhost:3457/alice/private/"
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
]
|
|
32
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"@id":"#test","http://example.org/value":42}
|
package/data/alice/settings/.acl
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"@context": {
|
|
3
|
-
"acl": "http://www.w3.org/ns/auth/acl#",
|
|
4
|
-
"foaf": "http://xmlns.com/foaf/0.1/"
|
|
5
|
-
},
|
|
6
|
-
"@graph": [
|
|
7
|
-
{
|
|
8
|
-
"@id": "#owner",
|
|
9
|
-
"@type": "acl:Authorization",
|
|
10
|
-
"acl:agent": {
|
|
11
|
-
"@id": "http://localhost:3457/alice/#me"
|
|
12
|
-
},
|
|
13
|
-
"acl:accessTo": {
|
|
14
|
-
"@id": "http://localhost:3457/alice/settings/"
|
|
15
|
-
},
|
|
16
|
-
"acl:mode": [
|
|
17
|
-
{
|
|
18
|
-
"@id": "acl:Read"
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
"@id": "acl:Write"
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
"@id": "acl:Control"
|
|
25
|
-
}
|
|
26
|
-
],
|
|
27
|
-
"acl:default": {
|
|
28
|
-
"@id": "http://localhost:3457/alice/settings/"
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
]
|
|
32
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"@context": {
|
|
3
|
-
"solid": "http://www.w3.org/ns/solid/terms#",
|
|
4
|
-
"pim": "http://www.w3.org/ns/pim/space#",
|
|
5
|
-
"publicTypeIndex": {
|
|
6
|
-
"@id": "solid:publicTypeIndex",
|
|
7
|
-
"@type": "@id"
|
|
8
|
-
},
|
|
9
|
-
"privateTypeIndex": {
|
|
10
|
-
"@id": "solid:privateTypeIndex",
|
|
11
|
-
"@type": "@id"
|
|
12
|
-
}
|
|
13
|
-
},
|
|
14
|
-
"@id": "http://localhost:3457/alice/settings/prefs",
|
|
15
|
-
"publicTypeIndex": "http://localhost:3457/alice/settings/publicTypeIndex",
|
|
16
|
-
"privateTypeIndex": "http://localhost:3457/alice/settings/privateTypeIndex"
|
|
17
|
-
}
|
package/data/bob/.acl
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"@context": {
|
|
3
|
-
"acl": "http://www.w3.org/ns/auth/acl#",
|
|
4
|
-
"foaf": "http://xmlns.com/foaf/0.1/"
|
|
5
|
-
},
|
|
6
|
-
"@graph": [
|
|
7
|
-
{
|
|
8
|
-
"@id": "#owner",
|
|
9
|
-
"@type": "acl:Authorization",
|
|
10
|
-
"acl:agent": {
|
|
11
|
-
"@id": "http://localhost:3457/bob/#me"
|
|
12
|
-
},
|
|
13
|
-
"acl:accessTo": {
|
|
14
|
-
"@id": "http://localhost:3457/bob/"
|
|
15
|
-
},
|
|
16
|
-
"acl:mode": [
|
|
17
|
-
{
|
|
18
|
-
"@id": "acl:Read"
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
"@id": "acl:Write"
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
"@id": "acl:Control"
|
|
25
|
-
}
|
|
26
|
-
],
|
|
27
|
-
"acl:default": {
|
|
28
|
-
"@id": "http://localhost:3457/bob/"
|
|
29
|
-
}
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
"@id": "#public",
|
|
33
|
-
"@type": "acl:Authorization",
|
|
34
|
-
"acl:agentClass": {
|
|
35
|
-
"@id": "foaf:Agent"
|
|
36
|
-
},
|
|
37
|
-
"acl:accessTo": {
|
|
38
|
-
"@id": "http://localhost:3457/bob/"
|
|
39
|
-
},
|
|
40
|
-
"acl:mode": [
|
|
41
|
-
{
|
|
42
|
-
"@id": "acl:Read"
|
|
43
|
-
}
|
|
44
|
-
],
|
|
45
|
-
"acl:default": {
|
|
46
|
-
"@id": "http://localhost:3457/bob/"
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
]
|
|
50
|
-
}
|