RBAC changes + fix rev console and process view in shellcode
This commit is contained in:
@@ -0,0 +1,249 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import {
|
||||
ALL_FEATURES,
|
||||
canUserAccessFeature,
|
||||
createUser,
|
||||
deleteUser,
|
||||
getUserByUsername,
|
||||
getUserFeaturePermissions,
|
||||
resetUserFeaturePermissions,
|
||||
setUserFeaturePermission,
|
||||
setUserFeaturePermissions,
|
||||
type FeatureName,
|
||||
} from "./users";
|
||||
|
||||
const createdUserIds: number[] = [];
|
||||
|
||||
async function createTempUser(role: "viewer" | "operator" | "admin") {
|
||||
const username = `fp_${role.slice(0, 3)}_${Date.now().toString(36)}${Math.floor(Math.random() * 1000).toString(36)}`;
|
||||
const result = await createUser(username, "Aa1!VeryLongTestPassword_2026", role, "test");
|
||||
expect(result.success).toBe(true);
|
||||
const user = getUserByUsername(username);
|
||||
expect(user).not.toBeNull();
|
||||
createdUserIds.push(user!.id);
|
||||
return user!;
|
||||
}
|
||||
|
||||
function cleanupCreatedUsers() {
|
||||
while (createdUserIds.length > 0) {
|
||||
const id = createdUserIds.pop();
|
||||
if (typeof id === "number") {
|
||||
deleteUser(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
describe("feature permissions", () => {
|
||||
test("ALL_FEATURES contains the expected feature keys", () => {
|
||||
expect(ALL_FEATURES).toEqual([
|
||||
"console",
|
||||
"remote_desktop",
|
||||
"hvnc",
|
||||
"webcam",
|
||||
"file_browser",
|
||||
"processes",
|
||||
"keylogger",
|
||||
"voice",
|
||||
]);
|
||||
});
|
||||
|
||||
test("admin always has every feature regardless of stored overrides", async () => {
|
||||
try {
|
||||
const admin = await createTempUser("admin");
|
||||
|
||||
for (const f of ALL_FEATURES) {
|
||||
expect(canUserAccessFeature(admin.id, admin.role, f)).toBe(true);
|
||||
}
|
||||
|
||||
// Even an explicit deny is ignored for admin role.
|
||||
setUserFeaturePermission(admin.id, "webcam", false);
|
||||
expect(canUserAccessFeature(admin.id, "admin", "webcam")).toBe(true);
|
||||
} finally {
|
||||
cleanupCreatedUsers();
|
||||
}
|
||||
});
|
||||
|
||||
test("viewer is denied every feature regardless of stored overrides", async () => {
|
||||
try {
|
||||
const viewer = await createTempUser("viewer");
|
||||
|
||||
for (const f of ALL_FEATURES) {
|
||||
expect(canUserAccessFeature(viewer.id, viewer.role, f)).toBe(false);
|
||||
}
|
||||
|
||||
// Granting access to a viewer at the per-feature level does not promote them.
|
||||
setUserFeaturePermission(viewer.id, "console", true);
|
||||
expect(canUserAccessFeature(viewer.id, "viewer", "console")).toBe(false);
|
||||
} finally {
|
||||
cleanupCreatedUsers();
|
||||
}
|
||||
});
|
||||
|
||||
test("operator defaults to having every feature enabled", async () => {
|
||||
try {
|
||||
const operator = await createTempUser("operator");
|
||||
|
||||
for (const f of ALL_FEATURES) {
|
||||
expect(canUserAccessFeature(operator.id, operator.role, f)).toBe(true);
|
||||
}
|
||||
} finally {
|
||||
cleanupCreatedUsers();
|
||||
}
|
||||
});
|
||||
|
||||
test("setUserFeaturePermission disables a single feature for an operator", async () => {
|
||||
try {
|
||||
const operator = await createTempUser("operator");
|
||||
|
||||
const result = setUserFeaturePermission(operator.id, "webcam", false);
|
||||
expect(result.success).toBe(true);
|
||||
|
||||
expect(canUserAccessFeature(operator.id, operator.role, "webcam")).toBe(false);
|
||||
// Other features remain enabled.
|
||||
expect(canUserAccessFeature(operator.id, operator.role, "console")).toBe(true);
|
||||
expect(canUserAccessFeature(operator.id, operator.role, "file_browser")).toBe(true);
|
||||
} finally {
|
||||
cleanupCreatedUsers();
|
||||
}
|
||||
});
|
||||
|
||||
test("setUserFeaturePermission can re-enable a previously denied feature", async () => {
|
||||
try {
|
||||
const operator = await createTempUser("operator");
|
||||
|
||||
setUserFeaturePermission(operator.id, "hvnc", false);
|
||||
expect(canUserAccessFeature(operator.id, operator.role, "hvnc")).toBe(false);
|
||||
|
||||
setUserFeaturePermission(operator.id, "hvnc", true);
|
||||
expect(canUserAccessFeature(operator.id, operator.role, "hvnc")).toBe(true);
|
||||
} finally {
|
||||
cleanupCreatedUsers();
|
||||
}
|
||||
});
|
||||
|
||||
test("setUserFeaturePermissions applies a bulk update", async () => {
|
||||
try {
|
||||
const operator = await createTempUser("operator");
|
||||
|
||||
const result = setUserFeaturePermissions(operator.id, {
|
||||
webcam: false,
|
||||
file_browser: false,
|
||||
keylogger: true,
|
||||
});
|
||||
expect(result.success).toBe(true);
|
||||
|
||||
expect(canUserAccessFeature(operator.id, operator.role, "webcam")).toBe(false);
|
||||
expect(canUserAccessFeature(operator.id, operator.role, "file_browser")).toBe(false);
|
||||
expect(canUserAccessFeature(operator.id, operator.role, "keylogger")).toBe(true);
|
||||
// Untouched features keep their default (allowed).
|
||||
expect(canUserAccessFeature(operator.id, operator.role, "console")).toBe(true);
|
||||
} finally {
|
||||
cleanupCreatedUsers();
|
||||
}
|
||||
});
|
||||
|
||||
test("setUserFeaturePermissions ignores unknown feature keys", async () => {
|
||||
try {
|
||||
const operator = await createTempUser("operator");
|
||||
|
||||
const result = setUserFeaturePermissions(operator.id, {
|
||||
webcam: false,
|
||||
// @ts-expect-error intentionally unknown feature key
|
||||
not_a_feature: true,
|
||||
});
|
||||
expect(result.success).toBe(true);
|
||||
|
||||
const perms = getUserFeaturePermissions(operator.id);
|
||||
expect(perms.webcam).toBe(false);
|
||||
// Unknown keys never get persisted.
|
||||
expect((perms as any).not_a_feature).toBeUndefined();
|
||||
} finally {
|
||||
cleanupCreatedUsers();
|
||||
}
|
||||
});
|
||||
|
||||
test("setUserFeaturePermission rejects invalid feature names", async () => {
|
||||
try {
|
||||
const operator = await createTempUser("operator");
|
||||
|
||||
const result = setUserFeaturePermission(
|
||||
operator.id,
|
||||
"not_a_feature" as FeatureName,
|
||||
false,
|
||||
);
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.error).toContain("Invalid feature");
|
||||
} finally {
|
||||
cleanupCreatedUsers();
|
||||
}
|
||||
});
|
||||
|
||||
test("getUserFeaturePermissions returns role-derived values", async () => {
|
||||
try {
|
||||
const admin = await createTempUser("admin");
|
||||
const operator = await createTempUser("operator");
|
||||
const viewer = await createTempUser("viewer");
|
||||
|
||||
const adminPerms = getUserFeaturePermissions(admin.id);
|
||||
const operatorPerms = getUserFeaturePermissions(operator.id);
|
||||
const viewerPerms = getUserFeaturePermissions(viewer.id);
|
||||
|
||||
for (const f of ALL_FEATURES) {
|
||||
expect(adminPerms[f]).toBe(true);
|
||||
expect(operatorPerms[f]).toBe(true);
|
||||
expect(viewerPerms[f]).toBe(false);
|
||||
}
|
||||
|
||||
// After a deny, the operator's map reflects it.
|
||||
setUserFeaturePermission(operator.id, "processes", false);
|
||||
const updated = getUserFeaturePermissions(operator.id);
|
||||
expect(updated.processes).toBe(false);
|
||||
expect(updated.console).toBe(true);
|
||||
} finally {
|
||||
cleanupCreatedUsers();
|
||||
}
|
||||
});
|
||||
|
||||
test("resetUserFeaturePermissions restores defaults (all enabled for operator)", async () => {
|
||||
try {
|
||||
const operator = await createTempUser("operator");
|
||||
|
||||
setUserFeaturePermissions(operator.id, {
|
||||
webcam: false,
|
||||
file_browser: false,
|
||||
keylogger: false,
|
||||
});
|
||||
expect(canUserAccessFeature(operator.id, operator.role, "webcam")).toBe(false);
|
||||
|
||||
const result = resetUserFeaturePermissions(operator.id);
|
||||
expect(result.success).toBe(true);
|
||||
|
||||
for (const f of ALL_FEATURES) {
|
||||
expect(canUserAccessFeature(operator.id, operator.role, f)).toBe(true);
|
||||
}
|
||||
} finally {
|
||||
cleanupCreatedUsers();
|
||||
}
|
||||
});
|
||||
|
||||
test("getUserFeaturePermissions for a missing user returns all-false", () => {
|
||||
const perms = getUserFeaturePermissions(0);
|
||||
for (const f of ALL_FEATURES) {
|
||||
expect(perms[f]).toBe(false);
|
||||
}
|
||||
});
|
||||
|
||||
test("feature denials are scoped to the affected user", async () => {
|
||||
try {
|
||||
const a = await createTempUser("operator");
|
||||
const b = await createTempUser("operator");
|
||||
|
||||
setUserFeaturePermission(a.id, "webcam", false);
|
||||
|
||||
expect(canUserAccessFeature(a.id, a.role, "webcam")).toBe(false);
|
||||
expect(canUserAccessFeature(b.id, b.role, "webcam")).toBe(true);
|
||||
} finally {
|
||||
cleanupCreatedUsers();
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user