Skip to main content

cedarling/bootstrap_config/
lock_config.rs

1// This software is available under the Apache-2.0 license.
2// See https://www.apache.org/licenses/LICENSE-2.0.txt for full text.
3//
4// Copyright (c) 2024, Gluu, Inc.
5
6use crate::log::LogLevel;
7use crate::{BootstrapConfigLoadingError, BootstrapConfigRaw};
8use serde::{Deserialize, Serialize};
9use std::time::Duration;
10use url::Url;
11
12/// Transport protocol for Lock Server communication
13#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
14#[serde(rename_all = "lowercase")]
15pub enum LockTransport {
16    /// REST/HTTP transport
17    #[default]
18    Rest,
19    /// gRPC transport (only available when the `grpc` feature is enabled)
20    #[cfg(feature = "grpc")]
21    Grpc,
22}
23
24#[cfg(feature = "grpc")]
25const PARSE_LOCK_TRANSPORT_ERR: &str = "Invalid lock transport. Must be `rest` or `grpc`";
26#[cfg(not(feature = "grpc"))]
27const PARSE_LOCK_TRANSPORT_ERR: &str = "Invalid lock transport. Must be `rest`";
28
29impl std::str::FromStr for LockTransport {
30    type Err = String;
31
32    fn from_str(s: &str) -> Result<Self, Self::Err> {
33        match s.to_lowercase().as_str() {
34            "rest" => Ok(Self::Rest),
35            #[cfg(feature = "grpc")]
36            "grpc" => Ok(Self::Grpc),
37            _ => Err(PARSE_LOCK_TRANSPORT_ERR.to_string()),
38        }
39    }
40}
41
42impl std::fmt::Display for LockTransport {
43    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44        match self {
45            LockTransport::Rest => write!(f, "rest"),
46            #[cfg(feature = "grpc")]
47            LockTransport::Grpc => write!(f, "grpc"),
48        }
49    }
50}
51
52/// Lock service config
53#[derive(Debug, Clone, PartialEq)]
54pub struct LockServiceConfig {
55    /// The logging level
56    pub log_level: LogLevel,
57    /// URI where Cedarling can get metadata about the lock server.
58    /// i.e. `.well-known/lock-server-configuration`
59    pub config_uri: Url,
60    /// Toggles whether Cedarling should listen for SSE config updates.
61    pub dynamic_config: bool,
62    /// Software Statement Assertion Json Web Token that Cedarling will use for Dynamic
63    /// Client Registration.
64    pub ssa_jwt: Option<String>,
65    /// Pre-issued access token for Lock Server authentication.
66    ///
67    /// When set, Cedarling skips the SSA → DCR → `access_token` flow and uses this
68    /// token directly. If both `ssa_jwt` and `access_token_jwt` are present,
69    /// `access_token_jwt` takes precedence.
70    ///
71    /// Not available on WASM targets.
72    #[cfg(not(target_arch = "wasm32"))]
73    pub access_token_jwt: Option<String>,
74    /// Intervals to send log messages to the lock server.
75    /// Set this to [`None`] to disable transmission.
76    pub log_interval: Option<Duration>,
77    /// Intervals to send health messages to the lock server.
78    /// Set this to [`None`] to disable transmission.
79    pub health_interval: Option<Duration>,
80    /// Intervals to send telemetry messages to the lock server.
81    /// Set this to [`None`] to disable transmission.
82    pub telemetry_interval: Option<Duration>,
83    /// Controls whether Cedarling should listen for updates from the Lock Server.
84    pub listen_sse: bool,
85    /// Allow interaction with a Lock server with invalid certificates. Used for testing.
86    pub accept_invalid_certs: bool,
87    /// Transport protocol to use for Lock Server communication
88    pub transport: LockTransport,
89    /// Channel capacity for buffering log entries before they are sent to the lock server.
90    pub log_channel_capacity: usize,
91    /// Maximum number of retry attempts for sending logs to the lock server.
92    pub log_max_retries: u32,
93}
94
95impl LockServiceConfig {
96    pub(super) const DEFAULT_CHANNEL_CAPACITY: usize = 100;
97    pub(super) const DEFAULT_LOG_MAX_RETRIES: u32 = 5;
98}
99
100/// Raw lock service config
101#[derive(Debug, Clone, PartialEq)]
102pub struct LockServiceConfigRaw {
103    /// The logging level
104    pub log_level: LogLevel,
105    /// Config URI
106    pub config_uri: String,
107    /// Dynamic config
108    pub dynamic_config: bool,
109    /// SSA JWT
110    pub ssa_jwt: Option<String>,
111    /// Pre-issued access token (bypasses SSA → DCR flow when set).
112    /// Not available on WASM targets.
113    #[cfg(not(target_arch = "wasm32"))]
114    pub access_token_jwt: Option<String>,
115    /// Log interval
116    pub log_interval: Option<Duration>,
117    /// Health interval
118    pub health_interval: Option<Duration>,
119    /// Telemetry interval
120    pub telemetry_interval: Option<Duration>,
121    /// Listen SSE
122    pub listen_sse: bool,
123    /// Accept invalid certs
124    pub accept_invalid_certs: bool,
125    /// Transport protocol
126    pub transport: LockTransport,
127    /// Channel capacity for log buffering
128    pub log_channel_capacity: usize,
129    /// Max retries for log sending
130    pub log_max_retries: u32,
131}
132
133impl Default for LockServiceConfig {
134    fn default() -> Self {
135        Self {
136            log_level: LogLevel::INFO,
137            config_uri: "http://localhost:8080/.well-known/lock-server-configuration"
138                .parse()
139                .expect("Failed to parse default lock server configuration URI"),
140            dynamic_config: false,
141            ssa_jwt: None,
142            #[cfg(not(target_arch = "wasm32"))]
143            access_token_jwt: None,
144            log_interval: None,
145            health_interval: None,
146            telemetry_interval: None,
147            listen_sse: false,
148            accept_invalid_certs: false,
149            transport: LockTransport::default(),
150            log_channel_capacity: Self::DEFAULT_CHANNEL_CAPACITY,
151            log_max_retries: Self::DEFAULT_LOG_MAX_RETRIES,
152        }
153    }
154}
155
156impl From<LockServiceConfigRaw> for LockServiceConfig {
157    fn from(raw: LockServiceConfigRaw) -> Self {
158        Self {
159            log_level: raw.log_level,
160            config_uri: raw
161                .config_uri
162                .parse()
163                .expect("Failed to parse lock server configuration URI from raw config"),
164            dynamic_config: raw.dynamic_config,
165            ssa_jwt: raw.ssa_jwt,
166            #[cfg(not(target_arch = "wasm32"))]
167            access_token_jwt: raw.access_token_jwt,
168            log_interval: raw.log_interval,
169            health_interval: raw.health_interval,
170            telemetry_interval: raw.telemetry_interval,
171            listen_sse: raw.listen_sse,
172            accept_invalid_certs: raw.accept_invalid_certs,
173            transport: raw.transport,
174            log_channel_capacity: raw.log_channel_capacity,
175            log_max_retries: raw.log_max_retries,
176        }
177    }
178}
179
180impl TryFrom<&BootstrapConfigRaw> for LockServiceConfig {
181    type Error = BootstrapConfigLoadingError;
182
183    fn try_from(raw: &BootstrapConfigRaw) -> Result<Self, Self::Error> {
184        let config_uri = raw
185            .lock_server_configuration_uri
186            .clone()
187            .ok_or(BootstrapConfigLoadingError::MissingLockServerConfigUri)?
188            .parse()?;
189
190        let ssa_jwt = raw.lock_ssa_jwt.clone();
191        #[cfg(not(target_arch = "wasm32"))]
192        let access_token_jwt = raw.lock_access_token_jwt.clone();
193
194        let log_interval =
195            (raw.audit_log_interval > 0).then(|| Duration::from_secs(raw.audit_log_interval));
196        let health_interval =
197            (raw.audit_health_interval > 0).then(|| Duration::from_secs(raw.audit_health_interval));
198        let telemetry_interval = (raw.audit_telemetry_interval > 0)
199            .then(|| Duration::from_secs(raw.audit_telemetry_interval));
200
201        let listen_sse = raw.listen_sse.into();
202
203        Ok(LockServiceConfig {
204            config_uri,
205            dynamic_config: raw.dynamic_configuration.into(),
206            ssa_jwt,
207            #[cfg(not(target_arch = "wasm32"))]
208            access_token_jwt,
209            log_interval,
210            health_interval,
211            telemetry_interval,
212            listen_sse,
213            log_level: raw.log_level,
214            accept_invalid_certs: raw.accept_invalid_certs.into(),
215            transport: raw.lock_transport,
216            log_channel_capacity: raw.lock_log_channel_capacity,
217            log_max_retries: raw.lock_log_max_retries,
218        })
219    }
220}