Add Permissions and Users pages

This commit is contained in:
Shweelan Samson authored and Zaid Abdulla committed 2019-03-04 16:12:29 -08:00
1 parent f878d828eb
commit 3b3369a236
16 files changed
+1223 -77

No files matched your search

+1 -1
View File
@@ -2,7 +2,7 @@ import pass from "./validators/pass";
import test from "./validators/test";
import { PermissionSet } from "../types";
const validators = { pass, test };
export const validators = { pass, test };
export default class Authorizer {
private permissionSets: PermissionSet[];
+28
View File
@@ -33,6 +33,8 @@ export let tasksCollection,
faultsCollection,
filesCollection,
operationsCollection,
permissionsCollection,
usersCollection,
configCollection;
export let client;
@@ -57,6 +59,8 @@ export function connect(callback): void {
virtualParametersCollection = db.collection("virtualParameters");
faultsCollection = db.collection("faults");
operationsCollection = db.collection("operations");
permissionsCollection = db.collection("permissions");
usersCollection = db.collection("users");
configCollection = db.collection("config");
callback();
@@ -931,3 +935,27 @@ export function getConfig(callback): void {
);
});
}
export function getPermissions(
callback: (
err?: Error,
permissions?: {
role: string;
resource: string;
access: number;
filter: string;
validate: string;
}[]
) => void
): void {
permissionsCollection.find().toArray(callback);
}
export function getUsers(
callback: (
err?: Error,
users?: { _id: string; password: string; roles: string }[]
) => void
): void {
usersCollection.find().toArray(callback);
}
+64 -11
View File
@@ -26,7 +26,20 @@ import * as query from "./query";
import * as logger from "./logger";
import * as scheduling from "./scheduling";
import * as expression from "./common/expression";
import { Preset } from "./types";
import { parse } from "./common/expression-parser";
import { Preset, Expression } from "./types";
interface Permissions {
[role: string]: {
[access: number]: {
[resource: string]: {
access: number;
filter: Expression;
validate: { [validator: string]: boolean };
};
};
};
}
const REFRESH = 3000;
const EVICT_TIMEOUT = 60000;
@@ -301,6 +314,48 @@ function refresh(callback): void {
})
);
promises.push(
new Promise((resolve, reject) => {
db.getPermissions((err, perms) => {
if (err) return void reject(err);
const permissions: Permissions = {};
for (const p of perms) {
if (!permissions[p.role]) permissions[p.role] = {};
if (!permissions[p.role][p.access])
permissions[p.role][p.access] = {};
const validate = {};
permissions[p.role][p.access][p.resource] = {
access: p.access,
filter: parse(p.filter || "true"),
validate: validate
};
for (const v of (p.validate || "").split(","))
validate[v.trim()] = true;
}
resolve(permissions);
});
})
);
promises.push(
new Promise((resolve, reject) => {
db.getUsers((err, _users) => {
if (err) return void reject(err);
const users = {};
for (const user of _users) {
users[user._id] = {
password: user.password,
roles: user.roles.split(",").map(s => s.trim())
};
}
resolve(users);
});
})
);
promises.push(
new Promise((resolve, reject) => {
db.getConfig((err, conf) => {
@@ -311,11 +366,7 @@ function refresh(callback): void {
const _config = {};
for (const c of conf) {
if (
c.id.startsWith("auth.") ||
c.id.startsWith("permissions.") ||
c.id.startsWith("ui.")
) {
if (c.id.startsWith("ui.")) {
const keys = c.id.split(".");
let ref = _config;
while (keys.length > 1) {
@@ -343,7 +394,9 @@ function refresh(callback): void {
provisions: res[1],
virtualParameters: res[2],
files: res[3],
config: res[4]
permissions: res[4],
users: res[5],
config: res[6]
};
if (currentSnapshot) {
@@ -465,18 +518,18 @@ export function getConfig(
return expression.evaluate(snapshot.config[key], context, now || Date.now());
}
export function getAuthConfig(snapshotKey): {} {
export function getUsers(snapshotKey): {} {
const snapshot = snapshots.get(snapshotKey);
if (!snapshot) throw new Error("Cache snapshot does not exist");
return snapshot.config.auth;
return snapshot.users;
}
export function getPermissionsConfig(snapshotKey): {} {
export function getPermissions(snapshotKey): {} {
const snapshot = snapshots.get(snapshotKey);
if (!snapshot) throw new Error("Cache snapshot does not exist");
return snapshot.config.permissions;
return snapshot.permissions;
}
export function getUiConfig(snapshotKey): {} {
+7 -31
View File
@@ -9,7 +9,6 @@ import koaJwt from "koa-jwt";
import * as config from "./config";
import api from "./ui/api";
import Authorizer from "./common/authorizer";
import { parse } from "./common/expression-parser";
import * as logger from "./logger";
import * as localCache from "./local-cache";
import { PermissionSet } from "./types";
@@ -27,41 +26,18 @@ const JWT_SECRET = "" + config.get("UI_JWT_SECRET");
const JWT_COOKIE = "genieacs-ui-jwt";
function getPermissionSets(ctx): PermissionSet[] {
const allPermissions = localCache.getPermissionsConfig(
ctx.state.configSnapshot
const allPermissions = localCache.getPermissions(ctx.state.configSnapshot);
const permissionSets = ctx.state.user.roles.map(role =>
Object.values(allPermissions[role] || {})
);
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)) {
p[k] = v = Object.assign({}, v);
if (
[
"devices",
"faults",
"presets",
"provisions",
"virtualParameters",
"files"
].includes(k)
) {
if (v.filter == null || v.filter === "") v.filter = true;
else v.filter = parse(v.filter);
}
}
return p;
});
});
return permissionSets;
}
function authSimple(ctx, username, password): Promise<string[]> {
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);
}
const users = localCache.getUsers(ctx.state.configSnapshot);
const user = users[username];
if (user && user.password === password) return Promise.resolve(user.roles);
return Promise.resolve(null);
}
+10
View File
@@ -65,6 +65,14 @@ export function deleteResource(resource, id): Promise<void> {
case "config":
promise = db.deleteConfig(id);
break;
case "permissions":
promise = db.deletePermission(id);
break;
case "users":
promise = db.deleteUser(id);
break;
}
promise
@@ -183,6 +191,8 @@ export function putResource(resource, id, data): Promise<void> {
else if (resource === "virtualParameters")
promise = db.putVirtualParameter(id, data);
else if (resource === "config") promise = db.putConfig(id, data);
else if (resource === "permissions") promise = db.putPermission(id, data);
else if (resource === "users") promise = db.putUser(id, data);
promise
.then(() => {
+4
View File
@@ -26,6 +26,8 @@ const RESOURCE_IDS = {
files: "_id",
virtualParameters: "_id",
config: "_id",
permissions: "_id",
users: "_id",
faults: "_id",
tasks: "_id"
};
@@ -37,6 +39,8 @@ const resources = {
files: 0 | RESOURCE_DELETE,
virtualParameters: 0 | RESOURCE_DELETE | RESOURCE_PUT,
config: 0 | RESOURCE_DELETE | RESOURCE_PUT,
permissions: 0 | RESOURCE_DELETE | RESOURCE_PUT,
users: 0 | RESOURCE_DELETE | RESOURCE_PUT,
faults: 0 | RESOURCE_DELETE,
tasks: 0
};
+35
View File
@@ -107,6 +107,9 @@ export function query(
: options.projection
);
}
if (resource === "users") cursor.project({ password: 0 });
if (options.skip) cursor.skip(options.skip);
if (options.limit) cursor.limit(options.limit);
@@ -282,6 +285,38 @@ export function deleteConfig(id): Promise<void> {
return deleteResource("config", id);
}
export function putPermission(id, object): Promise<void> {
return putResource("permissions", id, object);
}
export function deletePermission(id): Promise<void> {
return deleteResource("permissions", id);
}
export function putUser(id, object): Promise<void> {
return new Promise((resolve, reject) => {
getClient()
.then(client => {
const collection = client.db().collection("users");
// update instead of replace to keep the password if not set by user
collection.updateOne(
{ _id: id },
{ $set: object },
{ upsert: true },
err => {
if (err) return void reject(err);
resolve();
}
);
})
.catch(reject);
});
}
export function deleteUser(id): Promise<void> {
return deleteResource("users", id);
}
export function putFile(filename, metadata, contentStream): Promise<void> {
return new Promise((resolve, reject) => {
getClient()