From 0846860748910d85bf5b7a3af4263d04607401bd Mon Sep 17 00:00:00 2001 From: Frederic Merizen Date: Tue, 15 Jan 2019 17:58:53 +0100 Subject: [PATCH] Proxy for active storage service provider --- .../service/ds_proxy_service.rb | 34 +++++++++++++++++ config/environments/production.rb | 2 +- config/storage.yml | 3 ++ .../service/ds_proxy_service.rb | 38 +++++++++++++++++++ 4 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 app/lib/active_storage/service/ds_proxy_service.rb create mode 100644 spec/lib/active_storage/service/ds_proxy_service.rb diff --git a/app/lib/active_storage/service/ds_proxy_service.rb b/app/lib/active_storage/service/ds_proxy_service.rb new file mode 100644 index 000000000..78c420693 --- /dev/null +++ b/app/lib/active_storage/service/ds_proxy_service.rb @@ -0,0 +1,34 @@ +module ActiveStorage + # Wraps an ActiveStorage::Service to route direct upload and direct download URLs through our proxy, + # thus avoiding exposing the storage provider’s URL to our end-users. + class Service::DsProxyService < SimpleDelegator + attr_reader :wrapped + + def self.build(wrapped:, configurator:, **options) + new(wrapped: configurator.build(wrapped)) + end + + def initialize(wrapped:) + @wrapped = wrapped + super(wrapped) + end + + def url(*args) + url = wrapped.url(*args) + publicize(url) + end + + def url_for_direct_upload(*args) + url = wrapped.url_for_direct_upload(*args) + publicize(url) + end + + private + + def publicize(url) + search = %r{^https://[^/]+/v1/AUTH_[a-f0-9]{32}} + replace = 'https://static.demarches-simplifiees.fr' + url.gsub(search, replace) + end + end +end diff --git a/config/environments/production.rb b/config/environments/production.rb index a1b4ff87b..f2474f93d 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -93,7 +93,7 @@ Rails.application.configure do # the I18n.default_locale when a translation cannot be found). config.i18n.fallbacks = true - config.active_storage.service = :openstack + config.active_storage.service = :proxied # Send deprecation notices to registered listeners. config.active_support.deprecation = :notify diff --git a/config/storage.yml b/config/storage.yml index 0427a3f7a..11de850f6 100644 --- a/config/storage.yml +++ b/config/storage.yml @@ -4,6 +4,9 @@ local: test: service: Disk root: <%= Rails.root.join("tmp/storage") %> +proxied: + service: DsProxy + wrapped: openstack openstack: service: OpenStack container: "<%= ENV['FOG_ACTIVESTORAGE_DIRECTORY'] %>" diff --git a/spec/lib/active_storage/service/ds_proxy_service.rb b/spec/lib/active_storage/service/ds_proxy_service.rb new file mode 100644 index 000000000..90814b21b --- /dev/null +++ b/spec/lib/active_storage/service/ds_proxy_service.rb @@ -0,0 +1,38 @@ +describe ActiveStorage::Service::DsProxyService do + let(:private_host) { 'storage.sbg1.cloud.ovh.net:443' } + let(:public_host) { 'static.demarches-simplifiees.fr' } + let(:auth) { 'AUTH_a24c37ed11a84896914514384898c34b' } + let(:bucket) { 'test_local' } + let(:key) { '2R6rr89nFeSRkSgXHd3smvEf' } + let(:temp_url_params) { 'temp_url_sig=5ab8cfc3ba5da2598a6c88cc6b1b461fe4e115bc&temp_url_expires=1547598179' } + + let(:storage_service) { storage_service = double(ActiveStorage::Service) } + subject { ActiveStorage::Service::DsProxyService.new(wrapped: storage_service) } + + describe '#url' do + let(:private_url) { "https://#{private_host}/v1/#{auth}/#{bucket}/#{key}?#{temp_url_params}" } + let(:public_url) { "https://#{public_host}/#{bucket}/#{key}?#{temp_url_params}" } + + before do + expect(storage_service).to receive(:url).and_return(private_url) + end + + it 'rewrites the host and removes the "v1/auth..." prefix of the storage URL' do + expect(subject.url(key)).to eq(public_url) + end + end + + describe '#url_for_direct_upload' do + let(:download_params) { 'inline&filename=documents_top_confidentiels.bmp' } + let(:private_url) { "https://#{private_host}/v1/#{auth}/#{bucket}/#{key}?#{temp_url_params}&#{download_params}" } + let(:public_url) { "https://#{public_host}/#{bucket}/#{key}?#{temp_url_params}&#{download_params}" } + + before do + expect(storage_service).to receive(:url_for_direct_upload).and_return(private_url) + end + + it 'rewrites the host and removes the "v1/auth..." prefix of the storage URL' do + expect(subject.url_for_direct_upload(key)).to eq(public_url) + end + end +end