feature/rbac permisos y roles implementados
This commit is contained in:
101
api/rbac/management/commands/sync_rbac.py
Normal file
101
api/rbac/management/commands/sync_rbac.py
Normal file
@@ -0,0 +1,101 @@
|
||||
"""
|
||||
Sincroniza el catálogo de permisos de roles.py con la base de datos.
|
||||
|
||||
Uso básico (solo catálogo):
|
||||
python manage.py sync_rbac
|
||||
|
||||
Con propagación a roles existentes (agrega permisos nuevos a roles que ya existen):
|
||||
python manage.py sync_rbac --roles
|
||||
|
||||
Con listado de lo que hay actualmente:
|
||||
python manage.py sync_rbac --list
|
||||
"""
|
||||
from django.core.management.base import BaseCommand
|
||||
|
||||
from api.rbac.roles import DEFAULT_ROLES, PERMISSIONS_CATALOG
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'Sincroniza el catálogo de permisos (roles.py → BD) sin necesidad de migración.'
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
'--roles',
|
||||
action='store_true',
|
||||
help='Propaga los permisos nuevos a los OrganizationRoles existentes que coincidan con DEFAULT_ROLES.',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--list',
|
||||
action='store_true',
|
||||
help='Lista los permisos actuales en la BD agrupados por módulo.',
|
||||
)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
from api.rbac.models import OrganizationRole, RolePermission
|
||||
|
||||
if options['list']:
|
||||
self._list_permisos(RolePermission)
|
||||
return
|
||||
|
||||
self._sync_catalogo(RolePermission)
|
||||
|
||||
if options['roles']:
|
||||
self._sync_roles(RolePermission, OrganizationRole)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def _sync_catalogo(self, RolePermission):
|
||||
creados = 0
|
||||
existentes = 0
|
||||
|
||||
for codename, descripcion, modulo in PERMISSIONS_CATALOG:
|
||||
_, created = RolePermission.objects.get_or_create(
|
||||
codename=codename,
|
||||
defaults={'descripcion': descripcion, 'modulo': modulo},
|
||||
)
|
||||
if created:
|
||||
self.stdout.write(self.style.SUCCESS(f' [+] {codename} ({modulo})'))
|
||||
creados += 1
|
||||
else:
|
||||
existentes += 1
|
||||
|
||||
self.stdout.write(
|
||||
self.style.SUCCESS(f'\nCatálogo: {creados} permisos creados, {existentes} ya existían.')
|
||||
)
|
||||
|
||||
def _sync_roles(self, RolePermission, OrganizationRole):
|
||||
perms_map = {p.codename: p for p in RolePermission.objects.all()}
|
||||
roles_actualizados = 0
|
||||
permisos_agregados = 0
|
||||
|
||||
for org_role in OrganizationRole.objects.select_related('organizacion').prefetch_related('permissions'):
|
||||
config = DEFAULT_ROLES.get(org_role.nombre)
|
||||
if not config:
|
||||
continue
|
||||
|
||||
esperados = {c: perms_map[c] for c in config['permissions'] if c in perms_map}
|
||||
actuales = {p.codename for p in org_role.permissions.all()}
|
||||
nuevos = {c: p for c, p in esperados.items() if c not in actuales}
|
||||
|
||||
if nuevos:
|
||||
org_role.permissions.add(*nuevos.values())
|
||||
roles_actualizados += 1
|
||||
permisos_agregados += len(nuevos)
|
||||
self.stdout.write(
|
||||
f' Rol "{org_role.nombre}" en {org_role.organizacion}: '
|
||||
f'+{len(nuevos)} → {", ".join(nuevos.keys())}'
|
||||
)
|
||||
|
||||
self.stdout.write(
|
||||
self.style.SUCCESS(
|
||||
f'\nRoles: {roles_actualizados} roles actualizados, {permisos_agregados} asignaciones nuevas.'
|
||||
)
|
||||
)
|
||||
|
||||
def _list_permisos(self, RolePermission):
|
||||
modulo_actual = None
|
||||
for perm in RolePermission.objects.order_by('modulo', 'codename'):
|
||||
if perm.modulo != modulo_actual:
|
||||
modulo_actual = perm.modulo
|
||||
self.stdout.write(self.style.HTTP_INFO(f'\n {modulo_actual}'))
|
||||
self.stdout.write(f' {perm.codename:<40} {perm.descripcion}')
|
||||
Reference in New Issue
Block a user