Skip to content

Commit 38abe16

Browse files
committed
fix: correctly propagate user id
Signed-off-by: Wouter Termont <wouter.termont@ugent.be>
1 parent 7229549 commit 38abe16

27 files changed

Lines changed: 121 additions & 156 deletions
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"key":"accounts/data/c5b28411-2340-4820-8f4f-62c209c20172","payload":{"linkedLoginsCount":1,"id":"c5b28411-2340-4820-8f4f-62c209c20172","authzServer":"http://localhost:4000/uma","**password**":{"934434d8-d44e-49c2-9618-694594059554":{"accountId":"c5b28411-2340-4820-8f4f-62c209c20172","email":"catalog@example.org","password":"$2a$10$8El17QwKSx3XaHjm.puBiOiNdNQv5t6JHPOVSvPnl8meQFE63CWo6","verified":true,"id":"934434d8-d44e-49c2-9618-694594059554"}},"**clientCredentials**":{},"**pod**":{"f1d42d48-8b96-4122-9e5d-f5803863a243":{"baseUrl":"http://localhost:3000/catalog/","accountId":"c5b28411-2340-4820-8f4f-62c209c20172","id":"f1d42d48-8b96-4122-9e5d-f5803863a243","**owner**":{"6bf4fe03-20c1-419d-9934-2b7533296edf":{"podId":"f1d42d48-8b96-4122-9e5d-f5803863a243","webId":"http://localhost:3000/catalog/profile/card#me","visible":false,"id":"6bf4fe03-20c1-419d-9934-2b7533296edf"}}}},"**webIdLink**":{"0c9522ea-b362-4991-bc72-fd1516834770":{"webId":"http://localhost:3000/catalog/profile/card#me","accountId":"c5b28411-2340-4820-8f4f-62c209c20172","id":"0c9522ea-b362-4991-bc72-fd1516834770"}}}}
1+
{"key":"accounts/data/c5b28411-2340-4820-8f4f-62c209c20172","payload":{"linkedLoginsCount":1,"id":"c5b28411-2340-4820-8f4f-62c209c20172","authzServer":"http://localhost:4000/catalog","**password**":{"934434d8-d44e-49c2-9618-694594059554":{"accountId":"c5b28411-2340-4820-8f4f-62c209c20172","email":"catalog@example.org","password":"$2a$10$8El17QwKSx3XaHjm.puBiOiNdNQv5t6JHPOVSvPnl8meQFE63CWo6","verified":true,"id":"934434d8-d44e-49c2-9618-694594059554"}},"**clientCredentials**":{},"**pod**":{"f1d42d48-8b96-4122-9e5d-f5803863a243":{"baseUrl":"http://localhost:3000/catalog/","accountId":"c5b28411-2340-4820-8f4f-62c209c20172","id":"f1d42d48-8b96-4122-9e5d-f5803863a243","**owner**":{"6bf4fe03-20c1-419d-9934-2b7533296edf":{"podId":"f1d42d48-8b96-4122-9e5d-f5803863a243","webId":"http://localhost:3000/catalog/profile/card#me","visible":false,"id":"6bf4fe03-20c1-419d-9934-2b7533296edf"}}}},"**webIdLink**":{"0c9522ea-b362-4991-bc72-fd1516834770":{"webId":"http://localhost:3000/catalog/profile/card#me","accountId":"c5b28411-2340-4820-8f4f-62c209c20172","id":"0c9522ea-b362-4991-bc72-fd1516834770"}}}}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"key":"accounts/data/d3156f11-ffb2-42f3-b928-b9752a9873ce","payload":{"linkedLoginsCount":1,"id":"d3156f11-ffb2-42f3-b928-b9752a9873ce","authzServer":"http://localhost:4000/uma","**password**":{"084fd63e-faf3-4169-a917-0cdeb768710d":{"accountId":"d3156f11-ffb2-42f3-b928-b9752a9873ce","email":"demo@example.org","password":"$2a$10$CRQGngKyURJztvqyDIdXfOuZMiE43z1kuV7BDwAJCmi/gL4TCcPJ2","verified":true,"id":"084fd63e-faf3-4169-a917-0cdeb768710d"}},"**clientCredentials**":{},"**pod**":{"eb3898e1-d409-41d7-b928-f11a2116f218":{"baseUrl":"http://localhost:3000/demo/","accountId":"d3156f11-ffb2-42f3-b928-b9752a9873ce","id":"eb3898e1-d409-41d7-b928-f11a2116f218","**owner**":{"63f475ea-e87c-472c-a224-1b918a9ae059":{"podId":"eb3898e1-d409-41d7-b928-f11a2116f218","webId":"http://localhost:3000/demo/profile/card#me","visible":false,"id":"63f475ea-e87c-472c-a224-1b918a9ae059"}}}},"**webIdLink**":{"ccd6dcae-8e4c-4e43-9888-cc3bdf49acbd":{"webId":"http://localhost:3000/demo/profile/card#me","accountId":"d3156f11-ffb2-42f3-b928-b9752a9873ce","id":"ccd6dcae-8e4c-4e43-9888-cc3bdf49acbd"}}}}
1+
{"key":"accounts/data/d3156f11-ffb2-42f3-b928-b9752a9873ce","payload":{"linkedLoginsCount":1,"id":"d3156f11-ffb2-42f3-b928-b9752a9873ce","authzServer":"http://localhost:4000/demo","**password**":{"084fd63e-faf3-4169-a917-0cdeb768710d":{"accountId":"d3156f11-ffb2-42f3-b928-b9752a9873ce","email":"demo@example.org","password":"$2a$10$CRQGngKyURJztvqyDIdXfOuZMiE43z1kuV7BDwAJCmi/gL4TCcPJ2","verified":true,"id":"084fd63e-faf3-4169-a917-0cdeb768710d"}},"**clientCredentials**":{},"**pod**":{"eb3898e1-d409-41d7-b928-f11a2116f218":{"baseUrl":"http://localhost:3000/demo/","accountId":"d3156f11-ffb2-42f3-b928-b9752a9873ce","id":"eb3898e1-d409-41d7-b928-f11a2116f218","**owner**":{"63f475ea-e87c-472c-a224-1b918a9ae059":{"podId":"eb3898e1-d409-41d7-b928-f11a2116f218","webId":"http://localhost:3000/demo/profile/card#me","visible":false,"id":"63f475ea-e87c-472c-a224-1b918a9ae059"}}}},"**webIdLink**":{"ccd6dcae-8e4c-4e43-9888-cc3bdf49acbd":{"webId":"http://localhost:3000/demo/profile/card#me","accountId":"d3156f11-ffb2-42f3-b928-b9752a9873ce","id":"ccd6dcae-8e4c-4e43-9888-cc3bdf49acbd"}}}}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"key":"accounts/data/f644f883-ef0f-4986-b5ff-df6866707cf6","payload":{"linkedLoginsCount":1,"id":"f644f883-ef0f-4986-b5ff-df6866707cf6","authzServer":"http://localhost:4000/uma","**password**":{"126fe0d0-8189-4a51-954a-79e09ff88e18":{"accountId":"f644f883-ef0f-4986-b5ff-df6866707cf6","email":"ruben@example.org","password":"$2a$10$76sVaHi0nDwl46jKtXZR1uxwwIg8hp6gcfzgT7GCzEdKaOVZSnd1e","verified":true,"id":"126fe0d0-8189-4a51-954a-79e09ff88e18"}},"**clientCredentials**":{},"**pod**":{"b79c41e7-a00d-421d-9b57-009c99e7b0d5":{"baseUrl":"http://localhost:3000/ruben/","accountId":"f644f883-ef0f-4986-b5ff-df6866707cf6","id":"b79c41e7-a00d-421d-9b57-009c99e7b0d5","**owner**":{"173cb7a2-2b22-4b25-b4fb-6f61e0adbd35":{"podId":"b79c41e7-a00d-421d-9b57-009c99e7b0d5","webId":"http://localhost:3000/ruben/profile/card#me","visible":false,"id":"173cb7a2-2b22-4b25-b4fb-6f61e0adbd35"}}}},"**webIdLink**":{"fd6d91cf-4b8c-4769-962f-d9f667ee6ee9":{"webId":"http://localhost:3000/ruben/profile/card#me","accountId":"f644f883-ef0f-4986-b5ff-df6866707cf6","id":"fd6d91cf-4b8c-4769-962f-d9f667ee6ee9"}}}}
1+
{"key":"accounts/data/f644f883-ef0f-4986-b5ff-df6866707cf6","payload":{"linkedLoginsCount":1,"id":"f644f883-ef0f-4986-b5ff-df6866707cf6","authzServer":"http://localhost:4000/ruben","**password**":{"126fe0d0-8189-4a51-954a-79e09ff88e18":{"accountId":"f644f883-ef0f-4986-b5ff-df6866707cf6","email":"ruben@example.org","password":"$2a$10$76sVaHi0nDwl46jKtXZR1uxwwIg8hp6gcfzgT7GCzEdKaOVZSnd1e","verified":true,"id":"126fe0d0-8189-4a51-954a-79e09ff88e18"}},"**clientCredentials**":{},"**pod**":{"b79c41e7-a00d-421d-9b57-009c99e7b0d5":{"baseUrl":"http://localhost:3000/ruben/","accountId":"f644f883-ef0f-4986-b5ff-df6866707cf6","id":"b79c41e7-a00d-421d-9b57-009c99e7b0d5","**owner**":{"173cb7a2-2b22-4b25-b4fb-6f61e0adbd35":{"podId":"b79c41e7-a00d-421d-9b57-009c99e7b0d5","webId":"http://localhost:3000/ruben/profile/card#me","visible":false,"id":"173cb7a2-2b22-4b25-b4fb-6f61e0adbd35"}}}},"**webIdLink**":{"fd6d91cf-4b8c-4769-962f-d9f667ee6ee9":{"webId":"http://localhost:3000/ruben/profile/card#me","accountId":"f644f883-ef0f-4986-b5ff-df6866707cf6","id":"fd6d91cf-4b8c-4769-962f-d9f667ee6ee9"}}}}

