Replace auth.js script with config expression
This commit is contained in:
1 parent
6c6d88e88b
commit
2c9e7e8e29
8 files changed
+280
-234
No files matched your search
@@ -2,7 +2,6 @@
|
|||||||
node_modules
|
node_modules
|
||||||
dist
|
dist
|
||||||
debug
|
debug
|
||||||
config/auth.js
|
|
||||||
config/config.json
|
config/config.json
|
||||||
config/config-ui.json
|
config/config-ui.json
|
||||||
config/cwmp.key
|
config/cwmp.key
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
*~
|
*~
|
||||||
.npmignore
|
.npmignore
|
||||||
|
|
||||||
config/auth.js
|
|
||||||
config/config.json
|
config/config.json
|
||||||
config/cwmp.key
|
config/cwmp.key
|
||||||
config/cwmp.crt
|
config/cwmp.crt
|
||||||
|
|||||||
@@ -103,7 +103,6 @@ async function copyStatic(): Promise<void> {
|
|||||||
"CHANGELOG.md",
|
"CHANGELOG.md",
|
||||||
"npm-shrinkwrap.json",
|
"npm-shrinkwrap.json",
|
||||||
"config/config-sample.json",
|
"config/config-sample.json",
|
||||||
"config/auth-sample.js",
|
|
||||||
"config/ext-sample.js",
|
"config/ext-sample.js",
|
||||||
"public/logo.svg",
|
"public/logo.svg",
|
||||||
"public/favicon.png"
|
"public/favicon.png"
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
|
|
||||||
function connectionRequest(deviceId, url, username, password, callback) {
|
|
||||||
return callback(username || deviceId, password || "");
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.connectionRequest = connectionRequest;
|
|
||||||
+73
-215
@@ -17,188 +17,24 @@
|
|||||||
* along with GenieACS. If not, see <http://www.gnu.org/licenses/>.
|
* along with GenieACS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as crypto from "crypto";
|
|
||||||
import * as dgram from "dgram";
|
|
||||||
import * as URL from "url";
|
|
||||||
import * as http from "http";
|
|
||||||
import * as config from "./config";
|
|
||||||
import * as db from "./db";
|
import * as db from "./db";
|
||||||
import * as common from "./common";
|
import * as common from "./common";
|
||||||
import * as auth from "./auth";
|
|
||||||
import * as cache from "./cache";
|
import * as cache from "./cache";
|
||||||
|
import {
|
||||||
function udpConReq(address, un, key, callback): boolean {
|
getCurrentSnapshot,
|
||||||
if (!address) return false;
|
getConfig,
|
||||||
|
getConfigExpression
|
||||||
let [host, port] = address.split(":", 2);
|
} from "./local-cache";
|
||||||
|
import {
|
||||||
if (!port) {
|
httpConnectionRequest,
|
||||||
port = 80;
|
udpConnectionRequest
|
||||||
} else {
|
} from "./connection-request";
|
||||||
port = parseInt(port);
|
import { Expression } from "./types";
|
||||||
if (isNaN(port)) return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ts = Math.trunc(Date.now() / 1000);
|
|
||||||
const id = Math.trunc(Math.random() * 4294967295);
|
|
||||||
const cn = crypto.randomBytes(8).toString("hex");
|
|
||||||
const sig = crypto
|
|
||||||
.createHmac("sha1", key)
|
|
||||||
.update(`${ts}${id}${un}${cn}`)
|
|
||||||
.digest("hex");
|
|
||||||
const uri = `http://${address}?ts=${ts}&id=${id}&un=${un}&cn=${cn}&sig=${sig}`;
|
|
||||||
const message = Buffer.from(
|
|
||||||
`GET ${uri} HTTP/1.1\r\nHost: ${address}\r\n\r\n`
|
|
||||||
);
|
|
||||||
|
|
||||||
const client = dgram.createSocket({ type: "udp4", reuseAddr: true });
|
|
||||||
const UDP_CONNECTION_REQUEST_PORT = +config.get(
|
|
||||||
"UDP_CONNECTION_REQUEST_PORT"
|
|
||||||
);
|
|
||||||
|
|
||||||
if (UDP_CONNECTION_REQUEST_PORT)
|
|
||||||
// When a device is NAT'ed, the UDP Connection Request must originate from
|
|
||||||
// the same address and port used by the STUN server, in order to traverse
|
|
||||||
// the firewall. This does require that the Genieacs NBI and STUN server
|
|
||||||
// are allowed to bind to the same address and port. The STUN server needs
|
|
||||||
// to open its UDP port with the SO_REUSEADDR option, allowing the NBI to
|
|
||||||
// also bind to the same port.
|
|
||||||
client.bind({ port: UDP_CONNECTION_REQUEST_PORT, exclusive: true });
|
|
||||||
|
|
||||||
let count = 3;
|
|
||||||
const func = (err: Error): void => {
|
|
||||||
if (err || --count <= 0) {
|
|
||||||
client.close();
|
|
||||||
return void callback(err);
|
|
||||||
}
|
|
||||||
client.send(message, 0, message.length, port, host, func);
|
|
||||||
};
|
|
||||||
client.send(message, 0, message.length, port, host, func);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function httpConReq(
|
|
||||||
url,
|
|
||||||
username,
|
|
||||||
password,
|
|
||||||
allowBasicAuth,
|
|
||||||
timeout,
|
|
||||||
callback
|
|
||||||
): void {
|
|
||||||
const options: http.RequestOptions = URL.parse(url);
|
|
||||||
if (options.protocol !== "http:") {
|
|
||||||
return void callback(
|
|
||||||
new Error("Invalid connection request URL or protocol")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
options.agent = new http.Agent({ maxSockets: 1 });
|
|
||||||
|
|
||||||
function statusToError(statusCode): Error {
|
|
||||||
switch (statusCode) {
|
|
||||||
case 200:
|
|
||||||
case 204:
|
|
||||||
return null;
|
|
||||||
case 401:
|
|
||||||
return new Error("Incorrect connection request credentials");
|
|
||||||
case 0:
|
|
||||||
return new Error("Device is offline");
|
|
||||||
default:
|
|
||||||
return new Error(`Unexpected response code from device: ${statusCode}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const request = http
|
|
||||||
.get(options, response => {
|
|
||||||
if (response.statusCode === 401 && response.headers["www-authenticate"]) {
|
|
||||||
const authHeader = auth.parseAuthHeader(
|
|
||||||
response.headers["www-authenticate"]
|
|
||||||
);
|
|
||||||
if (authHeader["method"] === "Basic") {
|
|
||||||
if (!allowBasicAuth) {
|
|
||||||
request.abort();
|
|
||||||
return void callback(
|
|
||||||
new Error("Basic HTTP authentication not allowed")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
options.headers = {
|
|
||||||
Authorization: auth.basic(username || "", password || "")
|
|
||||||
};
|
|
||||||
} else if (authHeader["method"] === "Digest") {
|
|
||||||
options.headers = {
|
|
||||||
Authorization: auth.digest(
|
|
||||||
username || "",
|
|
||||||
password || "",
|
|
||||||
options.path,
|
|
||||||
"GET",
|
|
||||||
null,
|
|
||||||
authHeader
|
|
||||||
)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const req = http
|
|
||||||
.get(options, res => {
|
|
||||||
if (res.statusCode === 0) {
|
|
||||||
// Workaround for some devices unexpectedly closing the connection
|
|
||||||
const req2 = http
|
|
||||||
.get(options, res2 => {
|
|
||||||
callback(statusToError(res2.statusCode));
|
|
||||||
res2.resume();
|
|
||||||
})
|
|
||||||
.on("error", () => {
|
|
||||||
req2.abort();
|
|
||||||
callback(statusToError(0));
|
|
||||||
})
|
|
||||||
.on("socket", socket => {
|
|
||||||
socket.setTimeout(timeout);
|
|
||||||
socket.on("timeout", () => {
|
|
||||||
req2.abort();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
callback(statusToError(res.statusCode));
|
|
||||||
}
|
|
||||||
res.resume();
|
|
||||||
})
|
|
||||||
.on("error", () => {
|
|
||||||
req.abort();
|
|
||||||
callback(statusToError(0));
|
|
||||||
})
|
|
||||||
.on("socket", socket => {
|
|
||||||
socket.setTimeout(timeout);
|
|
||||||
socket.on("timeout", () => {
|
|
||||||
req.abort();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
callback(statusToError(response.statusCode));
|
|
||||||
}
|
|
||||||
// No listener for data so emit resume
|
|
||||||
response.resume();
|
|
||||||
})
|
|
||||||
.on("error", () => {
|
|
||||||
request.abort();
|
|
||||||
callback(statusToError(0));
|
|
||||||
})
|
|
||||||
.on("socket", socket => {
|
|
||||||
socket.setTimeout(timeout);
|
|
||||||
socket.on("timeout", () => {
|
|
||||||
request.abort();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function connectionRequest(deviceId, callback): void {
|
export function connectionRequest(deviceId, callback): void {
|
||||||
const CONNECTION_REQUEST_TIMEOUT = config.get(
|
|
||||||
"CONNECTION_REQUEST_TIMEOUT",
|
|
||||||
deviceId
|
|
||||||
);
|
|
||||||
const CONNECTION_REQUEST_ALLOW_BASIC_AUTH = config.get(
|
|
||||||
"CONNECTION_REQUEST_ALLOW_BASIC_AUTH",
|
|
||||||
deviceId
|
|
||||||
);
|
|
||||||
const options = {
|
const options = {
|
||||||
projection: {
|
projection: {
|
||||||
|
_deviceId: 1,
|
||||||
"Device.ManagementServer.ConnectionRequestURL._value": 1,
|
"Device.ManagementServer.ConnectionRequestURL._value": 1,
|
||||||
"Device.ManagementServer.UDPConnectionRequestAddress._value": 1,
|
"Device.ManagementServer.UDPConnectionRequestAddress._value": 1,
|
||||||
"Device.ManagementServer.ConnectionRequestUsername._value": 1,
|
"Device.ManagementServer.ConnectionRequestUsername._value": 1,
|
||||||
@@ -236,52 +72,74 @@ export function connectionRequest(deviceId, callback): void {
|
|||||||
if (managementServer.ConnectionRequestPassword)
|
if (managementServer.ConnectionRequestPassword)
|
||||||
password = managementServer.ConnectionRequestPassword._value;
|
password = managementServer.ConnectionRequestPassword._value;
|
||||||
|
|
||||||
const conReq = (): void => {
|
const context = {
|
||||||
const udpSent = udpConReq(
|
id: device["_id"],
|
||||||
udpConnectionRequestAddress,
|
serialNumber: device["_deviceId"]["SerialNumber"],
|
||||||
username,
|
productClass: device["_deviceId"]["ProductClass"],
|
||||||
password,
|
oui: device["_deviceId"]["OUI"],
|
||||||
err => {
|
username: username || "",
|
||||||
if (err) throw err;
|
password: password || ""
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
httpConReq(
|
|
||||||
connectionRequestUrl,
|
|
||||||
username,
|
|
||||||
password,
|
|
||||||
CONNECTION_REQUEST_ALLOW_BASIC_AUTH,
|
|
||||||
CONNECTION_REQUEST_TIMEOUT,
|
|
||||||
err => {
|
|
||||||
if (udpSent) return void callback();
|
|
||||||
callback(err);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (config.auth && config.auth.connectionRequest) {
|
getCurrentSnapshot()
|
||||||
// Callback is optional for backward compatibility
|
.then(snapshot => {
|
||||||
if (config.auth.connectionRequest.length > 4) {
|
const now = Date.now();
|
||||||
return void config.auth.connectionRequest(
|
const UDP_CONNECTION_REQUEST_PORT = +getConfig(
|
||||||
deviceId,
|
snapshot,
|
||||||
connectionRequestUrl,
|
"cwmp.udpConnectionRequestAuth",
|
||||||
username,
|
context,
|
||||||
password,
|
now
|
||||||
(u, p) => {
|
);
|
||||||
username = u;
|
const CONNECTION_REQUEST_TIMEOUT = +getConfig(
|
||||||
password = p;
|
snapshot,
|
||||||
conReq();
|
"cwmp.connectionRequestTimeout",
|
||||||
|
context,
|
||||||
|
now
|
||||||
|
);
|
||||||
|
const CONNECTION_REQUEST_ALLOW_BASIC_AUTH = !!getConfig(
|
||||||
|
snapshot,
|
||||||
|
"cwmp.connectionRequestAllowBasicAuth",
|
||||||
|
context,
|
||||||
|
now
|
||||||
|
);
|
||||||
|
let authExp: Expression = getConfigExpression(
|
||||||
|
snapshot,
|
||||||
|
"cwmp.connectionRequestAuth"
|
||||||
|
);
|
||||||
|
|
||||||
|
if (authExp === undefined) {
|
||||||
|
authExp = [
|
||||||
|
"FUNC",
|
||||||
|
"AUTH",
|
||||||
|
["PARAM", "username"],
|
||||||
|
["PARAM", "password"]
|
||||||
|
] as Expression;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let udpProm;
|
||||||
|
if (udpConnectionRequestAddress) {
|
||||||
|
udpProm = udpConnectionRequest(
|
||||||
|
udpConnectionRequestAddress,
|
||||||
|
authExp,
|
||||||
|
db.configCollection,
|
||||||
|
UDP_CONNECTION_REQUEST_PORT
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
[username, password] = config.auth.connectionRequest(
|
|
||||||
deviceId,
|
httpConnectionRequest(
|
||||||
connectionRequestUrl,
|
connectionRequestUrl,
|
||||||
username,
|
authExp,
|
||||||
password
|
context,
|
||||||
);
|
CONNECTION_REQUEST_ALLOW_BASIC_AUTH,
|
||||||
}
|
CONNECTION_REQUEST_TIMEOUT
|
||||||
conReq();
|
)
|
||||||
|
.then(callback)
|
||||||
|
.catch(err => {
|
||||||
|
if (udpProm) return void udpProm.then(callback).catch(callback);
|
||||||
|
callback(err);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(callback);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -235,11 +235,3 @@ export function getDefault(optionName): string | number | boolean {
|
|||||||
|
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
export let auth;
|
|
||||||
// Load authentication scripts
|
|
||||||
try {
|
|
||||||
auth = require(resolve(allConfig.CONFIG_DIR as string, "auth.js"));
|
|
||||||
} catch (error) {
|
|
||||||
// No auth.js exists
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,198 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2013-2019 Zaid Abdulla
|
||||||
|
*
|
||||||
|
* This file is part of GenieACS.
|
||||||
|
*
|
||||||
|
* GenieACS is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* GenieACS 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with GenieACS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as crypto from "crypto";
|
||||||
|
import * as dgram from "dgram";
|
||||||
|
import { parse } from "url";
|
||||||
|
import * as http from "http";
|
||||||
|
import { evaluate } from "./common/expression";
|
||||||
|
import { Expression } from "./types";
|
||||||
|
import * as auth from "./auth";
|
||||||
|
|
||||||
|
function extractAuth(
|
||||||
|
exp: Expression,
|
||||||
|
context: {},
|
||||||
|
now: number,
|
||||||
|
dflt: any
|
||||||
|
): [string, string, Expression] {
|
||||||
|
let username, password;
|
||||||
|
exp = evaluate(exp, context, now, e => {
|
||||||
|
if (!username && Array.isArray(e) && e[0] === "FUNC" && e[1] === "AUTH") {
|
||||||
|
if (!Array.isArray(e[2]) && !Array.isArray(e[3])) {
|
||||||
|
username = e[2];
|
||||||
|
password = e[3];
|
||||||
|
}
|
||||||
|
return dflt;
|
||||||
|
}
|
||||||
|
return e;
|
||||||
|
});
|
||||||
|
|
||||||
|
return [username, password, exp];
|
||||||
|
}
|
||||||
|
|
||||||
|
function httpGet(
|
||||||
|
options: {},
|
||||||
|
timeout
|
||||||
|
): Promise<{ statusCode: number; headers: {} }> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const req = http
|
||||||
|
.get(options, res => {
|
||||||
|
res.resume();
|
||||||
|
resolve({ statusCode: res.statusCode, headers: res.headers });
|
||||||
|
})
|
||||||
|
.on("error", () => {
|
||||||
|
req.abort();
|
||||||
|
reject(new Error("Device is offline"));
|
||||||
|
})
|
||||||
|
.on("socket", socket => {
|
||||||
|
socket.setTimeout(timeout);
|
||||||
|
socket.on("timeout", () => {
|
||||||
|
req.abort();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function httpConnectionRequest(
|
||||||
|
address: string,
|
||||||
|
authExp: Expression,
|
||||||
|
context: {},
|
||||||
|
allowBasicAuth: boolean,
|
||||||
|
timeout: number
|
||||||
|
): Promise<void> {
|
||||||
|
const now = Date.now();
|
||||||
|
const options: http.RequestOptions = parse(address);
|
||||||
|
if (options.protocol !== "http:")
|
||||||
|
throw new Error("Invalid connection request URL or protocol");
|
||||||
|
|
||||||
|
options.agent = new http.Agent({
|
||||||
|
maxSockets: 1,
|
||||||
|
keepAlive: true
|
||||||
|
});
|
||||||
|
|
||||||
|
let authHeader: {};
|
||||||
|
let username: string;
|
||||||
|
let password: string;
|
||||||
|
|
||||||
|
while (!authHeader || (username != null && password != null)) {
|
||||||
|
let opts = options;
|
||||||
|
if (authHeader) {
|
||||||
|
if (authHeader["method"] === "Basic") {
|
||||||
|
if (!allowBasicAuth)
|
||||||
|
throw new Error("Basic HTTP authentication not allowed");
|
||||||
|
|
||||||
|
opts = Object.assign(
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Authorization: auth.basic(username || "", password || "")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
options
|
||||||
|
);
|
||||||
|
} else if (authHeader["method"] === "Digest") {
|
||||||
|
opts = Object.assign(
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Authorization: auth.digest(
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
options.path,
|
||||||
|
"GET",
|
||||||
|
null,
|
||||||
|
authHeader
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
options
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
throw new Error("Unrecognized auth method");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let res = await httpGet(opts, timeout);
|
||||||
|
|
||||||
|
// Workaround for some devices unexpectedly closing the connection
|
||||||
|
if (res.statusCode === 0 && authHeader) res = await httpGet(opts, timeout);
|
||||||
|
if (res.statusCode === 0) throw new Error("Device is offline");
|
||||||
|
if (res.statusCode === 200 || res.statusCode === 204) return;
|
||||||
|
|
||||||
|
if (res.statusCode === 401 && res.headers["www-authenticate"]) {
|
||||||
|
authHeader = auth.parseAuthHeader(res.headers["www-authenticate"]);
|
||||||
|
[username, password, authExp] = extractAuth(authExp, context, now, false);
|
||||||
|
} else {
|
||||||
|
throw new Error(
|
||||||
|
`Unexpected response code from device: ${res.statusCode}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Error("Incorrect connection request credentials");
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function udpConnectionRequest(
|
||||||
|
address: string,
|
||||||
|
authExp: Expression,
|
||||||
|
context: {},
|
||||||
|
sourcePort: number = 0
|
||||||
|
): Promise<void> {
|
||||||
|
const [host, portStr] = address.split(":", 2);
|
||||||
|
const port = portStr ? parseInt(portStr) : 80;
|
||||||
|
const now = Date.now();
|
||||||
|
|
||||||
|
const client = dgram.createSocket({ type: "udp4", reuseAddr: true });
|
||||||
|
// When a device is NAT'ed, the UDP Connection Request must originate from
|
||||||
|
// the same address and port used by the STUN server, in order to traverse
|
||||||
|
// the firewall. This does require that the Genieacs NBI and STUN server
|
||||||
|
// are allowed to bind to the same address and port. The STUN server needs
|
||||||
|
// to open its UDP port with the SO_REUSEADDR option, allowing the NBI to
|
||||||
|
// also bind to the same port.
|
||||||
|
if (sourcePort) client.bind({ port: sourcePort, exclusive: true });
|
||||||
|
|
||||||
|
let username: string;
|
||||||
|
let password: string;
|
||||||
|
|
||||||
|
[username, password, authExp] = extractAuth(authExp, context, now, null);
|
||||||
|
if (username == null) username = "";
|
||||||
|
if (password == null) password = "";
|
||||||
|
while (username != null && password != null) {
|
||||||
|
const ts = Math.trunc(now / 1000);
|
||||||
|
const id = Math.trunc(Math.random() * 4294967295);
|
||||||
|
const cn = crypto.randomBytes(8).toString("hex");
|
||||||
|
const sig = crypto
|
||||||
|
.createHmac("sha1", password)
|
||||||
|
.update(`${ts}${id}${username}${cn}`)
|
||||||
|
.digest("hex");
|
||||||
|
const uri = `http://${address}?ts=${ts}&id=${id}&un=${username}&cn=${cn}&sig=${sig}`;
|
||||||
|
const message = Buffer.from(
|
||||||
|
`GET ${uri} HTTP/1.1\r\nHost: ${address}\r\n\r\n`
|
||||||
|
);
|
||||||
|
|
||||||
|
for (let i = 0; i < 3; ++i) {
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
client.send(message, 0, message.length, port, host, (err: Error) => {
|
||||||
|
if (err) reject(err);
|
||||||
|
else resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[username, password, authExp] = extractAuth(authExp, context, now, null);
|
||||||
|
}
|
||||||
|
client.close();
|
||||||
|
}
|
||||||
+9
-1
@@ -530,7 +530,8 @@ export function getConfig(
|
|||||||
"cwmp.connectionRequestAllowBasicAuth":
|
"cwmp.connectionRequestAllowBasicAuth":
|
||||||
"CONNECTION_REQUEST_ALLOW_BASIC_AUTH",
|
"CONNECTION_REQUEST_ALLOW_BASIC_AUTH",
|
||||||
"cwmp.maxCommitIterations": "MAX_COMMIT_ITERATIONS",
|
"cwmp.maxCommitIterations": "MAX_COMMIT_ITERATIONS",
|
||||||
"cwmp.deviceOnlineThreshold": "DEVICE_ONLINE_THRESHOLD"
|
"cwmp.deviceOnlineThreshold": "DEVICE_ONLINE_THRESHOLD",
|
||||||
|
"cwmp.udpConnectionRequestPort": "UDP_CONNECTION_REQUEST_PORT"
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!(key in snapshot.config)) {
|
if (!(key in snapshot.config)) {
|
||||||
@@ -541,6 +542,13 @@ export function getConfig(
|
|||||||
return expression.evaluate(snapshot.config[key], context, now || Date.now());
|
return expression.evaluate(snapshot.config[key], context, now || Date.now());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getConfigExpression(snapshotKey, key): Expression {
|
||||||
|
const snapshot = snapshots.get(snapshotKey);
|
||||||
|
if (!snapshot) throw new Error("Cache snapshot does not exist");
|
||||||
|
|
||||||
|
return snapshot.config[key];
|
||||||
|
}
|
||||||
|
|
||||||
export function getUsers(snapshotKey): {} {
|
export function getUsers(snapshotKey): {} {
|
||||||
const snapshot = snapshots.get(snapshotKey);
|
const snapshot = snapshots.get(snapshotKey);
|
||||||
if (!snapshot) throw new Error("Cache snapshot does not exist");
|
if (!snapshot) throw new Error("Cache snapshot does not exist");
|
||||||
|
|||||||
Reference in new issue
Block a user