From 8533ee7f2ebf5d5576bb4f1e970fba43810db809 Mon Sep 17 00:00:00 2001 From: Zaid Abdulla Date: Wed, 23 Oct 2019 18:32:32 -0700 Subject: [PATCH] UI authentication improvements --- lib/common/authorizer.ts | 4 +++ lib/ui.ts | 60 ++++++++++++++++++++++++---------------- lib/ui/api-functions.ts | 6 ++-- lib/ui/api.ts | 2 +- 4 files changed, 44 insertions(+), 28 deletions(-) diff --git a/lib/common/authorizer.ts b/lib/common/authorizer.ts index 81c0b9f..793df6b 100644 --- a/lib/common/authorizer.ts +++ b/lib/common/authorizer.ts @@ -141,4 +141,8 @@ export default class Authorizer { this.validatorCache.set(resource, validator); return validator; } + + public getPermissionSets(): PermissionSet[] { + return this.permissionSets; + } } diff --git a/lib/ui.ts b/lib/ui.ts index 6547194..c5a15c7 100644 --- a/lib/ui.ts +++ b/lib/ui.ts @@ -31,7 +31,7 @@ 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 { authLocal } from "./ui/api-functions"; import * as init from "./init"; import { version as VERSION } from "../package.json"; @@ -47,16 +47,6 @@ 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; }); @@ -73,14 +63,39 @@ koa.use( koaJwt({ secret: JWT_SECRET, passthrough: true, - cookie: JWT_COOKIE + cookie: JWT_COOKIE, + isRevoked: async (ctx, token) => { + if (token["authMethod"] === "local") { + return !localCache.getUsers(ctx.state.configSnapshot)[ + token["username"] + ]; + } + + return true; + } }) ); 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([]); + const permissionSets: PermissionSet[] = []; + + if (ctx.state.user && ctx.state.user.username) { + const allPermissions = localCache.getPermissions(ctx.state.configSnapshot); + + let user; + if (ctx.state.user.authMethod === "local") { + user = localCache.getUsers(ctx.state.configSnapshot)[ + ctx.state.user.username + ]; + } else { + throw new Error("Invalid auth method"); + } + + for (const role of (user.roles || []).sort()) + permissionSets.push(Object.values(allPermissions[role] || {})); + } + + ctx.state.authorizer = new Authorizer(permissionSets); return next(); }); @@ -103,9 +118,9 @@ router.post("/login", async ctx => { method: null }; - function success(method): void { - log.method = method; - const token = jwt.sign({ username }, JWT_SECRET); + function success(authMethod): void { + log.method = authMethod; + const token = jwt.sign({ username, authMethod }, JWT_SECRET); ctx.cookies.set(JWT_COOKIE, token, { sameSite: "lax" }); ctx.body = JSON.stringify(token); logger.accessInfo(log); @@ -118,9 +133,8 @@ router.post("/login", async ctx => { logger.accessWarn(log); } - const roles = await authSimple(ctx.state.configSnapshot, username, password); - - if (roles) return void success("simple"); + if (await authLocal(ctx.state.configSnapshot, username, password)) + return void success("local"); failure(); }); @@ -192,9 +206,7 @@ router.post("/init", async ctx => { }); router.get("/", async ctx => { - let permissionSets = []; - if (ctx.state.user && ctx.state.user.username) - permissionSets = getPermissionSets(ctx); + const permissionSets = ctx.state.authorizer.getPermissionSets(); let wizard = ""; if (!Object.keys(localCache.getUsers(ctx.state.configSnapshot)).length) diff --git a/lib/ui/api-functions.ts b/lib/ui/api-functions.ts index 28b8dd6..8fb3fbb 100644 --- a/lib/ui/api-functions.ts +++ b/lib/ui/api-functions.ts @@ -168,15 +168,15 @@ export async function putResource(resource, id, data): Promise { await del("presets_hash"); } -export function authSimple(snapshot, username, password): Promise { +export function authLocal(snapshot, username, password): Promise { return new Promise((resolve, reject) => { const users = getUsers(snapshot); const user = users[username]; if (!user || !user.password) return void resolve(null); hashPassword(password, user.salt) .then(hash => { - if (hash === user.password) resolve(user.roles); - else resolve(null); + if (hash === user.password) resolve(true); + else resolve(false); }) .catch(reject); }); diff --git a/lib/ui/api.ts b/lib/ui/api.ts index b78fd38..c452840 100644 --- a/lib/ui/api.ts +++ b/lib/ui/api.ts @@ -577,7 +577,7 @@ router.put("/users/:id/password", async (ctx, next) => { if (!ctx.state.user) { // User not logged in if ( - !(await apiFunctions.authSimple( + !(await apiFunctions.authLocal( ctx.state.configSnapshot, username, ctx.request.body.authPassword