@stackmemoryai/stackmemory
Version:
Project-scoped memory for AI coding tools. Durable context across sessions with MCP integration, frames, smart retrieval, Claude Code skills, and automatic hooks.
186 lines (185 loc) • 5.31 kB
JavaScript
import { fileURLToPath as __fileURLToPath } from 'url';
import { dirname as __pathDirname } from 'path';
const __filename = __fileURLToPath(import.meta.url);
const __dirname = __pathDirname(__filename);
import { ValidationError, ErrorCode } from "../errors/index.js";
import { logger } from "../monitoring/logger.js";
class PermissionManager {
userPermissions = /* @__PURE__ */ new Map();
adminUsers = /* @__PURE__ */ new Set();
constructor() {
this.initializeDefaultPermissions();
}
/**
* Check if user has permission for specific operation
*/
async checkPermission(context) {
try {
if (this.adminUsers.has(context.userId)) {
return true;
}
const stackPermissions = this.getStackPermissions(
context.userId,
context.stackContext?.stackId || context.resourceId
);
if (!stackPermissions) {
logger.warn("No permissions found for user", {
userId: context.userId,
stackId: context.stackContext?.stackId,
operation: context.operation
});
return false;
}
switch (context.operation) {
case "read":
return stackPermissions.canRead;
case "write":
return stackPermissions.canWrite;
case "handoff":
return stackPermissions.canHandoff;
case "merge":
return stackPermissions.canMerge;
case "administer":
return stackPermissions.canAdminister;
default:
logger.error("Unknown operation type", {
operation: context.operation
});
return false;
}
} catch (error) {
logger.error("Permission check failed", error);
return false;
}
}
/**
* Enforce permission check - throws if access denied
*/
async enforcePermission(context) {
const hasPermission = await this.checkPermission(context);
if (!hasPermission) {
throw new ValidationError(
`Access denied: User ${context.userId} lacks ${context.operation} permission for ${context.resourceType} ${context.resourceId}`,
ErrorCode.PERMISSION_VIOLATION,
{
userId: context.userId,
operation: context.operation,
resourceType: context.resourceType,
resourceId: context.resourceId
}
);
}
logger.debug("Permission granted", {
userId: context.userId,
operation: context.operation,
resourceType: context.resourceType,
resourceId: context.resourceId
});
}
/**
* Set permissions for user on specific stack
*/
setStackPermissions(userId, stackId, permissions) {
if (!this.userPermissions.has(userId)) {
this.userPermissions.set(userId, /* @__PURE__ */ new Map());
}
this.userPermissions.get(userId).set(stackId, permissions);
logger.info("Updated stack permissions", {
userId,
stackId,
permissions
});
}
/**
* Get permissions for user on specific stack
*/
getStackPermissions(userId, stackId) {
const userPerms = this.userPermissions.get(userId);
if (!userPerms) return null;
return userPerms.get(stackId) || null;
}
/**
* Grant admin privileges to user
*/
grantAdminAccess(userId) {
this.adminUsers.add(userId);
logger.info("Granted admin access", { userId });
}
/**
* Revoke admin privileges from user
*/
revokeAdminAccess(userId) {
this.adminUsers.delete(userId);
logger.info("Revoked admin access", { userId });
}
/**
* Check if user is admin
*/
isAdmin(userId) {
return this.adminUsers.has(userId);
}
/**
* Get all permissions for user
*/
getUserPermissions(userId) {
return this.userPermissions.get(userId) || /* @__PURE__ */ new Map();
}
/**
* Remove all permissions for user
*/
removeUserPermissions(userId) {
this.userPermissions.delete(userId);
this.adminUsers.delete(userId);
logger.info("Removed all permissions for user", { userId });
}
/**
* Initialize default permissions
*/
initializeDefaultPermissions() {
const defaultAdmin = process.env["STACKMEMORY_DEFAULT_ADMIN"];
if (defaultAdmin) {
this.grantAdminAccess(defaultAdmin);
}
}
/**
* Create permission context helper
*/
createContext(userId, operation, resourceType, resourceId, stackContext) {
return {
userId,
operation,
resourceType,
resourceId,
stackContext
};
}
/**
* Bulk permission update for multiple stacks
*/
setBulkStackPermissions(userId, stackPermissions) {
if (!this.userPermissions.has(userId)) {
this.userPermissions.set(userId, /* @__PURE__ */ new Map());
}
const userPerms = this.userPermissions.get(userId);
Object.entries(stackPermissions).forEach(([stackId, permissions]) => {
userPerms.set(stackId, permissions);
});
logger.info("Updated bulk stack permissions", {
userId,
stackCount: Object.keys(stackPermissions).length
});
}
/**
* Get permission summary for debugging
*/
getPermissionSummary(userId) {
return {
isAdmin: this.isAdmin(userId),
stackPermissions: Object.fromEntries(this.getUserPermissions(userId))
};
}
}
export {
PermissionManager
};
//# sourceMappingURL=permission-manager.js.map