@twin.org/api-tenant-processor
Version:
API Tenant Processor for converting and api key to a tenant id.
83 lines • 2.97 kB
JavaScript
// 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