238 lines
6.5 KiB
TypeScript
238 lines
6.5 KiB
TypeScript
/**
|
|
* Copyright 2013-2019 GenieACS Inc.
|
|
*
|
|
* 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 { Z_SYNC_FLUSH } from "zlib";
|
|
import Koa from "koa";
|
|
import Router from "koa-router";
|
|
import * as jwt from "jsonwebtoken";
|
|
import koaStatic from "koa-static";
|
|
import koaCompress from "koa-compress";
|
|
import koaBodyParser from "koa-bodyparser";
|
|
import koaJwt from "koa-jwt";
|
|
import * as config from "./config";
|
|
import api from "./ui/api";
|
|
import Authorizer from "./common/authorizer";
|
|
import * as logger from "./logger";
|
|
import * as localCache from "./local-cache";
|
|
import { PermissionSet } from "./types";
|
|
import { authSimple } from "./ui/api-functions";
|
|
import * as init from "./init";
|
|
import { version as VERSION } from "../package.json";
|
|
|
|
declare module "koa" {
|
|
interface Request {
|
|
body: any;
|
|
}
|
|
}
|
|
|
|
const koa = new Koa();
|
|
const router = new Router();
|
|
|
|
const JWT_SECRET = "" + config.get("UI_JWT_SECRET");
|
|
const JWT_COOKIE = "genieacs-ui-jwt";
|
|
|
|
function getPermissionSets(ctx): PermissionSet[] {
|
|
const allPermissions = localCache.getPermissions(ctx.state.configSnapshot);
|
|
const users = localCache.getUsers(ctx.state.configSnapshot);
|
|
const user = users[ctx.state.user.username];
|
|
const permissionSets = user.roles.map(role =>
|
|
Object.values(allPermissions[role] || {})
|
|
);
|
|
return permissionSets;
|
|
}
|
|
|
|
koa.on("error", async err => {
|
|
throw err;
|
|
});
|
|
|
|
koa.use(async (ctx, next) => {
|
|
const configSnapshot = await localCache.getCurrentSnapshot();
|
|
ctx.state.configSnapshot = configSnapshot;
|
|
ctx.set("X-Config-Snapshot", configSnapshot);
|
|
ctx.set("GenieACS-Version", VERSION);
|
|
return next();
|
|
});
|
|
|
|
koa.use(
|
|
koaJwt({
|
|
secret: JWT_SECRET,
|
|
passthrough: true,
|
|
cookie: JWT_COOKIE
|
|
})
|
|
);
|
|
|
|
koa.use(async (ctx, next) => {
|
|
if (ctx.state.user && ctx.state.user.username)
|
|
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;
|
|
|
|
const log = {
|
|
message: "Log in",
|
|
context: ctx,
|
|
username: username,
|
|
method: null
|
|
};
|
|
|
|
function success(method): void {
|
|
log.method = method;
|
|
const token = jwt.sign({ username }, JWT_SECRET);
|
|
ctx.cookies.set(JWT_COOKIE, token, { sameSite: "lax" });
|
|
ctx.body = JSON.stringify(token);
|
|
logger.accessInfo(log);
|
|
}
|
|
|
|
function failure(): void {
|
|
ctx.status = 400;
|
|
ctx.body = "Incorrect username or password";
|
|
log.message += " failed";
|
|
logger.accessWarn(log);
|
|
}
|
|
|
|
const roles = await authSimple(ctx.state.configSnapshot, username, password);
|
|
|
|
if (roles) return void success("simple");
|
|
|
|
failure();
|
|
});
|
|
|
|
router.post("/logout", async ctx => {
|
|
ctx.cookies.set(JWT_COOKIE); // Delete cookie
|
|
ctx.body = "";
|
|
|
|
logger.accessInfo({
|
|
message: "Log out",
|
|
context: ctx
|
|
});
|
|
});
|
|
|
|
koa.use(async (ctx, next) => {
|
|
if (ctx.request.type === "application/octet-stream")
|
|
ctx.disableBodyParser = true;
|
|
|
|
return next();
|
|
});
|
|
|
|
koa.use(koaBodyParser());
|
|
router.use("/api", api.routes(), api.allowedMethods());
|
|
|
|
router.get("/status", ctx => {
|
|
ctx.body = "OK";
|
|
});
|
|
|
|
router.get("/init", async ctx => {
|
|
const status = await init.getStatus();
|
|
if (Object.keys(localCache.getUsers(ctx.state.configSnapshot)).length) {
|
|
if (!ctx.state.authorizer.hasAccess("users", 3)) status["users"] = false;
|
|
if (!ctx.state.authorizer.hasAccess("permissions", 3))
|
|
status["users"] = false;
|
|
if (!ctx.state.authorizer.hasAccess("config", 3)) {
|
|
status["filters"] = false;
|
|
status["device"] = false;
|
|
status["index"] = false;
|
|
status["overview"] = false;
|
|
}
|
|
if (!ctx.state.authorizer.hasAccess("presets", 3))
|
|
status["presets"] = false;
|
|
if (!ctx.state.authorizer.hasAccess("provisions", 3))
|
|
status["presets"] = false;
|
|
}
|
|
|
|
ctx.body = status;
|
|
});
|
|
|
|
router.post("/init", async ctx => {
|
|
const status = ctx.request.body;
|
|
if (Object.keys(localCache.getUsers(ctx.state.configSnapshot)).length) {
|
|
if (!ctx.state.authorizer.hasAccess("users", 3)) status["users"] = false;
|
|
if (!ctx.state.authorizer.hasAccess("permissions", 3))
|
|
status["users"] = false;
|
|
if (!ctx.state.authorizer.hasAccess("config", 3)) {
|
|
status["filters"] = false;
|
|
status["device"] = false;
|
|
status["index"] = false;
|
|
status["overview"] = false;
|
|
}
|
|
if (!ctx.state.authorizer.hasAccess("presets", 3))
|
|
status["presets"] = false;
|
|
if (!ctx.state.authorizer.hasAccess("provisions", 3))
|
|
status["presets"] = false;
|
|
}
|
|
await init.seed(status);
|
|
ctx.body = "";
|
|
});
|
|
|
|
router.get("/", async ctx => {
|
|
let permissionSets = [];
|
|
if (ctx.state.user && ctx.state.user.username)
|
|
permissionSets = getPermissionSets(ctx);
|
|
|
|
let wizard = "";
|
|
if (!Object.keys(localCache.getUsers(ctx.state.configSnapshot)).length)
|
|
wizard = '<script>window.location.hash = "#!/wizard";</script>';
|
|
|
|
ctx.body = `
|
|
<html>
|
|
<head>
|
|
<title>GenieACS</title>
|
|
<link rel="shortcut icon" type="image/png" href="favicon.png" />
|
|
<link rel="stylesheet" href="app.css">
|
|
</head>
|
|
<body>
|
|
<script>
|
|
window.clientConfig = ${JSON.stringify({
|
|
ui: localCache.getUiConfig(ctx.state.configSnapshot)
|
|
})};
|
|
window.configSnapshot = ${JSON.stringify(ctx.state.configSnapshot)};
|
|
window.genieacsVersion = ${JSON.stringify(VERSION)};
|
|
window.username = ${JSON.stringify(
|
|
ctx.state.user ? ctx.state.user.username : ""
|
|
)};
|
|
window.permissionSets = ${JSON.stringify(permissionSets)};
|
|
</script>
|
|
<script src="app.js"></script>${wizard}
|
|
</body>
|
|
</html>
|
|
`;
|
|
});
|
|
|
|
koa.use(
|
|
koaCompress({
|
|
flush: Z_SYNC_FLUSH
|
|
})
|
|
);
|
|
|
|
koa.use(router.routes());
|
|
koa.use(koaStatic(config.ROOT_DIR + "/public"));
|
|
|
|
export const listener = koa.callback();
|