diff --git a/.syncpackrc b/.syncpackrc new file mode 100644 index 00000000..e1fa2c98 --- /dev/null +++ b/.syncpackrc @@ -0,0 +1,10 @@ +{ + "versionGroups": [ + { + "label": "Use workspace protocol when developing local packages", + "dependencies": ["@solidlab/uma-css", "@solidlab/ucp", "@solidlab/uma"], + "dependencyTypes": ["prod", "dev"], + "pinVersion": "workspace:^" + } + ] +} diff --git a/README.md b/README.md index 8ee0df4f..e4d87ba8 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,69 @@ -# Pacsoi POC +# SolidLab's User Managed Access -This repository contains a demonstrator for the [SolidLab project](https://solidlab.be/) on managing trust-flows in decentralized data storage systems such as Solid. +This repository contains SolidLab research artefacts on use of UMA in the Solid ecosystem. -## Cloning the repository +## Packages -To run the demonstrator, you will have to clone the repository. -``` -git clone -b project/pacsoi-poc1 git@github.com:SolidLabResearch/user-managed-access.git +- [`@solidlab/uma`](packages/uma): Experimental and opinionated implementation of [UMA Grants](https://docs.kantarainitiative.org/uma/wg/rec-oauth-uma-grant-2.0.html) and [UMA Federation](https://docs.kantarainitiative.org/uma/wg/rec-oauth-uma-federated-authz-2.0.html). -cd user-managed-access/ -``` +- [`@solidlab/uma-css`](packages/css): UMA modules for the [Community Solid Server](https://github.com/CommunitySolidServer/CommunitySolidServer/). -## Getting started - -### Setting up the Authorization Server +- [`@solidlab/ucp`](packages/ucp): Usage Control Policy decision/enforcement component. -Before starting, make sure you are on the correct branch (pacsoi-poc1). -See the above command to clone only the relevant branch for the demonstrator. +## Getting started -In order to run the demonstrator you need to perform the following steps. +In order to run this project you need to perform the following steps. 1. Ensure that you are using Node.js 20 or higher, e.g. by running `nvm use`. (see [.nvmrc](./.nvmrc)) 2. Enable Node.js Corepack with `corepack enable`. -3. Run `yarn install` in the project root to install the requirements. -4. Run `yarn build` in the project root to build. -5. Run `yarn run start:demo` in the project root to start all services. +3. Run `yarn install` in the project root (this will automatically call `yarn build`). +4. Run `yarn start`. + +This will boot up a UMA server and compatible Community Solid Server instance. + +You can then execute the following flows: + +- `yarn script:public`: `GET` the public `/alice/profile/card` without redirection to the UMA server; +- `yarn script:private`: `PUT` some text to the private `/alice/private/resource.txt`, protected by a simple WebID check; +- `yarn script:uma-ucp`: `PUT` some text to the private `/alice/other/resource.txt`, protected by a UCP enforcer checking WebIDs according to policies in `packages/uma/config/rules/policy/`. +- `yarn script:registration`: `POST`, `GET` and `DELETE` some text to/from `/alice/public/resource.txt` to test the correct creation and deletion of resource registrations on the UNA server. +- `yarn script:ucp-enforcement`: Run the UCP enforcer in a script (`scripts/test-ucp-enforcement.ts`). This does not need the servers to be started. + +`yarn script:flow` runs all flows in sequence. + +As we are still in the progress of documenting everything, +the above scripts are the best way to learn about how everything works. + +## Demonstration + +Instead of running `yarn start`, you can run `yarn start:demo` to start the server with an alternative configuration. +With this configuration you can run the `script:demo`, +which runs with experimental contracts. + +## Implemented features + +The packages in this project currently only support a fixed UMA AS per CSS RS. +Authorization can be done with a simple, unverified, WebID embedded in the ticket +using the [WebIdAuthorizer](packages/uma/src/policies/authorizers/WebIdAuthorizer.ts) +or the [PolicyBasedAuthorizer](packages/uma/src/policies/authorizers/PolicyBasedAuthorizer.ts) +which supports simple ODRL policies. + +### Usage control policy enforcement + +Used for creating a modular engine that calculates which access modes are granted based on: + +- Usage Control Rules +- Interpretation of those rules +- The request of the Requested Party together with all its claims + +For more information, you can check out its [own repository](https://github.com/woutslabbinck/ucp-enforcement) which has three engines that use [ODRL rules](https://www.w3.org/TR/odrl-model/). + +A test script is provided for a CRUD ODRL engine: `yarn script:ucp-enforcement`. +In the [script](./scripts/test-ucp-enforcement.ts) a read Usage Control Rule (in ODRL) is present together with N3 interpretation rules. +Then a read request is performed using the engine, which results in a list of grants. This list is then printed to the console. -### Docker +## Next steps -The docker is not working atm. -I am trying to get it working, but there seem to be some problems with the internal networking. +More advanced ODRL evaluation can be found in the `feat/ODRL-evaluator` branch. diff --git a/package.json b/package.json index 375fd27d..583749ff 100644 --- a/package.json +++ b/package.json @@ -44,14 +44,14 @@ "private": true, "packageManager": "yarn@4.1.0", "engines": { - "node": ">=18.18", + "node": ">=20.0", "yarn": ">=4.0" }, "type": "commonjs", "exports": {}, "files": [], "scripts": { - "prepare": "yarn build", + "postinstall": "yarn run sync:list && yarn build", "clean": "shx rm -rf ./**/node_modules", "build": "yarn workspaces foreach --include 'packages/*' -A -pi -j unlimited -t run build", "test": "yarn workspaces foreach --include 'packages/*' -A -pi -j unlimited run test", @@ -64,29 +64,32 @@ "script:registration": "yarn exec ts-node ./scripts/test-registration.ts", "script:ucp-enforcement": "yarn exec ts-node ./scripts/test-ucp-enforcement.ts", "script:uma-ucp": "yarn exec ts-node ./scripts/test-uma-ucp.ts", - "script:flow": "yarn run script:public && yarn run script:private && yarn run script:uma-ucp && yarn run script:registration && yarn run script:ucp-enforcement" + "script:flow": "yarn run script:public && yarn run script:private && yarn run script:uma-ucp && yarn run script:registration && yarn run script:ucp-enforcement", + "sync:list": "syncpack list-mismatches", + "sync:fix": "syncpack fix-mismatches" }, "devDependencies": { "@commitlint/cli": "^16.1.0", "@commitlint/config-conventional": "^16.0.0", "@solidlab/ucp": "workspace:^", - "@types/jest": "^29.5.6", - "@types/node": "^20.9.4", + "@types/jest": "^29.5.12", + "@types/node": "^20.11.25", "@typescript-eslint/eslint-plugin": "^5.12.1", "@typescript-eslint/parser": "^5.12.1", "componentsjs-generator": "^3.1.2", "concurrently": "^8.2.2", - "cross-fetch": "^4.0.0", "eslint": "^8.10.0", "jest": "^29.7.0", + "jest-rdf": "^1.8.1", "koreografeye": "^0.4.8", "shx": "^0.3.4", - "ts-jest": "^29.1.1", + "syncpack": "^13.0.2", + "ts-jest": "^29.1.2", "ts-node": "^10.9.2", - "typescript": "^5.2.2" + "typescript": "^5.3.3" }, "resolutions": { - "@types/node": "^18.18.11" + "@types/node": "^20.11.25" }, "workspaces": [ "packages/*" @@ -163,7 +166,6 @@ "@inrupt/solid-client-authn-core": "^2.1.0", "chalk": "^5.4.1", "jsonld": "^8.3.3", - "odrl-evaluator": "^0.1.1", "tsx": "^4.19.2" } } diff --git a/packages/css/config/app/init/base/init.json b/packages/css/config/app/init/base/init.json deleted file mode 100644 index 9f9b1285..00000000 --- a/packages/css/config/app/init/base/init.json +++ /dev/null @@ -1,102 +0,0 @@ -{ - "@context": [ - "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^7.0.0/components/context.jsonld", - "https://linkedsoftwaredependencies.org/bundles/npm/@solidlab/uma-css/^0.0.0/components/context.jsonld" - ], - "import": [ - "css:config/app/init/initializers/base-url.json", - "css:config/app/init/initializers/logger.json", - "css:config/app/init/initializers/server.json", - "css:config/app/init/initializers/version.json", - "css:config/app/init/initializers/workers.json", - "css:config/app/init/migration/base.json", - - "uma-css:config/app/init/initializers/seeding.json" - ], - "@graph": [ - { - "comment": "These initializers will be all be executed sequentially when starting the server.", - "@id": "urn:solid-server:default:Initializer", - "@type": "SequenceHandler", - "handlers": [ - { - "@id": "urn:solid-server:default:LoggerInitializer" - }, - { - "@id": "urn:solid-server:default:EarlyProcessParallelInitializer" - }, - { - "@id": "urn:solid-server:default:PrimaryInitializer" - }, - { - "@id": "urn:solid-server:default:WorkerInitializer" - } - ] - }, - { - "comment": "This wrapped sequence handler will be executed ONLY BY THE PRIMARY PROCESS when starting the server.", - "@id": "urn:solid-server:default:PrimaryInitializer", - "@type": "ProcessHandler", - "executeOnPrimary": true, - "clusterManager": { - "@id": "urn:solid-server:default:ClusterManager" - }, - "source": { - "comment": "These initializers will all be executed sequentially when starting the server.", - "@id": "urn:solid-server:default:PrimarySequenceInitializer", - "@type": "SequenceHandler", - "handlers": [ - { - "@id": "urn:solid-server:default:CleanupInitializer" - }, - { - "@id": "urn:solid-server:default:MigrationInitializer" - }, - { - "@id": "urn:solid-server:default:BaseUrlVerifier" - }, - { - "@id": "urn:solid-server:default:PrimaryParallelInitializer" - }, - { - "@id": "urn:solid-server:default:SeededAccountInitializer" - }, - { - "@id": "urn:solid-server:default:ModuleVersionVerifier" - }, - { - "@id": "urn:solid-server:default:WorkerManager" - } - ] - } - }, - { - "comment": "This wrapped sequence handler will be executed ONLY BY THE WORKER PROCESSES when starting the server.", - "@id": "urn:solid-server:default:WorkerInitializer", - "@type": "ProcessHandler", - "executeOnPrimary": false, - "clusterManager": { - "@id": "urn:solid-server:default:ClusterManager" - }, - "source": { - "comment": "These initializers will all be executed sequentially when starting the server.", - "@id": "urn:solid-server:default:WorkerSequenceInitializer", - "@type": "SequenceHandler", - "handlers": [ - { - "@id": "urn:solid-server:default:WorkerParallelInitializer" - }, - { - "@id": "urn:solid-server:default:ServerInitializer" - } - ] - } - }, - { - "comment": "Initializers that need to cleanup or do anything else before something writes to the backend should be added here.", - "@id": "urn:solid-server:default:CleanupInitializer", - "@type": "SequenceHandler", - "handlers": [] - } - ] -} diff --git a/packages/css/config/app/init/default.json b/packages/css/config/app/init/default.json deleted file mode 100644 index 0a1482d1..00000000 --- a/packages/css/config/app/init/default.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "@context": [ - "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^7.0.0/components/context.jsonld", - "https://linkedsoftwaredependencies.org/bundles/npm/@solidlab/uma-css/^0.0.0/components/context.jsonld" - ], - "import": [ - "uma-css:config/app/init/base/init.json" - ], - "@graph": [ - { - "comment": "These handlers are called for all processes whenever the server is started, and can be used to ensure that all necessary resources for booting are available.", - "@id": "urn:solid-server:default:EarlyProcessParallelInitializer", - "@type": "ParallelHandler", - "handlers": [] - }, - { - "comment": "These handlers are called only for the Primary process whenever the server is started, and can be used to ensure that all necessary resources for booting are available. (in singlethreaded mode, these are always called)", - "@id": "urn:solid-server:default:PrimaryParallelInitializer", - "@type": "ParallelHandler", - "handlers": [] - }, - { - "comment": "These handlers are called only for the workers processes whenever the server is started, and can be used to ensure that all necessary resources for booting are available. (in singlethreaded mode, these are always called)", - "@id": "urn:solid-server:default:WorkerParallelInitializer", - "@type": "ParallelHandler", - "handlers": [] - } - ] -} diff --git a/packages/css/config/app/init/initialize-intro.json b/packages/css/config/app/init/initialize-intro.json deleted file mode 100644 index 38aba3c1..00000000 --- a/packages/css/config/app/init/initialize-intro.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "@context": [ - "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^7.0.0/components/context.jsonld", - "https://linkedsoftwaredependencies.org/bundles/npm/@solidlab/uma-css/^0.0.0/components/context.jsonld" - ], - "import": [ - "css:config/app/init/initializers/root.json", - - "uma-css:config/app/init/default.json" - ], - "@graph": [ - { - "comment": "Initializes the root container resource.", - "@id": "urn:solid-server:default:PrimaryParallelInitializer", - "@type": "ParallelHandler", - "handlers": [ - { - "@id": "urn:solid-server:default:RootInitializer" - } - ] - }, - { - "@id": "urn:solid-server:default:RootFolderGenerator", - "@type": "StaticFolderGenerator", - "templateFolder": "@css:templates/root/intro" - } - ] -} diff --git a/packages/css/config/app/init/initializers/seeding.json b/packages/css/config/app/init/initializers/seeding.json deleted file mode 100644 index 58601231..00000000 --- a/packages/css/config/app/init/initializers/seeding.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "@context": [ - "https://linkedsoftwaredependencies.org/bundles/npm/@solidlab/uma-css/^0.0.0/components/context.jsonld" - ], - "@graph": [ - { - "comment": "Initializer that instantiates all the seeded accounts and pods.", - "@id": "urn:solid-server:default:SeededAccountInitializer", - "@type": "SeededAccountInitializer", - "accountStore": { - "@id": "urn:solid-server:default:AccountStore" - }, - "passwordStore": { - "@id": "urn:solid-server:default:PasswordStore" - }, - "podCreator": { - "@id": "urn:solid-server:default:PodCreator" - }, - "configFilePath": { - "@id": "urn:solid-server:default:variable:seedConfig" - } - } - ] -} diff --git a/packages/css/config/default.json b/packages/css/config/default.json index b7d1b836..4b6d6bec 100644 --- a/packages/css/config/default.json +++ b/packages/css/config/default.json @@ -4,21 +4,29 @@ "https://linkedsoftwaredependencies.org/bundles/npm/@solidlab/uma-css/^0.0.0/components/context.jsonld" ], "import": [ + "css:config/app/init/initialize-intro.json", "css:config/app/main/default.json", "css:config/app/variables/default.json", + "css:config/http/handler/default.json", "css:config/http/middleware/default.json", "css:config/http/notifications/all.json", "css:config/http/server-factory/http.json", "css:config/http/static/default.json", "css:config/identity/access/public.json", "css:config/identity/email/default.json", + "css:config/identity/handler/default.json", "css:config/identity/oidc/default.json", "css:config/identity/ownership/token.json", "css:config/identity/pod/static.json", + "css:config/ldp/authentication/dpop-bearer.json", + "uma-css:config/ldp/authorization/uma.json", + "css:config/ldp/handler/default.json", "css:config/ldp/metadata-parser/default.json", + "css:config/ldp/metadata-writer/default.json", "css:config/ldp/modes/default.json", "css:config/storage/backend/memory.json", "css:config/storage/key-value/resource-store.json", + "css:config/storage/location/pod.json", "css:config/storage/middleware/default.json", "css:config/util/auxiliary/acl.json", "css:config/util/identifiers/suffix.json", @@ -27,15 +35,8 @@ "css:config/util/representation-conversion/default.json", "css:config/util/resource-locker/memory.json", "css:config/util/variables/default.json", - "uma-css:config/uma/default.json", - "uma-css:config/http/handler/default.json", - "uma-css:config/ldp/authentication/uma.json", - "uma-css:config/ldp/authorization/uma.json", - "uma-css:config/ldp/handler/uma.json", - "uma-css:config/ldp/metadata-writer/uma.json", - "uma-css:config/identity/handler/default.json", - "uma-css:config/app/init/initialize-intro.json", - "css:config/storage/location/pod.json" + + "uma-css:config/uma/default.json" ], "@graph": [ { diff --git a/packages/css/config/http/handler/default.json b/packages/css/config/http/handler/default.json deleted file mode 100644 index 44914a54..00000000 --- a/packages/css/config/http/handler/default.json +++ /dev/null @@ -1,56 +0,0 @@ -{ - "@context": [ - "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^7.0.0/components/context.jsonld", - "https://linkedsoftwaredependencies.org/bundles/npm/@solidlab/uma-css/^0.0.0/components/context.jsonld" - ], - "import": [ - "css:config/http/handler/handlers/storage-description.json" - ], - "@graph": [ - { - "comment": "These are all the handlers a request will go through until it is handled.", - "@id": "urn:solid-server:default:HttpHandler", - "@type": "SequenceHandler", - "handlers": [ - { - "@id": "urn:solid-server:default:Middleware" - }, - { - "@id": "urn:solid-server:default:BaseHttpHandler", - "@type": "WaterfallHandler", - "handlers": [ - { - "@id": "urn:solid-server:default:JwksHandler", - "@type": "JwksHandler", - "path": "/.well-known/jwks.json", - "generator": { - "@id": "urn:solid-server:default:JwkGenerator" - } - }, - { - "@id": "urn:solid-server:default:StaticAssetHandler" - }, - { - "@id": "urn:solid-server:default:OidcHandler" - }, - { - "@id": "urn:solid-server:default:NotificationHttpHandler" - }, - { - "@id": "urn:solid-server:default:StorageDescriptionHandler" - }, - { - "@id": "urn:solid-server:default:AuthResourceHttpHandler" - }, - { - "@id": "urn:solid-server:default:IdentityProviderHandler" - }, - { - "@id": "urn:solid-server:default:LdpHandler" - } - ] - } - ] - } - ] -} diff --git a/packages/css/config/identity/handler/base/default.json b/packages/css/config/identity/handler/base/default.json deleted file mode 100644 index 05608c21..00000000 --- a/packages/css/config/identity/handler/base/default.json +++ /dev/null @@ -1,77 +0,0 @@ -{ - "@context": [ - "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^7.0.0/components/context.jsonld", - "https://linkedsoftwaredependencies.org/bundles/npm/@solidlab/uma-css/^0.0.0/components/context.jsonld" - ], - "import": [ - "css:config/identity/handler/base/adapter-factory.json", - "css:config/identity/handler/base/jwks.json", - "css:config/identity/handler/base/provider-factory.json", - - "uma-css:config/identity/handler/storage/default.json" - ], - "@graph": [ - { - "comment": "Routes all IDP related requests to the relevant handlers.", - "@id": "urn:solid-server:default:IdentityProviderHandler", - "@type": "RouterHandler", - "baseUrl": { - "@id": "urn:solid-server:default:variable:baseUrl" - }, - "targetExtractor": { - "@id": "urn:solid-server:default:TargetExtractor" - }, - "allowedPathNames": [ - "^/.account/.*" - ], - "handler": { - "@id": "urn:solid-server:default:IdentityProviderParsingHandler" - } - }, - { - "comment": "Handles IDP input parsing.", - "@id": "urn:solid-server:default:IdentityProviderParsingHandler", - "@type": "ParsingHttpHandler", - "requestParser": { - "@id": "urn:solid-server:default:RequestParser" - }, - "errorHandler": { - "@id": "urn:solid-server:default:ErrorHandler" - }, - "responseWriter": { - "@id": "urn:solid-server:default:ResponseWriter" - }, - "operationHandler": { - "comment": "Handles IDP input authorization. Permission reader should be set to allow all if no authorization is needed.", - "@type": "AuthorizingHttpHandler", - "@id": "urn:solid-server:default:IdentityProviderAuthorizingHandler", - "credentialsExtractor": { - "@id": "urn:solid-server:default:CredentialsExtractor" - }, - "modesExtractor": { - "@id": "urn:solid-server:default:ModesExtractor" - }, - "authorizer": { - "@id": "urn:solid-server:default:Authorizer" - }, - "operationHandler": { - "@id": "urn:solid-server:default:IdentityProviderHttpHandler" - } - } - }, - { - "comment": "Handles IDP handler behaviour.", - "@id": "urn:solid-server:default:IdentityProviderHttpHandler", - "@type": "IdentityProviderHttpHandler", - "providerFactory": { - "@id": "urn:solid-server:default:IdentityProviderFactory" - }, - "cookieStore": { - "@id": "urn:solid-server:default:CookieStore" - }, - "handler": { - "@id": "urn:solid-server:default:InteractionHandler" - } - } - ] -} diff --git a/packages/css/config/identity/handler/default.json b/packages/css/config/identity/handler/default.json deleted file mode 100644 index eb578361..00000000 --- a/packages/css/config/identity/handler/default.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "@context": [ - "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^7.0.0/components/context.jsonld", - "https://linkedsoftwaredependencies.org/bundles/npm/@solidlab/uma-css/^0.0.0/components/context.jsonld" - ], - "import": [ - "css:config/identity/handler/routing/default.json", - "css:config/identity/handler/storage/password.json", - "css:config/identity/handler/enable/account.json", - "css:config/identity/handler/enable/client-credentials.json", - "css:config/identity/handler/enable/password.json", - "css:config/identity/handler/enable/pod.json", - "css:config/identity/handler/enable/webid.json", - - "uma-css:config/identity/handler/base/default.json" - ], - "@graph": [ - { - "comment": "Enables all account-related features." - } - ] -} diff --git a/packages/css/config/identity/handler/storage/account/default.json b/packages/css/config/identity/handler/storage/account/default.json deleted file mode 100644 index 050777d7..00000000 --- a/packages/css/config/identity/handler/storage/account/default.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "@context": [ - "https://linkedsoftwaredependencies.org/bundles/npm/@solidlab/uma-css/^0.0.0/components/context.jsonld" - ], - "@graph": [ - { - "@id": "urn:solid-server:default:AccountStore", - "@type": "BaseAccountStore", - "storage": { - "@id": "urn:solid-server:default:AccountStorage" - } - } - ] -} diff --git a/packages/css/config/identity/handler/storage/default.json b/packages/css/config/identity/handler/storage/default.json deleted file mode 100644 index 5ad34397..00000000 --- a/packages/css/config/identity/handler/storage/default.json +++ /dev/null @@ -1,113 +0,0 @@ -{ - "@context": [ - "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^7.0.0/components/context.jsonld", - "https://linkedsoftwaredependencies.org/bundles/npm/@solidlab/uma-css/^0.0.0/components/context.jsonld" - ], - "import": [ - "uma-css:config/identity/handler/storage/account/default.json" - ], - "@graph": [ - { - "@id": "urn:solid-server:default:AccountStorage", - "@type": "BaseLoginAccountStorage", - "storage": { - "@id": "urn:solid-server:default:IndexedStorage", - "@type": "WrappedIndexedStorage", - "valueStorage": { - "@type": "ContainerPathStorage", - "relativePath": "/accounts/data/", - "source": { - "@id": "urn:solid-server:default:KeyValueStorage" - } - }, - "indexStorage": { - "@type": "ContainerPathStorage", - "relativePath": "/accounts/index/", - "source": { - "@id": "urn:solid-server:default:KeyValueStorage" - } - } - } - }, - { - "@id": "urn:solid-server:default:AccountStore" - }, - { - "@id": "urn:solid-server:default:WebIdStore", - "@type": "BaseWebIdStore", - "storage": { - "@id": "urn:solid-server:default:AccountStorage" - } - }, - { - "@id": "urn:solid-server:default:CookieStore", - "@type": "BaseCookieStore", - "storage": { - "@id": "urn:solid-server:default:CookieStorage", - "@type": "WrappedExpiringStorage", - "source": { - "@type": "ContainerPathStorage", - "relativePath": "/accounts/cookies/", - "source": { - "@id": "urn:solid-server:default:KeyValueStorage" - } - } - } - }, - { - "@id": "urn:solid-server:default:PodStore", - "@type": "BasePodStore", - "storage": { - "@id": "urn:solid-server:default:AccountStorage" - }, - "manager": { - "@id": "urn:solid-server:default:PodManager" - } - }, - { - "@id": "urn:solid-server:default:ClientCredentialsStore", - "@type": "BaseClientCredentialsStore", - "storage": { - "@id": "urn:solid-server:default:AccountStorage" - } - }, - { - "comment": "Initialize all the stores. Also necessary on primary thread for pod seeding.", - "@id": "urn:solid-server:default:PrimaryParallelInitializer", - "@type": "ParallelHandler", - "handlers": [ - { - "@id": "urn:solid-server:default:AccountStore" - }, - { - "@id": "urn:solid-server:default:ClientCredentialsStore" - }, - { - "@id": "urn:solid-server:default:PodStore" - }, - { - "@id": "urn:solid-server:default:WebIdStore" - } - ] - }, - { - "comment": "Initialize all the stores.", - "@id": "urn:solid-server:default:EarlyProcessParallelInitializer", - "@type": "ParallelHandler", - "handlers": [ - { - "@id": "urn:solid-server:default:AccountStore" - }, - { - "@id": "urn:solid-server:default:ClientCredentialsStore" - }, - { - "@id": "urn:solid-server:default:PodStore" - }, - { - "@id": "urn:solid-server:default:WebIdStore" - } - ] - } - ] -} diff --git a/packages/css/config/ldp/authentication/uma.json b/packages/css/config/ldp/authentication/uma.json deleted file mode 100644 index 7b13db68..00000000 --- a/packages/css/config/ldp/authentication/uma.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "@context": [ - "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^7.0.0/components/context.jsonld", - "https://linkedsoftwaredependencies.org/bundles/npm/@solidlab/uma-css/^0.0.0/components/context.jsonld" - ], - "@graph": [ - { - "comment": "Caches the credentials based on the incoming request.", - "@id": "urn:solid-server:default:CredentialsExtractor", - "@type": "CachedHandler", - "source": { - "comment": "Supports UMA tokens, or no credentials.", - "@type": "UnionCredentialsExtractor", - "extractors": [ - { - "comment": "Extracts information from UMA access tokens.", - "@id": "urn:solid-server:default:UMATokenExtractor", - "@type": "UmaTokenExtractor", - "client": { - "@id": "urn:solid-server:default:UmaClient" - }, - "targetExtractor": { - "@id": "urn:solid-server:default:TargetExtractor" - }, - "ownerUtil": { - "@id": "urn:solid-server:default:OwnerUtil" - }, - "introspect": false - }, - { - "@type": "PublicCredentialsExtractor" - } - ] - } - } - ] -} diff --git a/packages/css/config/ldp/authorization/uma.json b/packages/css/config/ldp/authorization/uma.json index 6d39877b..097515dd 100644 --- a/packages/css/config/ldp/authorization/uma.json +++ b/packages/css/config/ldp/authorization/uma.json @@ -8,14 +8,9 @@ ], "@graph": [ { - "comment": "In case of WebACL authorization the ACL resources determine authorization.", + "comment": "There are no auth-specific resources, but this ID still has to exist.", "@id": "urn:solid-server:default:AuthResourceHttpHandler", - "@type": "RouterHandler", - "args_baseUrl": { "@id": "urn:solid-server:default:variable:baseUrl" }, - "args_targetExtractor": { "@id": "urn:solid-server:default:TargetExtractor" }, - "args_allowedMethods": [ "*" ], - "args_allowedPathNames": [ "^/.*\\.acl$" ], - "args_handler": { "@id": "urn:solid-server:default:LdpHandler" } + "@type": "UnsupportedAsyncHandler" } ] - } \ No newline at end of file + } diff --git a/packages/css/config/ldp/handler/uma.json b/packages/css/config/ldp/handler/uma.json deleted file mode 100644 index 52e56d0a..00000000 --- a/packages/css/config/ldp/handler/uma.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "@context": [ - "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^7.0.0/components/context.jsonld", - "https://linkedsoftwaredependencies.org/bundles/npm/@solidlab/uma-css/^0.0.0/components/context.jsonld" - ], - "import": [ - "css:config/ldp/handler/components/authorizer.json", - "css:config/ldp/handler/components/error-handler.json", - "css:config/ldp/handler/components/operation-handler.json", - "css:config/ldp/handler/components/operation-metadata.json", - "css:config/ldp/handler/components/preferences.json", - "css:config/ldp/handler/components/request-parser.json", - "css:config/ldp/handler/components/response-writer.json" - ], - "@graph": [ - { - "comment": "The main entry point into the main Solid behaviour.", - "@id": "urn:solid-server:default:LdpHandler", - "@type": "ParsingHttpHandler", - "args_requestParser": { "@id": "urn:solid-server:default:RequestParser" }, - "args_errorHandler": { "@id": "urn:solid-server:default:ErrorHandler" }, - "args_responseWriter": { "@id": "urn:solid-server:default:ResponseWriter" }, - "args_operationHandler": { - "@type": "AuthorizingHttpHandler", - "args_credentialsExtractor": { "@id": "urn:solid-server:default:CredentialsExtractor" }, - "args_modesExtractor": { "@id": "urn:solid-server:default:ModesExtractor" }, - "args_permissionReader": { "@id": "urn:solid-server:default:PermissionReader" }, - "args_authorizer": { - "comment": "Requests UMA ticket when authorization fails.", - "@id": "urn:solid-server:default:UmaAuthorizer", - "@type": "UmaAuthorizer", - "authorizer": { "@id": "urn:solid-server:default:Authorizer" }, - "umaClient": { "@id": "urn:solid-server:default:UmaClient" }, - "ownerUtil": { "@id": "urn:solid-server:default:OwnerUtil" } - }, - "args_operationHandler": { "@id": "urn:solid-server:default:OperationHandler" } - } - } - ] -} \ No newline at end of file diff --git a/packages/css/config/ldp/metadata-writer/uma.json b/packages/css/config/ldp/metadata-writer/uma.json deleted file mode 100644 index 108fed9a..00000000 --- a/packages/css/config/ldp/metadata-writer/uma.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "@context": [ - "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^7.0.0/components/context.jsonld", - "https://linkedsoftwaredependencies.org/bundles/npm/@solidlab/uma-css/^0.0.0/components/context.jsonld" - ], - "import": [ - "css:config/ldp/metadata-writer/writers/allow-accept.json", - "css:config/ldp/metadata-writer/writers/content-type.json", - "css:config/ldp/metadata-writer/writers/cookie.json", - "css:config/ldp/metadata-writer/writers/link-rel.json", - "css:config/ldp/metadata-writer/writers/link-rel-metadata.json", - "css:config/ldp/metadata-writer/writers/mapped.json", - "css:config/ldp/metadata-writer/writers/modified.json", - "css:config/ldp/metadata-writer/writers/range.json", - "css:config/ldp/metadata-writer/writers/storage-description.json", - "uma-css:config/ldp/metadata-writer/writers/uma-ticket.json" - ], - "@graph": [ - { - "comment": "Adds metadata to the response based on the RDF metadata.", - "@id": "urn:solid-server:default:MetadataWriter", - "@type": "ParallelHandler", - "handlers": [ - { - "@id": "urn:solid-server:default:MetadataWriter_AllowAccept" - }, - { - "@id": "urn:solid-server:default:MetadataWriter_ContentType" - }, - { - "@id": "urn:solid-server:default:MetadataWriter_LinkRel" - }, - { - "@id": "urn:solid-server:default:MetadataWriter_LinkRelMetadata" - }, - { - "@id": "urn:solid-server:default:MetadataWriter_Cookie" - }, - { - "@id": "urn:solid-server:default:MetadataWriter_Mapped" - }, - { - "@id": "urn:solid-server:default:MetadataWriter_Modified" - }, - { - "@id": "urn:solid-server:default:MetadataWriter_Range" - }, - { - "@id": "urn:solid-server:default:MetadataWriter_StorageDescription" - }, - { - "@id": "urn:solid-server:default:MetadataWriter_UmaTicket" - } - ] - } - ] -} \ No newline at end of file diff --git a/packages/css/config/ldp/metadata-writer/writers/uma-ticket.json b/packages/css/config/ldp/metadata-writer/writers/uma-ticket.json deleted file mode 100644 index 706681fc..00000000 --- a/packages/css/config/ldp/metadata-writer/writers/uma-ticket.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "@context": [ - "https://linkedsoftwaredependencies.org/bundles/npm/@solidlab/uma-css/^0.0.0/components/context.jsonld" - ], - "@graph": [ - { - "comment": "Returns the UMA ticket in case of an unauthorized request.", - "@id": "urn:solid-server:default:MetadataWriter_UmaTicket", - "@type": "UmaTicketMetadataWriter" - } - ] -} \ No newline at end of file diff --git a/packages/css/config/uma/default.json b/packages/css/config/uma/default.json index 7fb40233..59fffc44 100644 --- a/packages/css/config/uma/default.json +++ b/packages/css/config/uma/default.json @@ -1,141 +1,20 @@ { "@context": [ - "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^7.0.0/components/context.jsonld", "https://linkedsoftwaredependencies.org/bundles/npm/@solidlab/uma-css/^0.0.0/components/context.jsonld" ], - "@graph": [ - { - "@id": "urn:solid-server:default:ServerConfigurator", - "@type": "ParallelHandler", - "handlers": [ - { - "@id": "urn:solid-server:default:StatusDependantServerConfigurator", - "@type": "StatusDependantServerConfigurator", - "dependants": [ - { "@id": "urn:solid-server:default:UmaFetcher" } - ], - "statusMap": [ - { - "StatusDependantServerConfigurator:_statusMap_key": "listening", - "StatusDependantServerConfigurator:_statusMap_value": true - }, - { - "StatusDependantServerConfigurator:_statusMap_key": "close", - "StatusDependantServerConfigurator:_statusMap_value": false - }, - { - "StatusDependantServerConfigurator:_statusMap_key": "error", - "StatusDependantServerConfigurator:_statusMap_value": false - } - ] - } - ] - }, - { - "comment": "Returns the UMA ticket in case of an unauthorized request.", - "@id": "urn:solid-server:default:UmaClient", - "@type": "UmaClient", - "umaIdStore": { - "@id": "urn:solid-server:default:UmaIdStore", - "@type": "MemoryMapStorage" - }, - "fetcher": { - "@id": "urn:solid-server:default:UmaFetcher", - "@type": "PausableFetcher", - "fetcher": { - "@type": "RetryingFetcher", - "fetcher": { - "@type": "SignedFetcher", - "fetcher": { - "@type": "BaseFetcher" - }, - "baseUrl": { "@id": "urn:solid-server:default:variable:baseUrl" }, - "keyGen": { "@id": "urn:solid-server:default:JwkGenerator" } - }, - "retries": 150, - "exponent": 3, - "retryOn": [401, 500] - } - } - }, - { - "@id": "urn:solid-server:default:OwnerUtil", - "@type": "OwnerUtil", - "podStore": { - "@id": "urn:solid-server:default:PodStore" - }, - "accountStore": { - "@id": "urn:solid-server:default:AccountStore" - }, - "storageStrategy": { - "@id": "urn:solid-server:default:StorageLocationStrategy" - }, - "umaPatStore": { - "@id": "urn:solid-server:default:UmaPatStore", - "@type": "MemoryMapStorage" - }, - "umaServerURL": { - "@id": "urn:solid-server:uma:variable:AuthorizationServer" - } - }, - { - "comment": "Listens to the activities emitted by the MonitoringStore.", - "@id": "urn:solid-server:default:ResourceRegistrar", - "@type": "ResourceRegistrar", - "store": { - "@id": "urn:solid-server:default:ResourceStore" - }, - "ownerUtil": { - "@id": "urn:solid-server:default:OwnerUtil" - }, - "umaClient": { - "@id": "urn:solid-server:default:UmaClient" - } - }, - { - "comment": "The ResourceRegistrar is added to the list of Initializers so Components.js finds and instantiates it.", - "@id": "urn:solid-server:default:PrimaryParallelInitializer", - "@type": "ParallelHandler", - "handlers": [ - { - "@id": "urn:solid-server:default:ResourceRegistrar" - } - ] - }, - { - "@id": "urn:solid-server-app-setup:default:CliExtractor", - "@type": "YargsCliExtractor", - "parameters": [ - { - "@type": "YargsParameter", - "name": "authServer", - "options": { - "alias": "a", - "requiresArg": true, - "type": "string", - "describe": "The URL of the UMA Authorization Server." - } - } - ] - }, { - "comment": "Converts an input key/value object into an object mapping values to Components.js variables", - "@id": "urn:solid-server-app-setup:default:ShorthandResolver", - "@type": "CombinedShorthandResolver", - "resolvers": [ - { - "CombinedShorthandResolver:_resolvers_key": "urn:solid-server:uma:variable:AuthorizationServer", - "CombinedShorthandResolver:_resolvers_value": { - "@type": "KeyExtractor", - "key": "authServer", - "defaultValue": "http://localhost:4000" - } - } - ] - }, - { - "comment": "URL of the UMA Authorization Server.", - "@id": "urn:solid-server:uma:variable:AuthorizationServer", - "@type": "Variable" - } + "import": [ + "uma-css:config/uma/overrides/account-seeding.json", + "uma-css:config/uma/overrides/account-store.json", + "uma-css:config/uma/overrides/authorization-handler.json", + "uma-css:config/uma/overrides/jwks.json", + "uma-css:config/uma/overrides/token-extractor.json", + "uma-css:config/uma/overrides/www-auth.json", + + "uma-css:config/uma/parts/cli.json", + "uma-css:config/uma/parts/client.json", + "uma-css:config/uma/parts/fetcher.json", + "uma-css:config/uma/parts/owner-util.json", + "uma-css:config/uma/parts/resource-registrar.json", + "uma-css:config/uma/parts/server-configurator.json" ] } diff --git a/packages/css/config/uma/overrides/account-seeding.json b/packages/css/config/uma/overrides/account-seeding.json new file mode 100644 index 00000000..c3050657 --- /dev/null +++ b/packages/css/config/uma/overrides/account-seeding.json @@ -0,0 +1,23 @@ +{ + "@context": [ + "https://linkedsoftwaredependencies.org/bundles/npm/@solidlab/uma-css/^0.0.0/components/context.jsonld" + ], + "@graph": [ + { + "comment": "Replace the account seeder with the UMA version so the AS is taken into account.", + "@id": "urn:solid-server:override:SeededAccountInitializer", + "@type": "Override", + "overrideInstance": { + "@id": "urn:solid-server:default:SeededAccountInitializer" + }, + "overrideParameters": { + "comment": "Initializer that instantiates all the seeded accounts and pods.", + "@type": "UmaSeededAccountInitializer", + "accountStore": { "@id": "urn:solid-server:default:AccountStore" }, + "passwordStore": { "@id": "urn:solid-server:default:PasswordStore" }, + "podCreator": { "@id": "urn:solid-server:default:PodCreator" }, + "configFilePath": { "@id": "urn:solid-server:default:variable:seedConfig" } + } + } + ] +} diff --git a/packages/css/config/uma/overrides/account-store.json b/packages/css/config/uma/overrides/account-store.json new file mode 100644 index 00000000..dcbba5de --- /dev/null +++ b/packages/css/config/uma/overrides/account-store.json @@ -0,0 +1,21 @@ +{ + "@context": [ + "https://linkedsoftwaredependencies.org/bundles/npm/@solidlab/uma-css/^0.0.0/components/context.jsonld" + ], + "@graph": [ + { + "comment": "Replace the account store with the UMA version that stores AS settings.", + "@id": "urn:solid-server:override:AccountStore", + "@type": "Override", + "overrideInstance": { + "@id": "urn:solid-server:default:AccountStore" + }, + "overrideParameters": { + "@type": "UmaAccountStore", + "storage": { + "@id": "urn:solid-server:default:AccountStorage" + } + } + } + ] +} diff --git a/packages/css/config/uma/overrides/authorization-handler.json b/packages/css/config/uma/overrides/authorization-handler.json new file mode 100644 index 00000000..b3a4d4a8 --- /dev/null +++ b/packages/css/config/uma/overrides/authorization-handler.json @@ -0,0 +1,34 @@ +{ + "@context": [ + "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^7.0.0/components/context.jsonld", + "https://linkedsoftwaredependencies.org/bundles/npm/@solidlab/uma-css/^0.0.0/components/context.jsonld" + ], + "@graph": [ + { + "comment": "Replace the authorization handler with UMA support.", + "@id": "urn:solid-server:override:LdpHandler", + "@type": "Override", + "overrideInstance": { + "@id": "urn:solid-server:default:LdpHandler" + }, + "overrideParameters": { + "@type": "ParsingHttpHandler", + "operationHandler": { + "@type": "AuthorizingHttpHandler", + "credentialsExtractor": { "@id": "urn:solid-server:default:CredentialsExtractor" }, + "modesExtractor": { "@id": "urn:solid-server:default:ModesExtractor" }, + "permissionReader": { "@id": "urn:solid-server:default:PermissionReader" }, + "authorizer": { + "comment": "Requests UMA ticket when authorization fails.", + "@id": "urn:solid-server:default:UmaAuthorizer", + "@type": "UmaAuthorizer", + "authorizer": { "@id": "urn:solid-server:default:Authorizer" }, + "umaClient": { "@id": "urn:solid-server:default:UmaClient" }, + "ownerUtil": { "@id": "urn:solid-server:default:OwnerUtil" } + }, + "operationHandler": { "@id": "urn:solid-server:default:OperationHandler" } + } + } + } + ] +} diff --git a/packages/css/config/uma/overrides/jwks.json b/packages/css/config/uma/overrides/jwks.json new file mode 100644 index 00000000..1221f095 --- /dev/null +++ b/packages/css/config/uma/overrides/jwks.json @@ -0,0 +1,27 @@ +{ + "@context": [ + "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^7.0.0/components/context.jsonld", + "https://linkedsoftwaredependencies.org/bundles/npm/@solidlab/uma-css/^0.0.0/components/context.jsonld" + ], + "@graph": [ + { + "comment": "Add JWKS handler to list of base handlers so the server exposes its public key.", + "@id": "urn:solid-server:override:BaseHttpHandler", + "@type": "Override", + "overrideInstance": { "@id": "urn:solid-server:default:BaseHttpHandler" }, + "overrideSteps": [{ + "@type": "OverrideListInsertAt", + "overrideParameter": { "@id": "WaterfallHandler:_handlers" }, + "overrideTarget": 0, + "overrideValue": { + "@id": "urn:solid-server:default:JwksHandler", + "@type": "JwksHandler", + "path": "/.well-known/jwks.json", + "generator": { + "@id": "urn:solid-server:default:JwkGenerator" + } + } + }] + } + ] +} diff --git a/packages/css/config/uma/overrides/token-extractor.json b/packages/css/config/uma/overrides/token-extractor.json new file mode 100644 index 00000000..dd51c3e5 --- /dev/null +++ b/packages/css/config/uma/overrides/token-extractor.json @@ -0,0 +1,23 @@ +{ + "@context": [ + "https://linkedsoftwaredependencies.org/bundles/npm/@solidlab/uma-css/^0.0.0/components/context.jsonld" + ], + "@graph": [ + { + "comment": "Replace the DPoP authentication with UMA authentication.", + "@id": "urn:solid-server:override:AccessTokenExtractor", + "@type": "Override", + "overrideInstance": { + "@id": "urn:solid-server:default:AccessTokenExtractor" + }, + "overrideParameters": { + "comment": "Extracts information from UMA access tokens.", + "@type": "UmaTokenExtractor", + "client": { "@id": "urn:solid-server:default:UmaClient" }, + "targetExtractor": { "@id": "urn:solid-server:default:TargetExtractor" }, + "ownerUtil": { "@id": "urn:solid-server:default:OwnerUtil" }, + "introspect": false + } + } + ] +} diff --git a/packages/css/config/uma/overrides/www-auth.json b/packages/css/config/uma/overrides/www-auth.json new file mode 100644 index 00000000..95402403 --- /dev/null +++ b/packages/css/config/uma/overrides/www-auth.json @@ -0,0 +1,20 @@ +{ + "@context": [ + "https://linkedsoftwaredependencies.org/bundles/npm/@solidlab/uma-css/^0.0.0/components/context.jsonld" + ], + "@graph": [ + { + "comment": "Replace WWW-Authenticate metadata writer with the UMA version.", + "@id": "urn:solid-server:override:MetadataWriter_WwwAuth", + "@type": "Override", + "overrideInstance": { + "@id": "urn:solid-server:default:MetadataWriter_WwwAuth" + }, + "overrideParameters": { + "comment": "Returns the UMA ticket in case of an unauthorized request.", + "@id": "urn:solid-server:default:MetadataWriter_UmaTicket", + "@type": "UmaTicketMetadataWriter" + } + } + ] +} diff --git a/packages/css/config/uma/parts/cli.json b/packages/css/config/uma/parts/cli.json new file mode 100644 index 00000000..5a4b3cd6 --- /dev/null +++ b/packages/css/config/uma/parts/cli.json @@ -0,0 +1,44 @@ +{ + "@context": [ + "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^7.0.0/components/context.jsonld", + "https://linkedsoftwaredependencies.org/bundles/npm/@solidlab/uma-css/^0.0.0/components/context.jsonld" + ], + "@graph": [ + { + "@id": "urn:solid-server-app-setup:default:CliExtractor", + "@type": "YargsCliExtractor", + "parameters": [ + { + "@type": "YargsParameter", + "name": "authServer", + "options": { + "alias": "a", + "requiresArg": true, + "type": "string", + "describe": "The URL of the UMA Authorization Server." + } + } + ] + }, + { + "comment": "Converts an input key/value object into an object mapping values to Components.js variables", + "@id": "urn:solid-server-app-setup:default:ShorthandResolver", + "@type": "CombinedShorthandResolver", + "resolvers": [ + { + "CombinedShorthandResolver:_resolvers_key": "urn:solid-server:uma:variable:AuthorizationServer", + "CombinedShorthandResolver:_resolvers_value": { + "@type": "KeyExtractor", + "key": "authServer", + "defaultValue": "http://localhost:4000" + } + } + ] + }, + { + "comment": "URL of the UMA Authorization Server.", + "@id": "urn:solid-server:uma:variable:AuthorizationServer", + "@type": "Variable" + } + ] +} diff --git a/packages/css/config/uma/parts/client.json b/packages/css/config/uma/parts/client.json new file mode 100644 index 00000000..8946838a --- /dev/null +++ b/packages/css/config/uma/parts/client.json @@ -0,0 +1,20 @@ +{ + "@context": [ + "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^7.0.0/components/context.jsonld", + "https://linkedsoftwaredependencies.org/bundles/npm/@solidlab/uma-css/^0.0.0/components/context.jsonld" + ], + "@graph": [ + { + "comment": "Provides support for interactions with the UMA AS.", + "@id": "urn:solid-server:default:UmaClient", + "@type": "UmaClient", + "umaIdStore": { + "@id": "urn:solid-server:default:UmaIdStore", + "@type": "MemoryMapStorage" + }, + "fetcher": { + "@id": "urn:solid-server:default:UmaFetcher" + } + } + ] +} diff --git a/packages/css/config/uma/parts/fetcher.json b/packages/css/config/uma/parts/fetcher.json new file mode 100644 index 00000000..2e36441d --- /dev/null +++ b/packages/css/config/uma/parts/fetcher.json @@ -0,0 +1,26 @@ +{ + "@context": [ + "https://linkedsoftwaredependencies.org/bundles/npm/@solidlab/uma-css/^0.0.0/components/context.jsonld" + ], + "@graph": [ + { + "comment": "Adds additional features when performing HTTP requests", + "@id": "urn:solid-server:default:UmaFetcher", + "@type": "PausableFetcher", + "fetcher": { + "@type": "RetryingFetcher", + "fetcher": { + "@type": "SignedFetcher", + "fetcher": { + "@type": "BaseFetcher" + }, + "baseUrl": { "@id": "urn:solid-server:default:variable:baseUrl" }, + "keyGen": { "@id": "urn:solid-server:default:JwkGenerator" } + }, + "retries": 150, + "exponent": 3, + "retryOn": [401, 500] + } + } + ] +} diff --git a/packages/css/config/uma/parts/owner-util.json b/packages/css/config/uma/parts/owner-util.json new file mode 100644 index 00000000..b772de56 --- /dev/null +++ b/packages/css/config/uma/parts/owner-util.json @@ -0,0 +1,29 @@ +{ + "@context": [ + "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^7.0.0/components/context.jsonld", + "https://linkedsoftwaredependencies.org/bundles/npm/@solidlab/uma-css/^0.0.0/components/context.jsonld" + ], + "@graph": [ + { + "comment": "Provides utility for interacting with pod owner metadata", + "@id": "urn:solid-server:default:OwnerUtil", + "@type": "OwnerUtil", + "podStore": { + "@id": "urn:solid-server:default:PodStore" + }, + "accountStore": { + "@id": "urn:solid-server:default:AccountStore" + }, + "storageStrategy": { + "@id": "urn:solid-server:default:StorageLocationStrategy" + }, + "umaPatStore": { + "@id": "urn:solid-server:default:UmaPatStore", + "@type": "MemoryMapStorage" + }, + "umaServerURL": { + "@id": "urn:solid-server:uma:variable:AuthorizationServer" + } + } + ] +} diff --git a/packages/css/config/uma/parts/resource-registrar.json b/packages/css/config/uma/parts/resource-registrar.json new file mode 100644 index 00000000..b1b3ce30 --- /dev/null +++ b/packages/css/config/uma/parts/resource-registrar.json @@ -0,0 +1,32 @@ +{ + "@context": [ + "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^7.0.0/components/context.jsonld", + "https://linkedsoftwaredependencies.org/bundles/npm/@solidlab/uma-css/^0.0.0/components/context.jsonld" + ], + "@graph": [ + { + "comment": "Listens to the activities emitted by the MonitoringStore.", + "@id": "urn:solid-server:default:ResourceRegistrar", + "@type": "ResourceRegistrar", + "store": { + "@id": "urn:solid-server:default:ResourceStore" + }, + "ownerUtil": { + "@id": "urn:solid-server:default:OwnerUtil" + }, + "umaClient": { + "@id": "urn:solid-server:default:UmaClient" + } + }, + { + "comment": "The ResourceRegistrar is added to the list of Initializers so Components.js finds and instantiates it.", + "@id": "urn:solid-server:default:PrimaryParallelInitializer", + "@type": "ParallelHandler", + "handlers": [ + { + "@id": "urn:solid-server:default:ResourceRegistrar" + } + ] + } + ] +} diff --git a/packages/css/config/uma/parts/server-configurator.json b/packages/css/config/uma/parts/server-configurator.json new file mode 100644 index 00000000..c22226dc --- /dev/null +++ b/packages/css/config/uma/parts/server-configurator.json @@ -0,0 +1,36 @@ +{ + "@context": [ + "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^7.0.0/components/context.jsonld", + "https://linkedsoftwaredependencies.org/bundles/npm/@solidlab/uma-css/^0.0.0/components/context.jsonld" + ], + "@graph": [ + { + "@id": "urn:solid-server:default:ServerConfigurator", + "@type": "ParallelHandler", + "handlers": [ + { + "comment": "Informs relevant components of changes to the server state", + "@id": "urn:solid-server:default:StatusDependantServerConfigurator", + "@type": "StatusDependantServerConfigurator", + "dependants": [ + { "@id": "urn:solid-server:default:UmaFetcher" } + ], + "statusMap": [ + { + "StatusDependantServerConfigurator:_statusMap_key": "listening", + "StatusDependantServerConfigurator:_statusMap_value": true + }, + { + "StatusDependantServerConfigurator:_statusMap_key": "close", + "StatusDependantServerConfigurator:_statusMap_value": false + }, + { + "StatusDependantServerConfigurator:_statusMap_key": "error", + "StatusDependantServerConfigurator:_statusMap_value": false + } + ] + } + ] + } + ] +} diff --git a/packages/css/package.json b/packages/css/package.json index f866fa46..77fbab42 100644 --- a/packages/css/package.json +++ b/packages/css/package.json @@ -38,7 +38,7 @@ "private": true, "packageManager": "yarn@4.0.2", "engines": { - "node": ">=18.18", + "node": ">=20.0", "yarn": ">=4.0" }, "type": "commonjs", @@ -67,20 +67,16 @@ "demo:start": "yarn run community-solid-server -m . -c ./config/demo.json -f ./tmp -a http://localhost:4000/ -l debug" }, "dependencies": { - "@solid/community-server": "^7.0.2", + "@solid/community-server": "^7.1.7", "@solidlab/derived-resources-component": "^1.0.2", "@solidlab/uma": "workspace:^", - "componentsjs": "^5.4.2", - "cross-fetch": "^4.0.0", + "@types/n3": "^1.16.4", + "componentsjs": "^5.5.1", + "fetch-retry": "^6.0.0", "http-message-signatures": "^1.0.4", "jose": "^5.2.2", "n3": "^1.17.2" }, - "devDependencies": { - "@types/n3": "^1.16.4", - "@types/node": "^18.18.11", - "fetch-retry": "^6.0.0" - }, "jest": { "preset": "ts-jest", "testEnvironment": "node", diff --git a/packages/css/src/identity/interaction/account/util/AccountSettings.ts b/packages/css/src/identity/interaction/account/util/AccountSettings.ts new file mode 100644 index 00000000..5ccbe87f --- /dev/null +++ b/packages/css/src/identity/interaction/account/util/AccountSettings.ts @@ -0,0 +1,31 @@ +import { ACCOUNT_SETTINGS_REMEMBER_LOGIN } from '@solid/community-server'; + +/** + * Settings parameter containing the URL of the user's Authorization Server. + */ +export const ACCOUNT_SETTINGS_AUTHZ_SERVER = 'authzServer'; + +/** + * Settings parameter containing the Personal Access Token (PAT) of the user at their AS. + */ +export const ACCOUNT_SETTINGS_AS_TOKEN = 'asToken'; + +/** + * Settings parameter containing the private key of the user. + */ +export const ACCOUNT_SETTINGS_KEYS = 'keys'; + +export const UMA_ACCOUNT_STORAGE_DESCRIPTION = { + [ACCOUNT_SETTINGS_REMEMBER_LOGIN]: 'boolean?', + [ACCOUNT_SETTINGS_AUTHZ_SERVER]: 'string?', + [ACCOUNT_SETTINGS_AS_TOKEN]: 'string?', + [ACCOUNT_SETTINGS_KEYS]: 'string[]?', +} as const; + +// Duplication but needed to get around Components.js limitations +export type UMA_ACCOUNT_STORAGE_TYPE = { + [ACCOUNT_SETTINGS_REMEMBER_LOGIN]: 'boolean?', + [ACCOUNT_SETTINGS_AUTHZ_SERVER]: 'string?', + [ACCOUNT_SETTINGS_AS_TOKEN]: 'string?', + [ACCOUNT_SETTINGS_KEYS]: 'string[]?', +} diff --git a/packages/css/src/identity/interaction/account/util/AccountStore.ts b/packages/css/src/identity/interaction/account/util/AccountStore.ts deleted file mode 100644 index e730385c..00000000 --- a/packages/css/src/identity/interaction/account/util/AccountStore.ts +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Settings parameter used to determine if the user wants the login to be remembered. - */ -export const ACCOUNT_SETTINGS_REMEMBER_LOGIN = 'rememberLogin'; - -/** - * Settings parameter containing the URL of the user's Authorization Server. - */ -export const ACCOUNT_SETTINGS_AUTHZ_SERVER = 'authzServer'; - -/** - * Settings parameter containing the Personal Access Token (PAT) of the user at their AS. - */ -export const ACCOUNT_SETTINGS_AS_TOKEN = 'asToken'; - -/** - * Settings parameter containing the private key of the user. - */ -export const ACCOUNT_SETTINGS_KEYS = 'keys'; - -export type AccountSettings = { - [ACCOUNT_SETTINGS_REMEMBER_LOGIN]?: boolean, - [ACCOUNT_SETTINGS_AUTHZ_SERVER]?: string, - [ACCOUNT_SETTINGS_AS_TOKEN]?: string, - [ACCOUNT_SETTINGS_KEYS]?: string[], -}; - -/* eslint-disable ts/method-signature-style */ -/** - * Used to store account data. - */ -export interface AccountStore { - /** - * Creates a new and empty account. - * Since this account will not yet have a login method, - * implementations should restrict what is possible with this account, - * and should potentially have something in place to clean these accounts up if they are unused. - */ - create: () => Promise; - - /** - * Finds the setting of the account with the given identifier. - * @param id - The account identifier. - * @param setting - The setting to find the value of. - */ - getSetting(id: string, setting: T): Promise; - - /** - * Updates the settings for the account with the given identifier to the new values. - * @param id - The account identifier. - * @param setting - The setting to update. - * @param value - The new value for the setting. - */ - updateSetting(id: string, setting: T, value: AccountSettings[T]): Promise; -} diff --git a/packages/css/src/identity/interaction/account/util/BaseAccountStore.ts b/packages/css/src/identity/interaction/account/util/BaseAccountStore.ts deleted file mode 100644 index 881eaa3c..00000000 --- a/packages/css/src/identity/interaction/account/util/BaseAccountStore.ts +++ /dev/null @@ -1,63 +0,0 @@ - -import { Initializer, getLoggerFor, createErrorMessage, InternalServerError } from '@solid/community-server'; -import { AccountLoginStorage, ACCOUNT_TYPE } from './LoginStorage'; -import { ACCOUNT_SETTINGS_AS_TOKEN, ACCOUNT_SETTINGS_AUTHZ_SERVER, ACCOUNT_SETTINGS_KEYS, - ACCOUNT_SETTINGS_REMEMBER_LOGIN, AccountSettings, AccountStore } from './AccountStore'; -import { ValueType } from '../../../../storage/keyvalue/IndexedStorage'; - -export const ACCOUNT_STORAGE_DESCRIPTION = { - [ACCOUNT_SETTINGS_REMEMBER_LOGIN]: 'boolean?', - [ACCOUNT_SETTINGS_AUTHZ_SERVER]: 'string?', - [ACCOUNT_SETTINGS_AS_TOKEN]: 'string?', - [ACCOUNT_SETTINGS_KEYS]: 'string[]?', -} as const; - -/** - * A {@link AccountStore} that uses an {@link AccountLoginStorage} to keep track of the accounts. - * Needs to be initialized before it can be used. - */ -export class BaseAccountStore extends Initializer implements AccountStore { - private readonly logger = getLoggerFor(this); - - private readonly storage: AccountLoginStorage<{ [ACCOUNT_TYPE]: typeof ACCOUNT_STORAGE_DESCRIPTION }>; - private initialized = false; - - public constructor(storage: AccountLoginStorage) { - super(); - this.storage = storage as typeof this.storage; - } - - // Initialize the type definitions - public async handle(): Promise { - if (this.initialized) { - return; - } - try { - await this.storage.defineType(ACCOUNT_TYPE, ACCOUNT_STORAGE_DESCRIPTION, false); - this.initialized = true; - } catch (cause: unknown) { - throw new InternalServerError(`Error defining account in storage: ${createErrorMessage(cause)}`, { cause }); - } - } - - public async create(): Promise { - const { id } = await this.storage.create(ACCOUNT_TYPE, {}); - this.logger.debug(`Created new account ${id}`); - - return id; - } - - public async getSetting(id: string, setting: T): Promise { - const account = await this.storage.get(ACCOUNT_TYPE, id); - if (!account) { - return; - } - const { id: unused, ...settings } = account; - return settings[setting]; - } - - public async updateSetting(id: string, setting: T, value: AccountSettings[T]): - Promise { - await this.storage.setField(ACCOUNT_TYPE, id, setting, value as ValueType); - } -} diff --git a/packages/css/src/identity/interaction/account/util/LoginStorage.ts b/packages/css/src/identity/interaction/account/util/LoginStorage.ts deleted file mode 100644 index b30fafa2..00000000 --- a/packages/css/src/identity/interaction/account/util/LoginStorage.ts +++ /dev/null @@ -1,35 +0,0 @@ -import type { - IndexedStorage, - IndexTypeCollection, - StringKey, - ValueTypeDescription, -} from '../../../../storage/keyvalue/IndexedStorage'; - -export const ACCOUNT_TYPE = 'account'; - -/** - * A {@link IndexedStorage} where the `defineType` function - * takes an extra parameter to indicate if the type corresponds to a login method. - * This is useful for storages that want to add extra requirements based on the data being edited. - * - * In practice, we use this because we want to require accounts to have at least 1 login method. - */ -export interface LoginStorage> extends Omit, 'defineType'> { - /** - * Defines a type in the storage, just like in an {@link IndexedStorage}, - * but additionally it needs to be indicated if the type corresponds to a login method or not. - * - * @param type - Type to define. - * @param description - Description of the type. - * @param isLogin - Whether this type corresponds to a login method or not. - */ - defineType: >(type: TType, description: T[TType], isLogin: boolean) => Promise; -} - -/** - * A {@link LoginStorage} with specific typings to ensure other types can reference account IDs - * without actually needing to specify it explicitly in their storage type. - */ -export type AccountLoginStorage>>> = - LoginStorage; diff --git a/packages/css/src/identity/interaction/account/util/UmaAccountStore.ts b/packages/css/src/identity/interaction/account/util/UmaAccountStore.ts new file mode 100644 index 00000000..cb53f21f --- /dev/null +++ b/packages/css/src/identity/interaction/account/util/UmaAccountStore.ts @@ -0,0 +1,12 @@ +import { AccountLoginStorage, AccountStore, GenericAccountStore } from '@solid/community-server'; +import { UMA_ACCOUNT_STORAGE_DESCRIPTION, UMA_ACCOUNT_STORAGE_TYPE } from './AccountSettings'; + +/** + * A {@link AccountStore} that uses an {@link AccountLoginStorage} to keep track of the accounts. + * Needs to be initialized before it can be used. + */ +export class UmaAccountStore extends GenericAccountStore { + public constructor(storage: AccountLoginStorage>) { + super(storage, UMA_ACCOUNT_STORAGE_DESCRIPTION); + } +} diff --git a/packages/css/src/index.ts b/packages/css/src/index.ts index 6fb27d2d..dc844c56 100644 --- a/packages/css/src/index.ts +++ b/packages/css/src/index.ts @@ -5,21 +5,15 @@ export * from './authorization/UmaPermissionReader'; export * from './http/output/metadata/UmaTicketMetadataWriter'; -// export * from './identity/configuration/JwksKeyHolder'; -// export * from './identity/configuration/InMemoryJwksKeyHolder'; +export * from './identity/interaction/account/util/AccountSettings'; +export * from './identity/interaction/account/util/UmaAccountStore'; -export * from './identity/interaction/account/util/AccountStore'; -export * from './identity/interaction/account/util/BaseAccountStore'; -export * from './identity/interaction/account/util/LoginStorage'; - -export * from './init/SeededAccountInitializer'; +export * from './init/UmaSeededAccountInitializer'; export * from './server/description/AccountSettingsStorageDescriber'; export * from './server/middleware/JwksHandler'; -export * from './storage/keyvalue/IndexedStorage'; - export * from './uma/ResourceRegistrar'; export * from './uma/UmaClient'; diff --git a/packages/css/src/init/SeededAccountInitializer.ts b/packages/css/src/init/UmaSeededAccountInitializer.ts similarity index 86% rename from packages/css/src/init/SeededAccountInitializer.ts rename to packages/css/src/init/UmaSeededAccountInitializer.ts index 52843483..2cade024 100644 --- a/packages/css/src/init/SeededAccountInitializer.ts +++ b/packages/css/src/init/UmaSeededAccountInitializer.ts @@ -1,9 +1,19 @@ +import { + AccountStore, + createErrorMessage, + getLoggerFor, + Initializer, + PasswordStore, + PodCreator, + URL_SCHEMA +} from '@solid/community-server'; import { readJson } from 'fs-extra'; import { array, object, string } from 'yup'; -import { PasswordStore, PodCreator, URL_SCHEMA, getLoggerFor, - createErrorMessage, Initializer} from '@solid/community-server'; -import { ACCOUNT_SETTINGS_AUTHZ_SERVER, ACCOUNT_SETTINGS_KEYS, - type AccountStore } from '../identity/interaction/account/util/AccountStore' +import { + ACCOUNT_SETTINGS_AUTHZ_SERVER, + ACCOUNT_SETTINGS_KEYS, + UMA_ACCOUNT_STORAGE_TYPE +} from '../identity/interaction/account/util/AccountSettings'; const inSchema = array().of(object({ email: string().trim().email().lowercase().required(), @@ -24,7 +34,7 @@ export interface SeededAccountInitializerArgs { /** * Creates the accounts. */ - accountStore: AccountStore; + accountStore: AccountStore; /** * Adds the login methods. */ @@ -44,10 +54,10 @@ export interface SeededAccountInitializerArgs { * These accounts have exactly 1 email/password login method, and 0 or more pods. * The pod settings that can be defined are identical to those of the {@link CreatePodHandler}. */ -export class SeededAccountInitializer extends Initializer { +export class UmaSeededAccountInitializer extends Initializer { protected readonly logger = getLoggerFor(this); - private readonly accountStore: AccountStore; + private readonly accountStore: AccountStore; private readonly passwordStore: PasswordStore; private readonly podCreator: PodCreator; private readonly configFilePath?: string; diff --git a/packages/css/src/server/description/AccountSettingsStorageDescriber.ts b/packages/css/src/server/description/AccountSettingsStorageDescriber.ts index afc054e7..0444a6a8 100644 --- a/packages/css/src/server/description/AccountSettingsStorageDescriber.ts +++ b/packages/css/src/server/description/AccountSettingsStorageDescriber.ts @@ -1,16 +1,15 @@ import type { NamedNode, Quad, Quad_Object, Term } from '@rdfjs/types'; +import type { AccountSettings, AccountStore, PodStore, ResourceIdentifier } from '@solid/community-server'; +import { StorageDescriber } from '@solid/community-server'; import { DataFactory } from 'n3'; import { stringToTerm } from 'rdf-string'; -import type { PodStore, ResourceIdentifier } from '@solid/community-server'; -import { StorageDescriber } from '@solid/community-server'; -import quad = DataFactory.quad; import namedNode = DataFactory.namedNode; -import type { AccountStore, AccountSettings } from '../../identity/interaction/account/util/AccountStore'; +import quad = DataFactory.quad; /** * Adds triples to the storage description resource, based on the settings of - * the account that created the storage. - * + * the account that created the storage. + * * The resource identifier of the storage is used as subject. */ export class AccountSettingsStorageDescriber extends StorageDescriber { @@ -22,15 +21,15 @@ export class AccountSettingsStorageDescriber extends StorageDescriber { terms: Record, ) { super(); - + const termMap = new Map(); for (const [ predicate, settingsKey ] of Object.entries(terms)) { - + const predTerm = stringToTerm(predicate); if (predTerm.termType !== 'NamedNode') { throw new Error('Predicate needs to be a named node.'); } - + termMap.set(predTerm, settingsKey); } @@ -51,7 +50,7 @@ export class AccountSettingsStorageDescriber extends StorageDescriber { } private async* generateTriples(subject: NamedNode, account: string): AsyncGenerator { - + for (const [ predicate, settingsKey ] of this.terms.entries()) { const settingsValue = await this.accountStore.getSetting(account, settingsKey); diff --git a/packages/css/src/storage/keyvalue/IndexedStorage.ts b/packages/css/src/storage/keyvalue/IndexedStorage.ts deleted file mode 100644 index 18acdeb5..00000000 --- a/packages/css/src/storage/keyvalue/IndexedStorage.ts +++ /dev/null @@ -1,204 +0,0 @@ -/** - * The key that needs to be present in all output results of {@link IndexedStorage}. - */ -export const INDEX_ID_KEY = 'id'; - -/** - * Used to define the value of a key in a type entry of a {@link IndexedStorage}. - * Valid values are `"string"`, `"boolean"`, `"number"` and `"id:TYPE"`, - * with TYPE being one of the types in the definition. - * In the latter case this means that key points to an identifier of the specified type. - * A `[]` can be appended to the type to indicate the value is an array. - * A `?` can be appended to the type to indicate this key is optional. - */ -export type ValueTypeDescription = - `${('string' | 'boolean' | 'number' | (TType extends string ? `${typeof INDEX_ID_KEY}:${TType}` : never))}${ - '[]' | ''}${ - '?' | ''}`; - -/** - * Converts a {@link ValueTypeDescription} to the type it should be interpreted as. - */ -export type ValueType = - (T extends `${infer E extends ValueTypeDescription}[]${'?'|''}` ? ValueType[] : - T extends 'boolean' | 'boolean?' ? boolean : T extends 'number' | 'number?' ? number : string) | - (T extends `${string}?` ? undefined : never); - -/** - * Used to filter on optional keys in a {@link IndexedStorage} definition. - */ -export type OptionalKey = {[K in keyof T ]: T[K] extends `${string}?` ? K : never }[keyof T]; - -/** - * Converts a {@link IndexedStorage} definition of a specific type - * to the typing an object would have that is returned as an output on function calls. - */ -export type TypeObject> = { - -readonly [K in Exclude>]: ValueType; -} & { - -readonly [K in keyof TDesc]?: ValueType; -} & { [INDEX_ID_KEY]: string }; - -/** - * Input expected for `create()` call in {@link IndexedStorage}. - * This is the same as {@link TypeObject} but without the index key. - */ -export type CreateTypeObject> = Omit, typeof INDEX_ID_KEY>; - -/** - * Key of an object that is also a string. - */ -export type StringKey = keyof T & string; - -/** - * The description of a single type in an {@link IndexedStorage}. - */ -export type IndexTypeDescription = Record>; - -/** - * The full description of all the types of an {@link IndexedStorage}. - */ -export type IndexTypeCollection = Record>; - -// This is necessary to prevent infinite recursion in types -type Prev = [never, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ...0[]]; - -/** - * Object that represents a valid query starting from a specific type on an {@link IndexedStorage}. - * The keys of the object need to be one or more keys from the starting type, - * with the values being corresponding valid values of an object of that type. - * If the value definition of a key is one that contains the identifier of a different type, - * the value in the query can also be a nested object that has the same IndexedQuery requirements for that type. - * This can be done recursively. - * - * E.g., if the storage has the following definition: - *```ts - * { - * account: {}, - * pod: { baseUrl: 'string', account: 'id:account' }, - * pod: { owner: 'string', pod: 'id:pod' }, - * } - *``` - * A valid query on the `pod` type could be `{ pod: '123456' }`, - * but also `{ pod: { baseUrl: 'http://example.com/pod/', account: { id: '789' }}}`. - */ -export type IndexedQuery, TType extends keyof T, TDepth extends number = 10> = - [TDepth] extends [never] ? never : - {[K in keyof T[TType] | typeof INDEX_ID_KEY]?: - ValueType | - (T[TType][K] extends `${typeof INDEX_ID_KEY}:${infer U}` ? IndexedQuery : never) - }; - -/* eslint-disable ts/method-signature-style */ -/** - * A storage solution that allows for more complex queries than a key/value storage - * and allows setting indexes on specific keys. - */ -export interface IndexedStorage> { - /** - * Informs the storage of the definition of a specific type. - * A definition is a key/value object with the values being a valid {@link ValueTypeDescription}. - * Generally, this call needs to happen for every type of this storage, - * and before any calls are made to interact with the data. - * - * @param type - The type to define. - * @param description - A description of the values stored in objects of that type. - */ - defineType>(type: TType, description: T[TType]): Promise; - - /** - * Creates an index on a key of the given type, to allow for better queries involving those keys. - * Similar to {@link IndexedStorage.defineType} these calls need to happen first. - * - * @param type - The type to create an index on. - * @param key - The key of that type to create an index on. - */ - createIndex>(type: TType, key: StringKey): Promise; - - /** - * Creates an object of the given type. - * The storage will generate an identifier for the newly created object. - * - * @param type - The type to create. - * @param value - The value to set for the created object. - * - * @returns A representation of the newly created object, including its new identifier. - */ - create>(type: TType, value: CreateTypeObject): Promise>; - - /** - * Returns `true` if the object of the given type with the given identifier exists. - * - * @param type - The type of object to get. - * @param id - The identifier of that object. - * - * @returns Whether this object exists. - */ - has>(type: TType, id: string): Promise; - - /** - * Returns the object of the given type with the given identifier. - * - * @param type - The type of object to get. - * @param id - The identifier of that object. - * - * @returns A representation of the object, or `undefined` if there is no object of that type with that identifier. - */ - get>(type: TType, id: string): Promise | undefined>; - - /** - * Finds all objects matching a specific {@link IndexedQuery}. - * - * @param type - The type of objects to find. - * @param query - The query to execute. - * - * @returns A list of objects matching the query. - */ - find>(type: TType, query: IndexedQuery): Promise<(TypeObject)[]>; - - /** - * Similar to {@link IndexedStorage.find}, but only returns the identifiers of the found objects. - * - * @param type - The type of objects to find. - * @param query - The query to execute. - * - * @returns A list of identifiers of the matching objects. - */ - findIds>(type: TType, query: IndexedQuery): Promise; - - /** - * Sets the value of a specific object. - * The identifier in the object is used to identify the object. - * - * @param type - The type of the object to set. - * @param value - The new value for the object. - */ - set>(type: TType, value: TypeObject): Promise; - - /** - * Sets the value of one specific field in an object. - * - * @param type - The type of the object to update. - * @param id - The identifier of the object to update. - * @param key - The key to update. - * @param value - The new value for the given key. - */ - setField, TKey extends StringKey>( - type: TType, id: string, key: TKey, value: ValueType): Promise; - - /** - * Deletes the given object. - * This will also delete all objects that reference that object if the corresponding key is not optional. - * - * @param type - The type of the object to delete. - * @param id - The identifier of the object. - */ - delete>(type: TType, id: string): Promise; - - /** - * Returns an iterator over all objects of the given type. - * - * @param type - The type to iterate over. - */ - entries>(type: TType): AsyncIterableIterator>; -} diff --git a/packages/css/src/util/OwnerUtil.ts b/packages/css/src/util/OwnerUtil.ts index b67c83a9..d192c59d 100644 --- a/packages/css/src/util/OwnerUtil.ts +++ b/packages/css/src/util/OwnerUtil.ts @@ -1,6 +1,16 @@ -import { KeyValueStorage, PodStore, ResourceIdentifier, StorageLocationStrategy, WrappedSetMultiMap, - getLoggerFor } from '@solid/community-server'; -import { ACCOUNT_SETTINGS_AUTHZ_SERVER, type AccountStore } from '../identity/interaction/account/util/AccountStore'; +import { + AccountStore, + getLoggerFor, + KeyValueStorage, + PodStore, + ResourceIdentifier, + StorageLocationStrategy, + WrappedSetMultiMap +} from '@solid/community-server'; +import { + ACCOUNT_SETTINGS_AUTHZ_SERVER, + UMA_ACCOUNT_STORAGE_TYPE +} from '../identity/interaction/account/util/AccountSettings'; /** * ... @@ -8,15 +18,9 @@ import { ACCOUNT_SETTINGS_AUTHZ_SERVER, type AccountStore } from '../identity/in export class OwnerUtil { protected readonly logger = getLoggerFor(this); - /** - * ... - * - * @param podStore - * @param storageStrategy - */ public constructor( protected podStore: PodStore, - protected accountStore: AccountStore, + protected accountStore: AccountStore, protected storageStrategy: StorageLocationStrategy, protected umaPatStore: KeyValueStorage, protected umaServerURL: string, @@ -59,7 +63,7 @@ export class OwnerUtil { public async findCommonOwner(resources: Iterable): Promise { const resourceSet = new Set(resources); const ownerMap = new WrappedSetMultiMap(); - + for (const target of resourceSet) { const storage = await this.findStorage(target); const owners = await this.findOwners(storage); @@ -82,7 +86,7 @@ export class OwnerUtil { this.logger.verbose(`Using UMA Authorization Server at ${this.umaServerURL} for WebID ${webid}.`) return this.umaServerURL.endsWith('/') ? this.umaServerURL + 'uma' : this.umaServerURL + '/uma' - // Dunno if it makes sense to code this as retrieving it from the WebID at this point? + // Dunno if it makes sense to code this as retrieving it from the WebID at this point? // I think we are far off from dynamically attaching multiple auth servers to a single solid server. // TODO: softcode diff --git a/packages/css/src/util/fetch/BaseFetcher.ts b/packages/css/src/util/fetch/BaseFetcher.ts index c9bb8a4b..99008339 100644 --- a/packages/css/src/util/fetch/BaseFetcher.ts +++ b/packages/css/src/util/fetch/BaseFetcher.ts @@ -1,9 +1,8 @@ -import { fetch as crossFetch } from 'cross-fetch'; import type { FetchParams, Fetcher } from './Fetcher'; /** - * A simple {@link Fetcher} relying on cross-fetch. + * A simple {@link Fetcher} relying on fetch. */ export class BaseFetcher implements Fetcher { - fetch(...args: FetchParams): Promise { return crossFetch(...args) } + fetch(...args: FetchParams): Promise { return fetch(...args) } } diff --git a/packages/css/src/util/fetch/SignedFetcher.ts b/packages/css/src/util/fetch/SignedFetcher.ts index fe0d0dd5..2fe262d0 100644 --- a/packages/css/src/util/fetch/SignedFetcher.ts +++ b/packages/css/src/util/fetch/SignedFetcher.ts @@ -18,7 +18,7 @@ const algMap = { } /** - * A {@link Fetcher} wrapper that signes requests. + * A {@link Fetcher} wrapper that signes requests. */ export class SignedFetcher implements Fetcher { protected readonly logger = getLoggerFor(this); @@ -26,10 +26,10 @@ export class SignedFetcher implements Fetcher { constructor( protected fetcher: Fetcher, protected baseUrl: string, - protected keyGen: JwkGenerator, + protected keyGen: JwkGenerator, ) {} - public async fetch(input: NodeJS.fetch.RequestInfo, init?: RequestInit): Promise { + public async fetch(input: RequestInfo, init?: RequestInit): Promise { const jwk = await this.keyGen.getPrivateKey(); const { alg, kid } = jwk; @@ -59,7 +59,7 @@ export class SignedFetcher implements Fetcher { request.headers['Authorization'] = `HttpSig cred="${this.baseUrl}"`; const signed = await httpbis.signMessage({ key, paramValues: { keyid: 'TODO' } }, request); - + return await this.fetcher.fetch(url, signed); } } diff --git a/packages/ucp/package.json b/packages/ucp/package.json index 02d37489..c2726fe4 100644 --- a/packages/ucp/package.json +++ b/packages/ucp/package.json @@ -33,7 +33,7 @@ "private": true, "packageManager": "yarn@4.0.2", "engines": { - "node": ">=18.18", + "node": ">=20.0", "yarn": ">=4.0" }, "type": "commonjs", @@ -64,22 +64,12 @@ }, "dependencies": { "@smessie/readable-web-to-node-stream": "^3.0.3", - "@types/n3": "^1.16.3", + "@types/n3": "^1.16.4", "koreografeye": "^0.4.8", - "n3": "^1.17.1", + "n3": "^1.17.2", "rdf-parse": "^2.3.3", "rdf-store-stream": "^2.0.1" }, - "devDependencies": { - "@solid/community-server": "7.0.3", - "@types/jest": "^29.5.12", - "@types/node": "^20.11.25", - "jest": "^29.7.0", - "jest-rdf": "^1.8.1", - "ts-jest": "^29.1.2", - "ts-node": "^10.9.1", - "typescript": "^5.3.3" - }, "lsd:module": "https://linkedsoftwaredependencies.org/bundles/npm/@solidlab/ucp", "lsd:components": "dist/components/components.jsonld", "lsd:contexts": { diff --git a/packages/ucp/src/Explanation.ts b/packages/ucp/src/Explanation.ts index 99cf9bc4..0d3bae4e 100644 --- a/packages/ucp/src/Explanation.ts +++ b/packages/ucp/src/Explanation.ts @@ -1,6 +1,6 @@ import { Store, DataFactory } from "n3"; +import { randomUUID } from 'node:crypto'; import { UconRequest, createContext } from "./Request" -import { v4 as uuidv4 } from 'uuid'; import { ACCESS_MODES_ALLOWED } from "./util/Constants"; import { SimplePolicy } from "./policy/UsageControlPolicy"; import { storeToString } from "./util/Conversion"; @@ -93,7 +93,7 @@ export function explanationToRdf(explanation: Explanation): Store { // conclusions for (const conclusion of explanation.conclusions) { - const conclusionIri = baseIRI + uuidv4(); + const conclusionIri = baseIRI + randomUUID(); const conclusionPred = namedNode(baseIRI + 'conclusion'); const conclusionNode = namedNode(conclusionIri); store.addQuad(explanationNode, conclusionPred, conclusionNode); @@ -110,8 +110,8 @@ export function explanationToRdf(explanation: Explanation): Store { } /** * Serialize the request together with a set of ucon rules and set of N3 rules interpreting into a single string. - * - * This string can be given to an N3 reasoning engine. + * + * This string can be given to an N3 reasoning engine. * When there is a conclusion, one or multiple rule activations will occur. (rule must be seen in the context of a policy rule) * Each rule activation contains permissions granted. * @param policies @@ -135,10 +135,10 @@ export function serializePremises(policies: SimplePolicy[], request: UconRequest * - request * - ucon rules set * - n3 intepretation rules - * @param explanation - * @param uconRules - * @param n3InterpretationRules - * @returns + * @param explanation + * @param uconRules + * @param n3InterpretationRules + * @returns */ export function serializeFullExplanation(explanation: Explanation, uconRules: Store, n3InterpretationRules: string): string { const explanationString = storeToString(explanationToRdf(explanation)); diff --git a/packages/ucp/src/policy/ODRL.ts b/packages/ucp/src/policy/ODRL.ts index 21d15b70..fda60e16 100644 --- a/packages/ucp/src/policy/ODRL.ts +++ b/packages/ucp/src/policy/ODRL.ts @@ -1,17 +1,17 @@ import { DataFactory, Quad, Store } from "n3"; -import { v4 as uuidv4 } from 'uuid'; +import { randomUUID } from 'node:crypto'; import { ODRL, RDF, XSD } from "../util/Vocabularies"; import { SimplePolicy, UCPConstraint, UCPPolicy, UCPRule } from "./UsageControlPolicy"; const { quad, namedNode, literal } = DataFactory /** * Create a simple ODRL policy with an agreement and one rule - * @param policy - * @param policyIRI - * @returns + * @param policy + * @param policyIRI + * @returns */ export function basicPolicy(policy: UCPPolicy, policyIRI?: string): SimplePolicy { - policyIRI = policyIRI ?? "urn:ucp:policy:" + uuidv4(); + policyIRI = policyIRI ?? "urn:ucp:policy:" + randomUUID(); const store = new Store(); const ruleIRIs: string[] = [] if (policy.type) { @@ -33,13 +33,13 @@ export function basicPolicy(policy: UCPPolicy, policyIRI?: string): SimplePolicy /** * Convert an Usage Control Rule to ODRL quads - * @param rule - * @param policyIRI - * @returns + * @param rule + * @param policyIRI + * @returns */ export function createRuleQuads(rule: UCPRule, policyIRI?: string): { quads: Quad[], ruleIRI: string } { const quads: Quad[] = [] - const ruleIRI = "urn:ucp:rule:" + uuidv4(); + const ruleIRI = "urn:ucp:rule:" + randomUUID(); quads.push(quad(namedNode(ruleIRI), ODRL.terms.action, namedNode(rule.action))) quads.push(quad(namedNode(ruleIRI), ODRL.terms.target, namedNode(rule.resource))) quads.push(quad(namedNode(ruleIRI), ODRL.terms.assignee, namedNode(rule.requestingParty))) @@ -67,21 +67,21 @@ export function createRuleQuads(rule: UCPRule, policyIRI?: string): { quads: Qua /** * Convert an Usage Control Constraint to ODRL quads - * @param rule - * @param policyIRI - * @returns + * @param rule + * @param policyIRI + * @returns */ export function createConstraintQuads( - constraint: UCPConstraint, + constraint: UCPConstraint, ruleIRI?: string ): { quads: Quad[], constraintIRI: string } { const quads: Quad[] = [] - const constraintIRI = "urn:ucp:constraint:" + uuidv4(); + const constraintIRI = "urn:ucp:constraint:" + randomUUID(); switch (constraint.type) { case "temporal": // maybe have as type something more semantically defined? quads.push(quad(namedNode(constraintIRI), ODRL.terms.leftOperand, ODRL.terms.dateTime)); quads.push(quad(namedNode(constraintIRI), ODRL.terms.operator, namedNode(constraint.operator))); - quads.push(quad(namedNode(constraintIRI), ODRL.terms.rightOperand, + quads.push(quad(namedNode(constraintIRI), ODRL.terms.rightOperand, literal((constraint.value as Date).toISOString(), XSD.terms.dateTime))); if (ruleIRI) { quads.push(quad(namedNode(ruleIRI), ODRL.terms.constraint, namedNode(constraintIRI))) @@ -104,6 +104,3 @@ export function createConstraintQuads( constraintIRI } } - - - diff --git a/packages/uma/.componentsignore b/packages/uma/.componentsignore index 194ab69c..7303a118 100644 --- a/packages/uma/.componentsignore +++ b/packages/uma/.componentsignore @@ -3,6 +3,7 @@ "AccessToken", "Authorization", "Authorizer", + "Buffer", "Error", "EventEmitter", "Map", @@ -13,5 +14,6 @@ "ReType", "ScopeDescription", "Ticket", - "URL" + "URL", + "WeakMap" ] diff --git a/packages/uma/bin/demo.ts b/packages/uma/bin/demo.ts index 397c71ac..c4414feb 100644 --- a/packages/uma/bin/demo.ts +++ b/packages/uma/bin/demo.ts @@ -1,8 +1,6 @@ import * as path from 'path'; import { ComponentsManager } from 'componentsjs'; -import { NodeHttpServer } from '../src/util/http/server/NodeHttpServer'; -import { setLogger } from '../src/util/logging/LoggerUtils'; -import { WinstonLogger } from '../src/util/logging/WinstonLogger'; +import { ServerInitializer, setGlobalLoggerFactory, WinstonLoggerFactory } from '@solid/community-server'; const protocol = 'http'; const host = 'localhost'; @@ -19,7 +17,7 @@ export const launch: () => Promise = async () => { variables['urn:uma:variables:host'] = host; variables['urn:uma:variables:protocol'] = protocol; variables['urn:uma:variables:baseUrl'] = baseUrl; - + // variables['urn:uma:variables:policyDir'] = path.join(rootDir, './config/rules/policy'); variables['urn:uma:variables:rulesDir'] = path.join(rootDir, './config/rules/n3'); @@ -29,7 +27,7 @@ export const launch: () => Promise = async () => { const mainModulePath = variables['urn:uma:variables:mainModulePath']; const configPath = variables['urn:uma:variables:customConfigPath']; - setLogger(new WinstonLogger('test-logger', 60, 30)); + setGlobalLoggerFactory(new WinstonLoggerFactory('info')); const manager = await ComponentsManager.build({ mainModulePath, @@ -39,8 +37,8 @@ export const launch: () => Promise = async () => { await manager.configRegistry.register(configPath); - const umaServer: NodeHttpServer = await manager.instantiate('urn:uma:default:NodeHttpServer',{variables}); - umaServer.start(); + const umaServer: ServerInitializer = await manager.instantiate('urn:uma:default:NodeHttpServer',{variables}); + await umaServer.handleSafe(); }; diff --git a/packages/uma/bin/main.ts b/packages/uma/bin/main.ts index 073b0a1c..2e32ab73 100644 --- a/packages/uma/bin/main.ts +++ b/packages/uma/bin/main.ts @@ -1,8 +1,6 @@ import * as path from 'path'; import { ComponentsManager } from 'componentsjs'; -import { NodeHttpServer } from '../src/util/http/server/NodeHttpServer'; -import { setLogger } from '../src/util/logging/LoggerUtils'; -import { WinstonLogger } from '../src/util/logging/WinstonLogger'; +import { ServerInitializer, setGlobalLoggerFactory, WinstonLoggerFactory } from '@solid/community-server'; const protocol = 'http'; const host = 'localhost'; @@ -19,7 +17,7 @@ export const launch: () => Promise = async () => { variables['urn:uma:variables:host'] = host; variables['urn:uma:variables:protocol'] = protocol; variables['urn:uma:variables:baseUrl'] = baseUrl; - + variables['urn:uma:variables:policyDir'] = path.join(rootDir, './config/rules/policy'); variables['urn:uma:variables:rulesDir'] = path.join(rootDir, './config/rules/n3'); @@ -29,7 +27,7 @@ export const launch: () => Promise = async () => { const mainModulePath = variables['urn:uma:variables:mainModulePath']; const configPath = variables['urn:uma:variables:customConfigPath']; - setLogger(new WinstonLogger('test-logger', 60, 30)); + setGlobalLoggerFactory(new WinstonLoggerFactory('info')); const manager = await ComponentsManager.build({ mainModulePath, @@ -39,8 +37,8 @@ export const launch: () => Promise = async () => { await manager.configRegistry.register(configPath); - const umaServer: NodeHttpServer = await manager.instantiate('urn:uma:default:NodeHttpServer',{variables}); - umaServer.start(); + const umaServer: ServerInitializer = await manager.instantiate('urn:uma:default:NodeHttpServer',{variables}); + await umaServer.handleSafe(); }; diff --git a/packages/uma/config/default.json b/packages/uma/config/default.json index 00532cca..a0ed3888 100644 --- a/packages/uma/config/default.json +++ b/packages/uma/config/default.json @@ -1,6 +1,7 @@ { "@context": [ - "https://linkedsoftwaredependencies.org/bundles/npm/@solidlab/uma/^0.0.0/components/context.jsonld" + "https://linkedsoftwaredependencies.org/bundles/npm/@solidlab/uma/^0.0.0/components/context.jsonld", + "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^7.0.0/components/context.jsonld" ], "import": [ "sai-uma:config/credentials/verifiers/default.json", @@ -25,40 +26,84 @@ "@graph": [ { "@id": "urn:uma:default:NodeHttpServer", - "@type": "NodeHttpServer", + "@type": "ServerInitializer", "port": { "@id": "urn:uma:variables:port" }, - "host": { - "@id": "urn:uma:variables:host" + "serverFactory": { + "@id": "urn:uma:default:ServerFactory", + "@type": "BaseServerFactory", + "configurator": { + "comment": "Handles all request events from the server.", + "@id": "urn:uma:default:HandlerServerConfigurator", + "@type": "HandlerServerConfigurator", + "handler": { + "@id": "urn:uma:default:NodeHttpRequestResponseHandler" + } + } + } + }, + { + "@id": "urn:uma:default:HttpHandler", + "@type": "SequenceHandler", + "handlers": [ + { + "comment": "Adds all the necessary CORS headers.", + "@id": "urn:uma:default:CorsHandler", + "@type": "CorsHandler", + "options_methods": [ + "GET", + "HEAD", + "OPTIONS", + "POST", + "PUT", + "PATCH", + "DELETE" + ], + "options_credentials": true, + "options_preflightContinue": false, + "options_exposedHeaders": [ + "Allow", + "ETag", + "Last-Modified", + "Link", + "Location", + "Updates-Via", + "Www-Authenticate" + ] + }, + { + "@id": "urn:uma:default:NodeHttpRequestResponseHandler" + } + ] + }, + { + "@id": "urn:uma:default:NodeHttpRequestResponseHandler", + "@type": "NodeHttpRequestResponseHandler", + "targetExtractor": { + "@type": "BaseTargetExtractor", + "includeQueryString": true }, - "nodeHttpStreamsHandler": { - "@id": "urn:uma:default:NodeHttpRequestResponseHandler", - "@type": "NodeHttpRequestResponseHandler", - "httpHandler": { - "@id": "urn:uma:default:CorsRequestHandler", - "@type": "CorsRequestHandler", + "httpHandler": { + "@id": "urm:uma:default:JsonHttpErrorHandler", + "@type": "JsonHttpErrorHandler", + "handler": { + "@id": "urm:uma:default:JsonFormHttpHandler", + "@type": "JsonFormHttpHandler", "handler": { "@id": "urn:uma:default:RoutedHttpRequestHandler", "@type": "RoutedHttpRequestHandler", - "handlerControllerList": [ - { - "@id": "urn:uma:default:HttpHandlerController", - "@type": "HttpHandlerController", - "label": "ControllerList", - "routes": [ - { "@id": "urn:uma:default:UmaConfigRoute" }, - { "@id": "urn:uma:default:JwksRoute" }, - { "@id": "urn:uma:default:DemoTokenRoute" }, - { "@id": "urn:uma:default:PermissionRegistrationRoute" }, - { "@id": "urn:uma:default:ResourceRegistrationRoute" }, - { "@id": "urn:uma:default:ResourceRegistrationOpsRoute" }, - { "@id": "urn:uma:default:IntrospectionRoute" }, - { "@id": "urn:uma:default:LogRoute" }, - { "@id": "urn:uma:default:VCRoute" }, - { "@id": "urn:uma:default:ContractRoute" } - ] - } + "routes": [ + { "@id": "urn:uma:default:UmaConfigRoute" }, + { "@id": "urn:uma:default:JwksRoute" }, + { "@id": "urn:uma:default:DemoTokenRoute" }, + { "@id": "urn:uma:default:PermissionRegistrationRoute" }, + { "@id": "urn:uma:default:ResourceRegistrationRoute" }, + { "@id": "urn:uma:default:ResourceRegistrationOpsRoute" }, + { "@id": "urn:uma:default:IntrospectionRoute" }, + { "@id": "urn:uma:default:LogRoute" }, + { "@id": "urn:uma:default:VCRoute" }, + { "@id": "urn:uma:default:ContractRoute" } ], "defaultHandler": { "@type": "DefaultRequestHandler" diff --git a/packages/uma/config/resources/storage/default.json b/packages/uma/config/resources/storage/default.json index 56ae4cf1..815a7b63 100644 --- a/packages/uma/config/resources/storage/default.json +++ b/packages/uma/config/resources/storage/default.json @@ -1,11 +1,11 @@ { "@context": [ - "https://linkedsoftwaredependencies.org/bundles/npm/@solidlab/uma/^0.0.0/components/context.jsonld" + "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^7.0.0/components/context.jsonld" ], "@graph": [ { "@id": "urn:uma:default:ResourceRegistrationStore", - "@type": "MemoryStore" + "@type": "MemoryMapStorage" } ] -} \ No newline at end of file +} diff --git a/packages/uma/config/routes/contract.json b/packages/uma/config/routes/contract.json index f019a947..011a2907 100644 --- a/packages/uma/config/routes/contract.json +++ b/packages/uma/config/routes/contract.json @@ -6,13 +6,7 @@ { "@id": "urn:uma:default:ContractRoute", "@type": "HttpHandlerRoute", - "operations": [ - { - "@type": "HttpHandlerOperation", - "method": "GET", - "publish": true - } - ], + "methods": [ "GET" ], "handler": { "@type": "ContractRequestHandler", "baseUrl": { "@id": "urn:uma:variables:baseUrl" } @@ -20,4 +14,4 @@ "path": "/uma/contract" } ] -} \ No newline at end of file +} diff --git a/packages/uma/config/routes/discovery.json b/packages/uma/config/routes/discovery.json index 86dc5627..c857f518 100644 --- a/packages/uma/config/routes/discovery.json +++ b/packages/uma/config/routes/discovery.json @@ -6,13 +6,7 @@ { "@id": "urn:uma:default:UmaConfigRoute", "@type": "HttpHandlerRoute", - "operations": [ - { - "@type": "HttpHandlerOperation", - "method": "GET", - "publish": true - } - ], + "methods": [ "GET" ], "handler": { "@type": "ConfigRequestHandler", "baseUrl": { "@id": "urn:uma:variables:baseUrl" } @@ -20,4 +14,4 @@ "path": "/uma/.well-known/uma2-configuration" } ] -} \ No newline at end of file +} diff --git a/packages/uma/config/routes/introspection.json b/packages/uma/config/routes/introspection.json index c323d717..3827d8de 100644 --- a/packages/uma/config/routes/introspection.json +++ b/packages/uma/config/routes/introspection.json @@ -6,22 +6,13 @@ { "@id": "urn:uma:default:IntrospectionRoute", "@type": "HttpHandlerRoute", - "operations": [ - { - "@type": "HttpHandlerOperation", - "method": "POST", - "publish": true - } - ], + "methods": [ "POST" ], "handler": { - "@type": "JsonHttpErrorHandler", - "nestedHandler": { - "@type": "IntrospectionHandler", - "tokenStore": { "@id": "urn:uma:default:TokenStore" }, - "jwtTokenFactory": { "@id": "urn:uma:default:TokenFactory" } - } + "@type": "IntrospectionHandler", + "tokenStore": { "@id": "urn:uma:default:TokenStore" }, + "jwtTokenFactory": { "@id": "urn:uma:default:TokenFactory" } }, "path": "/uma/introspect" } ] -} \ No newline at end of file +} diff --git a/packages/uma/config/routes/keys.json b/packages/uma/config/routes/keys.json index 14262316..491e3bbe 100644 --- a/packages/uma/config/routes/keys.json +++ b/packages/uma/config/routes/keys.json @@ -17,13 +17,7 @@ { "@id": "urn:uma:default:JwksRoute", "@type": "HttpHandlerRoute", - "operations": [ - { - "@type": "HttpHandlerOperation", - "method": "GET", - "publish": true - } - ], + "methods": [ "GET" ], "handler": { "@type": "JwksRequestHandler", "generator": { "@id": "urn:uma:default:JwkGenerator" } diff --git a/packages/uma/config/routes/log.json b/packages/uma/config/routes/log.json index 5ad0fe7d..069ac3f3 100644 --- a/packages/uma/config/routes/log.json +++ b/packages/uma/config/routes/log.json @@ -6,13 +6,7 @@ { "@id": "urn:uma:default:LogRoute", "@type": "HttpHandlerRoute", - "operations": [ - { - "@type": "HttpHandlerOperation", - "method": "GET", - "publish": true - } - ], + "methods": [ "GET" ], "handler": { "@type": "LogRequestHandler", "baseUrl": { "@id": "urn:uma:variables:baseUrl" } @@ -20,4 +14,4 @@ "path": "/uma/log" } ] -} \ No newline at end of file +} diff --git a/packages/uma/config/routes/resources.json b/packages/uma/config/routes/resources.json index a81b5913..49af99c1 100644 --- a/packages/uma/config/routes/resources.json +++ b/packages/uma/config/routes/resources.json @@ -11,28 +11,16 @@ { "@id": "urn:uma:default:ResourceRegistrationRoute", "@type": "HttpHandlerRoute", - "operations": [ - { - "@type": "HttpHandlerOperation", - "method": "POST", - "publish": true - } - ], + "methods": [ "POST" ], "handler": { "@id": "urn:uma:default:ResourceRegistrationHandler" }, "path": "/uma/resources" }, { "@id": "urn:uma:default:ResourceRegistrationOpsRoute", "@type": "HttpHandlerRoute", - "operations": [ - { - "@type": "HttpHandlerOperation", - "method": "DELETE", - "publish": true - } - ], + "methods": [ "DELETE" ], "handler": { "@id": "urn:uma:default:ResourceRegistrationHandler" }, - "path": "/uma/resources/:id" + "path": "/uma/resources/{id}" } ] } diff --git a/packages/uma/config/routes/tickets.json b/packages/uma/config/routes/tickets.json index 906393eb..14526171 100644 --- a/packages/uma/config/routes/tickets.json +++ b/packages/uma/config/routes/tickets.json @@ -6,20 +6,11 @@ { "@id": "urn:uma:default:PermissionRegistrationRoute", "@type": "HttpHandlerRoute", - "operations": [ - { - "@type": "HttpHandlerOperation", - "method": "POST", - "publish": true - } - ], + "methods": [ "POST" ], "handler": { - "@type": "JsonHttpErrorHandler", - "nestedHandler": { - "@type": "TicketRequestHandler", - "ticketingStrategy": { "@id": "urn:uma:default:TicketingStrategy" }, - "ticketStore": { "@id": "urn:uma:default:TicketStore" } - } + "@type": "TicketRequestHandler", + "ticketingStrategy": { "@id": "urn:uma:default:TicketingStrategy" }, + "ticketStore": { "@id": "urn:uma:default:TicketStore" } }, "path": "/uma/ticket" } diff --git a/packages/uma/config/routes/tokens.json b/packages/uma/config/routes/tokens.json index 63052c35..8d935fe2 100644 --- a/packages/uma/config/routes/tokens.json +++ b/packages/uma/config/routes/tokens.json @@ -6,21 +6,12 @@ { "@id": "urn:uma:default:TokenRoute", "@type": "HttpHandlerRoute", - "operations": [ - { - "@type": "HttpHandlerOperation", - "method": "POST", - "publish": true - } - ], + "methods": [ "POST" ], "handler": { - "@type": "JsonHttpErrorHandler", - "nestedHandler": { - "@type": "TokenRequestHandler", - "negotiator": { "@id": "urn:uma:default:Negotiator" } - } + "@type": "TokenRequestHandler", + "negotiator": { "@id": "urn:uma:default:Negotiator" } }, "path": "/uma/token" } ] -} \ No newline at end of file +} diff --git a/packages/uma/config/routes/tokens_contract.json b/packages/uma/config/routes/tokens_contract.json index 3c62ad7f..5100d97b 100644 --- a/packages/uma/config/routes/tokens_contract.json +++ b/packages/uma/config/routes/tokens_contract.json @@ -6,21 +6,12 @@ { "@id": "urn:uma:default:DemoTokenRoute", "@type": "HttpHandlerRoute", - "operations": [ - { - "@type": "HttpHandlerOperation", - "method": "POST", - "publish": true - } - ], + "methods": [ "POST" ], "handler": { - "@type": "JsonHttpErrorHandler", - "nestedHandler": { - "@type": "TokenRequestHandler", - "negotiator": { "@id": "urn:uma:default:DemoNegotiator" } - } + "@type": "TokenRequestHandler", + "negotiator": { "@id": "urn:uma:default:DemoNegotiator" } }, "path": "/uma/token" } ] -} \ No newline at end of file +} diff --git a/packages/uma/config/routes/vc.json b/packages/uma/config/routes/vc.json index c0dc4141..b5ece17c 100644 --- a/packages/uma/config/routes/vc.json +++ b/packages/uma/config/routes/vc.json @@ -6,17 +6,11 @@ { "@id": "urn:uma:default:VCRoute", "@type": "HttpHandlerRoute", - "operations": [ - { - "@type": "HttpHandlerOperation", - "method": "POST", - "publish": true - } - ], + "methods": [ "POST" ], "handler": { "@type": "VCRequestVerificationHandler" }, "path": "/uma/vc/verify" } ] - } \ No newline at end of file + } diff --git a/packages/uma/config/tickets/storage/default.json b/packages/uma/config/tickets/storage/default.json index 39c553aa..d6038cc2 100644 --- a/packages/uma/config/tickets/storage/default.json +++ b/packages/uma/config/tickets/storage/default.json @@ -1,11 +1,11 @@ { "@context": [ - "https://linkedsoftwaredependencies.org/bundles/npm/@solidlab/uma/^0.0.0/components/context.jsonld" + "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^7.0.0/components/context.jsonld" ], "@graph": [ { "@id": "urn:uma:default:TicketStore", - "@type": "MemoryStore" + "@type": "MemoryMapStorage" } ] -} \ No newline at end of file +} diff --git a/packages/uma/config/tokens/storage/default.json b/packages/uma/config/tokens/storage/default.json index cfa13713..70b70da5 100644 --- a/packages/uma/config/tokens/storage/default.json +++ b/packages/uma/config/tokens/storage/default.json @@ -1,11 +1,11 @@ { "@context": [ - "https://linkedsoftwaredependencies.org/bundles/npm/@solidlab/uma/^0.0.0/components/context.jsonld" + "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^7.0.0/components/context.jsonld" ], "@graph": [ { "@id": "urn:uma:default:TokenStore", - "@type": "MemoryStore" + "@type": "MemoryMapStorage" } ] -} \ No newline at end of file +} diff --git a/packages/uma/package.json b/packages/uma/package.json index 5a374f8b..84d3c918 100644 --- a/packages/uma/package.json +++ b/packages/uma/package.json @@ -34,7 +34,7 @@ "private": true, "packageManager": "yarn@4.0.2", "engines": { - "node": ">=18.18", + "node": ">=20.0", "yarn": ">=4.0" }, "type": "commonjs", @@ -62,27 +62,20 @@ "dependencies": { "@httpland/authorization-parser": "^1.1.0", "@solid/access-token-verifier": "^1.2.0", - "@solid/community-server": "^7.0.4", + "@solid/community-server": "^7.1.7", "@solidlab/ucp": "workspace:^", - "clone": "^2.1.2", - "componentsjs": "5.4.2", - "cross-fetch": "^4.0.0", + "@types/n3": "^1.16.4", + "componentsjs": "^5.5.1", "get-jwks": "^9.0.1", "http-message-signatures": "^1.0.4", - "jose": "^4.5.1", + "jose": "^5.2.2", "koreografeye": "^0.4.8", "logform": "^2.6.0", "n3": "^1.17.2", "ts-node": "^10.9.2", - "uuid": "^9.0.1", + "uri-template-lite": "^23.4.0", "winston": "^3.11.0" }, - "devDependencies": { - "@types/clone": "^2.1.4", - "@types/n3": "^1.16.4", - "@types/node": "^18.18.11", - "@types/uuid": "^8.3.4" - }, "jest": { "preset": "ts-jest", "testEnvironment": "node", diff --git a/packages/uma/src/credentials/verify/JwtVerifier.ts b/packages/uma/src/credentials/verify/JwtVerifier.ts index bf194eae..4e16cef2 100644 --- a/packages/uma/src/credentials/verify/JwtVerifier.ts +++ b/packages/uma/src/credentials/verify/JwtVerifier.ts @@ -1,5 +1,4 @@ -import { Logger } from '../../util/logging/Logger'; -import { getLoggerFor } from '../../util/logging/LoggerUtils'; +import { getLoggerFor } from '@solid/community-server'; import { Verifier } from './Verifier'; import { ClaimSet } from '../ClaimSet'; import { Credential } from "../Credential"; @@ -12,7 +11,7 @@ import buildGetJwks from 'get-jwks'; * without performing any further verification. */ export class JwtVerifier implements Verifier { - protected readonly logger: Logger = getLoggerFor(this); + protected readonly logger = getLoggerFor(this); protected jwks = buildGetJwks(); constructor( @@ -23,18 +22,18 @@ export class JwtVerifier implements Verifier { /** @inheritdoc */ public async verify(credential: Credential): Promise { - this.logger.debug("Verifying credential", credential) + this.logger.debug(`Verifying credential ${JSON.stringify(credential)}`); if (credential.format !== JWT) { throw new Error(`Token format '${credential.format}' does not match this processor's format.`); } const claims = decodeJwt(credential.token); - + if (this.verifyJwt) { if (!claims.iss) { throw new Error(`JWT should contain 'iss' claim.`); } - + const params = decodeProtectedHeader(credential.token); if (!params.alg) { @@ -58,7 +57,7 @@ export class JwtVerifier implements Verifier { if (this.errorOnExtraClaims) throw new Error(`Claim '${claim}' not allowed.`); delete claims[claim]; - } + } this.logger.debug(`Returning discovered claims: ${JSON.stringify(claims)}`) return claims; diff --git a/packages/uma/src/credentials/verify/SolidOidcVerifier.ts b/packages/uma/src/credentials/verify/SolidOidcVerifier.ts index 71a57253..976f4ae0 100644 --- a/packages/uma/src/credentials/verify/SolidOidcVerifier.ts +++ b/packages/uma/src/credentials/verify/SolidOidcVerifier.ts @@ -1,5 +1,4 @@ -import { Logger } from '../../util/logging/Logger'; -import { getLoggerFor } from '../../util/logging/LoggerUtils'; +import { getLoggerFor } from '@solid/community-server'; import { Verifier } from './Verifier'; import { ClaimSet } from '../ClaimSet'; import { Credential } from "../Credential"; @@ -11,27 +10,27 @@ import { CLIENTID, WEBID } from '../Claims'; * A Verifier for OIDC ID Tokens. */ export class SolidOidcVerifier implements Verifier { - protected readonly logger: Logger = getLoggerFor(this); + protected readonly logger = getLoggerFor(this); private readonly verifyToken = createSolidTokenVerifier(); /** @inheritdoc */ public async verify(credential: Credential): Promise { - this.logger.debug("Verifying credential", credential) + this.logger.debug(`Verifying credential ${JSON.stringify(credential)}`); if (credential.format !== OIDC) { throw new Error(`Token format ${credential.format} does not match this processor's format.`); } try { const claims = await this.verifyToken(`Basic ${credential.token}`); - - this.logger.info(`Authenticated via a Solid OIDC.`, claims); + + this.logger.info(`Authenticated via a Solid OIDC. ${JSON.stringify(claims)}`); return ({ // TODO: keep issuer (and other metadata) for validation ?? [WEBID]: claims.webid, ...claims.client_id && { [CLIENTID]: claims.client_id } }); - + } catch (error: unknown) { const message = `Error verifying OIDC ID Token: ${(error as Error).message}`; diff --git a/packages/uma/src/credentials/verify/TypedVerifier.ts b/packages/uma/src/credentials/verify/TypedVerifier.ts index dc8805e0..f1441340 100644 --- a/packages/uma/src/credentials/verify/TypedVerifier.ts +++ b/packages/uma/src/credentials/verify/TypedVerifier.ts @@ -1,19 +1,17 @@ -import { BadRequestHttpError } from "../../util/http/errors/BadRequestHttpError"; -import { Logger } from "../../util/logging/Logger"; -import { getLoggerFor } from "../../util/logging/LoggerUtils"; +import { BadRequestHttpError, getLoggerFor } from '@solid/community-server'; import { ClaimSet } from "../ClaimSet"; import { Credential } from "../Credential"; import { Verifier } from "./Verifier"; export class TypedVerifier implements Verifier { - private readonly logger: Logger = getLoggerFor(this); + private readonly logger = getLoggerFor(this); constructor(protected verifiers: Record) {} public async verify(credential: Credential): Promise { const verifier = this.verifiers[credential.format]; - this.logger.debug("Verifying credential with typed verifier", credential) - + this.logger.debug(`Verifying credential with typed verifier ${JSON.stringify(credential)}`); + if (!verifier) { this.logger.warn('The provided "claim_token_format" is not supported.'); throw new BadRequestHttpError('The provided "claim_token_format" is not supported.'); @@ -21,4 +19,4 @@ export class TypedVerifier implements Verifier { return verifier.verify(credential); } -} \ No newline at end of file +} diff --git a/packages/uma/src/credentials/verify/UnsecureVerifier.ts b/packages/uma/src/credentials/verify/UnsecureVerifier.ts index 9d433641..b4a0b5cf 100644 --- a/packages/uma/src/credentials/verify/UnsecureVerifier.ts +++ b/packages/uma/src/credentials/verify/UnsecureVerifier.ts @@ -1,5 +1,4 @@ -import { Logger } from '../../util/logging/Logger'; -import { getLoggerFor } from '../../util/logging/LoggerUtils'; +import { getLoggerFor } from '@solid/community-server'; import { Verifier } from './Verifier'; import { ClaimSet } from '../ClaimSet'; import { Credential } from "../Credential"; @@ -11,7 +10,7 @@ import { CLIENTID, WEBID } from '../Claims'; * without performing any further verification. */ export class UnsecureVerifier implements Verifier { - protected readonly logger: Logger = getLoggerFor(this); + protected readonly logger = getLoggerFor(this); constructor() { this.logger.warn("You are using an UnsecureVerifier. DO NOT USE THIS IN PRODUCTION !!!"); @@ -19,7 +18,7 @@ export class UnsecureVerifier implements Verifier { /** @inheritdoc */ public async verify(credential: Credential): Promise { - this.logger.debug("Verifying credential", credential) + this.logger.debug(`Verifying credential ${JSON.stringify(credential)}`); if (credential.format !== UNSECURE) { throw new Error(`Token format ${credential.format} does not match this processor's format.`); } @@ -35,14 +34,14 @@ export class UnsecureVerifier implements Verifier { [WEBID]: new URL(decodeURIComponent(raw[0])).toString(), [CLIENTID]: raw.length === 2 && new URL(decodeURIComponent(raw[1])).toString() }; - - this.logger.info(`Authenticated as via unsecure verifier.`, claims); - + + this.logger.info(`Authenticated as via unsecure verifier. ${JSON.stringify(claims)}`); + return claims; - + } catch (error: unknown) { const message = `Error verifying Access Token via WebID: ${(error as Error).message}`; - + this.logger.debug(message); throw new Error(message); } diff --git a/packages/uma/src/dialog/BaseNegotiator.ts b/packages/uma/src/dialog/BaseNegotiator.ts index 35417c3d..a800ae81 100644 --- a/packages/uma/src/dialog/BaseNegotiator.ts +++ b/packages/uma/src/dialog/BaseNegotiator.ts @@ -1,18 +1,19 @@ -import { BadRequestHttpError } from '../util/http/errors/BadRequestHttpError'; +import { randomUUID } from 'node:crypto'; import { Ticket } from '../ticketing/Ticket'; import { Verifier } from '../credentials/verify/Verifier'; import { TokenFactory } from '../tokens/TokenFactory'; import { Negotiator } from './Negotiator'; -import { getLoggerFor } from '../util/logging/LoggerUtils'; -import { Logger } from '../util/logging/Logger'; import { NeedInfoError } from '../errors/NeedInfoError'; import { DialogInput } from './Input'; import { DialogOutput } from './Output'; import { reType } from '../util/ReType'; -import { KeyValueStore } from '../util/storage/models/KeyValueStore'; import { TicketingStrategy } from '../ticketing/strategy/TicketingStrategy'; -import { v4 } from 'uuid'; -import { ForbiddenHttpError } from '@solid/community-server'; +import { + BadRequestHttpError, + ForbiddenHttpError, + getLoggerFor, + HttpErrorClass, + KeyValueStorage } from '@solid/community-server'; import { getOperationLogger } from '../logging/OperationLogger'; import { serializePolicyInstantiation } from '../logging/OperationSerializer'; @@ -21,19 +22,19 @@ import { serializePolicyInstantiation } from '../logging/OperationSerializer'; * according to a TicketingStrategy. */ export class BaseNegotiator implements Negotiator { - protected readonly logger: Logger = getLoggerFor(this); + protected readonly logger = getLoggerFor(this); operationLogger = getOperationLogger(); /** * Construct a new Negotiator * @param verifier - The Verifier used to verify Claims of incoming Credentials. - * @param ticketStore - A KeyValueStore to track Tickets. + * @param ticketStore - A KeyValueStorage to track Tickets. * @param ticketManager - The strategy describing the life cycle of a Ticket. * @param tokenFactory - A factory for minting Access Tokens. */ public constructor( protected verifier: Verifier, - protected ticketStore: KeyValueStore, + protected ticketStore: KeyValueStorage, protected ticketingStrategy: TicketingStrategy, protected tokenFactory: TokenFactory, ) {} @@ -50,40 +51,40 @@ export class BaseNegotiator implements Negotiator { // Create or retrieve ticket const ticket = await this.getTicket(input); - this.logger.debug(`Processing ticket.`, ticket); + this.logger.debug(`Processing ticket. ${JSON.stringify(ticket)}`); // Process pushed credentials const updatedTicket = await this.processCredentials(input, ticket); - this.logger.debug('resolved result', JSON.stringify(updatedTicket, null, 2)) + this.logger.debug(`resolved result ${JSON.stringify(updatedTicket)}`); // Try to resolve ticket ... const resolved = await this.ticketingStrategy.resolveTicket(updatedTicket); - this.logger.debug('Resolved ticket.', JSON.stringify(resolved, null, 2)); + this.logger.debug(`Resolved ticket ${JSON.stringify(resolved)}`); // ... on success, create Access Token if (resolved.success) { // Retrieve / create instantiated policy const { token, tokenType } = await this.tokenFactory.serialize({ permissions: resolved.value }); - this.logger.debug('Minted token', JSON.stringify(token)); + this.logger.debug(`Minted token ${JSON.stringify(token)}`); // TODO:: test logging this.operationLogger.addLogEntry(serializePolicyInstantiation()) - // TODO:: dynamic contract link to stored signed contract. + // TODO:: dynamic contract link to stored signed contract. // If needed we can always embed here directly into the return JSON return ({ access_token: token, token_type: tokenType, }); } - + // ... on failure, deny if no solvable requirements const requiredClaims = ticket.required.map(req => Object.keys(req)); if (requiredClaims.length === 0) throw new ForbiddenHttpError(); // ... require more info otherwise - const id = v4(); + const id = randomUUID(); this.ticketStore.set(id, ticket); throw new NeedInfoError('Need more info to authorize request ...', id, { required_claims: { @@ -95,9 +96,9 @@ export class BaseNegotiator implements Negotiator { /** * Helper function that retrieves a Ticket from the TicketStore if it exists, * or initializes a new one otherwise. - * + * * @param input - The input of the negotiation dialog. - * + * * @returns The Ticket describing the dialog at hand. */ private async getTicket(input: DialogInput): Promise { @@ -124,7 +125,7 @@ export class BaseNegotiator implements Negotiator { * * @param input - The input of the negotiation dialog. * @param ticket - The Ticket against which to validate any Credentials. - * + * * @returns An updated Ticket in which the Credentials have been validated. */ private async processCredentials(input: DialogInput, ticket: Ticket): Promise { @@ -145,16 +146,14 @@ export class BaseNegotiator implements Negotiator { /** * Logs and throws an error * - * @param {ErrorConstructor} constructor - The error constructor. + * @param {HttpErrorClass} constructor - The error constructor. * @param {string} message - The error message. - * + * * @throws An Error constructed with the provided constructor with the * provided message */ - private error(constructor: ErrorConstructor, message: string): never { + private error(constructor: HttpErrorClass, message: string): never { this.logger.warn(message); throw new constructor(message); } } - -type ErrorConstructor = { new(msg: string): Error }; diff --git a/packages/uma/src/dialog/ContractNegotiator.ts b/packages/uma/src/dialog/ContractNegotiator.ts index c94ac99e..4b78655b 100644 --- a/packages/uma/src/dialog/ContractNegotiator.ts +++ b/packages/uma/src/dialog/ContractNegotiator.ts @@ -1,32 +1,33 @@ -import { BadRequestHttpError } from '../util/http/errors/BadRequestHttpError'; -import { Ticket } from '../ticketing/Ticket'; +import { + BadRequestHttpError, + createErrorMessage, + ForbiddenHttpError, + getLoggerFor, + HttpErrorClass, + KeyValueStorage +} from '@solid/community-server'; +import { v4 } from 'uuid'; +import { AccessToken, Permission, Requirements } from '..'; import { Verifier } from '../credentials/verify/Verifier'; -import { TokenFactory } from '../tokens/TokenFactory'; -import { Negotiator } from './Negotiator'; -import { getLoggerFor } from '../util/logging/LoggerUtils'; -import { Logger } from '../util/logging/Logger'; import { NeedInfoError } from '../errors/NeedInfoError'; -import { DialogInput } from './Input'; -import { DialogOutput } from './Output'; -import { reType } from '../util/ReType'; -import { KeyValueStore } from '../util/storage/models/KeyValueStore'; -import { TicketingStrategy } from '../ticketing/strategy/TicketingStrategy'; -import { v4 } from 'uuid'; -import { ForbiddenHttpError } from '@solid/community-server'; -// import { getOperationLogger } from '../logging/OperationLogger'; -// import { serializePolicyInstantiation } from '../logging/OperationSerializer'; import { ContractManager } from '../policies/contracts/ContractManager'; -import { Result, Success } from '../util/Result'; -import { AccessToken, Permission, Requirements } from '..'; -import { convertStringOrJsonLdIdentifierToString, JsonLdIdentifier, ODRLContract, ODRLPermission, StringOrJsonLdIdentifier } from '../views/Contract'; +import { TicketingStrategy } from '../ticketing/strategy/TicketingStrategy'; +import { Ticket } from '../ticketing/Ticket'; +import { TokenFactory } from '../tokens/TokenFactory'; import { processRequestPermission, switchODRLandCSSPermission } from '../util/rdf/RequestProcessing'; +import { Result, Success } from '../util/Result'; +import { reType } from '../util/ReType'; +import { convertStringOrJsonLdIdentifierToString, ODRLContract, StringOrJsonLdIdentifier } from '../views/Contract'; +import { DialogInput } from './Input'; +import { Negotiator } from './Negotiator'; +import { DialogOutput } from './Output'; /** * A mocked Negotiator for demonstration purposes to display contract negotiation */ export class ContractNegotiator implements Negotiator { - protected readonly logger: Logger = getLoggerFor(this); + protected readonly logger = getLoggerFor(this); // protected readonly operationLogger = getOperationLogger(); protected readonly contractManager = new ContractManager(); @@ -40,7 +41,7 @@ export class ContractNegotiator implements Negotiator { */ public constructor( protected verifier: Verifier, - protected ticketStore: KeyValueStore, + protected ticketStore: KeyValueStorage, protected ticketingStrategy: TicketingStrategy, protected tokenFactory: TokenFactory, ) { @@ -56,16 +57,16 @@ export class ContractNegotiator implements Negotiator { */ public async negotiate(input: DialogInput): Promise { reType(input, DialogInput); - if (!input.permissions && input.permission?.length) + if (!input.permissions && input.permission?.length) input.permissions = input.permission.map(p => processRequestPermission(p)) - this.logger.debug(`Input.`, input); + this.logger.debug(`Input. ${JSON.stringify(input)}`); // Create or retrieve ticket const ticket = await this.getTicket(input); - this.logger.debug(`Processing ticket.`, ticket); + this.logger.debug(`Processing ticket. ${JSON.stringify(ticket)}`); // Process pushed credentials const updatedTicket = await this.processCredentials(input, ticket); - this.logger.debug('Processed credentials', JSON.parse(JSON.stringify(updatedTicket))) + this.logger.debug(`Processed credentials ${JSON.stringify(updatedTicket)}`); let result : Result let contract: ODRLContract | undefined; @@ -74,62 +75,71 @@ export class ContractNegotiator implements Negotiator { try { contract = this.contractManager.findContract(updatedTicket) } catch (e) { - this.logger.debug('Error', e) + this.logger.debug(`Error: ${createErrorMessage(e)}`); } - - this.logger.debug('Contract retrieval attempt', contract) - if (contract) { + this.logger.debug(`Contract retrieval attempt ${JSON.stringify(contract)}`); + + if (contract) { result = Success(contract) - this.logger.debug('Existing contract discovered', contract) + this.logger.debug(`Existing contract discovered ${JSON.stringify(contract)}`); } else { this.logger.debug(`No existing contract discovered. Attempting to resolve ticket.`) - + const resolved = await this.ticketingStrategy.resolveTicket(updatedTicket); - this.logger.debug('Resolved ticket.', resolved); + this.logger.debug(`Resolved ticket. ${JSON.stringify(resolved)}`); if (resolved.success) { - this.logger.debug('Ticket resolved succesfully.', resolved) + this.logger.debug('Ticket resolved succesfully.') // todo: get necessary information here for contract creation contract = this.contractManager.createContract(resolved.value) - if (contract) result = Success(contract) - else throw new Error('It should not be possible to get an incorrect contract with a resolved policy evaluation') + result = Success(contract); this.logger.debug('New contract created') this.logger.debug(JSON.stringify(contract, null, 2)) } else { - this.logger.debug('Ticket not resolved.', resolved) + this.logger.debug('Ticket not resolved.') result = resolved } } - if (result.success) { + if (result.success) { let contract : ODRLContract = result.value this.logger.debug(JSON.stringify(contract, null, 2)) // todo: set resource scopes according to contract! - let permissions: Permission[] = contract.permission.map( (p: ODRLPermission) => { - const perm : Permission = { - // We do not accept AssetCollections as targets of an UMA access request formatted as an ODRL request! - resource_id: convertStringOrJsonLdIdentifierToString(p.target as StringOrJsonLdIdentifier), - resource_scopes: [ // mapping from ODRL to internal CSS read permission - switchODRLandCSSPermission(convertStringOrJsonLdIdentifierToString(p.action)) - ] + // Using a map first as the contract could return multiple entries for the same resource_id + // as it only allows 1 action per entry. + const permissionMap: Record = {}; + for (const permission of contract.permission) { + const id = convertStringOrJsonLdIdentifierToString(permission.target as StringOrJsonLdIdentifier); + if (!permissionMap[id]) { + permissionMap[id] = { + // We do not accept AssetCollections as targets of an UMA access request formatted as an ODRL request! + resource_id: id, + resource_scopes: [ // mapping from ODRL to internal CSS read permission + switchODRLandCSSPermission(convertStringOrJsonLdIdentifierToString(permission.action)) + ] + }; + } else { + permissionMap[id].resource_scopes.push( + switchODRLandCSSPermission(convertStringOrJsonLdIdentifierToString(permission.action)) + ); } - return(perm) - }) - this.logger.debug('granting permissions:', permissions) + } + let permissions: Permission[] = Object.values(permissionMap); + this.logger.debug(`granting permissions: ${JSON.stringify(permissions)}`); // Create response const tokenContents: AccessToken = { permissions, contract } - this.logger.debug('resolved result', result) + this.logger.debug(`resolved result ${JSON.stringify(result)}`); const { token, tokenType } = await this.tokenFactory.serialize(tokenContents); - this.logger.debug('Minted token', token); + this.logger.debug(`Minted token ${JSON.stringify(token)}`); // TODO:: test logging // this.operationLogger.addLogEntry(serializePolicyInstantiation()) @@ -137,8 +147,9 @@ export class ContractNegotiator implements Negotiator { // Store created instantiated policy (above contract variable) in the pod storage as an instantiated policy // todo: dynamic URL // todo: fix instantiated from url - // contract['http://www.w3.org/ns/prov#wasDerivedFrom'] = [ 'urn:ucp:be-gov:policy:d81b8118-af99-4ab3-b2a7-63f8477b6386 '] - const instantiatedPolicyContainer = 'http://localhost:3000/ruben/settings/policies/instantiated/'; + // contract['http://www.w3.org/ns/prov#wasDerivedFrom'] = [ 'urn:ucp:be-gov:policy:d81b8118-af99-4ab3-b2a7-63f8477b6386 '] + // TODO: test-private error: this container does not exist and unauth does not have append perms + const instantiatedPolicyContainer = 'http://localhost:3000/ruben/settings/policies/instantiated/'; const policyCreationResponse = await fetch(instantiatedPolicyContainer, { method: 'POST', headers: { 'content-type': 'application/ld+json' }, @@ -146,15 +157,15 @@ export class ContractNegotiator implements Negotiator { }); if (policyCreationResponse.status !== 201) { this.logger.warn('Adding a policy did not succeed...') } - - // TODO:: dynamic contract link to stored signed contract. + + // TODO:: dynamic contract link to stored signed contract. // If needed we can always embed here directly into the return JSON return ({ access_token: token, token_type: tokenType, }); } - + // ... on failure, deny if no solvable requirements const requiredClaims = ticket.required.map(req => Object.keys(req)); if (requiredClaims.length === 0) throw new ForbiddenHttpError(); @@ -172,9 +183,9 @@ export class ContractNegotiator implements Negotiator { /** * Helper function that retrieves a Ticket from the TicketStore if it exists, * or initializes a new one otherwise. - * + * * @param input - The input of the negotiation dialog. - * + * * @returns The Ticket describing the dialog at hand. */ private async getTicket(input: DialogInput): Promise { @@ -201,7 +212,7 @@ export class ContractNegotiator implements Negotiator { * * @param input - The input of the negotiation dialog. * @param ticket - The Ticket against which to validate any Credentials. - * + * * @returns An updated Ticket in which the Credentials have been validated. */ private async processCredentials(input: DialogInput, ticket: Ticket): Promise { @@ -222,16 +233,14 @@ export class ContractNegotiator implements Negotiator { /** * Logs and throws an error * - * @param {ErrorConstructor} constructor - The error constructor. + * @param {HttpErrorClass} constructor - The error constructor. * @param {string} message - The error message. - * + * * @throws An Error constructed with the provided constructor with the * provided message */ - private error(constructor: ErrorConstructor, message: string): never { + private error(constructor: HttpErrorClass, message: string): never { this.logger.warn(message); throw new constructor(message); } } - -type ErrorConstructor = { new(msg: string): Error }; diff --git a/packages/uma/src/dialog/Input.ts b/packages/uma/src/dialog/Input.ts index 7a214424..b11c4e31 100644 --- a/packages/uma/src/dialog/Input.ts +++ b/packages/uma/src/dialog/Input.ts @@ -7,6 +7,7 @@ import { Permission } from "../views/Permission"; */ export const DialogInput = ({ "@context": $(string), + grant_type: $(string), ticket: $(string), claim_token: $(string), claim_token_format: $(string), // TODO: switch to array of claims objects with unknown structure diff --git a/packages/uma/src/errors/InvalidGrantError.ts b/packages/uma/src/errors/InvalidGrantError.ts deleted file mode 100644 index 858d067d..00000000 --- a/packages/uma/src/errors/InvalidGrantError.ts +++ /dev/null @@ -1,17 +0,0 @@ -import {BadRequestHttpError} from '../util/http/errors/BadRequestHttpError'; - -/** - * If the provided permission ticket was not found at the authorization server, - * or the provided permission ticket has expired, the authorization server responds - * with the HTTP 400 (Bad Request) status code. - */ -export class InvalidGrantError extends BadRequestHttpError { - public type: string = 'invalid_grant'; - /** - * InvalidGrant UMA Error - * @param {string} message - */ - public constructor(message: string) { - super(message); - } -} diff --git a/packages/uma/src/errors/NeedInfoError.ts b/packages/uma/src/errors/NeedInfoError.ts index d5a3922d..7f3d5a84 100644 --- a/packages/uma/src/errors/NeedInfoError.ts +++ b/packages/uma/src/errors/NeedInfoError.ts @@ -1,4 +1,4 @@ -import {ForbiddenHttpError} from '../util/http/errors/ForbiddenHttpError'; +import { ForbiddenHttpError } from '@solid/community-server'; export type RedirectUserInfo = { redirect_user: string @@ -31,4 +31,8 @@ export class NeedInfoError extends ForbiddenHttpError { this.ticket = ticket; this.additionalParams = additionalParams; } + + public static isInstance(error: unknown): error is NeedInfoError { + return ForbiddenHttpError.isInstance(error) && typeof (error as NeedInfoError).ticket === 'string'; + } } diff --git a/packages/uma/src/errors/RequestDeniedError.ts b/packages/uma/src/errors/RequestDeniedError.ts deleted file mode 100644 index 440f0a36..00000000 --- a/packages/uma/src/errors/RequestDeniedError.ts +++ /dev/null @@ -1,16 +0,0 @@ -import {ForbiddenHttpError} from '../util/http/errors/ForbiddenHttpError'; - -/** - * The client is not authorized to have these permissions. The authorization - * server responds with the HTTP 403 (Forbidden) status code. - */ -export class RequestDeniedError extends ForbiddenHttpError { - public type: string = 'request_denied'; - /** - * InvalidGrant UMA Error - * @param {string} message - */ - public constructor(message: string) { - super(message); - } -} diff --git a/packages/uma/src/index.ts b/packages/uma/src/index.ts index dee26e5c..3d4dc657 100644 --- a/packages/uma/src/index.ts +++ b/packages/uma/src/index.ts @@ -59,42 +59,17 @@ export * from './views/Contract'; export * from './views/ResourceDescription'; export * from './views/ScopeDescription'; -/* Replace the following with CSS types */ - -// Logging -export * from './util/logging/Logger'; -export * from './util/logging/LoggerUtils'; -export * from './util/logging/WinstonLogger'; - -// Storage -export * from './util/storage/JsonFileStore'; -export * from './util/storage/MemoryStore'; -export * from './util/storage/models/KeyValueStore'; -export * from './util/storage/models/TimedKeyValueStore'; -export * from './util/storage/models/TimedTypedKeyValueStore'; -export * from './util/storage/models/TypedKeyValueStore'; - // HTTP -export * from './util/http/errors/BadRequestHttpError'; -export * from './util/http/errors/ForbiddenHttpError'; -export * from './util/http/errors/HttpError'; -export * from './util/http/errors/InternalServerError'; -export * from './util/http/errors/UnauthorizedHttpError'; -export * from './util/http/errors/UnsupportedMediaTypeHttpError'; -export * from './util/http/models/Daemon'; -export * from './util/http/models/Server'; -export * from './util/http/models/Handler'; +export * from './util/http/identifier/BaseTargetExtractor'; export * from './util/http/models/HttpHandler'; -export * from './util/http/models/HttpHandlerContext'; -export * from './util/http/models/HttpHandlerController'; -export * from './util/http/models/HttpHandlerRequest'; -export * from './util/http/models/HttpHandlerResponse'; export * from './util/http/models/HttpHandlerRoute'; -export * from './util/http/models/HttpMethod'; -export * from './util/http/server/ErrorHandler'; -export * from './util/http/server/CorsRequestHandler'; +export * from './util/http/server/JsonHttpErrorHandler'; +export * from './util/http/server/JsonFormHttpHandler'; export * from './util/http/server/NodeHttpRequestResponseHandler'; -export * from './util/http/server/NodeHttpServer'; -export * from './util/http/server/NodeHttpStreamsHandler'; -export * from './util/http/server/NodeHttpStreams'; export * from './util/http/server/RoutedHttpRequestHandler'; + +// Util +export * from './util/ConvertUtil'; +export * from './util/HttpMessageSignatures'; +export * from './util/Result'; +export * from './util/ReType'; diff --git a/packages/uma/src/policies/authorizers/AllAuthorizer.ts b/packages/uma/src/policies/authorizers/AllAuthorizer.ts index c110c633..f8006e00 100644 --- a/packages/uma/src/policies/authorizers/AllAuthorizer.ts +++ b/packages/uma/src/policies/authorizers/AllAuthorizer.ts @@ -1,7 +1,5 @@ -import { Logger } from '../../util/logging/Logger'; -import { getLoggerFor } from '../../util/logging/LoggerUtils'; +import { getLoggerFor } from '@solid/community-server'; import { ANY_RESOURCE, ANY_SCOPE, Authorizer } from './Authorizer'; -import { Ticket } from '../../ticketing/Ticket'; import { Permission } from '../../views/Permission'; import { ClaimSet } from '../../credentials/ClaimSet'; import { Requirements } from '../../credentials/Requirements'; @@ -13,7 +11,7 @@ import { Requirements } from '../../credentials/Requirements'; * NOTE: DO NOT USE THIS IN PRODUCTION */ export class AllAuthorizer implements Authorizer { - protected readonly logger: Logger = getLoggerFor(this); + protected readonly logger = getLoggerFor(this); /** * Creates a new AllAuthorizer. Warns for usage! @@ -24,7 +22,7 @@ export class AllAuthorizer implements Authorizer { /** @inheritdoc */ public async permissions(claims: ClaimSet, query?: Partial[]): Promise { - if (query) return query.map(permission => ({ + if (query) return query.map(permission => ({ resource_id: permission.resource_id ?? ANY_RESOURCE, resource_scopes: permission.resource_scopes ?? [ ANY_SCOPE ] })); @@ -33,7 +31,7 @@ export class AllAuthorizer implements Authorizer { /** @inheritdoc */ public async credentials(permissions: Permission[]): Promise { - this.logger.info('Skipping credentials.', { permissions }); + this.logger.info(`Skipping credentials. ${JSON.stringify(permissions)}`); return [{}]; } } diff --git a/packages/uma/src/policies/authorizers/Authorizer.ts b/packages/uma/src/policies/authorizers/Authorizer.ts index 06142314..393c6708 100644 --- a/packages/uma/src/policies/authorizers/Authorizer.ts +++ b/packages/uma/src/policies/authorizers/Authorizer.ts @@ -1,6 +1,5 @@ import { Requirements } from '../../credentials/Requirements'; import { ClaimSet } from '../../credentials/ClaimSet'; -import { ForbiddenHttpError } from '../../util/http/errors/ForbiddenHttpError'; import { Permission } from '../../views/Permission'; export const ANY_RESOURCE = 'urn:solidlab:uma:resources:any'; @@ -10,21 +9,21 @@ export abstract class Authorizer { /** * Calculates the available Permissions for a given set of Claims. - * + * * @param {ClaimSet} claims - The set of asserted Claims. * @param {Permission[]} query - An optional query to constrain the calculated * Permissions. - * + * * @return {Promise} - An Array of available Permissions. */ public abstract permissions(claims: ClaimSet, query?: Partial[]): Promise; /** * Calculates the required Credentials to achieve a set of given Permissions. - * + * * @param {Permissions[]} permissions - The requested Permissions. * @param {Requirements} query - An optional query to constrain the calculated Requirements. - * + * * @return {Promise} An object containing ClaimDescriptions. */ public abstract credentials(permissions: Permission[], query?: Requirements): Promise; diff --git a/packages/uma/src/policies/authorizers/NamespacedAuthorizer.ts b/packages/uma/src/policies/authorizers/NamespacedAuthorizer.ts index 5adc809a..2d199800 100644 --- a/packages/uma/src/policies/authorizers/NamespacedAuthorizer.ts +++ b/packages/uma/src/policies/authorizers/NamespacedAuthorizer.ts @@ -1,4 +1,4 @@ -import { getLoggerFor } from '../../util/logging/LoggerUtils'; +import { getLoggerFor } from '@solid/community-server'; import { Authorizer } from './Authorizer'; import { Permission } from '../../views/Permission'; import { Requirements, type ClaimVerifier } from '../../credentials/Requirements'; @@ -15,7 +15,7 @@ export class NamespacedAuthorizer implements Authorizer { /** * Creates a NamespacedAuthorizer with the given namespaces. - * + * * @param config - A list of objects refering a list of namespaces to a specific Authorizer. */ constructor( @@ -25,7 +25,7 @@ export class NamespacedAuthorizer implements Authorizer { /** @inheritdoc */ public async permissions(claims: ClaimSet, query?: Partial[]): Promise { - this.logger.info('Calculating permissions.', { claims, query }); + this.logger.info(`Calculating permissions. ${JSON.stringify({ claims, query })}`); // No permissions if no query if (!query || query.length === 0) return []; @@ -50,7 +50,7 @@ export class NamespacedAuthorizer implements Authorizer { /** @inheritdoc */ public async credentials(permissions: Permission[], query?: Requirements): Promise { - this.logger.info('Calculating credentials.', { permissions, query }); + this.logger.info(`Calculating credentials. ${JSON.stringify({ permissions, query })}`); // No requirements if no requested permissions if (!permissions || permissions.length === 0) return []; diff --git a/packages/uma/src/policies/authorizers/NoneAuthorizer.ts b/packages/uma/src/policies/authorizers/NoneAuthorizer.ts index 30cdb65d..699d4e36 100644 --- a/packages/uma/src/policies/authorizers/NoneAuthorizer.ts +++ b/packages/uma/src/policies/authorizers/NoneAuthorizer.ts @@ -1,5 +1,4 @@ -import { Logger } from '../../util/logging/Logger'; -import { getLoggerFor } from '../../util/logging/LoggerUtils'; +import { getLoggerFor } from '@solid/community-server'; import { Authorizer } from './Authorizer'; import { Permission } from '../../views/Permission'; import { Requirements } from '../../credentials/Requirements'; @@ -9,7 +8,7 @@ import { ClaimSet } from '../../credentials/ClaimSet'; * Mock authorizer granting no access to any client. */ export class NoneAuthorizer implements Authorizer { - protected readonly logger: Logger = getLoggerFor(this); + protected readonly logger = getLoggerFor(this); /** @inheritdoc */ public async permissions(claims: ClaimSet, query?: Partial[]): Promise { @@ -18,7 +17,7 @@ export class NoneAuthorizer implements Authorizer { /** @inheritdoc */ public async credentials(permissions: Permission[], query?: Requirements): Promise { - this.logger.info('Skipping credentials.', { permissions, query }); + this.logger.info(`Skipping credentials. ${JSON.stringify({ permissions, query })}`); // throw new ForbiddenHttpError(); // TODO: indicating impossibility to RS would save roundtrip return []; } diff --git a/packages/uma/src/policies/authorizers/PolicyBasedAuthorizer.ts b/packages/uma/src/policies/authorizers/PolicyBasedAuthorizer.ts index d4300669..054fd9a2 100644 --- a/packages/uma/src/policies/authorizers/PolicyBasedAuthorizer.ts +++ b/packages/uma/src/policies/authorizers/PolicyBasedAuthorizer.ts @@ -1,22 +1,20 @@ -import { Logger } from '../../util/logging/Logger'; -import { getLoggerFor } from '../../util/logging/LoggerUtils'; import { Authorizer } from './Authorizer'; import { Permission } from '../../views/Permission'; import { Requirements, type ClaimVerifier } from '../../credentials/Requirements'; import { ClaimSet } from '../../credentials/ClaimSet'; -import { ODRL, PolicyExecutor, UconRequest, +import { ODRL, PolicyExecutor, UconRequest, UcpPatternEnforcement, UcpPlugin, UCRulesStorage } from '@solidlab/ucp'; import { EyeJsReasoner } from "koreografeye"; import { lstatSync, readFileSync, readdirSync } from 'fs'; import path from 'path'; import { WEBID } from '../../credentials/Claims'; -import { RDF } from '@solid/community-server'; +import { getLoggerFor, RDF } from '@solid/community-server'; /** * An Authorizer granting access according to Usage Control Policies. */ export class PolicyBasedAuthorizer implements Authorizer { - protected readonly logger: Logger = getLoggerFor(this); + protected readonly logger = getLoggerFor(this); private reasoner: EyeJsReasoner = new EyeJsReasoner(["--quiet", "--nope", "--pass"]); private plugins = { "http://example.org/dataUsage": new UcpPlugin() }; @@ -25,7 +23,7 @@ export class PolicyBasedAuthorizer implements Authorizer { /** * Creates a PublicNamespaceAuthorizer with the given public namespaces. - * + * * @param rules - A store containing the UCP policy rules. * @param enforcer - The UcpPatternEnforcement engine. */ @@ -46,7 +44,7 @@ export class PolicyBasedAuthorizer implements Authorizer { /** @inheritdoc */ public async permissions(claims: ClaimSet, query?: Partial[]): Promise { - this.logger.info('Calculating permissions.', { claims, query }); + this.logger.info(`Calculating permissions. ${JSON.stringify({ claims, query })}`); if (!query) { this.logger.warn('The PolicyBasedAuthorizer can only calculate permissions for explicit queries.') @@ -81,22 +79,22 @@ export class PolicyBasedAuthorizer implements Authorizer { /** @inheritdoc */ public async credentials(permissions: Permission[], query?: Requirements): Promise { - this.logger.info('Calculating credentials.', { permissions, query }); - + this.logger.info(`Calculating credentials. ${JSON.stringify({ permissions, query })}`); + // No permissions => empty requirements if (permissions.length === 0) return [{}]; const policyStore = await this.policies.getStore(); const requirements: Requirements[] = []; - + const policyPermissions = policyStore.getSubjects(RDF.terms.type, ODRL.terms.Permission, null); - + // No policies => no solvable requirements if (policyPermissions.length === 0) return []; - + for (const policyPermission of policyPermissions) { const verifiers: Record = {}; - + // TODO: remove this default and treat webid as any claim const webids = policyStore.getObjects(policyPermission, ODRL.terms.assignee, null).map(webid => webid.value); verifiers[WEBID] = async (webid: string) => webids.includes(webid); diff --git a/packages/uma/src/policies/authorizers/WebIdAuthorizer.ts b/packages/uma/src/policies/authorizers/WebIdAuthorizer.ts index 78e875a6..9151e73b 100644 --- a/packages/uma/src/policies/authorizers/WebIdAuthorizer.ts +++ b/packages/uma/src/policies/authorizers/WebIdAuthorizer.ts @@ -1,5 +1,4 @@ -import { Logger } from '../../util/logging/Logger'; -import { getLoggerFor } from '../../util/logging/LoggerUtils'; +import { getLoggerFor } from '@solid/community-server'; import { ANY_RESOURCE, ANY_SCOPE, Authorizer } from './Authorizer'; import { Permission } from '../../views/Permission'; import { Requirements } from '../../credentials/Requirements'; @@ -10,11 +9,11 @@ import { WEBID } from '../../credentials/Claims'; * An Authorizer granting access for WebID's to resources in given namespaces. */ export class WebIdAuthorizer implements Authorizer { - protected readonly logger: Logger = getLoggerFor(this); + protected readonly logger = getLoggerFor(this); /** * Creates a PublicNamespaceAuthorizer with the given public namespaces. - * + * * @param namespaces - A list of namespaces that should be publicly accessible. * @param authorizer - The Authorizer to use for other resources. */ @@ -24,15 +23,15 @@ export class WebIdAuthorizer implements Authorizer { /** @inheritdoc */ public async permissions(claims: ClaimSet, query?: Partial[]): Promise { - this.logger.info('Calculating permissions.', { claims, query }); + this.logger.info(`Calculating permissions. ${JSON.stringify({ claims, query })}`); const webid = claims[WEBID]; - + if (!(typeof webid === 'string' && this.webids.includes(webid))) return []; return (query ?? []).map( - (permission): Permission => ({ - resource_id: permission.resource_id ?? ANY_RESOURCE, + (permission): Permission => ({ + resource_id: permission.resource_id ?? ANY_RESOURCE, resource_scopes: permission.resource_scopes ?? [ ANY_SCOPE ] }) ); @@ -40,8 +39,8 @@ export class WebIdAuthorizer implements Authorizer { /** @inheritdoc */ public async credentials(permissions: Permission[], query?: Requirements): Promise { - this.logger.info('Calculating credentials.', { permissions, query }); - + this.logger.info(`Calculating credentials. ${JSON.stringify({ permissions, query })}`); + if (query && !Object.keys(query).includes(WEBID)) return []; return [{ diff --git a/packages/uma/src/policies/contracts/ContractManager.ts b/packages/uma/src/policies/contracts/ContractManager.ts index 0bcdd218..e9f733ac 100644 --- a/packages/uma/src/policies/contracts/ContractManager.ts +++ b/packages/uma/src/policies/contracts/ContractManager.ts @@ -1,9 +1,9 @@ -import { getLoggerFor } from "@solid/community-server"; -import { DialogInput, Permission, Ticket } from "../.." -import { ContractStorage } from "./ContractStorage"; -import { ODRLContract, ODRLConstraint, ODRLPermission } from "../../views/Contract"; -import { randomUUID } from "crypto"; -import { switchODRLandCSSPermission } from "../../util/rdf/RequestProcessing"; +import { getLoggerFor } from '@solid/community-server'; +import { randomUUID } from 'crypto'; +import { Permission, Ticket } from '../..' +import { switchODRLandCSSPermission } from '../../util/rdf/RequestProcessing'; +import { ODRLContract, ODRLPermission } from '../../views/Contract'; +import { ContractStorage } from './ContractStorage'; export class ContractManager { @@ -28,7 +28,10 @@ export class ContractManager { console.log('Creating Contract', JSON.stringify(perms, null, 2)) // todo: un-mock this!!! - const permission = perms[0]; + type Pair = { action: string, target: string }; + const permissionPairs: Pair[] = perms.flatMap( + (perm): Pair[] => perm.resource_scopes.map( + (scope): Pair => ({ action: scope, target: perm.resource_id }))); const contract: ODRLContract = { "@context": "http://www.w3.org/ns/odrl.jsonld", @@ -36,10 +39,10 @@ export class ContractManager { uid: `urn:uma:pacsoi:agreement:${randomUUID()}`, "http://purl.org/dc/terms/description": "Agreement for HCP X to read Alice's health data for bariatric care.", "https://w3id.org/dpv#hasLegalBasis": { "@id": "https://w3id.org/dpv/legal/eu/gdpr#eu-gdpr:A9-2-a" }, - permission: [ { + permission: permissionPairs.map(({ action, target }): ODRLPermission => ({ "@type": "Permission", - action: switchODRLandCSSPermission(permission.resource_scopes[0]), - target: permission.resource_id, + action: switchODRLandCSSPermission(action), + target: target, assigner: 'http://localhost:3000/ruben/profile/card#me', // user WebID assignee: 'http://localhost:3000/alice/profile/card#me', // target WebID constraint: [ { @@ -48,7 +51,7 @@ export class ContractManager { operator: "eq", rightOperand: { "@id": "http://example.org/bariatric-care" }, } ] - } ] + })), } return contract; } @@ -59,4 +62,4 @@ function nextweek(): Date{ var today = new Date(); var nextweek = new Date(today.getFullYear(), today.getMonth(), today.getDate()+7); return nextweek; -} \ No newline at end of file +} diff --git a/packages/uma/src/routes/Config.ts b/packages/uma/src/routes/Config.ts index 7d866d8d..f0c172ce 100644 --- a/packages/uma/src/routes/Config.ts +++ b/packages/uma/src/routes/Config.ts @@ -1,27 +1,24 @@ import { ASYMMETRIC_CRYPTOGRAPHIC_ALGORITHM } from '@solid/access-token-verifier/dist/constant/ASYMMETRIC_CRYPTOGRAPHIC_ALGORITHM'; -import { HttpHandler } from '../util/http/models/HttpHandler'; -import { HttpHandlerContext } from '../util/http/models/HttpHandlerContext'; -import { HttpHandlerResponse } from '../util/http/models/HttpHandlerResponse'; -import { Logger } from '../util/logging/Logger'; -import { getLoggerFor } from '../util/logging/LoggerUtils'; +import { getLoggerFor } from '@solid/community-server'; +import { HttpHandler, HttpHandlerContext, HttpHandlerResponse } from '../util/http/models/HttpHandler'; // eslint-disable no-unused-vars export enum ResponseType { Token = 'token', Code = 'code', IDToken = 'id_token' -}; +} // eslint-enable export type OAuthConfiguration = { - issuer: string, - jwks_uri?: string, - token_endpoint?: string, - grant_types_supported?: string[], - dpop_signing_alg_values_supported?: string[], - response_types_supported?: ResponseType[] - scopes_supported?: string[] + issuer: string, + jwks_uri?: string, + token_endpoint?: string, + grant_types_supported?: string[], + dpop_signing_alg_values_supported?: string[], + response_types_supported?: ResponseType[] + scopes_supported?: string[] } export type UmaConfiguration = OAuthConfiguration & { @@ -36,7 +33,7 @@ export type UmaConfiguration = OAuthConfiguration & { * of the UMA Authorization Service. */ export class ConfigRequestHandler extends HttpHandler { - protected readonly logger: Logger = getLoggerFor(this); + protected readonly logger = getLoggerFor(this); /** * An HttpHandler used for returning the configuration @@ -47,18 +44,11 @@ export class ConfigRequestHandler extends HttpHandler { super(); } - /** - * Returns the endpoint's UMA configuration - * - * @param {HttpHandlerContext} context - an irrelevant incoming context - * @return {Observable} - the mock response - */ - async handle(context: HttpHandlerContext): Promise { + public async handle(context: HttpHandlerContext): Promise { this.logger.info(`Received discovery request at '${context.request.url}'`); - + return { - body: JSON.stringify(this.getConfig()), - headers: {'content-type': 'application/json'}, + body: this.getConfig(), status: 200, }; } diff --git a/packages/uma/src/routes/Contract.ts b/packages/uma/src/routes/Contract.ts index 0d79f388..14e9882f 100644 --- a/packages/uma/src/routes/Contract.ts +++ b/packages/uma/src/routes/Contract.ts @@ -1,18 +1,13 @@ -import { ASYMMETRIC_CRYPTOGRAPHIC_ALGORITHM } - from '@solid/access-token-verifier/dist/constant/ASYMMETRIC_CRYPTOGRAPHIC_ALGORITHM'; -import { HttpHandler } from '../util/http/models/HttpHandler'; -import { HttpHandlerContext } from '../util/http/models/HttpHandlerContext'; -import { HttpHandlerResponse } from '../util/http/models/HttpHandlerResponse'; -import { Logger } from '../util/logging/Logger'; -import { getLoggerFor } from '../util/logging/LoggerUtils'; +import { getLoggerFor } from '@solid/community-server'; import { getOperationLogger } from '../logging/OperationLogger'; +import { HttpHandler, HttpHandlerContext, HttpHandlerResponse } from '../util/http/models/HttpHandler'; /** - * An HttpHandler used for returning the logs + * An HttpHandler used for returning the logs * stored in the UMA Authorization Service. */ export class ContractRequestHandler extends HttpHandler { - protected readonly logger: Logger = getLoggerFor(this); + protected readonly logger = getLoggerFor(this); operationLogger = getOperationLogger() @@ -33,7 +28,7 @@ export class ContractRequestHandler extends HttpHandler { */ async handle(context: HttpHandlerContext): Promise { this.logger.info(`Received contract retrieval request at '${context.request.url}'`); - + return { body: ' "the contract endpoint".', headers: {'content-type': 'application/trig'}, @@ -42,5 +37,3 @@ export class ContractRequestHandler extends HttpHandler { } } - - diff --git a/packages/uma/src/routes/Default.ts b/packages/uma/src/routes/Default.ts index bfd144f5..5cdc78cc 100644 --- a/packages/uma/src/routes/Default.ts +++ b/packages/uma/src/routes/Default.ts @@ -1,23 +1,15 @@ -import {HttpHandler} from '../util/http/models/HttpHandler'; -import {HttpHandlerContext} from '../util/http/models/HttpHandlerContext'; -import {HttpHandlerResponse} from '../util/http/models/HttpHandlerResponse'; +import { HttpHandler, HttpHandlerContext, HttpHandlerResponse } from '../util/http/models/HttpHandler'; /** - * Default route handler + * Default route handler that returns a 404. */ export class DefaultRequestHandler extends HttpHandler { - /** - * Default request handler returning a 404 error - * @param {HttpHandlerContext} input - * @return {Observable>} - */ async handle(input: HttpHandlerContext): Promise> { return { - body: JSON.stringify({ + body: { 'status': 404, 'error': 'Not Found', - }), - headers: {'content-type': 'application/json'}, + }, status: 404, }; } diff --git a/packages/uma/src/routes/Introspection.ts b/packages/uma/src/routes/Introspection.ts index 1bbdee56..c1eeb96d 100644 --- a/packages/uma/src/routes/Introspection.ts +++ b/packages/uma/src/routes/Introspection.ts @@ -1,16 +1,8 @@ -import { BadRequestHttpError } from '../util/http/errors/BadRequestHttpError'; -import { HttpHandlerContext } from '../util/http/models/HttpHandlerContext'; -import { HttpHandler } from '../util/http/models/HttpHandler'; -import { HttpHandlerResponse } from '../util/http/models/HttpHandlerResponse'; -import { UnauthorizedHttpError } from '../util/http/errors/UnauthorizedHttpError'; -import { UnsupportedMediaTypeHttpError } from '../util/http/errors/UnsupportedMediaTypeHttpError'; -import { Logger } from '../util/logging/Logger'; -import { getLoggerFor } from '../util/logging/LoggerUtils'; -import { KeyValueStore } from '../util/storage/models/KeyValueStore'; +import { BadRequestHttpError, getLoggerFor, KeyValueStorage, UnauthorizedHttpError } from '@solid/community-server'; import { AccessToken } from '../tokens/AccessToken'; import { JwtTokenFactory } from '../tokens/JwtTokenFactory'; import { SerializedToken } from '../tokens/TokenFactory'; -import { JwkGenerator } from '@solid/community-server'; +import { HttpHandler, HttpHandlerContext, HttpHandlerResponse } from '../util/http/models/HttpHandler'; import { verifyRequest } from '../util/HttpMessageSignatures'; import { jwtDecrypt } from 'jose'; @@ -30,49 +22,34 @@ type IntrospectionResponse = { /** * An HTTP handler that provides introspection into opaque access tokens. */ -export class IntrospectionHandler implements HttpHandler { - protected readonly logger: Logger = getLoggerFor(this); +export class IntrospectionHandler extends HttpHandler { + protected readonly logger = getLoggerFor(this); /** * Creates an introspection handler for tokens in the given token store. - * + * * @param tokenStore - The store containing the tokens. * @param jwtTokenFactory - The factory with which to produce JWT representations of the tokens. */ constructor( - private readonly tokenStore: KeyValueStore, + private readonly tokenStore: KeyValueStorage, private readonly jwtTokenFactory: JwtTokenFactory, - private readonly keyGen: JwkGenerator, - ) {} + ) { + super(); + } - /** - * Handle incoming requests for token introspection - * @param {HttpHandlerContext} param0 - * @return {Observable>} - */ async handle({request}: HttpHandlerContext): Promise> { if (!await verifyRequest(request)) throw new UnauthorizedHttpError(); - if (request.headers['content-type'] !== 'application/x-www-form-urlencoded') { - throw new UnsupportedMediaTypeHttpError( - 'Only Media Type "application/x-www-form-urlencoded" is supported for this route.'); - } - - if (request.headers['accept'] !== 'application/json') { - throw new UnsupportedMediaTypeHttpError( - 'Only "application/json" can be served by this route.'); - } - if (!request.body /*|| !(request.body instanceof Object) */) { // todo: why was the object check here?? throw new BadRequestHttpError('Missing request body.'); } - const token = new URLSearchParams(request.body).get('token'); + const token = new URLSearchParams(request.body as Record).get('token'); try { if(!token) throw new Error('could not extract token from request body') const unsignedToken = await this.processJWTToken(token) return { - headers: {'content-type': 'application/json'}, status: 200, body: unsignedToken, }; @@ -88,7 +65,7 @@ export class IntrospectionHandler implements HttpHandler { // try { // const opaqueToken = new URLSearchParams(request.body).get('token'); // if (!opaqueToken) throw new Error (); - + // const jwt = this.opaqueToJwt(opaqueToken); // return { // headers: {'content-type': 'application/json'}, @@ -115,7 +92,7 @@ export class IntrospectionHandler implements HttpHandler { const token = await this.tokenStore.get(opaque); if (!token) throw new Error('Token not found.'); - return this.jwtTokenFactory.serialize(Object.assign(token, { active: true })); + return this.jwtTokenFactory.serialize({ ...token, active: true } as AccessToken); } } diff --git a/packages/uma/src/routes/Jwks.ts b/packages/uma/src/routes/Jwks.ts index 462b6d4b..3337df23 100644 --- a/packages/uma/src/routes/Jwks.ts +++ b/packages/uma/src/routes/Jwks.ts @@ -1,30 +1,19 @@ -import { HttpHandler } from '../util/http/models/HttpHandler'; -import { HttpHandlerContext } from '../util/http/models/HttpHandlerContext'; -import { HttpHandlerResponse } from '../util/http/models/HttpHandlerResponse'; -import { Logger } from '../util/logging/Logger'; -import { getLoggerFor } from '../util/logging/LoggerUtils'; -import { JwkGenerator } from '@solid/community-server'; +import { getLoggerFor, JwkGenerator } from '@solid/community-server'; +import { HttpHandler, HttpHandlerContext, HttpHandlerResponse } from '../util/http/models/HttpHandler'; /** * An HttpHandler used for returning the configuration * of the UMA Authorization Service. */ -export class JwksRequestHandler implements HttpHandler { - protected readonly logger: Logger = getLoggerFor(this); +export class JwksRequestHandler extends HttpHandler { + protected readonly logger = getLoggerFor(this); - /** - * Yields a new request handler for JWKS - * @param {JwksKeyHolder} keyholder - the keyholder to be used for serving JWKS - */ public constructor( private readonly generator: JwkGenerator - ) {} + ) { + super(); + } - /** - * Returns the JSON Web KeySet for specified keyholder - * @param {HttpHandlerContext} context - an irrelevant incoming context - * @return {Observable} - the JWKS response - */ async handle(context: HttpHandlerContext): Promise { this.logger.info(`Received JWKS request at '${context.request.url}'`); @@ -32,10 +21,7 @@ export class JwksRequestHandler implements HttpHandler { return { status: 200, - headers: { - 'content-type': 'application/json' - }, - body: JSON.stringify({ keys: [ key ] }), + body: { keys: [ key ] }, }; } } diff --git a/packages/uma/src/routes/Log.ts b/packages/uma/src/routes/Log.ts index e2e66da0..b359dde1 100644 --- a/packages/uma/src/routes/Log.ts +++ b/packages/uma/src/routes/Log.ts @@ -1,13 +1,6 @@ -import { ASYMMETRIC_CRYPTOGRAPHIC_ALGORITHM } - from '@solid/access-token-verifier/dist/constant/ASYMMETRIC_CRYPTOGRAPHIC_ALGORITHM'; -import { HttpHandler } from '../util/http/models/HttpHandler'; -import { HttpHandlerContext } from '../util/http/models/HttpHandlerContext'; -import { HttpHandlerResponse } from '../util/http/models/HttpHandlerResponse'; -import { Logger } from '../util/logging/Logger'; -import { getLoggerFor } from '../util/logging/LoggerUtils'; +import { getLoggerFor, serializeQuads } from '@solid/community-server'; import { getOperationLogger } from '../logging/OperationLogger'; -import { Quad } from 'n3'; -import { serializeQuads } from '@solid/community-server'; +import { HttpHandler, HttpHandlerContext, HttpHandlerResponse } from '../util/http/models/HttpHandler'; export type LogMessage = { @@ -16,11 +9,11 @@ export type LogMessage = { } /** - * An HttpHandler used for returning the logs + * An HttpHandler used for returning the logs * stored in the UMA Authorization Service. */ export class LogRequestHandler extends HttpHandler { - protected readonly logger: Logger = getLoggerFor(this); + protected readonly logger = getLoggerFor(this); operationLogger = getOperationLogger() @@ -41,7 +34,7 @@ export class LogRequestHandler extends HttpHandler { */ async handle(context: HttpHandlerContext): Promise { this.logger.info(`Received log access request at '${context.request.url}'`); - + return { body: JSON.stringify(await this.getLogMessages()), headers: {'content-type': 'application/trig'}, diff --git a/packages/uma/src/routes/ResourceRegistration.ts b/packages/uma/src/routes/ResourceRegistration.ts index 94418c32..01d53e39 100644 --- a/packages/uma/src/routes/ResourceRegistration.ts +++ b/packages/uma/src/routes/ResourceRegistration.ts @@ -1,21 +1,21 @@ -import {BadRequestHttpError} from '../util/http/errors/BadRequestHttpError'; -import {HttpHandler} from '../util/http/models/HttpHandler'; -import {HttpHandlerContext} from '../util/http/models/HttpHandlerContext'; -import {HttpHandlerResponse} from '../util/http/models/HttpHandlerResponse'; -import {UnauthorizedHttpError} from '../util/http/errors/UnauthorizedHttpError'; -import {UnsupportedMediaTypeHttpError} from '../util/http/errors/UnsupportedMediaTypeHttpError'; -import {Logger} from '../util/logging/Logger'; -import {getLoggerFor} from '../util/logging/LoggerUtils'; -import {KeyValueStore} from '../util/storage/models/KeyValueStore'; -import {v4} from 'uuid'; -import { HttpMethods } from '../util/http/models/HttpMethod'; -import { MethodNotAllowedHttpError } from '../util/http/errors/MethodNotAllowedHttpError'; -import { HttpHandlerRequest } from '../util/http/models/HttpHandlerRequest'; -import { ResourceDescription } from '../views/ResourceDescription'; -import { reType } from '../util/ReType'; +import { + BadRequestHttpError, + createErrorMessage, + getLoggerFor, + KeyValueStorage, + MethodNotAllowedHttpError, NotFoundHttpError, + UnauthorizedHttpError +} from '@solid/community-server'; +import { randomUUID } from 'node:crypto'; +import { + HttpHandler, + HttpHandlerContext, + HttpHandlerRequest, + HttpHandlerResponse +} from '../util/http/models/HttpHandler'; import { extractRequestSigner, verifyRequest } from '../util/HttpMessageSignatures'; - -type ErrorConstructor = { new(msg: string): Error }; +import { reType } from '../util/ReType'; +import { ResourceDescription } from '../views/ResourceDescription'; /** * A ResourceRegistrationRequestHandler is tasked with implementing @@ -23,21 +23,15 @@ type ErrorConstructor = { new(msg: string): Error }; * * It provides an endpoint to a Resource Server for registering its resources. */ -export class ResourceRegistrationRequestHandler implements HttpHandler { - protected readonly logger: Logger = getLoggerFor(this); +export class ResourceRegistrationRequestHandler extends HttpHandler { + protected readonly logger = getLoggerFor(this); - /** - * @param {RequestingPartyRegistration[]} resourceServers - Pod Servers to be registered with the UMA AS - */ constructor( - private readonly resourceStore: KeyValueStore, - ) {} + private readonly resourceStore: KeyValueStorage, + ) { + super(); + } - /** - * Handle incoming requests for resource registration - * @param {HttpHandlerContext} param0 - * @return {Observable>} - */ async handle({ request }: HttpHandlerContext): Promise> { const signer = await extractRequestSigner(request); @@ -48,66 +42,45 @@ export class ResourceRegistrationRequestHandler implements HttpHandler { } switch (request.method) { - case HttpMethods.POST: return this.handlePost(request); - case HttpMethods.DELETE: return this.handleDelete(request); + case 'POST': return this.handlePost(request); + case 'DELETE': return this.handleDelete(request); default: throw new MethodNotAllowedHttpError(); } } private async handlePost(request: HttpHandlerRequest): Promise> { - const { headers, body } = request; - - if (headers['content-type'] !== 'application/json') { - throw new UnsupportedMediaTypeHttpError('Only Media Type "application/json" is supported for this route.'); - } + const { body } = request; try { reType(body, ResourceDescription); } catch (e) { - this.logger.warn('Syntax error: ' + (e as Error).message, body); - this.error(BadRequestHttpError, `Request has bad syntax${e instanceof Error ? ': ' + e.message : ''}`) + this.logger.warn(`Syntax error: ${createErrorMessage(e)}, ${body}`); + throw new BadRequestHttpError(`Request has bad syntax: ${createErrorMessage(e)}`); } - const resource = v4(); - this.resourceStore.set(resource, body); + const resource = randomUUID(); + await this.resourceStore.set(resource, body); this.logger.info(`Registered resource ${resource}.`); return ({ status: 201, - headers: { - 'content-type': 'application/json' - }, - body: JSON.stringify({ + body: { _id: resource, user_access_policy_uri: 'TODO: implement policy UI', - }), + }, }) } private async handleDelete({ parameters }: HttpHandlerRequest): Promise> { if (typeof parameters?.id !== 'string') throw new Error('URI for DELETE operation should include an id.'); - if (!await this.resourceStore.has(parameters.id)) { - throw new Error('Registration to be deleted does not exist (id unknown).'); + if (!await this.resourceStore.delete(parameters.id)) { + throw new NotFoundHttpError('Registration to be deleted does not exist (id unknown).'); } this.logger.info(`Deleted resource ${parameters.id}.`); - - return ({ - status: 204, - headers: {}, - }); - } - /** - * Logs and throws an error - * - * @param {ErrorConstructor} constructor - the error constructor - * @param {string} message - the error message - */ - private error(constructor: ErrorConstructor, message: string): never { - this.logger.warn(message); - throw new constructor(message); + return { status: 204 }; } } diff --git a/packages/uma/src/routes/Ticket.ts b/packages/uma/src/routes/Ticket.ts index 76d27161..0f580d77 100644 --- a/packages/uma/src/routes/Ticket.ts +++ b/packages/uma/src/routes/Ticket.ts @@ -1,20 +1,17 @@ -import { BadRequestHttpError } from '../util/http/errors/BadRequestHttpError'; -import { HttpHandler } from '../util/http/models/HttpHandler'; -import { HttpHandlerContext } from '../util/http/models/HttpHandlerContext'; -import { HttpHandlerResponse } from '../util/http/models/HttpHandlerResponse'; -import { UnauthorizedHttpError } from '../util/http/errors/UnauthorizedHttpError'; -import { UnsupportedMediaTypeHttpError } from '../util/http/errors/UnsupportedMediaTypeHttpError'; -import { Logger } from '../util/logging/Logger'; -import { getLoggerFor } from '../util/logging/LoggerUtils'; -import { array, reType } from '../util/ReType'; -import { Permission } from '../views/Permission'; -import { Ticket } from '../ticketing/Ticket'; -import { KeyValueStore } from '../util/storage/models/KeyValueStore'; +import { + BadRequestHttpError, + createErrorMessage, + getLoggerFor, + KeyValueStorage, + UnauthorizedHttpError +} from '@solid/community-server'; +import { randomUUID } from 'node:crypto'; import { TicketingStrategy } from '../ticketing/strategy/TicketingStrategy'; -import { v4 } from 'uuid'; +import { Ticket } from '../ticketing/Ticket'; +import { HttpHandler, HttpHandlerContext, HttpHandlerResponse } from '../util/http/models/HttpHandler'; import { verifyRequest } from '../util/HttpMessageSignatures'; - -type ErrorConstructor = { new(msg: string): Error }; +import { array, reType } from '../util/ReType'; +import { Permission } from '../views/Permission'; /** * A TicketRequestHandler is tasked with implementing @@ -22,69 +19,38 @@ type ErrorConstructor = { new(msg: string): Error }; * * It provides an endpoint to a Resource Server for requesting UMA tickets. */ -export class TicketRequestHandler implements HttpHandler { - protected readonly logger: Logger = getLoggerFor(this); +export class TicketRequestHandler extends HttpHandler { + protected readonly logger = getLoggerFor(this); - /** - * A TicketRequestHandler is tasked with implementing - * section 3.2 from the User-Managed Access (UMA) Profile of OAuth 2.0. - * @param {RequestingPartyRegistration[]} resourceServers - Pod Servers to be registered with the UMA AS - */ constructor( private readonly ticketingStrategy: TicketingStrategy, - private readonly ticketStore: KeyValueStore, - ) {} + private readonly ticketStore: KeyValueStorage, + ) { + super(); + } - /** - * Handle incoming requests for permission registration - * @param {HttpHandlerContext} param0 - * @return {Observable>} - */ async handle({request}: HttpHandlerContext): Promise> { - this.logger.info('Received permission registration request.', request); + this.logger.info(`Received permission registration request.`); if (!await verifyRequest(request)) throw new UnauthorizedHttpError(); - if (request.headers['content-type'] !== 'application/json') { - throw new UnsupportedMediaTypeHttpError( - 'Only Media Type "application/json" is supported for this route.'); - } - - if (!request.body || !Array.isArray(request.body)) { - this.error(BadRequestHttpError, 'Request body must be a JSON array.'); - } - try { reType(request.body, array(Permission)); } catch (e) { - this.logger.debug('Syntax error: ' + (e as Error).message, request.body); - e instanceof Error - ? this.error(BadRequestHttpError, 'Request has bad syntax: ' + e.message) - : this.error(BadRequestHttpError, 'Request has bad syntax'); + this.logger.warn(`Syntax error: ${createErrorMessage(e)}, ${request.body}`); + throw new BadRequestHttpError(`Request has bad syntax: ${createErrorMessage(e)}`); } const ticket = await this.ticketingStrategy.initializeTicket(request.body); const resolved = await this.ticketingStrategy.resolveTicket(ticket); - + if (resolved.success) return { status: 200 }; - const id = v4(); - this.ticketStore.set(id, ticket); + const id = randomUUID(); + await this.ticketStore.set(id, ticket); return { - headers: {'content-type': 'application/json'}, status: 201, body: { ticket: id }, }; } - - /** - * Logs and throws an error - * - * @param {ErrorConstructor} constructor - the error constructor - * @param {string} message - the error message - */ - private error(constructor: ErrorConstructor, message: string): never { - this.logger.warn(message); - throw new constructor(message); - } } diff --git a/packages/uma/src/routes/Token.ts b/packages/uma/src/routes/Token.ts index 3e2c26d2..e58bffb9 100644 --- a/packages/uma/src/routes/Token.ts +++ b/packages/uma/src/routes/Token.ts @@ -1,74 +1,54 @@ -import { UnsupportedMediaTypeHttpError } from '../util/http/errors/UnsupportedMediaTypeHttpError'; -import { BadRequestHttpError } from '../util/http/errors/BadRequestHttpError'; -import { HttpHandler } from '../util/http/models/HttpHandler'; -import { HttpHandlerContext } from '../util/http/models/HttpHandlerContext'; -import { HttpHandlerResponse } from '../util/http/models/HttpHandlerResponse'; -import { Negotiator } from '../dialog/Negotiator'; -import { Logger } from '../util/logging/Logger'; -import { getLoggerFor } from '../util/logging/LoggerUtils'; +import { BadRequestHttpError, ForbiddenHttpError, getLoggerFor } from '@solid/community-server'; import { DialogInput } from '../dialog/Input'; -import { reType } from '../util/ReType'; +import { Negotiator } from '../dialog/Negotiator'; import { NeedInfoError } from '../errors/NeedInfoError'; -import { ForbiddenHttpError } from '../util/http/errors/ForbiddenHttpError'; +import { HttpHandler, HttpHandlerContext, HttpHandlerResponse } from '../util/http/models/HttpHandler'; +import { reType } from '../util/ReType'; /** * The TokenRequestHandler implements the interface of the UMA Token Endpoint. */ -export class TokenRequestHandler implements HttpHandler { - protected readonly logger: Logger = getLoggerFor(this); +export class TokenRequestHandler extends HttpHandler { + protected readonly logger = getLoggerFor(this); constructor( protected negotiator: Negotiator, - ) {} + ) { + super(); + } - /** - * Handles an incoming token request. - * - * @param {HttpHandlerContext} input - Request context - * @return {Observable>} - response - */ async handle(input: HttpHandlerContext): Promise> { - this.logger.info('Received token request.', input); - - // This deviates from UMA, which reads application/x-www-form-urlencoded - if (input.request.headers['content-type'] !== 'application/json') { - throw new UnsupportedMediaTypeHttpError(); - } - + this.logger.info(`Received token request.`); const params = input.request.body; - // if (params['grant_type'] !== 'urn:ietf:params:oauth:grant-type:uma-ticket') { - // throw new BadRequestHttpError( - // `Expected 'grant_type' to be set to 'urn:ietf:params:oauth:grant-type:uma-ticket' - // `); - // } - try { reType(params, DialogInput); } catch (e) { throw new BadRequestHttpError(`Invalid token request body: ${e instanceof Error ? e.message : ''}`); } + if (params['grant_type'] !== 'urn:ietf:params:oauth:grant-type:uma-ticket') { + throw new BadRequestHttpError( + `Expected 'grant_type' to be set to 'urn:ietf:params:oauth:grant-type:uma-ticket' + `); + } + try { const tokenResponse = await this.negotiator.negotiate(params); return { status: 200, - headers: {'content-type': 'application/json'}, - body: JSON.stringify(tokenResponse) + body: tokenResponse }; } catch (e) { - if (ForbiddenHttpError.isInstance(e)) return ({ + if (NeedInfoError.isInstance(e)) return ({ status: 403, - headers: {'content-type': 'application/json'}, - body: JSON.stringify({ - ticket: (e as NeedInfoError).ticket, - ...(e as NeedInfoError).additionalParams - }) + body: { + ticket: e.ticket, + ...e.additionalParams + } }); throw e; // TODO: distinguish other errors } } } - - diff --git a/packages/uma/src/routes/VC.ts b/packages/uma/src/routes/VC.ts index 0e741a5b..e80936d0 100644 --- a/packages/uma/src/routes/VC.ts +++ b/packages/uma/src/routes/VC.ts @@ -1,21 +1,14 @@ -import { ASYMMETRIC_CRYPTOGRAPHIC_ALGORITHM } - from '@solid/access-token-verifier/dist/constant/ASYMMETRIC_CRYPTOGRAPHIC_ALGORITHM'; -import { HttpHandler } from '../util/http/models/HttpHandler'; -import { HttpHandlerContext } from '../util/http/models/HttpHandlerContext'; -import { HttpHandlerResponse } from '../util/http/models/HttpHandlerResponse'; -import { Logger } from '../util/logging/Logger'; -import { getLoggerFor } from '../util/logging/LoggerUtils'; +import { getLoggerFor } from '@solid/community-server'; import { getOperationLogger } from '../logging/OperationLogger'; -import { Quad } from 'n3'; -import { serializeQuads } from '@solid/community-server'; +import { HttpHandler, HttpHandlerContext, HttpHandlerResponse } from '../util/http/models/HttpHandler'; /** - * An HttpHandler used for returning the logs + * An HttpHandler used for returning the logs * stored in the UMA Authorization Service. */ export class VCRequestVerificationHandler extends HttpHandler { - protected readonly logger: Logger = getLoggerFor(this); + protected readonly logger = getLoggerFor(this); operationLogger = getOperationLogger() @@ -34,7 +27,7 @@ export class VCRequestVerificationHandler extends HttpHandler { */ async handle(context: HttpHandlerContext): Promise { this.logger.info(`Received VC endpoint request at '${context.request.url}'`); - + return { body: ' "the vc endpoint".', headers: {'content-type': 'application/trig'}, @@ -43,4 +36,3 @@ export class VCRequestVerificationHandler extends HttpHandler { } } - diff --git a/packages/uma/src/ticketing/strategy/ClaimEliminationStrategy.ts b/packages/uma/src/ticketing/strategy/ClaimEliminationStrategy.ts index 23d0d8f8..d9da2d42 100644 --- a/packages/uma/src/ticketing/strategy/ClaimEliminationStrategy.ts +++ b/packages/uma/src/ticketing/strategy/ClaimEliminationStrategy.ts @@ -1,3 +1,4 @@ +import { getLoggerFor } from '@solid/community-server'; import { ClaimSet } from "../../credentials/ClaimSet"; import { Ticket } from "../Ticket"; import { Permission } from "../../views/Permission"; @@ -5,8 +6,6 @@ import { Failure, Result, Success } from "../../util/Result"; import { TicketingStrategy } from "./TicketingStrategy"; import { Requirements } from "../../credentials/Requirements"; import { Authorizer } from "../../policies/authorizers/Authorizer"; -import { Logger } from "../../util/logging/Logger"; -import { getLoggerFor } from "../../util/logging/LoggerUtils"; /** * A TicketingStrategy that calculates all necessary Claims for a given Permissions @@ -15,7 +14,7 @@ import { getLoggerFor } from "../../util/logging/LoggerUtils"; * requested Permissions. */ export class ClaimEliminationStrategy implements TicketingStrategy { - protected readonly logger: Logger = getLoggerFor(this); + protected readonly logger = getLoggerFor(this); constructor( private authorizer: Authorizer, @@ -23,7 +22,7 @@ export class ClaimEliminationStrategy implements TicketingStrategy { /** @inheritdoc */ async initializeTicket(permissions: Permission[]): Promise { - this.logger.info('Initializing ticket.', permissions) + this.logger.info(`Initializing ticket. ${JSON.stringify(permissions)}`) return ({ permissions, @@ -38,7 +37,7 @@ export class ClaimEliminationStrategy implements TicketingStrategy { /** @inheritdoc */ async validateClaims(ticket: Ticket, claims: ClaimSet): Promise { - this.logger.debug('Validating claims.', { ticket, claims }); + this.logger.debug(`Validating claims. ${JSON.stringify({ ticket, claims })}`); for (const key of Object.keys(claims)) { ticket.provided[key] = claims[key]; @@ -47,7 +46,7 @@ export class ClaimEliminationStrategy implements TicketingStrategy { const requirement = requirements[key]; if (requirement && await requirement(claims[key])) { - delete requirements[key]; + delete requirements[key]; } } } @@ -57,10 +56,10 @@ export class ClaimEliminationStrategy implements TicketingStrategy { /** @inheritdoc {@link TicketingStrategy.resolveTicket} */ async resolveTicket(ticket: Ticket): Promise> { - this.logger.debug('Resolving ticket.', ticket); - - return ticket.required.some(req => Object.keys(req).length === 0) - ? Success(ticket.permissions) + this.logger.debug(`Resolving ticket. ${JSON.stringify(ticket)}`); + + return ticket.required.some(req => Object.keys(req).length === 0) + ? Success(ticket.permissions) : Failure(ticket.required); } } diff --git a/packages/uma/src/ticketing/strategy/ImmediateAuthorizerStrategy.ts b/packages/uma/src/ticketing/strategy/ImmediateAuthorizerStrategy.ts index 67c8de28..a4ad2614 100644 --- a/packages/uma/src/ticketing/strategy/ImmediateAuthorizerStrategy.ts +++ b/packages/uma/src/ticketing/strategy/ImmediateAuthorizerStrategy.ts @@ -1,11 +1,10 @@ +import { getLoggerFor } from '@solid/community-server'; import { ClaimSet } from "../../credentials/ClaimSet"; import { Ticket } from "../Ticket"; import { Permission } from "../../views/Permission"; import { Failure, Result, Success } from "../../util/Result"; import { TicketingStrategy } from "./TicketingStrategy"; import { Authorizer } from "../../policies/authorizers/Authorizer"; -import { getLoggerFor } from "../../util/logging/LoggerUtils"; -import { Logger } from "../../util/logging/Logger"; import type { Requirements } from "../../credentials/Requirements"; /** @@ -13,7 +12,7 @@ import type { Requirements } from "../../credentials/Requirements"; * available Permissions from them upon resolution. */ export class ImmediateAuthorizerStrategy implements TicketingStrategy { - protected readonly logger: Logger = getLoggerFor(this); + protected readonly logger = getLoggerFor(this); constructor( private authorizer: Authorizer, @@ -21,7 +20,7 @@ export class ImmediateAuthorizerStrategy implements TicketingStrategy { /** @inheritdoc */ async initializeTicket(permissions: Permission[]): Promise { - this.logger.info('Initializing ticket.', permissions) + this.logger.info(`Initializing ticket. ${JSON.stringify(permissions)}`) return ({ permissions, @@ -32,8 +31,8 @@ export class ImmediateAuthorizerStrategy implements TicketingStrategy { /** @inheritdoc */ async validateClaims(ticket: Ticket, claims: ClaimSet): Promise { - this.logger.info('Validating claims.', { ticket, claims }); - + this.logger.info(`Validating claims. ${JSON.stringify({ ticket, claims })}`); + for (const key of Object.keys(claims)) { ticket.provided[key] = claims[key]; } @@ -43,7 +42,7 @@ export class ImmediateAuthorizerStrategy implements TicketingStrategy { /** @inheritdoc */ async resolveTicket(ticket: Ticket): Promise> { - this.logger.info('Resolving ticket.', ticket); + this.logger.info(`Resolving ticket. ${JSON.stringify(ticket)}`); const permissions = await this.calculatePermissions(ticket); diff --git a/packages/uma/src/tokens/JwtTokenFactory.ts b/packages/uma/src/tokens/JwtTokenFactory.ts index fbff7a20..4af3a7c1 100644 --- a/packages/uma/src/tokens/JwtTokenFactory.ts +++ b/packages/uma/src/tokens/JwtTokenFactory.ts @@ -1,20 +1,19 @@ -import { BadRequestHttpError } from '../util/http/errors/BadRequestHttpError'; -import { Logger } from '../util/logging/Logger'; -import { getLoggerFor } from '../util/logging/LoggerUtils'; import { importJWK, jwtVerify, SignJWT } from 'jose'; -import { v4 } from 'uuid'; -import { JwkGenerator } from '@solid/community-server'; -import { isString } from '../util/StringGuard'; +import { + BadRequestHttpError, + getLoggerFor, + HttpErrorClass, + JwkGenerator, + KeyValueStorage +} from '@solid/community-server'; +import { randomUUID } from 'node:crypto'; import { SerializedToken , TokenFactory} from './TokenFactory'; import { AccessToken } from './AccessToken'; import { array, reType } from '../util/ReType'; import { Permission } from '../views/Permission'; -import { KeyValueStore } from '../util/storage/models/KeyValueStore'; const AUD = 'solid'; -type ErrorConstructor = { new(msg: string): Error }; - export interface JwtTokenParams { expirationTime: string | number aud: string @@ -24,7 +23,7 @@ export interface JwtTokenParams { * A TokenFactory yielding its tokens as signed JWTs. */ export class JwtTokenFactory extends TokenFactory { - protected readonly logger: Logger = getLoggerFor(this); + protected readonly logger = getLoggerFor(this); /** * Construct a new ticket factory @@ -32,10 +31,10 @@ export class JwtTokenFactory extends TokenFactory { * @param {KeyValueStore} tokenStore */ constructor( - private readonly keyGen: JwkGenerator, + private readonly keyGen: JwkGenerator, private readonly issuer: string, private readonly params: JwtTokenParams = {expirationTime: '30m', aud: 'solid'}, - private readonly tokenStore: KeyValueStore + private readonly tokenStore: KeyValueStorage ) { super(); } @@ -54,10 +53,10 @@ export class JwtTokenFactory extends TokenFactory { .setIssuer(this.issuer) .setAudience(AUD) .setExpirationTime(this.params.expirationTime) - .setJti(v4()) + .setJti(randomUUID()) .sign(jwk); - this.logger.debug('Issued new JWT Token', JSON.stringify(token, null, 2)); + this.logger.debug(`Issued new JWT Token ${JSON.stringify(token)}`); await this.tokenStore.set(jwt, token); return {token: jwt, tokenType: 'Bearer'}; } @@ -80,8 +79,8 @@ export class JwtTokenFactory extends TokenFactory { throw new Error('Missing JWT parameter(s): {sub, aud, permissions, webid, azp} are required.'); } - if (!isString(payload.webid)) throw new Error('JWT claim "webid" is not a string.'); - if (!isString(payload.azp)) throw new Error('JWT claim "azp" is not a string.'); + if (typeof payload.webid !== 'string') throw new Error('JWT claim "webid" is not a string.'); + if (typeof payload.azp !== 'string') throw new Error('JWT claim "azp" is not a string.'); const permissions = payload.permissions; @@ -96,10 +95,10 @@ export class JwtTokenFactory extends TokenFactory { /** * Logs and throws an error * - * @param {ErrorConstructor} constructor - the error constructor + * @param {HttpErrorClass} constructor - the error constructor * @param {string} message - the error message */ - private error(constructor: ErrorConstructor, message: string): never { + private error(constructor: HttpErrorClass, message: string): never { this.logger.warn(message); throw new constructor(message); } diff --git a/packages/uma/src/tokens/OpaqueTokenFactory.ts b/packages/uma/src/tokens/OpaqueTokenFactory.ts index b4cdb462..5ca8846c 100644 --- a/packages/uma/src/tokens/OpaqueTokenFactory.ts +++ b/packages/uma/src/tokens/OpaqueTokenFactory.ts @@ -1,7 +1,7 @@ +import { KeyValueStorage } from '@solid/community-server'; +import { randomUUID } from 'node:crypto'; import {AccessToken} from './AccessToken'; import {SerializedToken, TokenFactory} from './TokenFactory'; -import {KeyValueStore} from '../util/storage/models/KeyValueStore'; -import {v4} from 'uuid'; /** * A TokenFactory that serializes to an opaque string @@ -9,9 +9,9 @@ import {v4} from 'uuid'; export class OpaqueTokenFactory extends TokenFactory { /** * - * @param {KeyValueStore} tokenStore + * @param {KeyValueStorage} tokenStore */ - constructor(private tokenStore: KeyValueStore) { + constructor(private tokenStore: KeyValueStorage) { super(); } @@ -21,7 +21,7 @@ export class OpaqueTokenFactory extends TokenFactory { * @return {Promise} */ public async serialize(token: AccessToken): Promise { - const serialized = v4(); + const serialized = randomUUID(); await this.tokenStore.set(serialized, token); return {tokenType: 'Bearer', token: serialized}; } diff --git a/packages/uma/src/util/ConvertUtil.ts b/packages/uma/src/util/ConvertUtil.ts new file mode 100644 index 00000000..b449c61a --- /dev/null +++ b/packages/uma/src/util/ConvertUtil.ts @@ -0,0 +1,36 @@ +import { parse, stringify } from 'node:querystring'; + +/** + * Converts a x-www-form-urlencoded string to a JSON object. + */ +export function formToJson(form: string): unknown { + return parse(form); +} + +/** + * Converts a JSON object to a x-www-form-urlencoded, if possible. + */ +export function jsonToForm(json: unknown): string { + if (typeof json !== 'object' || json === null) { + throw new Error('Can only convert JSON objects to urlencoded string.'); + } + + for (const val of Object.values(json)) { + if (Array.isArray(val)) { + if (!val.every(isPrimitive)) { + throw new Error('Can only convert JSON objects with primitives or arrays of primitives to urlencoded string.'); + } + } else if (!isPrimitive(val)) { + throw new Error('Can only convert JSON objects with primitives or arrays of primitives to urlencoded string.'); + } + } + + return stringify(json as Record); +} + +/** + * Checks if the input is a string, number, or boolean. + */ +export function isPrimitive(val: unknown): val is string | number | boolean { + return typeof val === 'string' || typeof val === 'number' || typeof val === 'boolean'; +} diff --git a/packages/uma/src/util/HttpMessageSignatures.ts b/packages/uma/src/util/HttpMessageSignatures.ts index d379e273..bcbe1cfc 100644 --- a/packages/uma/src/util/HttpMessageSignatures.ts +++ b/packages/uma/src/util/HttpMessageSignatures.ts @@ -2,15 +2,15 @@ import { UnauthorizedHttpError, type AlgJwk, BadRequestHttpError, InternalServer import { httpbis, type SigningKey, type Request as SignRequest } from 'http-message-signatures'; import { verifyMessage } from 'http-message-signatures/lib/httpbis'; import { type SignatureParameters, type VerifierFinder, type VerifyingKey } from 'http-message-signatures/lib/types'; -import type { HttpHandlerRequest } from './http/models/HttpHandlerRequest'; +import { HttpHandlerRequest } from './http/models/HttpHandler'; import buildGetJwks from 'get-jwks'; import crypto from 'node:crypto'; const authParserMod = import('@httpland/authorization-parser'); export async function signRequest( - url: string, - request: RequestInit & Omit, + url: string, + request: RequestInit & Omit, jwk: AlgJwk ): Promise { const key: SigningKey = { @@ -52,10 +52,10 @@ export async function verifyRequest( signer?: string, ): Promise { signer = signer ?? await extractRequestSigner(request); - + if (signer.startsWith('"')) signer = signer.slice(1); if (signer.endsWith('"')) signer = signer.slice(0,-1); - + const jwks = buildGetJwks(); const keyLookup: VerifierFinder = async (params: SignatureParameters) => { @@ -67,7 +67,7 @@ export async function verifyRequest( alg: alg ?? '', kid: keyid ?? '', }) - + if (!alg) throw new BadRequestHttpError('Invalid HTTP message Signature parameters.'); // if (alg === 'EdDSA') throw new InternalServerError('EdDSA signing is not supported'); // if (alg === 'ES256K') throw new InternalServerError('ES256K signing is not supported'); @@ -83,7 +83,7 @@ export async function verifyRequest( } catch (err) { console.log(err); return null } }, }; - + return verifier; } catch (err) { @@ -111,4 +111,3 @@ const algMap: Record = { 'RS384': { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-384' }, 'RS512': { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-512' }, } - diff --git a/packages/uma/src/util/StringGuard.ts b/packages/uma/src/util/StringGuard.ts deleted file mode 100644 index 848b0d11..00000000 --- a/packages/uma/src/util/StringGuard.ts +++ /dev/null @@ -1,8 +0,0 @@ -/** - * Is value a string - * @param {any} value - value to be checked - * @return {boolean} - true if value is a string - */ -export function isString(value: any): value is string { - return typeof value === 'string' || value instanceof String; -} diff --git a/packages/uma/src/util/ThrowingLogger.ts b/packages/uma/src/util/ThrowingLogger.ts deleted file mode 100644 index 9cc5750e..00000000 --- a/packages/uma/src/util/ThrowingLogger.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { Logger, LoggerLevel } from "./logging/Logger"; - -export type ErrorConstructor = { new(msg: string): Error }; - -export class ThrowingLogger extends Logger { - constructor(private logger: Logger) { - super( - logger.label, - logger['minimumLevel'], - logger['minimumLevelPrintData'], - ); - } - - /** - * Logs and throws an error - * - * @param {ErrorConstructor} constructor - The error constructor. - * @param {string} message - The error message. - * @param {unknown} data - Optional data to log. - * @param {LoggerLevel} level - The level to log, defaults to 'warn'. - * - * @throws An Error constructed with the provided constructor, containing the - * provided message. - */ - throw( - constructor: ErrorConstructor, - message: string, - data?: unknown, - level: LoggerLevel = LoggerLevel.warn - ): never { - this.logger.log(level, message, data); - throw new constructor(message); - } - - /* All other methods are implemented by the dependency. */ - - setLabel(label: string | { constructor: { name: string; }; }): Logger { return this.logger.setLabel(label); } - - setVariable(key: string, value: string): Logger { return this.logger.setVariable(key, value); } - removeVariable(key: string): Logger { return this.logger.removeVariable(key); } - clearVariables(): Logger { return this.logger.clearVariables(); } - getVariables(): Record { return this.logger.getVariables(); } - - fatal(message: string, data?: unknown): void { return this.logger.fatal(message, data); } - error(message: string, data?: unknown): void { return this.logger.error(message, data); } - warn(message: string, data?: unknown): void { return this.logger.warn(message, data); } - info(message: string, data?: unknown): void { return this.logger.info(message, data); } - debug(message: string, data?: unknown): void { return this.logger.debug(message, data); } - trace(message: string, data?: unknown): void { return this.logger.trace(message, data); } - - log(level: LoggerLevel, message: string, data?: unknown): void { return this.logger.log(level, message, data); } -} \ No newline at end of file diff --git a/packages/uma/src/util/UrlGuard.ts b/packages/uma/src/util/UrlGuard.ts deleted file mode 100644 index dd008acd..00000000 --- a/packages/uma/src/util/UrlGuard.ts +++ /dev/null @@ -1,30 +0,0 @@ -import {isString} from './StringGuard'; - -export type UrlString = string; - -/** - * Is this string a valid URL? - * - * @param {any} value - potential URL - * @return {boolean} - true if is a string - */ -export function isUrlString(value: any): value is UrlString { - return isString(value) && isValidHttpUrl(value); -} - -/** - * Checks whether a string value is an HTTP URL - * @param {string} value - * @return {boolean} - */ -function isValidHttpUrl(value: string ): boolean { - let url; - - try { - url = new URL(value); - } catch (_) { - return false; - } - - return url.protocol === 'http:' || url.protocol === 'https:'; -} diff --git a/packages/uma/src/util/http/LICENSE b/packages/uma/src/util/http/LICENSE deleted file mode 100644 index c1ce5c8e..00000000 --- a/packages/uma/src/util/http/LICENSE +++ /dev/null @@ -1,52 +0,0 @@ - -The code in this directory was published by Digita BV under the MIT license. -Their code in turn was based on original work published under the same license -by the Solid Community. - -=============================================================================== - -MIT License - -Copyright (c) 2023 Digita BV - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - -=============================================================================== - -MIT License - -Copyright (c) 2020 Solid Community - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/packages/uma/src/util/http/errors/BadRequestHttpError.ts b/packages/uma/src/util/http/errors/BadRequestHttpError.ts deleted file mode 100644 index a07d2010..00000000 --- a/packages/uma/src/util/http/errors/BadRequestHttpError.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { HttpError } from './HttpError'; - -/** - * An error thrown when incoming data is not supported. - */ -export class BadRequestHttpError extends HttpError { - - /** - * Default message is 'The given input is not supported by the server configuration.'. - * - * @param message - Optional, more specific, message. - */ - constructor(message?: string) { - - super(400, 'BadRequestHttpError', message ?? 'The given input is not supported by the server configuration.'); - - } - - static isInstance(error: unknown): error is BadRequestHttpError { - - const errorIsInstance = HttpError.isInstance(error) && error.statusCode === 400; - - this.logger.info(`Checking if ${error} is an instance of ${this.name}: `, errorIsInstance); - - return errorIsInstance; - - } - -} diff --git a/packages/uma/src/util/http/errors/ForbiddenHttpError.ts b/packages/uma/src/util/http/errors/ForbiddenHttpError.ts deleted file mode 100644 index 0b041c50..00000000 --- a/packages/uma/src/util/http/errors/ForbiddenHttpError.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { HttpError } from './HttpError'; - -/** - * An error thrown when an agent is not allowed to access data. - */ -export class ForbiddenHttpError extends HttpError { - - constructor(message?: string) { - - super(403, 'ForbiddenHttpError', message); - - } - - static isInstance(error: unknown): error is ForbiddenHttpError { - - const errorIsInstance = HttpError.isInstance(error) && error.statusCode === 403; - - this.logger.info(`Checking if ${error} is an instance of ${this.name}: `, errorIsInstance); - - return errorIsInstance; - - } - -} diff --git a/packages/uma/src/util/http/errors/HttpError.ts b/packages/uma/src/util/http/errors/HttpError.ts deleted file mode 100644 index 096eaec7..00000000 --- a/packages/uma/src/util/http/errors/HttpError.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { types } from 'util'; -import { getLogger } from '../../logging/LoggerUtils'; - -/** - * A class for all errors that could be thrown by Solid. - * All errors inheriting from this should fix the status code thereby hiding the HTTP internals from other components. - */ -export class HttpError extends Error { - - protected static readonly statusCode: number; - public readonly statusCode: number; - protected static readonly logger = getLogger(); - - /** - * Creates a new HTTP error. Subclasses should call this with their fixed status code. - * - * @param statusCode - HTTP status code needed for the HTTP response. - * @param name - Error name. Useful for logging and stack tracing. - * @param message - Message to be thrown. - */ - constructor(statusCode: number, name: string, message?: string) { - - super(message); - this.statusCode = statusCode; - this.name = name; - - } - - static isInstance(error: unknown): error is HttpError { - - this.logger.info(`Checking if ${error} is an instance of ${this.name}`); - - return types.isNativeError(error) && Object.entries(error).find( - ([ key, val ]) => key === 'statusCode' && typeof val === 'number' - ) !== undefined; - - } - -} diff --git a/packages/uma/src/util/http/errors/InternalServerError.ts b/packages/uma/src/util/http/errors/InternalServerError.ts deleted file mode 100644 index 2f372449..00000000 --- a/packages/uma/src/util/http/errors/InternalServerError.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { HttpError } from './HttpError'; - -/** - * A generic error message, given when an unexpected condition was encountered and no more specific message is suitable. - */ -export class InternalServerError extends HttpError { - - constructor(message?: string) { - - super(500, 'InternalServerError', message); - - } - - static isInstance(error: unknown): error is InternalServerError { - - const errorIsInstance = HttpError.isInstance(error) && error.statusCode === 500; - - this.logger.info(`Checking if ${error} is an instance of ${this.name}: `, errorIsInstance); - - return errorIsInstance; - - } - -} diff --git a/packages/uma/src/util/http/errors/MethodNotAllowedHttpError.ts b/packages/uma/src/util/http/errors/MethodNotAllowedHttpError.ts deleted file mode 100644 index daa1f990..00000000 --- a/packages/uma/src/util/http/errors/MethodNotAllowedHttpError.ts +++ /dev/null @@ -1,25 +0,0 @@ - -import { HttpError } from './HttpError'; - -/** - * An error thrown when data was found for the requested identifier, but is not supported by the target resource. - */ -export class MethodNotAllowedHttpError extends HttpError { - - constructor(message?: string) { - - super(405, 'MethodNotAllowedHttpError', message); - - } - - static isInstance(error: unknown): error is MethodNotAllowedHttpError { - - const errorIsInstance = HttpError.isInstance(error) && error.statusCode === 405; - - this.logger.info(`Checking if ${error} is an instance of ${this.name}: `, errorIsInstance); - - return errorIsInstance; - - } - -} \ No newline at end of file diff --git a/packages/uma/src/util/http/errors/UnauthorizedHttpError.ts b/packages/uma/src/util/http/errors/UnauthorizedHttpError.ts deleted file mode 100644 index bd8c2a21..00000000 --- a/packages/uma/src/util/http/errors/UnauthorizedHttpError.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { HttpError } from './HttpError'; - -/** - * An error thrown when an agent is not authorized. - */ -export class UnauthorizedHttpError extends HttpError { - - constructor(message?: string) { - - super(401, 'UnauthorizedHttpError', message); - - } - - static isInstance(error: unknown): error is UnauthorizedHttpError { - - const errorIsInstance = HttpError.isInstance(error) && error.statusCode === 401; - - this.logger.info(`Checking if ${error} is an instance of ${this.name}: `, errorIsInstance); - - return errorIsInstance; - - } - -} diff --git a/packages/uma/src/util/http/errors/UnsupportedMediaTypeHttpError.ts b/packages/uma/src/util/http/errors/UnsupportedMediaTypeHttpError.ts deleted file mode 100644 index ea2b0e59..00000000 --- a/packages/uma/src/util/http/errors/UnsupportedMediaTypeHttpError.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { HttpError } from './HttpError'; - -/** - * An error thrown when the media type of incoming data is not supported by a parser. - */ -export class UnsupportedMediaTypeHttpError extends HttpError { - - constructor(message?: string) { - - super(415, 'UnsupportedMediaTypeHttpError', message); - - } - - static isInstance(error: unknown): error is UnsupportedMediaTypeHttpError { - - const errorIsInstance = HttpError.isInstance(error) && error.statusCode === 415; - - this.logger.info(`Checking if ${error} is an instance of ${this.name}: `, errorIsInstance); - - return errorIsInstance; - - } - -} diff --git a/packages/uma/src/util/http/identifier/BaseTargetExtractor.ts b/packages/uma/src/util/http/identifier/BaseTargetExtractor.ts new file mode 100644 index 00000000..1d7f90f8 --- /dev/null +++ b/packages/uma/src/util/http/identifier/BaseTargetExtractor.ts @@ -0,0 +1,32 @@ +import { HttpRequest, OriginalUrlExtractor, ResourceIdentifier, TargetExtractor } from '@solid/community-server'; + +/** + * Class designed to wrap around the CSS OriginalUrlExtractor, + * so we don't need an identifier strategy. + * Ideally the original CSS class would be changed to split off that functionality. + */ +export class BaseTargetExtractor extends TargetExtractor { + protected readonly extractor: TargetExtractor; + + public constructor(includeQueryString = false) { + super(); + this.extractor = new OriginalUrlExtractor({ + includeQueryString, + identifierStrategy: { + // Only this one matters + supportsIdentifier: () => true, + contains: () => false, + getParentContainer: () => ({ path: '' }), + isRootContainer: () => true, + } + }) + } + + public async canHandle(input: { request: HttpRequest }): Promise { + return this.extractor.canHandle(input); + } + + handle(input: { request: HttpRequest }): Promise { + return this.extractor.handle(input); + } +} diff --git a/packages/uma/src/util/http/models/Daemon.ts b/packages/uma/src/util/http/models/Daemon.ts deleted file mode 100644 index 8ab1b8b7..00000000 --- a/packages/uma/src/util/http/models/Daemon.ts +++ /dev/null @@ -1,17 +0,0 @@ - -/** - * This class represents typically long-running daemon processes that can be started and stopped. - */ -export abstract class Daemon { - - /** - * Start the server - */ - abstract start(): Promise; - - /** - * Stop the server - */ - abstract stop(): Promise; - -} diff --git a/packages/uma/src/util/http/models/Handler.ts b/packages/uma/src/util/http/models/Handler.ts deleted file mode 100644 index 74386ee4..00000000 --- a/packages/uma/src/util/http/models/Handler.ts +++ /dev/null @@ -1,6 +0,0 @@ - -export abstract class Handler { - - abstract handle(input: T): Promise; - -} diff --git a/packages/uma/src/util/http/models/HttpHandler.ts b/packages/uma/src/util/http/models/HttpHandler.ts index 9fbfcf90..6650b472 100644 --- a/packages/uma/src/util/http/models/HttpHandler.ts +++ b/packages/uma/src/util/http/models/HttpHandler.ts @@ -1,7 +1,23 @@ -import { Handler } from './Handler'; -import { HttpHandlerResponse } from './HttpHandlerResponse'; -import { HttpHandlerContext } from './HttpHandlerContext'; +import { AsyncHandler } from '@solid/community-server'; +import { OutgoingHttpHeaders } from 'http'; -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export abstract class HttpHandler - extends Handler { } +export interface HttpHandlerContext { + request: HttpHandlerRequest; +} + +export interface HttpHandlerRequest { + url: URL; + method: string; + parameters?: { [key: string]: string }; + headers: { [key: string]: string }; + body?: B; +} + +export interface HttpHandlerResponse { + body?: B; + headers?: OutgoingHttpHeaders; + status: number; +} + +export abstract class HttpHandler + extends AsyncHandler> { } diff --git a/packages/uma/src/util/http/models/HttpHandlerContext.ts b/packages/uma/src/util/http/models/HttpHandlerContext.ts deleted file mode 100644 index a5ca925f..00000000 --- a/packages/uma/src/util/http/models/HttpHandlerContext.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { HttpHandlerRequest } from './HttpHandlerRequest'; -import { HttpHandlerRoute } from './HttpHandlerRoute'; - -export interface HttpHandlerContext { - request: HttpHandlerRequest; - route?: HttpHandlerRoute; -} diff --git a/packages/uma/src/util/http/models/HttpHandlerController.ts b/packages/uma/src/util/http/models/HttpHandlerController.ts deleted file mode 100644 index a81ad06a..00000000 --- a/packages/uma/src/util/http/models/HttpHandlerController.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Handler } from './Handler'; -import { HttpHandlerContext } from './HttpHandlerContext'; -import { HttpHandlerRoute } from './HttpHandlerRoute'; - -export class HttpHandlerController { - - constructor( - public label: string, - public routes: HttpHandlerRoute[], - public preResponseHandler?: Handler, - ) { } - -} diff --git a/packages/uma/src/util/http/models/HttpHandlerRequest.ts b/packages/uma/src/util/http/models/HttpHandlerRequest.ts deleted file mode 100644 index e895d6cd..00000000 --- a/packages/uma/src/util/http/models/HttpHandlerRequest.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { HttpMethod } from './HttpMethod'; - -export interface HttpHandlerRequest { - url: URL; - method: HttpMethod; - parameters?: { [key: string]: string }; - headers: { [key: string]: string }; - body?: B; -} diff --git a/packages/uma/src/util/http/models/HttpHandlerResponse.ts b/packages/uma/src/util/http/models/HttpHandlerResponse.ts deleted file mode 100644 index f4fc24ec..00000000 --- a/packages/uma/src/util/http/models/HttpHandlerResponse.ts +++ /dev/null @@ -1,5 +0,0 @@ -export interface HttpHandlerResponse { - body?: B; - headers?: { [key: string]: string }; - status: number; -} diff --git a/packages/uma/src/util/http/models/HttpHandlerRoute.ts b/packages/uma/src/util/http/models/HttpHandlerRoute.ts index 622b378c..e8daf4bb 100644 --- a/packages/uma/src/util/http/models/HttpHandlerRoute.ts +++ b/packages/uma/src/util/http/models/HttpHandlerRoute.ts @@ -1,61 +1,9 @@ -import { HttpHandler } from './HttpHandler'; -import { HttpHandlerContext } from './HttpHandlerContext'; - -export abstract class HttpHandlerOperationMediaPayload { -} - -export abstract class HttpHandlerOperationMedia { - - constructor( - public type: string, - public example?: HttpHandlerOperationMediaPayload, - ) {} - -} - -export abstract class HttpHandlerOperationHeader{ - - constructor( - public type: string, - public description: string, - ) {} - -} - -export abstract class HttpHandlerOperationResponse { - - constructor( - public code: number, - public description?: string, - public body?: HttpHandlerOperationMedia[], - public headers?: HttpHandlerOperationHeader[], - ) {} - -} - -export enum HttpHandlerOperationSecurityType { - BEARER = 'Bearer', -} - -export abstract class HttpHandlerOperation { - - constructor( - public method: string, - public publish: boolean, - public description?: string, - public responses?: HttpHandlerOperationResponse[], - public security?: HttpHandlerOperationSecurityType, - public vary?: string[], - ) {} - -} +import { HttpHandler, HttpHandlerContext } from './HttpHandler'; export abstract class HttpHandlerRoute { - constructor( - public operations: HttpHandlerOperation[], public path: string, public handler: HttpHandler, + public methods?: string[], ) {} - } diff --git a/packages/uma/src/util/http/models/HttpMethod.ts b/packages/uma/src/util/http/models/HttpMethod.ts deleted file mode 100644 index 1f2683e6..00000000 --- a/packages/uma/src/util/http/models/HttpMethod.ts +++ /dev/null @@ -1,14 +0,0 @@ - -export enum HttpMethods { - CONNECT = 'CONNECT', - DELETE = 'DELETE', - GET = 'GET', - HEAD = 'HEAD', - OPTIONS = 'OPTIONS', - PATCH = 'PATCH', - POST = 'POST', - PUT = 'PUT', - TRACE = 'TRACE', -} - -export type HttpMethod = keyof typeof HttpMethods; diff --git a/packages/uma/src/util/http/models/Server.ts b/packages/uma/src/util/http/models/Server.ts deleted file mode 100644 index 7e9b11a4..00000000 --- a/packages/uma/src/util/http/models/Server.ts +++ /dev/null @@ -1,36 +0,0 @@ -import EventEmitter from 'stream'; -import { Daemon } from './Daemon'; - - -/** - * A {Daemon} process listening on a given `scheme``://``host``:``port` location - * - */ -export abstract class Server extends EventEmitter implements Daemon { - - /** - * Creates a new {Server} that will listen on specified `scheme``://``host``:``port`. - * - * @param {string} scheme - the url scheme of the location on which the server will listen - * @param {string} host - the host name of the location on which the server will listen - * @param {number} port - the port number of the location on which the server will listen - */ - constructor (protected scheme: string, protected host: string, protected port: number) { - - super(); - - } - - /** - * @override - * { @inheritDoc Server.start } - */ - abstract start(): Promise; - - /** - * @override - * { @inheritDoc Server.stop } - */ - abstract stop(): Promise; - -} diff --git a/packages/uma/src/util/http/server/CorsRequestHandler.ts b/packages/uma/src/util/http/server/CorsRequestHandler.ts deleted file mode 100644 index 279bb69c..00000000 --- a/packages/uma/src/util/http/server/CorsRequestHandler.ts +++ /dev/null @@ -1,137 +0,0 @@ -import { getLogger } from '../../logging/LoggerUtils'; -import { HttpHandler } from '../models/HttpHandler'; -import { HttpHandlerContext } from '../models/HttpHandlerContext'; -import { HttpHandlerResponse } from '../models/HttpHandlerResponse'; - -export const cleanHeaders = (headers: Record): Record => Object.entries(headers).reduce( - (acc: Record, [ key, value ]) => { - - const lKey = key.toLowerCase(); - - return { ... acc, [lKey]: acc[lKey] ? `${acc[lKey]},${value}` : value }; - - }, {}, -); - -export interface HttpCorsOptions { - origins?: string[]; - allowMethods?: string[]; - allowHeaders?: string[]; - exposeHeaders?: string[]; - credentials?: boolean; - maxAge?: number; -} -export class CorsRequestHandler implements HttpHandler { - - public logger = getLogger(); - - constructor( - private handler: HttpHandler, - private options?: HttpCorsOptions, - private passThroughOptions: boolean = false, - ) { } - - async handle(context: HttpHandlerContext): Promise { - - const { origins, allowMethods, allowHeaders, exposeHeaders, credentials, maxAge } = this.options || ({}); - - const requestHeaders = context.request.headers; - - const cleanRequestHeaders = cleanHeaders(requestHeaders); - - const { - /* eslint-disable-next-line @typescript-eslint/no-unused-vars -- destructuring for removal */ - ['access-control-request-method']: requestedMethod, - ['access-control-request-headers']: requestedHeaders, - ... noCorsHeaders - } = cleanRequestHeaders; - - const noCorsRequestContext = { - ... context, - request: { - ... context.request, - headers: { - ... noCorsHeaders, - }, - }, - }; - - const requestedOrigin = cleanRequestHeaders.origin ?? ''; - - const allowOrigin = origins - ? origins.includes(requestedOrigin) - ? requestedOrigin - : undefined - : credentials - ? requestedOrigin - : '*'; - - const allowHeadersOrRequested = allowHeaders?.join(',') ?? requestedHeaders; - - if (context.request.method === 'OPTIONS') { - - /* Preflight Request */ - - this.logger.debug('Processing preflight request'); - - const routeMethods = context.route?.operations.map((op) => op.method); - const allMethods = [ 'GET', 'HEAD', 'PUT', 'POST', 'DELETE', 'PATCH' ]; - - const initialOptions = this.passThroughOptions - ? this.handler.handle(noCorsRequestContext) - : Promise.resolve({ status: 204, headers: {} }); - - return initialOptions - .then((response) => ({ - ... response, - headers: response.headers ? cleanHeaders(response.headers) : {}, - })) - .then((response) => ({ - ... response, - headers: { - - ... response.headers, - ... allowOrigin && ({ - ... (allowOrigin !== '*') && { - 'vary': [ ... new Set([ - ... response.headers.vary?.split(',').map((v) => v.trim().toLowerCase()) ?? [], `origin` - ]) ].join(', ') - }, - 'access-control-allow-origin': allowOrigin, - 'access-control-allow-methods': (allowMethods ?? routeMethods ?? allMethods).join(', '), - ... (allowHeadersOrRequested) && { 'access-control-allow-headers': allowHeadersOrRequested }, - ... (credentials) && { 'access-control-allow-credentials': 'true' }, - 'access-control-max-age': (maxAge ?? -1).toString(), - }), - }, - })); - - } else { - - /* CORS Request */ - - this.logger.debug('Processing CORS request'); - - return this.handler.handle(noCorsRequestContext) - .then((response) => ({ - ... response, - headers: { - ... response.headers, - ... allowOrigin && ({ - 'access-control-allow-origin': allowOrigin, - ... (allowOrigin !== '*') && { - 'vary': [ ... new Set([ - ... response.headers?.vary?.split(',').map((v) => v.trim().toLowerCase()) ?? [], `origin` - ]) ].join(', ') - }, - ... (credentials) && { 'access-control-allow-credentials': 'true' }, - ... (exposeHeaders) && { 'access-control-expose-headers': exposeHeaders.join(',') }, - }), - }, - })); - - } - - } - -} diff --git a/packages/uma/src/util/http/server/JsonFormHttpHandler.ts b/packages/uma/src/util/http/server/JsonFormHttpHandler.ts new file mode 100644 index 00000000..0cb26e90 --- /dev/null +++ b/packages/uma/src/util/http/server/JsonFormHttpHandler.ts @@ -0,0 +1,112 @@ +import { + APPLICATION_JSON, + APPLICATION_LD_JSON, + APPLICATION_X_WWW_FORM_URLENCODED, + getConversionTarget, + InternalServerError, + NotImplementedHttpError, + parseAccept, + parseContentType, + UnsupportedMediaTypeHttpError, + ValuePreferences +} from '@solid/community-server'; +import { formToJson, jsonToForm } from '../../ConvertUtil'; +import { HttpHandler, HttpHandlerContext, HttpHandlerResponse } from '../models/HttpHandler'; + +/** + * A {@link HttpHandler} that takes raw Buffer data from a request and converts it into a usable object, + * to be used by the source handler. + * The response then gets converted back to a Buffer. + * Supports JSON and x-www-form-urlencoded input bodies. + */ +export class JsonFormHttpHandler extends HttpHandler, Buffer> { + protected readonly cache: WeakMap + + public constructor( + protected readonly handler: HttpHandler, + ) { + super(); + this.cache = new WeakMap(); + } + + public async canHandle(context: HttpHandlerContext): Promise { + let body: unknown | undefined; + if (context.request.body) { + const contentType = parseContentType(context.request.headers['content-type']); + if (contentType.value === APPLICATION_X_WWW_FORM_URLENCODED) { + body = formToJson(context.request.body.toString()); + } else if (contentType.value === APPLICATION_JSON || contentType.value === APPLICATION_LD_JSON) { + body = JSON.parse(context.request.body.toString()); + } else { + throw new UnsupportedMediaTypeHttpError('Only JSON and urlencoded bodies are accepted.'); + } + } + + const parsedContext: HttpHandlerContext = body ? { + ...context, + request: { + ...context.request, + body + } + } : context; + + await this.handler.canHandle(parsedContext); + + this.cache.set(context, { context: parsedContext }); + } + + public async handle(context: HttpHandlerContext): Promise> { + const cached = this.cache.get(context); + if (!cached) { + throw new InternalServerError('Calling handle before calling canHandle.'); + } + + const response = await this.handler.handle(cached.context); + + if (!response.body) { + return response as HttpHandlerResponse; + } + + let responseData: Buffer | undefined; + let outputType = response.headers?.['content-type']; + if (outputType) { + if (typeof response.body === 'string') { + responseData = Buffer.from(response.body, 'utf8'); + } else if (Buffer.isBuffer(response.body)) { + responseData = response.body; + } + } + + // No content type given by handler, so assume it is a JSON object + if (!outputType) { + // Determine what to return based on the accept header + let preferences: ValuePreferences = {}; + const acceptHeader = context.request.headers['accept']; + if (acceptHeader) { + preferences = Object.fromEntries(parseAccept(acceptHeader) + .map(({ range, weight }): [string, number] => [ range, weight ])); + } + outputType = getConversionTarget( + { [APPLICATION_JSON]: 1, [APPLICATION_X_WWW_FORM_URLENCODED]: 0.9 }, + preferences, + ); + if (!outputType) { + throw new NotImplementedHttpError('Unable to convert output to JSON or urlencoded data.'); + } + + responseData = Buffer.from(outputType === APPLICATION_JSON ? + JSON.stringify(response.body) : + jsonToForm(response.body)); + } + + // TS does not realize that this has the correct body type + return { + ...response, + ...responseData && { body: responseData }, + headers: { + ...response.headers, + ...responseData && { 'content-type': outputType }, + } + } as HttpHandlerResponse; + } +} diff --git a/packages/uma/src/util/http/server/ErrorHandler.ts b/packages/uma/src/util/http/server/JsonHttpErrorHandler.ts similarity index 59% rename from packages/uma/src/util/http/server/ErrorHandler.ts rename to packages/uma/src/util/http/server/JsonHttpErrorHandler.ts index 452255f3..4de75ff8 100644 --- a/packages/uma/src/util/http/server/ErrorHandler.ts +++ b/packages/uma/src/util/http/server/JsonHttpErrorHandler.ts @@ -1,8 +1,5 @@ -import {HttpHandler} from '../models/HttpHandler'; -import {HttpHandlerContext} from '../models/HttpHandlerContext'; -import {HttpHandlerResponse} from '../models/HttpHandlerResponse'; -import {Logger} from '../../logging/Logger'; -import {getLoggerFor} from '../../logging/LoggerUtils'; +import { createErrorMessage, getLoggerFor } from '@solid/community-server'; +import { HttpHandler, HttpHandlerContext, HttpHandlerResponse } from '../models/HttpHandler'; export const statusCodes: { [code: number]: string } = { 400: 'Bad Request', @@ -48,43 +45,39 @@ export const statusCodes: { [code: number]: string } = { }; /** - * Handler class that properly processes the HttpErrors from handlersjs-http + * Handler class that properly processes the HttpErrors and returns JSON responses. + * Both canHandle and handle response errors will be returned as a JSON response, + * describing the error. */ -export class JsonHttpErrorHandler implements HttpHandler { - protected readonly logger: Logger = getLoggerFor(this); +export class JsonHttpErrorHandler extends HttpHandler, Buffer> { + protected readonly logger = getLoggerFor(this); - /** - * Creates an {ErrorHandler} that catches errors and returns an error response to the given handler. - */ constructor( - private nestedHandler: HttpHandler, + protected handler: HttpHandler, Buffer>, ) { + super(); } - /** - * Handle Http Request and catch any Errors that occur - * - * @param {HttpHandlerContext} context - Request context - * @return {Observable} - */ - async handle(context: HttpHandlerContext): Promise { + async handle(context: HttpHandlerContext): Promise> { try { - return this.nestedHandler.handle(context); + return await this.handler.handleSafe(context); } catch (error) { this.logger.error(`Returned error for ${context.request.method} '${context.request.url}':` + - ` ${(error as Error).name} ${(error as Error).message}`, error); + ` ${createErrorMessage(error)}`); return { status: statusCodes[error?.statusCode] ? error.statusCode : 500, - headers: {'content-type': 'application/json'}, - body: JSON.stringify({ + headers: { + 'content-type': 'application/json', + }, + body: Buffer.from(JSON.stringify({ 'status': statusCodes[error?.statusCode] ? error.statusCode : 500, 'description': statusCodes[error?.statusCode] ? statusCodes[error.statusCode] : statusCodes[500], 'error': error?.type ?? error.type, 'message': error?.message ?? error.message, - ...(error?.additionalParams?error.additionalParams:{}), - }), + ...(error?.additionalParams ? error.additionalParams : {}), + })), }; - }; + } } } diff --git a/packages/uma/src/util/http/server/NodeHttpRequestResponseHandler.ts b/packages/uma/src/util/http/server/NodeHttpRequestResponseHandler.ts index 130b8b75..6d6a2109 100644 --- a/packages/uma/src/util/http/server/NodeHttpRequestResponseHandler.ts +++ b/packages/uma/src/util/http/server/NodeHttpRequestResponseHandler.ts @@ -1,337 +1,104 @@ -import { v4 } from 'uuid'; -import { getLogger, makeErrorLoggable } from '../../logging/LoggerUtils'; -import { BadRequestHttpError } from '../errors/BadRequestHttpError'; -import { HttpHandler } from '../models/HttpHandler'; -import { HttpHandlerContext } from '../models/HttpHandlerContext'; -import { HttpHandlerRequest } from '../models/HttpHandlerRequest'; -import { HttpMethods } from '../models/HttpMethod'; -import { statusCodes } from './ErrorHandler'; -import { NodeHttpStreamsHandler } from './NodeHttpStreamsHandler'; -import { NodeHttpStreams } from './NodeHttpStreams'; +import { + BadRequestHttpError, + getLoggerFor, + HttpHandler as NodeHttpStreamsHandler, + HttpHandlerInput, + TargetExtractor +} from '@solid/community-server'; +import { buffer } from 'node:stream/consumers'; +import { HttpHandler, HttpHandlerContext, HttpHandlerRequest } from '../models/HttpHandler'; /** * A { NodeHttpStreamsHandler } reading the request stream into a { HttpHandlerRequest }, * passing it through a { HttpHandler } and writing the resulting { HttpHandlerResponse } to the response stream. */ -export class NodeHttpRequestResponseHandler implements NodeHttpStreamsHandler { +export class NodeHttpRequestResponseHandler extends NodeHttpStreamsHandler { + protected readonly logger = getLoggerFor(this); - public logger = getLogger(); - - private requestId = ''; - private correlationId = ''; - - /** - * Creates a { NodeHttpRequestResponseHandler } passing requests through the given handler. - * - * @param { HttpHandler } httpHandler - the handler through which to pass incoming requests. - */ constructor( - private httpHandler: HttpHandler, - private poweredBy = 'handlers.js', - private hsts?: { maxAge: number; includeSubDomains: boolean }, + protected readonly httpHandler: HttpHandler, Buffer>, + protected readonly targetExtractor: TargetExtractor, ) { - - if (!httpHandler) { - - throw new Error('A HttpHandler must be provided'); - - } - - } - - private parseBody( - body: string, - contentType?: string, - ): string | { [key: string]: string } { - this.logger.debug('Parsing request body', { body, contentType }); - - if (contentType?.startsWith('application/json')) { - - try { - - return JSON.parse(body); - - } catch (error: any) { - - throw new BadRequestHttpError(error instanceof Error ? error.message : ''); - - } - - } - - return body; - - } - - private parseResponseBody( - body: unknown, - contentType?: string, - ) { - - // don't log the body if it is a buffer. It results in a long, illegible log. - this.logger.debug('Parsing response body', { body: body instanceof Buffer ? '' : body, contentType }); - - if (contentType?.startsWith('application/json')) { - - return typeof body === 'string' || body instanceof Buffer ? body : JSON.stringify(body); - - } else { - - return body; - - } - + super(); } /** - * Reads the requestStream of its NodeHttpStreams pair into a HttpHandlerRequest, + * Reads the requestStream of its HttpHandlerInput pair into a HttpHandlerRequest, * creates a HttpHandlerContext from it, passes it through the { HttpHandler }, * and writes the result to the responseStream. * - * @param { NodeHttpStreams } noteHttpStreams - the incoming set of Node.js HTTP read and write streams + * @param { HttpHandlerInput } nodeHttpStreams - the incoming set of Node.js HTTP read and write streams * @returns an { Promise } for completion detection */ - async handle(nodeHttpStreams: NodeHttpStreams): Promise { - - if (!nodeHttpStreams) { - - this.logger.error('No node http streams received'); - - throw new Error('node http streams object cannot be null or undefined.'); - - } - - const { requestStream, responseStream } = nodeHttpStreams; - - if (!requestStream) { - - // No request was received, this path is technically impossible to reach - this.logger.error('No request stream received', { nodeHttpStreams }); - - throw new Error('request stream cannot be null or undefined.'); - - } - + async handle(nodeHttpStreams: HttpHandlerInput): Promise { + const { request: requestStream, response: responseStream } = nodeHttpStreams; const { headers } = requestStream; - if (!headers) { - - // No request headers were received, this path is technically impossible to reach - this.logger.error('No request headers received', { requestStream }); - - throw new Error('headers of the request cannot be null or undefined.'); - + if (!requestStream.method) { + // No request method was received, this path is technically impossible to reach + this.logger.warn('No method received'); + throw new Error('method of the request cannot be null or undefined.'); } - // Add a request id to to be logged with every log from here on - const requestIdHeader = headers['x-request-id']; - this.requestId = (Array.isArray(requestIdHeader) ? requestIdHeader[0] : requestIdHeader) ?? v4(); - this.logger.setVariable('requestId', this.requestId); - // Add a correlation id to be logged with every log from here on - const correlationIdHeader = headers['x-correlation-id']; - this.correlationId = (Array.isArray(correlationIdHeader) ? correlationIdHeader[0] : correlationIdHeader) ?? v4(); - this.logger.setVariable('correlationId', this.correlationId); - - // Set the logger label to the last 5 characters of the request id - this.logger.debug('Set initial Logger variables', { variables: this.logger.getVariables() }); - - if (!responseStream) { - - // No response was received, this path is technically impossible to reach - this.logger.error('No response stream received', { nodeHttpStreams }); - - throw new Error('response stream cannot be null or undefined.'); - + const urlObject: URL = new URL((await this.targetExtractor.handleSafe({ request: requestStream })).path); + + const { + 'content-type': contentType, + 'content-length': contentLength, + 'transfer-encoding': transferEncoding, + } = requestStream.headers; + // RFC7230, §3.3: The presence of a message body in a request + // is signaled by a Content-Length or Transfer-Encoding header field. + // While clients SHOULD NOT use a Content-Length header on GET, + // some still provide a Content-Length of 0 (but without Content-Type). + const hasInputBody = (contentLength && !(/^0+$/u.test(contentLength) && !contentType)) || transferEncoding; + if (hasInputBody && !contentType) { + this.logger.warn('HTTP request has a body, but no Content-Type header'); + throw new BadRequestHttpError('HTTP request body was passed without a Content-Type header'); } - const url = requestStream.url; - - if (!url) { - - // No request url was received, this path is technically impossible to reach - this.logger.warn('No url received', { requestStream }); - - throw new Error('url of the request cannot be null or undefined.'); - - } - - // Check if the request method is an HTTP method + this ensures typing throughout the file - const method = Object.values(HttpMethods).find((m) => m === requestStream.method); - - if (!method) { - - if (requestStream.method) { - - // An unsupported method was received - this.logger.debug('Invalid method received', { method: requestStream.method }); - this.logger.clearVariables(); - responseStream.writeHead(501, { 'Content-Type': 'application/json' }); - - responseStream.write(JSON.stringify({ - error: 'http_request_method_not_valid', - error_description: 'This is not a known HTTP verb', - })); - - responseStream.end(); - - return Promise.resolve(); - - } else { - - // No request method was received, this path is technically impossible to reach - this.logger.warn('No method received', { requestStream }); - - throw new Error('method of the request cannot be null or undefined.'); - - } - - } - - const chunks = []; - - for await (const chunk of requestStream) { - - chunks.push(chunk); - - } - - const buffer = Buffer.concat(chunks); - const message = buffer.toString(); - - // Make sure first param doesn't start with multiple slashes - const urlObject: URL = new URL(url.replace(/^\/+/, '/'), `http://${headers.host}`); - - // Add host, path and method to the logger variables - this.logger.setVariable('host', urlObject.host); - this.logger.setVariable('path', urlObject.pathname + urlObject.search + urlObject.hash); - this.logger.setVariable('method', method); - - const httpHandlerRequest: HttpHandlerRequest = { + const httpHandlerRequest: HttpHandlerRequest = { url: urlObject, - method, + method: requestStream.method, // eslint-disable-next-line @typescript-eslint/consistent-type-assertions headers: headers as { [key: string]: string }, - ... (message && message !== '') && { body: this.parseBody(message, headers['content-type']) }, + ...hasInputBody && { body: await buffer(requestStream) }, }; - const context: HttpHandlerContext = { request: httpHandlerRequest }; - - this.logger.info('Domestic request:', { eventType: 'domestic_request', context }); - - let response = await this.httpHandler.handle(context).catch((error) => { - - const status = error?.statusCode ?? error.status; - const message = error?.message ?? error.body; - - this.logger.warn(`Unhandled error is handled by Handlersjs :`, { error: makeErrorLoggable(error) }); - - return { - headers: {}, - ... error, - body: message ?? 'Internal Server Error', - status: statusCodes[status] ? status : 500 - }; - - }); + const context: HttpHandlerContext = { request: httpHandlerRequest }; - const contentTypeHeader = response.headers['content-type']; - - const charsetString = contentTypeHeader ? contentTypeHeader.split(';') - .filter((part: string[]) => part.includes('charset=')) - .map((part: string) => part.split('=')[1].toLowerCase())[0] - ?? 'utf-8' : 'utf-8'; - - if ( - charsetString !== 'ascii' - && charsetString !== 'utf8' - && charsetString !== 'utf-8' - && charsetString !== 'utf16le' - && charsetString !== 'ucs2' - && charsetString !== 'ucs-2' - && charsetString !== 'base64' - && charsetString !== 'latin1' - && charsetString !== 'binary' - && charsetString !== 'hex' - ) { - - this.logger.warn('Unsupported charset', { charsetString }); + this.logger.info(`Domestic request: ${JSON.stringify({ eventType: 'domestic_request', context: { + request: { + ...context.request, + ...context.request.body && { body: context.request.body.toString() } + } + } })}`); - throw new Error('The specified charset is not supported'); + let response = await this.httpHandler.handleSafe(context); + response.headers = { + // headers could be undefined at this point + ...response.headers, + ...response.body && { 'content-length': Buffer.byteLength(response.body).toString() } } - // If the body is not a string or a buffer, for example an object, stringify it. This is needed - // to use Buffer.byteLength and to eventually write the body to the response. - // Functions will result in 'undefined' which is desired behavior - const body: string | Buffer = response.body !== undefined && response.body !== null - ? typeof response.body === 'string' || response.body instanceof Buffer - ? response.body - : JSON.stringify(response.body) - : undefined; - - const extraHeaders = { - ... ( - body !== undefined && body !== null && - !response.headers['content-type'] && - !response.headers['Content-Type'] && - typeof response.body !== 'string' && !(response.body instanceof Buffer)) && { - 'content-type': 'application/json' }, - ... (body !== undefined && body !== null) && { - 'content-length': Buffer.byteLength(body, charsetString).toString() - }, - ... (this.hsts?.maxAge) && { - 'strict-transport-security': `max-age=${this.hsts.maxAge}${this.hsts.includeSubDomains - ? '; includeSubDomains' - : '' - }` - }, - 'x-powered-by': this.poweredBy, - 'x-request-id': this.requestId, - 'x-correlation-id': this.correlationId, - }; - - // Reset variables so new requests will never share ids - this.requestId = ''; - this.correlationId = ''; - - response = { - ... response, - body, - headers: { - ... response.headers, - ... extraHeaders, - }, - }; - this.logger.debug('Sending response'); responseStream.writeHead(response.status, response.headers); - - if (response.body !== undefined && response.body !== null) { - - const contentTypeHeader = response.headers['content-type'] || response.headers['Content-Type']; - - const body = this.parseResponseBody(response.body, contentTypeHeader); - responseStream.write(body); - + if (response.body) { + responseStream.write(response.body); } responseStream.end(); - this.logger.info('Domestic response:', { + this.logger.info(`Domestic response: ${JSON.stringify({ eventType: 'domestic_response', response: { ... response, - // Set body to string '' if it is a Buffer Object to not pollute logs - ... (response.body && response.body instanceof Buffer) && { body: '' }, - }, - }); - - this.logger.clearVariables(); - - return Promise.resolve(); - + // Limit max length in logs + ... response.body && { body: (response.body.length > 200 ? '' : response.body.toString()) }, + } + })}`); } - } diff --git a/packages/uma/src/util/http/server/NodeHttpServer.ts b/packages/uma/src/util/http/server/NodeHttpServer.ts deleted file mode 100644 index 88fd5095..00000000 --- a/packages/uma/src/util/http/server/NodeHttpServer.ts +++ /dev/null @@ -1,170 +0,0 @@ -/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ -import { createServer, IncomingMessage, ServerResponse, Server as NodeServer } from 'http'; -import { getLogger } from '../../logging/LoggerUtils'; -import { Server } from '../models/Server'; -import { NodeHttpStreams } from './NodeHttpStreams'; -import { NodeHttpStreamsHandler } from './NodeHttpStreamsHandler'; - -const events = [ - 'checkContinue', - 'checkExpectation', - 'clientError', - 'close', - 'connect', - 'connection', - 'drop', - 'dropRequest', - 'error', - 'listening', - 'newListener', - 'removeListener', - 'request', - 'upgrade', -]; - -/** - * A { Server } implemented with [Node.js HTTP]{@link https://nodejs.org/api/http.html}, handling requests through a { NodeHttpStreamsHandler }. - */ -export class NodeHttpServer extends Server { - - private server: NodeServer; - private logger = getLogger(); - - /** - * Creates a { NodeHttpServer } listening on `http://``host``:``port`, passing requests through the given { NodeHttpStreamsHandler }. - * - * @param { string } host- the host name on which to listen - * @param { number } port - the port number on which to listen - * @param { NodeHttpStreamsHandler } nodeHttpStreamsHandler - the handler handling incoming requests - * @constructor - */ - constructor( - protected host: string, - protected port: number, - private nodeHttpStreamsHandler: NodeHttpStreamsHandler, - ){ - - super(`http`, host, port); - - if (!host) { - - throw new Error('A host must be provided'); - - } - - if (!port) { - - throw new Error('A port must be provided'); - - } - - if (!nodeHttpStreamsHandler) { - - throw new Error('A handler must be provided'); - - } - - this.server = createServer(this.serverHelper.bind(this)); - - for (const event of events) { - - this.server.on(event, (... args) => this.emit(event, ... args)); - - } - - } - - /** - * @override - * { @inheritDoc Server.start } - */ - async start(): Promise { - - const server = new Promise((resolve, reject) => { - - this.on('listening', () => { - - this.logger.info(`The server is listening on ${this.host}:${this.port}`); - resolve(this); - - }); - - this.on('error', (err: unknown) => { - - this.logger.fatal(`The server ran into a problem: `, { error: err }); - reject(new Error(`The server ran into a problem: ${err}`)); - - }); - - }); - - this.server.listen(this.port, this.host); - - return server; - - } - - /** - * @override - * { @inheritDoc Server.start } - */ - stop(): Promise { - - const server = new Promise((resolve, reject) => { - - this.on('close', () => { - - this.logger.info(`The server is stopped.`); - resolve(this); - - }); - - this.on(('error'), (err: unknown) => { - - this.logger.fatal(`The server ran into a problem: `, { error: err }); - reject(new Error(`The server ran into a problem: ${err}`)); - - }); - - }); - - this.server.close(); - - return server; - - } - - /** - * Helper function that provides a callback through which the Node.js HTTP server - * can send requests. It combines the IncomingMessage and ServerResponse into - * a NodeHttpStreams and passes it through the handler. - * - * @param { IncomingMessage } req - the Node.js HTTP callback's request stream - * @param { ServerResponse } res - the Node.js HTTP callback's response stream - */ - async serverHelper(req: IncomingMessage, res: ServerResponse): Promise { - - if (!req) { - - this.logger.debug('No request received'); - throw new Error('request must be defined.'); - - } - - if (!res) { - - this.logger.debug('No response received'); - throw new Error('response must be defined.'); - - } - - const nodeHttpStreams: NodeHttpStreams = { - requestStream: req, - responseStream: res, - }; - - await this.nodeHttpStreamsHandler.handle(nodeHttpStreams); - - } - -} diff --git a/packages/uma/src/util/http/server/NodeHttpStreams.ts b/packages/uma/src/util/http/server/NodeHttpStreams.ts deleted file mode 100644 index c8a48ade..00000000 --- a/packages/uma/src/util/http/server/NodeHttpStreams.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { IncomingMessage, ServerResponse } from 'http'; - -/** - * A pair consisting of the IncomingMessage request steam and the ServerResponse response stream of the Node.js native HTTP library. - */ -export interface NodeHttpStreams { - requestStream: IncomingMessage; - responseStream: ServerResponse; -} diff --git a/packages/uma/src/util/http/server/NodeHttpStreamsHandler.ts b/packages/uma/src/util/http/server/NodeHttpStreamsHandler.ts deleted file mode 100644 index 26ba60de..00000000 --- a/packages/uma/src/util/http/server/NodeHttpStreamsHandler.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Handler } from '../models/Handler'; -import { NodeHttpStreams } from './NodeHttpStreams'; - -/** - * A {Handler} that handles the IncomingMessage and ServerResponse of a {NodeHttpStreams} object. - */ -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export abstract class NodeHttpStreamsHandler extends Handler { } diff --git a/packages/uma/src/util/http/server/RoutedHttpRequestHandler.ts b/packages/uma/src/util/http/server/RoutedHttpRequestHandler.ts index aeb591c9..0c627b37 100644 --- a/packages/uma/src/util/http/server/RoutedHttpRequestHandler.ts +++ b/packages/uma/src/util/http/server/RoutedHttpRequestHandler.ts @@ -1,159 +1,81 @@ -import { getLogger } from '../../logging/LoggerUtils'; -import { HttpHandler } from '../models/HttpHandler'; -import { HttpHandlerContext } from '../models/HttpHandlerContext'; -import { HttpHandlerController } from '../models/HttpHandlerController'; -import { HttpHandlerResponse } from '../models/HttpHandlerResponse'; +import { getLoggerFor } from '@solid/community-server'; +import Template from 'uri-template-lite'; +import { HttpHandler, HttpHandlerContext, HttpHandlerResponse } from '../models/HttpHandler'; import { HttpHandlerRoute } from '../models/HttpHandlerRoute'; /** - * A {HttpHandler} handling requests based on routes in a given list of {HttpHandlerController}s. + * A {@link HttpHandler} handling requests based on routes in a given list of {@link HttpHandlerRoute}s. + * Route paths can contain variables as described in RFC 6570. + * These will be added to the `parameters` object of the request. * - * @class + * In case no route matches the request, the `defaultHandler` will be called, if there is one. + * + * Routes are matched to the URL path. + * That is the part of the URL starting from the first slash after the domain. + * This can be an issue in case your server is not running on the root of the server domain. + * To work around this, `onlyMatchTail` can be set to true. + * In that case only the tail of the URL will be matched to the template. + * E.g., `http://example.com/foo/bar` will match the route template `/bar`. */ -export class RoutedHttpRequestHandler implements HttpHandler { +export class RoutedHttpRequestHandler extends HttpHandler { - private pathToRouteMap: Map; - public logger = getLogger(); + protected readonly routeMap: Map; + protected readonly logger = getLoggerFor(this); /** - * Creates a RoutedHttpRequestHandler, super calls the HttpHandler class and expects a list of HttpHandlerControllers - * - * @param {HttpHandlerController[]} handlerControllerList - a list of HttpHandlerController objects + * Creates a RoutedHttpRequestHandler, super calls the HttpHandler class and expects a list of HttpHandlerControllers. */ - constructor(private handlerControllerList: HttpHandlerController[], private defaultHandler?: HttpHandler) { - - if (!handlerControllerList) { - - throw new Error('handlerControllerList must be defined.'); - + constructor( + routes: HttpHandlerRoute[], + protected readonly defaultHandler?: HttpHandler, + onlyMatchTail = false, + ) { + super(); + + this.routeMap = new Map(); + for (const route of routes) { + // Add a catchall variable to the front if only the URL tail needs to be matched. + this.routeMap.set(new Template(onlyMatchTail ? `{_prefix}${route.path}` : route.path), route); } - - this.pathToRouteMap = new Map(); - - this.handlerControllerList.flatMap((controller) => - controller.routes.forEach((route) => { - - const existing = this.pathToRouteMap.get(route.path); - - existing - ? this.pathToRouteMap.set(route.path, [ ... existing, { controller, route } ]) - : this.pathToRouteMap.set(route.path, [ { controller, route } ]); - - })); - } - /** - * Passes the { HttpHandlerContext } to the handler of the { HttpHandlerRoute } matching the request's path. - * - * @param { HttpHandlerContext } context - a HttpHandlerContext containing a HttpHandlerRequest and HttpHandlerRoute - */ async handle(context: HttpHandlerContext): Promise { - const request = context.request; const path = request.url.pathname; - const pathSegments = path.split('#')[0].split('?')[0].split('/').slice(1); - - this.logger.debug('Finding route for path: ', { path }); - - // Find a matching route - const match = Array.from(this.pathToRouteMap.keys()).find((route) => { - - // Route starts with a '/' so the first segment is always empty. - const routeSegments = route.split('/').slice(1); - - // If there are different numbers of segments, then the route does not match the URL. - if (routeSegments.length !== pathSegments.length) return false; - - // If each segment in the url matches the corresponding segment in the route path, - // or the route path segment starts with a ':' then the route is matched. - return routeSegments.every((seg, i) => seg === pathSegments[i] || seg[0] === ':'); - - }); - - this.logger.debug('Route matched:', { path, match: match ?? 'none' }); - - const matchingRoutes = match ? this.pathToRouteMap.get(match) : undefined; - - if (matchingRoutes?.length) { - - const matchingRouteWithOperation = matchingRoutes - .map((r) => ({ ... r, operation: r.route.operations.find((op) => op.method === request.method) })) - .find((r) => r.operation); - - const allowedMethods = matchingRoutes.flatMap((r) => r.route.operations.map((op) => op.method)); + this.logger.debug(`Finding route for path: ${ path }`); - if (!matchingRouteWithOperation) { - - this.logger.info(`Operation not supported. Supported operations:`, { allowedMethods }); - - return { - status: request.method === 'OPTIONS' ? 204 : 405, - headers: { - 'allow': allowedMethods.join(', ') - }, - body: '', - }; + let match: { parameters: Record, route: HttpHandlerRoute } | undefined; + for (const [ template, route ] of this.routeMap) { + const parameters = template.match(path); + if (parameters) { + match = { parameters, route }; + break; } + } - // Add the route's path to the logger's variables - this.logger.setVariable('route', matchingRouteWithOperation.route.path); + if (!match) { + if (this.defaultHandler) { + this.logger.info(`No matching route found, calling default handler. ${path}`); + return this.defaultHandler.handleSafe(context); + } else { + this.logger.error(`No matching route found. ${path}`); + return { body: '', headers: {}, status: 404 }; + } + } - // add parameters from requestPath to the request object - const paramNames = matchingRouteWithOperation.route.path.split('/').slice(1); - const parameters = this.extractParameters(paramNames, pathSegments); - this.logger.debug('Extracted parameters from path: ', { parameters }); - const requestWithParams = Object.assign(request, { parameters }); - const newContext = { ... context, request: requestWithParams, route: matchingRouteWithOperation.route }; - const preResponseHandler = matchingRouteWithOperation.controller.preResponseHandler; + this.logger.debug(`Route matched: ${JSON.stringify({ path, parameters: match.parameters })}`); - const preResponse = preResponseHandler ? await preResponseHandler.handle(newContext) : newContext; - const response = await matchingRouteWithOperation.route.handler.handle(preResponse); - + if (match.route.methods && !match.route.methods.includes(request.method)) { + this.logger.info(`Operation not supported. Supported operations: ${ match.route.methods }`); return { - ... response, - headers: { - ... response.headers, - ... (request.method === 'OPTIONS') && { Allow: allowedMethods.join(', ') }, - ... (matchingRouteWithOperation.operation?.vary) && { - vary: matchingRouteWithOperation.operation.vary.join(', ') - }, - }, + status: 405, + headers: { 'allow': match.route.methods.join(', ') }, + body: '', }; - - } else if (this.defaultHandler) { - - this.logger.info('No matching route found, calling default handler.', { path }); - - return this.defaultHandler.handle(context); - - } else { - - this.logger.error('No matching route found.', { path }); - - return { body: '', headers: {}, status: 404 }; } + request.parameters = { ...request.parameters, ...match.parameters }; + return match.route.handler.handleSafe(context); } - - private extractParameters(routeSegments: string[], pathSegments: string[]): { [key: string]: string } { - - const parameters: { [key: string]: string } = {}; - - routeSegments.forEach((segment, i) => { - - if (segment[0] === ':') { - - const propName = segment.slice(1); // Cut ':' - parameters[propName] = decodeURIComponent(pathSegments[i]); - - } - - }); - - return parameters; - - } - } diff --git a/packages/uma/src/util/logging/LICENSE b/packages/uma/src/util/logging/LICENSE deleted file mode 100644 index c1ce5c8e..00000000 --- a/packages/uma/src/util/logging/LICENSE +++ /dev/null @@ -1,52 +0,0 @@ - -The code in this directory was published by Digita BV under the MIT license. -Their code in turn was based on original work published under the same license -by the Solid Community. - -=============================================================================== - -MIT License - -Copyright (c) 2023 Digita BV - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - -=============================================================================== - -MIT License - -Copyright (c) 2020 Solid Community - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/packages/uma/src/util/logging/Logger.ts b/packages/uma/src/util/logging/Logger.ts deleted file mode 100644 index 376e9e3b..00000000 --- a/packages/uma/src/util/logging/Logger.ts +++ /dev/null @@ -1,185 +0,0 @@ - -export interface LoggerOptions { - minimumLevel: LoggerLevel; - minimumLevelPrintData: LoggerLevel; -} - -export interface PinoLoggerOptions extends LoggerOptions { - prettyPrint?: boolean; -} -/** - * Level of log severity based on Bunyan's log levels - */ -export enum LoggerLevel { - trace = 10, - debug = 20, - info = 30, - warn = 40, - error = 50, - fatal = 60, -} - -export abstract class Logger { - - public label: string; - public variables: Record = {}; - - constructor( - defaultLabel: string, - protected readonly minimumLevel: LoggerLevel, - protected readonly minimumLevelPrintData: LoggerLevel, - ) { - - this.label = defaultLabel; - - } - - /** - * Set the label of the logger - * - * @param label the string or class value that should be used as the label - * @returns the Logger object - */ - setLabel(label: string | { constructor: { name: string } }): Logger { - - this.label = typeof label === 'string' ? label : label.constructor.name; - - return this; - - } - - /** - * set a variable that will be logged with every log message - * - * @param key key of the variable - * @param value value of the variable - * @returns the Logger object - */ - setVariable(key: string, value: string): Logger { - - this.variables[key] = value; - - return this; - - } - - /** - * Remove a variable from the logger - * - * @param key key of the variable - * @returns the Logger object - */ - removeVariable(key: string): Logger { - - delete this.variables[key]; - - return this; - - } - - /** - * Clear all variables from the logger - * - * @returns the Logger object - */ - clearVariables(): Logger { - - this.variables = {}; - - return this; - - } - - /** - * retrieve the variables of the logger - * - * @returns the variables of the logger - */ - getVariables(): Record { - - return this.variables; - - } - - /** - * Logs a fatal message - * - * @param message Message that should be logged - * @param data Any relevant data that should be logged - */ - fatal(message: string, data?: unknown): void { - console.log(message, data) // note: fatal does not exist in winston... - this.log(LoggerLevel.fatal, message, data); - - } - - /** - * Logs an error message - * - * @param message Message that should be logged - * @param data Any relevant data that should be logged - */ - error(message: string, data?: unknown): void { - - this.log(LoggerLevel.error, message, data); - - } - - /** - * Logs a warning message - * - * @param message Message that should be logged - * @param data Any relevant data that should be logged - */ - warn(message: string, data?: unknown): void { - - this.log(LoggerLevel.warn, message, data); - - } - - /** - * Logs an info message - * - * @param message Message that should be logged - * @param data Any relevant data that should be logged - */ - info(message: string, data?: unknown): void { - - this.log(LoggerLevel.info, message, data); - - } - - /** - * Logs a debug message - * - * @param message Message that should be logged - * @param data Any relevant data that should be logged - */ - debug(message: string, data?: unknown): void { - - this.log(LoggerLevel.debug, message, data); - - } - - /** - * Logs a trace message - * - * @param message Message that should be logged - * @param data Any relevant data that should be logged - */ - trace(message: string, data?: unknown): void { - - this.log(LoggerLevel.trace, message, data); - - } - - /** - * Logs a message - * - * @param level Severity level of the log - * @param message Message that should be logged - * @param data Any relevant data that should be logged - */ - abstract log(level: LoggerLevel, message: string, data?: unknown): void; - -} diff --git a/packages/uma/src/util/logging/LoggerUtils.ts b/packages/uma/src/util/logging/LoggerUtils.ts deleted file mode 100644 index ff80062f..00000000 --- a/packages/uma/src/util/logging/LoggerUtils.ts +++ /dev/null @@ -1,63 +0,0 @@ -/* eslint-disable @typescript-eslint/consistent-type-assertions */ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ -import {LoggerLevel, Logger} from './Logger'; -import {WinstonLogger} from './WinstonLogger'; - -const loggerLevel = LoggerLevel.debug; -const loggerMinimumPrintLevel = LoggerLevel.debug; - -/** - * Gets a logger instance for the given class instance. - * - * @param {string | { constructor: { name: string } }} loggable - A class instance or a class string name. - * @return {Logger} - */ -export const getLoggerFor = ( - loggable: string | { constructor: { name: string } }, -): Logger => { - return new WinstonLogger(typeof loggable === 'string' ? loggable : loggable.constructor.name, - loggerLevel, loggerMinimumPrintLevel); -}; - -const loggerKey = '__LOGGER__HANDLERSJS__LOGGING__'; - -const getGlobalLogger = (): Logger => (global as any)[loggerKey]; - -const setGlobalLogger = (newLogger: Logger): void => { - - (global as any)[loggerKey] = newLogger; - -}; - -export const setLogger = ( - logger: Logger, -): void => { - - setGlobalLogger(logger); - -}; - -export const getLogger = (): Logger => { - - const logger = getGlobalLogger(); - // if (!logger) throw new Error('No logger was set. Set a logger using setLogger()'); - - return logger; - -}; - -export const makeErrorLoggable = (error: unknown): Record => { - - if (error instanceof Error) { - - return { - message: error.message, - name: error.name, - stack: error.stack ? error.stack.split(/\n/) : [], - }; - - } - - return { 'error': 'not-an-error' }; - -}; diff --git a/packages/uma/src/util/logging/WinstonLogger.ts b/packages/uma/src/util/logging/WinstonLogger.ts deleted file mode 100644 index c9ae91e7..00000000 --- a/packages/uma/src/util/logging/WinstonLogger.ts +++ /dev/null @@ -1,61 +0,0 @@ -/* eslint-disable valid-jsdoc */ -/* eslint-disable require-jsdoc */ - -import * as util from 'util'; -import {transports, createLogger, format, Logger as WLogger} from 'winston'; -import {Logger, LoggerLevel} from './Logger'; -import {TransformableInfo} from 'logform'; -import * as fs from 'fs'; - -/** - * Winston-based logger service - * - * @note this logger will disable console logging output during testing - */ -export class WinstonLogger extends Logger { - private logger: WLogger; - - constructor( - public readonly label: string, - protected readonly minimumLevel: LoggerLevel, - protected readonly minimumLevelPrintData: LoggerLevel, - ) { - super(label, minimumLevel, minimumLevelPrintData); - - this.logger = createLogger({ - format: format.combine( - format.timestamp(), - format.colorize(), - format.printf(this.formatLog), - ), - level: LoggerLevel[this.minimumLevel], - transports: [process.env.NODE_ENV !== 'test' ? - new transports.Console() : new transports.Stream({ - stream: fs.createWriteStream('/dev/null'), - }), - ], - }); - } - - log(level: LoggerLevel, message: string, data?: unknown): void { - const logLevel = LoggerLevel[level]; - const printData = level <= this.minimumLevelPrintData; - - if (level >= this.minimumLevel) { - this.logger.log({level: logLevel, message, typeName: this.label, data, printData}); - } - } - - /** - * Formats log info - * - * @param info The log info to format - * @return The formatted string - */ - public formatLog(info: TransformableInfo): string { - return info.printData ? - `${info.timestamp} [${info.typeName}] ${info.level}: ${info.message}${info.data ? - `\n${util.inspect(info.data)}` : ''}` : - `${info.timestamp} [${info.typeName}] ${info.level}: ${info.message}`; - } -} diff --git a/packages/uma/src/util/storage/JsonFileStore.ts b/packages/uma/src/util/storage/JsonFileStore.ts deleted file mode 100644 index 7a595c3d..00000000 --- a/packages/uma/src/util/storage/JsonFileStore.ts +++ /dev/null @@ -1,129 +0,0 @@ -/* eslint-disable no-param-reassign */ -import { promises as fsPromises } from 'fs'; -import { dirname, isAbsolute, join } from 'path'; -import clone from 'clone'; -import { TypedKeyValueStore } from './models/TypedKeyValueStore'; - -/** - * Uses a JSON file to store key/value pairs. - */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export class JsonFileStore> implements TypedKeyValueStore { - - private path: string; - - constructor(path: string) { - - this.path = isAbsolute(path) ? path : join(process.cwd(), path); - - } - - async get(key: T): Promise { - - const json = await this.getJson(); - - return Promise.resolve(json[key]); - - } - - async has(key: T): Promise { - - const json = await this.getJson(); - - return typeof json[key] !== 'undefined'; - - } - - async set(key: T, value: M[T]): Promise { - - return this.updateJson((json: M): this => { - - json[key] = value; - - return this; - - })(); - - } - - async delete(key: T): Promise { - - return this.updateJson((json: M): boolean => { - - if (typeof json[key] !== 'undefined') { - - // eslint-disable-next-line @typescript-eslint/no-dynamic-delete, no-param-reassign - delete json[key]; - - return true; - - } - - return false; - - })(); - - } - - async* entries(): AsyncIterableIterator<[keyof M, M[keyof M]]> { - - const json = await this.getJson(); - - for (const [ key, value ] of Object.entries(json)) { - - yield [ key, clone(value) ]; - - } - - } - - /** - * Updates the data in the JSON file. - * - * @param updateFn - A function that updates the JSON object. - * - * @returns The return value of `updateFn`. - */ - private updateJson(updateFn: (json: M) => T): () => Promise { - - return async () => { - - const json = await this.getJson(); - const result = updateFn(json); - // eslint-disable-next-line no-null/no-null - const updatedText = JSON.stringify(json, null, 2); - await fsPromises.mkdir(dirname(this.path), { recursive: true }); - await fsPromises.writeFile(this.path, updatedText, 'utf8'); - - return result; - - }; - - } - - /** - * Reads and parses the data from the JSON file (without locking). - */ - private async getJson(): Promise { - - try { - - const text = await fsPromises.readFile(this.path, 'utf8'); - - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - return JSON.parse(text) as M; - - // eslint-disable-next-line padded-blocks - // eslint-disable-next-line @typescript-eslint/no-explicit-any - } catch (error: any) { - - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions, @typescript-eslint/dot-notation, @typescript-eslint/no-unsafe-member-access - if (error && error['syscall'] && error['code'] && error['code'] === 'ENOENT') { return {} as M; } - - throw error; - - } - - } - -} diff --git a/packages/uma/src/util/storage/LICENSE b/packages/uma/src/util/storage/LICENSE deleted file mode 100644 index c1ce5c8e..00000000 --- a/packages/uma/src/util/storage/LICENSE +++ /dev/null @@ -1,52 +0,0 @@ - -The code in this directory was published by Digita BV under the MIT license. -Their code in turn was based on original work published under the same license -by the Solid Community. - -=============================================================================== - -MIT License - -Copyright (c) 2023 Digita BV - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - -=============================================================================== - -MIT License - -Copyright (c) 2020 Solid Community - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/packages/uma/src/util/storage/MemoryStore.ts b/packages/uma/src/util/storage/MemoryStore.ts deleted file mode 100644 index 80c1f83b..00000000 --- a/packages/uma/src/util/storage/MemoryStore.ts +++ /dev/null @@ -1,76 +0,0 @@ -import clone from 'clone'; -import { TimedValue } from './models/TimedKeyValueStore'; -import { TimedTypedKeyValueStore } from './models/TimedTypedKeyValueStore'; - -/** - * A {@link KeyValueStore} which uses a JavaScript Map for internal storage. - * - * @inheritdoc - */ -export class MemoryStore implements TimedTypedKeyValueStore { - - private readonly data: Map>; - - /** - * - * @param initialData data to initialize the memorystore with @range {json} - */ - constructor(initialData?: [keyof M, M[keyof M]][]) { - - this.data = new Map(initialData?.map(([ key, value ]) => [ key, { value: clone(value), timestamp: Date.now() } ])); - - } - - get(key: T): Promise { - - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - return Promise.resolve(this.data.has(key) ? clone(this.data.get(key)?.value as M[T]) : undefined); - - } - - has(key: T): Promise { - - return Promise.resolve(this.data.has(key)); - - } - - set(key: T, value: M[T]): Promise { - - this.data.set(key, { value: clone(value), timestamp: Date.now() }); - - return Promise.resolve(this); - - } - - delete(key: T): Promise { - - return Promise.resolve(this.data.delete(key)); - - } - - // eslint-disable-next-line @typescript-eslint/require-await - async* entries(): AsyncIterableIterator<[keyof M, M[keyof M]]> { - - for (const [ key, value ] of this.data.entries()) { - - yield [ key, clone(value.value) ]; - - } - - } - - latestUpdate(key: T): Promise { - - return Promise.resolve(this.data.get(key)?.timestamp); - - } - - hasUpdate (key: T, time: number): Promise { - - const timedValue = this.data.get(key); - - return Promise.resolve(timedValue ? timedValue.timestamp > time : undefined); - - } - -} diff --git a/packages/uma/src/util/storage/models/KeyValueStore.ts b/packages/uma/src/util/storage/models/KeyValueStore.ts deleted file mode 100644 index 616b70d8..00000000 --- a/packages/uma/src/util/storage/models/KeyValueStore.ts +++ /dev/null @@ -1,53 +0,0 @@ -/** - * A simple storage solution that can be used for internal values that need to be stored. - * In general storages taking objects as keys are expected to work with different instances - * of an object with the same values. Exceptions to this expectation should be clearly documented. - */ -export interface KeyValueStore { - - /** - * Returns the value stored for the given identifier. - * `undefined` if no value is stored. - * - * @param key - Identifier to get the value for. - * - * @returns the value identified by the given key - */ - get: (key: K) => Promise; - - /** - * Checks if there is a value stored for the given key. - * - * @param key - Identifier to check. - * - * @returns whether the key is in the store - */ - has: (key: K) => Promise; - - /** - * Sets the value for the given key. - * - * @param key - Key to set/update. - * @param value - Value to store. - * - * @returns The storage. - */ - set: (key: K, value: V) => Promise; - - /** - * Deletes the value stored for the given key. - * - * @param key - Key to delete. - * - * @returns If there was a value to delete. - */ - delete: (key: K) => Promise; - - /** - * An iterable of entries in the storage. - * - * @returns the asynchronous iterator - */ - entries: () => AsyncIterableIterator<[K, V]>; - -} diff --git a/packages/uma/src/util/storage/models/TimedKeyValueStore.ts b/packages/uma/src/util/storage/models/TimedKeyValueStore.ts deleted file mode 100644 index 8930be6a..00000000 --- a/packages/uma/src/util/storage/models/TimedKeyValueStore.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { KeyValueStore } from './KeyValueStore'; - -/** - * Data structure that links a value to a timestamp - */ -export interface TimedValue { - value: V; - timestamp: number; -} - -export interface TimedKeyValueStore extends KeyValueStore { - - /** - * Returns the timestamp at which a given key was latest updated. - * - * @param key - Key to check. - * - * @returns the timestamp if the key was set, otherwise undefined. - */ - latestUpdate: (key: K) => Promise; - - /** - * Checks whether a value has had an update since the given timestamp. - * - * @param key - Key to check. - * @param time - Time to compare to. - * - * @returns A boolean indicating if there was an update, or undefined if the key wasn't set - */ - hasUpdate: (key: K, time: number) => Promise; - -} diff --git a/packages/uma/src/util/storage/models/TimedTypedKeyValueStore.ts b/packages/uma/src/util/storage/models/TimedTypedKeyValueStore.ts deleted file mode 100644 index 65800e5e..00000000 --- a/packages/uma/src/util/storage/models/TimedTypedKeyValueStore.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { TimedKeyValueStore } from './TimedKeyValueStore'; -import { TypedKeyValueStore } from './TypedKeyValueStore'; - -/** - * Combines the methods of the TypedKeyValueStore and TimedKeyValueStore - */ -export type TimedTypedKeyValueStore = TypedKeyValueStore & TimedKeyValueStore; diff --git a/packages/uma/src/util/storage/models/TypedKeyValueStore.ts b/packages/uma/src/util/storage/models/TypedKeyValueStore.ts deleted file mode 100644 index 68dbda4b..00000000 --- a/packages/uma/src/util/storage/models/TypedKeyValueStore.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { KeyValueStore } from './KeyValueStore'; - -export interface TypedKeyValueStore extends KeyValueStore { - - /** - * Returns the value stored for the given identifier. - * `undefined` if no value is stored. - * - * @param key - Key to get the value for. - * - * @returns the value identified by the given key - */ - get: (key: T) => Promise; - - /** - * Checks if there is a value stored for the given key. - * - * @param key - Key to check. - * - * @returns whether the key is in the store - */ - has: (key: T) => Promise; - - /** - * Sets the value for the given key. - * - * @param key - Key to set/update. - * @param value - Value to store. - * - * @returns The storage. - */ - set: (key: T, value: M[T]) => Promise; - - /** - * Deletes the value stored for the given key. - * - * @param key - Key to delete. - * - * @returns If there was a value to delete. - */ - delete: (key: T) => Promise; - - /** - * An iterable of entries in the storage. - * - * @returns the asynchronous iterator - */ - entries: () => AsyncIterableIterator<[keyof M, M[keyof M]]>; - -} diff --git a/packages/uma/tsconfig.json b/packages/uma/tsconfig.json index a5c6ff9a..2da8ab54 100644 --- a/packages/uma/tsconfig.json +++ b/packages/uma/tsconfig.json @@ -10,12 +10,15 @@ "downlevelIteration": true, "module": "node16", "moduleResolution": "node16", + "typeRoots": [ + "types", + "node_modules/@types" + ], "skipLibCheck": true, // voor ESM typings in CSS (@types/rdf-validate-shacl) "esModuleInterop": true, "strictNullChecks": true, "lib": ["ES2022"] }, - "include": ["src"], + "include": ["src", "types"], "exclude": ["node_modules", "**/*.test.ts"] } - diff --git a/packages/uma/types/uri-template-lite.d.ts b/packages/uma/types/uri-template-lite.d.ts new file mode 100644 index 00000000..d4e00d25 --- /dev/null +++ b/packages/uma/types/uri-template-lite.d.ts @@ -0,0 +1,10 @@ +// Unfortunately the published types are wrong +declare module 'uri-template-lite' { + export function expand(template: string, data: Record): string; + + export default class Template { + public constructor(template: string); + public expand: (data: Record) => string; + public match: (template: string) => Record; + } +} diff --git a/screencast/trust-flows-demo.mp4 b/screencast/trust-flows-demo.mp4 deleted file mode 100644 index b9ba8b3d..00000000 Binary files a/screencast/trust-flows-demo.mp4 and /dev/null differ diff --git a/scripts/test-private.ts b/scripts/test-private.ts index bbfc00a0..7b02f6f4 100644 --- a/scripts/test-private.ts +++ b/scripts/test-private.ts @@ -1,15 +1,13 @@ #!/usr/bin/env ts-node -import { fetch } from 'cross-fetch' - const privateResource = "http://localhost:3000/alice/private/resource.txt" function parseJwt (token:string) { return JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString()); } -const request: RequestInit = { - method: "PUT", +const request: RequestInit = { + method: "PUT", headers: {}, body: 'Some text ...' , }; @@ -31,7 +29,7 @@ async function main() { const { as_uri, ticket } = Object.fromEntries(wwwAuthenticateHeader.replace(/^UMA /,'').split(', ').map( param => param.split('=').map(s => s.replace(/"/g,'')) )); - + const tokenEndpoint = as_uri + "/token" // should normally be retrieved from .well-known/uma2-configuration const claim_token = "https://woslabbi.pod.knows.idlab.ugent.be/profile/card#me" @@ -59,7 +57,7 @@ async function main() { // console.log("Authorization Server response:", await asRequestResponse.text()); // throw 'stop' - const asResponse = await asRequestResponse.json(); + const asResponse: any = await asRequestResponse.json(); const decodedToken = parseJwt(asResponse.access_token); @@ -71,14 +69,14 @@ async function main() { // for (const permission of decodedToken.permissions) { // console.log(`Permissioned scopes for resource ${permission.resource_id}:`, permission.resource_scopes) // } - + console.log(`=== Trying to create private resource <${privateResource}> WITH access token.\n`); request.headers = { 'Authorization': `${asResponse.token_type} ${asResponse.access_token}` }; - + const tokenResponse = await fetch(privateResource, request); - console.log(`= Status: ${tokenResponse.status}\n`); + console.log(`= Status: ${tokenResponse.status}\n`); } main(); diff --git a/scripts/test-public.ts b/scripts/test-public.ts index a773b5a0..ead0553c 100644 --- a/scripts/test-public.ts +++ b/scripts/test-public.ts @@ -1,7 +1,5 @@ #!/usr/bin/env ts-node -import { fetch } from 'cross-fetch' - const publicResource = "http://localhost:3000/alice/profile/card" async function main() { @@ -9,7 +7,7 @@ async function main() { console.log('\n\n'); console.log(`=== Trying to read public resource <${publicResource}> without access token.\n`); - + const publicResponse = await fetch(publicResource, { method: "GET" }); console.log(`= Status: ${publicResponse.status}\n`); diff --git a/scripts/test-registration.ts b/scripts/test-registration.ts index 779803b1..d69d330d 100644 --- a/scripts/test-registration.ts +++ b/scripts/test-registration.ts @@ -1,7 +1,5 @@ #!/usr/bin/env ts-node -import { fetch } from 'cross-fetch' - const container = "http://localhost:3000/alice/public/"; const slug = "resource.txt"; const body = "This is a resource."; diff --git a/scripts/test-uma-ucp.ts b/scripts/test-uma-ucp.ts index 9bf456c4..889462c7 100644 --- a/scripts/test-uma-ucp.ts +++ b/scripts/test-uma-ucp.ts @@ -1,7 +1,5 @@ #!/usr/bin/env ts-node -import { fetch } from 'cross-fetch' - // Resource and WebID as set in config/rules/policy/policy0.ttl const resource = "http://localhost:3000/alice/other/resource.txt"; const webid = "https://woslabbi.pod.knows.idlab.ugent.be/profile/card#me"; @@ -10,8 +8,8 @@ function parseJwt (token:string) { return JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString()); } -const request: RequestInit = { - method: "PUT", +const request: RequestInit = { + method: "PUT", headers: {}, body: 'Some text ...' , }; @@ -33,7 +31,7 @@ async function main() { const { as_uri, ticket } = Object.fromEntries(wwwAuthenticateHeader.replace(/^UMA /,'').split(', ').map( param => param.split('=').map(s => s.replace(/"/g,'')) )); - + const tokenEndpoint = as_uri + "/token" // should normally be retrieved from .well-known/uma2-configuration const content = { @@ -59,7 +57,7 @@ async function main() { // console.log("Authorization Server response:", await asRequestResponse.text()); // throw 'stop' - const asResponse = await asRequestResponse.json() + const asResponse: any = await asRequestResponse.json() const decodedToken = parseJwt(asResponse.access_token); @@ -72,13 +70,13 @@ async function main() { // console.log(`Permissioned scopes for resource ${permission.resource_id}:`, permission.resource_scopes) // } - console.log(`=== Trying to create private resource <${resource}> WITH access token.\n`); - + console.log(`=== Trying to create private resource <${resource}> WITH access token.\n`); + request.headers = { 'Authorization': `${asResponse.token_type} ${asResponse.access_token}` }; const tokenResponse = await fetch(resource, request); - console.log(`= Status: ${tokenResponse.status}\n`); + console.log(`= Status: ${tokenResponse.status}\n`); } main(); diff --git a/yarn.lock b/yarn.lock index 008934a4..a7b2adf1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -698,21 +698,7 @@ __metadata: languageName: node linkType: hard -"@comunica/actor-dereference-http@npm:^2.0.2, @comunica/actor-dereference-http@npm:^2.10.0": - version: 2.10.0 - resolution: "@comunica/actor-dereference-http@npm:2.10.0" - dependencies: - "@comunica/bus-dereference": "npm:^2.10.0" - "@comunica/bus-http": "npm:^2.10.0" - "@comunica/core": "npm:^2.10.0" - cross-fetch: "npm:^4.0.0" - relative-to-absolute-iri: "npm:^1.0.7" - stream-to-string: "npm:^1.2.0" - checksum: 10c0/ec792e4e87e63adf64803f2426ab1316dc6c96c6ce815b4ae58aeba1bd882b5561d47dddafe12b00f5e0db66302adc4660988a72293ea717563d2d0b736815eb - languageName: node - linkType: hard - -"@comunica/actor-dereference-http@npm:^2.10.2": +"@comunica/actor-dereference-http@npm:^2.0.2, @comunica/actor-dereference-http@npm:^2.10.2": version: 2.10.2 resolution: "@comunica/actor-dereference-http@npm:2.10.2" dependencies: @@ -750,33 +736,7 @@ __metadata: languageName: node linkType: hard -"@comunica/actor-http-fetch@npm:^2.0.1": - version: 2.10.0 - resolution: "@comunica/actor-http-fetch@npm:2.10.0" - dependencies: - "@comunica/bus-http": "npm:^2.10.0" - "@comunica/context-entries": "npm:^2.10.0" - "@comunica/mediatortype-time": "npm:^2.10.0" - abort-controller: "npm:^3.0.0" - cross-fetch: "npm:^4.0.0" - checksum: 10c0/0624b7a12c646b8a8541d39a4ae867dd28d2a5aaeece48f49f7b38ae24712eea6c8c83199734b5aa0495b23caae0cb0978147aa707b64662aa3cafbd735a0a1b - languageName: node - linkType: hard - -"@comunica/actor-http-fetch@npm:^2.10.1": - version: 2.10.1 - resolution: "@comunica/actor-http-fetch@npm:2.10.1" - dependencies: - "@comunica/bus-http": "npm:^2.10.0" - "@comunica/context-entries": "npm:^2.10.0" - "@comunica/mediatortype-time": "npm:^2.10.0" - abort-controller: "npm:^3.0.0" - cross-fetch: "npm:^4.0.0" - checksum: 10c0/a75f3d5d58b49a0903e2f4745e4b185ee2ba967e2710608b49ce09f0529227c6181c9f12550da6ea47754eae8d7abe0fe494e3976106b13fb4cc80f37427ccd2 - languageName: node - linkType: hard - -"@comunica/actor-http-fetch@npm:^2.10.2": +"@comunica/actor-http-fetch@npm:^2.0.1, @comunica/actor-http-fetch@npm:^2.10.2": version: 2.10.2 resolution: "@comunica/actor-http-fetch@npm:2.10.2" dependencies: @@ -789,19 +749,7 @@ __metadata: languageName: node linkType: hard -"@comunica/actor-http-proxy@npm:^2.0.1, @comunica/actor-http-proxy@npm:^2.10.0": - version: 2.10.0 - resolution: "@comunica/actor-http-proxy@npm:2.10.0" - dependencies: - "@comunica/bus-http": "npm:^2.10.0" - "@comunica/context-entries": "npm:^2.10.0" - "@comunica/mediatortype-time": "npm:^2.10.0" - "@comunica/types": "npm:^2.10.0" - checksum: 10c0/f271e40cd6ad6ed3ee1465ec8ec78f1ad99e8921d02e6704d06c3f8c41c01006a6088c7babcff27aa27e738430706fad605e39879627c68cfa25c6e333d88445 - languageName: node - linkType: hard - -"@comunica/actor-http-proxy@npm:^2.10.2": +"@comunica/actor-http-proxy@npm:^2.0.1, @comunica/actor-http-proxy@npm:^2.10.2": version: 2.10.2 resolution: "@comunica/actor-http-proxy@npm:2.10.2" dependencies: @@ -813,19 +761,6 @@ __metadata: languageName: node linkType: hard -"@comunica/actor-http-wayback@npm:^2.10.0": - version: 2.10.0 - resolution: "@comunica/actor-http-wayback@npm:2.10.0" - dependencies: - "@comunica/bus-http": "npm:^2.10.0" - "@comunica/context-entries": "npm:^2.10.0" - "@comunica/core": "npm:^2.10.0" - cross-fetch: "npm:^4.0.0" - stream-to-string: "npm:^1.2.0" - checksum: 10c0/854b822bcd7f5de70cf667757041b3cfa3fcb1aa1a035056503dbb7057f3d7ad5d218d943d1284f01e40b5006040b4dec5555317f63ee48e62e97d5a8d318c09 - languageName: node - linkType: hard - "@comunica/actor-http-wayback@npm:^2.10.2": version: 2.10.2 resolution: "@comunica/actor-http-wayback@npm:2.10.2" @@ -839,40 +774,6 @@ __metadata: languageName: node linkType: hard -"@comunica/actor-init-query@npm:^2.10.1": - version: 2.10.1 - resolution: "@comunica/actor-init-query@npm:2.10.1" - dependencies: - "@comunica/actor-http-proxy": "npm:^2.10.0" - "@comunica/bus-context-preprocess": "npm:^2.10.0" - "@comunica/bus-http-invalidate": "npm:^2.10.0" - "@comunica/bus-init": "npm:^2.10.0" - "@comunica/bus-optimize-query-operation": "npm:^2.10.0" - "@comunica/bus-query-operation": "npm:^2.10.1" - "@comunica/bus-query-parse": "npm:^2.10.0" - "@comunica/bus-query-result-serialize": "npm:^2.10.0" - "@comunica/context-entries": "npm:^2.10.0" - "@comunica/core": "npm:^2.10.0" - "@comunica/logger-pretty": "npm:^2.10.0" - "@comunica/runner": "npm:^2.10.0" - "@comunica/types": "npm:^2.10.0" - "@rdfjs/types": "npm:*" - "@types/yargs": "npm:^17.0.24" - asynciterator: "npm:^3.8.1" - negotiate: "npm:^1.0.1" - process: "npm:^0.11.10" - rdf-quad: "npm:^1.5.0" - rdf-string: "npm:^1.6.1" - sparqlalgebrajs: "npm:^4.2.0" - streamify-string: "npm:^1.0.1" - yargs: "npm:^17.7.2" - dependenciesMeta: - process: - optional: true - checksum: 10c0/ab53055f88057d38a01d5aaf29432c080fe56357eaac2f6fedad592b30cbc862afb6c55d81c390a0c5fc984e049887ee1487d891fa218414d09d5d8981c70a04 - languageName: node - linkType: hard - "@comunica/actor-init-query@npm:^2.10.2": version: 2.10.2 resolution: "@comunica/actor-init-query@npm:2.10.2" @@ -1321,29 +1222,6 @@ __metadata: languageName: node linkType: hard -"@comunica/actor-query-operation-sparql-endpoint@npm:^2.10.1": - version: 2.10.1 - resolution: "@comunica/actor-query-operation-sparql-endpoint@npm:2.10.1" - dependencies: - "@comunica/bindings-factory": "npm:^2.10.1" - "@comunica/bus-http": "npm:^2.10.0" - "@comunica/bus-query-operation": "npm:^2.10.1" - "@comunica/bus-rdf-resolve-quad-pattern": "npm:^2.10.0" - "@comunica/bus-rdf-update-quads": "npm:^2.10.1" - "@comunica/context-entries": "npm:^2.10.0" - "@comunica/core": "npm:^2.10.0" - "@comunica/mediatortype-httprequests": "npm:^2.10.0" - "@comunica/metadata": "npm:^2.10.0" - "@comunica/types": "npm:^2.10.0" - "@rdfjs/types": "npm:*" - asynciterator: "npm:^3.8.1" - fetch-sparql-endpoint: "npm:^4.1.0" - rdf-data-factory: "npm:^1.1.1" - sparqlalgebrajs: "npm:^4.2.0" - checksum: 10c0/268636c8339eba3cd03eb24e17498b0c2a62df340d6a0b28b86377c9087adc4967303708d6611925cd088224bdb5a06f65e606c257b1919018bf02e95cad4a94 - languageName: node - linkType: hard - "@comunica/actor-query-operation-sparql-endpoint@npm:^2.10.2": version: 2.10.2 resolution: "@comunica/actor-query-operation-sparql-endpoint@npm:2.10.2" @@ -1396,21 +1274,6 @@ __metadata: languageName: node linkType: hard -"@comunica/actor-query-operation-update-clear@npm:^2.10.1": - version: 2.10.1 - resolution: "@comunica/actor-query-operation-update-clear@npm:2.10.1" - dependencies: - "@comunica/bus-query-operation": "npm:^2.10.1" - "@comunica/bus-rdf-update-quads": "npm:^2.10.1" - "@comunica/core": "npm:^2.10.0" - "@comunica/types": "npm:^2.10.0" - "@rdfjs/types": "npm:*" - rdf-data-factory: "npm:^1.1.1" - sparqlalgebrajs: "npm:^4.2.0" - checksum: 10c0/c3e1b7e6dfbff23810a6af8591f4363aea9f49ab58cc510c4e48fbb7c9577465f86f629ac343f55fa30552183efb50ecf867bd50c52ed3e18da7986f17cac926 - languageName: node - linkType: hard - "@comunica/actor-query-operation-update-clear@npm:^2.10.2": version: 2.10.2 resolution: "@comunica/actor-query-operation-update-clear@npm:2.10.2" @@ -1450,19 +1313,6 @@ __metadata: languageName: node linkType: hard -"@comunica/actor-query-operation-update-create@npm:^2.10.1": - version: 2.10.1 - resolution: "@comunica/actor-query-operation-update-create@npm:2.10.1" - dependencies: - "@comunica/bus-query-operation": "npm:^2.10.1" - "@comunica/bus-rdf-update-quads": "npm:^2.10.1" - "@comunica/core": "npm:^2.10.0" - "@comunica/types": "npm:^2.10.0" - sparqlalgebrajs: "npm:^4.2.0" - checksum: 10c0/fb460f186a4a7fef7409560ce069374ce2750866587a285d6cb6f674a9c5564f13504b2eef9751ebc8e7703187d783e709f8d50e26d5cf388db28f450875cafd - languageName: node - linkType: hard - "@comunica/actor-query-operation-update-create@npm:^2.10.2": version: 2.10.2 resolution: "@comunica/actor-query-operation-update-create@npm:2.10.2" @@ -1476,23 +1326,6 @@ __metadata: languageName: node linkType: hard -"@comunica/actor-query-operation-update-deleteinsert@npm:^2.10.1": - version: 2.10.1 - resolution: "@comunica/actor-query-operation-update-deleteinsert@npm:2.10.1" - dependencies: - "@comunica/actor-query-operation-construct": "npm:^2.10.1" - "@comunica/bindings-factory": "npm:^2.10.1" - "@comunica/bus-query-operation": "npm:^2.10.1" - "@comunica/bus-rdf-update-quads": "npm:^2.10.1" - "@comunica/core": "npm:^2.10.0" - "@comunica/types": "npm:^2.10.0" - "@rdfjs/types": "npm:*" - asynciterator: "npm:^3.8.1" - sparqlalgebrajs: "npm:^4.2.0" - checksum: 10c0/4c13003b8de8c27815a6520fa349f39be6d29721f0a6e71114341138d3a97f9a52cb0ed52a3652498c8eb54b5b9739592a459f79d63a0e595cdb1a5de8980e12 - languageName: node - linkType: hard - "@comunica/actor-query-operation-update-deleteinsert@npm:^2.10.2": version: 2.10.2 resolution: "@comunica/actor-query-operation-update-deleteinsert@npm:2.10.2" @@ -1510,21 +1343,6 @@ __metadata: languageName: node linkType: hard -"@comunica/actor-query-operation-update-drop@npm:^2.10.1": - version: 2.10.1 - resolution: "@comunica/actor-query-operation-update-drop@npm:2.10.1" - dependencies: - "@comunica/bus-query-operation": "npm:^2.10.1" - "@comunica/bus-rdf-update-quads": "npm:^2.10.1" - "@comunica/core": "npm:^2.10.0" - "@comunica/types": "npm:^2.10.0" - "@rdfjs/types": "npm:*" - rdf-data-factory: "npm:^1.1.1" - sparqlalgebrajs: "npm:^4.2.0" - checksum: 10c0/ca18870858dae1ae0a7ceaf6aeb9202958392eb65338c22a67317248d914b0bde1693748a7dc7c90cea5e5e529364f81852ba4501b44ac155fd10f40f808bd3e - languageName: node - linkType: hard - "@comunica/actor-query-operation-update-drop@npm:^2.10.2": version: 2.10.2 resolution: "@comunica/actor-query-operation-update-drop@npm:2.10.2" @@ -1540,21 +1358,6 @@ __metadata: languageName: node linkType: hard -"@comunica/actor-query-operation-update-load@npm:^2.10.1": - version: 2.10.1 - resolution: "@comunica/actor-query-operation-update-load@npm:2.10.1" - dependencies: - "@comunica/bus-query-operation": "npm:^2.10.1" - "@comunica/bus-rdf-update-quads": "npm:^2.10.1" - "@comunica/context-entries": "npm:^2.10.0" - "@comunica/core": "npm:^2.10.0" - "@comunica/types": "npm:^2.10.0" - rdf-data-factory: "npm:^1.1.1" - sparqlalgebrajs: "npm:^4.2.0" - checksum: 10c0/7c953f60f7ff955dfcdfd385ce44d9687fe7121c472ac0ac9cd218f1990e7f14cf219642ae11087ba3fd018f3468b8e7c53bad0dc52e869c5c9ef6f83c737a48 - languageName: node - linkType: hard - "@comunica/actor-query-operation-update-load@npm:^2.10.2": version: 2.10.2 resolution: "@comunica/actor-query-operation-update-load@npm:2.10.2" @@ -1672,21 +1475,6 @@ __metadata: languageName: node linkType: hard -"@comunica/actor-query-result-serialize-sparql-json@npm:^2.10.0": - version: 2.10.0 - resolution: "@comunica/actor-query-result-serialize-sparql-json@npm:2.10.0" - dependencies: - "@comunica/bus-http": "npm:^2.10.0" - "@comunica/bus-http-invalidate": "npm:^2.10.0" - "@comunica/bus-query-result-serialize": "npm:^2.10.0" - "@comunica/core": "npm:^2.10.0" - "@comunica/types": "npm:^2.10.0" - "@rdfjs/types": "npm:*" - readable-stream: "npm:^4.4.2" - checksum: 10c0/b89c7ebeed30cfe9e55e06908ae4ad4f4688c595228f1f394b5061c2087cea680d2744af40e7bb1864177e524a7bde4b3dc152aee5cf2bdd77b9e4371644cbe5 - languageName: node - linkType: hard - "@comunica/actor-query-result-serialize-sparql-json@npm:^2.10.2": version: 2.10.2 resolution: "@comunica/actor-query-result-serialize-sparql-json@npm:2.10.2" @@ -1727,21 +1515,6 @@ __metadata: languageName: node linkType: hard -"@comunica/actor-query-result-serialize-stats@npm:^2.10.0": - version: 2.10.0 - resolution: "@comunica/actor-query-result-serialize-stats@npm:2.10.0" - dependencies: - "@comunica/bus-http": "npm:^2.10.0" - "@comunica/bus-http-invalidate": "npm:^2.10.0" - "@comunica/bus-query-result-serialize": "npm:^2.10.0" - "@comunica/core": "npm:^2.10.0" - "@comunica/types": "npm:^2.10.0" - process: "npm:^0.11.10" - readable-stream: "npm:^4.4.2" - checksum: 10c0/97ad6423c611fdb557b39c4ba2f627f93f87ac874a9d99d06f4dc7c7d2f7cfb3ae8814d28d45e65701cfadad375594da1d772c30e2463545b5f25d9dc1dd0307 - languageName: node - linkType: hard - "@comunica/actor-query-result-serialize-stats@npm:^2.10.2": version: 2.10.2 resolution: "@comunica/actor-query-result-serialize-stats@npm:2.10.2" @@ -2165,23 +1938,7 @@ __metadata: languageName: node linkType: hard -"@comunica/actor-rdf-parse-jsonld@npm:^2.0.1, @comunica/actor-rdf-parse-jsonld@npm:^2.10.0": - version: 2.10.0 - resolution: "@comunica/actor-rdf-parse-jsonld@npm:2.10.0" - dependencies: - "@comunica/bus-http": "npm:^2.10.0" - "@comunica/bus-rdf-parse": "npm:^2.10.0" - "@comunica/context-entries": "npm:^2.10.0" - "@comunica/core": "npm:^2.10.0" - "@comunica/types": "npm:^2.10.0" - jsonld-context-parser: "npm:^2.2.2" - jsonld-streaming-parser: "npm:^3.0.1" - stream-to-string: "npm:^1.2.0" - checksum: 10c0/0291356d77cc6a97bfd6ca92a6e65eea92a91a645c2af67f4fbe5d9880592b24caf35a25da0e114908cff1befa91354e0ecd051bc69f20a3b1c6bdc9583dd55c - languageName: node - linkType: hard - -"@comunica/actor-rdf-parse-jsonld@npm:^2.10.2": +"@comunica/actor-rdf-parse-jsonld@npm:^2.0.1, @comunica/actor-rdf-parse-jsonld@npm:^2.10.2": version: 2.10.2 resolution: "@comunica/actor-rdf-parse-jsonld@npm:2.10.2" dependencies: @@ -2297,26 +2054,6 @@ __metadata: languageName: node linkType: hard -"@comunica/actor-rdf-resolve-hypermedia-sparql@npm:^2.10.1": - version: 2.10.1 - resolution: "@comunica/actor-rdf-resolve-hypermedia-sparql@npm:2.10.1" - dependencies: - "@comunica/bindings-factory": "npm:^2.10.1" - "@comunica/bus-http": "npm:^2.10.0" - "@comunica/bus-rdf-resolve-hypermedia": "npm:^2.10.0" - "@comunica/bus-rdf-resolve-quad-pattern": "npm:^2.10.0" - "@comunica/types": "npm:^2.10.0" - "@rdfjs/types": "npm:*" - asynciterator: "npm:^3.8.1" - fetch-sparql-endpoint: "npm:^4.0.0" - lru-cache: "npm:^10.0.0" - rdf-data-factory: "npm:^1.1.1" - rdf-terms: "npm:^1.11.0" - sparqlalgebrajs: "npm:^4.2.0" - checksum: 10c0/84691d1e50d2a44bad1f06bc38250f001a23d1b4f377dc92d3f550f48a64b1e831f94ecfcb1c39affe739c79a6b5a0f2cb754febbed84f4af605a25c2dd53fea - languageName: node - linkType: hard - "@comunica/actor-rdf-resolve-hypermedia-sparql@npm:^2.10.2": version: 2.10.2 resolution: "@comunica/actor-rdf-resolve-hypermedia-sparql@npm:2.10.2" @@ -2455,24 +2192,6 @@ __metadata: languageName: node linkType: hard -"@comunica/actor-rdf-update-hypermedia-patch-sparql-update@npm:^2.10.1": - version: 2.10.1 - resolution: "@comunica/actor-rdf-update-hypermedia-patch-sparql-update@npm:2.10.1" - dependencies: - "@comunica/bus-http": "npm:^2.10.0" - "@comunica/bus-rdf-update-hypermedia": "npm:^2.10.1" - "@comunica/bus-rdf-update-quads": "npm:^2.10.1" - "@comunica/core": "npm:^2.10.0" - "@comunica/types": "npm:^2.10.0" - "@rdfjs/types": "npm:*" - asynciterator: "npm:^3.8.1" - cross-fetch: "npm:^4.0.0" - rdf-string-ttl: "npm:^1.3.2" - readable-stream: "npm:^4.4.2" - checksum: 10c0/08dc71646c7f96b0d337841b5594d5e77e7133ef3106f6c190be8395697cc026236710cffe4379f10bc137f831bad8d0742ce29f4138f04da99b181281773c20 - languageName: node - linkType: hard - "@comunica/actor-rdf-update-hypermedia-patch-sparql-update@npm:^2.10.2": version: 2.10.2 resolution: "@comunica/actor-rdf-update-hypermedia-patch-sparql-update@npm:2.10.2" @@ -2491,23 +2210,6 @@ __metadata: languageName: node linkType: hard -"@comunica/actor-rdf-update-hypermedia-put-ldp@npm:^2.10.1": - version: 2.10.1 - resolution: "@comunica/actor-rdf-update-hypermedia-put-ldp@npm:2.10.1" - dependencies: - "@comunica/bus-http": "npm:^2.10.0" - "@comunica/bus-rdf-serialize": "npm:^2.10.0" - "@comunica/bus-rdf-update-hypermedia": "npm:^2.10.1" - "@comunica/bus-rdf-update-quads": "npm:^2.10.1" - "@comunica/core": "npm:^2.10.0" - "@comunica/types": "npm:^2.10.0" - "@rdfjs/types": "npm:*" - asynciterator: "npm:^3.8.1" - cross-fetch: "npm:^4.0.0" - checksum: 10c0/bb1fdde7ffa0eaa6f3726bc6e8ebac7d241bf90bb0f6b492e0bfea67e3f152b907412e42e06f82f3171d9cf76ed055dfa336c20f6ac17a564d5d12ca24f8e435 - languageName: node - linkType: hard - "@comunica/actor-rdf-update-hypermedia-put-ldp@npm:^2.10.2": version: 2.10.2 resolution: "@comunica/actor-rdf-update-hypermedia-put-ldp@npm:2.10.2" @@ -2525,24 +2227,6 @@ __metadata: languageName: node linkType: hard -"@comunica/actor-rdf-update-hypermedia-sparql@npm:^2.10.1": - version: 2.10.1 - resolution: "@comunica/actor-rdf-update-hypermedia-sparql@npm:2.10.1" - dependencies: - "@comunica/bus-http": "npm:^2.10.0" - "@comunica/bus-rdf-update-hypermedia": "npm:^2.10.1" - "@comunica/bus-rdf-update-quads": "npm:^2.10.1" - "@comunica/core": "npm:^2.10.0" - "@comunica/types": "npm:^2.10.0" - "@rdfjs/types": "npm:*" - asynciterator: "npm:^3.8.1" - fetch-sparql-endpoint: "npm:^4.0.0" - rdf-string-ttl: "npm:^1.3.2" - stream-to-string: "npm:^1.2.0" - checksum: 10c0/1da848a68f08255734b4a10a86fbbea51e7eb2e23962acba60df1da02b01477ee8de0b36813c623eda6f38a8f85c1f37464d0425998227e21359ad6f871657d4 - languageName: node - linkType: hard - "@comunica/actor-rdf-update-hypermedia-sparql@npm:^2.10.2": version: 2.10.2 resolution: "@comunica/actor-rdf-update-hypermedia-sparql@npm:2.10.2" @@ -2561,23 +2245,6 @@ __metadata: languageName: node linkType: hard -"@comunica/actor-rdf-update-quads-hypermedia@npm:^2.10.1": - version: 2.10.1 - resolution: "@comunica/actor-rdf-update-quads-hypermedia@npm:2.10.1" - dependencies: - "@comunica/bus-dereference-rdf": "npm:^2.10.0" - "@comunica/bus-http-invalidate": "npm:^2.10.0" - "@comunica/bus-rdf-metadata": "npm:^2.10.0" - "@comunica/bus-rdf-metadata-extract": "npm:^2.10.0" - "@comunica/bus-rdf-update-hypermedia": "npm:^2.10.1" - "@comunica/bus-rdf-update-quads": "npm:^2.10.1" - "@comunica/core": "npm:^2.10.0" - "@comunica/types": "npm:^2.10.0" - lru-cache: "npm:^10.0.0" - checksum: 10c0/f2476252212380c4b8293723b8fd59e198b058febc5a40761e0a7711ddabd433bc710fa0f577d606c20c0a7673375ce1f11ebd070401514a1db47a9191a76135 - languageName: node - linkType: hard - "@comunica/actor-rdf-update-quads-hypermedia@npm:^2.10.2": version: 2.10.2 resolution: "@comunica/actor-rdf-update-quads-hypermedia@npm:2.10.2" @@ -2595,21 +2262,6 @@ __metadata: languageName: node linkType: hard -"@comunica/actor-rdf-update-quads-rdfjs-store@npm:^2.10.1": - version: 2.10.1 - resolution: "@comunica/actor-rdf-update-quads-rdfjs-store@npm:2.10.1" - dependencies: - "@comunica/bus-rdf-update-quads": "npm:^2.10.1" - "@comunica/core": "npm:^2.10.0" - "@comunica/types": "npm:^2.10.0" - "@rdfjs/types": "npm:*" - asynciterator: "npm:^3.8.1" - rdf-data-factory: "npm:^1.1.1" - rdf-string: "npm:^1.6.1" - checksum: 10c0/a0babecf61adf8b8912a141da7f5278700e950a088c772e98e50fe6308f849748988e41aaa07cd7cf53422f6460d455e69152a519a927fc5ac4bf2f23b3fd547 - languageName: node - linkType: hard - "@comunica/actor-rdf-update-quads-rdfjs-store@npm:^2.10.2": version: 2.10.2 resolution: "@comunica/actor-rdf-update-quads-rdfjs-store@npm:2.10.2" @@ -2692,20 +2344,7 @@ __metadata: languageName: node linkType: hard -"@comunica/bus-http@npm:^2.0.1, @comunica/bus-http@npm:^2.10.0": - version: 2.10.0 - resolution: "@comunica/bus-http@npm:2.10.0" - dependencies: - "@comunica/core": "npm:^2.10.0" - is-stream: "npm:^2.0.1" - readable-stream-node-to-web: "npm:^1.0.1" - readable-web-to-node-stream: "npm:^3.0.2" - web-streams-ponyfill: "npm:^1.4.2" - checksum: 10c0/dd1913b9ecc090f4a1fdbd07d645e6dcf7ff719eba372d8fe75fc999cd10d3478f6557d2541f1178d46f0b219d3156408181930143f046acf9ffb1784bb1e911 - languageName: node - linkType: hard - -"@comunica/bus-http@npm:^2.10.2": +"@comunica/bus-http@npm:^2.0.1, @comunica/bus-http@npm:^2.10.2": version: 2.10.2 resolution: "@comunica/bus-http@npm:2.10.2" dependencies: @@ -2927,16 +2566,6 @@ __metadata: languageName: node linkType: hard -"@comunica/bus-rdf-update-hypermedia@npm:^2.10.1": - version: 2.10.1 - resolution: "@comunica/bus-rdf-update-hypermedia@npm:2.10.1" - dependencies: - "@comunica/bus-rdf-update-quads": "npm:^2.10.1" - "@comunica/core": "npm:^2.10.0" - checksum: 10c0/ddc37e6504643e4976b467f43a498bd2734d358e76d9da71d75351e2d5a2ee90a65a33fca8d347a2e5ff8d02cd0f3bf816339ba2db4cb50655c4064d332f8786 - languageName: node - linkType: hard - "@comunica/bus-rdf-update-hypermedia@npm:^2.10.2": version: 2.10.2 resolution: "@comunica/bus-rdf-update-hypermedia@npm:2.10.2" @@ -2947,22 +2576,6 @@ __metadata: languageName: node linkType: hard -"@comunica/bus-rdf-update-quads@npm:^2.10.1": - version: 2.10.1 - resolution: "@comunica/bus-rdf-update-quads@npm:2.10.1" - dependencies: - "@comunica/actor-rdf-resolve-quad-pattern-federated": "npm:^2.10.1" - "@comunica/bus-http": "npm:^2.10.0" - "@comunica/context-entries": "npm:^2.10.0" - "@comunica/core": "npm:^2.10.0" - "@comunica/types": "npm:^2.10.0" - "@rdfjs/types": "npm:*" - asynciterator: "npm:^3.8.1" - stream-to-string: "npm:^1.2.0" - checksum: 10c0/47756a2ec95b77495ff068e50482c93c4ab8939e6a5e05d8ee900742a2e7c56a87754b7561339088e48d19a6ecdd6ff2f1705b4467454442328f2238d6880c8f - languageName: node - linkType: hard - "@comunica/bus-rdf-update-quads@npm:^2.10.2": version: 2.10.2 resolution: "@comunica/bus-rdf-update-quads@npm:2.10.2" @@ -3265,7 +2878,7 @@ __metadata: languageName: node linkType: hard -"@comunica/query-sparql@npm:^2.10.2": +"@comunica/query-sparql@npm:^2.10.2, @comunica/query-sparql@npm:^2.9.0": version: 2.10.2 resolution: "@comunica/query-sparql@npm:2.10.2" dependencies: @@ -3409,150 +3022,6 @@ __metadata: languageName: node linkType: hard -"@comunica/query-sparql@npm:^2.9.0": - version: 2.10.1 - resolution: "@comunica/query-sparql@npm:2.10.1" - dependencies: - "@comunica/actor-context-preprocess-source-to-destination": "npm:^2.10.0" - "@comunica/actor-dereference-fallback": "npm:^2.10.0" - "@comunica/actor-dereference-http": "npm:^2.10.0" - "@comunica/actor-dereference-rdf-parse": "npm:^2.10.0" - "@comunica/actor-hash-bindings-sha1": "npm:^2.10.0" - "@comunica/actor-http-fetch": "npm:^2.10.1" - "@comunica/actor-http-proxy": "npm:^2.10.0" - "@comunica/actor-http-wayback": "npm:^2.10.0" - "@comunica/actor-init-query": "npm:^2.10.1" - "@comunica/actor-optimize-query-operation-bgp-to-join": "npm:^2.10.0" - "@comunica/actor-optimize-query-operation-join-bgp": "npm:^2.10.0" - "@comunica/actor-optimize-query-operation-join-connected": "npm:^2.10.0" - "@comunica/actor-query-operation-ask": "npm:^2.10.1" - "@comunica/actor-query-operation-bgp-join": "npm:^2.10.1" - "@comunica/actor-query-operation-construct": "npm:^2.10.1" - "@comunica/actor-query-operation-describe-subject": "npm:^2.10.1" - "@comunica/actor-query-operation-distinct-hash": "npm:^2.10.1" - "@comunica/actor-query-operation-extend": "npm:^2.10.1" - "@comunica/actor-query-operation-filter-sparqlee": "npm:^2.10.1" - "@comunica/actor-query-operation-from-quad": "npm:^2.10.1" - "@comunica/actor-query-operation-group": "npm:^2.10.1" - "@comunica/actor-query-operation-join": "npm:^2.10.1" - "@comunica/actor-query-operation-leftjoin": "npm:^2.10.1" - "@comunica/actor-query-operation-minus": "npm:^2.10.1" - "@comunica/actor-query-operation-nop": "npm:^2.10.1" - "@comunica/actor-query-operation-orderby-sparqlee": "npm:^2.10.1" - "@comunica/actor-query-operation-path-alt": "npm:^2.10.1" - "@comunica/actor-query-operation-path-inv": "npm:^2.10.1" - "@comunica/actor-query-operation-path-link": "npm:^2.10.1" - "@comunica/actor-query-operation-path-nps": "npm:^2.10.1" - "@comunica/actor-query-operation-path-one-or-more": "npm:^2.10.1" - "@comunica/actor-query-operation-path-seq": "npm:^2.10.1" - "@comunica/actor-query-operation-path-zero-or-more": "npm:^2.10.1" - "@comunica/actor-query-operation-path-zero-or-one": "npm:^2.10.1" - "@comunica/actor-query-operation-project": "npm:^2.10.1" - "@comunica/actor-query-operation-quadpattern": "npm:^2.10.1" - "@comunica/actor-query-operation-reduced-hash": "npm:^2.10.1" - "@comunica/actor-query-operation-service": "npm:^2.10.1" - "@comunica/actor-query-operation-slice": "npm:^2.10.1" - "@comunica/actor-query-operation-sparql-endpoint": "npm:^2.10.1" - "@comunica/actor-query-operation-union": "npm:^2.10.1" - "@comunica/actor-query-operation-update-add-rewrite": "npm:^2.10.1" - "@comunica/actor-query-operation-update-clear": "npm:^2.10.1" - "@comunica/actor-query-operation-update-compositeupdate": "npm:^2.10.1" - "@comunica/actor-query-operation-update-copy-rewrite": "npm:^2.10.1" - "@comunica/actor-query-operation-update-create": "npm:^2.10.1" - "@comunica/actor-query-operation-update-deleteinsert": "npm:^2.10.1" - "@comunica/actor-query-operation-update-drop": "npm:^2.10.1" - "@comunica/actor-query-operation-update-load": "npm:^2.10.1" - "@comunica/actor-query-operation-update-move-rewrite": "npm:^2.10.1" - "@comunica/actor-query-operation-values": "npm:^2.10.1" - "@comunica/actor-query-parse-graphql": "npm:^2.10.0" - "@comunica/actor-query-parse-sparql": "npm:^2.10.0" - "@comunica/actor-query-result-serialize-json": "npm:^2.10.0" - "@comunica/actor-query-result-serialize-rdf": "npm:^2.10.0" - "@comunica/actor-query-result-serialize-simple": "npm:^2.10.0" - "@comunica/actor-query-result-serialize-sparql-csv": "npm:^2.10.0" - "@comunica/actor-query-result-serialize-sparql-json": "npm:^2.10.0" - "@comunica/actor-query-result-serialize-sparql-tsv": "npm:^2.10.0" - "@comunica/actor-query-result-serialize-sparql-xml": "npm:^2.10.0" - "@comunica/actor-query-result-serialize-stats": "npm:^2.10.0" - "@comunica/actor-query-result-serialize-table": "npm:^2.10.0" - "@comunica/actor-query-result-serialize-tree": "npm:^2.10.0" - "@comunica/actor-rdf-join-entries-sort-cardinality": "npm:^2.10.0" - "@comunica/actor-rdf-join-inner-hash": "npm:^2.10.1" - "@comunica/actor-rdf-join-inner-multi-bind": "npm:^2.10.1" - "@comunica/actor-rdf-join-inner-multi-empty": "npm:^2.10.1" - "@comunica/actor-rdf-join-inner-multi-smallest": "npm:^2.10.1" - "@comunica/actor-rdf-join-inner-nestedloop": "npm:^2.10.1" - "@comunica/actor-rdf-join-inner-none": "npm:^2.10.1" - "@comunica/actor-rdf-join-inner-single": "npm:^2.10.1" - "@comunica/actor-rdf-join-inner-symmetrichash": "npm:^2.10.1" - "@comunica/actor-rdf-join-minus-hash": "npm:^2.10.1" - "@comunica/actor-rdf-join-minus-hash-undef": "npm:^2.10.1" - "@comunica/actor-rdf-join-optional-bind": "npm:^2.10.1" - "@comunica/actor-rdf-join-optional-nestedloop": "npm:^2.10.1" - "@comunica/actor-rdf-join-selectivity-variable-counting": "npm:^2.10.0" - "@comunica/actor-rdf-metadata-accumulate-cancontainundefs": "npm:^2.10.0" - "@comunica/actor-rdf-metadata-accumulate-cardinality": "npm:^2.10.0" - "@comunica/actor-rdf-metadata-accumulate-pagesize": "npm:^2.10.0" - "@comunica/actor-rdf-metadata-accumulate-requesttime": "npm:^2.10.0" - "@comunica/actor-rdf-metadata-all": "npm:^2.10.0" - "@comunica/actor-rdf-metadata-extract-allow-http-methods": "npm:^2.10.0" - "@comunica/actor-rdf-metadata-extract-hydra-controls": "npm:^2.10.0" - "@comunica/actor-rdf-metadata-extract-hydra-count": "npm:^2.10.0" - "@comunica/actor-rdf-metadata-extract-hydra-pagesize": "npm:^2.10.0" - "@comunica/actor-rdf-metadata-extract-patch-sparql-update": "npm:^2.10.0" - "@comunica/actor-rdf-metadata-extract-put-accepted": "npm:^2.10.0" - "@comunica/actor-rdf-metadata-extract-request-time": "npm:^2.10.0" - "@comunica/actor-rdf-metadata-extract-sparql-service": "npm:^2.10.0" - "@comunica/actor-rdf-metadata-primary-topic": "npm:^2.10.0" - "@comunica/actor-rdf-parse-html": "npm:^2.10.0" - "@comunica/actor-rdf-parse-html-microdata": "npm:^2.10.0" - "@comunica/actor-rdf-parse-html-rdfa": "npm:^2.10.0" - "@comunica/actor-rdf-parse-html-script": "npm:^2.10.0" - "@comunica/actor-rdf-parse-jsonld": "npm:^2.10.0" - "@comunica/actor-rdf-parse-n3": "npm:^2.10.0" - "@comunica/actor-rdf-parse-rdfxml": "npm:^2.10.0" - "@comunica/actor-rdf-parse-shaclc": "npm:^2.10.0" - "@comunica/actor-rdf-parse-xml-rdfa": "npm:^2.10.0" - "@comunica/actor-rdf-resolve-hypermedia-links-next": "npm:^2.10.0" - "@comunica/actor-rdf-resolve-hypermedia-links-queue-fifo": "npm:^2.10.0" - "@comunica/actor-rdf-resolve-hypermedia-none": "npm:^2.10.0" - "@comunica/actor-rdf-resolve-hypermedia-qpf": "npm:^2.10.0" - "@comunica/actor-rdf-resolve-hypermedia-sparql": "npm:^2.10.1" - "@comunica/actor-rdf-resolve-quad-pattern-federated": "npm:^2.10.1" - "@comunica/actor-rdf-resolve-quad-pattern-hypermedia": "npm:^2.10.1" - "@comunica/actor-rdf-resolve-quad-pattern-rdfjs-source": "npm:^2.10.0" - "@comunica/actor-rdf-resolve-quad-pattern-string-source": "npm:^2.10.0" - "@comunica/actor-rdf-serialize-jsonld": "npm:^2.10.0" - "@comunica/actor-rdf-serialize-n3": "npm:^2.10.0" - "@comunica/actor-rdf-serialize-shaclc": "npm:^2.10.0" - "@comunica/actor-rdf-update-hypermedia-patch-sparql-update": "npm:^2.10.1" - "@comunica/actor-rdf-update-hypermedia-put-ldp": "npm:^2.10.1" - "@comunica/actor-rdf-update-hypermedia-sparql": "npm:^2.10.1" - "@comunica/actor-rdf-update-quads-hypermedia": "npm:^2.10.1" - "@comunica/actor-rdf-update-quads-rdfjs-store": "npm:^2.10.1" - "@comunica/bus-http-invalidate": "npm:^2.10.0" - "@comunica/bus-query-operation": "npm:^2.10.1" - "@comunica/config-query-sparql": "npm:^2.7.0" - "@comunica/core": "npm:^2.10.0" - "@comunica/logger-void": "npm:^2.10.0" - "@comunica/mediator-all": "npm:^2.10.0" - "@comunica/mediator-combine-pipeline": "npm:^2.10.0" - "@comunica/mediator-combine-union": "npm:^2.10.0" - "@comunica/mediator-join-coefficients-fixed": "npm:^2.10.1" - "@comunica/mediator-number": "npm:^2.10.0" - "@comunica/mediator-race": "npm:^2.10.0" - "@comunica/runner": "npm:^2.10.0" - "@comunica/runner-cli": "npm:^2.10.0" - "@comunica/types": "npm:^2.10.0" - process: "npm:^0.11.10" - bin: - comunica-dynamic-sparql: bin/query-dynamic.js - comunica-sparql: bin/query.js - comunica-sparql-http: bin/http.js - checksum: 10c0/cecad48f38d3f7545ad39389c4387f600909d00672d464084028a5a7627e7407e447a4f2f8fefb54c37aa194bec5a2c44e9c41451e25b50bb9f0976b53be99c5 - languageName: node - linkType: hard - "@comunica/runner-cli@npm:^2.10.0": version: 2.10.0 resolution: "@comunica/runner-cli@npm:2.10.0" @@ -3805,6 +3274,17 @@ __metadata: languageName: node linkType: hard +"@effect/schema@npm:0.75.5": + version: 0.75.5 + resolution: "@effect/schema@npm:0.75.5" + dependencies: + fast-check: "npm:^3.21.0" + peerDependencies: + effect: ^3.9.2 + checksum: 10c0/287a860f67eb41d54d0b46f2303eba273b35a9db41f1c2c352fc94023e88574969e03c6284d5dcefdda953f56eabdd98d2cdd268fa872f5966108f540c9e3f44 + languageName: node + linkType: hard + "@esbuild/aix-ppc64@npm:0.23.1": version: 0.23.1 resolution: "@esbuild/aix-ppc64@npm:0.23.1" @@ -4130,6 +3610,13 @@ __metadata: languageName: node linkType: hard +"@isaacs/ttlcache@npm:^1.4.1": + version: 1.4.1 + resolution: "@isaacs/ttlcache@npm:1.4.1" + checksum: 10c0/6921de516917b02673a58e543c2b06fd04237cbf6d089ca22d6e98defa4b1e9a48258cb071d6b581284bb497bea687320788830541511297eecbe6e93a665bbf + languageName: node + linkType: hard + "@istanbuljs/load-nyc-config@npm:^1.0.0": version: 1.1.0 resolution: "@istanbuljs/load-nyc-config@npm:1.1.0" @@ -4632,6 +4119,13 @@ __metadata: languageName: node linkType: hard +"@sindresorhus/merge-streams@npm:^2.1.0": + version: 2.3.0 + resolution: "@sindresorhus/merge-streams@npm:2.3.0" + checksum: 10c0/69ee906f3125fb2c6bb6ec5cdd84e8827d93b49b3892bce8b62267116cc7e197b5cccf20c160a1d32c26014ecd14470a72a5e3ee37a58f1d6dadc0db1ccf3894 + languageName: node + linkType: hard + "@sinonjs/commons@npm:^3.0.0": version: 3.0.0 resolution: "@sinonjs/commons@npm:3.0.0" @@ -4665,196 +4159,41 @@ __metadata: resolution: "@solid/access-control-policy@npm:0.1.3" checksum: 10c0/3aacce607be723eeb4e253098751eabbd43277fd6923a8341181ac5f07f88fdee0541095a33b58e31cea762b1f5add1dd8a893ff913a4570a1c87d5cd972c8e4 languageName: node - linkType: hard - -"@solid/access-token-verifier@npm:^1.2.0": - version: 1.2.9 - resolution: "@solid/access-token-verifier@npm:1.2.9" - dependencies: - jose: "npm:^4.7.0" - lru-cache: "npm:^6.0.0" - n3: "npm:^1.16.1" - node-fetch: "npm:^2.6.7" - ts-guards: "npm:^0.5.1" - checksum: 10c0/b9727907208b50d4668d3d897939a5cbb5c3e01da97a74588a2773eadb871be127de9c2850ee1184d2509f2b3df8c59702c0c532e634a34f96f42ff81579a8e7 - languageName: node - linkType: hard - -"@solid/access-token-verifier@npm:^2.0.5, @solid/access-token-verifier@npm:^2.1.0": - version: 2.1.0 - resolution: "@solid/access-token-verifier@npm:2.1.0" - dependencies: - jose: "npm:^5.1.3" - lru-cache: "npm:^6.0.0" - n3: "npm:^1.17.1" - node-fetch: "npm:^2.7.0" - ts-guards: "npm:^0.5.1" - checksum: 10c0/1741eed98569c957b59de717bfc39964035d64d298f987c8e05d73982265518afcb9949c282316cce41d6fc2abc98dfaba12247ade841e42bda0c611a30e6119 - languageName: node - linkType: hard - -"@solid/community-server@npm:7.0.3": - version: 7.0.3 - resolution: "@solid/community-server@npm:7.0.3" - dependencies: - "@comunica/context-entries": "npm:^2.8.2" - "@comunica/query-sparql": "npm:^2.9.0" - "@rdfjs/types": "npm:^1.1.0" - "@solid/access-control-policy": "npm:^0.1.3" - "@solid/access-token-verifier": "npm:^2.0.5" - "@types/async-lock": "npm:^1.4.0" - "@types/bcryptjs": "npm:^2.4.4" - "@types/cookie": "npm:^0.5.2" - "@types/cors": "npm:^2.8.14" - "@types/ejs": "npm:^3.1.3" - "@types/end-of-stream": "npm:^1.4.2" - "@types/fs-extra": "npm:^11.0.2" - "@types/lodash.orderby": "npm:^4.6.7" - "@types/mime-types": "npm:^2.1.2" - "@types/n3": "npm:^1.16.3" - "@types/node": "npm:^18.18.4" - "@types/nodemailer": "npm:^6.4.11" - "@types/oidc-provider": "npm:^8.4.0" - "@types/proper-lockfile": "npm:^4.1.2" - "@types/pump": "npm:^1.1.1" - "@types/punycode": "npm:^2.1.0" - "@types/rdf-validate-shacl": "npm:^0.4.4" - "@types/sparqljs": "npm:^3.1.6" - "@types/url-join": "npm:^4.0.1" - "@types/uuid": "npm:^9.0.5" - "@types/ws": "npm:^8.5.7" - "@types/yargs": "npm:^17.0.28" - arrayify-stream: "npm:^2.0.1" - async-lock: "npm:^1.4.0" - bcryptjs: "npm:^2.4.3" - componentsjs: "npm:^5.4.2" - cookie: "npm:^0.5.0" - cors: "npm:^2.8.5" - cross-fetch: "npm:^4.0.0" - ejs: "npm:^3.1.9" - end-of-stream: "npm:^1.4.4" - escape-string-regexp: "npm:^4.0.0" - fetch-sparql-endpoint: "npm:^4.1.0" - fs-extra: "npm:^11.1.1" - handlebars: "npm:^4.7.8" - ioredis: "npm:^5.3.2" - iso8601-duration: "npm:^2.1.1" - jose: "npm:^4.15.2" - jsonld-context-parser: "npm:^2.3.2" - lodash.orderby: "npm:^4.6.0" - marked: "npm:^9.1.0" - mime-types: "npm:^2.1.35" - n3: "npm:^1.17.1" - nodemailer: "npm:^6.9.6" - oidc-provider: "npm:^8.4.0" - proper-lockfile: "npm:^4.1.2" - pump: "npm:^3.0.0" - punycode: "npm:^2.3.0" - rdf-dereference: "npm:^2.2.0" - rdf-parse: "npm:^2.3.2" - rdf-serialize: "npm:^2.2.2" - rdf-string: "npm:^1.6.3" - rdf-terms: "npm:^1.11.0" - rdf-validate-shacl: "npm:^0.4.5" - sparqlalgebrajs: "npm:^4.3.0" - sparqljs: "npm:^3.7.1" - url-join: "npm:^4.0.1" - uuid: "npm:^9.0.1" - winston: "npm:^3.11.0" - winston-transport: "npm:^4.5.0" - ws: "npm:^8.14.2" - yargs: "npm:^17.7.2" - yup: "npm:^1.3.2" - bin: - community-solid-server: bin/server.js - checksum: 10c0/290242c5ae5b1dc3d418b5de4a61b3c39a5461769a9a01a0782d4a9214acf9a0f640aee5dbdf249ca3a70f783e8e45b309012a37ea2f714d8a8b477dcc1c14f3 - languageName: node - linkType: hard - -"@solid/community-server@npm:^7.0.2": - version: 7.0.2 - resolution: "@solid/community-server@npm:7.0.2" - dependencies: - "@comunica/context-entries": "npm:^2.8.2" - "@comunica/query-sparql": "npm:^2.9.0" - "@rdfjs/types": "npm:^1.1.0" - "@solid/access-control-policy": "npm:^0.1.3" - "@solid/access-token-verifier": "npm:^2.0.5" - "@types/async-lock": "npm:^1.4.0" - "@types/bcryptjs": "npm:^2.4.4" - "@types/cookie": "npm:^0.5.2" - "@types/cors": "npm:^2.8.14" - "@types/ejs": "npm:^3.1.3" - "@types/end-of-stream": "npm:^1.4.2" - "@types/fs-extra": "npm:^11.0.2" - "@types/lodash.orderby": "npm:^4.6.7" - "@types/mime-types": "npm:^2.1.2" - "@types/n3": "npm:^1.16.3" - "@types/node": "npm:^18.18.4" - "@types/nodemailer": "npm:^6.4.11" - "@types/oidc-provider": "npm:^8.4.0" - "@types/proper-lockfile": "npm:^4.1.2" - "@types/pump": "npm:^1.1.1" - "@types/punycode": "npm:^2.1.0" - "@types/rdf-validate-shacl": "npm:^0.4.4" - "@types/sparqljs": "npm:^3.1.6" - "@types/url-join": "npm:^4.0.1" - "@types/uuid": "npm:^9.0.5" - "@types/ws": "npm:^8.5.7" - "@types/yargs": "npm:^17.0.28" - arrayify-stream: "npm:^2.0.1" - async-lock: "npm:^1.4.0" - bcryptjs: "npm:^2.4.3" - componentsjs: "npm:^5.4.2" - cookie: "npm:^0.5.0" - cors: "npm:^2.8.5" - cross-fetch: "npm:^4.0.0" - ejs: "npm:^3.1.9" - end-of-stream: "npm:^1.4.4" - escape-string-regexp: "npm:^4.0.0" - fetch-sparql-endpoint: "npm:^4.1.0" - fs-extra: "npm:^11.1.1" - handlebars: "npm:^4.7.8" - ioredis: "npm:^5.3.2" - iso8601-duration: "npm:^2.1.1" - jose: "npm:^4.15.2" - jsonld-context-parser: "npm:^2.3.2" - lodash.orderby: "npm:^4.6.0" - marked: "npm:^9.1.0" - mime-types: "npm:^2.1.35" + linkType: hard + +"@solid/access-token-verifier@npm:^1.2.0": + version: 1.2.9 + resolution: "@solid/access-token-verifier@npm:1.2.9" + dependencies: + jose: "npm:^4.7.0" + lru-cache: "npm:^6.0.0" + n3: "npm:^1.16.1" + node-fetch: "npm:^2.6.7" + ts-guards: "npm:^0.5.1" + checksum: 10c0/b9727907208b50d4668d3d897939a5cbb5c3e01da97a74588a2773eadb871be127de9c2850ee1184d2509f2b3df8c59702c0c532e634a34f96f42ff81579a8e7 + languageName: node + linkType: hard + +"@solid/access-token-verifier@npm:^2.1.0": + version: 2.1.0 + resolution: "@solid/access-token-verifier@npm:2.1.0" + dependencies: + jose: "npm:^5.1.3" + lru-cache: "npm:^6.0.0" n3: "npm:^1.17.1" - nodemailer: "npm:^6.9.6" - oidc-provider: "npm:^8.4.0" - proper-lockfile: "npm:^4.1.2" - pump: "npm:^3.0.0" - punycode: "npm:^2.3.0" - rdf-dereference: "npm:^2.2.0" - rdf-parse: "npm:^2.3.2" - rdf-serialize: "npm:^2.2.2" - rdf-string: "npm:^1.6.3" - rdf-terms: "npm:^1.11.0" - rdf-validate-shacl: "npm:^0.4.5" - sparqlalgebrajs: "npm:^4.3.0" - sparqljs: "npm:^3.7.1" - url-join: "npm:^4.0.1" - uuid: "npm:^9.0.1" - winston: "npm:^3.11.0" - winston-transport: "npm:^4.5.0" - ws: "npm:^8.14.2" - yargs: "npm:^17.7.2" - yup: "npm:^1.3.2" - bin: - community-solid-server: bin/server.js - checksum: 10c0/944d4c0ec91873f6d86c19e089e6e79a71612d779e8dc5df93afe74a797404814ac03b19980f9ff95e9bcb883546f516daaa3c69581a9ae846df641d179df9ba + node-fetch: "npm:^2.7.0" + ts-guards: "npm:^0.5.1" + checksum: 10c0/1741eed98569c957b59de717bfc39964035d64d298f987c8e05d73982265518afcb9949c282316cce41d6fc2abc98dfaba12247ade841e42bda0c611a30e6119 languageName: node linkType: hard -"@solid/community-server@npm:^7.0.3, @solid/community-server@npm:^7.0.4": - version: 7.0.4 - resolution: "@solid/community-server@npm:7.0.4" +"@solid/community-server@npm:^7.0.3, @solid/community-server@npm:^7.1.7": + version: 7.1.7 + resolution: "@solid/community-server@npm:7.1.7" dependencies: "@comunica/context-entries": "npm:^2.8.2" "@comunica/query-sparql": "npm:^2.9.0" + "@isaacs/ttlcache": "npm:^1.4.1" "@rdfjs/types": "npm:^1.1.0" "@solid/access-control-policy": "npm:^0.1.3" "@solid/access-token-verifier": "npm:^2.1.0" @@ -4883,8 +4222,8 @@ __metadata: arrayify-stream: "npm:^2.0.1" async-lock: "npm:^1.4.0" bcryptjs: "npm:^2.4.3" - componentsjs: "npm:^5.4.2" - cookie: "npm:^0.5.0" + componentsjs: "npm:^5.5.1" + cookie: "npm:^0.7.0" cors: "npm:^2.8.5" cross-fetch: "npm:^4.0.0" ejs: "npm:^3.1.9" @@ -4901,7 +4240,7 @@ __metadata: marked: "npm:^9.1.0" mime-types: "npm:^2.1.35" n3: "npm:^1.17.1" - nodemailer: "npm:^6.9.6" + nodemailer: "npm:^6.9.9" oidc-provider: "npm:^8.4.0" proper-lockfile: "npm:^4.1.2" pump: "npm:^3.0.0" @@ -4923,7 +4262,7 @@ __metadata: yup: "npm:^1.3.2" bin: community-solid-server: bin/server.js - checksum: 10c0/40c6f2b0a77bbf56fadc9f75bcc1d10f97bbc04f37e26fe916363a4b21d53360a878e389031f2ddd2d0763b5d123dce8c4f7760b7d3497ec64bd70650d8c32da + checksum: 10c0/a9a28d268e49561508c3e24f2c337736d4ea0da9378e42e727553e292383de2d2bf0d95e422befe45527091d23a4091f2512f5dbafe494bb0c74f0ec62f92b41 languageName: node linkType: hard @@ -4946,19 +4285,11 @@ __metadata: resolution: "@solidlab/ucp@workspace:packages/ucp" dependencies: "@smessie/readable-web-to-node-stream": "npm:^3.0.3" - "@solid/community-server": "npm:7.0.3" - "@types/jest": "npm:^29.5.12" - "@types/n3": "npm:^1.16.3" - "@types/node": "npm:^20.11.25" - jest: "npm:^29.7.0" - jest-rdf: "npm:^1.8.1" + "@types/n3": "npm:^1.16.4" koreografeye: "npm:^0.4.8" - n3: "npm:^1.17.1" + n3: "npm:^1.17.2" rdf-parse: "npm:^2.3.3" rdf-store-stream: "npm:^2.0.1" - ts-jest: "npm:^29.1.2" - ts-node: "npm:^10.9.1" - typescript: "npm:^5.3.3" languageName: unknown linkType: soft @@ -4966,13 +4297,11 @@ __metadata: version: 0.0.0-use.local resolution: "@solidlab/uma-css@workspace:packages/css" dependencies: - "@solid/community-server": "npm:^7.0.2" + "@solid/community-server": "npm:^7.1.7" "@solidlab/derived-resources-component": "npm:^1.0.2" "@solidlab/uma": "workspace:^" "@types/n3": "npm:^1.16.4" - "@types/node": "npm:^18.18.11" - componentsjs: "npm:^5.4.2" - cross-fetch: "npm:^4.0.0" + componentsjs: "npm:^5.5.1" fetch-retry: "npm:^6.0.0" http-message-signatures: "npm:^1.0.4" jose: "npm:^5.2.2" @@ -4986,23 +4315,18 @@ __metadata: dependencies: "@httpland/authorization-parser": "npm:^1.1.0" "@solid/access-token-verifier": "npm:^1.2.0" - "@solid/community-server": "npm:^7.0.4" + "@solid/community-server": "npm:^7.1.7" "@solidlab/ucp": "workspace:^" - "@types/clone": "npm:^2.1.4" "@types/n3": "npm:^1.16.4" - "@types/node": "npm:^18.18.11" - "@types/uuid": "npm:^8.3.4" - clone: "npm:^2.1.2" - componentsjs: "npm:5.4.2" - cross-fetch: "npm:^4.0.0" + componentsjs: "npm:^5.5.1" get-jwks: "npm:^9.0.1" http-message-signatures: "npm:^1.0.4" - jose: "npm:^4.5.1" + jose: "npm:^5.2.2" koreografeye: "npm:^0.4.8" logform: "npm:^2.6.0" n3: "npm:^1.17.2" ts-node: "npm:^10.9.2" - uuid: "npm:^9.0.1" + uri-template-lite: "npm:^23.4.0" winston: "npm:^3.11.0" languageName: unknown linkType: soft @@ -5023,24 +4347,24 @@ __metadata: "@inrupt/solid-client": "npm:^2.0.1" "@inrupt/solid-client-authn-core": "npm:^2.1.0" "@solidlab/ucp": "workspace:^" - "@types/jest": "npm:^29.5.6" - "@types/node": "npm:^20.9.4" + "@types/jest": "npm:^29.5.12" + "@types/node": "npm:^20.11.25" "@typescript-eslint/eslint-plugin": "npm:^5.12.1" "@typescript-eslint/parser": "npm:^5.12.1" chalk: "npm:^5.4.1" componentsjs-generator: "npm:^3.1.2" concurrently: "npm:^8.2.2" - cross-fetch: "npm:^4.0.0" eslint: "npm:^8.10.0" jest: "npm:^29.7.0" + jest-rdf: "npm:^1.8.1" jsonld: "npm:^8.3.3" koreografeye: "npm:^0.4.8" - odrl-evaluator: "npm:^0.1.1" shx: "npm:^0.3.4" - ts-jest: "npm:^29.1.1" + syncpack: "npm:^13.0.2" + ts-jest: "npm:^29.1.2" ts-node: "npm:^10.9.2" tsx: "npm:^4.19.2" - typescript: "npm:^5.2.2" + typescript: "npm:^5.3.3" languageName: unknown linkType: soft @@ -5217,13 +4541,6 @@ __metadata: languageName: node linkType: hard -"@types/clone@npm:^2.1.4": - version: 2.1.4 - resolution: "@types/clone@npm:2.1.4" - checksum: 10c0/89a18c96be125417b21191baa9a7eb7e1e80734b1a7e00dc1e32b268d0da616d6f5ec873dce8f2d36f9640a694d20039f419232f81c56a1bd4e94f07d04bc490 - languageName: node - linkType: hard - "@types/clownface@npm:*": version: 2.0.5 resolution: "@types/clownface@npm:2.0.5" @@ -5291,13 +4608,6 @@ __metadata: languageName: node linkType: hard -"@types/emscripten@npm:^1.39.13": - version: 1.40.0 - resolution: "@types/emscripten@npm:1.40.0" - checksum: 10c0/2c809da43cb42396a78bc1bf1f8bb1eb23874b22425ccc0efd2dff80522318739fc38e845d98983948ca271fe1a551f68043094d20df14e745aff8db2123a0e5 - languageName: node - linkType: hard - "@types/end-of-stream@npm:^1.4.2": version: 1.4.4 resolution: "@types/end-of-stream@npm:1.4.4" @@ -5415,16 +4725,6 @@ __metadata: languageName: node linkType: hard -"@types/jest@npm:^29.5.6": - version: 29.5.9 - resolution: "@types/jest@npm:29.5.9" - dependencies: - expect: "npm:^29.0.0" - pretty-format: "npm:^29.0.0" - checksum: 10c0/66fe6b9b1c72739d02edaf1c98468aec84c700d841376afab9321b5268cb7b656a00a3561408b6c37f8619fffa7902e2ef29357fbc355e3e641c18b254899e49 - languageName: node - linkType: hard - "@types/json-schema@npm:^7.0.9": version: 7.0.15 resolution: "@types/json-schema@npm:7.0.15" @@ -5541,12 +4841,12 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:^18.18.11": - version: 18.18.11 - resolution: "@types/node@npm:18.18.11" +"@types/node@npm:^20.11.25": + version: 20.17.17 + resolution: "@types/node@npm:20.17.17" dependencies: - undici-types: "npm:~5.26.4" - checksum: 10c0/174bcf5c79084117528b0fda658b348b5d449e68ac24e462232a871e350b9148377068107f8961aeda981b39d0aee7eec8061698cdb8feef8c8f5de13c79017b + undici-types: "npm:~6.19.2" + checksum: 10c0/6aebc8053b96f1b6d82c3a3fe5fc20d9f7eeb507d79d967221bbecaa2b26665fa1c9efe460be2a2f4aaae4e98f37797df7d5a4189f2b6edf8abf9f82f7fd8b1f languageName: node linkType: hard @@ -5685,16 +4985,7 @@ __metadata: languageName: node linkType: hard -"@types/sparqljs@npm:^3.1.3": - version: 3.1.8 - resolution: "@types/sparqljs@npm:3.1.8" - dependencies: - rdf-js: "npm:^4.0.2" - checksum: 10c0/6e760b27a2aa3f8004306af4d5fa71ac28a572883004827d12082ec845acfd8ea5c13663585ced2477a191c3d1b9c3130cae4bc614ec9bd778a1863dc2148655 - languageName: node - linkType: hard - -"@types/sparqljs@npm:^3.1.6": +"@types/sparqljs@npm:^3.1.3, @types/sparqljs@npm:^3.1.6": version: 3.1.9 resolution: "@types/sparqljs@npm:3.1.9" dependencies: @@ -5731,13 +5022,6 @@ __metadata: languageName: node linkType: hard -"@types/uuid@npm:8.3.4, @types/uuid@npm:^8.3.4": - version: 8.3.4 - resolution: "@types/uuid@npm:8.3.4" - checksum: 10c0/b9ac98f82fcf35962317ef7dc44d9ac9e0f6fdb68121d384c88fe12ea318487d5585d3480fa003cf28be86a3bbe213ca688ba786601dce4a97724765eb5b1cf2 - languageName: node - linkType: hard - "@types/uuid@npm:^9.0.0, @types/uuid@npm:^9.0.5": version: 9.0.7 resolution: "@types/uuid@npm:9.0.7" @@ -5992,6 +5276,13 @@ __metadata: languageName: node linkType: hard +"ansi-colors@npm:^4.1.1": + version: 4.1.3 + resolution: "ansi-colors@npm:4.1.3" + checksum: 10c0/ec87a2f59902f74e61eada7f6e6fe20094a628dab765cfdbd03c3477599368768cffccdb5d3bb19a1b6c99126783a143b1fee31aab729b31ffe5836c7e5e28b9 + languageName: node + linkType: hard + "ansi-escapes@npm:^4.2.1": version: 4.3.2 resolution: "ansi-escapes@npm:4.3.2" @@ -6319,12 +5610,12 @@ __metadata: languageName: node linkType: hard -"braces@npm:^3.0.2": - version: 3.0.2 - resolution: "braces@npm:3.0.2" +"braces@npm:^3.0.3": + version: 3.0.3 + resolution: "braces@npm:3.0.3" dependencies: - fill-range: "npm:^7.0.1" - checksum: 10c0/321b4d675791479293264019156ca322163f02dc06e3c4cab33bb15cd43d80b51efef69b0930cfde3acd63d126ebca24cd0544fa6f261e093a0fb41ab9dda381 + fill-range: "npm:^7.1.1" + checksum: 10c0/7c6dfd30c338d2997ba77500539227b9d1f85e388a5f43220865201e407e076783d0881f2d297b9f80951b4c957fcf0b51c1d2d24227631643c3f7c284b0aa04 languageName: node linkType: hard @@ -6496,6 +5787,22 @@ __metadata: languageName: node linkType: hard +"chalk-template@npm:1.1.0": + version: 1.1.0 + resolution: "chalk-template@npm:1.1.0" + dependencies: + chalk: "npm:^5.2.0" + checksum: 10c0/bb6eda6115a33d06828caf8c44f786c26e0d392c74c2bd6bb0f7526588b15664e3e7c0305858531cdd9b266fc54a31fe71fe3844afcd47a3e67445313f149437 + languageName: node + linkType: hard + +"chalk@npm:5.4.1, chalk@npm:^5.2.0, chalk@npm:^5.3.0, chalk@npm:^5.4.1": + version: 5.4.1 + resolution: "chalk@npm:5.4.1" + checksum: 10c0/b23e88132c702f4855ca6d25cb5538b1114343e41472d5263ee8a37cccfccd9c4216d111e1097c6a27830407a1dc81fecdf2a56f2c63033d4dbbd88c10b0dcef + languageName: node + linkType: hard + "chalk@npm:^2.4.2": version: 2.4.2 resolution: "chalk@npm:2.4.2" @@ -6517,13 +5824,6 @@ __metadata: languageName: node linkType: hard -"chalk@npm:^5.4.1": - version: 5.4.1 - resolution: "chalk@npm:5.4.1" - checksum: 10c0/b23e88132c702f4855ca6d25cb5538b1114343e41472d5263ee8a37cccfccd9c4216d111e1097c6a27830407a1dc81fecdf2a56f2c63033d4dbbd88c10b0dcef - languageName: node - linkType: hard - "char-regex@npm:^1.0.2": version: 1.0.2 resolution: "char-regex@npm:1.0.2" @@ -6559,6 +5859,22 @@ __metadata: languageName: node linkType: hard +"cli-cursor@npm:^5.0.0": + version: 5.0.0 + resolution: "cli-cursor@npm:5.0.0" + dependencies: + restore-cursor: "npm:^5.0.0" + checksum: 10c0/7ec62f69b79f6734ab209a3e4dbdc8af7422d44d360a7cb1efa8a0887bbe466a6e625650c466fe4359aee44dbe2dc0b6994b583d40a05d0808a5cb193641d220 + languageName: node + linkType: hard + +"cli-spinners@npm:^2.9.2": + version: 2.9.2 + resolution: "cli-spinners@npm:2.9.2" + checksum: 10c0/907a1c227ddf0d7a101e7ab8b300affc742ead4b4ebe920a5bf1bc6d45dce2958fcd195eb28fa25275062fe6fa9b109b93b63bc8033396ed3bcb50297008b3a3 + languageName: node + linkType: hard + "cliui@npm:^8.0.1": version: 8.0.1 resolution: "cliui@npm:8.0.1" @@ -6570,13 +5886,6 @@ __metadata: languageName: node linkType: hard -"clone@npm:^2.1.2": - version: 2.1.2 - resolution: "clone@npm:2.1.2" - checksum: 10c0/ed0601cd0b1606bc7d82ee7175b97e68d1dd9b91fd1250a3617b38d34a095f8ee0431d40a1a611122dcccb4f93295b4fdb94942aa763392b5fe44effa50c2d5e - languageName: node - linkType: hard - "clownface@npm:^1.4.0": version: 1.5.1 resolution: "clownface@npm:1.5.1" @@ -6670,6 +5979,13 @@ __metadata: languageName: node linkType: hard +"commander@npm:13.1.0": + version: 13.1.0 + resolution: "commander@npm:13.1.0" + checksum: 10c0/7b8c5544bba704fbe84b7cab2e043df8586d5c114a4c5b607f83ae5060708940ed0b5bd5838cf8ce27539cde265c1cbd59ce3c8c6b017ed3eec8943e3a415164 + languageName: node + linkType: hard + "commander@npm:^12.0.0": version: 12.0.0 resolution: "commander@npm:12.0.0" @@ -6714,9 +6030,9 @@ __metadata: languageName: node linkType: hard -"componentsjs@npm:5.4.2, componentsjs@npm:^5.0.1, componentsjs@npm:^5.3.2, componentsjs@npm:^5.4.2": - version: 5.4.2 - resolution: "componentsjs@npm:5.4.2" +"componentsjs@npm:^5.0.1, componentsjs@npm:^5.3.2, componentsjs@npm:^5.5.1": + version: 5.5.1 + resolution: "componentsjs@npm:5.5.1" dependencies: "@rdfjs/types": "npm:*" "@types/minimist": "npm:^1.2.0" @@ -6734,7 +6050,7 @@ __metadata: winston: "npm:^3.3.3" bin: componentsjs-compile-config: bin/compile-config.js - checksum: 10c0/3f538333e0260722d9c10675bc2b426a66965986500e5c2086c14829297fabfb553b9fc7f66eb35345f49a50ce42eae947c9ae56a1db5c77de017c7a75820ba5 + checksum: 10c0/10a19a7cf34ae515a57d7387755b5e2319bcbd5e14868e772702e44f10b09b3b1d2eb38671a410a364a02330d3f37949d195e04268672558780296278a0d2bfa languageName: node linkType: hard @@ -6825,10 +6141,10 @@ __metadata: languageName: node linkType: hard -"cookie@npm:^0.5.0": - version: 0.5.0 - resolution: "cookie@npm:0.5.0" - checksum: 10c0/c01ca3ef8d7b8187bae434434582288681273b5a9ed27521d4d7f9f7928fe0c920df0decd9f9d3bbd2d14ac432b8c8cf42b98b3bdd5bfe0e6edddeebebe8b61d +"cookie@npm:^0.7.0": + version: 0.7.2 + resolution: "cookie@npm:0.7.2" + checksum: 10c0/9596e8ccdbf1a3a88ae02cf5ee80c1c50959423e1022e4e60b91dd87c622af1da309253d8abdb258fb5e3eacb4f08e579dc58b4897b8087574eee0fd35dfa5d2 languageName: node linkType: hard @@ -6866,6 +6182,23 @@ __metadata: languageName: node linkType: hard +"cosmiconfig@npm:9.0.0": + version: 9.0.0 + resolution: "cosmiconfig@npm:9.0.0" + dependencies: + env-paths: "npm:^2.2.1" + import-fresh: "npm:^3.3.0" + js-yaml: "npm:^4.1.0" + parse-json: "npm:^5.2.0" + peerDependencies: + typescript: ">=4.9.5" + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/1c1703be4f02a250b1d6ca3267e408ce16abfe8364193891afc94c2d5c060b69611fdc8d97af74b7e6d5d1aac0ab2fb94d6b079573146bc2d756c2484ce5f0ee + languageName: node + linkType: hard + "cosmiconfig@npm:^7, cosmiconfig@npm:^7.0.0": version: 7.1.0 resolution: "cosmiconfig@npm:7.1.0" @@ -7210,6 +6543,15 @@ __metadata: languageName: node linkType: hard +"effect@npm:3.12.7": + version: 3.12.7 + resolution: "effect@npm:3.12.7" + dependencies: + fast-check: "npm:^3.23.1" + checksum: 10c0/0b15bd7f407783f2c98ec7efd4f1525c87eb83da3e57680e52224d8c54d218b70e1f4a4d445a2660269d97a1a580ec7aa913053708933a41acba1fc92bb87336 + languageName: node + linkType: hard + "ejs@npm:^3.1.9": version: 3.1.9 resolution: "ejs@npm:3.1.9" @@ -7250,6 +6592,13 @@ __metadata: languageName: node linkType: hard +"emoji-regex@npm:^10.3.0": + version: 10.4.0 + resolution: "emoji-regex@npm:10.4.0" + checksum: 10c0/a3fcedfc58bfcce21a05a5f36a529d81e88d602100145fcca3dc6f795e3c8acc4fc18fe773fbf9b6d6e9371205edb3afa2668ec3473fa2aa7fd47d2a9d46482d + languageName: node + linkType: hard + "emoji-regex@npm:^8.0.0": version: 8.0.0 resolution: "emoji-regex@npm:8.0.0" @@ -7296,6 +6645,16 @@ __metadata: languageName: node linkType: hard +"enquirer@npm:2.4.1": + version: 2.4.1 + resolution: "enquirer@npm:2.4.1" + dependencies: + ansi-colors: "npm:^4.1.1" + strip-ansi: "npm:^6.0.1" + checksum: 10c0/43850479d7a51d36a9c924b518dcdc6373b5a8ae3401097d336b7b7e258324749d0ad37a1fcaa5706f04799baa05585cd7af19ebdf7667673e7694435fcea918 + languageName: node + linkType: hard + "entities@npm:^4.2.0, entities@npm:^4.4.0, entities@npm:^4.5.0": version: 4.5.0 resolution: "entities@npm:4.5.0" @@ -7303,7 +6662,7 @@ __metadata: languageName: node linkType: hard -"env-paths@npm:^2.2.0": +"env-paths@npm:^2.2.0, env-paths@npm:^2.2.1": version: 2.2.1 resolution: "env-paths@npm:2.2.1" checksum: 10c0/285325677bf00e30845e330eec32894f5105529db97496ee3f598478e50f008c5352a41a30e5e72ec9de8a542b5a570b85699cd63bd2bc646dbcb9f311d83bc4 @@ -7665,17 +7024,12 @@ __metadata: languageName: node linkType: hard -"eyereasoner@npm:^16.18.4": - version: 16.34.1 - resolution: "eyereasoner@npm:16.34.1" +"fast-check@npm:3.23.2, fast-check@npm:^3.21.0, fast-check@npm:^3.23.1": + version: 3.23.2 + resolution: "fast-check@npm:3.23.2" dependencies: - n3: "npm:^1.16.3" - swipl-wasm: "npm:4.0.13" - peerDependencies: - "@rdfjs/types": ^1.1.0 - bin: - eyereasoner: dist/bin/index.js - checksum: 10c0/a1c78114edc20d94b7cb052305d97bc16e49ece53b7c221cda4309c6e92deb1ed485ba31fff728e4bd1195ee16421e1cf25f5da43225e52939ddac2bca9678ba + pure-rand: "npm:^6.1.0" + checksum: 10c0/16fcff3c80321ee765e23c3aebd0f6427f175c9c6c1753104ec658970162365dc2d56bda046d815e8f2e90634c07ba7d6f0bcfd327fbd576d98c56a18a9765ed languageName: node linkType: hard @@ -7686,16 +7040,16 @@ __metadata: languageName: node linkType: hard -"fast-glob@npm:^3.2.9": - version: 3.3.2 - resolution: "fast-glob@npm:3.3.2" +"fast-glob@npm:^3.2.9, fast-glob@npm:^3.3.2": + version: 3.3.3 + resolution: "fast-glob@npm:3.3.3" dependencies: "@nodelib/fs.stat": "npm:^2.0.2" "@nodelib/fs.walk": "npm:^1.2.3" glob-parent: "npm:^5.1.2" merge2: "npm:^1.3.0" - micromatch: "npm:^4.0.4" - checksum: 10c0/42baad7b9cd40b63e42039132bde27ca2cb3a4950d0a0f9abe4639ea1aa9d3e3b40f98b1fe31cbc0cc17b664c9ea7447d911a152fa34ec5b72977b125a6fc845 + micromatch: "npm:^4.0.8" + checksum: 10c0/f6aaa141d0d3384cf73cbcdfc52f475ed293f6d5b65bfc5def368b09163a9f7e5ec2b3014d80f733c405f58e470ee0cc451c2937685045cddcdeaa24199c43fe languageName: node linkType: hard @@ -7804,12 +7158,12 @@ __metadata: languageName: node linkType: hard -"fill-range@npm:^7.0.1": - version: 7.0.1 - resolution: "fill-range@npm:7.0.1" +"fill-range@npm:^7.1.1": + version: 7.1.1 + resolution: "fill-range@npm:7.1.1" dependencies: to-regex-range: "npm:^5.0.1" - checksum: 10c0/7cdad7d426ffbaadf45aeb5d15ec675bbd77f7597ad5399e3d2766987ed20bda24d5fac64b3ee79d93276f5865608bb22344a26b9b1ae6c4d00bd94bf611623f + checksum: 10c0/b75b691bbe065472f38824f694c2f7449d7f5004aa950426a2c28f0306c60db9b880c0b0e4ed819997ffb882d1da02cfcfc819bddc94d71627f5269682edf018 languageName: node linkType: hard @@ -7989,6 +7343,13 @@ __metadata: languageName: node linkType: hard +"get-east-asian-width@npm:^1.0.0": + version: 1.3.0 + resolution: "get-east-asian-width@npm:1.3.0" + checksum: 10c0/1a049ba697e0f9a4d5514c4623781c5246982bdb61082da6b5ae6c33d838e52ce6726407df285cdbb27ec1908b333cf2820989bd3e986e37bb20979437fdf34b + languageName: node + linkType: hard + "get-jwks@npm:^9.0.1": version: 9.0.1 resolution: "get-jwks@npm:9.0.1" @@ -8110,6 +7471,20 @@ __metadata: languageName: node linkType: hard +"globby@npm:14.0.2": + version: 14.0.2 + resolution: "globby@npm:14.0.2" + dependencies: + "@sindresorhus/merge-streams": "npm:^2.1.0" + fast-glob: "npm:^3.3.2" + ignore: "npm:^5.2.4" + path-type: "npm:^5.0.0" + slash: "npm:^5.1.0" + unicorn-magic: "npm:^0.1.0" + checksum: 10c0/3f771cd683b8794db1e7ebc8b6b888d43496d93a82aad4e9d974620f578581210b6c5a6e75ea29573ed16a1345222fab6e9b877a8d1ed56eeb147e09f69c6f78 + languageName: node + linkType: hard + "globby@npm:^11.1.0": version: 11.1.0 resolution: "globby@npm:11.1.0" @@ -8281,6 +7656,15 @@ __metadata: languageName: node linkType: hard +"hosted-git-info@npm:^8.0.0": + version: 8.0.2 + resolution: "hosted-git-info@npm:8.0.2" + dependencies: + lru-cache: "npm:^10.0.1" + checksum: 10c0/e64f6c1b6db625869934b35c4959aacc365799d9cb1856e0224b5557ee5ecfe224bb8aa850479179a8f3968063ea0f92b8fbb67fe009d46859431dcde7fdc36d + languageName: node + linkType: hard + "html-escaper@npm:^2.0.0": version: 2.0.2 resolution: "html-escaper@npm:2.0.2" @@ -8355,14 +7739,7 @@ __metadata: languageName: node linkType: hard -"http-link-header@npm:^1.0.2": - version: 1.1.1 - resolution: "http-link-header@npm:1.1.1" - checksum: 10c0/a97b679e01e9f0860c823917c2c6637e3f0c06aa6cc0de4da98e1a433690e19b21323595889aa9347618134600bc7e2b0ff3cbeea7e4cea2f8cdae65dd9cc7f1 - languageName: node - linkType: hard - -"http-link-header@npm:^1.1.1": +"http-link-header@npm:^1.0.2, http-link-header@npm:^1.1.1": version: 1.1.3 resolution: "http-link-header@npm:1.1.3" checksum: 10c0/56698a9d3aee4d5319d1cdfe62ef5d7179f179ec1e6432d23c9e6a0c896be642ba47a4985a45419cff91008032aef920aca9df94ff9e763e646c83bf54b7243d @@ -8440,10 +7817,10 @@ __metadata: languageName: node linkType: hard -"ignore@npm:^5.2.0": - version: 5.3.0 - resolution: "ignore@npm:5.3.0" - checksum: 10c0/dc06bea5c23aae65d0725a957a0638b57e235ae4568dda51ca142053ed2c352de7e3bc93a69b2b32ac31966a1952e9a93c5ef2e2ab7c6b06aef9808f6b55b571 +"ignore@npm:^5.2.0, ignore@npm:^5.2.4": + version: 5.3.2 + resolution: "ignore@npm:5.3.2" + checksum: 10c0/f9f652c957983634ded1e7f02da3b559a0d4cc210fca3792cb67f1b153623c9c42efdc1c4121af171e295444459fc4a9201101fb041b1104a3c000bccb188337 languageName: node linkType: hard @@ -8454,13 +7831,13 @@ __metadata: languageName: node linkType: hard -"import-fresh@npm:^3.0.0, import-fresh@npm:^3.2.1": - version: 3.3.0 - resolution: "import-fresh@npm:3.3.0" +"import-fresh@npm:^3.0.0, import-fresh@npm:^3.2.1, import-fresh@npm:^3.3.0": + version: 3.3.1 + resolution: "import-fresh@npm:3.3.1" dependencies: parent-module: "npm:^1.0.0" resolve-from: "npm:^4.0.0" - checksum: 10c0/7f882953aa6b740d1f0e384d0547158bc86efbf2eea0f1483b8900a6f65c5a5123c2cf09b0d542cc419d0b98a759ecaeb394237e97ea427f2da221dc3cd80cc3 + checksum: 10c0/bf8cc494872fef783249709385ae883b447e3eb09db0ebd15dcead7d9afe7224dad7bd7591c6b73b0b19b3c0f9640eb8ee884f01cfaf2887ab995b0b36a0cbec languageName: node linkType: hard @@ -8618,6 +7995,13 @@ __metadata: languageName: node linkType: hard +"is-interactive@npm:^2.0.0": + version: 2.0.0 + resolution: "is-interactive@npm:2.0.0" + checksum: 10c0/801c8f6064f85199dc6bf99b5dd98db3282e930c3bc197b32f2c5b89313bb578a07d1b8a01365c4348c2927229234f3681eb861b9c2c92bee72ff397390fa600 + languageName: node + linkType: hard + "is-lambda@npm:^1.0.1": version: 1.0.1 resolution: "is-lambda@npm:1.0.1" @@ -8669,6 +8053,20 @@ __metadata: languageName: node linkType: hard +"is-unicode-supported@npm:^1.3.0": + version: 1.3.0 + resolution: "is-unicode-supported@npm:1.3.0" + checksum: 10c0/b8674ea95d869f6faabddc6a484767207058b91aea0250803cbf1221345cb0c56f466d4ecea375dc77f6633d248d33c47bd296fb8f4cdba0b4edba8917e83d8a + languageName: node + linkType: hard + +"is-unicode-supported@npm:^2.0.0": + version: 2.1.0 + resolution: "is-unicode-supported@npm:2.1.0" + checksum: 10c0/a0f53e9a7c1fdbcf2d2ef6e40d4736fdffff1c9f8944c75e15425118ff3610172c87bf7bc6c34d3903b04be59790bb2212ddbe21ee65b5a97030fc50370545a5 + languageName: node + linkType: hard + "isexe@npm:^2.0.0": version: 2.0.0 resolution: "isexe@npm:2.0.0" @@ -9233,21 +8631,14 @@ __metadata: languageName: node linkType: hard -"jose@npm:^4.15.2, jose@npm:^4.5.1, jose@npm:^4.7.0": +"jose@npm:^4.15.2, jose@npm:^4.7.0": version: 4.15.4 resolution: "jose@npm:4.15.4" checksum: 10c0/ce8b29f84d6172a566b12b599dafa82f3bef0f16278bb76d562490ac1516fcc14017b05a39d20ffad25ed504f4996d4af4c9d3e0273d95b2d5559bf6d1112bc0 languageName: node linkType: hard -"jose@npm:^5.1.3": - version: 5.2.0 - resolution: "jose@npm:5.2.0" - checksum: 10c0/a5631e9a7148fb56c8feca8e00d1f03de6fa6e18e494fe62428b53bed525a9dba554ff3e860bdfc4aee60574194691015ba991557874ae826509c4c19dba4fea - languageName: node - linkType: hard - -"jose@npm:^5.2.2": +"jose@npm:^5.1.3, jose@npm:^5.2.2": version: 5.2.2 resolution: "jose@npm:5.2.2" checksum: 10c0/f6a439d41fa36e1c0a3a1554c1955ba656f37a7fc5e353bdcbda144cc9210fe513cc1b0480ec5bd5de50c0ec23714c82d82ae89f1b906656b683aa35ae075b68 @@ -9273,7 +8664,7 @@ __metadata: languageName: node linkType: hard -"js-yaml@npm:^4.1.0": +"js-yaml@npm:^4.0.0, js-yaml@npm:^4.1.0": version: 4.1.0 resolution: "js-yaml@npm:4.1.0" dependencies: @@ -9339,6 +8730,13 @@ __metadata: languageName: node linkType: hard +"jsonc-parser@npm:3.3.1": + version: 3.3.1 + resolution: "jsonc-parser@npm:3.3.1" + checksum: 10c0/269c3ae0a0e4f907a914bf334306c384aabb9929bd8c99f909275ebd5c2d3bc70b9bcd119ad794f339dec9f24b6a4ee9cd5a8ab2e6435e730ad4075388fc2ab6 + languageName: node + linkType: hard + "jsonfile@npm:^4.0.0": version: 4.0.0 resolution: "jsonfile@npm:4.0.0" @@ -9391,25 +8789,7 @@ __metadata: languageName: node linkType: hard -"jsonld-streaming-parser@npm:^3.0.1": - version: 3.3.0 - resolution: "jsonld-streaming-parser@npm:3.3.0" - dependencies: - "@bergos/jsonparse": "npm:^1.4.0" - "@rdfjs/types": "npm:*" - "@types/http-link-header": "npm:^1.0.1" - "@types/readable-stream": "npm:^2.3.13" - buffer: "npm:^6.0.3" - canonicalize: "npm:^1.0.1" - http-link-header: "npm:^1.0.2" - jsonld-context-parser: "npm:^2.4.0" - rdf-data-factory: "npm:^1.1.0" - readable-stream: "npm:^4.0.0" - checksum: 10c0/da92dae7a25dff8622e9c42f071c145b55771ea47f53b6d0221f0051edca3948c4cb6f74ffe7c1d161c7ea471d14c384784f1925735e8c31232d8244f9e4f8d0 - languageName: node - linkType: hard - -"jsonld-streaming-parser@npm:^3.3.0": +"jsonld-streaming-parser@npm:^3.0.1, jsonld-streaming-parser@npm:^3.3.0": version: 3.4.0 resolution: "jsonld-streaming-parser@npm:3.4.0" dependencies: @@ -9440,7 +8820,7 @@ __metadata: languageName: node linkType: hard -"jsonld@npm:^8.0.0, jsonld@npm:^8.3.1, jsonld@npm:^8.3.3": +"jsonld@npm:^8.0.0, jsonld@npm:^8.3.1, jsonld@npm:^8.3.3, jsonld@npm:^8.x.x": version: 8.3.3 resolution: "jsonld@npm:8.3.3" dependencies: @@ -9452,18 +8832,6 @@ __metadata: languageName: node linkType: hard -"jsonld@npm:^8.x.x": - version: 8.3.2 - resolution: "jsonld@npm:8.3.2" - dependencies: - "@digitalbazaar/http-client": "npm:^3.4.1" - canonicalize: "npm:^1.0.1" - lru-cache: "npm:^6.0.0" - rdf-canonize: "npm:^3.4.0" - checksum: 10c0/9c3596ffcde7e7ebeeaaa37f76b31f45f459552f4210adb16e9fc66781a94c4edf3d181cd6e5f758bd340c347f423c7c85a17387d60ef9d57a49a419cec6927e - languageName: node - linkType: hard - "jsonparse@npm:^1.2.0": version: 1.3.1 resolution: "jsonparse@npm:1.3.1" @@ -9712,6 +9080,16 @@ __metadata: languageName: node linkType: hard +"log-symbols@npm:^6.0.0": + version: 6.0.0 + resolution: "log-symbols@npm:6.0.0" + dependencies: + chalk: "npm:^5.3.0" + is-unicode-supported: "npm:^1.3.0" + checksum: 10c0/36636cacedba8f067d2deb4aad44e91a89d9efb3ead27e1846e7b82c9a10ea2e3a7bd6ce28a7ca616bebc60954ff25c67b0f92d20a6a746bb3cc52c3701891f6 + languageName: node + linkType: hard + "log4js@npm:^6.7.0": version: 6.9.1 resolution: "log4js@npm:6.9.1" @@ -9746,20 +9124,13 @@ __metadata: languageName: node linkType: hard -"lru-cache@npm:^10.0.0": +"lru-cache@npm:^10.0.0, lru-cache@npm:^10.0.1, lru-cache@npm:^9.1.1 || ^10.0.0": version: 10.1.0 resolution: "lru-cache@npm:10.1.0" checksum: 10c0/778bc8b2626daccd75f24c4b4d10632496e21ba064b126f526c626fbdbc5b28c472013fccd45d7646b9e1ef052444824854aed617b59cd570d01a8b7d651fc1e languageName: node linkType: hard -"lru-cache@npm:^10.0.1, lru-cache@npm:^9.1.1 || ^10.0.0": - version: 10.0.3 - resolution: "lru-cache@npm:10.0.3" - checksum: 10c0/e1745db7682df7ea890aced922975528e9fd85897459e003ff3b71d7f69656792ddd9c50e852b7c461000b9c08a9370b110bfbe6e44de3d81f43c197eaa652b1 - languageName: node - linkType: hard - "lru-cache@npm:^5.1.1": version: 5.1.1 resolution: "lru-cache@npm:5.1.1" @@ -9905,13 +9276,13 @@ __metadata: languageName: node linkType: hard -"micromatch@npm:^4.0.4": - version: 4.0.5 - resolution: "micromatch@npm:4.0.5" +"micromatch@npm:^4.0.4, micromatch@npm:^4.0.8": + version: 4.0.8 + resolution: "micromatch@npm:4.0.8" dependencies: - braces: "npm:^3.0.2" + braces: "npm:^3.0.3" picomatch: "npm:^2.3.1" - checksum: 10c0/3d6505b20f9fa804af5d8c596cb1c5e475b9b0cd05f652c5b56141cf941bd72adaeb7a436fda344235cef93a7f29b7472efc779fcdb83b478eab0867b95cdeff + checksum: 10c0/166fa6eb926b9553f32ef81f5f531d27b4ce7da60e5baf8c021d043b27a388fb95e46a8038d5045877881e673f8134122b59624d5cecbd16eb50a42e7a6b5ca8 languageName: node linkType: hard @@ -9938,6 +9309,13 @@ __metadata: languageName: node linkType: hard +"mimic-function@npm:^5.0.0": + version: 5.0.1 + resolution: "mimic-function@npm:5.0.1" + checksum: 10c0/f3d9464dd1816ecf6bdf2aec6ba32c0728022039d992f178237d8e289b48764fee4131319e72eedd4f7f094e22ded0af836c3187a7edc4595d28dd74368fd81d + languageName: node + linkType: hard + "mimic-response@npm:^3.1.0": version: 3.1.0 resolution: "mimic-response@npm:3.1.0" @@ -9973,6 +9351,15 @@ __metadata: languageName: node linkType: hard +"minimatch@npm:9.0.5, minimatch@npm:^9.0.1": + version: 9.0.5 + resolution: "minimatch@npm:9.0.5" + dependencies: + brace-expansion: "npm:^2.0.1" + checksum: 10c0/de96cf5e35bdf0eab3e2c853522f98ffbe9a36c37797778d2665231ec1f20a9447a7e567cb640901f89e4daaa95ae5d70c65a9e8aa2bb0019b6facbc3c0575ed + languageName: node + linkType: hard + "minimatch@npm:^3.0.4, minimatch@npm:^3.0.5, minimatch@npm:^3.1.1, minimatch@npm:^3.1.2": version: 3.1.2 resolution: "minimatch@npm:3.1.2" @@ -9991,15 +9378,6 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:^9.0.1": - version: 9.0.3 - resolution: "minimatch@npm:9.0.3" - dependencies: - brace-expansion: "npm:^2.0.1" - checksum: 10c0/85f407dcd38ac3e180f425e86553911d101455ca3ad5544d6a7cec16286657e4f8a9aa6695803025c55e31e35a91a2252b5dc8e7d527211278b8b65b4dbd5eac - languageName: node - linkType: hard - "minimist-options@npm:4.1.0": version: 4.1.0 resolution: "minimist-options@npm:4.1.0" @@ -10135,17 +9513,6 @@ __metadata: languageName: node linkType: hard -"n3@npm:^1.20.4": - version: 1.23.1 - resolution: "n3@npm:1.23.1" - dependencies: - buffer: "npm:^6.0.3" - queue-microtask: "npm:^1.1.2" - readable-stream: "npm:^4.0.0" - checksum: 10c0/9f7f179b3f358f37c284dc65907f4f12b079657d5eaea0ed4e0f1dc248e54caf68e3c62439c6a1d3d1b3eec06edb18e20aa7d12da494b49ff112f5dd72b0d414 - languageName: node - linkType: hard - "nanoid@npm:^5.0.4": version: 5.0.4 resolution: "nanoid@npm:5.0.4" @@ -10256,10 +9623,10 @@ __metadata: languageName: node linkType: hard -"nodemailer@npm:^6.9.6": - version: 6.9.8 - resolution: "nodemailer@npm:6.9.8" - checksum: 10c0/9332587975240ac648e1295b1df15e339fcace3f7fab8af0382e7f2dd10e48296344dfa698d58f1667f220f7fe13c779d55d39144c9cd9ed6f5f559714183c75 +"nodemailer@npm:^6.9.9": + version: 6.10.0 + resolution: "nodemailer@npm:6.10.0" + checksum: 10c0/39fd35d65b021b94c968eeac82a66dd843021b6ba53c659d01b1dd4cda73b6a2f96e20facfe500efa4b8d3f1cb23df10245c6c86b2bde5f806691ed17ce87826 languageName: node linkType: hard @@ -10312,6 +9679,18 @@ __metadata: languageName: node linkType: hard +"npm-package-arg@npm:12.0.1": + version: 12.0.1 + resolution: "npm-package-arg@npm:12.0.1" + dependencies: + hosted-git-info: "npm:^8.0.0" + proc-log: "npm:^5.0.0" + semver: "npm:^7.3.5" + validate-npm-package-name: "npm:^6.0.0" + checksum: 10c0/e7cafb0952541858abe63dfa2fd7b45f1626e310c0b60d6266fafe20c1b5b76388913c3f39390820bee9eac035705639dc62adbcf14748536f867c4d06bbf209 + languageName: node + linkType: hard + "npm-run-path@npm:^4.0.1": version: 4.0.1 resolution: "npm-run-path@npm:4.0.1" @@ -10342,24 +9721,6 @@ __metadata: languageName: node linkType: hard -"odrl-evaluator@npm:^0.1.1": - version: 0.1.1 - resolution: "odrl-evaluator@npm:0.1.1" - dependencies: - "@rdfjs/types": "npm:^1.1.0" - "@types/n3": "npm:^1.16.3" - eyereasoner: "npm:^16.18.4" - n3: "npm:^1.20.4" - rdf-isomorphic: "npm:^1.3.1" - rdf-parse: "npm:^3.0.0" - rdf-store-stream: "npm:^2.0.1" - streamify-string: "npm:^1.0.1" - tmp: "npm:^0.2.3" - uuidv4: "npm:^6.2.13" - checksum: 10c0/5091b4402b2def1fb6bf8e7749d7f2b89a1817da59abfa6ae9f1a0db2fd3ae50222eebc578fba21f9d0f455cb5b515a067ac13584c43e3ba98f50454ccf30e16 - languageName: node - linkType: hard - "oidc-provider@npm:^8.4.0": version: 8.4.3 resolution: "oidc-provider@npm:8.4.3" @@ -10424,6 +9785,15 @@ __metadata: languageName: node linkType: hard +"onetime@npm:^7.0.0": + version: 7.0.0 + resolution: "onetime@npm:7.0.0" + dependencies: + mimic-function: "npm:^5.0.0" + checksum: 10c0/5cb9179d74b63f52a196a2e7037ba2b9a893245a5532d3f44360012005c9cadb60851d56716ebff18a6f47129dab7168022445df47c2aff3b276d92585ed1221 + languageName: node + linkType: hard + "only@npm:~0.0.2": version: 0.0.2 resolution: "only@npm:0.0.2" @@ -10445,6 +9815,23 @@ __metadata: languageName: node linkType: hard +"ora@npm:8.2.0": + version: 8.2.0 + resolution: "ora@npm:8.2.0" + dependencies: + chalk: "npm:^5.3.0" + cli-cursor: "npm:^5.0.0" + cli-spinners: "npm:^2.9.2" + is-interactive: "npm:^2.0.0" + is-unicode-supported: "npm:^2.0.0" + log-symbols: "npm:^6.0.0" + stdin-discarder: "npm:^0.2.2" + string-width: "npm:^7.2.0" + strip-ansi: "npm:^7.1.0" + checksum: 10c0/7d9291255db22e293ea164f520b6042a3e906576ab06c9cf408bf9ef5664ba0a9f3bd258baa4ada058cfcc2163ef9b6696d51237a866682ce33295349ba02c3a + languageName: node + linkType: hard + "p-cancelable@npm:^3.0.0": version: 3.0.0 resolution: "p-cancelable@npm:3.0.0" @@ -10584,6 +9971,13 @@ __metadata: languageName: node linkType: hard +"path-type@npm:^5.0.0": + version: 5.0.0 + resolution: "path-type@npm:5.0.0" + checksum: 10c0/e8f4b15111bf483900c75609e5e74e3fcb79f2ddb73e41470028fcd3e4b5162ec65da9907be077ee5012c18801ff7fffb35f9f37a077f3f81d85a0b7d6578efd + languageName: node + linkType: hard + "picocolors@npm:^1.0.0": version: 1.0.0 resolution: "picocolors@npm:1.0.0" @@ -10639,6 +10033,13 @@ __metadata: languageName: node linkType: hard +"proc-log@npm:^5.0.0": + version: 5.0.0 + resolution: "proc-log@npm:5.0.0" + checksum: 10c0/bbe5edb944b0ad63387a1d5b1911ae93e05ce8d0f60de1035b218cdcceedfe39dbd2c697853355b70f1a090f8f58fe90da487c85216bf9671f9499d1a897e9e3 + languageName: node + linkType: hard + "process@npm:^0.11.10": version: 0.11.10 resolution: "process@npm:0.11.10" @@ -10663,7 +10064,7 @@ __metadata: languageName: node linkType: hard -"prompts@npm:^2.0.1": +"prompts@npm:2.4.2, prompts@npm:^2.0.1": version: 2.4.2 resolution: "prompts@npm:2.4.2" dependencies: @@ -10708,10 +10109,10 @@ __metadata: languageName: node linkType: hard -"pure-rand@npm:^6.0.0": - version: 6.0.4 - resolution: "pure-rand@npm:6.0.4" - checksum: 10c0/0fe7b12f25b10ea5b804598a6f37e4bcf645d2be6d44fe963741f014bf0095bdb6ff525106d6da6e76addc8142358fd380f1a9b8c62ea4d5516bf26a96a37c95 +"pure-rand@npm:^6.0.0, pure-rand@npm:^6.1.0": + version: 6.1.0 + resolution: "pure-rand@npm:6.1.0" + checksum: 10c0/1abe217897bf74dcb3a0c9aba3555fe975023147b48db540aa2faf507aee91c03bf54f6aef0eb2bf59cc259a16d06b28eca37f0dc426d94f4692aeff02fb0e65 languageName: node linkType: hard @@ -10831,7 +10232,7 @@ __metadata: languageName: node linkType: hard -"rdf-isomorphic@npm:^1.3.0, rdf-isomorphic@npm:^1.3.1": +"rdf-isomorphic@npm:^1.3.0": version: 1.3.1 resolution: "rdf-isomorphic@npm:1.3.1" dependencies: @@ -10875,39 +10276,7 @@ __metadata: languageName: node linkType: hard -"rdf-parse@npm:^2.0.0, rdf-parse@npm:^2.1.1, rdf-parse@npm:^2.3.2": - version: 2.3.2 - resolution: "rdf-parse@npm:2.3.2" - dependencies: - "@comunica/actor-http-fetch": "npm:^2.0.1" - "@comunica/actor-http-proxy": "npm:^2.0.1" - "@comunica/actor-rdf-parse-html": "npm:^2.0.1" - "@comunica/actor-rdf-parse-html-microdata": "npm:^2.0.1" - "@comunica/actor-rdf-parse-html-rdfa": "npm:^2.0.1" - "@comunica/actor-rdf-parse-html-script": "npm:^2.0.1" - "@comunica/actor-rdf-parse-jsonld": "npm:^2.0.1" - "@comunica/actor-rdf-parse-n3": "npm:^2.0.1" - "@comunica/actor-rdf-parse-rdfxml": "npm:^2.0.1" - "@comunica/actor-rdf-parse-shaclc": "npm:^2.6.2" - "@comunica/actor-rdf-parse-xml-rdfa": "npm:^2.0.1" - "@comunica/bus-http": "npm:^2.0.1" - "@comunica/bus-init": "npm:^2.0.1" - "@comunica/bus-rdf-parse": "npm:^2.0.1" - "@comunica/bus-rdf-parse-html": "npm:^2.0.1" - "@comunica/config-query-sparql": "npm:^2.0.1" - "@comunica/core": "npm:^2.0.1" - "@comunica/mediator-combine-pipeline": "npm:^2.0.1" - "@comunica/mediator-combine-union": "npm:^2.0.1" - "@comunica/mediator-number": "npm:^2.0.1" - "@comunica/mediator-race": "npm:^2.0.1" - "@rdfjs/types": "npm:*" - readable-stream: "npm:^4.3.0" - stream-to-string: "npm:^1.2.0" - checksum: 10c0/03793a5c02ec6f71f7cebbde47aed4480d7a9e51cb123ff8c9b9e1da1db3d4115a60d814b6fa305919f18d6bd89583d9378a3c9039b26b63bb2f15e6d3b5940e - languageName: node - linkType: hard - -"rdf-parse@npm:^2.3.3": +"rdf-parse@npm:^2.0.0, rdf-parse@npm:^2.1.1, rdf-parse@npm:^2.3.2, rdf-parse@npm:^2.3.3": version: 2.3.3 resolution: "rdf-parse@npm:2.3.3" dependencies: @@ -10939,38 +10308,6 @@ __metadata: languageName: node linkType: hard -"rdf-parse@npm:^3.0.0": - version: 3.0.0 - resolution: "rdf-parse@npm:3.0.0" - dependencies: - "@comunica/actor-http-fetch": "npm:^2.0.1" - "@comunica/actor-http-proxy": "npm:^2.0.1" - "@comunica/actor-rdf-parse-html": "npm:^2.0.1" - "@comunica/actor-rdf-parse-html-microdata": "npm:^2.0.1" - "@comunica/actor-rdf-parse-html-rdfa": "npm:^2.0.1" - "@comunica/actor-rdf-parse-html-script": "npm:^2.0.1" - "@comunica/actor-rdf-parse-jsonld": "npm:^2.0.1" - "@comunica/actor-rdf-parse-n3": "npm:^2.0.1" - "@comunica/actor-rdf-parse-rdfxml": "npm:^2.0.1" - "@comunica/actor-rdf-parse-shaclc": "npm:^2.6.2" - "@comunica/actor-rdf-parse-xml-rdfa": "npm:^2.0.1" - "@comunica/bus-http": "npm:^2.0.1" - "@comunica/bus-init": "npm:^2.0.1" - "@comunica/bus-rdf-parse": "npm:^2.0.1" - "@comunica/bus-rdf-parse-html": "npm:^2.0.1" - "@comunica/config-query-sparql": "npm:^2.0.1" - "@comunica/core": "npm:^2.0.1" - "@comunica/mediator-combine-pipeline": "npm:^2.0.1" - "@comunica/mediator-combine-union": "npm:^2.0.1" - "@comunica/mediator-number": "npm:^2.0.1" - "@comunica/mediator-race": "npm:^2.0.1" - "@rdfjs/types": "npm:*" - readable-stream: "npm:^4.3.0" - stream-to-string: "npm:^1.2.0" - checksum: 10c0/c85c1caed0772e69a3862ca6875c986ad44ab3d46744c6781e46395333f751b4f505ba490a983771d2eeddf791cae5fefb404fd1f3ff414a493962c009016335 - languageName: node - linkType: hard - "rdf-quad@npm:^1.5.0": version: 1.5.0 resolution: "rdf-quad@npm:1.5.0" @@ -11003,17 +10340,7 @@ __metadata: languageName: node linkType: hard -"rdf-store-stream@npm:^2.0.0": - version: 2.0.0 - resolution: "rdf-store-stream@npm:2.0.0" - dependencies: - "@rdfjs/types": "npm:*" - rdf-stores: "npm:^1.0.0" - checksum: 10c0/1d5527e6c989c10d2af058d8356df31fee8f72a0acc1e9ae41a26c15a225a7bdc0bf3446e624f7935236d69feace42c5fddecf674133a60027e58698a9bbd2d6 - languageName: node - linkType: hard - -"rdf-store-stream@npm:^2.0.1": +"rdf-store-stream@npm:^2.0.0, rdf-store-stream@npm:^2.0.1": version: 2.0.1 resolution: "rdf-store-stream@npm:2.0.1" dependencies: @@ -11166,6 +10493,16 @@ __metadata: languageName: node linkType: hard +"read-yaml-file@npm:2.1.0": + version: 2.1.0 + resolution: "read-yaml-file@npm:2.1.0" + dependencies: + js-yaml: "npm:^4.0.0" + strip-bom: "npm:^4.0.0" + checksum: 10c0/bad0673abe78a5bde7c53b22ee7cdd0dfe49ab7338a4ad8618be9fcd2fd25ab9ae60d395907fddbfb2e7fbbf494867aeec78295c2396bec2669fd7087d2734c1 + languageName: node + linkType: hard + "readable-stream-node-to-web@npm:^1.0.1": version: 1.0.1 resolution: "readable-stream-node-to-web@npm:1.0.1" @@ -11184,20 +10521,7 @@ __metadata: languageName: node linkType: hard -"readable-stream@npm:^4.0.0, readable-stream@npm:^4.1.0, readable-stream@npm:^4.3.0, readable-stream@npm:^4.4.2": - version: 4.4.2 - resolution: "readable-stream@npm:4.4.2" - dependencies: - abort-controller: "npm:^3.0.0" - buffer: "npm:^6.0.3" - events: "npm:^3.3.0" - process: "npm:^0.11.10" - string_decoder: "npm:^1.3.0" - checksum: 10c0/cf7cc8daa2b57872d120945a20a1458c13dcb6c6f352505421115827b18ac4df0e483ac1fe195cb1f5cd226e1073fc55b92b569269d8299e8530840bcdbba40c - languageName: node - linkType: hard - -"readable-stream@npm:^4.5.1": +"readable-stream@npm:^4.0.0, readable-stream@npm:^4.1.0, readable-stream@npm:^4.3.0, readable-stream@npm:^4.4.2, readable-stream@npm:^4.5.1": version: 4.5.2 resolution: "readable-stream@npm:4.5.2" dependencies: @@ -11363,6 +10687,16 @@ __metadata: languageName: node linkType: hard +"restore-cursor@npm:^5.0.0": + version: 5.1.0 + resolution: "restore-cursor@npm:5.1.0" + dependencies: + onetime: "npm:^7.0.0" + signal-exit: "npm:^4.1.0" + checksum: 10c0/c2ba89131eea791d1b25205bdfdc86699767e2b88dee2a590b1a6caa51737deac8bad0260a5ded2f7c074b7db2f3a626bcf1fcf3cdf35974cbeea5e2e6764f60 + languageName: node + linkType: hard + "retry@npm:^0.12.0": version: 0.12.0 resolution: "retry@npm:0.12.0" @@ -11461,23 +10795,21 @@ __metadata: languageName: node linkType: hard -"semver@npm:^6.3.0, semver@npm:^6.3.1": - version: 6.3.1 - resolution: "semver@npm:6.3.1" +"semver@npm:7.7.0, semver@npm:^7.3.2, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.5.3, semver@npm:^7.5.4": + version: 7.7.0 + resolution: "semver@npm:7.7.0" bin: semver: bin/semver.js - checksum: 10c0/e3d79b609071caa78bcb6ce2ad81c7966a46a7431d9d58b8800cfa9cb6a63699b3899a0e4bcce36167a284578212d9ae6942b6929ba4aa5015c079a67751d42d + checksum: 10c0/bcd1c03209b4be7d8ca86c976a0410beba7d4ec1d49d846a4be154b958db1ff5eaee50760c1d4f4070b19dee3236b8672d3e09642c53ea23740398bba2538a2d languageName: node linkType: hard -"semver@npm:^7.3.2, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.5.3, semver@npm:^7.5.4": - version: 7.5.4 - resolution: "semver@npm:7.5.4" - dependencies: - lru-cache: "npm:^6.0.0" +"semver@npm:^6.3.0, semver@npm:^6.3.1": + version: 6.3.1 + resolution: "semver@npm:6.3.1" bin: semver: bin/semver.js - checksum: 10c0/5160b06975a38b11c1ab55950cb5b8a23db78df88275d3d8a42ccf1f29e55112ac995b3a26a522c36e3b5f76b0445f1eef70d696b8c7862a2b4303d7b0e7609e + checksum: 10c0/e3d79b609071caa78bcb6ce2ad81c7966a46a7431d9d58b8800cfa9cb6a63699b3899a0e4bcce36167a284578212d9ae6942b6929ba4aa5015c079a67751d42d languageName: node linkType: hard @@ -11580,7 +10912,7 @@ __metadata: languageName: node linkType: hard -"signal-exit@npm:^4.0.1": +"signal-exit@npm:^4.0.1, signal-exit@npm:^4.1.0": version: 4.1.0 resolution: "signal-exit@npm:4.1.0" checksum: 10c0/41602dce540e46d599edba9d9860193398d135f7ff72cab629db5171516cfae628d21e7bfccde1bbfdf11c48726bc2a6d1a8fb8701125852fbfda7cf19c6aa83 @@ -11610,6 +10942,13 @@ __metadata: languageName: node linkType: hard +"slash@npm:^5.1.0": + version: 5.1.0 + resolution: "slash@npm:5.1.0" + checksum: 10c0/eb48b815caf0bdc390d0519d41b9e0556a14380f6799c72ba35caf03544d501d18befdeeef074bc9c052acf69654bc9e0d79d7f1de0866284137a40805299eb3 + languageName: node + linkType: hard + "smart-buffer@npm:^4.2.0": version: 4.2.0 resolution: "smart-buffer@npm:4.2.0" @@ -11662,7 +11001,7 @@ __metadata: languageName: node linkType: hard -"sparqlalgebrajs@npm:^4.0.0, sparqlalgebrajs@npm:^4.3.0": +"sparqlalgebrajs@npm:^4.0.0, sparqlalgebrajs@npm:^4.2.0, sparqlalgebrajs@npm:^4.3.0": version: 4.3.2 resolution: "sparqlalgebrajs@npm:4.3.2" dependencies: @@ -11681,25 +11020,6 @@ __metadata: languageName: node linkType: hard -"sparqlalgebrajs@npm:^4.2.0": - version: 4.3.1 - resolution: "sparqlalgebrajs@npm:4.3.1" - dependencies: - "@rdfjs/types": "npm:*" - "@types/sparqljs": "npm:^3.1.3" - fast-deep-equal: "npm:^3.1.3" - minimist: "npm:^1.2.6" - rdf-data-factory: "npm:^1.1.0" - rdf-isomorphic: "npm:^1.3.0" - rdf-string: "npm:^1.6.0" - rdf-terms: "npm:^1.10.0" - sparqljs: "npm:^3.7.1" - bin: - sparqlalgebrajs: bin/sparqlalgebrajs.js - checksum: 10c0/5510b3cb2c8263a5a9e44526e6388331888824d28faf5f2b22173475b2520766de71ce48b2d9c9cf85e808db85a45e966662f68f1acc1327ff4c149fda322f0f - languageName: node - linkType: hard - "sparqljs@npm:^3.1.2, sparqljs@npm:^3.7.1": version: 3.7.1 resolution: "sparqljs@npm:3.7.1" @@ -11851,6 +11171,13 @@ __metadata: languageName: node linkType: hard +"stdin-discarder@npm:^0.2.2": + version: 0.2.2 + resolution: "stdin-discarder@npm:0.2.2" + checksum: 10c0/c78375e82e956d7a64be6e63c809c7f058f5303efcaf62ea48350af072bacdb99c06cba39209b45a071c1acbd49116af30df1df9abb448df78a6005b72f10537 + languageName: node + linkType: hard + "stream-to-string@npm:^1.1.0, stream-to-string@npm:^1.2.0": version: 1.2.1 resolution: "stream-to-string@npm:1.2.1" @@ -11917,6 +11244,17 @@ __metadata: languageName: node linkType: hard +"string-width@npm:^7.2.0": + version: 7.2.0 + resolution: "string-width@npm:7.2.0" + dependencies: + emoji-regex: "npm:^10.3.0" + get-east-asian-width: "npm:^1.0.0" + strip-ansi: "npm:^7.1.0" + checksum: 10c0/eb0430dd43f3199c7a46dcbf7a0b34539c76fe3aa62763d0b0655acdcbdf360b3f66f3d58ca25ba0205f42ea3491fa00f09426d3b7d3040e506878fc7664c9b9 + languageName: node + linkType: hard + "string_decoder@npm:^1.1.1, string_decoder@npm:^1.3.0": version: 1.3.0 resolution: "string_decoder@npm:1.3.0" @@ -11935,7 +11273,7 @@ __metadata: languageName: node linkType: hard -"strip-ansi@npm:^7.0.1": +"strip-ansi@npm:^7.0.1, strip-ansi@npm:^7.1.0": version: 7.1.0 resolution: "strip-ansi@npm:7.1.0" dependencies: @@ -12027,14 +11365,40 @@ __metadata: languageName: node linkType: hard -"swipl-wasm@npm:4.0.13": - version: 4.0.13 - resolution: "swipl-wasm@npm:4.0.13" - dependencies: - "@types/emscripten": "npm:^1.39.13" +"syncpack@npm:^13.0.2": + version: 13.0.2 + resolution: "syncpack@npm:13.0.2" + dependencies: + "@effect/schema": "npm:0.75.5" + chalk: "npm:5.4.1" + chalk-template: "npm:1.1.0" + commander: "npm:13.1.0" + cosmiconfig: "npm:9.0.0" + effect: "npm:3.12.7" + enquirer: "npm:2.4.1" + fast-check: "npm:3.23.2" + globby: "npm:14.0.2" + jsonc-parser: "npm:3.3.1" + minimatch: "npm:9.0.5" + npm-package-arg: "npm:12.0.1" + ora: "npm:8.2.0" + prompts: "npm:2.4.2" + read-yaml-file: "npm:2.1.0" + semver: "npm:7.7.0" + tightrope: "npm:0.2.0" + ts-toolbelt: "npm:9.6.0" bin: - swipl-generate: dist/bin/index.js - checksum: 10c0/ea61942ceb60883bddd213d029ce21bb7010860621b69dfd1e13209b17948c90bc3c422153244a8662f5eb89275135b3905be5274d827ce4f2effac6a383f038 + syncpack: dist/bin.js + syncpack-fix-mismatches: dist/bin-fix-mismatches/index.js + syncpack-format: dist/bin-format/index.js + syncpack-lint: dist/bin-lint/index.js + syncpack-lint-semver-ranges: dist/bin-lint-semver-ranges/index.js + syncpack-list: dist/bin-list/index.js + syncpack-list-mismatches: dist/bin-list-mismatches/index.js + syncpack-prompt: dist/bin-prompt/index.js + syncpack-set-semver-ranges: dist/bin-set-semver-ranges/index.js + syncpack-update: dist/bin-update/index.js + checksum: 10c0/3b7a248e7dfb56431843308ee8ad750f0f0c14b346770504ad5e356d574386e85608745e414411e574d084e1662d3cd7c608c86ee408a823f03b00b0be218667 languageName: node linkType: hard @@ -12100,6 +11464,13 @@ __metadata: languageName: node linkType: hard +"tightrope@npm:0.2.0": + version: 0.2.0 + resolution: "tightrope@npm:0.2.0" + checksum: 10c0/124e1f94bc6bbe8da60b22e236b12083379716a80d22f21856235107a53f2de34f0197a7b8d86c34327dcdea10ef9ffedeee360626f0ac3ec3693881dc71abca + languageName: node + linkType: hard + "tiny-case@npm:^1.0.3": version: 1.0.3 resolution: "tiny-case@npm:1.0.3" @@ -12116,13 +11487,6 @@ __metadata: languageName: node linkType: hard -"tmp@npm:^0.2.3": - version: 0.2.3 - resolution: "tmp@npm:0.2.3" - checksum: 10c0/3e809d9c2f46817475b452725c2aaa5d11985cf18d32a7a970ff25b568438e2c076c2e8609224feef3b7923fa9749b74428e3e634f6b8e520c534eef2fd24125 - languageName: node - linkType: hard - "tmpl@npm:1.0.5": version: 1.0.5 resolution: "tmpl@npm:1.0.5" @@ -12197,39 +11561,6 @@ __metadata: languageName: node linkType: hard -"ts-jest@npm:^29.1.1": - version: 29.1.1 - resolution: "ts-jest@npm:29.1.1" - dependencies: - bs-logger: "npm:0.x" - fast-json-stable-stringify: "npm:2.x" - jest-util: "npm:^29.0.0" - json5: "npm:^2.2.3" - lodash.memoize: "npm:4.x" - make-error: "npm:1.x" - semver: "npm:^7.5.3" - yargs-parser: "npm:^21.0.1" - peerDependencies: - "@babel/core": ">=7.0.0-beta.0 <8" - "@jest/types": ^29.0.0 - babel-jest: ^29.0.0 - jest: ^29.0.0 - typescript: ">=4.3 <6" - peerDependenciesMeta: - "@babel/core": - optional: true - "@jest/types": - optional: true - babel-jest: - optional: true - esbuild: - optional: true - bin: - ts-jest: cli.js - checksum: 10c0/6c45e0aeeff9cc54a64f931c43e1b99f4a1f0ddf44786cc128e7e55603ab7473c8c8f62fd83bd7e51bfe83e3c0c683132152efaeb844516bf7c923f4e92d157d - languageName: node - linkType: hard - "ts-jest@npm:^29.1.2": version: 29.1.2 resolution: "ts-jest@npm:29.1.2" @@ -12263,45 +11594,7 @@ __metadata: languageName: node linkType: hard -"ts-node@npm:^10.8.1, ts-node@npm:^10.9.1": - version: 10.9.1 - resolution: "ts-node@npm:10.9.1" - dependencies: - "@cspotcode/source-map-support": "npm:^0.8.0" - "@tsconfig/node10": "npm:^1.0.7" - "@tsconfig/node12": "npm:^1.0.7" - "@tsconfig/node14": "npm:^1.0.0" - "@tsconfig/node16": "npm:^1.0.2" - acorn: "npm:^8.4.1" - acorn-walk: "npm:^8.1.1" - arg: "npm:^4.1.0" - create-require: "npm:^1.1.0" - diff: "npm:^4.0.1" - make-error: "npm:^1.1.1" - v8-compile-cache-lib: "npm:^3.0.1" - yn: "npm:3.1.1" - peerDependencies: - "@swc/core": ">=1.2.50" - "@swc/wasm": ">=1.2.50" - "@types/node": "*" - typescript: ">=2.7" - peerDependenciesMeta: - "@swc/core": - optional: true - "@swc/wasm": - optional: true - bin: - ts-node: dist/bin.js - ts-node-cwd: dist/bin-cwd.js - ts-node-esm: dist/bin-esm.js - ts-node-script: dist/bin-script.js - ts-node-transpile-only: dist/bin-transpile.js - ts-script: dist/bin-script-deprecated.js - checksum: 10c0/95187932fb83f3901e22546bd2feeac7d2feb4f412f42ac3a595f049a23e8dcf70516dffb51866391228ea2dbcfaea039e250fb2bb334d48a86ab2b6aea0ae2d - languageName: node - linkType: hard - -"ts-node@npm:^10.9.2": +"ts-node@npm:^10.8.1, ts-node@npm:^10.9.2": version: 10.9.2 resolution: "ts-node@npm:10.9.2" dependencies: @@ -12339,6 +11632,13 @@ __metadata: languageName: node linkType: hard +"ts-toolbelt@npm:9.6.0": + version: 9.6.0 + resolution: "ts-toolbelt@npm:9.6.0" + checksum: 10c0/838f9a2f0fe881d5065257a23b402c41315b33ff987b73db3e2b39fcb70640c4c7220e1ef118ed5676763543724fdbf4eda7b0e2c17acb667ed1401336af9f8c + languageName: node + linkType: hard + "tslib@npm:^1.8.1": version: 1.14.1 resolution: "tslib@npm:1.14.1" @@ -12465,16 +11765,6 @@ __metadata: languageName: node linkType: hard -"typescript@npm:^5.2.2": - version: 5.3.2 - resolution: "typescript@npm:5.3.2" - bin: - tsc: bin/tsc - tsserver: bin/tsserver - checksum: 10c0/d7dbe1fbe19039e36a65468ea64b5d338c976550394ba576b7af9c68ed40c0bc5d12ecce390e4b94b287a09a71bd3229f19c2d5680611f35b7c53a3898791159 - languageName: node - linkType: hard - "typescript@npm:^5.3.3": version: 5.3.3 resolution: "typescript@npm:5.3.3" @@ -12495,16 +11785,6 @@ __metadata: languageName: node linkType: hard -"typescript@patch:typescript@npm%3A^5.2.2#optional!builtin": - version: 5.3.2 - resolution: "typescript@patch:typescript@npm%3A5.3.2#optional!builtin::version=5.3.2&hash=e012d7" - bin: - tsc: bin/tsc - tsserver: bin/tsserver - checksum: 10c0/73c8bad74e732d93211c9d77f28b03307e2f5fc6a0afc73f4b783261ab567686a16d6ae958bdaef383a00be1b0b8c8b6741dd6ca3d13af4963fa7e47456d49c7 - languageName: node - linkType: hard - "typescript@patch:typescript@npm%3A^5.3.3#optional!builtin": version: 5.3.3 resolution: "typescript@patch:typescript@npm%3A5.3.3#optional!builtin::version=5.3.3&hash=e012d7" @@ -12524,10 +11804,10 @@ __metadata: languageName: node linkType: hard -"undici-types@npm:~5.26.4": - version: 5.26.5 - resolution: "undici-types@npm:5.26.5" - checksum: 10c0/bb673d7876c2d411b6eb6c560e0c571eef4a01c1c19925175d16e3a30c4c428181fb8d7ae802a261f283e4166a0ac435e2f505743aa9e45d893f9a3df017b501 +"undici-types@npm:~6.19.2": + version: 6.19.8 + resolution: "undici-types@npm:6.19.8" + checksum: 10c0/078afa5990fba110f6824823ace86073b4638f1d5112ee26e790155f481f2a868cc3e0615505b6f4282bdf74a3d8caad715fd809e870c2bb0704e3ea6082f344 languageName: node linkType: hard @@ -12547,6 +11827,13 @@ __metadata: languageName: node linkType: hard +"unicorn-magic@npm:^0.1.0": + version: 0.1.0 + resolution: "unicorn-magic@npm:0.1.0" + checksum: 10c0/e4ed0de05b0a05e735c7d8a2930881e5efcfc3ec897204d5d33e7e6247f4c31eac92e383a15d9a6bccb7319b4271ee4bea946e211bf14951fec6ff2cbbb66a92 + languageName: node + linkType: hard + "unique-filename@npm:^3.0.0": version: 3.0.0 resolution: "unique-filename@npm:3.0.0" @@ -12637,15 +11924,6 @@ __metadata: languageName: node linkType: hard -"uuid@npm:8.3.2": - version: 8.3.2 - resolution: "uuid@npm:8.3.2" - bin: - uuid: dist/bin/uuid - checksum: 10c0/bcbb807a917d374a49f475fae2e87fdca7da5e5530820ef53f65ba1d12131bd81a92ecf259cc7ce317cbe0f289e7d79fdfebcef9bfa3087c8c8a2fa304c9be54 - languageName: node - linkType: hard - "uuid@npm:^9.0.0, uuid@npm:^9.0.1": version: 9.0.1 resolution: "uuid@npm:9.0.1" @@ -12655,16 +11933,6 @@ __metadata: languageName: node linkType: hard -"uuidv4@npm:^6.2.13": - version: 6.2.13 - resolution: "uuidv4@npm:6.2.13" - dependencies: - "@types/uuid": "npm:8.3.4" - uuid: "npm:8.3.2" - checksum: 10c0/33287c7c71e19c5a9fe0d936c0df648338965442c80b5b26e51c13ec9a9c524a72c718355e0cea8a9431a78680d72a41a236b87046ecc0a05f518c8e22df9e35 - languageName: node - linkType: hard - "v8-compile-cache-lib@npm:^3.0.1": version: 3.0.1 resolution: "v8-compile-cache-lib@npm:3.0.1" @@ -12700,6 +11968,13 @@ __metadata: languageName: node linkType: hard +"validate-npm-package-name@npm:^6.0.0": + version: 6.0.0 + resolution: "validate-npm-package-name@npm:6.0.0" + checksum: 10c0/35d1896d90a4f00291cfc17077b553910d45018b3562841acc6471731794eeebe39b409f678e8c1fee8ef1786e087cac8dea19abdd43649c30fd0b9c752afa2f + languageName: node + linkType: hard + "vary@npm:^1, vary@npm:^1.1.2": version: 1.1.2 resolution: "vary@npm:1.1.2"