Skip to content

Commit 4356220

Browse files
committed
refactor: Clean up code
1 parent de02e14 commit 4356220

22 files changed

+130
-151
lines changed

packages/uma/config/routes/introspection.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@
99
"methods": [ "POST" ],
1010
"handler": {
1111
"@type": "IntrospectionHandler",
12-
"tokenStore": { "@id": "urn:uma:default:TokenStore" },
13-
"jwtTokenFactory": { "@id": "urn:uma:default:TokenFactory" }
12+
"tokenStore": { "@id": "urn:uma:default:TokenStore" }
1413
},
1514
"path": "/uma/introspect"
1615
}

packages/uma/src/credentials/verify/JwtVerifier.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,14 @@ export class JwtVerifier implements Verifier {
5353
await jwtVerify(credential.token, Object.assign(jwk, { type: 'JWK' }));
5454
}
5555

56-
for (const claim of Object.keys(claims)) if (!this.allowedClaims.includes(claim)) {
57-
if (this.errorOnExtraClaims) throw new Error(`Claim '${claim}' not allowed.`);
56+
for (const claim of Object.keys(claims)) {
57+
if (!this.allowedClaims.includes(claim)) {
58+
if (this.errorOnExtraClaims) {
59+
throw new Error(`Claim '${claim}' not allowed.`);
60+
}
5861

59-
delete claims[claim];
62+
delete claims[claim];
63+
}
6064
}
6165

6266
this.logger.debug(`Returning discovered claims: ${JSON.stringify(claims)}`)

packages/uma/src/credentials/verify/SolidOidcVerifier.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { getLoggerFor } from '@solid/community-server';
1+
import { BadRequestHttpError, getLoggerFor } from '@solid/community-server';
22
import { Verifier } from './Verifier';
33
import { ClaimSet } from '../ClaimSet';
44
import { Credential } from "../Credential";
@@ -18,7 +18,7 @@ export class SolidOidcVerifier implements Verifier {
1818
public async verify(credential: Credential): Promise<ClaimSet> {
1919
this.logger.debug(`Verifying credential ${JSON.stringify(credential)}`);
2020
if (credential.format !== OIDC) {
21-
throw new Error(`Token format ${credential.format} does not match this processor's format.`);
21+
throw new BadRequestHttpError(`Token format ${credential.format} does not match this processor's format.`);
2222
}
2323

2424
try {
@@ -35,7 +35,7 @@ export class SolidOidcVerifier implements Verifier {
3535
const message = `Error verifying OIDC ID Token: ${(error as Error).message}`;
3636

3737
this.logger.debug(message);
38-
throw new Error(message);
38+
throw new BadRequestHttpError(message);
3939
}
4040
}
4141
}

packages/uma/src/dialog/BaseNegotiator.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,13 @@ import { serializePolicyInstantiation } from '../logging/OperationSerializer';
2323
*/
2424
export class BaseNegotiator implements Negotiator {
2525
protected readonly logger = getLoggerFor(this);
26-
operationLogger = getOperationLogger();
26+
protected readonly operationLogger = getOperationLogger();
2727

2828
/**
2929
* Construct a new Negotiator
3030
* @param verifier - The Verifier used to verify Claims of incoming Credentials.
3131
* @param ticketStore - A KeyValueStorage to track Tickets.
32-
* @param ticketManager - The strategy describing the life cycle of a Ticket.
32+
* @param ticketingStrategy - The strategy describing the life cycle of a Ticket.
3333
* @param tokenFactory - A factory for minting Access Tokens.
3434
*/
3535
public constructor(
@@ -41,10 +41,6 @@ export class BaseNegotiator implements Negotiator {
4141

4242
/**
4343
* Performs UMA grant negotiation.
44-
*
45-
* @param {TokenRequest} body - request body
46-
* @param {HttpHandlerContext} context - request context
47-
* @return {Promise<TokenResponse>} tokens - yielded tokens
4844
*/
4945
public async negotiate(input: DialogInput): Promise<DialogOutput> {
5046
reType(input, DialogInput);

packages/uma/src/dialog/ContractNegotiator.ts

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,13 @@ import { DialogOutput } from './Output';
2121
export class ContractNegotiator extends BaseNegotiator {
2222
protected readonly logger = getLoggerFor(this);
2323

24-
// protected readonly operationLogger = getOperationLogger();
2524
protected readonly contractManager = new ContractManager();
2625

2726
/**
2827
* Construct a new Negotiator
2928
* @param verifier - The Verifier used to verify Claims of incoming Credentials.
3029
* @param ticketStore - A KeyValueStore to track Tickets.
31-
* @param ticketManager - The strategy describing the life cycle of a Ticket.
30+
* @param ticketingStrategy - The strategy describing the life cycle of a Ticket.
3231
* @param tokenFactory - A factory for minting Access Tokens.
3332
*/
3433
public constructor(
@@ -43,15 +42,15 @@ export class ContractNegotiator extends BaseNegotiator {
4342

4443
/**
4544
* Performs UMA grant negotiation.
46-
*
47-
* @param {TokenRequest} body - request body
48-
* @param {HttpHandlerContext} context - request context
49-
* @return {Promise<TokenResponse>} tokens - yielded tokens
5045
*/
5146
public async negotiate(input: DialogInput): Promise<DialogOutput> {
5247
reType(input, DialogInput);
53-
if (!input.permissions && input.permission?.length)
54-
input.permissions = input.permission.map(p => processRequestPermission(p))
48+
if (!input.permissions && input.permission?.length) {
49+
input = {
50+
...input,
51+
permissions: input.permission.map(p => processRequestPermission(p)),
52+
};
53+
}
5554
this.logger.debug(`Input. ${JSON.stringify(input)}`);
5655
// Create or retrieve ticket
5756
const ticket = await this.getTicket(input);
@@ -170,7 +169,7 @@ export class ContractNegotiator extends BaseNegotiator {
170169
const policyCreationResponse = await fetch(instantiatedPolicyContainer, {
171170
method: 'POST',
172171
headers: { 'content-type': 'application/ld+json' },
173-
body: JSON.stringify(contract, null, 2)
172+
body: JSON.stringify(contract),
174173
});
175174

176175
if (policyCreationResponse.status !== 201) {

packages/uma/src/policies/authorizers/NamespacedAuthorizer.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@ import { getLoggerFor, KeyValueStorage } from '@solid/community-server';
22
import { ResourceDescription } from '../../views/ResourceDescription';
33
import { Authorizer } from './Authorizer';
44
import { Permission } from '../../views/Permission';
5-
import { Requirements, type ClaimVerifier } from '../../credentials/Requirements';
5+
import { Requirements } from '../../credentials/Requirements';
66
import { ClaimSet } from '../../credentials/ClaimSet';
77

8-
const NO_RESOURCE = Symbol();
98
const namespace = (resource: string) => new URL(resource).pathname.split('/')?.[2] ?? '';
109

1110
/**
@@ -39,8 +38,8 @@ export class NamespacedAuthorizer implements Authorizer {
3938
const ns = query[0].resource_id ? await this.findNamespace(query[0].resource_id) : undefined;
4039

4140
// Check namespaces of other resources
42-
for (const permission of query) {
43-
if ((permission.resource_id ? namespace(permission.resource_id) : undefined) !== ns) {
41+
for (let i = 1; i < query.length; ++i) {
42+
if ((query[i].resource_id ? await this.findNamespace(query[i].resource_id) : undefined) !== ns) {
4443
this.logger.warn(`Cannot calculate permissions over multiple namespaces at once.`);
4544
return [];
4645
}
@@ -64,8 +63,8 @@ export class NamespacedAuthorizer implements Authorizer {
6463
const ns = await this.findNamespace(permissions[0].resource_id);
6564

6665
// Check namespaces of other resources
67-
for (const permission of permissions) {
68-
if (namespace(permission.resource_id) !== ns) {
66+
for (let i = 1; i < permissions.length; ++i) {
67+
if (await this.findNamespace(permissions[i].resource_id) !== ns) {
6968
this.logger.warn(`Cannot calculate credentials over multiple namespaces at once.`);
7069
return [];
7170
}

packages/uma/src/policies/authorizers/OdrlAuthorizer.ts

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
1-
import { createVocabulary, DC, getLoggerFor, RDF } from '@solid/community-server';
1+
import {
2+
BadRequestHttpError,
3+
createVocabulary,
4+
DC,
5+
getLoggerFor,
6+
NotImplementedHttpError,
7+
RDF
8+
} from '@solid/community-server';
29
import { basicPolicy, ODRL, UCPPolicy, UCRulesStorage } from '@solidlab/ucp';
3-
import { DataFactory, Literal, NamedNode, Quad_Subject, Store, Writer } from 'n3';
10+
import { DataFactory, Literal, NamedNode, Quad_Subject, Store } from 'n3';
411
import { EyeReasoner, ODRLEngineMultipleSteps, ODRLEvaluator } from 'odrl-evaluator'
512
import { WEBID } from '../../credentials/Claims';
613
import { ClaimSet } from '../../credentials/ClaimSet';
@@ -62,19 +69,17 @@ export class OdrlAuthorizer implements Authorizer {
6269

6370
// prepare sotw
6471
const sotw = new Store();
65-
sotw.add(quad(namedNode('http://example.com/request/currentTime'), namedNode('http://purl.org/dc/terms/issued'), literal(new Date().toISOString(), namedNode("http://www.w3.org/2001/XMLSchema#dateTime"))));
72+
sotw.add(quad(
73+
namedNode('http://example.com/request/currentTime'),
74+
namedNode('http://purl.org/dc/terms/issued'),
75+
literal(new Date().toISOString(), namedNode("http://www.w3.org/2001/XMLSchema#dateTime"))),
76+
);
6677

6778
const subject = typeof claims[WEBID] === 'string' ? claims[WEBID] : 'urn:solidlab:uma:id:anonymous';
6879

69-
7080
for (const {resource_id, resource_scopes} of query) {
71-
if (!resource_id) {
72-
this.logger.warn('The OdrlAuthorizer can only calculate permissions for explicit resources.');
73-
continue;
74-
}
75-
7681
grantedPermissions[resource_id] = [];
77-
const actions = resource_scopes ? transformActionsCssToOdrl(resource_scopes) : ["http://www.w3.org/ns/odrl/2/use"]
82+
const actions = transformActionsCssToOdrl(resource_scopes);
7883
for (const action of actions) {
7984
this.logger.info(`Evaluating Request [S R AR]: [${subject} ${resource_id} ${action}]`);
8085
const requestPolicy: UCPPolicy = {
@@ -119,7 +124,7 @@ export class OdrlAuthorizer implements Authorizer {
119124
}
120125

121126
public async credentials(permissions: Permission[], query?: Requirements | undefined): Promise<Requirements[]> {
122-
throw new Error("Method not implemented.");
127+
throw new NotImplementedHttpError('Method not implemented.');
123128
}
124129

125130
}
@@ -141,7 +146,13 @@ function transformActionsCssToOdrl(actions: string[]): string[] {
141146

142147
// in UMAPermissionReader, only the last part of the URN will be used, divided by a colon
143148
// again, see CSS package
144-
return actions.map(action => scopeCssToOdrl.get(action)!);
149+
return actions.map(action => {
150+
const result = scopeCssToOdrl.get(action);
151+
if (!result) {
152+
throw new BadRequestHttpError(`Unsupported action ${action}`);
153+
}
154+
return result;
155+
});
145156
}
146157
/**
147158
* Transform ODRL Actions to equivalent Actions enforced by the Community Solid Server

packages/uma/src/policies/authorizers/WebIdAuthorizer.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@ export class WebIdAuthorizer implements Authorizer {
1414
/**
1515
* Creates a PublicNamespaceAuthorizer with the given public namespaces.
1616
*
17-
* @param namespaces - A list of namespaces that should be publicly accessible.
18-
* @param authorizer - The Authorizer to use for other resources.
17+
* @param webids - The WebIDs that can be used.
1918
*/
2019
constructor(
2120
protected webids: string[],
@@ -44,7 +43,7 @@ export class WebIdAuthorizer implements Authorizer {
4443
if (query && !Object.keys(query).includes(WEBID)) return [];
4544

4645
return [{
47-
[WEBID]: async (webid) => typeof webid === 'string' && this.webids.includes(webid),
46+
[WEBID]: async (webid) => typeof webid === 'string' && this.webids.includes(webid),
4847
}];
4948
}
5049
}

packages/uma/src/routes/Config.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { ASYMMETRIC_CRYPTOGRAPHIC_ALGORITHM }
22
from '@solid/access-token-verifier/dist/constant/ASYMMETRIC_CRYPTOGRAPHIC_ALGORITHM';
3-
import { getLoggerFor } from '@solid/community-server';
3+
import { getLoggerFor, joinUrl } from '@solid/community-server';
44
import { HttpHandler, HttpHandlerContext, HttpHandlerResponse } from '../util/http/models/HttpHandler';
55

66
// eslint-disable no-unused-vars
@@ -59,13 +59,13 @@ export class ConfigRequestHandler extends HttpHandler {
5959
*/
6060
public getConfig(): UmaConfiguration {
6161
return {
62-
jwks_uri: `${this.baseUrl}/keys`,
63-
token_endpoint: `${this.baseUrl}/token`,
62+
jwks_uri: joinUrl(this.baseUrl, 'keys'),
63+
token_endpoint: joinUrl(this.baseUrl, 'token'),
6464
grant_types_supported: ['urn:ietf:params:oauth:grant-type:uma-ticket'],
65-
issuer: `${this.baseUrl}`,
66-
permission_endpoint: `${this.baseUrl}/ticket`,
67-
introspection_endpoint: `${this.baseUrl}/introspect`,
68-
resource_registration_endpoint: `${this.baseUrl}/resources/`,
65+
issuer: this.baseUrl,
66+
permission_endpoint: joinUrl(this.baseUrl, 'ticket'),
67+
introspection_endpoint: joinUrl(this.baseUrl, 'introspect'),
68+
resource_registration_endpoint: joinUrl(this.baseUrl, 'resources/'),
6969
uma_profiles_supported: ['http://openid.net/specs/openid-connect-core-1_0.html#IDToken'],
7070
dpop_signing_alg_values_supported: [...ASYMMETRIC_CRYPTOGRAPHIC_ALGORITHM],
7171
response_types_supported: [ResponseType.Token],

packages/uma/src/routes/Introspection.ts

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
import { BadRequestHttpError, getLoggerFor, KeyValueStorage, UnauthorizedHttpError } from '@solid/community-server';
22
import { AccessToken } from '../tokens/AccessToken';
3-
import { JwtTokenFactory } from '../tokens/JwtTokenFactory';
4-
import { SerializedToken } from '../tokens/TokenFactory';
53
import { HttpHandler, HttpHandlerContext, HttpHandlerResponse } from '../util/http/models/HttpHandler';
64
import { verifyRequest } from '../util/HttpMessageSignatures';
7-
import { jwtDecrypt } from 'jose';
85

96

107
type IntrospectionResponse = {
@@ -29,24 +26,22 @@ export class IntrospectionHandler extends HttpHandler {
2926
* Creates an introspection handler for tokens in the given token store.
3027
*
3128
* @param tokenStore - The store containing the tokens.
32-
* @param jwtTokenFactory - The factory with which to produce JWT representations of the tokens.
3329
*/
3430
constructor(
3531
private readonly tokenStore: KeyValueStorage<string, AccessToken>,
36-
private readonly jwtTokenFactory: JwtTokenFactory,
3732
) {
3833
super();
3934
}
4035

41-
async handle({request}: HttpHandlerContext): Promise<HttpHandlerResponse<any>> {
36+
public async handle({request}: HttpHandlerContext): Promise<HttpHandlerResponse<any>> {
4237
if (!await verifyRequest(request)) throw new UnauthorizedHttpError();
4338

44-
if (!request.body /*|| !(request.body instanceof Object) */) { // todo: why was the object check here??
39+
if (!request.body) {
4540
throw new BadRequestHttpError('Missing request body.');
4641
}
4742

48-
const token = new URLSearchParams(request.body as Record<string, string>).get('token');
4943
try {
44+
const token = new URLSearchParams(request.body as Record<string, string>).get('token');
5045
if(!token) throw new Error('could not extract token from request body')
5146
const unsignedToken = await this.processJWTToken(token)
5247
return {
@@ -55,17 +50,17 @@ export class IntrospectionHandler extends HttpHandler {
5550
};
5651
} catch (e) {
5752
// Todo: The JwtTokenFactory DOES NOT STORE THE TOKEN IN THE TOKENSTORE IN A WAY WE CAN RETRIEVE HERE! How to fix?
58-
this.logger.warn(`Token introspection failed: ${e}`)
53+
this.logger.warn(`Token introspection failed: ${e}`);
5954
throw new BadRequestHttpError('Invalid request body.');
6055
}
6156
}
6257

63-
64-
private async processJWTToken(signedJWT: string): Promise<IntrospectionResponse> {
65-
this.logger.info(JSON.stringify(this.tokenStore.entries().next(), null, 2))
66-
const token = (await this.tokenStore.get(signedJWT)) as IntrospectionResponse;
58+
protected async processJWTToken(signedJWT: string): Promise<IntrospectionResponse> {
59+
const token = await this.tokenStore.get(signedJWT);
6760
if (!token) throw new Error('Token not found.');
68-
token.active = true
69-
return token
61+
return {
62+
active: true,
63+
...token,
64+
};
7065
}
7166
}

0 commit comments

Comments
 (0)