UNPKG

unifi-client

Version:

NodeJs client for Unifi products (https://www.ui.com/)

374 lines (373 loc) 19.2 kB
"use strict"; var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.UnifiAuth = void 0; var util_1 = require("./util"); var set_cookie_parser_1 = __importDefault(require("set-cookie-parser")); var cookie_1 = __importDefault(require("cookie")); var jsonwebtoken_1 = __importDefault(require("jsonwebtoken")); var Errors_1 = require("./Errors"); var Validate_1 = require("./commons/Validate"); var ObjectWithPrivateValues_1 = require("./commons/ObjectWithPrivateValues"); var interfaces_1 = require("./interfaces"); var debug = (0, util_1.createDebugger)('UnifiAuth'); var UnifiAuth = /** @class */ (function (_super) { __extends(UnifiAuth, _super); function UnifiAuth(props, instance) { var _this = _super.call(this) || this; _this.autoReLogin = true; debug('Construct()'); _this.username = props.username; _this.password = props.password; if (Validate_1.Validate.isBoolean(props.rememberMe)) { _this.rememberMe = props.rememberMe; } else { _this.rememberMe = true; } _this.controllerInstance = _this.addInterceptors(instance); return _this; } Object.defineProperty(UnifiAuth.prototype, "username", { get: function () { return this.getPrivate('username'); }, set: function (data) { this.setPrivate('username', data); }, enumerable: false, configurable: true }); Object.defineProperty(UnifiAuth.prototype, "password", { get: function () { return this.getPrivate('password'); }, set: function (data) { this.setPrivate('password', data); }, enumerable: false, configurable: true }); UnifiAuth.prototype.getCookieTokenName = function () { return this.unifiOs ? 'TOKEN' : 'unifises'; }; UnifiAuth.prototype.getCookies = function () { return __awaiter(this, arguments, void 0, function (authenticationRequest) { var curDebug, cookies, value; if (authenticationRequest === void 0) { authenticationRequest = false; } return __generator(this, function (_a) { switch (_a.label) { case 0: curDebug = debug.extend('getCookies'); cookies = []; if (!!authenticationRequest) return [3 /*break*/, 2]; curDebug('not authentification request, include cookies'); return [4 /*yield*/, this.getToken()]; case 1: value = _a.sent(); if (value) { cookies.push({ name: this.getCookieTokenName(), value: value }); } _a.label = 2; case 2: if (this.csrfToken && !this.unifiOs) { curDebug('set csrfToken (!unifiOs)'); //non unifiOs cookies.push({ name: 'csrf_token', value: this.csrfToken }); } return [2 /*return*/, cookies]; } }); }); }; UnifiAuth.prototype.addInterceptors = function (instance) { var _this = this; debug('addInterceptors()'); instance.interceptors.request.use(function (config) { return __awaiter(_this, void 0, void 0, function () { var curDebug, cookies; return __generator(this, function (_a) { switch (_a.label) { case 0: curDebug = debug.extend('interceptedRequest'); curDebug('intercept request'); return [4 /*yield*/, this.getCookies(config.authenticationRequest)]; case 1: cookies = _a.sent(); if (this.unifiOs && this.csrfToken) { curDebug('set csrfToken (unifiOs)'); //TODO retry this with new axios versions config.headers = __assign(__assign({}, config.headers), { 'X-CSRF-Token': this.csrfToken }); } if (cookies.length > 0) { //TODO retry this with new axios versions config.headers = __assign(__assign({}, config.headers), { Cookie: cookies.map(function (c) { return cookie_1.default.serialize(c.name, c.value, c); }).join('; ') }); } return [2 /*return*/, config]; } }); }); }); instance.interceptors.response.use(function (response) { var curDebug = debug.extend('interceptedSucceedResponse'); curDebug('intercept success response'); if (response.headers && response.headers['x-csrf-token']) { curDebug('x-csrf-token header found, saving it'); _this.csrfToken = response.headers['x-csrf-token']; } if (!_this.unifiOs) { var cookies = _this.getCookiesFromResponse(response); if (cookies['csrf_token']) { curDebug('x-csrf-token cookie found, saving it'); _this.csrfToken = cookies['csrf_token'].value; } } return response; }, function (error) { return __awaiter(_this, void 0, void 0, function () { var curDebug, response; var _a; return __generator(this, function (_b) { switch (_b.label) { case 0: curDebug = debug.extend('interceptedErroredResponse'); curDebug('intercept errored response'); if (!(error.response || error instanceof Errors_1.UnifiError)) return [3 /*break*/, 2]; response = error instanceof Errors_1.UnifiError ? (_a = error.axiosError) === null || _a === void 0 ? void 0 : _a.response : error.response; if ((response === null || response === void 0 ? void 0 : response.headers) && (response === null || response === void 0 ? void 0 : response.headers['x-csrf-token'])) { curDebug('x-csrf-token header found, saving it'); this.csrfToken = response.headers['x-csrf-token']; } if (!(!this.unifiOs && (response === null || response === void 0 ? void 0 : response.config) && (response === null || response === void 0 ? void 0 : response.status) === 401 && !(response === null || response === void 0 ? void 0 : response.config.retryAuth) && this.autoReLogin)) return [3 /*break*/, 2]; curDebug('login is expired, try to re-login'); return [4 /*yield*/, this.login()]; case 1: _b.sent(); return [2 /*return*/, this.controllerInstance.request(__assign(__assign({}, response.config), { retryAuth: true }))]; case 2: return [2 /*return*/, Promise.reject(error)]; } }); }); }); return instance; }; UnifiAuth.prototype.getCookiesFromResponse = function (res) { //res is compatible with incomingMessage for this use return (0, set_cookie_parser_1.default)(res, { map: true }); }; /** * * @param token2FA - 2FA token, will disable re-login */ UnifiAuth.prototype.login = function (token2FA) { return __awaiter(this, void 0, void 0, function () { var curDebug, resCheck, data, res, cookies; return __generator(this, function (_a) { switch (_a.label) { case 0: debug('login()'); curDebug = debug.extend('login'); //reset tokens this.token = undefined; this.csrfToken = undefined; if (!Validate_1.Validate.isUndefined(this.unifiOs)) return [3 /*break*/, 2]; curDebug('check if unifiOs'); return [4 /*yield*/, this.controllerInstance.get('/', { validateStatus: function () { return true; }, authenticationRequest: true })]; case 1: resCheck = _a.sent(); //this is not unifiOS if (resCheck.status === 302 && resCheck.headers.location === '/manage') { curDebug('os found : not unifiOs'); this.unifiOs = false; } else if (resCheck.status === 200) { curDebug('os found : unifiOs'); this.unifiOs = true; } else { throw new Errors_1.ClientError('fail to detect unifiOs or not !', Errors_1.EErrorsCodes.FAIL_TO_DETECT_UNIFIOS); } _a.label = 2; case 2: curDebug('start login request'); data = { username: this.username, password: this.password, rememberMe: this.rememberMe }; if (token2FA) { //on unifios 2FA is in token field . Else in ubic_2fa_token data[this.unifiOs ? 'token' : 'ubic_2fa_token'] = token2FA; } if (!this.unifiOs) { // without this non unifios > 9 doesn't return csrf token data.strict = true; } return [4 /*yield*/, this.controllerInstance.post(this.unifiOs ? '/auth/login' : '/login', data, { authenticationRequest: true, apiPart: true })]; case 3: res = _a.sent(); if (token2FA) { this.autoReLogin = false; } curDebug('end login request'); cookies = this.getCookiesFromResponse(res); if (!cookies[this.getCookieTokenName()]) { throw new Errors_1.ClientError("fail to get token from cookies[".concat(this.getCookieTokenName(), "]"), Errors_1.EErrorsCodes.FAIL_LOGIN); } this.token = cookies[this.getCookieTokenName()].value; if (!this.unifiOs) { if (!cookies['csrf_token']) { throw new Errors_1.ClientError('fail to get CSRF token from cookies', Errors_1.EErrorsCodes.FAIL_GET_CSRF_COOKIE); } curDebug('found csrf token in cookie, saving it'); this.csrfToken = cookies['csrf_token'].value; } return [2 /*return*/, res.data]; } }); }); }; UnifiAuth.prototype.logout = function () { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: debug('logout()'); return [4 /*yield*/, this.controllerInstance.post("/api".concat(this.unifiOs ? '/auth' : '', "/logout"))]; case 1: _a.sent(); return [2 /*return*/]; } }); }); }; UnifiAuth.prototype.getVersion = function () { return __awaiter(this, arguments, void 0, function (site) { var version, e_1; var _a, _b, _c; if (site === void 0) { site = 'default'; } return __generator(this, function (_d) { switch (_d.label) { case 0: _d.trys.push([0, 2, , 3]); return [4 /*yield*/, this.controllerInstance.get('/api/s/:site/stat/sysinfo', { urlParams: { site: site }, proxyNamespace: interfaces_1.EProxyNamespaces.NETWORK })]; case 1: version = (_c = (_b = (_a = (_d.sent()).data) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.pop()) === null || _c === void 0 ? void 0 : _c.version; debug('controller version is : %s', version); return [2 /*return*/, version]; case 2: e_1 = _d.sent(); debug('fail to load version'); return [3 /*break*/, 3]; case 3: return [2 /*return*/]; } }); }); }; UnifiAuth.prototype.getToken = function () { return __awaiter(this, void 0, void 0, function () { var curDebug, token; return __generator(this, function (_a) { switch (_a.label) { case 0: curDebug = debug.extend('getToken'); curDebug('()'); token = this.token; if (! // non unifiOs token is not a JWT token, can't know if the token is expired with this method ... (this.unifiOs && //check if token, or if jwt token will not expire in the 2 next minutes, just in case (!token || jsonwebtoken_1.default.decode(token).exp * 1000 < Date.now() + 2 * 60 * 1000) && // autoReLogin enabled ? this.autoReLogin)) // non unifiOs token is not a JWT token, can't know if the token is expired with this method ... return [3 /*break*/, 2]; // console.log(this.unifiOs, (jwt.decode(token) as { exp: number }).exp * 1000, Date.now() + 2 * 60 * 1000, this.autoReLogin); curDebug('token seems invalid, try to relogin'); return [4 /*yield*/, this.login()]; case 1: _a.sent(); _a.label = 2; case 2: return [2 /*return*/, this.token]; } }); }); }; UnifiAuth.prototype.addInterceptorsToInstance = function (instance) { debug('addInterceptorsToInstance'); this.addInterceptors(instance); }; return UnifiAuth; }(ObjectWithPrivateValues_1.ObjectWithPrivateValues)); exports.UnifiAuth = UnifiAuth;