Skip to main content

swarmhive_api_types/
role.rs

1use serde::{Deserialize, Serialize};
2use utoipa::ToSchema;
3use uuid::Uuid;
4
5/// Built-in permission strings per docs/13-rbac.md.
6///
7/// Wire format is the verb-scoped string (`"release:publish"`); the enum is
8/// the strongly-typed representation used in server middleware. New permissions
9/// are added by appending a new variant **and** an entry to [`PERMISSIONS`];
10/// `as_str` / `all` / `from_wire` derive from that single table.
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, ToSchema)]
12pub enum PermissionName {
13    // System
14    #[serde(rename = "system:manage")]
15    SystemManage,
16    #[serde(rename = "user:manage")]
17    UserManage,
18    #[serde(rename = "role:manage")]
19    RoleManage,
20    #[serde(rename = "token:manage")]
21    TokenManage,
22    #[serde(rename = "storage:manage")]
23    StorageManage,
24    #[serde(rename = "mail:manage")]
25    MailManage,
26    #[serde(rename = "auth:manage")]
27    AuthManage,
28    // App
29    #[serde(rename = "app:create")]
30    AppCreate,
31    #[serde(rename = "app:read")]
32    AppRead,
33    #[serde(rename = "app:update")]
34    AppUpdate,
35    #[serde(rename = "app:delete")]
36    AppDelete,
37    // Release
38    #[serde(rename = "release:create")]
39    ReleaseCreate,
40    #[serde(rename = "release:read")]
41    ReleaseRead,
42    #[serde(rename = "release:update")]
43    ReleaseUpdate,
44    #[serde(rename = "release:publish")]
45    ReleasePublish,
46    #[serde(rename = "release:promote")]
47    ReleasePromote,
48    #[serde(rename = "release:rollback")]
49    ReleaseRollback,
50    #[serde(rename = "release:yank")]
51    ReleaseYank,
52    // Artifact
53    #[serde(rename = "artifact:upload")]
54    ArtifactUpload,
55    #[serde(rename = "artifact:read")]
56    ArtifactRead,
57    #[serde(rename = "artifact:delete")]
58    ArtifactDelete,
59    // Analytics
60    #[serde(rename = "analytics:read")]
61    AnalyticsRead,
62    #[serde(rename = "telemetry:read")]
63    TelemetryRead,
64}
65
66/// Single source of truth: variant ↔ wire string. Order is preserved by
67/// [`PermissionName::all`] (seed iteration order).
68const PERMISSIONS: &[(PermissionName, &str)] = {
69    use PermissionName::*;
70    &[
71        (SystemManage, "system:manage"),
72        (UserManage, "user:manage"),
73        (RoleManage, "role:manage"),
74        (TokenManage, "token:manage"),
75        (StorageManage, "storage:manage"),
76        (MailManage, "mail:manage"),
77        (AuthManage, "auth:manage"),
78        (AppCreate, "app:create"),
79        (AppRead, "app:read"),
80        (AppUpdate, "app:update"),
81        (AppDelete, "app:delete"),
82        (ReleaseCreate, "release:create"),
83        (ReleaseRead, "release:read"),
84        (ReleaseUpdate, "release:update"),
85        (ReleasePublish, "release:publish"),
86        (ReleasePromote, "release:promote"),
87        (ReleaseRollback, "release:rollback"),
88        (ReleaseYank, "release:yank"),
89        (ArtifactUpload, "artifact:upload"),
90        (ArtifactRead, "artifact:read"),
91        (ArtifactDelete, "artifact:delete"),
92        (AnalyticsRead, "analytics:read"),
93        (TelemetryRead, "telemetry:read"),
94    ]
95};
96
97impl PermissionName {
98    /// Wire / DB string representation, e.g. `"release:publish"`.
99    pub fn as_str(self) -> &'static str {
100        PERMISSIONS
101            .iter()
102            .find_map(|(p, s)| (*p == self).then_some(*s))
103            .expect("PERMISSIONS must enumerate every PermissionName variant")
104    }
105
106    /// Every permission as a flat list (used by seed).
107    pub fn all() -> impl Iterator<Item = PermissionName> {
108        PERMISSIONS.iter().map(|(p, _)| *p)
109    }
110
111    /// Total count of built-in permissions.
112    pub const fn count() -> usize {
113        PERMISSIONS.len()
114    }
115
116    /// Parse from the wire string (`"release:publish"` → `ReleasePublish`).
117    /// Returns `None` for unknown strings.
118    pub fn from_wire(s: &str) -> Option<PermissionName> {
119        PERMISSIONS
120            .iter()
121            .find_map(|(p, name)| (*name == s).then_some(*p))
122    }
123}
124
125#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
126pub struct Permission {
127    pub id: Uuid,
128    pub name: PermissionName,
129    pub description: Option<String>,
130}
131
132#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
133pub struct Role {
134    pub id: Uuid,
135    pub name: String,
136    pub description: Option<String>,
137}