Class IdentityMigration

java.lang.Object
com.codename1.backend.security.migration.IdentityMigration

public final class IdentityMigration extends Object

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 Details

    • IdentityMigration

      public IdentityMigration(UserDetailsManager users, DelegatingPasswordEncoder passwords)
      Parameters:
      users - where the accounts are created
      passwords - the encoder the server verifies with; decides which imported hashes can be kept
  • Method Details

    • setPasswordPrefix

      public void setPasswordPrefix(String id)
      The {id} to put in front of a hash that has none, such as bcrypt for a store of bare $2a$ values. A hash that already starts with { is left alone. Null (the default) adds nothing.
    • setFederatedIdentities

      public void setFederatedIdentities(FederatedIdentityRepository repository)
    • setPasskeys

      public void setPasskeys(PublicKeyCredentialUserEntityRepository userEntities, UserCredentialRepository credentials)
    • setTotp

      public void setTotp(TotpRepository repository)
    • 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 verbatim
      passwordHash - 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 included
      enabled - false keeps a disabled account disabled
    • federatedIdentity

      public void federatedIdentity(String provider, String subject, String username)
      Links a sign-in provider's subject to an imported user, verbatim.
    • passkey

      public void passkey(LegacyPasskey passkey)
      Imports a passkey, keeping its credential id and user handle.
    • totp

      public void totp(String username, byte[] secret)
      Imports a confirmed TOTP secret (the raw key, not its base32 text), so the user's authenticator app keeps working.
    • finish

      public MigrationReport finish()
      Closes the run: adds the state no migration carries and returns the report.