simple-api-auth 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/.rspec +3 -0
- data/.rubocop.yml +14 -0
- data/.ruby-version +1 -0
- data/.travis.yml +16 -0
- data/Gemfile +12 -0
- data/Guardfile +5 -0
- data/LICENSE +22 -0
- data/README.md +169 -0
- data/Rakefile +7 -0
- data/lib/simple-api-auth/authenticable.rb +72 -0
- data/lib/simple-api-auth/authenticator.rb +32 -0
- data/lib/simple-api-auth/config.rb +61 -0
- data/lib/simple-api-auth/hashers/sha1_hasher.rb +14 -0
- data/lib/simple-api-auth/helpers/auth_helpers.rb +49 -0
- data/lib/simple-api-auth/helpers/request_helpers.rb +18 -0
- data/lib/simple-api-auth/request.rb +34 -0
- data/lib/simple-api-auth/signer.rb +53 -0
- data/lib/simple-api-auth/version.rb +6 -0
- data/lib/simple-api-auth.rb +47 -0
- data/simple-api-auth.gemspec +25 -0
- data/spec/extensions/string_spec.rb +13 -0
- data/spec/internal/app/models/models.rb +13 -0
- data/spec/internal/config/database.yml +4 -0
- data/spec/internal/db/schema.rb +7 -0
- data/spec/lib/simple-api-auth/authenticable_spec.rb +106 -0
- data/spec/lib/simple-api-auth/authenticator_spec.rb +35 -0
- data/spec/lib/simple-api-auth/config_spec.rb +48 -0
- data/spec/lib/simple-api-auth/helpers/auth_helpers_spec.rb +61 -0
- data/spec/lib/simple-api-auth/helpers/request_helpers_spec.rb +39 -0
- data/spec/lib/simple-api-auth/request_spec.rb +49 -0
- data/spec/lib/simple-api-auth/signer_spec.rb +37 -0
- data/spec/lib/simple-api-auth_spec.rb +110 -0
- data/spec/spec_helper.rb +31 -0
- data/spec/support/auth.rb +13 -0
- data/spec/support/database.rb +14 -0
- data/spec/support/database_cleaner.rb +19 -0
- data/spec/support/extensions.rb +10 -0
- data/spec/support/mocks.rb +67 -0
- data/spec/support/requests.rb +51 -0
- metadata +189 -0
@@ -0,0 +1,25 @@
|
|
1
|
+
lib = File.expand_path('../lib', __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require 'simple-api-auth/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = 'simple-api-auth'
|
7
|
+
spec.version = SimpleApiAuth::VERSION
|
8
|
+
spec.authors = ['Daniel Perez']
|
9
|
+
spec.email = ['daniel@claudetech.com']
|
10
|
+
spec.summary = 'Basic token based authentication for APIs'
|
11
|
+
spec.homepage = 'https://github.com/claude-tech/ruby-simple-api-auth'
|
12
|
+
spec.license = 'MIT'
|
13
|
+
|
14
|
+
spec.files = `git ls-files -z`.split("\x0")
|
15
|
+
spec.test_files = Dir['specs/**/*']
|
16
|
+
spec.require_paths = ['lib']
|
17
|
+
|
18
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
19
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
20
|
+
spec.add_development_dependency 'rspec-its'
|
21
|
+
spec.add_development_dependency 'coveralls', '~> 0.7'
|
22
|
+
spec.add_development_dependency 'activerecord', ['>= 3', '< 5']
|
23
|
+
spec.add_development_dependency 'database_cleaner'
|
24
|
+
spec.add_development_dependency 'sqlite3'
|
25
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
describe String do
|
2
|
+
describe '#hexdecode' do
|
3
|
+
it 'should decode text' do
|
4
|
+
expect(Digest.hexencode('foo').hexdecode).to eq('foo')
|
5
|
+
end
|
6
|
+
|
7
|
+
it 'should decode binary' do
|
8
|
+
digest = OpenSSL::Digest.new('sha1')
|
9
|
+
binary_data = OpenSSL::HMAC.digest(digest, 'foo', 'bar')
|
10
|
+
expect(Digest.hexencode(binary_data).hexdecode).to eq(binary_data)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class AuthenticableModel < ActiveRecord::Base
|
2
|
+
acts_as_api_authenticable
|
3
|
+
end
|
4
|
+
|
5
|
+
class OverriddenAuthenticableModel < ActiveRecord::Base
|
6
|
+
acts_as_api_authenticable ssa_key: :ssa_key, ssa_secret: :ssa_secret
|
7
|
+
acts_as_api_authenticable ssa_key: :ssa_key, ssa_secret: :overriden_secret
|
8
|
+
end
|
9
|
+
|
10
|
+
class AuthenticableModelWithHooks < ActiveRecord::Base
|
11
|
+
self.table_name = 'authenticable_models'
|
12
|
+
acts_as_api_authenticable auto_generate: true
|
13
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
describe SimpleApiAuth do
|
2
|
+
describe SimpleApiAuth::Authenticable do
|
3
|
+
|
4
|
+
describe 'class extensions' do
|
5
|
+
subject(:clazz) { AuthenticableModel }
|
6
|
+
|
7
|
+
it { should respond_to(:ssa_options) }
|
8
|
+
it { should respond_to(:ssa_authenticate) }
|
9
|
+
it { should respond_to(:ssa_find) }
|
10
|
+
|
11
|
+
it 'should set options' do
|
12
|
+
expect(clazz.ssa_options[:ssa_key]).to eq(:ssa_key)
|
13
|
+
expect(clazz.ssa_options[:ssa_secret]).to eq(:ssa_secret)
|
14
|
+
end
|
15
|
+
|
16
|
+
context 'when included several times' do
|
17
|
+
subject(:clazz) { OverriddenAuthenticableModel }
|
18
|
+
|
19
|
+
it { should respond_to(:ssa_options) }
|
20
|
+
it 'should have overriden attributes' do
|
21
|
+
expect(clazz.ssa_options[:ssa_secret]).to eq(:overriden_secret)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe '#ssa_find' do
|
26
|
+
let(:request) { mock_request }
|
27
|
+
|
28
|
+
subject { clazz.ssa_find(request) }
|
29
|
+
|
30
|
+
it 'should return nil when not found' do
|
31
|
+
expect(subject).to be_nil
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'should return entity when present' do
|
35
|
+
entity = AuthenticableModel.create(ssa_key: 'user_personal_key')
|
36
|
+
expect(subject.id).to eq(entity.id)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe '#ssa_authenticate' do
|
41
|
+
let(:request) { mock_request }
|
42
|
+
before(:each) do
|
43
|
+
request.headers[:authorization] = "Signature: #{mock_signature}"
|
44
|
+
end
|
45
|
+
|
46
|
+
subject { clazz.ssa_authenticate(request) }
|
47
|
+
|
48
|
+
it 'should return false when the entity does not exists' do
|
49
|
+
expect(subject).to be false
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'should return false when the secret key does not match' do
|
53
|
+
clazz.create(ssa_key: 'user_personal_key', ssa_secret: 'something else')
|
54
|
+
expect(subject).to be false
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'should return the resource when signature matches' do
|
58
|
+
entity = clazz.create(
|
59
|
+
ssa_key: 'user_personal_key', ssa_secret: 'ultra_secret_key')
|
60
|
+
expect(subject.id).to eq(entity.id)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
{ 'generate_ssa_key' => 5, 'generate_ssa_secret' => 64 }.each do |method, value|
|
65
|
+
describe "##{method}" do
|
66
|
+
let(:options) { {} }
|
67
|
+
let(:key) { clazz.send(method.to_sym, options) }
|
68
|
+
it 'should generate a long enough key' do
|
69
|
+
expect(key.length).to be > value
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'should use options length' do
|
73
|
+
options[:length] = 1
|
74
|
+
expect(key.length).to be < 3
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe 'hooks' do
|
80
|
+
subject { AuthenticableModelWithHooks.new }
|
81
|
+
its(:ssa_key) { should_not be_nil }
|
82
|
+
its(:ssa_secret) { should_not be_nil }
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe 'instance extension' do
|
87
|
+
subject(:model) { AuthenticableModel.new }
|
88
|
+
|
89
|
+
it { should respond_to(:assign_ssa_key) }
|
90
|
+
it { should respond_to(:assign_ssa_secret) }
|
91
|
+
its(:ssa_key) { should be_nil }
|
92
|
+
its(:ssa_secret) { should be_nil }
|
93
|
+
|
94
|
+
[:assign_ssa_key, :assign_ssa_secret].each do |method|
|
95
|
+
field_name = method.to_s.gsub('assign_', '')
|
96
|
+
describe "##{method}" do
|
97
|
+
it "should assign #{field_name}" do
|
98
|
+
expect(model.send(field_name)).to be_nil
|
99
|
+
model.send(method)
|
100
|
+
expect(model.send(field_name)).not_to be_nil
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
describe SimpleApiAuth do
|
2
|
+
describe SimpleApiAuth::Authenticator do
|
3
|
+
include SimpleApiAuth::Helpers::Request
|
4
|
+
|
5
|
+
let(:dummy_headers) { mock_headers }
|
6
|
+
|
7
|
+
let(:authenticator) { SimpleApiAuth::Authenticator.new(dummy_request, mock_secret_key) }
|
8
|
+
|
9
|
+
requests.each do |name, request|
|
10
|
+
context "with #{name}" do
|
11
|
+
before(:each) do
|
12
|
+
request.configure
|
13
|
+
setup_dummy_signer
|
14
|
+
end
|
15
|
+
let(:dummy_request) { request.new(headers: dummy_headers, method: 'GET') }
|
16
|
+
|
17
|
+
describe '#valid_signature?' do
|
18
|
+
it 'should fail on missing header' do
|
19
|
+
dummy_headers.delete 'X-Saa-Key'
|
20
|
+
expect(authenticator.valid_signature?).to be_falsy
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should fail on outdated request' do
|
24
|
+
dummy_headers[:x_saa_auth_time] = outdated_time.iso8601
|
25
|
+
expect(authenticator.valid_signature?).to be_falsy
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should compare signature otherwise' do
|
29
|
+
expect(authenticator.valid_signature?).to be_truthy
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
describe SimpleApiAuth do
|
2
|
+
describe SimpleApiAuth::Config do
|
3
|
+
let(:config) { SimpleApiAuth::Config.new }
|
4
|
+
|
5
|
+
it 'should have default values' do
|
6
|
+
expect(config.request_fields[:headers]).to eq(:headers)
|
7
|
+
expect(config.request_fields[:http_verb]).to eq(:method)
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'should be mutable' do
|
11
|
+
config.request_fields[:headers] = :env
|
12
|
+
expect(config.request_fields[:headers]).to eq(:env)
|
13
|
+
end
|
14
|
+
|
15
|
+
describe 'make_model_options' do
|
16
|
+
let(:model_options) { { ssa_key: :foobar } }
|
17
|
+
subject(:options) { config.make_model_options(model_options) }
|
18
|
+
|
19
|
+
it 'should merge options' do
|
20
|
+
expect(options[:ssa_key]).to eq(:foobar)
|
21
|
+
expect(options[:ssa_secret]).to eq(:ssa_secret)
|
22
|
+
expect(options[:auto_generate]).to eq([])
|
23
|
+
end
|
24
|
+
|
25
|
+
describe 'auto_generate configuration' do
|
26
|
+
it 'should handle symbol' do
|
27
|
+
model_options[:auto_generate] = :ssa_key
|
28
|
+
expect(options[:auto_generate]).to eq([:ssa_key])
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should handle true' do
|
32
|
+
model_options[:auto_generate] = true
|
33
|
+
expect(options[:auto_generate]).to eq([:ssa_key, :ssa_secret])
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'should handle false' do
|
37
|
+
model_options[:auto_generate] = false
|
38
|
+
expect(options[:auto_generate]).to eq([])
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'should handle arrays' do
|
42
|
+
model_options[:auto_generate] = [:ssa_secret]
|
43
|
+
expect(options[:auto_generate]).to eq([:ssa_secret])
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
describe SimpleApiAuth do
|
2
|
+
describe SimpleApiAuth::Helpers::Auth do
|
3
|
+
include SimpleApiAuth::Helpers::Request
|
4
|
+
include SimpleApiAuth::Helpers::Auth
|
5
|
+
|
6
|
+
let(:headers) { normalize_headers(mock_headers) }
|
7
|
+
let(:request) { SimpleApiAuth::Request.create(rails_request) }
|
8
|
+
|
9
|
+
describe '#extract_signature' do
|
10
|
+
it 'should return signature when present' do
|
11
|
+
expect(extract_signature(headers)).to eq('dummy_signature')
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should return nil otherwise' do
|
15
|
+
headers[:authorization] = 'Signatur: foobar'
|
16
|
+
expect(extract_signature(headers)).to be_nil
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe '#check_data' do
|
21
|
+
it 'should return true when headers are present and http verb is valid' do
|
22
|
+
expect(check_data(request)).to be_truthy
|
23
|
+
request.http_verb = :post
|
24
|
+
expect(check_data(request)).to be_truthy
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'should fail when http verb is not valid' do
|
28
|
+
request.http_verb = nil
|
29
|
+
expect(check_data(request)).to be_falsy
|
30
|
+
request.http_verb = :forbidden_verb
|
31
|
+
expect(check_data(request)).to be_falsy
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'should fail when header is missing' do
|
35
|
+
request.headers.delete :authorization
|
36
|
+
expect(check_data(request)).to be_falsy
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe '#too_old?' do
|
41
|
+
it 'should allow recent enough requests' do
|
42
|
+
expect(too_old?(request)).to be_falsy
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should reject old requests' do
|
46
|
+
request.headers[:x_saa_auth_time] = Time.new(2014, 11, 8).iso8601
|
47
|
+
expect(too_old?(request)).to be_truthy
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe '#secure_equals?' do
|
52
|
+
it 'should succeed on equal values' do
|
53
|
+
expect(secure_equals?('foo', 'foo', 'secret_key')).to be_truthy
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'should fail on different values' do
|
57
|
+
expect(secure_equals?('foo', 'bar', 'secret_key')).to be_falsy
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
describe SimpleApiAuth do
|
2
|
+
describe SimpleApiAuth::Helpers::Request do
|
3
|
+
include SimpleApiAuth::Helpers::Request
|
4
|
+
|
5
|
+
describe '#normalize_headers' do
|
6
|
+
let(:raw_headers) { mock_headers }
|
7
|
+
let(:headers) { normalize_headers(raw_headers) }
|
8
|
+
|
9
|
+
it 'should normalize headers keys' do
|
10
|
+
[:authorization, :x_saa_auth_time, :x_saa_key].each do |key|
|
11
|
+
expect(headers).to include(key)
|
12
|
+
end
|
13
|
+
expect(headers.values).to eq(raw_headers.values)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#normalize' do
|
18
|
+
it 'should symbolize keys' do
|
19
|
+
expect(normalize('foo')).to eq(:foo)
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should work with symbols' do
|
23
|
+
expect(normalize(:foo)).to eq(:foo)
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should downcase' do
|
27
|
+
expect(normalize('FOO')).to eq(:foo)
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should replace hyphens' do
|
31
|
+
expect(normalize('foo-bar')).to eq(:foo_bar)
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'should work with more complex keys' do
|
35
|
+
expect(normalize('FOO-BAR-BAZ')).to eq(:foo_bar_baz)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
describe SimpleApiAuth do
|
2
|
+
describe SimpleApiAuth::Request do
|
3
|
+
include SimpleApiAuth::Helpers::Request
|
4
|
+
|
5
|
+
let(:base_request) { SimpleApiAuth::Request.create(rails_request) }
|
6
|
+
|
7
|
+
def check_request(request)
|
8
|
+
expect(request.headers).to eq(normalize_headers(mock_headers))
|
9
|
+
expect(request.http_verb).to eq(:get)
|
10
|
+
end
|
11
|
+
|
12
|
+
describe '#initialize' do
|
13
|
+
let(:request) { SimpleApiAuth::Request.create(rails_request) }
|
14
|
+
|
15
|
+
it 'should set headers and http verb' do
|
16
|
+
check_request(request)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe '#create' do
|
21
|
+
context 'with third party requests' do
|
22
|
+
requests.each do |name, request|
|
23
|
+
before(:each) { request.configure }
|
24
|
+
let(:dummy_request) { request.new(headers: mock_headers, method: 'GET') }
|
25
|
+
|
26
|
+
it "should create normalized request from #{name}" do
|
27
|
+
normalized_request = SimpleApiAuth::Request.create(dummy_request)
|
28
|
+
check_request(normalized_request)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should not modify already normalized requests' do
|
34
|
+
expect(SimpleApiAuth::Request.create(base_request).object_id).to eq(base_request.object_id)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe '#time' do
|
39
|
+
it 'should return request time' do
|
40
|
+
expect(base_request.time).to eq(request_time)
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'should return nil on wrong time' do
|
44
|
+
base_request.headers[:x_saa_auth_time] = 'foobar'
|
45
|
+
expect(base_request.time).to be_nil
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
describe SimpleApiAuth do
|
2
|
+
describe SimpleApiAuth::Signer do
|
3
|
+
|
4
|
+
let(:signer) { SimpleApiAuth::Signer.new }
|
5
|
+
let(:request) { mock_request }
|
6
|
+
|
7
|
+
describe '#make_hashed_request' do
|
8
|
+
it 'should make a hased canonical request' do
|
9
|
+
expect(signer.make_hashed_request(request)).to eq(mock_hashed_request)
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'should encode payload' do
|
13
|
+
request.body = StringIO.new('abc')
|
14
|
+
expected = <<-EOF.unindent[0..-2]
|
15
|
+
hashed:get
|
16
|
+
/foobar
|
17
|
+
foo=bar&baz=qux
|
18
|
+
#{Digest.hexencode('hashed:abc')}
|
19
|
+
EOF
|
20
|
+
expect(signer.make_hashed_request(request)).to eq(Digest.hexencode(expected))
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '#make_string_to_sign' do
|
25
|
+
it 'should generate correct string to sign' do
|
26
|
+
expect(signer.make_string_to_sign(request)).to eq(mock_string_to_sign)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe '#sign' do
|
31
|
+
let(:secret_key) { 'ultra_secret_key' }
|
32
|
+
it 'should generate correct signature' do
|
33
|
+
expect(signer.sign(request, secret_key)).to eq(mock_signature)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
describe SimpleApiAuth do
|
2
|
+
it 'should be configurable' do
|
3
|
+
expect(SimpleApiAuth.config.request_fields[:headers]).to eq(:headers)
|
4
|
+
SimpleApiAuth.configure do |config|
|
5
|
+
config.request_fields[:headers] = :env
|
6
|
+
end
|
7
|
+
expect(SimpleApiAuth.config.request_fields[:headers]).to eq(:env)
|
8
|
+
end
|
9
|
+
|
10
|
+
describe '#log' do
|
11
|
+
it 'should not fail on nil' do
|
12
|
+
SimpleApiAuth.log('foobar')
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should log when logger is present' do
|
16
|
+
s = StringIO.new('', 'a')
|
17
|
+
SimpleApiAuth.config.logger = Logger.new(s)
|
18
|
+
expect(s.string).not_to include('foobar')
|
19
|
+
SimpleApiAuth.log(Logger::WARN, 'foobar')
|
20
|
+
expect(s.string).to include('foobar')
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '#extract_key' do
|
25
|
+
let(:request) { mock_request }
|
26
|
+
subject { SimpleApiAuth.extract_key(request) }
|
27
|
+
|
28
|
+
it 'should extract correct key' do
|
29
|
+
expect(subject).to eq('user_personal_key')
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should work with custom headers' do
|
33
|
+
SimpleApiAuth.config.header_keys[:key] = :key
|
34
|
+
request.headers[:key] = request.headers.delete(:x_saa_key)
|
35
|
+
expect(subject).to eq('user_personal_key')
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe '#valid_signature?' do
|
40
|
+
let(:request) { mock_request }
|
41
|
+
let(:result) { SimpleApiAuth.valid_signature?(request, mock_secret_key) }
|
42
|
+
|
43
|
+
it 'should fail with wrong request' do
|
44
|
+
expect(result).to be_falsy
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'should succeed with correct signature' do
|
48
|
+
request.headers[:authorization] = "Signature: #{mock_signature}"
|
49
|
+
expect(result).to be_truthy
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'with real world example' do
|
53
|
+
before(:each) do
|
54
|
+
SimpleApiAuth.config.hasher = SimpleApiAuth::Hasher::SHA1
|
55
|
+
end
|
56
|
+
let(:request) do
|
57
|
+
SimpleApiAuth::Request.new(
|
58
|
+
http_verb: 'GET',
|
59
|
+
uri: '/foo/bar',
|
60
|
+
query_string: 'foo=bar&bar=qux',
|
61
|
+
body: StringIO.new('somerandombody'),
|
62
|
+
headers: {
|
63
|
+
authorization: 'Signature: 7c171d095fd65b7078afd13a6b3bd4ecfe596552',
|
64
|
+
x_saa_auth_time: request_time.iso8601,
|
65
|
+
x_saa_key: 'wedontreallycarehere'
|
66
|
+
}
|
67
|
+
)
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'should succeed with correct key' do
|
71
|
+
expect(SimpleApiAuth.valid_signature?(request, mock_secret_key)).to be_truthy
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'should fail with wrong key' do
|
75
|
+
expect(SimpleApiAuth.valid_signature?(request, 'somerandomkey')).to be_falsy
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe 'signing methods' do
|
81
|
+
let(:request) { mock_request }
|
82
|
+
let(:base_secret) { 'a_very_secret_key' }
|
83
|
+
let(:secret_key) { base_secret }
|
84
|
+
subject { SimpleApiAuth.valid_signature?(request, secret_key) }
|
85
|
+
|
86
|
+
describe '#compute_signature' do
|
87
|
+
let(:signature) { SimpleApiAuth.compute_signature(request, secret_key) }
|
88
|
+
|
89
|
+
it 'should return correct signature' do
|
90
|
+
request.headers[:authorization] = "Signature: #{signature}"
|
91
|
+
expect(subject).to be_truthy
|
92
|
+
end
|
93
|
+
|
94
|
+
context 'with a different key' do
|
95
|
+
let(:secret_key) { 'another cool key' }
|
96
|
+
it 'should not return the same signature' do
|
97
|
+
request.headers[:authorization] = "Signature: #{signature}"
|
98
|
+
expect(SimpleApiAuth.valid_signature?(request, base_secret)).to be_falsy
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
describe '#sign!' do
|
104
|
+
before(:each) { SimpleApiAuth.sign!(request, secret_key) }
|
105
|
+
it 'should sign the current request' do
|
106
|
+
expect(subject).to be_truthy
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'coveralls'
|
2
|
+
require 'codeclimate-test-reporter'
|
3
|
+
Coveralls.wear!
|
4
|
+
CodeClimate::TestReporter.start
|
5
|
+
|
6
|
+
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
|
7
|
+
Coveralls::SimpleCov::Formatter,
|
8
|
+
SimpleCov::Formatter::HTMLFormatter,
|
9
|
+
CodeClimate::TestReporter::Formatter
|
10
|
+
]
|
11
|
+
|
12
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__) + '/../lib')
|
13
|
+
|
14
|
+
require 'active_record'
|
15
|
+
require 'database_cleaner'
|
16
|
+
require 'simple-api-auth'
|
17
|
+
require 'rspec/its'
|
18
|
+
|
19
|
+
support_glob = File.expand_path('support/**/*.rb', File.dirname(__FILE__))
|
20
|
+
Dir[support_glob].each { |f| require f }
|
21
|
+
|
22
|
+
RSpec.configure do |config|
|
23
|
+
config.include SpecHelpers::Dummy
|
24
|
+
config.extend SpecHelpers::Requests
|
25
|
+
|
26
|
+
config.before(:each) do
|
27
|
+
allow(Time).to receive(:now) { Time.utc(2014, 11, 8, 0, 7) }
|
28
|
+
SimpleApiAuth.config.reset!
|
29
|
+
SimpleApiAuth.config.hasher = SpecHelpers::Auth::DummyHasher
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
database_yml = File.expand_path('../../internal/config/database.yml', __FILE__)
|
2
|
+
ActiveRecord::Base.default_timezone = :utc
|
3
|
+
ActiveRecord::Base.logger = Logger.new(File.join(File.dirname(__FILE__), '../debug.log'))
|
4
|
+
ActiveRecord::Base.logger.level = ENV['TRAVIS'] ? ::Logger::ERROR : ::Logger::DEBUG
|
5
|
+
ActiveRecord::Base.configurations = YAML.load_file(database_yml)
|
6
|
+
|
7
|
+
begin
|
8
|
+
ActiveRecord::Base.establish_connection(:test)
|
9
|
+
rescue
|
10
|
+
ActiveRecord::Base.establish_connection('test')
|
11
|
+
end
|
12
|
+
|
13
|
+
load(File.dirname(__FILE__) + '/../internal/db/schema.rb')
|
14
|
+
load(File.dirname(__FILE__) + '/../internal/app/models/models.rb')
|
@@ -0,0 +1,19 @@
|
|
1
|
+
RSpec.configure do |config|
|
2
|
+
config.before(:suite) do
|
3
|
+
DatabaseCleaner.clean_with(:truncation)
|
4
|
+
DatabaseCleaner.strategy = :transaction
|
5
|
+
DatabaseCleaner.clean
|
6
|
+
end
|
7
|
+
|
8
|
+
config.after(:suite) do
|
9
|
+
DatabaseCleaner.clean
|
10
|
+
end
|
11
|
+
|
12
|
+
config.before(:each) do
|
13
|
+
DatabaseCleaner.start
|
14
|
+
end
|
15
|
+
|
16
|
+
config.after(:each) do
|
17
|
+
DatabaseCleaner.clean
|
18
|
+
end
|
19
|
+
end
|