@solid/community-server
Version:
Community Solid Server: an open and modular implementation of the Solid specifications
89 lines • 4.98 kB
JavaScript
;
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