UNPKG

@twin.org/api-tenant-processor

Version:

API Tenant Processor for converting and api key to a tenant id.

83 lines 2.97 kB
// Copyright 2025 IOTA Stiftung. // SPDX-License-Identifier: Apache-2.0. import { HttpErrorHelper } from "@twin.org/api-models"; import { ContextIdKeys } from "@twin.org/context"; import { BaseError, Is, UnauthorizedError } from "@twin.org/core"; import { EntityStorageConnectorFactory } from "@twin.org/entity-storage-models"; import { HttpStatusCode } from "@twin.org/web"; /** * Handles incoming api keys and maps them to tenant ids. */ export class TenantProcessor { /** * The default name for the api key header. * @internal */ static DEFAULT_API_KEY_NAME = "x-api-key"; /** * Runtime name for the class. */ static CLASS_NAME = "TenantProcessor"; /** * The entity storage for api keys. * @internal */ _entityStorageConnector; /** * The key in the header to look for the api key. * @internal */ _apiKeyName; /** * Create a new instance of NodeTenantProcessor. * @param options Options for the processor. */ constructor(options) { this._entityStorageConnector = EntityStorageConnectorFactory.get(options?.tenantEntityStorageType ?? "tenant"); this._apiKeyName = options?.config?.apiKeyName ?? TenantProcessor.DEFAULT_API_KEY_NAME; } /** * Returns the class name of the component. * @returns The class name of the component. */ className() { return TenantProcessor.CLASS_NAME; } /** * Pre process the REST request for the specified route. * @param request The incoming request. * @param response The outgoing response. * @param route The route to process. * @param contextIds The context IDs of the request. * @param processorState The state handed through the processors. */ async pre(request, response, route, contextIds, processorState) { const apiKey = request.headers?.[this._apiKeyName] ?? request.query?.[this._apiKeyName]; let errorResponse; if (Is.stringValue(apiKey)) { try { const nodeTenant = await this._entityStorageConnector.get(apiKey, "apiKey"); if (Is.empty(nodeTenant)) { errorResponse = new UnauthorizedError(TenantProcessor.CLASS_NAME, "apiKeyNotFound", { key: apiKey }); } else { contextIds[ContextIdKeys.Tenant] = nodeTenant.id; } } catch (err) { errorResponse = BaseError.fromError(err); } } else { errorResponse = new UnauthorizedError(TenantProcessor.CLASS_NAME, "missingApiKey", { keyName: this._apiKeyName }); } if (!Is.empty(errorResponse)) { HttpErrorHelper.buildResponse(response, errorResponse, HttpStatusCode.unauthorized); } } } //# sourceMappingURL=tenantProcessor.js.map