X-Git-Url: https://coquelicot.potager.org/gitweb/?p=coquelicot.git;a=blobdiff_plain;f=test_coquelicot.rb;h=759dc379dc35c3eb3fe05b264a4222c3130b2d01;hp=b210ec129728ad9a9931c7f8eedd58919ddc3b52;hb=da90f4f11eca1ee096aee0fc178cb53788a2a97a;hpb=6acb4fe009d51ddce5ba6f14df3b7560287d9b83 diff --git a/test_coquelicot.rb b/test_coquelicot.rb index b210ec1..759dc37 100644 --- a/test_coquelicot.rb +++ b/test_coquelicot.rb @@ -1,7 +1,3 @@ -$:.unshift File.join(File.dirname(__FILE__), '../rack-test/lib') -$:.unshift File.join(File.dirname(__FILE__), '../timecop/lib') - -require 'sinatra' require 'coquelicot_app' require 'spec' require 'rack/test' @@ -11,19 +7,16 @@ require 'tmpdir' UPLOAD_PASSWORD = 'secret' -set :environment, :test -set :upload_password, Digest::SHA1.hexdigest(UPLOAD_PASSWORD) - describe 'Coquelicot' do include Rack::Test::Methods def app - Sinatra::Application + Coquelicot::Application end def upload(opts={}) opts = { :file => Rack::Test::UploadedFile.new(__FILE__, 'text/x-script.ruby'), - :upload_password => UPLOAD_PASSWORD + :upload_token => JSON.dump({ 'upload_password' => UPLOAD_PASSWORD}) }.merge(opts) post '/upload', opts return nil unless last_response.redirect? @@ -35,7 +28,12 @@ describe 'Coquelicot' do end before do - Coquelicot.setup :depot_path => Dir.mktmpdir('coquelicot') #"#{Time.now.to_f}" + # set a special test password + app.set :upload_password, Digest::SHA1.hexdigest(UPLOAD_PASSWORD) + + app.set :environment, :test + + app.set :depot_path, Dir.mktmpdir('coquelicot') end after do @@ -49,123 +47,176 @@ describe 'Coquelicot' do (doc/"form#upload").should have(1).items end - it "should allow retrieval of an uploaded file" do - url = upload - get url - last_response.should be_ok - last_response['Content-Type'].should eql('text/x-script.ruby') - last_response.body.should eql(File.new(__FILE__).read) + context "after a successful upload" do + before(:each) do + @url = upload + end + + it "should not store the file in cleartext" do + files = Dir.glob("#{Coquelicot.depot.path}/*") + files.should have(1).items + File.new(files[0]).read().should_not include('should not store an uploaded file') + end + + it "should generate a random URL to download the file" do + @url.should_not include(File.basename(__FILE__)) + end + + it "should store the file with a different name than the one in URL" do + url_name = @url.split('/')[-1] + files = Dir.glob("#{Coquelicot.depot.path}/*") + files.should have(1).items + url_name.should_not eql(File.basename(files[0])) + end + + it "should encode the encryption key in URL as no password has been specified" do + url_name = @url.split('/')[-1] + url_name.split('-').should have(2).items + end + + it "should download when using extra Base32 characters in URL" do + splitted = @url.split('/') + name = splitted[-1].upcase.gsub(/O/, '0').gsub(/L/, '1') + get "#{splitted[0..-2].join '/'}/#{name}" + last_response.should be_ok + last_response['Content-Type'].should eql('text/x-script.ruby') + end + + context "when the file has been downloaded" do + before(:each) do + get @url + end + + it "should be the same file as the uploaded" do + last_response.should be_ok + last_response['Content-Type'].should eql('text/x-script.ruby') + last_response.body.should eql(File.new(__FILE__).read) + end + + it "should always has the same Last-Modified header" do + last_modified = last_response['Last-Modified'] + last_modified.should_not be_nil + get @url + last_response['Last-Modified'].should eql(last_modified) + end + end end - it "should correctly set Last-Modified header when downloading" do - url = upload - get url - last_modified = last_response['Last-Modified'] - last_modified.should_not be_nil - get url - last_response['Last-Modified'].should eql(last_modified) + context "given an empty file" do + before do + @empty_file = Tempfile.new('empty') + end + it "should not be accepted when uploaded" do + url = upload :file => Rack::Test::UploadedFile.new(@empty_file.path, 'text/plain') + url.should be_nil + last_response.should_not be_redirect + end + after do + @empty_file.close true + end end it "should prevent upload without a password" do - url = upload :upload_password => '' + url = upload :upload_token => JSON.dump({'upload_password' => ''}) url.should be_nil last_response.status.should eql(403) end it "should prevent upload with a wrong password" do - url = upload :upload_password => "bad" + url = upload :upload_token => JSON.dump({'upload_password' => 'bad'}) url.should be_nil last_response.status.should eql(403) end - it "should not store an uploaded file in cleartext" do - upload - files = Dir.glob("#{Coquelicot.depot.path}/*") - files.should have(1).items - File.new(files[0]).read().should_not include('should not store an uploaded file') + it "should allow AJAX upload password verification" do + request "/authenticate", :method => "POST", :xhr => true, + :params => { :upload_token => { 'upload_password' => UPLOAD_PASSWORD } } + last_response.should be_ok + request "/authenticate", :method => "POST", :xhr => true, + :params => { :upload_token => '{}' } + last_response.status.should eql(403) + request "/authenticate", :method => "POST", :xhr => true, + :params => { :upload_token => JSON.dump({'upload_password' => 'wrong'}) } + last_response.status.should eql(403) end - it "should generate a random URL to retrieve a file" do - url = upload - url.should_not include(File.basename(__FILE__)) - end + context "when a 'one time download' has been retrieved" do + before(:each) do + @url = upload :one_time => true + get @url + end - it "should store files with a different name than then one in URL" do - url = upload - url_name = url.split('/')[-1] - files = Dir.glob("#{Coquelicot.depot.path}/*") - files.should have(1).items - url_name.should_not eql(File.basename(files[0])) - end + it "should be the same as the uploaded file" do + last_response.should be_ok + last_response['Content-Type'].should eql('text/x-script.ruby') + last_response.body.should eql(File.new(__FILE__).read) + end - it "should encode the encryption key in URL when no password has been specified" do - url = upload - url_name = url.split('/')[-1] - url_name.split('-').should have(2).items - end + it "should not be downloadable any more" do + get @url + last_response.status.should eql(410) + end - it "should not encode the encryption key in URL when a password has been specified" do - url = upload :file_key => 'somethingSecret' - url_name = url.split('/')[-1] - url_name.split('-').should have(1).items + it "should have zero'ed the file on the server" do + files = Dir.glob("#{Coquelicot.depot.path}/*") + files.should have(1).items + File.lstat(files[0]).size.should eql(0) + end end - it "should only allow one time download to be retrieved once" do - url = upload :one_time => true - get url - last_response.should be_ok - last_response['Content-Type'].should eql('text/x-script.ruby') - last_response.body.should eql(File.new(__FILE__).read) - get url - last_response.status.should eql(410) - end + context "after a password protected upload" do + before(:each) do + @url = upload :file_key => 'somethingSecret' + end - it "should have files zero'ed after 'one time' download" do - url = upload :one_time => true - get url - files = Dir.glob("#{Coquelicot.depot.path}/*") - files.should have(1).items - File.lstat(files[0]).size.should eql(0) - end + it "should not return an URL with the encryption key" do + url_name = @url.split('/')[-1] + url_name.split('-').should have(1).items + end - it "should allow retrieval of a password protected file" do - url = upload :file_key => 'somethingSecret' - get url - last_response.should be_ok - doc = Hpricot(last_response.body) - (doc/'input#file_key').should have(1).items - url = (doc/'form')[0].attributes['action'] - post url, :file_key => 'somethingSecret' - last_response.should be_ok - last_response['Content-Type'].should eql('text/x-script.ruby') - last_response.body.should eql(File.new(__FILE__).read) - end + it "should offer a password form before download" do + get @url + last_response.should be_ok + last_response['Content-Type'].should eql('text/html') + doc = Hpricot(last_response.body) + (doc/'input#file_key').should have(1).items + end - it "should not allow retrieval of a password protected file without the password" do - url = upload :file_key => 'somethingSecret' - get url - last_response.should be_ok - last_response['Content-Type'].should_not eql('text/x-script.ruby') - post url - last_response.status.should eql(403) - end + context "when given the correct password" do + it "should download the same file" do + post @url, :file_key => 'somethingSecret' + last_response.should be_ok + last_response['Content-Type'].should eql('text/x-script.ruby') + last_response.body.should eql(File.new(__FILE__).read) + end + end - it "should not allow retrieval of a password protected file with a wrong password" do - url = upload :file_key => 'somethingSecret' - post url, :file_key => 'BAD' - last_response.status.should eql(403) + it "should prevent download without a password" do + post @url + last_response.status.should eql(403) + end + + it "should prevent download with a wrong password" do + post @url, :file_key => 'BAD' + last_response.status.should eql(403) + end end - it "should not allow retrieval after the time limit has expired" do - url = upload :expire => 60 # 1 hour - # let's be tomorrow - Timecop.travel(Date.today + 1) do - get url - last_response.status.should eql(410) + context "after an upload with a time limit" do + before(:each) do + @url = upload :expire => 60 # 1 hour + end + + it "should prevent download after the time limit has expired" do + # let's be tomorrow + Timecop.travel(Date.today + 1) do + get @url + last_response.status.should eql(410) + end end end - it "should not allow an expiration time longer than the maximum" do + it "should refuse an expiration time longer than the maximum" do upload :expire => 60 * 24 * 31 * 12 # 1 year last_response.status.should eql(403) end @@ -189,13 +240,4 @@ describe 'Coquelicot' do Coquelicot.depot.get_file(url_name).should be_nil end end - - it "should map extra base32 characters to filenames" do - url = upload :expire => 60 # 1 hour - splitted = url.split('/') - name = splitted[-1].upcase.gsub(/O/, '0').gsub(/L/, '1') - get "#{splitted[0..-2].join '/'}/#{name}" - last_response.should be_ok - last_response['Content-Type'].should eql('text/x-script.ruby') - end end