From 3dacaf25efe67cdeb997dab8831e93cbf85a9bed Mon Sep 17 00:00:00 2001 From: Quentin Gliech Date: Fri, 3 Nov 2023 15:33:49 +0100 Subject: [PATCH] syn2mas: support for deactivated users & use timestamps when generating IDs --- tools/syn2mas/src/advisor.mts | 9 -- tools/syn2mas/src/migrate.mts | 176 ++++++++++++++++++---------------- 2 files changed, 93 insertions(+), 92 deletions(-) diff --git a/tools/syn2mas/src/advisor.mts b/tools/syn2mas/src/advisor.mts index 6c7505663..c3b5d7458 100644 --- a/tools/syn2mas/src/advisor.mts +++ b/tools/syn2mas/src/advisor.mts @@ -152,15 +152,6 @@ export async function advisor(): Promise { ); } - const deactivatedUsers = await count( - synapse.count("*").from("users").where({ deactivated: 1 }), - ); - if (deactivatedUsers > 0) { - error( - `Synapse database contains ${deactivatedUsers} deactivated users which aren't supported during migration`, - ); - } - const accessTokensWithoutDeviceId = await count( synapse .count("*") diff --git a/tools/syn2mas/src/migrate.mts b/tools/syn2mas/src/migrate.mts index 989e81be6..6934f3858 100644 --- a/tools/syn2mas/src/migrate.mts +++ b/tools/syn2mas/src/migrate.mts @@ -119,9 +119,9 @@ export async function migrate(): Promise { fatals += 1; } - function makeUuid(): UUID { - return id128.Uuid4.fromRaw( - id128.UlidMonotonic.generate().toRaw(), + function makeUuid(time: Date): UUID { + return id128.Uuid.construct( + id128.Ulid.generate({ time }).bytes, ).toCanonical(); } @@ -209,26 +209,24 @@ export async function migrate(): Promise { let warningsForUser = 0; const executions: Execution[] = []; - if (user.deactivated === 1) { - fatal(`Migration of deactivated users is not supported: ${user.name}`); - } - if (user.is_guest === 1) { fatal(`Migration of guest users is not supported: ${user.name}`); } // users => users + const userCreatedAt = new Date(parseInt(`${user.creation_ts}`) * 1000); const masUser = { - user_id: makeUuid(), + user_id: makeUuid(userCreatedAt), username: localpart, - created_at: new Date(parseInt(`${user.creation_ts}`) * 1000), + created_at: userCreatedAt, + locked_at: user.deactivated === 1 ? userCreatedAt : null, }; executions.push(() => mas.insert(masUser!).into("users")); log.debug(`${stringifyAndRedact(user)} => ${stringifyAndRedact(masUser)}`); // users.password_hash => user_passwords if (user.password_hash) { const masUserPassword: MUserPassword = { - user_password_id: makeUuid(), + user_password_id: makeUuid(userCreatedAt), user_id: masUser.user_id, hashed_password: user.password_hash, created_at: masUser.created_at, // TODO: should we use now() instead of created_at? @@ -257,11 +255,14 @@ export async function migrate(): Promise { ); continue; } + const threePidCreatedAt = new Date( + parseInt(`${threePid.added_at}`) * 1000, + ); const masUserEmail: MUserEmail = { - user_email_id: makeUuid(), + user_email_id: makeUuid(threePidCreatedAt), user_id: masUser.user_id, email: threePid.address.toLowerCase(), - created_at: new Date(parseInt(`${threePid.added_at}`) * 1000), + created_at: threePidCreatedAt, }; if (threePid.validated_at) { @@ -305,7 +306,7 @@ export async function migrate(): Promise { } const provider = upstreamProviders.get(externalId.auth_provider)!; const masUpstreamOauthLink: MUpstreamOauthLink = { - upstream_oauth_link_id: makeUuid(), + upstream_oauth_link_id: makeUuid(userCreatedAt), user_id: masUser.user_id, upstream_oauth_provider_id: provider.upstream_oauth_provider_id, subject: externalId.external_id, @@ -328,80 +329,89 @@ export async function migrate(): Promise { } } - // access_tokens,refresh_tokens => compat_sessions,compat_access_tokens - const synapseAccessTokens = await synapse - .select("*") - .from("access_tokens") - .where({ user_id: user.name }); - for (const accessToken of synapseAccessTokens) { - if (!accessToken.device_id) { - warningsForUser += 1; - warn( - `Skipping access token ${accessToken.token} for user ${user.name} with no device_id`, - ); - continue; - } - - const masCompatSession: MCompatSession = { - compat_session_id: makeUuid(), - user_id: masUser.user_id, - device_id: accessToken.device_id, - created_at: accessToken.last_validated - ? new Date(parseInt(`${accessToken.last_validated}`)) - : masUser.created_at, - is_synapse_admin: user.admin === 1, - }; - log.debug( - `${stringifyAndRedact(accessToken)} => ${stringifyAndRedact( - masCompatSession, - )}`, + // We only import access tokens for active users + if (user.deactivated === 1) { + log.info( + `Skipping access tokens import for deactivated user ${user.name}`, ); - executions.push(() => - mas.insert(masCompatSession).into("compat_sessions"), - ); - - const masCompatAccessToken: MCompatAccessToken = { - compat_access_token_id: makeUuid(), - compat_session_id: masCompatSession.compat_session_id, - access_token: accessToken.token, - created_at: masCompatSession.created_at, - }; - log.debug( - `Access token ${accessToken.id} => ${stringifyAndRedact( - masCompatAccessToken, - )}`, - ); - executions.push(() => - mas.insert(masCompatAccessToken).into("compat_access_tokens"), - ); - - if (accessToken.refresh_token_id) { - const synapseRefreshToken = await synapse - .select("*") - .from("refresh_tokens") - .where({ id: accessToken.refresh_token_id }) - .first(); - if (synapseRefreshToken) { - const masCompatRefreshToken: MCompatRefreshToken = { - compat_refresh_token_id: makeUuid(), - compat_session_id: masCompatSession.compat_session_id, - compat_access_token_id: masCompatAccessToken.compat_access_token_id, - refresh_token: synapseRefreshToken.token, - created_at: masCompatSession.created_at, - }; - log.debug( - `Refresh token ${synapseRefreshToken.id} => ${stringifyAndRedact( - masCompatRefreshToken, - )}`, - ); - executions.push(() => - mas.insert(masCompatRefreshToken).into("compat_refresh_tokens"), - ); - } else { + } else { + // access_tokens,refresh_tokens => compat_sessions,compat_access_tokens + const synapseAccessTokens = await synapse + .select("*") + .from("access_tokens") + .where({ user_id: user.name }); + for (const accessToken of synapseAccessTokens) { + if (!accessToken.device_id) { warningsForUser += 1; warn( - `Unable to locate refresh token ${accessToken.refresh_token_id} for user ${user.name}`, + `Skipping access token ${accessToken.token} for user ${user.name} with no device_id`, ); + continue; + } + + const tokenCreatedAt = accessToken.last_validated + ? new Date(parseInt(`${accessToken.last_validated}`)) + : masUser.created_at; + const masCompatSession: MCompatSession = { + compat_session_id: makeUuid(tokenCreatedAt), + user_id: masUser.user_id, + device_id: accessToken.device_id, + created_at: tokenCreatedAt, + is_synapse_admin: user.admin === 1, + }; + log.debug( + `${stringifyAndRedact(accessToken)} => ${stringifyAndRedact( + masCompatSession, + )}`, + ); + executions.push(() => + mas.insert(masCompatSession).into("compat_sessions"), + ); + + const masCompatAccessToken: MCompatAccessToken = { + compat_access_token_id: makeUuid(tokenCreatedAt), + compat_session_id: masCompatSession.compat_session_id, + access_token: accessToken.token, + created_at: tokenCreatedAt, + }; + log.debug( + `Access token ${accessToken.id} => ${stringifyAndRedact( + masCompatAccessToken, + )}`, + ); + executions.push(() => + mas.insert(masCompatAccessToken).into("compat_access_tokens"), + ); + + if (accessToken.refresh_token_id) { + const synapseRefreshToken = await synapse + .select("*") + .from("refresh_tokens") + .where({ id: accessToken.refresh_token_id }) + .first(); + if (synapseRefreshToken) { + const masCompatRefreshToken: MCompatRefreshToken = { + compat_refresh_token_id: makeUuid(tokenCreatedAt), + compat_session_id: masCompatSession.compat_session_id, + compat_access_token_id: + masCompatAccessToken.compat_access_token_id, + refresh_token: synapseRefreshToken.token, + created_at: tokenCreatedAt, + }; + log.debug( + `Refresh token ${synapseRefreshToken.id} => ${stringifyAndRedact( + masCompatRefreshToken, + )}`, + ); + executions.push(() => + mas.insert(masCompatRefreshToken).into("compat_refresh_tokens"), + ); + } else { + warningsForUser += 1; + warn( + `Unable to locate refresh token ${accessToken.refresh_token_id} for user ${user.name}`, + ); + } } } }