Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Feat/notification gw handler and webhook #1080

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
82e9653
feat: Add storage modification types [CREATED, CHANGED, DELETED]
TamSzaGot Nov 29, 2021
461a936
fix: Renamed factory functions to follow naming 'create...', changed …
ixuz Nov 30, 2021
3bde06c
feat: An initial draft implementation of a NotificationWellKnown, Not…
ixuz Nov 30, 2021
b5935c8
fix: add notification configuration, modify notification subscription…
TamSzaGot Dec 3, 2021
ee2e548
Unsubscribe route
jaxoncreed Dec 8, 2021
f9ce953
Wired up new well-known system
jaxoncreed Dec 9, 2021
6bcd769
General Pod JWKS
jaxoncreed Dec 10, 2021
d6be7f3
Added Webhook http client
jaxoncreed Dec 10, 2021
b0758a5
Completed webhook auth
jaxoncreed Dec 10, 2021
6c52bc4
Removed trailing slash
jaxoncreed Dec 11, 2021
4b54e26
Webhook Http Client https update
jaxoncreed Dec 13, 2021
dde5d33
Added colon to fix protocol handling
jaxoncreed Dec 13, 2021
175bf5c
Merge pull request #1 from jaxoncreed/feat/notification-gw-handler-an…
TamSzaGot Dec 16, 2021
033fefa
Merge branch 'main' into feat/notification-gw-handler-and-webhook
ixuz Dec 26, 2021
6c55efa
fix jose version problems
TamSzaGot Jan 14, 2022
6bb5ce3
add Access-Control-Allow-Origin * to CORS configuration to be able to…
TamSzaGot Jan 14, 2022
0a11271
change the notification configration to memory storage instead of file
TamSzaGot Jan 14, 2022
c75a908
add oak configuration to be able to test in cloud deployment
TamSzaGot Jan 14, 2022
6f053ec
add a file and sparql storage version for notifications configuration
TamSzaGot Jan 19, 2022
fa5165f
revert last four commits as they doesn't belong here
TamSzaGot Jan 25, 2022
63bf398
remove un used files
TamSzaGot Jan 28, 2022
46ad60c
remove un used method
TamSzaGot Jan 28, 2022
0518f8f
change call interface to return promise
TamSzaGot Jan 28, 2022
111de40
align to updated HttpCient
TamSzaGot Jan 28, 2022
ea0d608
fix notification configuration
TamSzaGot Jan 28, 2022
d01c61f
fix and add unit tests
TamSzaGot Jan 28, 2022
bdc00a2
Merge branch 'solid:main' into feat/notification-gw-handler-and-webhook
TamSzaGot Jan 28, 2022
1526baf
fix lint errors
TamSzaGot Jan 28, 2022
2ea40d0
ci: Trigger CI
ixuz Jan 29, 2022
d8740af
Merge branch 'solid:main' into feat/notification-gw-handler-and-webhook
TamSzaGot Feb 7, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions config/file.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"files-scs:config/ldp/metadata-parser/default.json",
"files-scs:config/ldp/metadata-writer/default.json",
"files-scs:config/ldp/modes/default.json",
"files-scs:config/notification/default.json",
"files-scs:config/storage/backend/file.json",
"files-scs:config/storage/key-value/resource-store.json",
"files-scs:config/storage/middleware/default.json",
Expand Down
3 changes: 3 additions & 0 deletions config/http/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,6 @@ The factory used to create the actual server object.
Support for static files that should be found at a specific path.
* *default*: The default handler with a favicon and css for the IDP.
New entries can easily be added for new files.

