-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- receives array of path in the same manner as /delete - will call "backup_file" on each relpath - if backup is configured, fog.copy_object will be called to copy the file from bucket to backup bucket Fixes #13
- Loading branch information
Showing
5 changed files
with
156 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
class Attache::Backup < Attache::Base | ||
def initialize(app) | ||
@app = app | ||
end | ||
|
||
def _call(env, config) | ||
case env['PATH_INFO'] | ||
when '/backup' | ||
request = Rack::Request.new(env) | ||
params = request.params | ||
return config.unauthorized unless config.authorized?(params) | ||
|
||
params['paths'].to_s.split("\n").each do |relpath| | ||
Attache.logger.info "CONFIRM local #{relpath}" | ||
cachekey = File.join(request_hostname(env), relpath) | ||
if config.storage && config.bucket | ||
Attache.logger.info "CONFIRM remote #{relpath}" | ||
config.async(:backup_file, relpath: relpath) | ||
end | ||
end | ||
[200, config.headers_with_cors, []] | ||
else | ||
@app.call(env) | ||
end | ||
rescue Exception | ||
Attache.logger.error $@ | ||
Attache.logger.error $! | ||
[500, { 'X-Exception' => $!.to_s }, []] | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
require 'spec_helper' | ||
|
||
describe Attache::Backup do | ||
let(:app) { ->(env) { [200, env, "app"] } } | ||
let(:middleware) { Attache::Backup.new(app) } | ||
let(:params) { {} } | ||
let(:filename) { "hello#{rand}.gif" } | ||
let(:reldirname) { "path#{rand}" } | ||
let(:file) { StringIO.new(IO.binread("spec/fixtures/transparent.gif"), 'rb') } | ||
|
||
before do | ||
allow(Attache).to receive(:logger).and_return(Logger.new('/dev/null')) | ||
allow(Attache).to receive(:localdir).and_return(Dir.tmpdir) # forced, for safety | ||
end | ||
|
||
after do | ||
FileUtils.rm_rf(Attache.localdir) | ||
end | ||
|
||
it "should passthrough irrelevant request" do | ||
code, env = middleware.call Rack::MockRequest.env_for('http://example.com', {}) | ||
expect(code).to eq 200 | ||
end | ||
|
||
context "backup file" do | ||
let(:params) { Hash(paths: ['image1.jpg', filename].join("\n")) } | ||
|
||
subject { proc { middleware.call Rack::MockRequest.env_for('http://example.com/backup?' + params.collect {|k,v| "#{CGI.escape k.to_s}=#{CGI.escape v.to_s}"}.join('&'), method: 'DELETE', "HTTP_HOST" => "example.com") } } | ||
|
||
it 'should respond with json' do | ||
end | ||
|
||
it 'should not touch local file' do | ||
expect(Attache).not_to receive(:cache) | ||
code, headers, body = subject.call | ||
expect(code).to eq(200) | ||
end | ||
|
||
context 'storage configured' do | ||
before do | ||
allow_any_instance_of(Attache::VHost).to receive(:storage).and_return(double(:storage)) | ||
allow_any_instance_of(Attache::VHost).to receive(:bucket).and_return(double(:bucket)) | ||
end | ||
|
||
it 'should backup file' do | ||
expect_any_instance_of(Attache::VHost).to receive(:async) do |instance, method, path| | ||
expect(method).to eq(:backup_file) | ||
end.exactly(2).times | ||
subject.call | ||
end | ||
end | ||
|
||
context 'storage NOT configured' do | ||
it 'should backup file' do | ||
expect_any_instance_of(Attache::VHost).not_to receive(:async) | ||
subject.call | ||
end | ||
end | ||
|
||
context 'with secret_key' do | ||
let(:secret_key) { "topsecret#{rand}" } | ||
|
||
before do | ||
allow_any_instance_of(Attache::VHost).to receive(:secret_key).and_return(secret_key) | ||
end | ||
|
||
it 'should respond with error' do | ||
code, headers, body = subject.call | ||
expect(code).to eq(401) | ||
expect(headers['X-Exception']).to eq('Authorization failed') | ||
end | ||
|
||
context 'invalid auth' do | ||
let(:expiration) { (Time.now + 10).to_i } | ||
let(:uuid) { "hi#{rand}" } | ||
let(:digest) { OpenSSL::Digest.new('sha1') } | ||
let(:params) { Hash(file: filename, expiration: expiration, uuid: uuid, hmac: OpenSSL::HMAC.hexdigest(digest, "wrong#{secret_key}", "#{uuid}#{expiration}")) } | ||
|
||
it 'should respond with error' do | ||
code, headers, body = subject.call | ||
expect(code).to eq(401) | ||
expect(headers['X-Exception']).to eq('Authorization failed') | ||
end | ||
end | ||
|
||
context 'valid auth' do | ||
let(:expiration) { (Time.now + 10).to_i } | ||
let(:uuid) { "hi#{rand}" } | ||
let(:digest) { OpenSSL::Digest.new('sha1') } | ||
let(:params) { Hash(file: filename, expiration: expiration, uuid: uuid, hmac: OpenSSL::HMAC.hexdigest(digest, secret_key, "#{uuid}#{expiration}")) } | ||
|
||
it 'should respond with success' do | ||
code, headers, body = subject.call | ||
expect(code).to eq(200) | ||
end | ||
|
||
context 'expired' do | ||
let(:expiration) { (Time.now - 1).to_i } # the past | ||
|
||
it 'should respond with error' do | ||
code, headers, body = subject.call | ||
expect(code).to eq(401) | ||
expect(headers['X-Exception']).to eq('Authorization failed') | ||
end | ||
end | ||
end | ||
end | ||
end | ||
end |