Skip to content

Commit fec4efd

Browse files
authored
syn2mas: Support migrating external IDs as upstream OAuth2 providers (#3917)
* Add `SynapseReader` support and test for external IDs * Run database migrations and do a config sync before syn2mas * FullUserId: implement Display * Add `MasWriter` support and test for upstream OAuth provider links * Remove special-purpose write buffers and use only the generic one * Build the provider ID mapping
1 parent de597da commit fec4efd

12 files changed

+508
-160
lines changed

crates/cli/src/commands/syn2mas.rs

Lines changed: 60 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
1-
use std::process::ExitCode;
1+
use std::{collections::HashMap, process::ExitCode};
22

33
use anyhow::Context;
44
use camino::Utf8PathBuf;
55
use clap::Parser;
66
use figment::Figment;
7-
use mas_config::{ConfigurationSection, ConfigurationSectionExt, DatabaseConfig, MatrixConfig};
7+
use mas_config::{
8+
ConfigurationSection, ConfigurationSectionExt, DatabaseConfig, MatrixConfig, SyncConfig,
9+
UpstreamOAuth2Config,
10+
};
11+
use mas_storage::SystemClock;
12+
use mas_storage_pg::MIGRATOR;
813
use rand::thread_rng;
9-
use sqlx::{postgres::PgConnectOptions, Connection, Either, PgConnection};
14+
use sqlx::{postgres::PgConnectOptions, types::Uuid, Connection, Either, PgConnection};
1015
use syn2mas::{synapse_config, LockedMasDatabase, MasWriter, SynapseReader};
11-
use tracing::{error, warn};
16+
use tracing::{error, info_span, warn, Instrument};
1217

1318
use crate::util::database_connection_from_config;
1419

@@ -75,6 +80,7 @@ enum Subcommand {
7580
const NUM_WRITER_CONNECTIONS: usize = 8;
7681

7782
impl Options {
83+
#[allow(clippy::too_many_lines)]
7884
pub async fn run(self, figment: &Figment) -> anyhow::Result<ExitCode> {
7985
warn!("This version of the syn2mas tool is EXPERIMENTAL and INCOMPLETE. Do not use it, except for TESTING.");
8086
if !self.experimental_accepted {
@@ -107,6 +113,35 @@ impl Options {
107113

108114
let mut mas_connection = database_connection_from_config(&config).await?;
109115

116+
MIGRATOR
117+
.run(&mut mas_connection)
118+
.instrument(info_span!("db.migrate"))
119+
.await
120+
.context("could not run migrations")?;
121+
122+
if matches!(&self.subcommand, Subcommand::Migrate { .. }) {
123+
// First perform a config sync
124+
// This is crucial to ensure we register upstream OAuth providers
125+
// in the MAS database
126+
//
127+
let config = SyncConfig::extract(figment)?;
128+
let clock = SystemClock::default();
129+
let encrypter = config.secrets.encrypter();
130+
131+
crate::sync::config_sync(
132+
config.upstream_oauth2,
133+
config.clients,
134+
&mut mas_connection,
135+
&encrypter,
136+
&clock,
137+
// Don't prune — we don't want to be unnecessarily destructive
138+
false,
139+
// Not a dry run — we do want to create the providers in the database
140+
false,
141+
)
142+
.await?;
143+
}
144+
110145
let Either::Left(mut mas_connection) = LockedMasDatabase::try_new(&mut mas_connection)
111146
.await
112147
.context("failed to issue query to lock database")?
@@ -166,6 +201,19 @@ impl Options {
166201
Ok(ExitCode::SUCCESS)
167202
}
168203
Subcommand::Migrate => {
204+
let provider_id_mappings: HashMap<String, Uuid> = {
205+
let mas_oauth2 = UpstreamOAuth2Config::extract_or_default(figment)?;
206+
207+
mas_oauth2
208+
.providers
209+
.iter()
210+
.filter_map(|provider| {
211+
let synapse_idp_id = provider.synapse_idp_id.clone()?;
212+
Some((synapse_idp_id, Uuid::from(provider.id)))
213+
})
214+
.collect()
215+
};
216+
169217
// TODO how should we handle warnings at this stage?
170218

171219
let mut reader = SynapseReader::new(&mut syn_conn, true).await?;
@@ -181,8 +229,14 @@ impl Options {
181229

182230
// TODO progress reporting
183231
let mas_matrix = MatrixConfig::extract(figment)?;
184-
syn2mas::migrate(&mut reader, &mut writer, &mas_matrix.homeserver, &mut rng)
185-
.await?;
232+
syn2mas::migrate(
233+
&mut reader,
234+
&mut writer,
235+
&mas_matrix.homeserver,
236+
&mut rng,
237+
&provider_id_mappings,
238+
)
239+
.await?;
186240

187241
reader.finish().await?;
188242
writer.finish().await?;

crates/syn2mas/.sqlx/query-d79fd99ebed9033711f96113005096c848ae87c43b6430246ef3b6a1dc6a7a32.json

Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
INSERT INTO upstream_oauth_providers
2+
(
3+
upstream_oauth_provider_id,
4+
scope,
5+
client_id,
6+
token_endpoint_auth_method,
7+
created_at
8+
)
9+
VALUES
10+
(
11+
'00000000-0000-0000-0000-000000000004',
12+
'openid',
13+
'someClientId',
14+
'client_secret_basic',
15+
'2011-12-13 14:15:16Z'
16+
);

0 commit comments

Comments
 (0)