## Well-Known
Supports the /.well-known/solid route
29 changes: 29 additions & 0 deletions config/http/handler/notification.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^2.0.0/components/context.jsonld",
"import": [
"files-scs:config/app/init/initializers/root.json",
"files-scs:config/http/well-known/default.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" },
{
"@type": "WaterfallHandler",
"handlers": [
{ "@id": "urn:solid-server:default:StaticAssetHandler" },
{ "@id": "urn:solid-server:default:SetupHandler" },
{ "@id": "urn:solid-server:default:WellKnownHandler" },
{ "@id": "urn:solid-server:default:NotificationHandler" },
{ "@id": "urn:solid-server:default:AuthResourceHttpHandler" },
{ "@id": "urn:solid-server:default:IdentityProviderHandler" },
{ "@id": "urn:solid-server:default:LdpHandler" }
]
}
]
}
]
}
25 changes: 25 additions & 0 deletions config/http/well-known/default.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^2.0.0/components/context.jsonld",
"@graph": [
{
"comment": "Selects the /.well-known/solid route.",
"@id": "urn:solid-server:default:WellKnownHandler",
"@type": "RouterHandler",
"args_baseUrl": { "@id": "urn:solid-server:default:variable:baseUrl" },
"args_targetExtractor": { "@id": "urn:solid-server:default:TargetExtractor" },
"args_allowedMethods": [ "GET" ],
"args_allowedPathNames": [ "^/\\.well-known/solid" ],
"args_handler": {
"comment": "The well-known route.",
"@type": "WellKnownHandler",
"wellKnownBuilder": {
"comment": "Aggregate Well-Known Builder",
"@type": "AggregateWellKnownBuilder",
"wellKnownBuilders": [
{ "@id": "urn:solid-server:default:NotificationWellKnownBuilder" }
]
}
}
}
]
}
7 changes: 7 additions & 0 deletions config/identity/handler/provider-factory/identity.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"args_storage": { "@id": "urn:solid-server:default:IdpKeyStorage" },
"args_errorHandler": { "@id": "urn:solid-server:default:ErrorHandler" },
"args_responseWriter": { "@id": "urn:solid-server:default:ResponseWriter" },
"args_jwksKeyGenerator": { "@id": "urn:solid-server:default:JwksKeyGenerator" },
"config": {
"claims": {
"openid": [ "webid", "client_id" ]
Expand Down Expand Up @@ -46,6 +47,12 @@
"RefreshToken": 86400
}
}
},
{
"comment": "Generates JWKS",
"@id": "urn:solid-server:default:JwksKeyGenerator",
"@type": "BasicJwksKeyGenerator",
"args_storage": { "@id": "urn:solid-server:default:IdpKeyStorage" }
}
]
}
40 changes: 40 additions & 0 deletions config/notification.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^2.0.0/components/context.jsonld",
"import": [
"files-scs:config/app/main/default.json",
"files-scs:config/app/init/initialize-prefilled-root.json",
"files-scs:config/app/setup/optional.json",
"files-scs:config/http/handler/notification.json",
"files-scs:config/http/middleware/websockets.json",
"files-scs:config/http/server-factory/websockets.json",
"files-scs:config/http/static/default.json",
"files-scs:config/identity/access/public.json",
"files-scs:config/identity/email/default.json",
"files-scs:config/identity/handler/default.json",
"files-scs:config/identity/ownership/token.json",
"files-scs:config/identity/pod/static.json",
"files-scs:config/identity/registration/enabled.json",
"files-scs:config/ldp/authentication/dpop-bearer.json",
"files-scs:config/ldp/authorization/webacl.json",
"files-scs:config/ldp/handler/default.json",
"files-scs:config/ldp/metadata-parser/default.json",
"files-scs:config/ldp/metadata-writer/default.json",
"files-scs:config/ldp/modes/default.json",
"files-scs:config/notification/default.json",
"files-scs:config/storage/backend/file.json",
"files-scs:config/storage/key-value/resource-store.json",
"files-scs:config/storage/middleware/default.json",
"files-scs:config/util/auxiliary/acl.json",
"files-scs:config/util/identifiers/suffix.json",
"files-scs:config/util/index/default.json",
"files-scs:config/util/logging/winston.json",
"files-scs:config/util/representation-conversion/default.json",
"files-scs:config/util/resource-locker/memory.json",
"files-scs:config/util/variables/default.json"
],
"@graph": [
{
"comment": "A single-pod server that uses notifications."
}
]
}
145 changes: 145 additions & 0 deletions config/notification/default.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
{
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^2.0.0/components/context.jsonld",
"@graph": [
{
"@id": "urn:solid-server:default:NotificationHandler",
"@type": "WaterfallHandler",
"handlers": [
{ "@id": "urn:solid-server:default:NotificationGatewayHandler" },
{ "@id": "urn:solid-server:default:NotificationSubscriptionHandler" },
{ "@id": "urn:solid-server:default:WebHookSubscription2021UnsubscribeHandler" },
{ "@id": "urn:solid-server:default:PodJwksHttpHandler" }
]
},
{
"comment": "The well known builder for all things pertaining to notifications",
"@id": "urn:solid-server:default:NotificationWellKnownBuilder",
"@type": "AggregateWellKnownBuilder",
"wellKnownBuilders": [
{ "@id": "urn:solid-server:default:BaseNotificationWellKnownBuilder" },
{ "@id": "urn:solid-server:default:WebHook2021AuthWellKnownBuilder" }
]
},
{
"comment": "The WellKnown Builder for base notifications",
"@id": "urn:solid-server:default:BaseNotificationWellKnownBuilder",
"@type": "NotificationWellKnownBuilder",
"args_baseUrl": { "@id": "urn:solid-server:default:variable:baseUrl" },
"args_notificationEndpointPath": "gateway"
},
{
"comment": "The WellKnown Builder for base notifications",
"@id": "urn:solid-server:default:WebHook2021AuthWellKnownBuilder",
"@type": "WebHook2021AuthWellKnownBuilder",
"args_baseUrl": { "@id": "urn:solid-server:default:variable:baseUrl" },
"args_jwksEndpointPath": ".well-known/podjwks"
},
{
"comment": "Http Handler for the Pod Jwks route",
"@id": "urn:solid-server:default:PodJwksHttpHandler",
"@type": "RouterHandler",
"args_baseUrl": { "@id": "urn:solid-server:default:variable:baseUrl" },
"args_targetExtractor": { "@id": "urn:solid-server:default:TargetExtractor" },
"args_allowedMethods": [ "GET" ],
"args_allowedPathNames": [ "^/.well-known/podjwks"],
"args_handler": {
"@type": "PodJwksHttpHandler",
"args_jwksKeyGenerator": { "@id": "urn:solid-server:default:JwksKeyGenerator" }
}
},
{
"comment": "Handles the notification gateway endpoint.",
"@id": "urn:solid-server:default:NotificationGatewayHandler",
"@type": "RouterHandler",
"args_baseUrl": { "@id": "urn:solid-server:default:variable:baseUrl" },
"args_targetExtractor": { "@id": "urn:solid-server:default:TargetExtractor" },
"args_allowedMethods": [ "POST" ],
"args_allowedPathNames": [ "^/gateway"],
"args_handler": { "@id": "urn:solid-server:default:NotificationGatewayParsingHandler" }
},
{
"comment": "Handles notification gateway input parsing.",
"@id": "urn:solid-server:default:NotificationGatewayParsingHandler",
"@type": "ParsingHttpHandler",
"args_requestParser": { "@id": "urn:solid-server:default:RequestParser" },
"args_metadataCollector": { "@id": "urn:solid-server:default:OperationMetadataCollector" },
"args_errorHandler": { "@id": "urn:solid-server:default:ErrorHandler" },
"args_responseWriter": { "@id": "urn:solid-server:default:ResponseWriter" },
"args_operationHandler": {
"comment": "Handles notification handler behaviour.",
"@id": "urn:solid-server:default:NotificationGatewayHttpHandler",
"@type": "NotificationGatewayHttpHandler",
"args_baseUrl": { "@id": "urn:solid-server:default:variable:baseUrl" },
"args_subscriptionPath": "subscription",
"args_subscriptionHandler": { "@id": "urn:solid-server:default:NotificationSubscriptionHttpHandler"}
}
},
{
"comment": "Handles the notification subscription endpoint.",
"@id": "urn:solid-server:default:NotificationSubscriptionHandler",
"@type": "RouterHandler",
"args_baseUrl": { "@id": "urn:solid-server:default:variable:baseUrl" },
"args_targetExtractor": { "@id": "urn:solid-server:default:TargetExtractor" },
"args_allowedMethods": [ "POST" ],
"args_allowedPathNames": [ "^/subscription" ],
"args_handler": { "@id": "urn:solid-server:default:NotificationSubscriptionParsingHandler" }
},
{
"comment": "Handles notification subscription input parsing.",
"@id": "urn:solid-server:default:NotificationSubscriptionParsingHandler",
"@type": "ParsingHttpHandler",
"args_requestParser": { "@id": "urn:solid-server:default:RequestParser" },
"args_metadataCollector": { "@id": "urn:solid-server:default:OperationMetadataCollector" },
"args_errorHandler": { "@id": "urn:solid-server:default:ErrorHandler" },
"args_responseWriter": { "@id": "urn:solid-server:default:ResponseWriter" },
"args_operationHandler": {
"comment": "Handles notification subscription behaviour.",
"@id": "urn:solid-server:default:NotificationSubscriptionHttpHandler",
"@type": "NotificationSubscriptionHttpHandler",
"args_baseUrl": { "@id": "urn:solid-server:default:variable:baseUrl" },
"args_wsEndpoint": { "@id": "urn:solid-server:default:variable:baseUrl" },
"args_credentialsExtractor": { "@id": "urn:solid-server:default:CredentialsExtractor" },
"args_permissionReader": { "@id": "urn:solid-server:default:PermissionReader" },
"args_notificationStorage": { "@id": "urn:solid-server:default:NotificationStorage"},
"args_handlers": [{ "@id": "urn:solid-server:default:WebHookSubscription2021Handler"}],
"args_source": { "@id": "urn:solid-server:default:ResourceStore"}
}
},
{
"comment": "Handles WebHookSubscription2021 subscriptions",
"@id": "urn:solid-server:default:WebHookSubscription2021Handler",
"@type": "WebHookSubscription2021Handler",
"args_httpClient": {
"@id": "urn:solid-server:default:WebHook2021HttpClient",
"@type": "WebHookAuthHttpClient",
"args_httpClient": { "@id": "urn:solid-server:default:BaseHttpClient" },
"args_baseUrl": { "@id": "urn:solid-server:default:variable:baseUrl" },
"args_jwksKeyGenerator": { "@id": "urn:solid-server:default:JwksKeyGenerator" }
},
"args_webhookUnsubscribePath": "webhook",
"args_baseUrl": { "@id": "urn:solid-server:default:variable:baseUrl" }
},
{
"comment": "Handles WebHookSubscription2021 unsubscribe route.",
"@id": "urn:solid-server:default:WebHookSubscription2021UnsubscribeHandler",
"@type": "RouterHandler",
"args_baseUrl": { "@id": "urn:solid-server:default:variable:baseUrl" },
"args_targetExtractor": { "@id": "urn:solid-server:default:TargetExtractor" },
"args_allowedMethods": [ "DELETE" ],
"args_allowedPathNames": [ "^/webhook/.*" ],
"args_handler": {
"comment": "Handles notification webhook unsubscribe behaviour.",
"@id": "urn:solid-server:default:WebHookSubscription2021UnsubscribeHttpHandler",
"@type": "WebHookSubscription2021UnsubscribeHttpHandler",
"args_baseUrl": { "@id": "urn:solid-server:default:variable:baseUrl" },
"args_credentialsExtractor": { "@id": "urn:solid-server:default:CredentialsExtractor" },
"args_notificationStorage": { "@id": "urn:solid-server:default:NotificationStorage"}
}
},
{
"comment": "Handles http calls",
"@id": "urn:solid-server:default:BaseHttpClient",
"@type": "BaseHttpClient"
}
]
}
5 changes: 5 additions & 0 deletions config/storage/key-value/memory.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@
"comment": "Storage used by setup components.",
"@id": "urn:solid-server:default:SetupStorage",
"@type": "MemoryMapStorage"
},
{
"comment": "Storage used for notification management.",
"@id": "urn:solid-server:default:NotificationStorage",
"@type": "MemoryMapStorage"
}
]
}
8 changes: 8 additions & 0 deletions config/storage/key-value/resource-store.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,14 @@
"baseUrl": { "@id": "urn:solid-server:default:variable:baseUrl" },
"container": "/.internal/setup/"
},
{
"comment": "Storage used for notification management.",
"@id": "urn:solid-server:default:NotificationStorage",
"@type": "JsonResourceStorage",
"source": { "@id": "urn:solid-server:default:ResourceStore" },
"baseUrl": { "@id": "urn:solid-server:default:variable:baseUrl" },
"container": "/.internal/notification/"
},
{
"comment": "Block external access to the storage containers to avoid exposing internal data.",
"@id": "urn:solid-server:default:PathBasedReader",
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"prepare": "npm run build",
"start": "node ./bin/server.js",
"start:file": "node ./bin/server.js -c config/file.json -f ./data",
"start:notification": "node ./bin/server.js -c config/notification.json -f ./data",
"test": "npm run test:ts && npm run jest",
"test:deploy": "test/deploy/validate-package.sh",
"test:ts": "tsc -p test --noEmit",
Expand Down
72 changes: 72 additions & 0 deletions src/http/NotificationGatewayHttpHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { Readable } from 'stream';
import type { ResponseDescription } from '../http/output/response/ResponseDescription';
import { getLoggerFor } from '../logging/LogUtil';
import { OperationHttpHandler } from '../server/OperationHttpHandler';
import type { OperationHttpHandlerInput } from '../server/OperationHttpHandler';
import { NotImplementedHttpError } from '../util/errors/NotImplementedHttpError';
import { guardStream } from '../util/GuardedStream';
import { joinUrl, trimTrailingSlashes } from '../util/PathUtil';
import type { OperationHandlerInput } from './ldp/OperationHandler';
import type { NotificationSubscriptionHttpHandler } from './NotificationSubscriptionHttpHandler';
import { OkResponseDescription } from './output/response/OkResponseDescription';
import { RepresentationMetadata } from './representation/RepresentationMetadata';

export interface NotificationGatewayHttpHandlerArgs {
/**
* Base URL of the gateway.
*/
baseUrl: string;
/**
* Relative path of the IDP entry point.
*/
subscriptionPath: string;
/**
* The notification handler.
*/
subscriptionHandler: NotificationSubscriptionHttpHandler;
}

/**
* Handles the negotiation of notification channels
*/
export class NotificationGatewayHttpHandler extends OperationHttpHandler {
protected readonly logger = getLoggerFor(this);

private readonly subscriptionHandler: NotificationSubscriptionHttpHandler;
private readonly subscriptionEndpoint: string;
private readonly supportedTypes: string[];

public constructor(args: NotificationGatewayHttpHandlerArgs) {
super();
// Trimming trailing slashes so the relative URL starts with a slash after slicing this off
this.subscriptionEndpoint = trimTrailingSlashes(joinUrl(args.baseUrl, args.subscriptionPath));
this.subscriptionHandler = args.subscriptionHandler;
this.supportedTypes = this.subscriptionHandler.getSupportedTypes();
}

public async canHandle({ operation }: OperationHandlerInput): Promise<void> {
if (operation.method !== 'POST') {
throw new NotImplementedHttpError('This handler only supports POST operations');
}
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
public async handle({ operation, request }: OperationHttpHandlerInput): Promise<ResponseDescription | undefined> {
const body = await request.read();
const json = JSON.parse(body.toString());
const requestedTypes: string[] = json.type;
const matches = requestedTypes.filter((type): boolean => this.supportedTypes.includes(type));
if (matches.length === 0) {
throw new NotImplementedHttpError(`This gateway only supports ${this.supportedTypes} notifications`);
}
const responseJson = {
'@context': [ 'https://www.w3.org/ns/solid/notification/v1' ],
type: matches[0],
endpoint: this.subscriptionEndpoint,
features: [],
};
const representationMetadata = new RepresentationMetadata('application/ld+json');
const data = guardStream(Readable.from(JSON.stringify(responseJson)));
return new OkResponseDescription(representationMetadata, data);
}
}
Loading