Skip to main content

rbac_api_contract/
domain.rs

1// SPDX-FileCopyrightText: 2026 Alexander R. Croft
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4use std::collections::BTreeMap;
5
6use serde::{Deserialize, Serialize};
7use serde_json::Value;
8
9#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
10pub struct K2Document {
11    #[serde(rename = "_uuid")]
12    pub uuid: String,
13    #[serde(rename = "_created")]
14    pub created: u64,
15    #[serde(rename = "_updated")]
16    pub updated: u64,
17    #[serde(rename = "_owner")]
18    pub owner: String,
19    #[serde(rename = "_deleted", skip_serializing_if = "Option::is_none")]
20    pub deleted: Option<bool>,
21}
22
23#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
24pub struct Membership {
25    #[serde(rename = "userId")]
26    pub user_id: String,
27    #[serde(default, skip_serializing_if = "Vec::is_empty")]
28    pub roles: Vec<String>,
29    #[serde(rename = "grantedBy", skip_serializing_if = "Option::is_none")]
30    pub granted_by: Option<String>,
31    #[serde(rename = "grantedAt", skip_serializing_if = "Option::is_none")]
32    pub granted_at: Option<u64>,
33    #[serde(rename = "expiresAt", skip_serializing_if = "Option::is_none")]
34    pub expires_at: Option<u64>,
35    #[serde(skip_serializing_if = "Option::is_none")]
36    pub meta: Option<Value>,
37}
38
39#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
40pub struct LicensePayload {
41    pub expires: u64,
42    #[serde(skip_serializing_if = "Option::is_none")]
43    pub grace: Option<u64>,
44    #[serde(default, skip_serializing_if = "Vec::is_empty")]
45    pub roles: Vec<String>,
46    #[serde(skip_serializing_if = "Option::is_none")]
47    pub meta: Option<Value>,
48}
49
50#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
51pub struct AccountDocument {
52    #[serde(flatten)]
53    pub doc: K2Document,
54    #[serde(skip_serializing_if = "Option::is_none")]
55    pub meta: Option<Value>,
56    #[serde(skip_serializing_if = "Option::is_none")]
57    pub memberships: Option<Vec<Membership>>,
58    #[serde(skip_serializing_if = "Option::is_none")]
59    pub roles: Option<Vec<String>>,
60    #[serde(skip_serializing_if = "Option::is_none")]
61    pub licenses: Option<BTreeMap<String, LicensePayload>>,
62}
63
64#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
65#[serde(tag = "kind")]
66pub enum Credential {
67    #[serde(rename = "password_argon2")]
68    PasswordArgon2(CredentialPasswordArgon2),
69    #[serde(rename = "oauth")]
70    OAuth(CredentialOAuth),
71}
72
73#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
74pub struct CredentialPasswordArgon2 {
75    #[serde(rename = "secretHash")]
76    pub secret_hash: String,
77    #[serde(rename = "createdAt")]
78    pub created_at: u64,
79    #[serde(rename = "lastUsedAt", skip_serializing_if = "Option::is_none")]
80    pub last_used_at: Option<u64>,
81    #[serde(rename = "disabledAt", skip_serializing_if = "Option::is_none")]
82    pub disabled_at: Option<u64>,
83}
84
85#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
86pub struct CredentialOAuth {
87    pub provider: String,
88    #[serde(rename = "providerSub")]
89    pub provider_sub: String,
90    #[serde(rename = "createdAt")]
91    pub created_at: u64,
92}
93
94#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
95pub struct IdentityAuth {
96    #[serde(default, skip_serializing_if = "Vec::is_empty")]
97    pub credentials: Vec<Credential>,
98}
99
100#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
101pub struct IdentityDocument {
102    #[serde(flatten)]
103    pub doc: K2Document,
104    pub email: String,
105    #[serde(rename = "emailVerified", skip_serializing_if = "Option::is_none")]
106    pub email_verified: Option<bool>,
107    #[serde(skip_serializing_if = "Option::is_none")]
108    pub auth: Option<IdentityAuth>,
109    #[serde(rename = "pendingEmail", skip_serializing_if = "Option::is_none")]
110    pub pending_email: Option<String>,
111    #[serde(rename = "pendingEmailSetAt", skip_serializing_if = "Option::is_none")]
112    pub pending_email_set_at: Option<u64>,
113}
114
115#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
116pub struct RoleDocument {
117    #[serde(flatten)]
118    pub doc: K2Document,
119    pub code: String,
120    #[serde(default, skip_serializing_if = "Vec::is_empty")]
121    pub permissions: Vec<String>,
122}
123
124#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
125pub struct ApiKeyDocument {
126    #[serde(flatten)]
127    pub doc: K2Document,
128    #[serde(default, skip_serializing_if = "Vec::is_empty")]
129    pub roles: Vec<String>,
130    #[serde(rename = "key_id")]
131    pub key_id: String,
132    #[serde(rename = "secret_hash")]
133    pub secret_hash: String,
134    #[serde(skip_serializing_if = "Option::is_none")]
135    pub name: Option<String>,
136    pub active: bool,
137    #[serde(rename = "expiresAt", skip_serializing_if = "Option::is_none")]
138    pub expires_at: Option<u64>,
139    #[serde(rename = "allowedIps", skip_serializing_if = "Option::is_none")]
140    pub allowed_ips: Option<Vec<String>>,
141    #[serde(rename = "lastUsedAt", skip_serializing_if = "Option::is_none")]
142    pub last_used_at: Option<u64>,
143    #[serde(rename = "lastUsedIp", skip_serializing_if = "Option::is_none")]
144    pub last_used_ip: Option<String>,
145}
146
147#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
148pub enum MagicPurpose {
149    #[serde(rename = "signin")]
150    Signin,
151    #[serde(rename = "reset_password")]
152    ResetPassword,
153    #[serde(rename = "verify_email")]
154    VerifyEmail,
155}
156
157#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
158pub struct MagicTokenDocument {
159    #[serde(flatten)]
160    pub doc: K2Document,
161    #[serde(rename = "userId")]
162    pub user_id: String,
163    pub purpose: MagicPurpose,
164    #[serde(rename = "tokenHash")]
165    pub token_hash: String,
166    #[serde(rename = "expiresAt")]
167    pub expires_at: u64,
168    #[serde(rename = "usedAt", skip_serializing_if = "Option::is_none")]
169    pub used_at: Option<u64>,
170    #[serde(rename = "consumedByIp", skip_serializing_if = "Option::is_none")]
171    pub consumed_by_ip: Option<String>,
172    #[serde(rename = "requirePassword", skip_serializing_if = "Option::is_none")]
173    pub require_password: Option<bool>,
174}
175
176#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
177pub struct RefreshTokenDocument {
178    #[serde(flatten)]
179    pub doc: K2Document,
180    #[serde(rename = "userId")]
181    pub user_id: String,
182    #[serde(rename = "tokenHash")]
183    pub token_hash: String,
184    #[serde(rename = "expiresAt")]
185    pub expires_at: u64,
186    #[serde(rename = "revokedAt", skip_serializing_if = "Option::is_none")]
187    pub revoked_at: Option<u64>,
188    #[serde(rename = "lastUsedAt", skip_serializing_if = "Option::is_none")]
189    pub last_used_at: Option<u64>,
190    #[serde(rename = "createdByIp", skip_serializing_if = "Option::is_none")]
191    pub created_by_ip: Option<String>,
192    #[serde(rename = "createdByUa", skip_serializing_if = "Option::is_none")]
193    pub created_by_ua: Option<String>,
194}
195
196#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
197pub struct Ticket {
198    pub account: String,
199    #[serde(default, skip_serializing_if = "Vec::is_empty")]
200    pub permissions: Vec<String>,
201    #[serde(skip_serializing_if = "Option::is_none")]
202    pub expires: Option<u64>,
203}