UNPKG

domotz-remote-pawn

Version:

Domotz Agent

325 lines (301 loc) 11.5 kB
/** This file is part of Domotz Agent. * Copyright (C) 2024 Domotz Ltd * * Domotz Agent is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Domotz Agent is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Domotz Agent. If not, see <http://www.gnu.org/licenses/>. * * Created by Andrea Azzara <a.azzara@domotz.com> on 18/01/2024. */ var jayson = require('jayson'); var fs = require('fs'); var lodash = require('lodash'); var async = require('async'); const DEFAULT_ROUTER_IP = '127.0.0.1'; const DOMOTZ_API_PATH = 'socketCommunication'; const TOKEN_PATH = '/tmp/.api_token'; const JSON_RPC_VERSION = '2.0'; function netgearManager(q, log) { var logger = log.decorateLogs(); var routerIp = DEFAULT_ROUTER_IP; var token; var session = 1; var utils = require('./netgearUtils').utils(); function messageBuilder(method, commandParameters) { return { method: method, params: commandParameters, }; } function loadTokenFromFS() { var content = null; try { var row = fs.readFileSync(TOKEN_PATH, 'utf8'); content = row.toString().trim(); } catch (error) { logger.error('Error reading file %s stack_trace: %s', TOKEN_PATH, JSON.stringify(error)); } return content; } function createSessionToken() { logger.info('Creating session token'); var message = messageBuilder('createApiToken', { name: 'domotz' }); return netgearRouterRequest(message); } function getToken(renewal) { if (token) { return q.resolve(token); } var deferred = q.defer(); if (!renewal) { token = loadTokenFromFS(); logger.info('Token: %s', token); } else { logger.debug('Renewing token %s', token); } if (token) { logger.info('Token already present'); deferred.resolve(token); } else { createSessionToken() .then(function () { logger.info('Token created'); token = loadTokenFromFS(); deferred.resolve(token); }) .catch(function () { deferred.reject('Unable to create a session token'); }); } return deferred.promise; } function netgearRouterRequest(message, requestToken, promise) { logger.debug('Calling netgear with message: %s and token %s', JSON.stringify(message), requestToken); var deferred = promise || q.defer(); var options = { host: routerIp, rejectUnauthorized: false, path: '/' + DOMOTZ_API_PATH, port: 443, version: JSON_RPC_VERSION, generator: function () { session += 1; return session; }, headers: requestToken ? { Authorization: 'Bearer ' + requestToken } : {}, }; var client = jayson.client.https(options); logger.debug('Headers %s', JSON.stringify(options.headers)); function removeToken() { try { fs.unlinkSync(TOKEN_PATH); } catch (e) { logger.info('Unable to remove token'); } token = null; logger.info('Token deleted'); } client.request(message.method, message.params, function (error, response) { if (error) { deferred.reject('error connecting to netgear: ' + error); if (error.code === 401 && !promise) { logger.info('Renewing token'); removeToken(); getToken(true) .then(function (newToken) { netgearRouterRequest(message, newToken, deferred); }) .catch(function () { logger.error('Unable to renew token'); deferred.reject('Unable to renew token'); }); logger.warn('Token expired'); } return; } logger.debug('RPC Response: %s', JSON.stringify(response)); deferred.resolve(response.result); }); return deferred.promise; } function getBlockedDevices() { var deferred = q.defer(); getToken() .then(function (token) { var message = messageBuilder('getTrafficRules', {}); netgearRouterRequest(message, token) .then(function (response) { var filteredResponse = lodash.filter(response, function (element) { return element.name.startsWith('domotz_') && element.enabled === 1; }); deferred.resolve(filteredResponse); }) .catch(function (err) { logger.error('Error getting blocked devices: ' + JSON.stringify(err)); deferred.reject(err); }); }) .catch(function (err) { manageTokenCreationError(err, deferred); }); return deferred.promise; } function manageTokenCreationError(error, deferred) { logger.error('Error getting token: ' + JSON.stringify(error)); deferred.reject('Error getting token'); } function blockDevice(macAddress, ipAddress) { macAddress = macAddress.toUpperCase(); ipAddress = ipAddress.toUpperCase(); var deferred = q.defer(); getToken() .then(function (token) { var message = messageBuilder('addTrafficRules', [ { name: utils.generateFirewallKey(macAddress, ipAddress), enabled: 1, action: 'Deny', service: 'All Traffic', schedule: 'Always', sourceInterface: 'Any', destinationInterface: 'Any', sourceAddressType: 'Single', destinationAddressType: 'Any', sourceIpV4Address: ipAddress, destinationIpV4Address: 'Any', }, ]); netgearRouterRequest(message, token) .then(function () { deferred.resolve('ok'); }) .catch(function (err) { deferred.reject(err); }); }) .catch(function (err) { manageTokenCreationError(err, deferred); }); return deferred.promise; } function unblockDeviceByKey(key) { var deferred = q.defer(); getToken() .then(function (token) { var message = messageBuilder('deleteTrafficRuleByName', { name: key }); netgearRouterRequest(message, token) .then(function () { deferred.resolve(); }) .catch(function () { deferred.reject(); }); }) .catch(function (err) { manageTokenCreationError(err, deferred); }); return deferred.promise; } function unblockAllDevices() { var deferred = q.defer(); var promises = []; getBlockedDevices() .then(function (rules) { lodash.forOwn(rules, function (rule) { var name = rule.name; logger.info('Deleting rule named: %s', name); promises.push(unblockDeviceByKey(name)); }); q.all(promises) .then(function () { deferred.resolve('All rules deleted'); }) .catch(function (err) { deferred.reject(err); }); }) .catch(function () { logger.error('Error retrieving blocked devices list'); }); return deferred.promise; } function blockDeviceKeys(deviceKeys) { var prepareParametersForCreateRules = function (keys) { var ret = []; lodash.forEach(keys, function (key) { var split = key.split('_'); var ipAddress = split[2]; ret.push({ name: key, enabled: 1, action: 'Deny', service: 'All Traffic', schedule: 'Always', sourceInterface: 'Any', destinationInterface: 'Any', sourceAddressType: 'Single', destinationAddressType: 'Any', sourceIpV4Address: ipAddress, destinationIpV4Address: 'Any', }); }); return ret; }; var params = prepareParametersForCreateRules(deviceKeys); var deferred = q.defer(); getToken() .then(function (token) { var message = messageBuilder('addTrafficRules', params); logger.debug('Calling netgear with message: %s and token %s', JSON.stringify(message), token); netgearRouterRequest(message, token) .then(function () { deferred.resolve(); }) .catch(function (err) { manageTokenCreationError(err, deferred); }); }) .catch(function () { logger.error('Error getting token'); }); return deferred.promise; } function unblockDeviceKeys(deviceKeys) { var deferred = q.defer(); async.eachOfSeries( deviceKeys, function (key, index, callback) { logger.info('Unlocking %s', key); unblockDeviceByKey(key).then(function () { callback(null, true); }); }, function (error) { if (error) { return deferred.reject(); } deferred.resolve(); } ); return deferred.promise; } return { getToken: getToken, getBlockedDevices: getBlockedDevices, blockDevice: blockDevice, unblockDeviceByKey: unblockDeviceByKey, blockDeviceKeys: blockDeviceKeys, unblockDeviceKeys: unblockDeviceKeys, unblockAllDevices: unblockAllDevices, }; } module.exports = netgearManager;