Use expressions as validators

This commit is contained in:
Shweelan Samson authored and Zaid Abdulla committed 2019-06-05 00:17:36 -07:00
1 parent 08e3933c4a
commit fdda68434b
7 files changed
+87 -90

No files matched your search

+41 -30
View File
@@ -1,8 +1,5 @@
import pass from "./validators/pass";
import test from "./validators/test";
import { PermissionSet } from "../types";
export const validators = { pass, test };
import { PermissionSet, Expression } from "../types";
import { evaluate } from "./expression";
export default class Authorizer {
private permissionSets: PermissionSet[];
@@ -46,20 +43,16 @@ export default class Authorizer {
if (this.validatorCache.has(resource))
return this.validatorCache.get(resource);
let funcs = {};
const validators: Expression[] = [];
for (const permissionSet of this.permissionSets) {
for (const perm of permissionSet) {
if (perm[resourceType]) {
if (perm[resourceType].access >= 3) {
if (perm[resourceType].validate) {
for (const [k, v] of Object.entries(perm[resourceType].validate))
funcs[k] = v;
}
} else {
funcs = {};
}
}
if (
perm[resourceType] &&
perm[resourceType].access >= 3 &&
perm[resourceType].validate
)
validators.push(perm[resourceType].validate);
}
}
@@ -68,23 +61,41 @@ export default class Authorizer {
mutation: any,
any: any
): boolean => {
let valid = false;
for (const [k, v] of Object.entries(funcs)) {
if (v) {
const res = validators[k](
resourceType,
resource,
mutationType,
mutation,
any
);
if (!validators.length) return false;
if (res > 0) valid = true;
else if (res < 0) return false;
const object = {
mutationType,
mutation,
resourceType,
object: resource,
options: any
};
const valueFunction = (paramName): any => {
const entry = paramName.split(".", 1)[0];
paramName = paramName.slice(entry.length + 1);
let value = null;
if (["mutation", "options"].includes(entry)) {
value = object[entry];
for (const seg of paramName.split(".")) {
// typeof null is "object"
if (value != null && typeof value !== "object") value = null;
else value = value[seg];
if (value == null) break;
}
} else if (object[entry]) {
value = object[entry][paramName];
}
}
return valid;
return value;
};
const res = evaluate(
validators.length > 1 ? ["OR", validators] : validators[0],
valueFunction,
Date.now()
);
return !Array.isArray(res) && !!res;
};
this.validatorCache.set(resource, validator);
-3
View File
@@ -1,3 +0,0 @@
export default function pass(): number {
return 1;
}
-12
View File
@@ -1,12 +0,0 @@
export default function test(resourceType, resource): number {
if (resourceType === "devices") {
if (resource["Tags.test"] && resource["Tags.test"].value[0]) return 1;
} else if (resourceType === "presets") {
if (resource["_id"].startsWith("test-"))
// TODO check "test" tag too
return 1;
} else if (resourceType === "provisions") {
if (resource["_id"].startsWith("test-")) return 1;
}
return -1;
}
+7 -7
View File
@@ -35,7 +35,7 @@ interface Permissions {
[resource: string]: {
access: number;
filter: Expression;
validate: { [validator: string]: boolean };
validate?: Expression;
};
};
};
@@ -351,15 +351,15 @@ function refresh(callback): void {
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
filter: parse(p.filter || "true")
};
for (const v of (p.validate || "").split(","))
validate[v.trim()] = true;
if (p.validate) {
permissions[p.role][p.access][p.resource].validate = parse(
p.validate
);
}
}
resolve(permissions);
+1 -3
View File
@@ -303,9 +303,7 @@ export interface Preset {
export type PermissionSet = {
[resource: string]: {
access: number;
validate?: {
[validator: string]: boolean;
};
validate?: Expression;
filter: Expression;
};
}[];
+20 -19
View File
@@ -3,30 +3,32 @@ import { MongoClient } from "mongodb";
import { get } from "../lib/config";
import { hashPassword, generateSalt } from "../lib/auth";
const pass = "true";
const permissionsData = [
{ role: "admin", resource: "devices", access: 3, validate: ["pass"] },
{ role: "admin", resource: "faults", access: 3, validate: ["pass"] },
{ role: "admin", resource: "files", access: 3, validate: ["pass"] },
{ role: "admin", resource: "presets", access: 3, validate: ["pass"] },
{ role: "admin", resource: "provisions", access: 3, validate: ["pass"] },
{ role: "admin", resource: "config", access: 3, validate: ["pass"] },
{ role: "admin", resource: "permissions", access: 3, validate: ["pass"] },
{ role: "admin", resource: "users", access: 3, validate: ["pass"] },
{ role: "admin", resource: "devices", access: 3, validate: pass },
{ role: "admin", resource: "faults", access: 3, validate: pass },
{ role: "admin", resource: "files", access: 3, validate: pass },
{ role: "admin", resource: "presets", access: 3, validate: pass },
{ role: "admin", resource: "provisions", access: 3, validate: pass },
{ role: "admin", resource: "config", access: 3, validate: pass },
{ role: "admin", resource: "permissions", access: 3, validate: pass },
{ role: "admin", resource: "users", access: 3, validate: pass },
{
role: "admin",
resource: "virtualParameters",
access: 3,
validate: ["pass"]
validate: pass
},
{ role: "view", resource: "devices", access: 2, validate: [] },
{ role: "view", resource: "faults", access: 2, validate: [] },
{ role: "view", resource: "files", access: 2, validate: [] },
{ role: "view", resource: "presets", access: 2, validate: [] },
{ role: "view", resource: "provisions", access: 2, validate: [] },
{ role: "view", resource: "config", access: 2, validate: [] },
{ role: "view", resource: "users", access: 2, validate: [] },
{ role: "view", resource: "permissions", access: 2, validate: [] },
{ role: "view", resource: "virtualParameters", access: 2, validate: [] }
{ role: "view", resource: "devices", access: 2 },
{ role: "view", resource: "faults", access: 2 },
{ role: "view", resource: "files", access: 2 },
{ role: "view", resource: "presets", access: 2 },
{ role: "view", resource: "provisions", access: 2 },
{ role: "view", resource: "config", access: 2 },
{ role: "view", resource: "users", access: 2 },
{ role: "view", resource: "permissions", access: 2 },
{ role: "view", resource: "virtualParameters", access: 2 }
];
const usersData = [
@@ -194,7 +196,6 @@ async function run(): Promise<void> {
.insertMany(objs)
);
const perms = permissionsData.map(p => {
p.validate = (p.validate || []).join(",");
return Object.assign({ _id: `${p.role}:${p.resource}:${p.access}` }, p);
});
inserts.push(
+18 -16
View File
@@ -9,7 +9,6 @@ import memoize from "../lib/common/memoize";
import putFormComponent from "./put-form-component";
import * as overlay from "./overlay";
import * as smartQuery from "./smart-query";
import { validators } from "../lib/common/authorizer";
import { map, parse, stringify } from "../lib/common/expression-parser";
import filterComponent from "./filter-component";
import { Children, ClosureComponent, Component } from "mithril";
@@ -43,13 +42,7 @@ const attributes = [
type: "combo",
options: ["1: count", "2: read", "3: write"]
},
{
id: "validate",
label: "Validators",
type: "multi",
options: Object.keys(validators),
unsortable: true
}
{ id: "validate", label: "Validate", unsortable: true }
];
const unpackSmartQuery = memoize(query => {
@@ -79,16 +72,25 @@ function putActionHandler(action, _object): Promise<ValidationErrors> {
else if (object.access === "1: count") object.access = 1;
else return void resolve({ access: "Invalid access level" });
object.filter = object.filter || "";
try {
memoizedParse(object.filter);
} catch (err) {
return void resolve({
filter: "Filter must be valid expression"
});
if (object.filter) {
try {
object.filter = stringify(memoizedParse(object.filter));
} catch (err) {
return void resolve({
filter: "Filter must be valid expression"
});
}
}
object.validate = (object.validate || []).join(",");
if (object.validate) {
try {
object.validate = stringify(memoizedParse(object.validate));
} catch (err) {
return void resolve({
validate: "Validate must be valid expression"
});
}
}
const id = `${object.role}:${object.resource}:${object.access}`;