Skip to content
This repository was archived by the owner on Sep 10, 2024. It is now read-only.

Commit a2f8d81

Browse files
committed
syn2mas: support for deactivated users & use timestamps when generating IDs
1 parent 72667b7 commit a2f8d81

File tree

2 files changed

+93
-92
lines changed

2 files changed

+93
-92
lines changed

tools/syn2mas/src/advisor.mts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -152,15 +152,6 @@ export async function advisor(): Promise<void> {
152152
);
153153
}
154154

155-
const deactivatedUsers = await count(
156-
synapse.count("*").from<SUser>("users").where({ deactivated: 1 }),
157-
);
158-
if (deactivatedUsers > 0) {
159-
error(
160-
`Synapse database contains ${deactivatedUsers} deactivated users which aren't supported during migration`,
161-
);
162-
}
163-
164155
const accessTokensWithoutDeviceId = await count(
165156
synapse
166157
.count("*")

tools/syn2mas/src/migrate.mts

Lines changed: 93 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -119,9 +119,9 @@ export async function migrate(): Promise<void> {
119119
fatals += 1;
120120
}
121121

122-
function makeUuid<T>(): UUID<T> {
123-
return id128.Uuid4.fromRaw(
124-
id128.UlidMonotonic.generate().toRaw(),
122+
function makeUuid<T>(time: Date): UUID<T> {
123+
return id128.Uuid.construct(
124+
id128.Ulid.generate({ time }).bytes,
125125
).toCanonical();
126126
}
127127

@@ -209,26 +209,24 @@ export async function migrate(): Promise<void> {
209209
let warningsForUser = 0;
210210
const executions: Execution[] = [];
211211

212-
if (user.deactivated === 1) {
213-
fatal(`Migration of deactivated users is not supported: ${user.name}`);
214-
}
215-
216212
if (user.is_guest === 1) {
217213
fatal(`Migration of guest users is not supported: ${user.name}`);
218214
}
219215

220216
// users => users
217+
const userCreatedAt = new Date(parseInt(`${user.creation_ts}`) * 1000);
221218
const masUser = {
222-
user_id: makeUuid(),
219+
user_id: makeUuid(userCreatedAt),
223220
username: localpart,
224-
created_at: new Date(parseInt(`${user.creation_ts}`) * 1000),
221+
created_at: userCreatedAt,
222+
locked_at: user.deactivated === 1 ? userCreatedAt : null,
225223
};
226224
executions.push(() => mas.insert(masUser!).into("users"));
227225
log.debug(`${stringifyAndRedact(user)} => ${stringifyAndRedact(masUser)}`);
228226
// users.password_hash => user_passwords
229227
if (user.password_hash) {
230228
const masUserPassword: MUserPassword = {
231-
user_password_id: makeUuid(),
229+
user_password_id: makeUuid(userCreatedAt),
232230
user_id: masUser.user_id,
233231
hashed_password: user.password_hash,
234232
created_at: masUser.created_at, // TODO: should we use now() instead of created_at?
@@ -257,11 +255,14 @@ export async function migrate(): Promise<void> {
257255
);
258256
continue;
259257
}
258+
const threePidCreatedAt = new Date(
259+
parseInt(`${threePid.added_at}`) * 1000,
260+
);
260261
const masUserEmail: MUserEmail = {
261-
user_email_id: makeUuid(),
262+
user_email_id: makeUuid(threePidCreatedAt),
262263
user_id: masUser.user_id,
263264
email: threePid.address.toLowerCase(),
264-
created_at: new Date(parseInt(`${threePid.added_at}`) * 1000),
265+
created_at: threePidCreatedAt,
265266
};
266267

267268
if (threePid.validated_at) {
@@ -305,7 +306,7 @@ export async function migrate(): Promise<void> {
305306
}
306307
const provider = upstreamProviders.get(externalId.auth_provider)!;
307308
const masUpstreamOauthLink: MUpstreamOauthLink = {
308-
upstream_oauth_link_id: makeUuid(),
309+
upstream_oauth_link_id: makeUuid(userCreatedAt),
309310
user_id: masUser.user_id,
310311
upstream_oauth_provider_id: provider.upstream_oauth_provider_id,
311312
subject: externalId.external_id,
@@ -328,80 +329,89 @@ export async function migrate(): Promise<void> {
328329
}
329330
}
330331

331-
// access_tokens,refresh_tokens => compat_sessions,compat_access_tokens
332-
const synapseAccessTokens = await synapse
333-
.select("*")
334-
.from<SAccessToken>("access_tokens")
335-
.where({ user_id: user.name });
336-
for (const accessToken of synapseAccessTokens) {
337-
if (!accessToken.device_id) {
338-
warningsForUser += 1;
339-
warn(
340-
`Skipping access token ${accessToken.token} for user ${user.name} with no device_id`,
341-
);
342-
continue;
343-
}
344-
345-
const masCompatSession: MCompatSession = {
346-
compat_session_id: makeUuid(),
347-
user_id: masUser.user_id,
348-
device_id: accessToken.device_id,
349-
created_at: accessToken.last_validated
350-
? new Date(parseInt(`${accessToken.last_validated}`))
351-
: masUser.created_at,
352-
is_synapse_admin: user.admin === 1,
353-
};
354-
log.debug(
355-
`${stringifyAndRedact(accessToken)} => ${stringifyAndRedact(
356-
masCompatSession,
357-
)}`,
358-
);
359-
executions.push(() =>
360-
mas.insert(masCompatSession).into("compat_sessions"),
361-
);
362-
363-
const masCompatAccessToken: MCompatAccessToken = {
364-
compat_access_token_id: makeUuid(),
365-
compat_session_id: masCompatSession.compat_session_id,
366-
access_token: accessToken.token,
367-
created_at: masCompatSession.created_at,
368-
};
369-
log.debug(
370-
`Access token ${accessToken.id} => ${stringifyAndRedact(
371-
masCompatAccessToken,
372-
)}`,
373-
);
374-
executions.push(() =>
375-
mas.insert(masCompatAccessToken).into("compat_access_tokens"),
332+
// We only import access tokens for active users
333+
if (user.deactivated === 1) {
334+
log.info(
335+
`Skipping access tokens import for deactivated user ${user.name}`,
376336
);
377-
378-
if (accessToken.refresh_token_id) {
379-
const synapseRefreshToken = await synapse
380-
.select("*")
381-
.from<SRefreshToken>("refresh_tokens")
382-
.where({ id: accessToken.refresh_token_id })
383-
.first();
384-
if (synapseRefreshToken) {
385-
const masCompatRefreshToken: MCompatRefreshToken = {
386-
compat_refresh_token_id: makeUuid(),
387-
compat_session_id: masCompatSession.compat_session_id,
388-
compat_access_token_id: masCompatAccessToken.compat_access_token_id,
389-
refresh_token: synapseRefreshToken.token,
390-
created_at: masCompatSession.created_at,
391-
};
392-
log.debug(
393-
`Refresh token ${synapseRefreshToken.id} => ${stringifyAndRedact(
394-
masCompatRefreshToken,
395-
)}`,
396-
);
397-
executions.push(() =>
398-
mas.insert(masCompatRefreshToken).into("compat_refresh_tokens"),
399-
);
400-
} else {
337+
} else {
338+
// access_tokens,refresh_tokens => compat_sessions,compat_access_tokens
339+
const synapseAccessTokens = await synapse
340+
.select("*")
341+
.from<SAccessToken>("access_tokens")
342+
.where({ user_id: user.name });
343+
for (const accessToken of synapseAccessTokens) {
344+
if (!accessToken.device_id) {
401345
warningsForUser += 1;
402346
warn(
403-
`Unable to locate refresh token ${accessToken.refresh_token_id} for user ${user.name}`,
347+
`Skipping access token ${accessToken.token} for user ${user.name} with no device_id`,
404348
);
349+
continue;
350+
}
351+
352+
const tokenCreatedAt = accessToken.last_validated
353+
? new Date(parseInt(`${accessToken.last_validated}`))
354+
: masUser.created_at;
355+
const masCompatSession: MCompatSession = {
356+
compat_session_id: makeUuid(tokenCreatedAt),
357+
user_id: masUser.user_id,
358+
device_id: accessToken.device_id,
359+
created_at: tokenCreatedAt,
360+
is_synapse_admin: user.admin === 1,
361+
};
362+
log.debug(
363+
`${stringifyAndRedact(accessToken)} => ${stringifyAndRedact(
364+
masCompatSession,
365+
)}`,
366+
);
367+
executions.push(() =>
368+
mas.insert(masCompatSession).into("compat_sessions"),
369+
);
370+
371+
const masCompatAccessToken: MCompatAccessToken = {
372+
compat_access_token_id: makeUuid(tokenCreatedAt),
373+
compat_session_id: masCompatSession.compat_session_id,
374+
access_token: accessToken.token,
375+
created_at: tokenCreatedAt,
376+
};
377+
log.debug(
378+
`Access token ${accessToken.id} => ${stringifyAndRedact(
379+
masCompatAccessToken,
380+
)}`,
381+
);
382+
executions.push(() =>
383+
mas.insert(masCompatAccessToken).into("compat_access_tokens"),
384+
);
385+
386+
if (accessToken.refresh_token_id) {
387+
const synapseRefreshToken = await synapse
388+
.select("*")
389+
.from<SRefreshToken>("refresh_tokens")
390+
.where({ id: accessToken.refresh_token_id })
391+
.first();
392+
if (synapseRefreshToken) {
393+
const masCompatRefreshToken: MCompatRefreshToken = {
394+
compat_refresh_token_id: makeUuid(tokenCreatedAt),
395+
compat_session_id: masCompatSession.compat_session_id,
396+
compat_access_token_id:
397+
masCompatAccessToken.compat_access_token_id,
398+
refresh_token: synapseRefreshToken.token,
399+
created_at: tokenCreatedAt,
400+
};
401+
log.debug(
402+
`Refresh token ${synapseRefreshToken.id} => ${stringifyAndRedact(
403+
masCompatRefreshToken,
404+
)}`,
405+
);
406+
executions.push(() =>
407+
mas.insert(masCompatRefreshToken).into("compat_refresh_tokens"),
408+
);
409+
} else {
410+
warningsForUser += 1;
411+
warn(
412+
`Unable to locate refresh token ${accessToken.refresh_token_id} for user ${user.name}`,
413+
);
414+
}
405415
}
406416
}
407417
}

0 commit comments

Comments
 (0)