diff --git a/packages/uma/bin/main.js b/packages/uma/bin/main.js index 29ac995..aa1b13f 100644 --- a/packages/uma/bin/main.js +++ b/packages/uma/bin/main.js @@ -1,12 +1,27 @@ const path = require('path'); +const yargs = require('yargs/yargs'); +const { hideBin } = require('yargs/helpers'); const { ComponentsManager } = require('componentsjs'); const { setGlobalLoggerFactory, WinstonLoggerFactory } = require('global-logger-factory'); -const protocol = 'http'; -const host = 'localhost'; -const port = 4000; - -const baseUrl = `${protocol}://${host}:${port}/uma`; +const argv = yargs(hideBin(process.argv)) + .option('port', { + alias: 'p', + type: 'number', + description: 'Port number for the UMA server', + default: 4000 + }) + .option('baseUrl', { + alias: 'b', + type: 'string', + description: 'Base URL for the UMA server' + }) + .help() + .alias('help', 'h') + .argv; + +const port = argv.port; +const baseUrl = argv.baseUrl || `http://localhost:${port}/uma`; const rootDir = path.join(__dirname, '../'); const launch = async () => { diff --git a/packages/uma/package.json b/packages/uma/package.json index cb606f2..3ff2581 100644 --- a/packages/uma/package.json +++ b/packages/uma/package.json @@ -77,7 +77,8 @@ "odrl-evaluator": "^0.5.0", "rdf-vocabulary": "^1.0.1", "uri-template-lite": "^23.4.0", - "winston": "^3.11.0" + "winston": "^3.11.0", + "yargs": "^18.0.0" }, "lsd:module": "https://linkedsoftwaredependencies.org/bundles/npm/@solidlab/uma", "lsd:components": "dist/components/components.jsonld", diff --git a/packages/uma/src/routes/ClientRegistration.ts b/packages/uma/src/routes/ClientRegistration.ts index d073e8a..31fad61 100644 --- a/packages/uma/src/routes/ClientRegistration.ts +++ b/packages/uma/src/routes/ClientRegistration.ts @@ -63,6 +63,7 @@ export class ClientRegistrationRequestHandler extends HttpHandler { protected async initializeStorage(): Promise { await this.storage.defineType(CLIENT_REGISTRATION_STORAGE_TYPE, CLIENT_REGISTRATION_STORAGE_DESCRIPTION); await this.storage.createIndex(CLIENT_REGISTRATION_STORAGE_TYPE, 'userId'); + await this.storage.createIndex(CLIENT_REGISTRATION_STORAGE_TYPE, 'clientUri'); await this.storage.createIndex(CLIENT_REGISTRATION_STORAGE_TYPE, 'clientId'); } diff --git a/packages/uma/test/unit/routes/ClientRegistration.test.ts b/packages/uma/test/unit/routes/ClientRegistration.test.ts index bb9dd54..745dbdf 100644 --- a/packages/uma/test/unit/routes/ClientRegistration.test.ts +++ b/packages/uma/test/unit/routes/ClientRegistration.test.ts @@ -2,8 +2,11 @@ import { BadRequestHttpError, ConflictHttpError, IndexedStorage, InternalServerError, - joinUrl, NotFoundHttpError, - UnauthorizedHttpError + joinUrl, + MemoryMapStorage, + NotFoundHttpError, + UnauthorizedHttpError, + WrappedIndexedStorage } from '@solid/community-server'; import { Mocked } from 'vitest'; import { WEBID } from '../../../src/credentials/Claims'; @@ -115,6 +118,41 @@ describe('ClientRegistration', (): void => { }); }); + it('allows multiple registrations for the same owner when client_uri differs.', async(): Promise => { + const realStorage = new WrappedIndexedStorage(new MemoryMapStorage(), new MemoryMapStorage()); + const realHandler = new ClientRegistrationRequestHandler(credentialParser, verifier, realStorage as any); + await new Promise((resolve) => setImmediate(resolve)); + + vi.spyOn(crypto, 'randomUUID') + .mockReturnValueOnce('0000-1111-2222-3333-4444') + .mockReturnValueOnce('4444-3333-2222-1111-0000'); + vi.spyOn(crypto, 'randomBytes') + .mockReturnValueOnce(Buffer.from('abc') as any) + .mockReturnValueOnce(Buffer.from('def') as any); + + const firstRequest = { + method: 'POST', + url: new URL('http://example1.com/'), + body: { + client_name: 'rs-1', + client_uri: 'http://example1.com/', + }, + } satisfies Partial as HttpHandlerRequest; + + await expect(realHandler.handle({ request: firstRequest })).resolves.toMatchObject({ status: 201 }); + + const secondRequest = { + method: 'POST', + url: new URL('http://example2.com/'), + body: { + client_name: 'rs-2', + client_uri: 'http://example2.com/', + }, + } satisfies Partial as HttpHandlerRequest; + + await expect(realHandler.handle({ request: secondRequest })).resolves.toMatchObject({ status: 201 }); + }); + it('requires valid input when registering.', async(): Promise => { request.method = 'POST'; request.body = {}; diff --git a/yarn.lock b/yarn.lock index a7d82c3..3e056eb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5670,6 +5670,7 @@ __metadata: rdf-vocabulary: "npm:^1.0.1" uri-template-lite: "npm:^23.4.0" winston: "npm:^3.11.0" + yargs: "npm:^18.0.0" languageName: unknown linkType: soft @@ -6833,6 +6834,13 @@ __metadata: languageName: node linkType: hard +"ansi-styles@npm:^6.2.1": + version: 6.2.3 + resolution: "ansi-styles@npm:6.2.3" + checksum: 10c0/23b8a4ce14e18fb854693b95351e286b771d23d8844057ed2e7d083cd3e708376c3323707ec6a24365f7d7eda3ca00327fe04092e29e551499ec4c8b7bfac868 + languageName: node + linkType: hard + "arg@npm:^4.1.0": version: 4.1.3 resolution: "arg@npm:4.1.3" @@ -7241,6 +7249,17 @@ __metadata: languageName: node linkType: hard +"cliui@npm:^9.0.1": + version: 9.0.1 + resolution: "cliui@npm:9.0.1" + dependencies: + string-width: "npm:^7.2.0" + strip-ansi: "npm:^7.1.0" + wrap-ansi: "npm:^9.0.0" + checksum: 10c0/13441832e9efe7c7a76bd2b8e683555c478d461a9f249dc5db9b17fe8d4b47fa9277b503914b90bd00e4a151abb6b9b02b2288972ffe2e5e3ca40bcb1c2330d3 + languageName: node + linkType: hard + "clownface@npm:^1.4.0": version: 1.5.1 resolution: "clownface@npm:1.5.1" @@ -12851,7 +12870,7 @@ __metadata: languageName: node linkType: hard -"string-width@npm:^7.2.0": +"string-width@npm:^7.0.0, string-width@npm:^7.2.0": version: 7.2.0 resolution: "string-width@npm:7.2.0" dependencies: @@ -13919,6 +13938,17 @@ __metadata: languageName: node linkType: hard +"wrap-ansi@npm:^9.0.0": + version: 9.0.2 + resolution: "wrap-ansi@npm:9.0.2" + dependencies: + ansi-styles: "npm:^6.2.1" + string-width: "npm:^7.0.0" + strip-ansi: "npm:^7.1.0" + checksum: 10c0/3305839b9a0d6fb930cb63a52f34d3936013d8b0682ff3ec133c9826512620f213800ffa19ea22904876d5b7e9a3c1f40682f03597d986a4ca881fa7b033688c + languageName: node + linkType: hard + "wrappy@npm:1": version: 1.0.2 resolution: "wrappy@npm:1.0.2" @@ -14021,6 +14051,13 @@ __metadata: languageName: node linkType: hard +"yargs-parser@npm:^22.0.0": + version: 22.0.0 + resolution: "yargs-parser@npm:22.0.0" + checksum: 10c0/cb7ef81759c4271cb1d96b9351dbbc9a9ce35d3e1122d2b739bf6c432603824fa02c67cc12dcef6ea80283379d63495686e8f41cc7b06c6576e792aba4d33e1c + languageName: node + linkType: hard + "yargs@npm:^16.2.0": version: 16.2.0 resolution: "yargs@npm:16.2.0" @@ -14051,6 +14088,20 @@ __metadata: languageName: node linkType: hard +"yargs@npm:^18.0.0": + version: 18.0.0 + resolution: "yargs@npm:18.0.0" + dependencies: + cliui: "npm:^9.0.1" + escalade: "npm:^3.1.1" + get-caller-file: "npm:^2.0.5" + string-width: "npm:^7.2.0" + y18n: "npm:^5.0.5" + yargs-parser: "npm:^22.0.0" + checksum: 10c0/bf290e4723876ea9c638c786a5c42ac28e03c9ca2325e1424bf43b94e5876456292d3ed905b853ebbba6daf43ed29e772ac2a6b3c5fb1b16533245d6211778f3 + languageName: node + linkType: hard + "yn@npm:3.1.1": version: 3.1.1 resolution: "yn@npm:3.1.1"