Skip to main content

modkit_db/secure/
entity_traits.rs

1use sea_orm::EntityTrait;
2
3/// Defines the contract for entities that can be scoped by tenant, resource, owner, and type.
4///
5/// Each entity implementing this trait must explicitly declare all four scope dimensions:
6/// - `tenant_col()`: Column for tenant-based isolation (multi-tenancy)
7/// - `resource_col()`: Column for resource-level access (typically the primary key)
8/// - `owner_col()`: Column for owner-based filtering
9/// - `type_col()`: Column for type-based filtering
10///
11/// **Important**: No implicit defaults are allowed. Every scope dimension must be explicitly
12/// specified as `Some(Column::...)` or `None` to enforce compile-time safety in secure systems.
13///
14/// # Example (Manual Implementation)
15/// ```rust,ignore
16/// impl ScopableEntity for user::Entity {
17///     fn tenant_col() -> Option<Self::Column> {
18///         Some(user::Column::TenantId)
19///     }
20///     fn resource_col() -> Option<Self::Column> {
21///         Some(user::Column::Id)
22///     }
23///     fn owner_col() -> Option<Self::Column> {
24///         None
25///     }
26///     fn type_col() -> Option<Self::Column> {
27///         None
28///     }
29/// }
30/// ```
31///
32/// # Example (Using Derive Macro)
33/// ```rust,ignore
34/// use modkit_db::secure::Scopable;
35///
36/// #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Scopable)]
37/// #[sea_orm(table_name = "users")]
38/// #[secure(
39///     tenant_col = "tenant_id",
40///     resource_col = "id",
41///     no_owner,
42///     no_type
43/// )]
44/// pub struct Model {
45///     #[sea_orm(primary_key)]
46///     pub id: Uuid,
47///     pub tenant_id: Uuid,
48///     pub email: String,
49/// }
50/// ```
51///
52/// # Unrestricted Entities
53/// ```rust,ignore
54/// #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Scopable)]
55/// #[sea_orm(table_name = "system_config")]
56/// #[secure(unrestricted)]
57/// pub struct Model {
58///     #[sea_orm(primary_key)]
59///     pub id: Uuid,
60///     pub config_key: String,
61/// }
62/// ```
63pub trait ScopableEntity: EntityTrait {
64    /// Indicates whether this entity is explicitly marked as unrestricted.
65    ///
66    /// This is a compile-time flag set via `#[secure(unrestricted)]` that documents
67    /// the entity's global nature (e.g., system configuration, lookup tables).
68    ///
69    /// When `IS_UNRESTRICTED` is true, all column methods return `None`.
70    ///
71    /// Default: `false` (entity participates in scoping logic)
72    const IS_UNRESTRICTED: bool = false;
73
74    /// Returns the column that stores the tenant identifier.
75    ///
76    /// - Multi-tenant entities: `Some(Column::TenantId)`
77    /// - Global/system entities: `None`
78    ///
79    /// Must be explicitly specified via `tenant_col = "..."` or `no_tenant`.
80    fn tenant_col() -> Option<Self::Column>;
81
82    /// Returns the column that stores the primary resource identifier.
83    ///
84    /// Typically the primary key column (e.g., `Column::Id`).
85    ///
86    /// Must be explicitly specified via `resource_col = "..."` or `no_resource`.
87    fn resource_col() -> Option<Self::Column>;
88
89    /// Returns the column that stores the resource owner identifier.
90    ///
91    /// Used for owner-based access control policies.
92    ///
93    /// Must be explicitly specified via `owner_col = "..."` or `no_owner`.
94    fn owner_col() -> Option<Self::Column>;
95
96    /// Returns the column that stores the resource type identifier.
97    ///
98    /// Used for type-based filtering in polymorphic scenarios.
99    ///
100    /// Must be explicitly specified via `type_col = "..."` or `no_type`.
101    fn type_col() -> Option<Self::Column>;
102}