homebridge-config-ui-x
Version:
A web based management, configuration and control platform for Homebridge.
132 lines • 5.42 kB
JavaScript
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var PluginsSettingsUiTicketService_1;
import { createHash, randomBytes } from 'node:crypto';
import process from 'node:process';
import { Injectable, UnauthorizedException } from '@nestjs/common';
import NodeCache from 'node-cache';
import { DEV_SERVER_PORTS } from '../../../core/regex.constants.js';
let PluginsSettingsUiTicketService = class PluginsSettingsUiTicketService {
static { PluginsSettingsUiTicketService_1 = this; }
static assetSessionTtl = 1800;
tickets = new NodeCache({ stdTTL: 60, useClones: false });
assetSessions = new NodeCache({
stdTTL: PluginsSettingsUiTicketService_1.assetSessionTtl,
useClones: false,
});
issue(pluginName, username, requestOrigin, requestHost) {
const ticket = randomBytes(32).toString('base64url');
this.tickets.set(this.digest(ticket), {
pluginName,
username,
uiOrigin: this.resolveDevelopmentOrigin(requestOrigin, requestHost),
});
return ticket;
}
consume(ticket, pluginName) {
if (typeof ticket !== 'string' || !ticket || ticket.length > 128) {
throw new UnauthorizedException();
}
const claims = this.tickets.take(this.digest(ticket));
if (!claims || claims.pluginName !== pluginName) {
throw new UnauthorizedException();
}
return claims;
}
issueAssetSession(pluginName, username, uiOrigin) {
const token = randomBytes(32).toString('base64url');
this.assetSessions.set(this.digest(token), { pluginName, username, uiOrigin });
return token;
}
validateAssetSession(token, pluginName) {
if (!token || token.length > 128) {
throw new UnauthorizedException();
}
const digest = this.digest(token);
const session = this.assetSessions.get(digest);
if (!session || session.pluginName !== pluginName) {
throw new UnauthorizedException();
}
this.assetSessions.ttl(digest, PluginsSettingsUiTicketService_1.assetSessionTtl);
return { ...session, token };
}
revokeAssetSession(token, pluginName) {
if (!token || token.length > 128) {
return;
}
const digest = this.digest(token);
const session = this.assetSessions.get(digest);
if (session?.pluginName === pluginName) {
this.assetSessions.del(digest);
}
}
revokeUserPlugin(username, pluginName) {
for (const key of this.tickets.keys()) {
const ticket = this.tickets.get(key);
if (ticket?.username === username && ticket.pluginName === pluginName) {
this.tickets.del(key);
}
}
for (const key of this.assetSessions.keys()) {
const session = this.assetSessions.get(key);
if (session?.username === username && session.pluginName === pluginName) {
this.assetSessions.del(key);
}
}
}
revokeUser(username) {
const pluginNames = new Set();
for (const key of this.tickets.keys()) {
if (this.tickets.get(key)?.username === username) {
this.tickets.del(key);
}
}
for (const key of this.assetSessions.keys()) {
const session = this.assetSessions.get(key);
if (session?.username === username) {
pluginNames.add(session.pluginName);
this.assetSessions.del(key);
}
}
return [...pluginNames];
}
extractAssetSession(cookieHeader) {
for (const part of cookieHeader?.split(';') ?? []) {
const [name, ...value] = part.trim().split('=');
if (name === 'hb-plugin-ui') {
return value.join('=') || undefined;
}
}
}
digest(ticket) {
return createHash('sha256').update(ticket).digest('base64url');
}
resolveDevelopmentOrigin(origin, requestHost) {
if (process.env.UIX_DEVELOPMENT !== '1' || !origin || !requestHost) {
return '';
}
try {
const url = new URL(origin);
const requestUrl = new URL(`http://${requestHost}`);
if ((url.protocol !== 'http:' && url.protocol !== 'https:')
|| !DEV_SERVER_PORTS.has(url.port)
|| url.origin !== origin
|| url.hostname !== requestUrl.hostname) {
return '';
}
return url.origin;
}
catch {
return '';
}
}
};
PluginsSettingsUiTicketService = PluginsSettingsUiTicketService_1 = __decorate([
Injectable()
], PluginsSettingsUiTicketService);
export { PluginsSettingsUiTicketService };
//# sourceMappingURL=plugins-settings-ui-ticket.service.js.map