UNPKG

node-hue-api

Version:
120 lines (119 loc) 5.79 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.LocalBootstrap = void 0; const https = __importStar(require("https")); const Api_1 = require("../Api"); const httpClient = __importStar(require("./HttpClientFetch")); const ApiError_1 = require("../../ApiError"); const Transport_1 = require("./Transport"); const sslCertificate_1 = require("./sslCertificate"); const urlUtil_1 = require("./urlUtil"); const DEBUG = /node-hue-api/.test(process.env.NODE_DEBUG || ''); const INITIAL_HTTPS_AGENT = new https.Agent({ rejectUnauthorized: false }); class LocalBootstrap { /** * Create a Local Network Bootstrap for connecting to the Hue Bridge. The connection is ALWAYS over TLS/HTTPS. * * @param {String} hostname The hostname or ip address of the hue bridge on the local network. * @param {number=} port The port number for the connections, defaults to 443 and should not need to be specified in the majority of use cases. */ constructor(hostname, rateLimits, port) { this.baseUrl = (0, urlUtil_1.getHttpsUrl)(hostname, port || 443); this.hostname = (0, urlUtil_1.cleanHostname)(hostname); this.rateLimits = rateLimits; } /** * Connects to the Hue Bridge using the local network. * * The connection will perform checks on the Hue Bridge TLS Certificate to verify it is correct before sending any * sensitive information. * * @param {String=} username The username to use when connecting, can be null, but will severely limit the endpoints that you can call/access * @param {String=} clientkey The clientkey for the user, used by the entertainment API, can be null * @param {Number=} timeout The timeout for requests sent to the Hue Bridge. If not set will default to 20 seconds. * @returns {Promise<Api>} The API for interacting with the hue bridge. */ connect(username, clientkey, timeout) { const self = this, hostname = self.hostname, baseUrl = self.baseUrl.href; return httpClient.request({ method: 'GET', url: `${baseUrl}api/config`, json: true, httpsAgent: INITIAL_HTTPS_AGENT, timeout: getTimeout(timeout), }).then(res => { const bridgeId = res.data.bridgeid.toLowerCase(); return (0, sslCertificate_1.getSSLCertificate)(hostname) .then((cert) => { const subjectCn = cert.subject.CN.toLowerCase(); if (DEBUG) { console.log('Bridge Certificate:\n' + ` subject: ${JSON.stringify(cert.subject)}\n` + ` issuer: ${JSON.stringify(cert.subject)}\n` + ` valid from: ${cert.valid_from}\n` + ` valid to: ${cert.valid_to}\n` + ` serial number: ${cert.serialNumber}\n`); console.log(`Performing validation of bridgeId "${bridgeId}" against certificate subject "${subjectCn}"; matched? ${subjectCn === bridgeId}`); } if (subjectCn === bridgeId) { return new https.Agent({ keepAlive: true, keepAliveMsecs: 10000, maxSockets: 50, // timeout: getTimeout(timeout), //node-fetch appears to ignore this rejectUnauthorized: false, // ca: cert.pemEncoded //TODO there are still issues here, as the certificate being self signed is failing somewhere deeper in TLS code }); } else { throw new ApiError_1.ApiError('The hue bridge certificate does not match the expected issuer'); } }).catch(error => { throw new ApiError_1.ApiError(error); }) .then(agent => { const apiBaseUrl = `${baseUrl}api`, fetchConfig = { baseURL: apiBaseUrl, httpsAgent: agent, timeout: getTimeout(timeout) }, transport = new Transport_1.Transport(httpClient.create(fetchConfig), this.rateLimits.transportRateLimit, username), config = { remote: false, baseUrl: apiBaseUrl, bridgeName: this.hostname, clientKey: clientkey, username: username, }; return new Api_1.Api(config, transport, this.rateLimits); }); }); } } exports.LocalBootstrap = LocalBootstrap; function getTimeout(timeout) { return timeout || 20000; }