Skip to content

Commit 66c298e

Browse files
authored
Add Tchap Configuration + Account Linking - Check if email is authorized by Tchap (#21)
* add tchap configuration * add :tchap: comment
1 parent 375c5d9 commit 66c298e

File tree

14 files changed

+347
-53
lines changed

14 files changed

+347
-53
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/cli/src/app_state.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,14 @@ use std::{convert::Infallible, net::IpAddr, sync::Arc};
99
use axum::extract::{FromRef, FromRequestParts};
1010
use ipnetwork::IpNetwork;
1111
use mas_context::LogContext;
12-
use mas_data_model::{BoxClock, BoxRng, SiteConfig, SystemClock};
12+
use mas_data_model::{
13+
BoxClock,
14+
BoxRng,
15+
SiteConfig,
16+
SystemClock,
17+
//:tchap:
18+
TchapConfig, //:tchap:end
19+
};
1320
use mas_handlers::{
1421
ActivityTracker, BoundActivityTracker, CookieManager, ErrorWrapper, GraphQLSchema, Limiter,
1522
MetadataCache, RequesterFingerprint, passwords::PasswordManager,
@@ -47,6 +54,9 @@ pub struct AppState {
4754
pub activity_tracker: ActivityTracker,
4855
pub trusted_proxies: Vec<IpNetwork>,
4956
pub limiter: Limiter,
57+
//:tchap:
58+
pub tchap_config: TchapConfig,
59+
//:tchap: end
5060
}
5161

5262
impl AppState {
@@ -214,6 +224,14 @@ impl FromRef<AppState> for Arc<dyn HomeserverConnection> {
214224
}
215225
}
216226

227+
//:tchap:
228+
impl FromRef<AppState> for TchapConfig {
229+
fn from_ref(input: &AppState) -> Self {
230+
input.tchap_config.clone()
231+
}
232+
}
233+
//:tchap:end
234+
217235
impl FromRequestParts<AppState> for BoxClock {
218236
type Rejection = Infallible;
219237

crates/cli/src/commands/server.rs

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,21 @@ use clap::Parser;
1111
use figment::Figment;
1212
use itertools::Itertools;
1313
use mas_config::{
14-
AppConfig, ClientsConfig, ConfigurationSection, ConfigurationSectionExt, UpstreamOAuth2Config,
14+
AppConfig,
15+
ClientsConfig,
16+
ConfigurationSection,
17+
ConfigurationSectionExt,
18+
//:tchap:
19+
TchapAppConfig,
20+
// :tchap: end
21+
UpstreamOAuth2Config,
1522
};
1623
use mas_context::LogContext;
17-
use mas_data_model::SystemClock;
24+
use mas_data_model::{
25+
SystemClock,
26+
//:tchap:
27+
TchapConfig, // :tchap: end
28+
};
1829
use mas_handlers::{ActivityTracker, CookieManager, Limiter, MetadataCache};
1930
use mas_listener::server::Server;
2031
use mas_router::UrlBuilder;
@@ -59,6 +70,10 @@ impl Options {
5970
let span = info_span!("cli.run.init").entered();
6071
let mut shutdown = LifecycleManager::new()?;
6172
let config = AppConfig::extract(figment).map_err(anyhow::Error::from_boxed)?;
73+
//:tchap:
74+
let tchap_app_config =
75+
TchapAppConfig::extract(figment).map_err(anyhow::Error::from_boxed)?;
76+
//:tchap: end
6277

6378
info!(version = crate::VERSION, "Starting up");
6479

@@ -159,6 +174,10 @@ impl Options {
159174
&config.captcha,
160175
)?;
161176

177+
//:tchap:
178+
let tchap_config = tchap_config_from_tchap_app_config(&tchap_app_config);
179+
//:tchap: end
180+
162181
// Load and compile the templates
163182
let templates =
164183
templates_from_config(&config.templates, &site_config, &url_builder).await?;
@@ -246,6 +265,9 @@ impl Options {
246265
activity_tracker,
247266
trusted_proxies,
248267
limiter,
268+
//:tchap:
269+
tchap_config,
270+
//:tchap:end
249271
};
250272
s.init_metrics();
251273
s.init_metadata_cache();
@@ -334,3 +356,11 @@ impl Options {
334356
Ok(exit_code)
335357
}
336358
}
359+
360+
//:tchap:
361+
fn tchap_config_from_tchap_app_config(tchap_app_config: &TchapAppConfig) -> TchapConfig {
362+
TchapConfig {
363+
identity_server_url: tchap_app_config.identity_server_url.clone(),
364+
}
365+
}
366+
//:tchap: end

crates/config/src/sections/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ mod passwords;
2121
mod policy;
2222
mod rate_limiting;
2323
mod secrets;
24+
//:tchap:
25+
mod tchap;
26+
//:tchap:end
2427
mod telemetry;
2528
mod templates;
2629
mod upstream_oauth2;
@@ -44,6 +47,9 @@ pub use self::{
4447
policy::PolicyConfig,
4548
rate_limiting::RateLimitingConfig,
4649
secrets::SecretsConfig,
50+
//:tchap:
51+
tchap::TchapAppConfig,
52+
//:tchap:end
4753
telemetry::{
4854
MetricsConfig, MetricsExporterKind, Propagator, TelemetryConfig, TracingConfig,
4955
TracingExporterKind,
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
//
2+
// MIT License
3+
//
4+
// Copyright (c) 2025, Direction interministérielle du numérique - Gouvernement
5+
// Français
6+
//
7+
// Permission is hereby granted, free of charge, to any person obtaining a copy
8+
// of this software and associated documentation files (the "Software"), to
9+
// deal in the Software without restriction, including without limitation the
10+
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
11+
// sell copies of the Software, and to permit persons to whom the Software is
12+
// furnished to do so, subject to the following conditions:
13+
//
14+
// The above copyright notice and this permission notice shall be included in
15+
// all copies or substantial portions of the Software.
16+
//
17+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18+
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19+
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20+
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
21+
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
22+
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
23+
// USE OR OTHER DEALINGS IN THE SOFTWARE.
24+
//
25+
26+
use schemars::JsonSchema;
27+
use serde::{Deserialize, Serialize};
28+
use serde_with::serde_as;
29+
use url::Url;
30+
31+
use super::ConfigurationSection;
32+
33+
fn default_identity_server_url() -> Url {
34+
Url::parse("http://localhost:8090/").unwrap()
35+
}
36+
37+
/// Tchap specific configuration
38+
#[serde_as]
39+
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
40+
pub struct TchapAppConfig {
41+
/// Identity Server Url
42+
#[serde(default = "default_identity_server_url")]
43+
pub identity_server_url: Url,
44+
}
45+
46+
impl ConfigurationSection for TchapAppConfig {
47+
const PATH: Option<&'static str> = Some("tchap");
48+
49+
// NOTE: implement this function to perform validation on config
50+
fn validate(
51+
&self,
52+
_figment: &figment::Figment,
53+
) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
54+
Ok(())
55+
}
56+
}
57+
58+
#[cfg(test)]
59+
mod tests {
60+
use figment::{
61+
Figment, Jail,
62+
providers::{Format, Yaml},
63+
};
64+
65+
use super::*;
66+
67+
#[test]
68+
fn load_config() {
69+
Jail::expect_with(|jail| {
70+
jail.create_file(
71+
"config.yaml",
72+
r"
73+
tchap:
74+
identity_server_url: http://localhost:8091
75+
",
76+
)?;
77+
78+
let config = Figment::new()
79+
.merge(Yaml::file("config.yaml"))
80+
.extract_inner::<TchapAppConfig>("tchap")?;
81+
82+
assert_eq!(
83+
&config.identity_server_url.as_str().to_owned(),
84+
"http://localhost:8091/"
85+
);
86+
87+
Ok(())
88+
});
89+
}
90+
}

crates/data-model/src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ pub(crate) mod compat;
1313
pub mod oauth2;
1414
pub(crate) mod policy_data;
1515
mod site_config;
16+
//:tchap:
17+
pub(crate) mod tchap_config;
18+
//:tchap:end
1619
pub(crate) mod tokens;
1720
pub(crate) mod upstream_oauth2;
1821
pub(crate) mod user_agent;
@@ -38,6 +41,9 @@ pub use self::{
3841
},
3942
policy_data::PolicyData,
4043
site_config::{CaptchaConfig, CaptchaService, SessionExpirationConfig, SiteConfig},
44+
//:tchap:
45+
tchap_config::*,
46+
//:tchap:end
4147
tokens::{
4248
AccessToken, AccessTokenState, RefreshToken, RefreshTokenState, TokenFormatError, TokenType,
4349
},
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//
2+
// MIT License
3+
//
4+
// Copyright (c) 2025, Direction interministérielle du numérique - Gouvernement
5+
// Français
6+
//
7+
// Permission is hereby granted, free of charge, to any person obtaining a copy
8+
// of this software and associated documentation files (the "Software"), to
9+
// deal in the Software without restriction, including without limitation the
10+
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
11+
// sell copies of the Software, and to permit persons to whom the Software is
12+
// furnished to do so, subject to the following conditions:
13+
//
14+
// The above copyright notice and this permission notice shall be included in
15+
// all copies or substantial portions of the Software.
16+
//
17+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18+
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19+
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20+
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
21+
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
22+
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
23+
// USE OR OTHER DEALINGS IN THE SOFTWARE.
24+
//
25+
26+
use url::Url;
27+
28+
/// Random tchap configuration we want accessible in various places.
29+
#[allow(clippy::struct_excessive_bools)]
30+
#[derive(Debug, Clone)]
31+
pub struct TchapConfig {
32+
/// Identity Server Url
33+
pub identity_server_url: Url,
34+
}

crates/handlers/src/lib.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,11 @@ use hyper::{
3636
},
3737
};
3838
use mas_axum_utils::{InternalError, cookies::CookieJar};
39-
use mas_data_model::SiteConfig;
39+
use mas_data_model::{
40+
SiteConfig,
41+
//:tchap:
42+
TchapConfig, //:tchap:end
43+
};
4044
use mas_http::CorsLayerExt;
4145
use mas_keystore::{Encrypter, Keystore};
4246
use mas_matrix::HomeserverConnection;
@@ -350,6 +354,9 @@ where
350354
BoxClock: FromRequestParts<S>,
351355
BoxRng: FromRequestParts<S>,
352356
Policy: FromRequestParts<S>,
357+
//:tchap:
358+
TchapConfig: FromRef<S>,
359+
//:tchap:end
353360
{
354361
Router::new()
355362
// XXX: hard-coded redirect from /account to /account/

crates/handlers/src/test_utils.rs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,15 @@ use mas_axum_utils::{
2828
cookies::{CookieJar, CookieManager},
2929
};
3030
use mas_config::RateLimitingConfig;
31-
use mas_data_model::{BoxClock, BoxRng, SiteConfig, clock::MockClock};
31+
use mas_data_model::{
32+
BoxClock,
33+
BoxRng,
34+
SiteConfig,
35+
//:tchap:
36+
TchapConfig,
37+
//:tchap:end
38+
clock::MockClock,
39+
};
3240
use mas_email::{MailTransport, Mailer};
3341
use mas_i18n::Translator;
3442
use mas_keystore::{Encrypter, JsonWebKey, JsonWebKeySet, Keystore, PrivateKey};
@@ -112,6 +120,9 @@ pub(crate) struct TestState {
112120
pub rng: Arc<Mutex<ChaChaRng>>,
113121
pub http_client: reqwest::Client,
114122
pub task_tracker: TaskTracker,
123+
//:tchap:
124+
pub tchap_config: TchapConfig,
125+
//:tchap:end
115126
queue_worker: Arc<tokio::sync::Mutex<QueueWorker>>,
116127

117128
#[allow(dead_code)] // It is used, as it will cancel the CancellationToken when dropped
@@ -256,6 +267,10 @@ impl TestState {
256267

257268
let queue_worker = Arc::new(tokio::sync::Mutex::new(queue_worker));
258269

270+
//:tchap:
271+
let tchap_config = tchap::test_tchap_config();
272+
//:tchap:end
273+
259274
Ok(Self {
260275
repository_factory: PgRepositoryFactory::new(pool),
261276
templates,
@@ -277,6 +292,9 @@ impl TestState {
277292
task_tracker,
278293
queue_worker,
279294
cancellation_drop_guard: Arc::new(shutdown_token.drop_guard()),
295+
//:tchap:
296+
tchap_config,
297+
//:tchap:end
280298
})
281299
}
282300

@@ -575,6 +593,14 @@ impl FromRef<TestState> for reqwest::Client {
575593
}
576594
}
577595

596+
//:tchap:
597+
impl FromRef<TestState> for TchapConfig {
598+
fn from_ref(input: &TestState) -> Self {
599+
input.tchap_config.clone()
600+
}
601+
}
602+
//:tchap:end
603+
578604
impl FromRequestParts<TestState> for ActivityTracker {
579605
type Rejection = Infallible;
580606

0 commit comments

Comments
 (0)