/home/techb158/cosmic.abdallabala.com/src/security
Edit: /home/techb158/cosmic.abdallabala.com/src/security/accessControl.js (12143B)
const { list, findById, insert, update, audit } = require("../storage/jsonDatabase.js");
const PERMISSIONS = [
"project:read",
"risk:read",
"risk:write",
"risk:delete",
"mitigation:write",
"gate:read",
"gate:evaluate",
"gate:review",
"gate:approve",
"report:export",
"integration:read",
"integration:write",
"integration:sync",
"oauth:manage",
"user:read",
"user:write",
"audit:read",
"metrics:write"
];
const ROLE_TEMPLATES = {
"ROLE-ADMIN": {
name: "System Admin",
permissions: ["*"]
},
"ROLE-PM": {
name: "Project Manager",
permissions: ["project:read", "risk:read", "risk:write", "mitigation:write", "gate:read", "gate:evaluate", "report:export", "integration:read", "integration:sync", "audit:read"]
},
"ROLE-RISK-OWNER": {
name: "Risk Owner",
permissions: ["project:read", "risk:read", "risk:write", "mitigation:write", "gate:read"]
},
"ROLE-AI-ENGINEER": {
name: "AI Engineer / Data Scientist",
permissions: ["project:read", "risk:read", "mitigation:write", "metrics:write", "gate:read", "report:export"]
},
"ROLE-GOV-REVIEWER": {
name: "Governance Reviewer",
permissions: ["project:read", "risk:read", "gate:read", "gate:evaluate", "gate:review", "report:export", "audit:read"]
},
"ROLE-LEGAL-ETHICS": {
name: "Legal / Ethics Reviewer",
permissions: ["project:read", "risk:read", "mitigation:write", "gate:read", "gate:review", "report:export", "audit:read"]
},
"ROLE-INTEGRATION-ADMIN": {
name: "Integration Admin",
permissions: ["project:read", "integration:read", "integration:write", "integration:sync", "oauth:manage", "report:export", "audit:read"]
},
"ROLE-VIEWER": {
name: "Read-only Viewer",
permissions: ["project:read", "risk:read", "gate:read", "integration:read", "report:export"]
}
};
function safeParsePermissions(value) {
if (Array.isArray(value)) return value;
if (!value) return [];
try {
const parsed = JSON.parse(value);
return Array.isArray(parsed) ? parsed : [];
} catch (_error) {
return String(value).split(",").map(item => item.trim()).filter(Boolean);
}
}
function unique(values) {
return Array.from(new Set(values.filter(Boolean)));
}
function normalizePermissions(value) {
const permissions = unique(safeParsePermissions(value));
if (permissions.includes("*")) return ["*"];
const unknown = permissions.filter(item => !PERMISSIONS.includes(item));
if (unknown.length) throw new Error(`Unknown permission(s): ${unknown.join(", ")}`);
return permissions;
}
function toRoleView(row) {
return {
id: row.id,
name: row.name,
permissions: normalizePermissions(row.permissions_json),
description: row.description || "",
createdAt: row.created_at,
updatedAt: row.updated_at
};
}
function toUserView(database, row) {
const role = findById(database, "roles", row.role_id);
const roleView = role ? toRoleView(role) : null;
return {
id: row.id,
displayName: row.display_name,
email: row.email,
roleId: row.role_id,
roleName: roleView ? roleView.name : "Unknown role",
permissions: roleView ? roleView.permissions : [],
active: row.active !== false,
createdAt: row.created_at,
updatedAt: row.updated_at
};
}
class AuthorizationError extends Error {
constructor(message, details = {}) {
super(message);
this.name = "AuthorizationError";
this.status = 403;
this.details = details;
}
}
class AccessControlService {
constructor(database) {
this.database = database;
}
listPermissions() {
return PERMISSIONS.slice();
}
ensureDefaultRolesAndUsers(actorUserId = "USER-SYSTEM") {
return this.database.transaction(db => {
const roles = list(db, "roles");
Object.entries(ROLE_TEMPLATES).forEach(([id, template]) => {
const existing = roles.find(role => role.id === id);
const row = {
id,
name: template.name,
permissions_json: JSON.stringify(template.permissions),
description: template.description || "Built-in COSMIC AI-Risk role."
};
if (existing) {
update(db, "roles", id, row);
} else {
insert(db, "roles", row, "ROLE");
}
});
const users = list(db, "users");
const defaultUsers = [
{ id: "USER-SYSTEM", display_name: "System", email: "system@cosmic.local", role_id: "ROLE-ADMIN", active: true },
{ id: "USER-PM", display_name: "Project Manager", email: "pm@cosmic.local", role_id: "ROLE-PM", active: true },
{ id: "USER-RISK", display_name: "Risk Owner", email: "risk.owner@cosmic.local", role_id: "ROLE-RISK-OWNER", active: true },
{ id: "USER-ENGINEER", display_name: "AI Engineer", email: "ai.engineer@cosmic.local", role_id: "ROLE-AI-ENGINEER", active: true },
{ id: "USER-GOV", display_name: "Governance Reviewer", email: "governance@cosmic.local", role_id: "ROLE-GOV-REVIEWER", active: true },
{ id: "USER-LEGAL", display_name: "Legal Ethics Reviewer", email: "legal.ethics@cosmic.local", role_id: "ROLE-LEGAL-ETHICS", active: true },
{ id: "USER-INTEGRATION", display_name: "Integration Admin", email: "integration.admin@cosmic.local", role_id: "ROLE-INTEGRATION-ADMIN", active: true },
{ id: "USER-VIEWER", display_name: "Read-only Viewer", email: "viewer@cosmic.local", role_id: "ROLE-VIEWER", active: true }
];
defaultUsers.forEach(user => {
if (!users.find(item => item.id === user.id)) {
insert(db, "users", user, "USER");
}
});
audit(db, {
project_id: "system",
actor_user_id: actorUserId,
entity_type: "AccessControl",
entity_id: "defaults",
action: "seed-defaults",
after_json: { roleCount: Object.keys(ROLE_TEMPLATES).length, userCount: defaultUsers.length }
});
return {
roles: list(db, "roles").map(toRoleView),
users: list(db, "users").map(user => toUserView(db, user))
};
});
}
listRoles() {
const db = this.database.read();
return list(db, "roles").map(toRoleView).sort((a, b) => a.name.localeCompare(b.name));
}
getRole(roleId) {
const db = this.database.read();
const role = findById(db, "roles", roleId);
return role ? toRoleView(role) : null;
}
listUsers() {
const db = this.database.read();
return list(db, "users").map(user => toUserView(db, user)).sort((a, b) => a.displayName.localeCompare(b.displayName));
}
getUser(userId) {
const db = this.database.read();
const user = findById(db, "users", userId);
return user ? toUserView(db, user) : null;
}
createUser(payload, actorUserId = "USER-SYSTEM") {
if (!payload || typeof payload !== "object") throw new Error("User payload is required");
const displayName = String(payload.displayName || payload.display_name || "").trim();
const email = String(payload.email || "").trim();
const roleId = String(payload.roleId || payload.role_id || "ROLE-VIEWER").trim();
if (!displayName) throw new Error("displayName is required");
if (!email || !email.includes("@")) throw new Error("valid email is required");
return this.database.transaction(db => {
if (!findById(db, "roles", roleId)) throw new Error(`Role not found: ${roleId}`);
const user = insert(db, "users", {
display_name: displayName,
email,
role_id: roleId,
active: payload.active !== false
}, "USER");
audit(db, {
project_id: "system",
actor_user_id: actorUserId,
entity_type: "User",
entity_id: user.id,
action: "create",
after_json: user
});
return toUserView(db, user);
});
}
updateUser(userId, payload, actorUserId = "USER-SYSTEM") {
if (!payload || typeof payload !== "object") throw new Error("User patch payload is required");
return this.database.transaction(db => {
const before = findById(db, "users", userId);
if (!before) return null;
const patch = {};
if (payload.displayName !== undefined || payload.display_name !== undefined) patch.display_name = String(payload.displayName || payload.display_name || "").trim();
if (payload.email !== undefined) patch.email = String(payload.email || "").trim();
if (payload.roleId !== undefined || payload.role_id !== undefined) {
const roleId = String(payload.roleId || payload.role_id || "").trim();
if (!findById(db, "roles", roleId)) throw new Error(`Role not found: ${roleId}`);
patch.role_id = roleId;
}
if (payload.active !== undefined) patch.active = Boolean(payload.active);
const user = update(db, "users", userId, patch);
audit(db, {
project_id: "system",
actor_user_id: actorUserId,
entity_type: "User",
entity_id: userId,
action: "update",
before_json: before,
after_json: user
});
return toUserView(db, user);
});
}
createRole(payload, actorUserId = "USER-SYSTEM") {
if (!payload || typeof payload !== "object") throw new Error("Role payload is required");
const name = String(payload.name || "").trim();
if (!name) throw new Error("name is required");
const permissions = normalizePermissions(payload.permissions || payload.permissions_json || []);
return this.database.transaction(db => {
const role = insert(db, "roles", {
name,
permissions_json: JSON.stringify(permissions),
description: payload.description || "Custom COSMIC AI-Risk role."
}, "ROLE");
audit(db, {
project_id: "system",
actor_user_id: actorUserId,
entity_type: "Role",
entity_id: role.id,
action: "create",
after_json: role
});
return toRoleView(role);
});
}
updateRole(roleId, payload, actorUserId = "USER-SYSTEM") {
if (!payload || typeof payload !== "object") throw new Error("Role patch payload is required");
return this.database.transaction(db => {
const before = findById(db, "roles", roleId);
if (!before) return null;
const patch = {};
if (payload.name !== undefined) patch.name = String(payload.name || "").trim();
if (payload.description !== undefined) patch.description = String(payload.description || "").trim();
if (payload.permissions !== undefined || payload.permissions_json !== undefined) {
patch.permissions_json = JSON.stringify(normalizePermissions(payload.permissions || payload.permissions_json || []));
}
const role = update(db, "roles", roleId, patch);
audit(db, {
project_id: "system",
actor_user_id: actorUserId,
entity_type: "Role",
entity_id: roleId,
action: "update",
before_json: before,
after_json: role
});
return toRoleView(role);
});
}
hasPermission(userId, permission) {
const user = this.getUser(userId || "USER-SYSTEM");
if (!user || user.active === false) return false;
return user.permissions.includes("*") || user.permissions.includes(permission);
}
requirePermission(userId, permission, context = {}) {
const user = this.getUser(userId || "USER-SYSTEM");
if (!user) throw new AuthorizationError(`Unknown actor user: ${userId}`, { permission, context });
if (user.active === false) throw new AuthorizationError(`Inactive actor user: ${userId}`, { permission, context });
if (user.permissions.includes("*") || user.permissions.includes(permission)) return user;
throw new AuthorizationError(`Permission denied: ${permission}`, { actorUserId: userId, permission, role: user.roleName, context });
}
getAccessSummary(userId) {
const user = this.getUser(userId || "USER-SYSTEM");
if (!user) return null;
return {
actor: user,
permissions: this.listPermissions(),
roles: this.listRoles(),
users: this.listUsers()
};
}
}
module.exports = {
AccessControlService,
AuthorizationError,
PERMISSIONS,
ROLE_TEMPLATES,
normalizePermissions
};