# frozen_string_literal: true

class EncryptPlaintextAttributesOnApplicationSettings < ActiveRecord::Migration[5.2]
  include Gitlab::Database::MigrationHelpers

  DOWNTIME = false

  PLAINTEXT_ATTRIBUTES = %w[
    akismet_api_key
    elasticsearch_aws_secret_access_key
    recaptcha_private_key
    recaptcha_site_key
    slack_app_secret
    slack_app_verification_token
  ].freeze

  class ApplicationSetting < ActiveRecord::Base
    self.table_name = 'application_settings'

    def self.encryption_options_base_truncated_aes_256_gcm
      {
        mode: :per_attribute_iv,
        key: Gitlab::Application.secrets.db_key_base[0..31],
        algorithm: 'aes-256-gcm',
        encode: true
      }
    end

    attr_encrypted :akismet_api_key, encryption_options_base_truncated_aes_256_gcm
    attr_encrypted :elasticsearch_aws_secret_access_key, encryption_options_base_truncated_aes_256_gcm
    attr_encrypted :recaptcha_private_key, encryption_options_base_truncated_aes_256_gcm
    attr_encrypted :recaptcha_site_key, encryption_options_base_truncated_aes_256_gcm
    attr_encrypted :slack_app_secret, encryption_options_base_truncated_aes_256_gcm
    attr_encrypted :slack_app_verification_token, encryption_options_base_truncated_aes_256_gcm

    def akismet_api_key
      decrypt(:akismet_api_key, self[:encrypted_akismet_api_key]) || self[:akismet_api_key]
    end

    def elasticsearch_aws_secret_access_key
      decrypt(:elasticsearch_aws_secret_access_key, self[:encrypted_elasticsearch_aws_secret_access_key]) || self[:elasticsearch_aws_secret_access_key]
    end

    def recaptcha_private_key
      decrypt(:recaptcha_private_key, self[:encrypted_recaptcha_private_key]) || self[:recaptcha_private_key]
    end

    def recaptcha_site_key
      decrypt(:recaptcha_site_key, self[:encrypted_recaptcha_site_key]) || self[:recaptcha_site_key]
    end

    def slack_app_secret
      decrypt(:slack_app_secret, self[:encrypted_slack_app_secret]) || self[:slack_app_secret]
    end

    def slack_app_verification_token
      decrypt(:slack_app_verification_token, self[:encrypted_slack_app_verification_token]) || self[:slack_app_verification_token]
    end
  end

  def up
    ApplicationSetting.find_each do |application_setting|
      # We are using the setter from attr_encrypted gem to encrypt the data.
      # The gem updates the two columns needed to decrypt the value:
      # - "encrypted_#{plaintext_attribute}"
      # - "encrypted_#{plaintext_attribute}_iv"
      application_setting.assign_attributes(
        PLAINTEXT_ATTRIBUTES.each_with_object({}) do |plaintext_attribute, attributes|
          attributes[plaintext_attribute] = application_setting.send(plaintext_attribute)
        end
      )
      application_setting.save(validate: false)
    end
  end

  def down
    ApplicationSetting.find_each do |application_setting|
      application_setting.update_columns(
        PLAINTEXT_ATTRIBUTES.each_with_object({}) do |plaintext_attribute, attributes|
          attributes[plaintext_attribute] = application_setting.send(plaintext_attribute)
          attributes["encrypted_#{plaintext_attribute}"] = nil
          attributes["encrypted_#{plaintext_attribute}_iv"] = nil
        end
      )
    end
  end
end