packages/css/config/seed.json

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,44 +3,28 @@
33
"email": "alice@example.org",
44
"password": "abc123",
55
"authz": {
6-
"server": "http://localhost:4000/uma"
6+
"server": "http://localhost:4000/alice"
77
},
88
"pods": [{
99
"name": "alice",
1010
"settings": {
1111
"name": "Alice",
12-
"umaServer": "http://localhost:4000/uma"
12+
"umaServer": "http://localhost:4000/alice"
1313
}
1414
}]
1515
},
1616
{
1717
"email": "bob@example.org",
1818
"password": "abc123",
1919
"authz": {
20-
"server": "http://localhost:4000/uma"
20+
"server": "http://localhost:4000/bob"
2121
},
2222
"pods": [
2323
{
2424
"name": "bob",
2525
"settings": {
2626
"name": "Bob",
27-
"umaServer": "http://localhost:4000/uma"
28-
}
29-
}
30-
]
31-
},
32-
{
33-
"email": "demo@example.org",
34-
"password": "abc123",
35-
"authz": {
36-
"server": "http://localhost:4000/uma"
37-
},
38-
"pods": [
39-
{
40-
"name": "demo",
41-
"settings": {
42-
"name": "Demo",
43-
"umaServer": "http://localhost:4000/uma"
27+
"umaServer": "http://localhost:4000/bob"
4428
}
4529
}
4630
]

packages/css/config/uma/default.json

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,6 @@
6969
},
7070
"storageStrategy": {
7171
"@id": "urn:solid-server:default:StorageLocationStrategy"
72-
},
73-
"umaPatStore": {
74-
"@id": "urn:solid-server:default:UmaPatStore",
75-
"@type": "MemoryMapStorage"
7672
}
7773
},
7874
{

packages/css/src/authentication/UmaTokenExtractor.ts

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -45,20 +45,16 @@ export class UmaTokenExtractor extends CredentialsExtractor {
4545

4646
try {
4747
const target = await this.targetExtractor.handle({ request });
48-
const owners = await this.ownerUtil.findOwners(target);
49-
const issuers = await Promise.all(owners.map(o => this.ownerUtil.findIssuer(o)))
50-
const validIssuers = issuers.filter((i): i is string => i !== undefined);
48+
const as = await this.ownerUtil.findAuthorizationServer(target);
5149

5250
if (this.introspect) {
5351
this.logger.debug('Performing token introspection.');
54-
const results = await Promise.allSettled(owners.map(owner => this.tryIntrospection(token, owner)));
55-
const succeeded = results.filter((r): r is PromiseFulfilledResult<UmaClaims> => r.status === 'fulfilled');
56-
if (succeeded.length === 0) throw new Error ();
57-
return { uma: { rpt: succeeded[0].value }};
52+
const as = await this.ownerUtil.findAuthorizationServer(target);
53+
const rpt = await this.client.verifyOpaqueToken(token, as)
54+
return { uma: { rpt }};
5855
} else {
5956
this.logger.debug('Verifying JWT.');
60-
const rpt = await this.client.verifyJwtToken(token, validIssuers ?? []);
61-
57+
const rpt = await this.client.verifyJwtToken(token, as);
6258
return { uma: { rpt } };
6359
}
6460
} catch (error: unknown) {
@@ -67,11 +63,4 @@ export class UmaTokenExtractor extends CredentialsExtractor {
6763
throw new BadRequestHttpError(msg, {cause: error});
6864
}
6965
}
70-
71-
private async tryIntrospection(token: string, owner: string): Promise<UmaClaims> {
72-
const issuer = await this.ownerUtil.findIssuer(owner);
73-
if (!issuer) return Promise.reject();
74-
return this.client.verifyOpaqueToken(token, issuer)
75-
}
76-
7766
}

packages/css/src/authorization/UmaAuthorizer.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -62,14 +62,18 @@ export class UmaAuthorizer extends Authorizer {
6262
}
6363

6464
protected async requestTicket(requestedModes: AccessMap): Promise<string | undefined> {
65-
const owner = await this.ownerUtil.findCommonOwner(requestedModes.keys());
66-
const issuer = await this.ownerUtil.findIssuer(owner);
67-
68-
if (!issuer) throw new Error(`No UMA authorization server found for ${owner}.`);
65+
const servers = new Set(await Promise.all(Array.from(requestedModes.keys()).map(
66+
(target) => this.ownerUtil.findAuthorizationServer(target))
67+
));
68+
69+
if (servers.size > 1) throw new Error(`Cannot request tickets at multiple authorization servers simultaneously.`);
70+
if (servers.size < 1) throw new Error(`No authorization server found for requested resources.`);
71+
72+
const as = servers.values().next().value as string;
6973

7074
try {
71-
const ticket = await this.umaClient.fetchTicket(requestedModes, issuer);
72-
return ticket ? `UMA realm="solid", as_uri="${issuer}", ticket="${ticket}"` : undefined;
75+
const ticket = await this.umaClient.fetchTicket(requestedModes, as);
76+
return ticket ? `UMA realm="solid", as_uri="${as}", ticket="${ticket}"` : undefined;
7377
} catch (e) {
7478
this.logger.error(`Error while requesting UMA header: ${(e as Error).message}`);
7579
throw new Error('Error while requesting UMA header.');

packages/css/src/uma/ResourceRegistrar.ts

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,25 +14,11 @@ export class ResourceRegistrar extends StaticHandler {
1414
super();
1515

1616
store.on(AS.Create, async (resource: ResourceIdentifier): Promise<void> => {
17-
for (const owner of await this.findOwners(resource)) {
18-
this.umaClient.createResource(resource, await this.findIssuer(owner));
19-
}
17+
this.umaClient.createResource(resource, await this.ownerUtil.findAuthorizationServer(resource));
2018
});
2119

2220
store.on(AS.Delete, async (resource: ResourceIdentifier): Promise<void> => {
23-
for (const owner of await this.findOwners(resource)) {
24-
this.umaClient.deleteResource(resource, await this.findIssuer(owner));
25-
}
21+
this.umaClient.deleteResource(resource, await this.ownerUtil.findAuthorizationServer(resource));
2622
});
2723
}
28-
29-
private async findOwners(resource: ResourceIdentifier): Promise<string[]> {
30-
return await this.ownerUtil.findOwners(resource).catch(() => []);
31-
}
32-
33-
private async findIssuer(owner: string): Promise<string> {
34-
const issuer = await this.ownerUtil.findIssuer(owner);
35-
if (!issuer) throw new Error(`Could not find UMA AS for resource owner ${owner}`);
36-
return issuer;
37-
}
3824
}

packages/css/src/uma/UmaClient.ts

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -152,22 +152,21 @@ export class UmaClient {
152152
* @param {string} token - the JWT access token
153153
* @return {UmaToken}
154154
*/
155-
public async verifyJwtToken(token: string, validIssuers: string[]): Promise<UmaClaims> {
156-
let config: UmaConfig;
157-
155+
public async verifyJwtToken(token: string, issuer: string): Promise<UmaClaims> {
158156
try {
159-
const issuer = decodeJwt(token).iss;
160-
if (!issuer) throw new Error('The JWT does not contain an "iss" parameter.');
161-
if (!validIssuers.includes(issuer))
162-
throw new Error(`The JWT wasn't issued by one of the target owners' issuers.`);
163-
config = await this.fetchUmaConfig(issuer);
157+
const { iss } = decodeJwt(token);
158+
159+
if (!iss) throw new Error('The JWT does not contain an "iss" parameter.');
160+
if (iss !== issuer) throw new Error(`The JWT wasn't issued by the target's authorization server.`);
161+
162+
const config = await this.fetchUmaConfig(issuer);
163+
164+
return await this.verifyTokenData(token, config.issuer, config.jwks_uri);
164165
} catch (error: unknown) {
165166
const message = `Error verifying UMA access token: ${(error as Error).message}`;
166167
this.logger.warn(message);
167168
throw new Error(message);
168169
}
169-
170-
return await this.verifyTokenData(token, config.issuer, config.jwks_uri);
171170
}
172171

173172
/**

packages/css/src/util/OwnerUtil.ts

Lines changed: 22 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
import { KeyValueStorage, PodStore, ResourceIdentifier, StorageLocationStrategy, WrappedSetMultiMap,
2-
getLoggerFor } from '@solid/community-server';
1+
import { PodStore, ResourceIdentifier, StorageLocationStrategy, getLoggerFor } from '@solid/community-server';
32
import { ACCOUNT_SETTINGS_AUTHZ_SERVER, type AccountStore } from '../identity/interaction/account/util/AccountStore';
43

54
/**
@@ -15,10 +14,9 @@ export class OwnerUtil {
1514
* @param storageStrategy
1615
*/
1716
public constructor(
18-
protected podStore: PodStore,
19-
protected accountStore: AccountStore,
20-
protected storageStrategy: StorageLocationStrategy,
21-
protected umaPatStore: KeyValueStorage<string, { issuer: string, pat: string }>,
17+
private podStore: PodStore,
18+
private accountStore: AccountStore,
19+
private storageStrategy: StorageLocationStrategy,
2220
) {}
2321

2422
/**
@@ -33,55 +31,32 @@ export class OwnerUtil {
3331
throw new Error(`Unable to find root storage for ${resource}`);
3432
}
3533
}
36-
34+
3735
/**
38-
* Finds the owners of the given resource.
36+
* Finds the pod of the given resource.
3937
*/
40-
public async findOwners(resource: ResourceIdentifier): Promise<string[]> {
41-
const storage = await this.storageStrategy.getStorageIdentifier(resource);
38+
public async findPod(resource: ResourceIdentifier): Promise<{ id: string, accountId: string }> {
39+
this.logger.debug(`Looking up pod containing ${resource.path}`);
40+
41+
const storage = await this.findStorage(resource);
4242

43-
this.logger.debug(`Looking up pod corresponding to storage ${storage.path}`);
4443
const pod = await this.podStore.findByBaseUrl(storage.path);
4544
if (!pod) throw new Error(`Unable to find pod ${storage.path}`);
4645

47-
this.logger.debug(`Looking up owners of pod ${pod.id}`);
48-
49-
const as = await this.accountStore.getSetting(pod.accountId, ACCOUNT_SETTINGS_AUTHZ_SERVER);
50-
this.logger.warn(`REAL AS is ${JSON.stringify(as)}`);
51-
52-
const owners = await this.podStore.getOwners(pod.id);
53-
if (!owners) throw new Error(`Unable to find owners for pod ${storage.path}`);
54-
55-
return owners.map((owner) => owner.webId);
46+
return pod;
5647
}
5748

58-
public async findCommonOwner(resources: Iterable<ResourceIdentifier>): Promise<string> {
59-
const resourceSet = new Set(resources);
60-
const ownerMap = new WrappedSetMultiMap<string, string>();
61-
62-
for (const target of resourceSet) {
63-
const storage = await this.findStorage(target);
64-
const owners = await this.findOwners(storage);
65-
66-
for (const owner of owners) ownerMap.add(owner, target.path);
67-
}
68-
69-
for (const [owner, targets] of ownerMap.entrySets()) {
70-
if (targets.size === resourceSet.size) return owner;
71-
}
72-
73-
throw new Error(`No common owner found for resources: ${Array.from(resources).map(r => r.path).join(', ')}`);
74-
}
75-
76-
public async findIssuer(webid: string): Promise<string | undefined> {
77-
return 'http://localhost:4000/uma';
78-
79-
// TODO: softcode
80-
81-
// const profile = await fetchDataset(webid);
82-
// const quads = await readableToQuads(profile.data);
83-
// const issuers = quads.getObjects(namedNode(webid), UMA.terms.as, null);
49+
/**
50+
* Finds the authorization server of the given resource.
51+
*/
52+
public async findAuthorizationServer(resource: ResourceIdentifier): Promise<string> {
53+
this.logger.debug(`Looking up authorization server of ${resource.path}`);
8454

85-
// return issuers.length > 0 ? issuers[0].value : undefined;
55+
const pod = await this.findPod(resource);
56+
57+
const as = await this.accountStore.getSetting(pod.accountId, ACCOUNT_SETTINGS_AUTHZ_SERVER);
58+
if (!as) throw new Error(`Unable to find authorization server for pod ${pod.id} (account ${pod.accountId})`);
59+
60+
return as;
8661
}
8762
}

0 commit comments

Comments
 (0)