feat: granular RBAC with resource/operation bindings, users, groups
- Replace admin role with granular roles: view, create, delete, edit, run - Two binding types: resource bindings (role+resource+optional name) and operation bindings (role:run + action like backup, logs, impersonate) - Name-scoped resource bindings for per-instance access control - Remove role from project members (all permissions via RBAC) - Add users, groups, RBAC CRUD endpoints and CLI commands - describe user/group shows all RBAC access (direct + inherited) - create rbac supports --subject, --binding, --operation flags - Backup/restore handles users, groups, RBAC definitions - mcplocal project-based MCP endpoint discovery - Full test coverage for all new functionality Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
54
src/mcpd/src/services/rbac-definition.service.ts
Normal file
54
src/mcpd/src/services/rbac-definition.service.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import type { RbacDefinition } from '@prisma/client';
|
||||
import type { IRbacDefinitionRepository } from '../repositories/rbac-definition.repository.js';
|
||||
import { CreateRbacDefinitionSchema, UpdateRbacDefinitionSchema } from '../validation/rbac-definition.schema.js';
|
||||
import { NotFoundError, ConflictError } from './mcp-server.service.js';
|
||||
|
||||
export class RbacDefinitionService {
|
||||
constructor(private readonly repo: IRbacDefinitionRepository) {}
|
||||
|
||||
async list(): Promise<RbacDefinition[]> {
|
||||
return this.repo.findAll();
|
||||
}
|
||||
|
||||
async getById(id: string): Promise<RbacDefinition> {
|
||||
const def = await this.repo.findById(id);
|
||||
if (def === null) {
|
||||
throw new NotFoundError(`RbacDefinition not found: ${id}`);
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
async getByName(name: string): Promise<RbacDefinition> {
|
||||
const def = await this.repo.findByName(name);
|
||||
if (def === null) {
|
||||
throw new NotFoundError(`RbacDefinition not found: ${name}`);
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
async create(input: unknown): Promise<RbacDefinition> {
|
||||
const data = CreateRbacDefinitionSchema.parse(input);
|
||||
|
||||
const existing = await this.repo.findByName(data.name);
|
||||
if (existing !== null) {
|
||||
throw new ConflictError(`RbacDefinition already exists: ${data.name}`);
|
||||
}
|
||||
|
||||
return this.repo.create(data);
|
||||
}
|
||||
|
||||
async update(id: string, input: unknown): Promise<RbacDefinition> {
|
||||
const data = UpdateRbacDefinitionSchema.parse(input);
|
||||
|
||||
// Verify exists
|
||||
await this.getById(id);
|
||||
|
||||
return this.repo.update(id, data);
|
||||
}
|
||||
|
||||
async delete(id: string): Promise<void> {
|
||||
// Verify exists
|
||||
await this.getById(id);
|
||||
await this.repo.delete(id);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user