Move UI configs to DB
This commit is contained in:
1 parent
192ed6b034
commit
1cceba3f8b
22 files changed
+337
-448
No files matched your search
@@ -59,6 +59,7 @@ const options = {
|
||||
UI_SSL: { type: "bool", default: false },
|
||||
UI_LOG_FILE: { type: "path", default: "" },
|
||||
UI_ACCESS_LOG_FILE: { type: "path", default: "" },
|
||||
UI_JWT_SECRET: { type: "string", default: "" },
|
||||
|
||||
UDP_CONNECTION_REQUEST_PORT: { type: "int", default: 0 },
|
||||
|
||||
|
||||
+63
-4
@@ -307,8 +307,30 @@ function refresh(callback) {
|
||||
db.getConfig((err, conf) => {
|
||||
if (err) return void reject(err);
|
||||
|
||||
conf.sort((a, b) => (a.id > b.id ? 1 : a.id < b.id ? -1 : 0));
|
||||
|
||||
const _config = {};
|
||||
for (const c of conf) _config[c.id] = c.value;
|
||||
|
||||
for (const c of conf) {
|
||||
if (
|
||||
c.id.startsWith("auth.") ||
|
||||
c.id.startsWith("permissions.") ||
|
||||
c.id.startsWith("ui.")
|
||||
) {
|
||||
const keys = c.id.split(".");
|
||||
let ref = _config;
|
||||
while (keys.length > 1) {
|
||||
const k = keys.shift();
|
||||
if (typeof ref[k] !== "object") ref[k] = {};
|
||||
ref = ref[k];
|
||||
}
|
||||
// Evaluate expressions to simplify them
|
||||
ref[keys[0]] = expression.evaluate(c.value);
|
||||
} else {
|
||||
// Evaluate expressions to simplify them
|
||||
_config[c.id] = expression.evaluate(c.value);
|
||||
}
|
||||
}
|
||||
|
||||
resolve(_config);
|
||||
});
|
||||
@@ -348,9 +370,22 @@ function refresh(callback) {
|
||||
}
|
||||
|
||||
function getCurrentSnapshot(callback) {
|
||||
if (Date.now() < nextRefresh) return void callback(null, currentSnapshot);
|
||||
refresh(err => {
|
||||
callback(err, currentSnapshot);
|
||||
if (Date.now() < nextRefresh) {
|
||||
if (callback) return callback(null, currentSnapshot);
|
||||
return Promise.resolve(currentSnapshot);
|
||||
}
|
||||
|
||||
if (callback) {
|
||||
return refresh(err => {
|
||||
callback(err, currentSnapshot);
|
||||
});
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
refresh(err => {
|
||||
if (err) return void reject(err);
|
||||
resolve(currentSnapshot);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -410,6 +445,27 @@ function getConfig(snapshotKey, key, context, now) {
|
||||
return expression.evaluate(snapshot.config[key], context, now || Date.now());
|
||||
}
|
||||
|
||||
function getAuthConfig(snapshotKey) {
|
||||
const snapshot = snapshots.get(snapshotKey);
|
||||
if (!snapshot) throw new Error("Cache snapshot does not exist");
|
||||
|
||||
return snapshot.config.auth;
|
||||
}
|
||||
|
||||
function getPermissionsConfig(snapshotKey) {
|
||||
const snapshot = snapshots.get(snapshotKey);
|
||||
if (!snapshot) throw new Error("Cache snapshot does not exist");
|
||||
|
||||
return snapshot.config.permissions;
|
||||
}
|
||||
|
||||
function getUiConfig(snapshotKey) {
|
||||
const snapshot = snapshots.get(snapshotKey);
|
||||
if (!snapshot) throw new Error("Cache snapshot does not exist");
|
||||
|
||||
return snapshot.config.ui;
|
||||
}
|
||||
|
||||
exports.getCurrentSnapshot = getCurrentSnapshot;
|
||||
exports.hasSnapshot = hasSnapshot;
|
||||
exports.getPresets = getPresets;
|
||||
@@ -417,3 +473,6 @@ exports.getProvisions = getProvisions;
|
||||
exports.getVirtualParameters = getVirtualParameters;
|
||||
exports.getFiles = getFiles;
|
||||
exports.getConfig = getConfig;
|
||||
exports.getAuthConfig = getAuthConfig;
|
||||
exports.getPermissionsConfig = getPermissionsConfig;
|
||||
exports.getUiConfig = getUiConfig;
|
||||
@@ -8,21 +8,24 @@ const koaBodyParser = require("koa-bodyparser");
|
||||
const koaJwt = require("koa-jwt");
|
||||
const jwt = require("jsonwebtoken");
|
||||
|
||||
const config = require("./ui/config");
|
||||
const config = require("./config");
|
||||
const api = require("./ui/api");
|
||||
const Authorizer = require("./common/authorizer");
|
||||
const expression = require("./common/expression");
|
||||
const logger = require("./logger");
|
||||
const localCache = require("./local-cache");
|
||||
|
||||
const koa = new Koa();
|
||||
const router = new Router();
|
||||
|
||||
const JWT_SECRET = config.server.jwtSecret;
|
||||
const JWT_SECRET = config.get("UI_JWT_SECRET");
|
||||
const JWT_COOKIE = "genieacs-ui-jwt";
|
||||
|
||||
function getPermissionSets(roles) {
|
||||
const allPermissions = config.permissions;
|
||||
const permissionSets = roles.map(role => {
|
||||
function getPermissionSets(ctx) {
|
||||
const allPermissions = localCache.getPermissionsConfig(
|
||||
ctx.state.configSnapshot
|
||||
);
|
||||
const permissionSets = ctx.state.user.roles.map(role => {
|
||||
return Object.values(allPermissions[role] || {}).map(p => {
|
||||
p = Object.assign({}, p);
|
||||
for (let [k, v] of Object.entries(p)) {
|
||||
@@ -47,8 +50,9 @@ function getPermissionSets(roles) {
|
||||
return permissionSets;
|
||||
}
|
||||
|
||||
function authSimple(username, password) {
|
||||
const user = config.auth.simple.users[username];
|
||||
function authSimple(ctx, username, password) {
|
||||
const authConfig = localCache.getAuthConfig(ctx.state.configSnapshot);
|
||||
const user = authConfig.simple.users[username];
|
||||
if (user && user.password === password) {
|
||||
const roles = user.roles.split(",").map(s => s.trim());
|
||||
return Promise.resolve(roles);
|
||||
@@ -60,6 +64,12 @@ koa.on("error", async err => {
|
||||
throw err;
|
||||
});
|
||||
|
||||
koa.use(async (ctx, next) => {
|
||||
const configSnapshot = await localCache.getCurrentSnapshot();
|
||||
ctx.state.configSnapshot = configSnapshot;
|
||||
return next();
|
||||
});
|
||||
|
||||
koa.use(
|
||||
koaJwt({
|
||||
secret: JWT_SECRET,
|
||||
@@ -69,18 +79,21 @@ koa.use(
|
||||
);
|
||||
|
||||
koa.use(async (ctx, next) => {
|
||||
if (ctx.state.user && ctx.state.user.roles) {
|
||||
ctx.state.authorizer = new Authorizer(
|
||||
getPermissionSets(ctx.state.user.roles)
|
||||
);
|
||||
} else {
|
||||
ctx.state.authorizer = new Authorizer([]);
|
||||
}
|
||||
if (ctx.state.user && ctx.state.user.roles)
|
||||
ctx.state.authorizer = new Authorizer(getPermissionSets(ctx));
|
||||
else ctx.state.authorizer = new Authorizer([]);
|
||||
|
||||
return next();
|
||||
});
|
||||
|
||||
router.post("/login", async ctx => {
|
||||
if (!JWT_SECRET) {
|
||||
ctx.status = 500;
|
||||
ctx.body = "UI_JWT_SECRET is not set";
|
||||
logger.error({ message: "UI_JWT_SECRET is not set" });
|
||||
return;
|
||||
}
|
||||
|
||||
const username = ctx.request.body.username;
|
||||
const password = ctx.request.body.password;
|
||||
|
||||
@@ -105,7 +118,7 @@ router.post("/login", async ctx => {
|
||||
logger.accessWarn(log);
|
||||
}
|
||||
|
||||
const roles = await authSimple(username, password);
|
||||
const roles = await authSimple(ctx, username, password);
|
||||
|
||||
if (roles) return void success(roles, "simple");
|
||||
|
||||
@@ -135,7 +148,7 @@ router.use("/api", api.routes(), api.allowedMethods());
|
||||
router.get("/", async ctx => {
|
||||
let permissionSets = [];
|
||||
if (ctx.state.user && ctx.state.user.roles)
|
||||
permissionSets = getPermissionSets(ctx.state.user.roles);
|
||||
permissionSets = getPermissionSets(ctx);
|
||||
|
||||
ctx.body = `
|
||||
<html>
|
||||
@@ -146,7 +159,9 @@ router.get("/", async ctx => {
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
window.clientConfig = ${JSON.stringify(config.getClientConfig())};
|
||||
window.clientConfig = ${JSON.stringify({
|
||||
ui: localCache.getUiConfig(ctx.state.configSnapshot)
|
||||
})};
|
||||
window.username = ${JSON.stringify(
|
||||
ctx.state.user ? ctx.state.user.username : ""
|
||||
)};
|
||||
|
||||
+19
-9
@@ -4,7 +4,7 @@ const http = require("http");
|
||||
const https = require("https");
|
||||
const url = require("url");
|
||||
|
||||
const config = require("./config");
|
||||
const config = require("../config");
|
||||
const db = require("./db");
|
||||
const mongodbFunctions = require("./mongodb-functions");
|
||||
|
||||
@@ -71,7 +71,9 @@ function deleteResource(resource, id) {
|
||||
const resourceUrl =
|
||||
resource === "virtualParameters" ? "virtual_parameters" : resource;
|
||||
const options = url.parse(
|
||||
`${config.server.nbi}${resourceUrl}/${encodeURIComponent(id)}`
|
||||
`http://127.0.0.1:${config.get(
|
||||
"NBI_PORT"
|
||||
)}/${resourceUrl}/${encodeURIComponent(id)}`
|
||||
);
|
||||
|
||||
options.method = "DELETE";
|
||||
@@ -91,9 +93,9 @@ function deleteResource(resource, id) {
|
||||
|
||||
function postTask(task, connectionRequest, callback) {
|
||||
const options = url.parse(
|
||||
`${config.server.nbi}devices/${encodeURIComponent(task.device)}/tasks${
|
||||
connectionRequest ? "?connection_request" : ""
|
||||
}`
|
||||
`http://127.0.0.1:${config.get("NBI_PORT")}/devices/${encodeURIComponent(
|
||||
task.device
|
||||
)}/tasks${connectionRequest ? "?connection_request" : ""}`
|
||||
);
|
||||
options.method = "POST";
|
||||
const _http = options.protocol === "https:" ? https : http;
|
||||
@@ -210,7 +212,9 @@ function updateTags(deviceId, tags) {
|
||||
([tag, onOff]) =>
|
||||
new Promise((resolve, reject) => {
|
||||
const options = url.parse(
|
||||
`${config.server.nbi}devices/${encodeURIComponent(
|
||||
`http://127.0.0.1:${config.get(
|
||||
"NBI_PORT"
|
||||
)}/devices/${encodeURIComponent(
|
||||
deviceId
|
||||
)}/tags/${encodeURIComponent(tag)}`
|
||||
);
|
||||
@@ -234,7 +238,9 @@ function updateTags(deviceId, tags) {
|
||||
function ping(host) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const options = url.parse(
|
||||
`${config.server.nbi}ping/${encodeURIComponent(host)}`
|
||||
`http://127.0.0.1:${config.get("NBI_PORT")}/ping/${encodeURIComponent(
|
||||
host
|
||||
)}`
|
||||
);
|
||||
|
||||
const _http = options.protocol === "https:" ? https : http;
|
||||
@@ -294,7 +300,9 @@ function putResource(resource, id, data) {
|
||||
const resourceUrl =
|
||||
resource === "virtualParameters" ? "virtual_parameters" : resource;
|
||||
const options = url.parse(
|
||||
`${config.server.nbi}${resourceUrl}/${encodeURIComponent(id)}`
|
||||
`http://127.0.0.1:${config.get(
|
||||
"NBI_PORT"
|
||||
)}/${resourceUrl}/${encodeURIComponent(id)}`
|
||||
);
|
||||
|
||||
options.method = "PUT";
|
||||
@@ -343,7 +351,9 @@ function putResource(resource, id, data) {
|
||||
function putFile(id, metadata, fileStream) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const options = url.parse(
|
||||
`${config.server.nbi}files/${encodeURIComponent(id)}`
|
||||
`http://127.0.0.1:${config.get("NBI_PORT")}/files/${encodeURIComponent(
|
||||
id
|
||||
)}`
|
||||
);
|
||||
|
||||
options.method = "PUT";
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
const fs = require("fs");
|
||||
const configFile = JSON.parse(fs.readFileSync("./config/config-ui.json"));
|
||||
|
||||
const config = loadConfig(configFile);
|
||||
|
||||
function getClientConfig() {
|
||||
return Object.assign({}, config, {
|
||||
server: null,
|
||||
auth: null,
|
||||
permissions: null
|
||||
});
|
||||
}
|
||||
|
||||
function loadConfig(conf, root = {}) {
|
||||
for (const [key, value] of Object.entries(conf)) {
|
||||
const keys = key.split(".");
|
||||
let ref = root;
|
||||
while (keys.length > 1) {
|
||||
const k = keys.shift();
|
||||
if (typeof ref[k] !== "object") ref[k] = {};
|
||||
ref = ref[k];
|
||||
}
|
||||
|
||||
if (typeof value === "object") {
|
||||
if (typeof ref[keys[0]] !== "object") ref[keys[0]] = {};
|
||||
loadConfig(value, ref[keys[0]]);
|
||||
} else {
|
||||
ref[keys[0]] = value;
|
||||
}
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
module.exports = Object.assign({}, config, { getClientConfig });
|
||||
+6
-17
@@ -1,7 +1,7 @@
|
||||
"use strict";
|
||||
|
||||
const mongodb = require("mongodb");
|
||||
const config = require("./config");
|
||||
const config = require("../config");
|
||||
const mongodbFunctions = require("./mongodb-functions");
|
||||
const expression = require("../common/expression");
|
||||
|
||||
@@ -9,24 +9,13 @@ const CACHE_TTL = 300000;
|
||||
|
||||
let _clientPromise = null;
|
||||
|
||||
const RESOURCE_DB = {
|
||||
devices: "genieacs",
|
||||
faults: "genieacs",
|
||||
tasks: "genieacs",
|
||||
presets: "genieacs",
|
||||
provisions: "genieacs",
|
||||
virtualParameters: "genieacs",
|
||||
files: "genieacs",
|
||||
cache: "genieacs-ui"
|
||||
};
|
||||
|
||||
const RESOURCE_COLLECTION = {
|
||||
files: "fs.files"
|
||||
};
|
||||
|
||||
function ensureIndexes(client) {
|
||||
client
|
||||
.db(RESOURCE_DB["cache"])
|
||||
.db()
|
||||
.collection("cache")
|
||||
.createIndex({ expire: 1 }, { expireAfterSeconds: 0 });
|
||||
}
|
||||
@@ -34,7 +23,7 @@ function ensureIndexes(client) {
|
||||
function getClient() {
|
||||
if (!_clientPromise) {
|
||||
_clientPromise = new Promise((resolve, reject) => {
|
||||
const CONNECTION_URL = config.server.mongodbConnectionUrl;
|
||||
const CONNECTION_URL = config.get("MONGODB_CONNECTION_URL");
|
||||
mongodb.MongoClient.connect(
|
||||
CONNECTION_URL,
|
||||
{ useNewUrlParser: true },
|
||||
@@ -54,7 +43,7 @@ function cache(key, valueGetter, ttl) {
|
||||
return new Promise((resolve, reject) => {
|
||||
getClient()
|
||||
.then(client => {
|
||||
const collection = client.db(RESOURCE_DB["cache"]).collection("cache");
|
||||
const collection = client.db().collection("cache");
|
||||
collection.findOne({ _id: key }, (err, doc) => {
|
||||
if (err) return void reject(err);
|
||||
if (doc != null) return void resolve(JSON.parse(doc.value));
|
||||
@@ -103,7 +92,7 @@ function query(resource, filter, options, callback) {
|
||||
return new Promise((resolve, reject) => {
|
||||
getClient().then(client => {
|
||||
const collection = client
|
||||
.db(RESOURCE_DB[resource])
|
||||
.db()
|
||||
.collection(RESOURCE_COLLECTION[resource] || resource);
|
||||
const cursor = collection.find(q);
|
||||
if (options.projection) {
|
||||
@@ -188,7 +177,7 @@ function count(resource, filter) {
|
||||
return new Promise((resolve, reject) => {
|
||||
getClient().then(client => {
|
||||
const collection = client
|
||||
.db(RESOURCE_DB[resource])
|
||||
.db()
|
||||
.collection(RESOURCE_COLLECTION[resource] || resource);
|
||||
collection.find(q).count((err, c) => {
|
||||
if (err) reject(err);
|
||||
|
||||
Reference in new issue
Block a user