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}