Control de modo editor/admin produccion/local

This commit is contained in:
Jordan Diaz
2026-04-10 16:52:00 +00:00
parent 0a8756c308
commit 224ac2dad7
6 changed files with 55 additions and 10 deletions

View File

@@ -18,6 +18,13 @@ import { fetchProjectInfo } from "./localClient.js";
const DEFAULT_ROLE = 'developer'; const DEFAULT_ROLE = 'developer';
const FORGE_INTERNAL_URL = process.env.ACAI_FORGE_WEB_URL || "http://web:80"; const FORGE_INTERNAL_URL = process.env.ACAI_FORGE_WEB_URL || "http://web:80";
const resolveEffectiveRole = (baseRole) => {
if (process.env.ACAI_ROLE_OVERRIDE) return process.env.ACAI_ROLE_OVERRIDE;
if (process.env.ACAI_MODE_OVERRIDE === "production") return "editor";
if (process.env.ACAI_MODE === "production") return "editor";
return baseRole || DEFAULT_ROLE;
};
// Session-based credentials storage (ephemeral, per-session) // Session-based credentials storage (ephemeral, per-session)
export const sessionCredentials = new Map(); export const sessionCredentials = new Map();
@@ -99,7 +106,7 @@ const readProjectAcaiFallback = () => {
tokenHash: data.tokenHash || process.env.ACAI_TOKEN_HASH || null, tokenHash: data.tokenHash || process.env.ACAI_TOKEN_HASH || null,
mode, mode,
profileName: "acai-file", profileName: "acai-file",
role: DEFAULT_ROLE, role: resolveEffectiveRole(data.role),
}; };
} catch (error) { } catch (error) {
console.error(`[Credentials] Failed to read .acai fallback: ${error.message}`); console.error(`[Credentials] Failed to read .acai fallback: ${error.message}`);
@@ -136,7 +143,7 @@ const resolveLocalProjectFallback = async () => {
tokenHash: info.tokenHash || process.env.ACAI_TOKEN_HASH || null, tokenHash: info.tokenHash || process.env.ACAI_TOKEN_HASH || null,
mode, mode,
profileName: "project-info", profileName: "project-info",
role: DEFAULT_ROLE, role: resolveEffectiveRole(info.role),
}; };
} }
} catch (error) { } catch (error) {
@@ -279,7 +286,7 @@ export const getSessionCredentials = async (sessionId, inlineCredentials = null)
forge_host: process.env.ACAI_FORGE_HOST || null, forge_host: process.env.ACAI_FORGE_HOST || null,
tokenHash: process.env.ACAI_TOKEN_HASH || null, tokenHash: process.env.ACAI_TOKEN_HASH || null,
profileName: 'default', profileName: 'default',
role: 'developer', // Env fallback = local dev, full access role: resolveEffectiveRole('developer'), // Env fallback = local dev, full access
}; };
}; };

View File

@@ -4,12 +4,17 @@ import { registerAcaiLineReplaceTool } from "./lineReplace.js";
import { registerAcaiDeleteTool } from "./delete.js"; import { registerAcaiDeleteTool } from "./delete.js";
import { registerAcaiGlobTool } from "./glob.js"; import { registerAcaiGlobTool } from "./glob.js";
import { registerAcaiGrepTool } from "./grep.js"; import { registerAcaiGrepTool } from "./grep.js";
import { canEditCode } from "../helpers/roleCheck.js";
export function registerFileTools(server) { export function registerFileTools(server) {
// Lectura: siempre disponible
registerAcaiViewTool(server); registerAcaiViewTool(server);
registerAcaiGlobTool(server); registerAcaiGlobTool(server);
registerAcaiGrepTool(server); registerAcaiGrepTool(server);
// Escritura: solo si el rol puede editar codigo
if (canEditCode()) {
registerAcaiWriteTool(server); registerAcaiWriteTool(server);
registerAcaiLineReplaceTool(server); registerAcaiLineReplaceTool(server);
registerAcaiDeleteTool(server); registerAcaiDeleteTool(server);
}
} }

View File

@@ -0,0 +1,24 @@
/**
* Helper central para determinar el rol efectivo del MCP y bloquear tools
* peligrosas cuando el user es "editor".
*
* El rol se recibe principalmente via env var ACAI_ROLE_OVERRIDE inyectada
* por el backend Python (agentic.py y cronjobs.py). Hay autoderivacion
* defensiva en caso de que alguien lance el MCP sin el override:
* - Si ACAI_MODE(_OVERRIDE) = "production" → rol editor por defecto.
* - Si no → rol developer.
*/
export function getEffectiveRole() {
if (process.env.ACAI_ROLE_OVERRIDE) return process.env.ACAI_ROLE_OVERRIDE;
if (process.env.ACAI_MODE_OVERRIDE === "production") return "editor";
if (process.env.ACAI_MODE === "production") return "editor";
return "developer";
}
/**
* True si el rol efectivo puede editar archivos de codigo.
* Los roles permitidos son todo lo que NO sea "editor".
*/
export function canEditCode() {
return getEffectiveRole() !== "editor";
}

View File

@@ -1,9 +1,12 @@
import { registerCheckModuleTool } from './check.js'; import { registerCheckModuleTool } from './check.js';
import { registerCheckModuleUsageTool } from './checkUsage.js'; import { registerCheckModuleUsageTool } from './checkUsage.js';
import { registerCompileModuleTool } from './compile.js'; import { registerCompileModuleTool } from './compile.js';
import { canEditCode } from '../helpers/roleCheck.js';
export function registerModuleTools(server) { export function registerModuleTools(server) {
registerCheckModuleTool(server); registerCheckModuleTool(server);
registerCheckModuleUsageTool(server); registerCheckModuleUsageTool(server);
if (canEditCode()) {
registerCompileModuleTool(server); registerCompileModuleTool(server);
}
} }

View File

@@ -1,7 +1,10 @@
import { registerSaveProjectStylesTool } from "./saveStyles.js"; import { registerSaveProjectStylesTool } from "./saveStyles.js";
import { registerGetWebUrlTool } from "./getWebUrl.js"; import { registerGetWebUrlTool } from "./getWebUrl.js";
import { canEditCode } from "../helpers/roleCheck.js";
export function registerProjectTools(server) { export function registerProjectTools(server) {
registerGetWebUrlTool(server); // siempre
if (canEditCode()) {
registerSaveProjectStylesTool(server); registerSaveProjectStylesTool(server);
registerGetWebUrlTool(server); }
} }

View File

@@ -1,5 +1,8 @@
import { registerRecoverGitTools } from './rollback.js'; import { registerRecoverGitTools } from './rollback.js';
import { canEditCode } from '../helpers/roleCheck.js';
export function registerRemoteGitTools(server) { export function registerRemoteGitTools(server) {
if (canEditCode()) {
registerRecoverGitTools(server); registerRecoverGitTools(server);
}
} }