Files
mcpctl/src/mcpd/src/services/rbac-definition.service.ts
Michal dcda93d179
Some checks failed
CI / lint (pull_request) Has been cancelled
CI / typecheck (pull_request) Has been cancelled
CI / test (pull_request) Has been cancelled
CI / build (pull_request) Has been cancelled
CI / package (pull_request) Has been cancelled
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>
2026-02-23 11:05:19 +00:00

55 lines
1.6 KiB
TypeScript

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);
}
}