UNPKG

homebridge-config-ui-x

Version:

A web based management, configuration and control platform for Homebridge.

132 lines 5.42 kB
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