Skip to content
This repository has been archived by the owner on Jan 8, 2022. It is now read-only.

Authenticate file downloads - requires server updates to deploy #104

Merged
merged 4 commits into from
Feb 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ coverage/*
## PROJECT::GENERAL
rdoc
public/uploads
uploads
/public/assets
doc
pkg
Expand Down
14 changes: 14 additions & 0 deletions app/controllers/object_files_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
class ObjectFilesController < ApplicationController
before_action :authenticate_user!

def show
@fobj = Hydrus::Item.find(params[:id])
@fobj.current_user = current_user
authorize! :read, @fobj # only users who are authorized to view this object and download the files
filename = params[:filename]
object_file = Hydrus::ObjectFile.new(pid: DruidTools::Druid.new(@fobj.pid).druid)
file_location = File.join(object_file.file.store_dir, filename)
file = File.new(file_location)
send_file file
end
end
4 changes: 2 additions & 2 deletions app/models/hydrus/contentable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ def parent_directory
end

def base_file_directory
f = File.join(Rails.root, 'public', Hydrus::Application.config.file_upload_path)
DruidTools::Druid.new(pid, f).path
file_object = Hydrus::ObjectFile.new(pid: DruidTools::Druid.new(pid).druid)
File.join(file_object.file.base_dir)
end

def content_directory
Expand Down
3 changes: 2 additions & 1 deletion app/models/hydrus/object_file.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ def size
file.size
end

# Override the URL supplied by CarrierWave
def url
file.url
Rails.application.routes.url_helpers.file_upload_path(id: pid, filename: filename)
end

def current_path
Expand Down
11 changes: 8 additions & 3 deletions app/uploaders/file_uploader.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,19 @@ def delete_tmp_dir(new_file)
end
end

# Override the directory where uploaded files will be stored.
# Set the base object directory name
def base_dir
File.join(Rails.root, Settings.hydrus.file_upload_path, DruidTools::Druid.new(model.pid).path)
end

# Set the directory where uploaded files will be stored.
def store_dir
DruidTools::Druid.new(model.pid, Hydrus::Application.config.file_upload_path).path('content')
File.join(base_dir, 'content')
end

# temp directory where files are stored before they are uploaded
def cache_dir
File.join(root, Hydrus::Application.config.file_upload_path, 'tmp')
File.join(Rails.root, Settings.hydrus.file_upload_path, 'tmp')
end

# Provide a default URL as a default if there hasn't been a file uploaded:
Expand Down
2 changes: 1 addition & 1 deletion config/deploy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
)

# Default value for linked_dirs is []
set :linked_dirs, %w{log config/certs config/settings tmp/pids tmp/cache tmp/sockets vendor/bundle public/system public/uploads}
set :linked_dirs, %w{log config/certs config/settings tmp/pids tmp/cache tmp/sockets vendor/bundle public/system uploads}

# Default value for default_env is {}
# set :default_env, { path: "/opt/ruby/bin:$PATH" }
Expand Down
3 changes: 0 additions & 3 deletions config/environment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@
Rails.application.initialize!

Hydrus::Application.configure do
# this is the path from the root of the public folder into which file uploads will be stored
config.file_upload_path = 'uploads'

# file attributes by mimetype, including defaults, to use when generating content metadata
config.cm_file_attributes = {
'default' => { publish: 'yes', preserve: 'yes', shelve: 'yes' }
Expand Down
5 changes: 5 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@
match 'dor/reindex/:id' => 'hydrus_solr#reindex', :as => 'reindex', via: [:get, :post, :put]
match 'dor/delete_from_index/:id' => 'hydrus_solr#delete_from_index', :as => 'delete_from_index', via: [:get, :post]

# Action to get an uploaded file
constraints filename: %r{[^/]+} do
get '/file/:id/:filename' => 'object_files#show', :as => 'file_upload'
end

get '/404', to: 'exceptions#render_404'
get '/500', to: 'exceptions#render_500'
end
1 change: 1 addition & 0 deletions config/settings.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ hydrus:
exception_error_page: true # if true, a generic error page will be shown with no exception messages; if false, standard Rails exceptions are shown directly to the user
exception_error_panel: false
host: 'hydrus-test.stanford.edu'
file_upload_path: 'uploads'
exception_recipients: ''
ur_apo_druid: 'druid:bb000bb0000'
project_tag: 'Project : Hydrus'
Expand Down
8 changes: 4 additions & 4 deletions lib/tasks/fixtures.rake
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ namespace :hydrus do
end
end

desc 'reload test uploaded files to public/upload directory'
desc 'reload test uploaded files to upload directory'
task :refresh_upload_files do
# Copies fixture files from source control to the app's public area:
# source: spec/fixtures/files/DRUID/*
Expand All @@ -106,7 +106,7 @@ namespace :hydrus do
require File.expand_path('config/environment')
app_base = File.expand_path('../../../', __FILE__)
src_base = File.join(app_base, 'spec/fixtures/files')
dst_base = File.join(app_base, 'public', Hydrus::Application.config.file_upload_path)
dst_base = File.join(app_base, Settings.hydrus.file_upload_path)
FIXTURE_PIDS.each do |pid|
pid.gsub!('druid:', '')
src = File.join(src_base, pid)
Expand All @@ -119,12 +119,12 @@ namespace :hydrus do
end
end

desc 'clear uploaded files [public/upload] directory'
desc 'clear uploaded files upload directory'
task :clear_upload_files do
puts 'clearing upload files directory'
require File.expand_path('config/environment')
app_base = File.expand_path('../../../', __FILE__)
dst_base = File.join(app_base, 'public', Hydrus::Application.config.file_upload_path)
dst_base = File.join(app_base, Settings.hydrus.file_upload_path)
puts "Removing all folders in #{dst_base}"
all_folders = Dir.glob("#{dst_base}/*")
all_folders.each do |folder|
Expand Down
2 changes: 1 addition & 1 deletion spec/features/home_page_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@

it 'breadcrumbs should not be displayed' do
# Logged out
logout
sign_out
visit root_path
expect(page).not_to have_css(@breadcrumbs)
end
Expand Down
1 change: 0 additions & 1 deletion spec/features/item_create_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,6 @@
f.file = Tempfile.new('mock_HydrusObjectFile_')
f.save
click_button(@buttons[:save])

# confirm validation message is shown and publish button is not available
expect(find(@div_alert)).to have_content(@notices[:save])
expect(find(@div_alert)).to have_content('Contributors must be entered')
Expand Down
6 changes: 3 additions & 3 deletions spec/features/item_edit_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -653,7 +653,7 @@
# Visit edit page: delete a file.
# Make corresponding changes in the exp hash.
i = 1
file_url = Hydrus::ObjectFile.find(i).url
object_file = Hydrus::ObjectFile.find(i)
should_visit_edit_page(@hi)
css_del = "delete_file_#{i}"
click_link(css_del)
Expand All @@ -668,8 +668,8 @@
check_file_info.call(exp)

# Restore the deleted file.
restore_upload_file(file_url)
expect(File.exists?('public' + file_url)).to eq(true)
restore_upload_file(object_file)
expect(File.exists?(object_file.current_path)).to eq(true)
end
end
end
8 changes: 3 additions & 5 deletions spec/features/models/object_file_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
it 'finds four files associated with the first item and it grabs the url of a given file' do
expect(files.size).to eq(4)
f = files[0]
exp_url = '/uploads/bb/123/bb/1234/bb123bb1234/content/pinocchio.htm'
exp_url = '/file/druid:bb123bb1234/pinocchio.htm'
expect(f.url).to eq(exp_url)
expect(f.current_path).to eq("#{Rails.root}/public#{exp_url}")
expect(f.current_path).to eq("#{Rails.root}/uploads/bb/123/bb/1234/bb123bb1234/content/pinocchio.htm")
expect(files[1].filename).to eq(%q{pinocchio characters tc in file name.pdf})
expect(files[1].size).to be > 0
end
Expand All @@ -24,8 +24,6 @@
files = @hi.files
expect(files.size).to eq(4)
file = files.first

file_url = file.url
full_file_path = file.current_path
expect(File.exists?(full_file_path)).to be_truthy

Expand All @@ -36,7 +34,7 @@
expect(File.exists?(full_file_path)).to be_falsey

# restore original file and stream from fixtures
restore_upload_file(file_url)
restore_upload_file(file)
expect(File.exists?(full_file_path)).to be_truthy
end
end
31 changes: 31 additions & 0 deletions spec/features/object_files_controller_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
require 'spec_helper'

describe('Object Files Download', type: :request, integration: true) do
fixtures :object_files
let(:archivist1) { create :archivist1 }
let(:archivist99) { create :archivist99 }

before :each do
@druid = 'druid:bb123bb1234'
@file = Hydrus::ObjectFile.find(3) # this txt file defined in the fixtures belongs to archivist1
end

it 'allows the owner of the file to download it' do
sign_in(archivist1) # owner of the item can download the file
visit @file.url
expect(page.status_code).to eq(200)
expect(page.response_headers['Content-Type']).to eq('text/plain')
logout(archivist1)
end

it 'redirects to home page with not authorized message when accessing a file URL when no access is allowed' do
sign_in(archivist99) # no view access on the item, no access
visit @file.url
expect(page.response_headers['Content-Type']).not_to eq('text/plain')
expect(current_path).to eq(root_path)
expect(find('#flash-notices div.alert')).to have_content('You are not authorized to access this page.')
logout(archivist99)
visit @file.url
expect(current_path).to eq(root_path) # logged out, still can't get the file
end
end
2 changes: 1 addition & 1 deletion spec/models/hydrus/contentable_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
before(:each) do
@go = Hydrus::GenericObject.new
@pid = 'oo000oo9999'
@base_dir = '/oo/000/oo/9999/oo000oo9999'
@base_dir = File.join(Rails.root, Settings.hydrus.file_upload_path, './oo/000/oo/9999/oo000oo9999')
allow(@go).to receive(:pid).and_return(@pid)
end

Expand Down
10 changes: 4 additions & 6 deletions spec/support/helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -216,13 +216,11 @@ def create_new_item(opts = {})
Hydrus::Item.find(druid)
end

# Takes the file_url of an Item's uploaded file.
# Takes an objet_file object.
# Helper method to restore a file to the uploads directory
# after it was deleted in a integration test.
def restore_upload_file(file_url)
parts = file_url.split /\//
parts[0] = 'public'
dst = File.join(*parts)
src = File.join('spec/fixtures/files', parts[-3], parts[-1])
def restore_upload_file(object_file)
src = File.join('spec/fixtures/files', DruidTools::Druid.new(object_file.pid).id, object_file.filename)
dst = object_file.current_path
FileUtils.cp(src, dst)
end
Empty file added uploads/.keep
Empty file.