diff --git a/app/controllers/active_storage/blobs/redirect_controller.rb b/app/controllers/active_storage/blobs/redirect_controller.rb new file mode 100644 index 0000000000000000000000000000000000000000..906eade463657346c1ad3b77dcbc796398bc6d4d --- /dev/null +++ b/app/controllers/active_storage/blobs/redirect_controller.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +# Take a signed permanent reference for a blob and turn it into an expiring service URL for download. +# Note: These URLs are publicly accessible. If you need to enforce access protection beyond the +# security-through-obscurity factor of the signed blob references, you'll need to implement your own +# authenticated redirection controller. +class ActiveStorage::Blobs::RedirectController < ActiveStorage::BaseController + include ActiveStorage::CheckAbilities + include ActiveStorage::SetBlob + + before_action :check_abilities, only: :show + skip_before_action :handle_two_factor_authentication + + def show + expires_in ActiveStorage.service_urls_expire_in + redirect_to @blob.url(disposition: params[:disposition]) + end + +end diff --git a/app/controllers/active_storage/representations/redirect_controller.rb b/app/controllers/active_storage/representations/redirect_controller.rb new file mode 100644 index 0000000000000000000000000000000000000000..a4c71baaf08571247075b9aad58ee6f43d5a9456 --- /dev/null +++ b/app/controllers/active_storage/representations/redirect_controller.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +# Take a signed permanent reference for a blob representation and turn it into an expiring service URL for download. +# Note: These URLs are publicly accessible. If you need to enforce access protection beyond the +# security-through-obscurity factor of the signed blob and variation reference, you'll need to implement your own +# authenticated redirection controller. +class ActiveStorage::Representations::RedirectController < ActiveStorage::Representations::BaseController + include ActiveStorage::CheckAbilities + + before_action :check_abilities, only: :show + skip_before_action :handle_two_factor_authentication + + def show + expires_in ActiveStorage.service_urls_expire_in + redirect_to @representation.url(disposition: params[:disposition]) + end + +end diff --git a/app/controllers/concerns/.keep b/app/controllers/concerns/.keep deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/app/controllers/concerns/active_storage/check_abilities.rb b/app/controllers/concerns/active_storage/check_abilities.rb new file mode 100644 index 0000000000000000000000000000000000000000..ea59d518d0694300f58b80a06ea371d6542baba1 --- /dev/null +++ b/app/controllers/concerns/active_storage/check_abilities.rb @@ -0,0 +1,10 @@ +module ActiveStorage::CheckAbilities + extend ActiveSupport::Concern + + private + + def check_abilities + university = University.with_host(request.host) + render(file: Rails.root.join('public/403.html'), formats: [:html], status: 403, layout: false) and return if university.present? && @blob.university_id != university.id + end +end diff --git a/app/models/research/journal/article.rb b/app/models/research/journal/article.rb index 20a415c655deaf08befa1c4a77cf15c2faa94b1f..0410c664d3b6e9859bf11e853a325e9043904631 100644 --- a/app/models/research/journal/article.rb +++ b/app/models/research/journal/article.rb @@ -10,7 +10,7 @@ # text :text # title :string # created_at :datetime not null -# updated_at :date not null +# updated_at :datetime not null # research_journal_id :uuid not null # research_journal_volume_id :uuid # university_id :uuid not null diff --git a/config/initializers/active_storage.rb b/config/initializers/active_storage.rb new file mode 100644 index 0000000000000000000000000000000000000000..efe52fbbf55c2eb6dfa68c97ee375552c0eaab4c --- /dev/null +++ b/config/initializers/active_storage.rb @@ -0,0 +1,34 @@ +require 'active_storage/attachment' +require 'active_storage/filename' + +ActiveStorage::Engine.config.active_storage.content_types_to_serve_as_binary.delete('image/svg+xml') + +# Hook ActiveStorage::Attachment to add brand_id to attachments records +ActiveStorage::Attachment.class_eval do + after_save :denormalize_university_id_for_blob + + def denormalize_university_id_for_blob + university_id = case self.record.class.name + when 'University' + self.record.id + when 'ActiveStorage::VariantRecord' + self.record.blob.university_id + else + self.record.university_id + end + + self.blob.update_column(:university_id, university_id) + end +end + +# Override ActiveStorage::Filename#sanitized to remove accents and all special chars +# Base method: https://github.com/rails/rails/blob/v6.1.3/activestorage/app/models/active_storage/filename.rb#L57 +ActiveStorage::Filename.class_eval do + def sanitized + base_filename = base.encode(Encoding::UTF_8, invalid: :replace, undef: :replace, replace: "�") + .strip + .tr("\u{202E}%$|:;/\t\r\n\\", "-") + .parameterize(preserve_case: true) + [base_filename, extension_with_delimiter].join('') + end +end diff --git a/db/migrate/20211008124136_add_university_id_to_active_storage_blob.rb b/db/migrate/20211008124136_add_university_id_to_active_storage_blob.rb new file mode 100644 index 0000000000000000000000000000000000000000..1dcdf0425a700f563c2a8c9cebfef2724a970735 --- /dev/null +++ b/db/migrate/20211008124136_add_university_id_to_active_storage_blob.rb @@ -0,0 +1,6 @@ +class AddUniversityIdToActiveStorageBlob < ActiveRecord::Migration[6.1] + def change + add_reference :active_storage_blobs, :university, type: :uuid, index: true + + end +end diff --git a/db/schema.rb b/db/schema.rb index ef44af74f7500f6c9f2cd55f56ce31a3ea83f5d5..a31dc6a068327c6c00930010697ab468e7635a51 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2021_10_08_090117) do +ActiveRecord::Schema.define(version: 2021_10_08_124136) do # These are extensions that must be enabled in order to support this database enable_extension "pgcrypto" @@ -35,7 +35,9 @@ ActiveRecord::Schema.define(version: 2021_10_08_090117) do t.bigint "byte_size", null: false t.string "checksum", null: false t.datetime "created_at", null: false + t.uuid "university_id" t.index ["key"], name: "index_active_storage_blobs_on_key", unique: true + t.index ["university_id"], name: "index_active_storage_blobs_on_university_id" end create_table "active_storage_variant_records", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| @@ -178,7 +180,7 @@ ActiveRecord::Schema.define(version: 2021_10_08_090117) do t.uuid "research_journal_id", null: false t.uuid "research_journal_volume_id" t.datetime "created_at", precision: 6, null: false - t.date "updated_at", null: false + t.datetime "updated_at", precision: 6, null: false t.uuid "updated_by_id" t.text "abstract" t.text "references" diff --git a/public/403.html b/public/403.html new file mode 100644 index 0000000000000000000000000000000000000000..edacef6a0fcef110042def4252e8ec28893fdc94 --- /dev/null +++ b/public/403.html @@ -0,0 +1,50 @@ +<!DOCTYPE html> +<html lang="fr"> + <head> + <title>You do not have access to this page (403)</title> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width,initial-scale=1"> + <style> + + .error-page { + font-family: arial, sans-serif; + margin: 0; + } + + .error-page div.dialog { + width: 95%; + max-width: 22em; + margin: calc(50vh - 140px) auto 0; + } + + .error-page div.dialog span{ + font-size: 200px; + font-weight: 900; + letter-spacing: -0.04em; + display: block; + width: 100%; + line-height: 0.9; + } + + .error-page h1 { + font-size: 16px; + line-height: 1.5em; + } + + .error-page h1 em { + font-weight: 400; + } + + </style> + </head> +<body class="error-page"> + <!-- This file lives in public/500.html --> + <div class="dialog"> + <span> + 403. + </span> + <div> + <h1>You do not have access to this page.</h1> + </div> + </body> +</html> diff --git a/test/fixtures/research/journal/articles.yml b/test/fixtures/research/journal/articles.yml index 432f9dfdfadb94b7ca93b6a486ff491bde470558..9db4f619ddc3077b9ee1726a1514bb3455aa1f21 100644 --- a/test/fixtures/research/journal/articles.yml +++ b/test/fixtures/research/journal/articles.yml @@ -10,7 +10,7 @@ # text :text # title :string # created_at :datetime not null -# updated_at :date not null +# updated_at :datetime not null # research_journal_id :uuid not null # research_journal_volume_id :uuid # university_id :uuid not null diff --git a/test/models/research/journal/article_test.rb b/test/models/research/journal/article_test.rb index 12249debb659d71a9fb563c9450468a466d3d66e..d1013ba1a1385e9ba42cbd8cbde079a9c2db7c8f 100644 --- a/test/models/research/journal/article_test.rb +++ b/test/models/research/journal/article_test.rb @@ -10,7 +10,7 @@ # text :text # title :string # created_at :datetime not null -# updated_at :date not null +# updated_at :datetime not null # research_journal_id :uuid not null # research_journal_volume_id :uuid # university_id :uuid not null