BC-B — Authorization Context
| Idioma: Español |
Versión en inglés no disponible |
Schema: [ums_authz] | Owner: UMS Core API .NET 8
Misión: Actuar como Policy Decisión Point (PDP). Compilar y resolver el grafo de autorización jerarquico. Gobernar la topologia de recursos (sistemas, modulos, acciones) y los perfiles de permisos.
FS cubiertos: FS-02, FS-04, FS-05, FS-06, FS-07
Versión: 2.0 | Fecha: 2026-05-15
Arquitectura de Agregados: Modelo completo con diagramas, secuencias, ER y API:
SystemSuite · PermissionTemplate · Profile
Agregados
[!NOTE]
En la implementación real de C# (base de código), Profile y PermissionTemplate son los únicos agregados locales de dominio activos. SystemSuite, Role y TemplateAssignmentRule están diferidos en esta fase y son consumidos mediante referencias desacopladas a nivel de Value Object ID (SystemSuiteId, RoleId).
| Agregado |
Raiz |
C# Status |
Descripción |
| Profile |
Profile |
Activo |
Colección de autorizaciónes de un usuario |
| PermissionTemplate |
PermissionTemplate |
Activo |
Blueprint de permisos reutilizable |
| SystemSuite |
SystemSuite |
Diferido (Ref ID) |
Aplicación cliente con topología de recursos |
| Role |
Role |
Diferido (Ref ID) |
Rol jerárquico dentro de un sistema |
| TemplateAssignmentRule |
TemplateAssignmentRule |
Diferido (Ref ID) |
Regla de auto-asignación de templates |
Axiomas de Autorización (ADR-0039)
| Axioma |
Enunciado |
| A1 Deny-by-Default |
Toda accion esta denegada hasta ALLOW explicito |
| A2 Permissive Union |
El usuario obtiene la union de ALLOW de todos sus Profiles activos |
| A3 Explicit Deny Dominance |
Un DENY en cualquier Profile invalida todos los ALLOW del usuario |
| A4 Branch Scope Precedence |
Profile BRANCH_SCOPED tiene precedencia sobre ORG_WIDE para el mismo branch |
Aggregate: SystemSuite
Aggregate Root: SystemSuite
FS: FS-04
Entidades
| Entidad |
Descripción |
SystemSuite (AR) |
Aplicacion cliente registrada en la plataforma |
FunctionalModule |
L1 de la topologia funcional (Modulo) |
FunctionalSubmodule |
L2 (Menu / Submenu) |
FunctionalOption |
L3 (Pagina / Vista) |
Action |
Unidad atomica de permiso; asignable en cualquier nivel |
Value Objects
| Value Object |
Tipo |
Regla |
SystemCode |
string |
Único globalmente; slug machine-readable |
BaseUrl |
string |
URL fisica del sistema para routing del gateway |
ApiCredentialHash |
string |
Hash de credencial M2M generado al registro |
SystemStatus |
enum |
DRAFT / PUBLISHED / RETIRED |
ActionLevel |
enum |
SYSTEM / MODULE / SUBMODULE / OPTION |
ActionCode |
string |
Único dentro del sistema (ej. USER_CREATE) |
Invariantes
| ID |
Regla |
Fuente |
| INV-S1 |
SystemCode único globalmente (no por tenant) |
FS-04 |
| INV-S2 |
Action XOR: (SuiteId NOT NULL AND ModuleId NULL) OR (SuiteId NULL AND ModuleId NOT NULL) |
ADR-0043 |
| INV-S3 |
Jerarquia estricta: Suite -> Module -> Submodule -> Option; no saltos |
ADR-0043 |
| INV-S4 |
Topologia DRAFT no puede usarse en PermissionTemplates |
FS-04 |
| INV-S5 |
FunctionalModule.SuiteId inmutable post-creación |
FS-04 |
Diagrama del Agregado
classDiagram
direction TB
class SystemSuite {
<<AggregateRoot>>
+Guid Id
+Guid TenantId
+SystemCode Code
+BaseUrl BaseUrl
+string ApiCredentialHash
+SystemStatus Status
}
class FunctionalModule {
<<Entity>>
+Guid Id
+string Code
+string Name
+int Order
}
class FunctionalSubmodule {
<<Entity>>
+Guid Id
+string Code
+string Name
+int Order
}
class FunctionalOption {
<<Entity>>
+Guid Id
+string Code
+string Name
+int Order
}
class Action {
<<Entity>>
+Guid Id
+ActionCode Code
+ActionLevel Level
+string Description
}
SystemSuite "1" --> "0..*" FunctionalModule : contains
FunctionalModule "1" --> "0..*" FunctionalSubmodule : contains
FunctionalSubmodule "1" --> "0..*" FunctionalOption : contains
SystemSuite "1" --> "0..*" Action : defines
FunctionalModule "1" --> "0..*" Action : defines
Máquina de Estado: SystemSuite
stateDiagram-v2
[*] --> DRAFT : RegisterSystem
DRAFT --> PUBLISHED : PublishSystemTopology
PUBLISHED --> RETIRED : RetireSystem
note right of RETIRED : Estado terminal
Comandos
| Comando |
Descripción |
RegisterSystemCommand |
Registra nuevo sistema con code y URL |
AddModuleToSystemCommand |
Agrega modulo a la topologia del sistema |
AddSubmoduleCommand |
Agrega submenu/menu al modulo |
AddOptionCommand |
Agrega opcion al submenu |
RegisterActionCommand |
Declara accion con nivel propietario |
PublishSystemTopologyCommand |
Cambia estado DRAFT -> PUBLISHED |
RetireSystemCommand |
Retira el sistema; estado terminal |
Eventos de Dominio
SystemRegisteredEvent { suiteId, systemCode, baseUrl }
SystemTopologyPublishedEvent { suiteId }
ActionRegisteredEvent { actionId, suiteId, actionCode, level }
SystemRetiredEvent { suiteId }
Aggregate: Role
Aggregate Root: Role
FS: FS-02, FS-05, FS-12
Value Objects
| Value Object |
Tipo |
Regla |
RoleHierarchyLevel |
int |
Nivel jerarquico; inmutable post-asignacion |
PromotionOrder |
int |
Orden lineal de promoción dentro del suite |
Invariantes
| ID |
Regla |
Fuente |
| INV-R1 |
ParentRoleId debe pertenecer al mismo SuiteId |
ADR-0046 |
| INV-R2 |
HierarchyLevel hijo estrictamente > padre |
ADR-0046 |
| INV-R3 |
PromotionOrder hijo = padre + 1; no saltos de nivel |
ADR-0046 |
| INV-R4 |
Un rol no puede tener mas de un padre (no herencia multiple) |
ADR-0046 |
Diagrama del Agregado
classDiagram
direction TB
class Role {
<<AggregateRoot>>
+Guid Id
+Guid SuiteId
+Guid TenantId
+string Name
+string Code
+RoleHierarchyLevel HierarchyLevel
+int PromotionOrder
+Guid ParentRoleId
}
Role "1" --> "0..*" Role : parentOf
Máquina de Estado: Role
stateDiagram-v2
[*] --> ACTIVE : CreateRole
ACTIVE --> DEPRECATED : DeprecateRole
note right of DEPRECATED : Roles deprecados no pueden asignarse a nuevos Profiles
Comandos y Eventos
CreateRoleCommand -> RoleCreatedEvent { roleId, suiteId, parentRoleId? }
UpdateRoleHierarchyCommand -> RoleHierarchyUpdatedEvent { roleId, oldParentRoleId, newParentRoleId }
Aggregate: PermissionTemplate
Aggregate Root: PermissionTemplate
FS: FS-02, FS-05, FS-06
Value Objects
| Value Object |
Tipo |
Regla |
ExclusiveArcTarget |
record |
Encapsula (SuiteId?, ModuleId?, SubmoduleId?, OptionId?); exactamente uno NOT NULL |
TemplateScope |
enum |
GLOBAL (TenantId=NULL) / TENANT (protegido por RLS) |
PermissionEffect |
enum |
ALLOW / DENY |
TemplateVersión |
string |
Semver (ej. 1.0.0); inmutable una vez publicada |
TemplateStatus |
enum |
DRAFT / PUBLISHED / DEPRECATED |
Invariantes
| ID |
Regla |
Fuente |
| INV-PT1 |
Exactamente uno de los 4 FKs del arco exclusivo debe ser NOT NULL |
ADR-0043, Regla 2 |
| INV-PT2 |
Template debe referenciar RoleId valido del mismo suite |
ADR-0043 |
| INV-PT3 |
Templates GLOBAL tienen TenantId = NULL |
ADR-0042 |
| INV-PT4 |
No puede existir mas de un template activo para (RoleId, ActionId, ExclusiveArcTarget) |
ADR-0042 |
| INV-PT5 |
DRAFT no puede asignarse a Profiles |
FS-02 |
| INV-PT6 |
DEPRECATED no puede asignarse a nuevos Profiles |
FS-02 |
| INV-PT7 |
Recursos referenciados deben pertenecer a un SystemSuite PUBLISHED |
FS-02 |
Diagrama del Agregado
classDiagram
direction TB
class PermissionTemplate {
<<AggregateRoot>>
+Guid Id
+Guid RoleId
+Guid TenantId
+TemplateScope Scope
+TemplateStatus Status
+TemplateVersion Version
}
class TemplateItem {
<<Entity>>
+Guid Id
+Guid ActionId
+PermissionEffect Effect
+ExclusiveArcTarget Target
+bool IsActive
}
PermissionTemplate "1" --> "1..*" TemplateItem : contains
Máquina de Estado: PermissionTemplate
Visualización: interactive-ddd-viewer.html — sección “PermissionTemplate”
stateDiagram-v2
[*] --> DRAFT : CreateTemplate
DRAFT --> PUBLISHED : PublishTemplate (recursos validos)
PUBLISHED --> DEPRECATED : DeprecateTemplate
Comandos
| Comando |
Descripción |
CreateTemplateCommand |
Crea template en estado DRAFT |
AddPermissionToTemplateCommand |
Agrega Action+Effect al template (solo en DRAFT) |
RemovePermissionFromTemplateCommand |
Elimina permiso del template (solo en DRAFT) |
PublishTemplateCommand |
Valida recursos y publica el template |
DeprecateTemplateCommand |
Marca el template como obsoleto |
Eventos de Dominio
PermissionTemplateCreatedEvent { templateId, roleId, scope, version }
PermissionTemplatePublishedEvent { templateId, roleId }
PermissionTemplateMutatedEvent { templateId, roleId, previousEffect, newEffect }
PermissionTemplateDeprecatedEvent { templateId, roleId }
Aggregate: TemplateAssignmentRule
Aggregate Root: TemplateAssignmentRule
FS: FS-06
Nota: Este agregado no aparece en el E/R actual. Pendiente de validación (gap V1 en 12-design-decisions.md).
Value Objects
| Value Object |
Tipo |
Regla |
RuleCondition |
JSON |
Condicion evaluable contra atributos del Profile (org type, branch, user category) |
RulePriority |
int |
Mayor numero = mayor prioridad; resuelve conflictos deterministicamente |
RuleStatus |
enum |
ACTIVE / INACTIVE |
Invariantes
| ID |
Regla |
Fuente |
| INV-AR1 |
RulePriority único dentro del mismo SuiteId |
FS-06 |
| INV-AR2 |
Template referenciado debe estar PUBLISHED |
FS-06 |
| INV-AR3 |
La razon de asignacion debe registrarse en ProfilePermission (rule reference) |
FS-06 |
Diagrama del Agregado
classDiagram
direction TB
class TemplateAssignmentRule {
<<AggregateRoot>>
+Guid Id
+Guid SuiteId
+Guid TenantId
+Guid TemplateId
+JSON RuleCondition
+RulePriority Priority
+RuleStatus Status
}
Máquina de Estado: TemplateAssignmentRule
stateDiagram-v2
[*] --> ACTIVE : CreateAssignmentRule
ACTIVE --> INACTIVE : DeactivateRule
INACTIVE --> ACTIVE : ReactivateRule
Comandos y Eventos
CreateAssignmentRuleCommand -> AssignmentRuleCreatedEvent { ruleId, suiteId, templateId, priority }
UpdateRulePriorityCommand -> AssignmentRulePriorityUpdated { ruleId, oldPriority, newPriority }
DeactivateRuleCommand -> AssignmentRuleDeactivatedEvent { ruleId }
Aggregate: Profile
Aggregate Root: Profile
FS: FS-05, FS-06, FS-07, FS-10
Entidades
| Entidad |
Descripción |
Profile (AR) |
Coleccion fisica de autorizaciónes asignada a un usuario |
ProfilePermission |
Materializacion del template sobre el perfil; soporta overrides IsAllowed/IsDenied |
Value Objects
| Value Object |
Tipo |
Regla |
ProfileScope |
enum |
ORG_WIDE (BranchId=NULL) / BRANCH_SCOPED (BranchId NOT NULL) |
AutoAssigned |
bool |
true si asignado por motor de reglas; false si manual |
AssignmentSource |
record |
(method: MANUAL/AUTO, ruleId?: Guid, assignedBy: Guid) |
PermissionOverride |
record |
(IsAllowed: bool, IsDenied: bool, Reason: string) |
Invariantes
| ID |
Regla |
Fuente |
| INV-P1 |
BranchId NOT NULL implica que el Branch pertenece al mismo TenantId |
FS-05 |
| INV-P2 |
Un usuario puede tener un solo Profile activo por (TenantId, BranchId, RoleId) |
FS-05 |
| INV-P3 |
Al asignar ProfilePermission, permisos del admin deben ser >= permisos concedidos (Least Privilege) |
ADR-0044 |
| INV-P4 |
UserAccount BLOCKED no puede recibir nuevos Profiles |
ADR-0044 |
| INV-P5 |
Profile BRANCH_SCOPED tiene precedencia sobre ORG_WIDE para el mismo branch (Axioma 4) |
database-design-er.md |
| INV-P6 |
DENY en cualquier Profile invalida ALLOW en todos los perfiles del usuario (Axioma 3) |
database-design-er.md |
| INV-P7 |
Accion bloqueada hasta ALLOW explicito (Deny-by-Default, Axioma 1) |
database-design-er.md |
| INV-P8 |
Profiles internos (INTERNAL_ONLY) no pueden asignarse a usuarios EXTERNAL/B2B/PARTNER |
FS-10 |
Diagrama del Agregado
classDiagram
direction TB
class Profile {
<<AggregateRoot>>
+Guid Id
+Guid UserId
+Guid TenantId
+Guid RoleId
+Guid BranchId
+ProfileScope Scope
+bool IsActive
+bool AutoAssigned
+AssignmentSource Source
}
class ProfilePermission {
<<Entity>>
+Guid Id
+Guid TemplateId
+Guid ActionId
+TargetType TargetType
+Guid TargetId
+bool IsAllowed
+bool IsDenied
+bool IsOverride
+bool IsActive
}
Profile "1" --> "0..*" ProfilePermission : materializes
Comandos
| Comando |
Descripción |
CreateProfileCommand |
Crea perfil vinculado a org, rol y opcionalmente branch |
AssignTemplateToProfileCommand |
Vincula template publicado al perfil (manual) |
AutoAssignTemplateCommand |
Motor de reglas asigna template automaticamente (FS-06) |
GrantPermissionOverrideCommand |
Override explicito ALLOW/DENY sobre una Action |
RevokePermissionOverrideCommand |
Elimina override de una Action |
AssignProfileToUserCommand |
Vincula el Profile a un UserAccount |
RevokeProfileFromUserCommand |
Desvincula el Profile del UserAccount |
Eventos de Dominio
ProfileCreatedEvent { profileId, userId, tenantId, roleId, branchId? }
TemplateLinkedToProfileEvent { profileId, templateId, method: MANUAL/AUTO, ruleId? }
PermissionOverrideGrantedEvent { profileId, userId, actionId, effect, grantedBy }
PermissionOverrideRevokedEvent { profileId, userId, actionId, revokedBy }
ProfileAssignedToUserEvent { profileId, userId, assignedBy }
ProfileRevokedFromUserEvent { profileId, userId, reason }
PermissionMutatedEvent { profileId, userId, actionId, effect, mutatedBy }
AuthorizationGraphInvalidatedEvent { userId, tenantId, suiteId }