Skip to main content

syncular_protocol/
auth_lease.rs

1use serde::{Deserialize, Serialize};
2use serde_json::{Map, Value};
3
4pub const AUTH_LEASE_VERSION: i32 = 1;
5pub const AUTH_LEASE_PROTOCOL_VERSION: i32 = 1;
6pub const AUTH_LEASE_ALG_ES256: &str = "ES256";
7pub const AUTH_LEASE_TYP: &str = "syncular-auth-lease+jws";
8
9pub const AUTH_LEASE_CODE_MISSING: &str = "sync.auth_lease_missing";
10pub const AUTH_LEASE_CODE_INVALID: &str = "sync.auth_lease_invalid";
11pub const AUTH_LEASE_CODE_EXPIRED: &str = "sync.auth_lease_expired";
12pub const AUTH_LEASE_CODE_SCHEMA_MISMATCH: &str = "sync.auth_lease_schema_mismatch";
13pub const AUTH_LEASE_CODE_SCOPE_MISMATCH: &str = "sync.auth_lease_scope_mismatch";
14pub const AUTH_LEASE_CODE_SCOPE_REVOKED: &str = "sync.auth_lease_scope_revoked";
15pub const AUTH_LEASE_CODE_BUSINESS_REJECTED: &str = "sync.auth_lease_business_rejected";
16
17#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
18#[serde(rename_all = "camelCase")]
19pub struct AuthLeaseProtectedHeader {
20    pub alg: String,
21    pub kid: String,
22    pub typ: String,
23}
24
25impl AuthLeaseProtectedHeader {
26    pub fn es256(kid: impl Into<String>) -> Self {
27        Self {
28            alg: AUTH_LEASE_ALG_ES256.to_string(),
29            kid: kid.into(),
30            typ: AUTH_LEASE_TYP.to_string(),
31        }
32    }
33}
34
35#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
36#[serde(rename_all = "camelCase")]
37pub struct AuthLeasePayload {
38    pub version: i32,
39    pub lease_id: String,
40    pub issuer: String,
41    pub audience: String,
42    pub actor_id: String,
43    #[serde(default, skip_serializing_if = "Map::is_empty")]
44    pub subject: Map<String, Value>,
45    pub schema_version: i32,
46    pub protocol_version: i32,
47    pub issued_at_ms: i64,
48    pub not_before_ms: i64,
49    pub expires_at_ms: i64,
50    pub max_clock_skew_ms: i64,
51    pub scopes: Vec<AuthLeaseScope>,
52    pub capabilities: AuthLeaseCapabilities,
53}
54
55#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
56#[serde(rename_all = "camelCase")]
57pub struct AuthLeaseScope {
58    pub subscription_id: String,
59    pub table: String,
60    pub values: Map<String, Value>,
61    pub operations: Vec<String>,
62}
63
64#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
65#[serde(rename_all = "camelCase")]
66pub struct AuthLeaseIssueRequest {
67    pub schema_version: i32,
68    #[serde(skip_serializing_if = "Option::is_none")]
69    pub ttl_ms: Option<i64>,
70    pub scopes: Vec<AuthLeaseScope>,
71}
72
73#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
74#[serde(rename_all = "camelCase")]
75pub struct AuthLeaseIssueResponse {
76    pub ok: bool,
77    pub token: String,
78    pub protected_header: AuthLeaseProtectedHeader,
79    pub payload: AuthLeasePayload,
80}
81
82#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
83#[serde(rename_all = "camelCase")]
84pub struct AuthLeaseCapabilities {
85    pub allow_blobs: bool,
86    pub allow_crdt: bool,
87    pub allow_encrypted_fields: bool,
88}
89
90#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
91#[serde(rename_all = "camelCase")]
92pub struct AuthLeaseValidationResult {
93    pub ok: bool,
94    #[serde(rename = "leaseId", skip_serializing_if = "Option::is_none")]
95    pub lease_id: Option<String>,
96    #[serde(skip_serializing_if = "Option::is_none")]
97    pub kid: Option<String>,
98    #[serde(skip_serializing_if = "Option::is_none")]
99    pub code: Option<String>,
100    #[serde(skip_serializing_if = "Option::is_none")]
101    pub message: Option<String>,
102    #[serde(rename = "expiresAtMs", skip_serializing_if = "Option::is_none")]
103    pub expires_at_ms: Option<i64>,
104}
105
106impl AuthLeaseValidationResult {
107    pub fn accepted(
108        lease_id: impl Into<String>,
109        kid: impl Into<String>,
110        expires_at_ms: i64,
111    ) -> Self {
112        Self {
113            ok: true,
114            lease_id: Some(lease_id.into()),
115            kid: Some(kid.into()),
116            code: None,
117            message: None,
118            expires_at_ms: Some(expires_at_ms),
119        }
120    }
121
122    pub fn rejected(code: impl Into<String>, message: impl Into<String>) -> Self {
123        Self {
124            ok: false,
125            lease_id: None,
126            kid: None,
127            code: Some(code.into()),
128            message: Some(message.into()),
129            expires_at_ms: None,
130        }
131    }
132}
133
134#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
135#[serde(rename_all = "camelCase")]
136pub struct AuthLeaseProvenance {
137    pub lease_id: String,
138    pub lease_expires_at_ms: i64,
139    pub lease_status_at_enqueue: String,
140    #[serde(skip_serializing_if = "Option::is_none")]
141    pub lease_scope_summary_json: Option<String>,
142    #[serde(rename = "leaseToken", skip_serializing_if = "Option::is_none")]
143    pub lease_token: Option<String>,
144}