Skip to main content

lexe_api_core/revocable_clients/
models.rs

1//! Request and response types for the revocable client endpoints.
2
3use lexe_common::{
4    api::{
5        auth::{BearerAuthToken, LexeScope},
6        user::UserPk,
7    },
8    time::TimestampMs,
9};
10use lexe_crypto::ed25519;
11use lexe_serde::{
12    base64_or_bytes,
13    optopt::{self, none},
14};
15#[cfg(any(test, feature = "test-utils"))]
16use proptest_derive::Arbitrary;
17use serde::{Deserialize, Serialize};
18
19use super::RevocableClient;
20
21/// A request to list all revocable clients.
22#[derive(Clone, Debug, Serialize, Deserialize)]
23#[cfg_attr(any(test, feature = "test-utils"), derive(Eq, PartialEq, Arbitrary))]
24pub struct ListRevocableClients {
25    /// Whether to return only clients which are currently valid.
26    pub valid_only: bool,
27}
28
29/// A request to create a new revocable client.
30#[derive(Clone, Debug, Serialize, Deserialize)]
31pub struct CreateRevocableClientRequest {
32    /// The expiration after which the node should reject this client.
33    /// [`None`] indicates that the client will never expire (use carefully!).
34    pub expires_at: Option<TimestampMs>,
35    /// Optional user-provided label for this client.
36    pub label: Option<String>,
37    /// The authorization scopes allowed for this client.
38    pub scope: LexeScope,
39}
40
41/// The response to [`CreateRevocableClientRequest`].
42#[derive(Clone, Debug, Serialize, Deserialize)]
43pub struct CreateRevocableClientResponse {
44    /// The user public key associated with these credentials.
45    /// Always `Some` since `node-v0.8.11`.
46    #[serde(skip_serializing_if = "Option::is_none")]
47    pub user_pk: Option<UserPk>,
48
49    /// The client cert pubkey.
50    pub pubkey: ed25519::PublicKey,
51
52    /// When this client was created.
53    pub created_at: TimestampMs,
54
55    /// The DER-encoded ephemeral issuing CA cert that the client should trust.
56    ///
57    /// This is just packaged alongside the rest for convenience.
58    // NOTE: This client cert goes *last* in the cert chain given to rustls.
59    #[serde(with = "base64_or_bytes")]
60    pub eph_ca_cert_der: Vec<u8>,
61
62    /// The DER-encoded client cert to present when connecting to the node.
63    // NOTE: This client cert goes *first* in the cert chain given to rustls.
64    #[serde(with = "base64_or_bytes")]
65    pub rev_client_cert_der: Vec<u8>,
66
67    /// The DER-encoded client cert key.
68    #[serde(with = "base64_or_bytes")]
69    pub rev_client_cert_key_der: Vec<u8>,
70
71    /// A long-lived [`LexeScope::GatewayProxy`] token for connecting to the
72    /// user's node via the gateway proxy. Always `Some` for user nodes.
73    ///
74    /// [`LexeScope::GatewayProxy`]: lexe_common::api::auth::LexeScope::GatewayProxy
75    #[serde(skip_serializing_if = "Option::is_none")]
76    pub gateway_proxy_token: Option<BearerAuthToken>,
77}
78
79/// A request to update a single [`RevocableClient`].
80///
81/// All fields except `pubkey` are optional. If a field is `None`, it will not
82/// be updated. For example:
83///
84/// * `expires_at: None` -> don't change
85/// * `expires_at: Some(None)` -> set to never expire
86/// * `expires_at: Some(TimestampMs(..))` -> set to expire at that time
87#[derive(Serialize, Deserialize)]
88#[cfg_attr(test, derive(Debug, Eq, PartialEq, Arbitrary))]
89pub struct UpdateClientRequest {
90    /// The pubkey of the client to update.
91    pub pubkey: ed25519::PublicKey,
92
93    /// Set this client's expiration (`Some(None)` means never expire).
94    #[serde(default, skip_serializing_if = "none", with = "optopt")]
95    pub expires_at: Option<Option<TimestampMs>>,
96
97    /// Set this client's label.
98    #[serde(default, skip_serializing_if = "none", with = "optopt")]
99    #[cfg_attr(test, proptest(strategy = "arb::any_label_update()"))]
100    pub label: Option<Option<String>>,
101
102    /// Set the authorization scopes allowed for this client.
103    #[serde(skip_serializing_if = "none")]
104    pub scope: Option<LexeScope>,
105
106    /// Set this to revoke or unrevoke the client. Revocation is permanent, so
107    /// you cannot unrevoke a client once it is revoked.
108    #[serde(skip_serializing_if = "none")]
109    pub is_revoked: Option<bool>,
110}
111
112/// The updated [`RevocableClient`] after a successful update.
113#[derive(Serialize, Deserialize)]
114pub struct UpdateClientResponse {
115    pub client: RevocableClient,
116}
117
118#[cfg(test)]
119mod arb {
120    use lexe_common::test_utils::arbitrary;
121    use proptest::{option, strategy::Strategy};
122
123    pub fn any_label_update() -> impl Strategy<Value = Option<Option<String>>> {
124        option::of(arbitrary::any_option_simple_string())
125    }
126}
127
128#[cfg(test)]
129mod test {
130    use lexe_common::test_utils::roundtrip;
131
132    use super::*;
133
134    #[test]
135    fn test_update_request_serde() {
136        roundtrip::json_string_roundtrip_proptest::<UpdateClientRequest>();
137    }
138
139    #[test]
140    fn test_list_revocable_clients_serde() {
141        roundtrip::query_string_roundtrip_proptest::<ListRevocableClients>();
142    }
143}