"""ABAC entity policy model — attribute-based access control policies. ⚠️ ABAC EntityPolicy ist implementiert aber wird nicht aktiv genutzt. Bei echtem Bedarf aktivieren, sonst bei Gelegenheit entfernen. Each policy defines a rule for a specific entity type: - allow policies: at least one must match for access - deny policies: if any matches, access is denied (deny takes precedence) Conditions use JSONB with format: { "operator": "AND" | "OR", "rules": [ {"field": "status", "op": "eq", "value": "active"}, {"field": "amount", "op": "gte", "value": 1000}, {"field": "tags", "op": "contains", "value": "vip"} ] } """ from __future__ import annotations import uuid from datetime import datetime from sqlalchemy import ( Boolean, CheckConstraint, DateTime, Index, Integer, String, func, ) from sqlalchemy.dialects.postgresql import JSONB from sqlalchemy.dialects.postgresql import UUID as PGUUID from sqlalchemy.orm import Mapped, mapped_column from app.core.db import Base, TenantMixin from app.models.owned_mixin import OwnedMixin class EntityPolicy(Base, TenantMixin, OwnedMixin): """ABAC policy entry for any entity type in the system. entity_type examples: 'contact', 'dms_file', 'mailbox', 'calendar_event', 'task', 'workflow', 'contact_folder', etc. principal_type: 'user', 'group', 'role' effect: 'allow' | 'deny' - allow: grants access if conditions match - deny: blocks access if conditions match (deny takes precedence over allow) conditions: JSONB with operator (AND/OR) and rules array priority: higher priority policies are evaluated first """ __tablename__ = "entity_policies" __table_args__ = ( CheckConstraint( "principal_type IN ('user', 'group', 'role')", name="ck_epol_principal_type", ), CheckConstraint( "effect IN ('allow', 'deny')", name="ck_epol_effect", ), Index("ix_epol_entity_type", "entity_type"), Index("ix_epol_principal", "principal_type", "principal_id"), Index("ix_epol_tenant", "tenant_id"), Index("ix_epol_priority", "priority"), Index("ix_epol_enabled", "enabled"), ) id: Mapped[uuid.UUID] = mapped_column( PGUUID(as_uuid=True), primary_key=True, default=uuid.uuid4 ) name: Mapped[str] = mapped_column(String(200), nullable=False) entity_type: Mapped[str] = mapped_column(String(50), nullable=False) principal_type: Mapped[str] = mapped_column(String(10), nullable=False) principal_id: Mapped[uuid.UUID] = mapped_column( PGUUID(as_uuid=True), nullable=False ) effect: Mapped[str] = mapped_column( String(10), nullable=False, default="allow" ) conditions: Mapped[dict | None] = mapped_column( JSONB, nullable=True, default=None ) priority: Mapped[int] = mapped_column( Integer, nullable=False, default=0 ) enabled: Mapped[bool] = mapped_column( Boolean, nullable=False, default=True ) created_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), nullable=False, server_default=func.now() ) updated_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), nullable=False, server_default=func.now(), onupdate=func.now(), )