Class IdentityMigration
Moves the accounts of another identity store into this server's stores, keeping everything a user signs in with, and reports every record.
The application reads its old store -- a SQL query, a JSON export, an LDAP dump -- and hands the rows over; this class decides what each one becomes:
IdentityMigration migration = new IdentityMigration(users, passwords);
migration.setPasswordPrefix("django"); // the old store's rows carry no {id}
migration.setFederatedIdentities(links);
migration.setPasskeys(userEntities, credentials);
for (OldRow row : oldRows) {
migration.user(row.username, row.passwordHash, row.roles, row.enabled);
if (row.googleSub != null) {
migration.federatedIdentity("google", row.googleSub, row.username);
}
}
MigrationReport report = migration.finish();
What it guarantees:
- Usernames, subjects and authorities are written verbatim. An issued
token's
sub, a federated link and a passkey's user handle are what other systems hold on to, so none of them is normalised or regenerated. - A password hash is kept only when it can be verified. One whose
{id}the encoder does not know is not imported as a hash nobody can check: the user is created with a random password nobody knows and the report says a reset is needed. A kept hash is re-encoded with the default on its owner's next sign in, through the existing rehash-on-login path. - Nothing existing is overwritten. A user, a link, a user handle or a credential id already present is reported as skipped. Run it again after fixing the rows it skipped; what it already wrote is skipped again.
- A dry run writes nothing (
setDryRun(boolean)) and produces the same report, apart from conflicts between two rows of the same run.
What it never carries is listed in the report too, as
MigrationReport.Outcome.NOT_CARRIED: sessions, remember-me tokens,
authorization codes and refresh tokens issued by the old server, and
recovery codes. Each is either bound to the old server's secret or stored
only as a hash the old server chose, and a user signs in again to get new
ones. Signing keys are configuration, not records: see
AuthorizationServerKeys.KEY_IDS and VERIFICATION_KEYS.
Not synchronized: a migration is one pass by one thread.
-
Constructor Summary
ConstructorsConstructorDescriptionIdentityMigration(UserDetailsManager users, DelegatingPasswordEncoder passwords) -
Method Summary
Modifier and TypeMethodDescriptionvoidfederatedIdentity(String provider, String subject, String username) Links a sign-in provider's subject to an imported user, verbatim.finish()Closes the run: adds the state no migration carries and returns the report.voidpasskey(LegacyPasskey passkey) Imports a passkey, keeping its credential id and user handle.voidsetDryRun(boolean dryRun) True to write nothing and only report.voidsetFederatedIdentities(FederatedIdentityRepository repository) voidsetPasskeys(PublicKeyCredentialUserEntityRepository userEntities, UserCredentialRepository credentials) voidThe{id}to put in front of a hash that has none, such asbcryptfor a store of bare$2a$values.voidsetTotp(TotpRepository repository) voidImports a confirmed TOTP secret (the raw key, not its base32 text), so the user's authenticator app keeps working.voiduser(String username, String passwordHash, Collection<String> authorities, boolean enabled) Imports one account.
-
Constructor Details
-
IdentityMigration
- Parameters:
users- where the accounts are createdpasswords- the encoder the server verifies with; decides which imported hashes can be kept
-
-
Method Details
-
setPasswordPrefix
The{id}to put in front of a hash that has none, such asbcryptfor a store of bare$2a$values. A hash that already starts with{is left alone. Null (the default) adds nothing. -
setFederatedIdentities
-
setPasskeys
public void setPasskeys(PublicKeyCredentialUserEntityRepository userEntities, UserCredentialRepository credentials) -
setTotp
-
setDryRun
public void setDryRun(boolean dryRun) True to write nothing and only report. Set it before the first record. -
user
public void user(String username, String passwordHash, Collection<String> authorities, boolean enabled) Imports one account.- Parameters:
username- kept verbatimpasswordHash- the stored hash, with or without its{id}; null or empty for an account that never had a password (federated only)authorities- kept verbatim,ROLE_prefixes includedenabled- false keeps a disabled account disabled
-
federatedIdentity
-
passkey
Imports a passkey, keeping its credential id and user handle. -
totp
Imports a confirmed TOTP secret (the raw key, not its base32 text), so the user's authenticator app keeps working. -
finish
Closes the run: adds the state no migration carries and returns the report.
-