From e303bf3048503fb29869207e7611430546d63203 Mon Sep 17 00:00:00 2001 From: Denis Date: Thu, 8 Aug 2019 14:55:09 +0300 Subject: [PATCH] Update migration --- Gemfile | 2 +- Gemfile.lock | 4 +- app/models/payment_address.rb | 4 +- app/models/wallet.rb | 3 +- config/initializers/vault.rb | 4 +- ...add_encrypted_secret_to_payment_address.rb | 57 ++++++++++++++++--- db/schema.rb | 4 +- spec/models/payment_address_spec.rb | 33 +++++++++-- spec/models/wallet_spec.rb | 28 +++++++++ 9 files changed, 117 insertions(+), 22 deletions(-) diff --git a/Gemfile b/Gemfile index 6ca853a19c..dc6c3bc430 100644 --- a/Gemfile +++ b/Gemfile @@ -55,7 +55,7 @@ gem 'peatio', '~> 0.6.1' gem 'rack-cors', '~> 1.0.2', require: false gem 'env-tweaks', '~> 1.0.0' gem 'vault', '~> 0.12', require: false -gem 'vault-rails', '~> 0.5.0', git: 'http://github.com/dnfd/vault-rails' +gem 'vault-rails', '~> 0.5.0', git: 'http://github.com/rubykube/vault-rails' gem 'bootsnap', '>= 1.1.0', require: false gem 'net-http-persistent', '~> 3.0.1' diff --git a/Gemfile.lock b/Gemfile.lock index 6d6514ef9c..0da5574858 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,6 +1,6 @@ GIT - remote: http://github.com/dnfd/vault-rails - revision: 1d191f32709a478675894c17c720cb5f7493e09a + remote: http://github.com/rubykube/vault-rails + revision: ef9b8626e4bf41dcea8696c4aec91e543ddd80a5 specs: vault-rails (0.5.0) rails (>= 4.1) diff --git a/app/models/payment_address.rb b/app/models/payment_address.rb index df0431cc64..2db08576a1 100644 --- a/app/models/payment_address.rb +++ b/app/models/payment_address.rb @@ -6,6 +6,8 @@ class PaymentAddress < ApplicationRecord include BelongsToCurrency include BelongsToAccount + vault_lazy_decrypt! + after_commit :enqueue_address_generation validates :address, uniqueness: { scope: :currency_id }, if: :address? @@ -53,7 +55,7 @@ def to_cash_address # account_id :integer not null # address :string(95) # secret_encrypted :string(255) -# details_encrypted :string(255) +# details_encrypted :string(1024) # created_at :datetime not null # updated_at :datetime not null # diff --git a/app/models/wallet.rb b/app/models/wallet.rb index b9f76a1ee4..d3da401a7d 100644 --- a/app/models/wallet.rb +++ b/app/models/wallet.rb @@ -15,6 +15,7 @@ class Wallet < ApplicationRecord ENUMERIZED_KINDS = { deposit: 100, fee: 200, hot: 310, warm: 320, cold: 330 }.freeze enumerize :kind, in: ENUMERIZED_KINDS, scope: true + # Remove after admin panel deletion. SETTING_ATTRIBUTES = %i[ uri secret bitgo_test_net @@ -123,10 +124,10 @@ def wallet_url # kind :integer not null # nsig :integer # gateway :string(20) default(""), not null +# settings_encrypted :string(1024) # max_balance :decimal(32, 16) default(0.0), not null # parent :integer # status :string(32) -# settings_encrypted :string(255) # created_at :datetime not null # updated_at :datetime not null # diff --git a/config/initializers/vault.rb b/config/initializers/vault.rb index 7efdc18f14..1ff3b24698 100644 --- a/config/initializers/vault.rb +++ b/config/initializers/vault.rb @@ -2,12 +2,12 @@ # frozen_string_literal: true require 'vault/totp' -require "vault/rails" +require 'vault/rails' Vault.configure do |config| config.address = ENV.fetch('VAULT_URL', 'http://127.0.0.1:8200') config.token = ENV.fetch('VAULT_TOKEN') config.ssl_verify = false config.timeout = 60 - config.application = 'peatio' + config.application = ENV.fetch('VAULT_APP_NAME', 'peatio') end diff --git a/db/migrate/20190807092706_add_encrypted_secret_to_payment_address.rb b/db/migrate/20190807092706_add_encrypted_secret_to_payment_address.rb index c376c900f7..112eb38248 100644 --- a/db/migrate/20190807092706_add_encrypted_secret_to_payment_address.rb +++ b/db/migrate/20190807092706_add_encrypted_secret_to_payment_address.rb @@ -1,26 +1,67 @@ class AddEncryptedSecretToPaymentAddress < ActiveRecord::Migration[5.2] def up secrets = PaymentAddress.pluck(:id, :secret) - # details = PaymentAddress.pluck(:id, :details) - # settings = Wallet.pluck(:id, :settings) + details = PaymentAddress.pluck(:id, :details) + settings = Wallet.pluck(:id, :settings) remove_column :payment_addresses, :secret add_column :payment_addresses, :secret_encrypted , :string, after: :address remove_column :payment_addresses, :details - add_column :payment_addresses, :details_encrypted , :string, after: :secret_encrypted + add_column :payment_addresses, :details_encrypted , :string, limit: 1024, after: :secret_encrypted remove_column :wallets, :settings - add_column :wallets, :settings_encrypted , :string, after: :status + add_column :wallets, :settings_encrypted , :string, limit: 1024, after: :gateway secrets.each do |s| atr = PaymentAddress.__vault_attributes[:secret] enc = Vault::Rails.encrypt(atr[:path], atr[:key], s[1]) - # PaymentAddress.find(s[0]).update!(secret: s[1]) - execute "UPDATE payment_addresses SET secret_encrypted = #{enc} WHERE id = #{s[0]}" + execute "UPDATE payment_addresses SET #{atr[:encrypted_column]} = '#{enc}' WHERE id = #{s[0]}" + end + + details.each do |d| + atr = PaymentAddress.__vault_attributes[:details] + enc = Vault::Rails.encrypt(atr[:path], atr[:key], d[1]) + execute "UPDATE payment_addresses SET #{atr[:encrypted_column]} = '#{enc}' WHERE id = #{d[0]}" + end + + settings.each do |s| + atr = Wallet.__vault_attributes[:settings] + enc = Vault::Rails.encrypt(atr[:path], atr[:key], s[1]) + execute "UPDATE wallets SET #{atr[:encrypted_column]} = '#{enc}' WHERE id = #{s[0]}" end - # details.each { |s| PaymentAddress.find(s[0]).update(details_encrypted: s[1]) } - # settings.each { |s| Wallet.find(s[0]).update(settings_encrypted: s[1]) } end + def down + secrets = PaymentAddress.pluck(:id, :secret_encrypted) + details = PaymentAddress.pluck(:id, :details_encrypted) + settings = Wallet.pluck(:id, :settings_encrypted) + + add_column :payment_addresses, :secret, :string, limit: 128, after: :address + remove_column :payment_addresses, :secret_encrypted , :string, after: :address + + add_column :payment_addresses, :details, :string, limit: 1.kilobyte, null: false, default: '{}', after: :secret + remove_column :payment_addresses, :details_encrypted , :string, limit: 1024, after: :secret_encrypted + + add_column :wallets, :settings, :string, limit: 1000, default: '{}', null: false, after: :gateway + remove_column :wallets, :settings_encrypted , :string, limit: 1024, after: :gateway + + secrets.each do |s| + atr = PaymentAddress.__vault_attributes[:secret] + dec = Vault::Rails.decrypt(atr[:path], atr[:key], s[1]) + execute "UPDATE payment_addresses SET secret = '#{dec}' WHERE id = #{s[0]}" + end + + details.each do |d| + atr = PaymentAddress.__vault_attributes[:details] + dec = Vault::Rails.decrypt(atr[:path], atr[:key], d[1]) + execute "UPDATE payment_addresses SET details = '#{dec}' WHERE id = #{d[0]}" + end + + settings.each do |s| + atr = Wallet.__vault_attributes[:settings] + dec = Vault::Rails.decrypt(atr[:path], atr[:key], s[1]) + execute "UPDATE wallets SET settings = '#{dec}' WHERE id = #{s[0]}" + end + end end diff --git a/db/schema.rb b/db/schema.rb index d35fab7db8..2feda2f9d7 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -209,7 +209,7 @@ t.integer "account_id", null: false t.string "address", limit: 95 t.string "secret_encrypted" - t.string "details_encrypted" + t.string "details_encrypted", limit: 1024 t.datetime "created_at", null: false t.datetime "updated_at", null: false t.index ["currency_id", "address"], name: "index_payment_addresses_on_currency_id_and_address", unique: true @@ -278,10 +278,10 @@ t.integer "kind", null: false t.integer "nsig" t.string "gateway", limit: 20, default: "", null: false + t.string "settings_encrypted", limit: 1024 t.decimal "max_balance", precision: 32, scale: 16, default: "0.0", null: false t.integer "parent" t.string "status", limit: 32 - t.string "settings_encrypted" t.datetime "created_at", null: false t.datetime "updated_at", null: false t.index ["currency_id"], name: "index_wallets_on_currency_id" diff --git a/spec/models/payment_address_spec.rb b/spec/models/payment_address_spec.rb index 7988429ce0..1d05d0e3de 100644 --- a/spec/models/payment_address_spec.rb +++ b/spec/models/payment_address_spec.rb @@ -5,15 +5,38 @@ context '.create' do let(:member) { create(:member, :level_3) } let!(:account) { member.get_account(:btc) } + let(:secret) { 's3cr3t' } + let(:details) { { 'a' => 'b', 'b' => 'c' } } + let!(:addr) { create(:payment_address, :btc_address, secret: secret) } - after do - DatabaseCleaner.strategy = :truncation - end - - it 'generate address after commit', clean_database_with_truncation: true do + it 'generate address after commit' do AMQPQueue.expects(:enqueue) .with(:deposit_coin_address, { account_id: account.id }, { persistent: true }) account.payment_address end + + it 'updates secret' do + expect { + addr.update(secret: 'new_secret') + }.to change { addr.reload.secret_encrypted }.and change { addr.reload.secret }.to 'new_secret' + end + + it 'updates details' do + expect { + addr.update(details: details) + }.to change { addr.reload.details_encrypted }.and change { addr.reload.details }.to details + end + + it 'long secret' do + expect { + addr.update(secret: Faker::String.random(1024)) + }.to raise_error ActiveRecord::ValueTooLong + end + + it 'long details' do + expect { + addr.update(details: { test: Faker::String.random(1024) }) + }.to raise_error ActiveRecord::ValueTooLong + end end end diff --git a/spec/models/wallet_spec.rb b/spec/models/wallet_spec.rb index 926f5ef35f..fe4e2378d3 100644 --- a/spec/models/wallet_spec.rb +++ b/spec/models/wallet_spec.rb @@ -51,5 +51,33 @@ expect(subject).to_not be_valid expect(subject.errors.full_messages).to eq ['Name has already been taken'] end + + it 'saves settings in encrypted column' do + subject.save + expect { + subject.uri = 'http://geth:8545/' + subject.save + }.to change { subject.settings_encrypted } + end + + it 'does not update settings_encrypted before model is saved' do + subject.save + expect { + subject.uri = 'http://geth:8545/' + }.not_to change { subject.settings_encrypted } + end + + it 'updates setting fields' do + expect { + subject.uri = 'http://geth:8545/' + }.to change { subject.settings['uri'] }.to 'http://geth:8545/' + end + + it 'long encrypted secret' do + expect { + subject.secret = Faker::String.random(1024) + subject.save! + }.to raise_error ActiveRecord::ValueTooLong + end end end