Skip to content

Commit ec7652e

Browse files
committed
trying to update docker - failing on network request still
1 parent 6f5736f commit ec7652e

5 files changed

Lines changed: 327 additions & 5 deletions

File tree

Dockerfile

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@ RUN yarn install
1414
# COPY . .
1515

1616
RUN yarn build
17-
RUN yarn install:demo
18-
RUN yarn build:demo
1917

2018
EXPOSE 3000
2119
EXPOSE 4000

demo/flow-test.ts

Lines changed: 323 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,323 @@
1+
/* eslint-disable max-len */
2+
3+
import { fetch } from 'cross-fetch';
4+
import { Parser, Writer, Store } from 'n3';
5+
import { randomUUID } from 'crypto';
6+
import chalk from 'chalk'
7+
8+
// import * as jsonld from 'jsonld';
9+
10+
// import vc from '@digitalcredentials/vc';
11+
12+
// // Required to set up a suite instance with private key
13+
// import {Ed25519VerificationKey2020} from
14+
// '@digitalcredentials/ed25519-verification-key-2020';
15+
// import {Ed25519Signature2020} from '@digitalcredentials/ed25519-signature-2020';
16+
17+
18+
19+
const parser = new Parser();
20+
const writer = new Writer();
21+
22+
const terms = {
23+
solid: {
24+
umaServer: 'http://www.w3.org/ns/solid/terms#umaServer',
25+
viewIndex: 'http://www.w3.org/ns/solid/terms#viewIndex',
26+
entry: 'http://www.w3.org/ns/solid/terms#entry',
27+
filter: 'http://www.w3.org/ns/solid/terms#filter',
28+
location: 'http://www.w3.org/ns/solid/terms#location',
29+
},
30+
filters: {
31+
bday: 'http://localhost:3000/catalog/public/filters/bday',
32+
age: 'http://localhost:3000/catalog/public/filters/age',
33+
},
34+
views: {
35+
bday: 'http://localhost:3000/ruben/private/derived/bday',
36+
age: 'http://localhost:3000/ruben/private/derived/age',
37+
},
38+
resources: {
39+
collectionSource: 'http://localhost:3000/ruben/medical/',
40+
smartwatch: 'http://localhost:3000/ruben/medical/smartwatch.ttl',
41+
heartrate: 'http://localhost:3000/ruben/medical/heartRate.ttl',
42+
},
43+
agents: {
44+
ruben: 'http://localhost:3000/ruben/profile/card#me',
45+
alice: 'http://localhost:3000/alice/profile/card#me',
46+
vendor: 'http://localhost:3000/demo/public/vendor',
47+
present: 'http://localhost:3000/demo/public/bday-app',
48+
},
49+
scopes: {
50+
read: 'urn:example:css:modes:read',
51+
}
52+
}
53+
54+
const policyContainer = 'http://localhost:3000/ruben/settings/policies/';
55+
56+
async function main() {
57+
58+
log('')
59+
log('=================== UMA prototype flow ======================')
60+
61+
let webIdData;
62+
try {
63+
webIdData = new Store(parser.parse(await (await fetch(terms.agents.ruben)).text()));
64+
} catch (e) {
65+
log('Error fetching WebID data:', e);
66+
return;
67+
}
68+
69+
const umaServer = webIdData.getObjects(terms.agents.ruben, terms.solid.umaServer, null)[0].value;
70+
const configUrl = new URL('.well-known/uma2-configuration', umaServer);
71+
const umaConfig = await (await fetch(configUrl)).json();
72+
const tokenEndpoint = umaConfig.token_endpoint;
73+
74+
log("This flow defines the retrieval by a doctor of a patient resource.")
75+
log(
76+
`Doctor WebID: ${terms.agents.alice}
77+
Patient WebID: ${terms.agents.ruben}
78+
Target Resource: ${terms.resources.smartwatch}`)
79+
80+
/*************************
81+
* Setting up the policy *
82+
*************************/
83+
84+
log('To protect this data, a policy is added restricting access to a specific healthcare employee for the purpose of bariatric care.');
85+
log(chalk.italic(`Note: Policy management is out of scope for POC1, right now they are just served from a public container on the pod.
86+
additionally, selecting relevant policies is not implemented at the moment, all policies are evaluated, but this is a minor fix in the AS.`))
87+
88+
const healthcare_patient_policy =
89+
`@prefix dcterms: <http://purl.org/dc/terms/>.
90+
@prefix eu-gdpr: <https://w3id.org/dpv/legal/eu/gdpr#>.
91+
@prefix oac: <https://w3id.org/oac#>.
92+
@prefix odrl: <http://www.w3.org/ns/odrl/2/>.
93+
@prefix xsd: <http://www.w3.org/2001/XMLSchema#>.
94+
95+
@prefix ex: <http://example.org/>.
96+
97+
<http://example.org/HCPX-request> a odrl:Request ;
98+
odrl:uid ex:HCPX-request ;
99+
odrl:profile oac: ;
100+
dcterms:description "HCP X requests to read Alice's health data for bariatric care.";
101+
odrl:permission <http://example.org/HCPX-request-permission> .
102+
103+
<http://example.org/HCPX-request-permission> a odrl:Permission ;
104+
odrl:action odrl:read ;
105+
odrl:target <http://example.org/medical-data-access-collection> ;
106+
odrl:assigner <${terms.agents.ruben}> ;
107+
odrl:assignee <${terms.agents.alice}> ;
108+
odrl:constraint <http://example.org/HCPX-request-permission-purpose>,
109+
<http://example.org/HCPX-request-permission-lb> .
110+
111+
<http://example.org/medical-data-access-collection> a odrl:AssetCollection;
112+
odrl:source <${terms.resources.collectionSource}> .
113+
114+
<http://example.org/HCPX-request-permission-purpose> a odrl:Constraint ;
115+
odrl:leftOperand odrl:purpose ; # can also be oac:Purpose, to conform with OAC profile
116+
odrl:operator odrl:eq ;
117+
odrl:rightOperand ex:bariatric-care .
118+
119+
<http://example.org/HCPX-request-permission-lb> a odrl:Constraint ;
120+
odrl:leftOperand oac:LegalBasis ;
121+
odrl:operator odrl:eq ;
122+
odrl:rightOperand eu-gdpr:A9-2-a .`
123+
124+
await addPolicy(healthcare_patient_policy, policyContainer)
125+
126+
log(`The policy assigns read permissions for the personal doctor ${terms.agents.alice} of the patient for the smartwatch resource on the condition
127+
of the purpose of the request being "http://example.org/bariatric-care" and the legal basis being "https://w3id.org/dpv/legal/eu/gdpr#A9-2-a".`)
128+
129+
130+
/**********************
131+
* Reading a resource *
132+
**********************/
133+
log(chalk.bold("The doctor now tries to access the private smartwatch resource."))
134+
135+
136+
const smartWatchAccessRequestNoClaimsODRL = {
137+
"@context": "http://www.w3.org/ns/odrl.jsonld",
138+
"@type": "Request",
139+
profile: { "@id": "https://w3id.org/oac#" },
140+
uid: `http://example.org/HCPX-request/${randomUUID()}`,
141+
description: "HCP X requests to read Alice's health data for bariatric care.",
142+
permission: [ {
143+
"@type": "Permission",
144+
"uid": `http://example.org/HCPX-request-permission/${randomUUID()}`,
145+
assigner: terms.agents.ruben,
146+
assignee: terms.agents.alice,
147+
action: { "@id": "https://w3id.org/oac#read" },
148+
target: terms.resources.smartwatch,
149+
} ],
150+
grant_type: "urn:ietf:params:oauth:grant-type:uma-ticket",
151+
}
152+
153+
const response = await executeReadWithClaims(terms.resources.smartwatch, smartWatchAccessRequestNoClaimsODRL, { tokenEndpoint })
154+
155+
if (response.succeed) {
156+
throw new Error(`Resource request for ${terms.resources.smartwatch} should not have succeeded without claims: ${response}`)
157+
}
158+
159+
log(`Based on the policy set above, the Authorization Server requests the following claims from the doctor:`);
160+
response.required_claims.claim_token_format[0].forEach((format: string) => log(` - ${format}`))
161+
log(`accompanied by an updated ticket: ${response.ticket}.`)
162+
163+
// JWT (HS256; secret: "ceci n'est pas un secret")
164+
// {
165+
// "http://www.w3.org/ns/odrl/2/purpose": "http://example.org/bariatric-care",
166+
// "urn:solidlab:uma:claims:types:webid": "http://localhost:3000/alice/profile/card#me",
167+
// "https://w3id.org/oac#LegalBasis": "https://w3id.org/dpv/legal/eu/gdpr#A9-2-a"
168+
// }
169+
const claim_token = "eyJhbGciOiJIUzI1NiJ9.eyJodHRwOi8vd3d3LnczLm9yZy9ucy9vZHJsLzIvcHVycG9zZSI6Imh0dHA6Ly9leGFtcGxlLm9yZy9iYXJpYXRyaWMtY2FyZSIsInVybjpzb2xpZGxhYjp1bWE6Y2xhaW1zOnR5cGVzOndlYmlkIjoiaHR0cDovL2xvY2FsaG9zdDozMDAwL2FsaWNlL3Byb2ZpbGUvY2FyZCNtZSIsImh0dHBzOi8vdzNpZC5vcmcvb2FjI0xlZ2FsQmFzaXMiOiJodHRwczovL3czaWQub3JnL2Rwdi9sZWdhbC9ldS9nZHByI0E5LTItYSJ9.nT55jaXNDsHgAo_zcRMsbJqcNj4FVdW_-xjcwNam-1M"
170+
171+
const claims: any = {
172+
"http://www.w3.org/ns/odrl/2/purpose": "http://example.org/bariatric-care",
173+
"urn:solidlab:uma:claims:types:webid": "http://localhost:3000/alice/profile/card#me",
174+
"https://w3id.org/oac#LegalBasis": "https://w3id.org/dpv/legal/eu/gdpr#A9-2-a"
175+
}
176+
177+
log(`The doctor's client now gathers the necessary claims (how is out-of-scope for this demo)`, claims)
178+
179+
log(`and bundles them as an UMA-compliant JWT.`, {
180+
claim_token: claim_token,
181+
claim_token_format: "urn:solidlab:uma:claims:formats:jwt"
182+
})
183+
184+
const smartWatchAccessRequestODRL = {
185+
"@context": "http://www.w3.org/ns/odrl.jsonld",
186+
"@type": "Request",
187+
profile: { "@id": "https://w3id.org/oac#" },
188+
uid: `http://example.org/HCPX-request/${randomUUID()}`,
189+
description: "HCP X requests to read Alice's health data for bariatric care.",
190+
permission: [ {
191+
"@type": "Permission",
192+
"@id": `http://example.org/HCPX-request-permission/${randomUUID()}`,
193+
target: terms.resources.smartwatch,
194+
action: { "@id": "https://w3id.org/oac#read" },
195+
assigner: terms.agents.ruben,
196+
assignee: terms.agents.alice,
197+
constraint: [
198+
{
199+
"@type": "Constraint",
200+
"@id": `http://example.org/HCPX-request-permission-purpose/${randomUUID()}`,
201+
leftOperand: "purpose",
202+
operator: "eq",
203+
rightOperand: { "@id": "http://example.org/bariatric-care" },
204+
}, {
205+
"@type": "Constraint",
206+
"@id": `http://example.org/HCPX-request-permission-purpose/${randomUUID()}`,
207+
leftOperand: { "@id": "https://w3id.org/oac#LegalBasis" },
208+
operator: "eq",
209+
rightOperand: {"@id": "https://w3id.org/dpv/legal/eu/gdpr#A9-2-a" },
210+
}
211+
],
212+
} ],
213+
// claims: [{
214+
claim_token: claim_token,
215+
claim_token_format: "urn:solidlab:uma:claims:formats:jwt",
216+
// }],
217+
// UMA specific fields
218+
grant_type: "urn:ietf:params:oauth:grant-type:uma-ticket",
219+
// ticket: response.ticket,
220+
}
221+
222+
const response2 = await executeReadWithClaims(terms.resources.smartwatch, smartWatchAccessRequestODRL, { tokenEndpoint })
223+
224+
if (response2.failed) {
225+
throw new Error(`Resource request for ${terms.resources.smartwatch} should not have failed with claims: ${response}`)
226+
}
227+
228+
const access_token = parseJwt(response2.access_token)
229+
230+
log(`The UMA server checks the claims with the relevant policy, and returns the agent an access token with the requested permissions.`,
231+
JSON.stringify(access_token.permissions, null, 2));
232+
233+
log(`and the accompanying agreement:`,
234+
JSON.stringify(access_token.contract, null, 2));
235+
236+
log(chalk.italic(`Future work: at a later stage, this agreements will be signed by both parties to form a binding contract.`))
237+
238+
const accessWithTokenResponse = await fetch(terms.resources.smartwatch, {
239+
headers: { 'Authorization': `${response2.token_type} ${response2.access_token}` }
240+
});
241+
242+
log(`Now the doctor can retrieve the resource:`, await accessWithTokenResponse.text());
243+
244+
if (accessWithTokenResponse.status !== 200) { log(`Access with token failed...`); throw 0; }
245+
246+
}
247+
248+
main();
249+
250+
251+
/* Helper functions */
252+
253+
function parseJwt (token:string) {
254+
return JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString());
255+
}
256+
257+
function log(msg: string, obj?: any) {
258+
console.log('');
259+
console.log(msg);
260+
if (obj) {
261+
console.log('\n');
262+
console.log(obj);
263+
}
264+
}
265+
266+
267+
async function executeReadWithClaims(target: string, request: any, options: { tokenEndpoint: string }) {
268+
269+
const res = await fetch(target, {
270+
method: "GET",
271+
headers: { "content-type": "application/json" },
272+
});
273+
274+
const umaHeader = await res.headers.get('WWW-Authenticate')
275+
276+
log(`Resource request to ${target} results in ${umaHeader}`)
277+
278+
let ticket = umaHeader?.split('ticket=')[1].replace(/"/g, '')
279+
280+
// setting ticket from resource request
281+
request.ticket = ticket;
282+
283+
log(`To the discovered AS, we now send a request for read permission to the target resource`, request)
284+
285+
const response = await fetch(options.tokenEndpoint, {
286+
method: "POST",
287+
headers: { "content-type": "application/json" },
288+
body: JSON.stringify(request),
289+
});
290+
291+
// if (response.status !== 403) { log('Access request succeeded without claims...', await response.text()); throw 0; }
292+
293+
const responseJSON = await response.json();
294+
if (responseJSON.required_claims) {
295+
responseJSON.status = false;
296+
return responseJSON
297+
} else {
298+
responseJSON.status = true;
299+
return responseJSON
300+
}
301+
}
302+
303+
async function executeWriteWithClaims(target: string, claims: any) {
304+
305+
}
306+
307+
async function addPolicy(policy: string, location: string) {
308+
309+
310+
const medicalPolicyCreationResponse = await fetch(location, {
311+
method: 'POST',
312+
headers: { 'content-type': 'text/turtle' },
313+
body: policy,
314+
});
315+
316+
log("The following policy is set for the AS:")
317+
log("----------------------------------------------------")
318+
log(policy)
319+
log("----------------------------------------------------")
320+
321+
if (medicalPolicyCreationResponse.status !== 201) { log('Adding a policy did not succeed...'); throw 0; }
322+
323+
}

docker-compose.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
version: '3.4'
22

33
services:
4-
solidtrustflows:
5-
image: solidtrustflows
4+
pacsoi-poc-1:
5+
image: pacsoi-poc1
66
build:
77
context: .
88
dockerfile: ./Dockerfile

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
"start": "yarn workspaces foreach --include 'packages/*' -A -pi -j unlimited run start",
5959
"start:demo": "yarn workspaces foreach --include 'packages/*' -A -pi -j unlimited run demo",
6060
"script:demo": "yarn exec tsx ./demo/flow.ts",
61+
"script:demo-test": "yarn exec tsx ./demo/flow-test.ts",
6162
"script:public": "yarn exec ts-node ./scripts/test-public.ts",
6263
"script:private": "yarn exec ts-node ./scripts/test-private.ts",
6364
"script:registration": "yarn exec ts-node ./scripts/test-registration.ts",

packages/css/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@
6464
"start": "yarn run community-solid-server -m . -c ./config/default.json --seedConfig ./config/seed.json -a http://localhost:4000/",
6565
"demo": "yarn run demo:setup && yarn run demo:start",
6666
"demo:setup": "yarn run -T shx rm -rf ./tmp && yarn run -T shx cp -R ../../demo/data ./tmp",
67-
"demo:start": "yarn run community-solid-server -m . -c ./config/demo.json -f ./tmp -a http://localhost:4000/"
67+
"demo:start": "yarn run community-solid-server -m . -c ./config/demo.json -f ./tmp -a http://localhost:4000/ -l debug"
6868
},
6969
"dependencies": {
7070
"@solid/community-server": "^7.0.2",

0 commit comments

Comments
 (0)