|
module Migrations |
|
class MigrateEncryption |
|
module DSL |
|
extend ActiveSupport::Concern |
|
|
|
included do |
|
class_attribute :migrate_encrypted_attributes |
|
end |
|
|
|
class_methods do |
|
def migrate_encryption_for(*names) |
|
self.migrate_encrypted_attributes ||= Set.new |
|
names.each do |name| |
|
migrate_encrypted_attribute(name) |
|
end |
|
end |
|
|
|
private |
|
def migrate_encrypted_attribute(name) |
|
migrate_encrypted_attributes << name.to_sym |
|
|
|
alias_method :"original_#{name}=", :"#{name}=" |
|
define_method "#{name}=" do |value| |
|
send("original_#{name}=", value) |
|
send("ar_enc_#{name}=", value) |
|
end |
|
|
|
define_method "ar_enc_#{name}=" do |value| |
|
scheme = self.class.send(:scheme_for) |
|
type = ActiveRecord::Encryption::EncryptedAttributeType.new(scheme: scheme) |
|
write_attribute(name, type.cast(value)) |
|
end |
|
|
|
define_method "ar_enc_#{name}" do |
|
scheme = self.class.send(:scheme_for) |
|
type = ActiveRecord::Encryption::EncryptedAttributeType.new(scheme: scheme) |
|
type.deserialize(read_attribute_before_type_cast(name)) |
|
end |
|
end |
|
end |
|
|
|
def migrate_encryption! |
|
return if self.class.migrate_encrypted_attributes.empty? |
|
|
|
self.class.migrate_encrypted_attributes.each do |name| |
|
send("ar_enc_#{name}=", send(name)) |
|
end |
|
|
|
save! |
|
end |
|
end |
|
|
|
# This isn't strictly required but it's what we use. |
|
include Metaractor |
|
|
|
# This is a _very_ simple approach to this data migration. If you have a lot of data |
|
# to work through, you're going to need to get fancy. |
|
def call |
|
[ |
|
# List of models here. |
|
].each do |klass| |
|
puts "Starting #{klass}" |
|
|
|
klass.find_each.with_index do |model, index| |
|
id = model.id |
|
|
|
if index % 10 == 0 |
|
puts id |
|
end |
|
|
|
model.migrate_encryption! |
|
rescue |
|
puts "Error! Failed on id #{id}" |
|
end |
|
end |
|
end |
|
end |
|
end |