2024-05-02 09:31:41 +00:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
class MigrateDeviseTwoFactorSecrets < ActiveRecord::Migration[7.1]
|
|
|
|
disable_ddl_transaction!
|
|
|
|
|
|
|
|
class MigrationUser < ApplicationRecord
|
|
|
|
self.table_name = :users
|
|
|
|
|
|
|
|
devise :two_factor_authenticatable,
|
|
|
|
otp_secret_encryption_key: Rails.configuration.x.otp_secret
|
|
|
|
|
|
|
|
include LegacyOtpSecret # Must be after the above `devise` line in order to override the legacy method
|
|
|
|
end
|
|
|
|
|
|
|
|
def up
|
|
|
|
MigrationUser.reset_column_information
|
|
|
|
|
|
|
|
users_with_otp_enabled.find_each do |user|
|
|
|
|
# Gets the new value on already-updated users
|
|
|
|
# Falls back to legacy value on not-yet-migrated users
|
2024-05-20 14:59:23 +00:00
|
|
|
otp_secret = begin
|
|
|
|
user.otp_secret
|
|
|
|
rescue OpenSSL::OpenSSLError
|
|
|
|
next if ENV['MIGRATION_IGNORE_INVALID_OTP_SECRET'] == 'true'
|
|
|
|
|
|
|
|
abort_with_decryption_error(user)
|
|
|
|
end
|
2024-05-02 09:31:41 +00:00
|
|
|
|
|
|
|
Rails.logger.debug { "Processing #{user.email}" }
|
|
|
|
|
|
|
|
# This is a no-op for migrated users and updates format for not migrated
|
|
|
|
user.update!(otp_secret: otp_secret)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def down
|
|
|
|
raise ActiveRecord::IrreversibleMigration
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
|
|
|
def users_with_otp_enabled
|
|
|
|
MigrationUser.where(otp_required_for_login: true, otp_secret: nil)
|
|
|
|
end
|
2024-05-20 14:59:23 +00:00
|
|
|
|
|
|
|
def abort_with_decryption_error(user)
|
|
|
|
abort <<~MESSAGE
|
|
|
|
|
|
|
|
ERROR: Unable to decrypt OTP secret for user #{user.id}.
|
|
|
|
|
|
|
|
This is most likely because you have changed the value of `OTP_SECRET` at some point in
|
|
|
|
time after the user configured 2FA.
|
|
|
|
|
|
|
|
In this case, their OTP secret had already been lost with the change to `OTP_SECRET`, and
|
|
|
|
proceeding with this migration will not make the situation worse.
|
|
|
|
|
|
|
|
Please double-check that you have not accidentally changed `OTP_SECRET` just for this
|
|
|
|
migration, and re-run the migration with `MIGRATION_IGNORE_INVALID_OTP_SECRET=true`.
|
|
|
|
|
|
|
|
Migration aborted.
|
|
|
|
MESSAGE
|
|
|
|
end
|
2024-05-02 09:31:41 +00:00
|
|
|
end
|