diff --git a/Gemfile b/Gemfile index 13763e699..77978a26f 100644 --- a/Gemfile +++ b/Gemfile @@ -4,7 +4,7 @@ gem 'aasm' gem 'actiontext', git: 'https://github.com/kobaltz/actiontext.git', branch: 'archive', require: 'action_text' # Port of ActionText to Rails 5 gem 'active_link_to' # Automatically set a class on active links gem 'active_model_serializers' -gem 'activestorage-openstack' +gem 'activestorage-openstack', git: 'https://github.com/fredZen/activestorage-openstack.git', branch: 'frederic/fix_upload_signature' gem 'administrate' gem 'after_party' gem 'anchored' diff --git a/Gemfile.lock b/Gemfile.lock index bbfe3c9ab..6ac80df50 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,3 +1,14 @@ +GIT + remote: https://github.com/fredZen/activestorage-openstack.git + revision: c71d5107a51701eab9d9267dd0000e6c1cf3e39a + branch: frederic/fix_upload_signature + specs: + activestorage-openstack (0.5.0) + fog-openstack (~> 1.0) + marcel + mime-types + rails (~> 5.2.0) + GIT remote: https://github.com/kobaltz/actiontext.git revision: ef59c4ba99d1b7614dd47f5a294eef553224db88 @@ -64,11 +75,6 @@ GEM actionpack (= 5.2.2.1) activerecord (= 5.2.2.1) marcel (~> 0.3.1) - activestorage-openstack (1.0.0) - fog-openstack (~> 1.0) - marcel - mime-types - rails (<= 6) activesupport (5.2.2.1) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 0.7, < 2) @@ -711,7 +717,7 @@ DEPENDENCIES actiontext! active_link_to active_model_serializers - activestorage-openstack + activestorage-openstack! administrate after_party anchored 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..fe540d591 --- /dev/null +++ b/app/lib/active_storage/service/ds_proxy_service.rb @@ -0,0 +1,57 @@ +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 object_for(key, &block) + blob_url = url(key) + if block_given? + request = Typhoeus::Request.new(blob_url) + request.on_headers do |response| + if response.code != 200 + raise Fog::OpenStack::Storage::NotFound.new + end + end + request.on_body do |chunk| + yield chunk + end + request.run + else + response = Typhoeus.get(blob_url) + if response.success? + response + else + raise Fog::OpenStack::Storage::NotFound.new + end + end + end + + 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 8837fb88b..0c93feb51 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/initializers/active_storage.rb b/config/initializers/active_storage.rb index 1d600d89c..7357c9267 100644 --- a/config/initializers/active_storage.rb +++ b/config/initializers/active_storage.rb @@ -7,33 +7,3 @@ ActiveStorage::Service.url_expires_in = 1.hour # cleaner (as it allows to enqueue the virus scan on attachment creation, rather # than on blob creation). ActiveSupport.on_load(:active_storage_blob) { include BlobVirusScanner } - -# When an OpenStack service is initialized it makes a request to fetch -# `publicURL` to use for all operations. We intercept the method that reads -# this url and replace the host with DS_Proxy host. This way all the operation -# are performed through DS_Proxy. -# -# https://github.com/fog/fog-openstack/blob/37621bb1d5ca78d037b3c56bd307f93bba022ae1/lib/fog/openstack/auth/catalog/v2.rb#L16 -require 'fog/openstack/auth/catalog/v2' - -module Fog::OpenStack::Auth::Catalog - class V2 - def endpoint_url(endpoint, interface) - url = endpoint["#{interface}URL"] - - if interface == 'public' - publicize(url) - else - url - end - end - - private - - def publicize(url) - search = %r{^https://[^/]+/} - replace = 'https://static.demarches-simplifiees.fr/' - url.gsub(search, replace) - end - end -end 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'] %>"