UNPKG

@solid/community-server

Version:

Community Solid Server: an open and modular implementation of the Solid specifications

89 lines 4.98 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.NotificationSubscriber = void 0; const global_logger_factory_1 = require("global-logger-factory"); const OkResponseDescription_1 = require("../../http/output/response/OkResponseDescription"); const BasicRepresentation_1 = require("../../http/representation/BasicRepresentation"); const ContentTypes_1 = require("../../util/ContentTypes"); const ErrorUtil_1 = require("../../util/errors/ErrorUtil"); const UnprocessableEntityHttpError_1 = require("../../util/errors/UnprocessableEntityHttpError"); const StreamUtil_1 = require("../../util/StreamUtil"); const OperationHttpHandler_1 = require("../OperationHttpHandler"); /** * Handles notification subscriptions by creating a notification channel. * * Uses the information from the provided {@link NotificationChannelType} to validate the input * and verify the request has the required permissions available. * If successful the generated channel will be stored in a {@link NotificationChannelStorage}. */ class NotificationSubscriber extends OperationHttpHandler_1.OperationHttpHandler { logger = (0, global_logger_factory_1.getLoggerFor)(this); channelType; converter; credentialsExtractor; permissionReader; authorizer; storage; maxDuration; constructor(args) { super(); this.channelType = args.channelType; this.converter = args.converter; this.credentialsExtractor = args.credentialsExtractor; this.permissionReader = args.permissionReader; this.authorizer = args.authorizer; this.storage = args.storage; this.maxDuration = (args.maxDuration ?? 20160) * 60 * 1000; } async handle({ operation, request }) { if (operation.method === 'GET' || operation.method === 'HEAD') { const description = JSON.stringify(this.channelType.getDescription(), null, 2); const representation = new BasicRepresentation_1.BasicRepresentation(description, operation.target, ContentTypes_1.APPLICATION_LD_JSON); return new OkResponseDescription_1.OkResponseDescription(representation.metadata, operation.method === 'GET' ? representation.data : undefined); } const credentials = await this.credentialsExtractor.handleSafe(request); this.logger.debug(`Extracted credentials: ${JSON.stringify(credentials)}`); let channel; try { const quadStream = await this.converter.handleSafe({ identifier: operation.target, representation: operation.body, preferences: { type: { [ContentTypes_1.INTERNAL_QUADS]: 1 } }, }); channel = await this.channelType.initChannel(await (0, StreamUtil_1.readableToQuads)(quadStream.data), credentials); } catch (error) { throw new UnprocessableEntityHttpError_1.UnprocessableEntityHttpError(`Unable to process notification channel: ${(0, ErrorUtil_1.createErrorMessage)(error)}`, { cause: error }); } if (this.maxDuration) { const duration = (channel.endAt ?? Number.POSITIVE_INFINITY) - Date.now(); if (duration > this.maxDuration) { channel.endAt = Date.now() + this.maxDuration; } } // Verify if the client is allowed to subscribe await this.authorize(credentials, channel); // Store the channel once it has been authorized await this.storage.add(channel); // Generate the response JSON-LD const jsonld = await this.channelType.toJsonLd(channel); const response = new BasicRepresentation_1.BasicRepresentation(JSON.stringify(jsonld), ContentTypes_1.APPLICATION_LD_JSON); // Complete the channel once the response has been sent out (0, StreamUtil_1.endOfStream)(response.data) .then(async () => this.channelType.completeChannel(channel)) .catch((error) => { this.logger.error(`There was an issue completing notification channel ${channel.id}: ${(0, ErrorUtil_1.createErrorMessage)(error)}`); }); return new OkResponseDescription_1.OkResponseDescription(response.metadata, response.data); } async authorize(credentials, channel) { const requestedModes = await this.channelType.extractModes(channel); this.logger.debug(`Retrieved required modes: ${[...requestedModes.entrySets()].join(',')}`); const availablePermissions = await this.permissionReader.handleSafe({ credentials, requestedModes }); this.logger.debug(`Available permissions are ${[...availablePermissions.entries()].join(',')}`); await this.authorizer.handleSafe({ credentials, requestedModes, availablePermissions }); this.logger.debug(`Authorization succeeded, creating notification channel`); } } exports.NotificationSubscriber = NotificationSubscriber; //# sourceMappingURL=NotificationSubscriber.js.map