slosilo 0.0.0 → 0.1.2
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.
- data/.gitignore +0 -2
- data/LICENSE +2 -2
- data/README.md +8 -128
- data/lib/slosilo/adapters/abstract_adapter.rb +0 -4
- data/lib/slosilo/adapters/mock_adapter.rb +1 -14
- data/lib/slosilo/adapters/sequel_adapter/migration.rb +2 -5
- data/lib/slosilo/adapters/sequel_adapter.rb +5 -67
- data/lib/slosilo/attr_encrypted.rb +7 -33
- data/lib/slosilo/http_request.rb +59 -0
- data/lib/slosilo/key.rb +6 -129
- data/lib/slosilo/keystore.rb +12 -40
- data/lib/slosilo/rack/middleware.rb +123 -0
- data/lib/slosilo/symmetric.rb +17 -47
- data/lib/slosilo/version.rb +2 -21
- data/lib/slosilo.rb +2 -2
- data/lib/tasks/slosilo.rake +0 -10
- data/slosilo.gemspec +6 -19
- data/spec/http_request_spec.rb +107 -0
- data/spec/http_stack_spec.rb +44 -0
- data/spec/key_spec.rb +32 -175
- data/spec/keystore_spec.rb +2 -15
- data/spec/rack_middleware_spec.rb +109 -0
- data/spec/random_spec.rb +2 -12
- data/spec/sequel_adapter_spec.rb +22 -133
- data/spec/slosilo_spec.rb +12 -78
- data/spec/spec_helper.rb +15 -37
- data/spec/symmetric_spec.rb +26 -69
- metadata +51 -104
- checksums.yaml +0 -7
- data/.github/CODEOWNERS +0 -10
- data/.gitleaks.toml +0 -221
- data/.kateproject +0 -4
- data/CHANGELOG.md +0 -50
- data/CONTRIBUTING.md +0 -16
- data/Jenkinsfile +0 -132
- data/SECURITY.md +0 -42
- data/dev/Dockerfile.dev +0 -7
- data/dev/docker-compose.yml +0 -8
- data/lib/slosilo/adapters/file_adapter.rb +0 -42
- data/lib/slosilo/adapters/memory_adapter.rb +0 -31
- data/lib/slosilo/errors.rb +0 -15
- data/lib/slosilo/jwt.rb +0 -122
- data/publish.sh +0 -5
- data/secrets.yml +0 -1
- data/spec/encrypted_attributes_spec.rb +0 -114
- data/spec/file_adapter_spec.rb +0 -81
- data/spec/jwt_spec.rb +0 -102
- data/test.sh +0 -8
data/lib/slosilo/keystore.rb
CHANGED
@@ -7,34 +7,26 @@ module Slosilo
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def put id, key
|
10
|
-
|
11
|
-
fail ArgumentError, "id can't be empty" if id.empty?
|
12
|
-
adapter.put_key id, key
|
10
|
+
adapter.put_key id.to_s, key.to_der
|
13
11
|
end
|
14
12
|
|
15
|
-
def get
|
16
|
-
|
17
|
-
|
18
|
-
key = adapter.get_key(id.to_s)
|
19
|
-
elsif fingerprint
|
20
|
-
key, _ = get_by_fingerprint(fingerprint)
|
21
|
-
end
|
22
|
-
key
|
23
|
-
end
|
24
|
-
|
25
|
-
def get_by_fingerprint fingerprint
|
26
|
-
adapter.get_by_fingerprint fingerprint
|
13
|
+
def get id
|
14
|
+
key = adapter.get_key(id.to_s)
|
15
|
+
key && Key.new(key)
|
27
16
|
end
|
28
17
|
|
29
|
-
def each
|
30
|
-
adapter.each
|
18
|
+
def each(&block)
|
19
|
+
adapter.each(&block)
|
31
20
|
end
|
32
21
|
|
33
22
|
def any? &block
|
34
|
-
|
35
|
-
|
23
|
+
catch :found do
|
24
|
+
adapter.each do |id, k|
|
25
|
+
throw :found if block.call(Key.new(k))
|
26
|
+
end
|
27
|
+
return false
|
36
28
|
end
|
37
|
-
|
29
|
+
true
|
38
30
|
end
|
39
31
|
end
|
40
32
|
|
@@ -59,26 +51,6 @@ module Slosilo
|
|
59
51
|
keystore.any? { |k| k.token_valid? token }
|
60
52
|
end
|
61
53
|
|
62
|
-
# Looks up the signer by public key fingerprint and checks the validity
|
63
|
-
# of the signature. If the token is JWT, exp and/or iat claims are also
|
64
|
-
# verified; the caller is responsible for validating any other claims.
|
65
|
-
def token_signer token
|
66
|
-
begin
|
67
|
-
# see if maybe it's a JWT
|
68
|
-
token = JWT token
|
69
|
-
fingerprint = token.header['kid']
|
70
|
-
rescue ArgumentError
|
71
|
-
fingerprint = token['key']
|
72
|
-
end
|
73
|
-
|
74
|
-
key, id = keystore.get_by_fingerprint fingerprint
|
75
|
-
if key && key.token_valid?(token)
|
76
|
-
return id
|
77
|
-
else
|
78
|
-
return nil
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
54
|
attr_accessor :adapter
|
83
55
|
|
84
56
|
private
|
@@ -0,0 +1,123 @@
|
|
1
|
+
module Slosilo
|
2
|
+
module Rack
|
3
|
+
# Con perform verification of request signature and decryption of request body.
|
4
|
+
#
|
5
|
+
# Signature verification and body decryption are enabled with constructor switches and are
|
6
|
+
# therefore performed (or not) for all requests.
|
7
|
+
#
|
8
|
+
# When signature verification is performed, the following elements are included in the
|
9
|
+
# signature string:
|
10
|
+
#
|
11
|
+
# 1. Request path and query string
|
12
|
+
# 2. base64 encoded request body
|
13
|
+
# 3. Request timestamp from HTTP_TIMESTAMP
|
14
|
+
# 4. Body encryption key from HTTP_X_SLOSILO_KEY (if present)
|
15
|
+
#
|
16
|
+
# When body decryption is performed, an encryption key for the message body is encrypted
|
17
|
+
# with this service's public key and placed in HTTP_X_SLOSILO_KEY. This middleware
|
18
|
+
# decryps the key using our :own private key, and then decrypts the body using the decrypted key.
|
19
|
+
class Middleware
|
20
|
+
class EncryptionError < SecurityError
|
21
|
+
end
|
22
|
+
class SignatureError < SecurityError
|
23
|
+
end
|
24
|
+
|
25
|
+
def initialize app, opts = {}
|
26
|
+
@app = app
|
27
|
+
@encryption_required = opts[:encryption_required] || false
|
28
|
+
@signature_required = opts[:signature_required] || false
|
29
|
+
end
|
30
|
+
|
31
|
+
def call env
|
32
|
+
@env = env
|
33
|
+
@body = env['rack.input'].read rescue ""
|
34
|
+
|
35
|
+
begin
|
36
|
+
verify
|
37
|
+
decrypt
|
38
|
+
rescue EncryptionError
|
39
|
+
return error 403, $!.message
|
40
|
+
rescue SignatureError
|
41
|
+
return error 401, $!.message
|
42
|
+
end
|
43
|
+
|
44
|
+
@app.call env
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
def verify
|
49
|
+
if signature
|
50
|
+
raise SignatureError, "Bad signature" unless Slosilo.token_valid?(token)
|
51
|
+
else
|
52
|
+
raise SignatureError, "Signature required" if signature_required?
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
attr_reader :env
|
57
|
+
|
58
|
+
def token
|
59
|
+
return nil unless signature
|
60
|
+
t = { "data" => { "path" => path, "body" => [body].pack('m0') }, "timestamp" => timestamp, "signature" => signature }
|
61
|
+
t["data"]["key"] = encoded_key if encoded_key
|
62
|
+
t['data']['authorization'] = env['HTTP_AUTHORIZATION'] if env['HTTP_AUTHORIZATION']
|
63
|
+
t
|
64
|
+
end
|
65
|
+
|
66
|
+
def path
|
67
|
+
env['SCRIPT_NAME'] + env['PATH_INFO'] + query_string
|
68
|
+
end
|
69
|
+
|
70
|
+
def query_string
|
71
|
+
if env['QUERY_STRING'].empty?
|
72
|
+
''
|
73
|
+
else
|
74
|
+
'?' + env['QUERY_STRING']
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
attr_reader :body
|
79
|
+
|
80
|
+
def timestamp
|
81
|
+
env['HTTP_TIMESTAMP']
|
82
|
+
end
|
83
|
+
|
84
|
+
def signature
|
85
|
+
env['HTTP_X_SLOSILO_SIGNATURE']
|
86
|
+
end
|
87
|
+
|
88
|
+
def encoded_key
|
89
|
+
env['HTTP_X_SLOSILO_KEY']
|
90
|
+
end
|
91
|
+
|
92
|
+
def key
|
93
|
+
if encoded_key
|
94
|
+
Base64::urlsafe_decode64(encoded_key)
|
95
|
+
else
|
96
|
+
raise EncryptionError, "Encryption required" if encryption_required?
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def decrypt
|
101
|
+
return unless key
|
102
|
+
plaintext = Slosilo[:own].decrypt body, key
|
103
|
+
env['rack.input'] = StringIO.new plaintext
|
104
|
+
rescue EncryptionError
|
105
|
+
raise unless body.empty? || body.nil?
|
106
|
+
rescue Exception => e
|
107
|
+
raise EncryptionError, "Bad encryption", e.backtrace
|
108
|
+
end
|
109
|
+
|
110
|
+
def error status, message
|
111
|
+
[status, { 'Content-Type' => 'text/plain', 'Content-Length' => message.length.to_s }, [message] ]
|
112
|
+
end
|
113
|
+
|
114
|
+
def encryption_required?
|
115
|
+
@encryption_required
|
116
|
+
end
|
117
|
+
|
118
|
+
def signature_required?
|
119
|
+
@signature_required
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
data/lib/slosilo/symmetric.rb
CHANGED
@@ -1,63 +1,33 @@
|
|
1
1
|
module Slosilo
|
2
2
|
class Symmetric
|
3
|
-
VERSION_MAGIC = 'G'
|
4
|
-
TAG_LENGTH = 16
|
5
|
-
|
6
3
|
def initialize
|
7
|
-
@cipher = OpenSSL::Cipher.new '
|
8
|
-
@cipher_mutex = Mutex.new
|
4
|
+
@cipher = OpenSSL::Cipher.new 'AES-256-CBC'
|
9
5
|
end
|
10
|
-
|
11
|
-
# This lets us do a final sanity check in migrations from older encryption versions
|
12
|
-
def cipher_name
|
13
|
-
@cipher.name
|
14
|
-
end
|
15
|
-
|
6
|
+
|
16
7
|
def encrypt plaintext, opts = {}
|
17
|
-
|
18
|
-
|
19
|
-
@
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
@cipher.iv = iv = random_iv
|
24
|
-
@cipher.auth_data = opts[:aad] || "" # Nothing good happens if you set this to nil, or don't set it at all
|
25
|
-
ctext = @cipher.update(plaintext) + @cipher.final
|
26
|
-
tag = @cipher.auth_tag(TAG_LENGTH)
|
27
|
-
"#{VERSION_MAGIC}#{tag}#{iv}#{ctext}"
|
28
|
-
end
|
8
|
+
@cipher.reset
|
9
|
+
@cipher.encrypt
|
10
|
+
@cipher.key = opts[:key]
|
11
|
+
@cipher.iv = iv = random_iv
|
12
|
+
ctxt = @cipher.update(plaintext)
|
13
|
+
iv + ctxt + @cipher.final
|
29
14
|
end
|
30
|
-
|
15
|
+
|
31
16
|
def decrypt ciphertext, opts = {}
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
@cipher_mutex.synchronize do
|
39
|
-
@cipher.reset
|
40
|
-
@cipher.decrypt
|
41
|
-
@cipher.key = opts[:key]
|
42
|
-
@cipher.iv = iv
|
43
|
-
@cipher.auth_tag = tag
|
44
|
-
@cipher.auth_data = opts[:aad] || ""
|
45
|
-
@cipher.update(ctext) + @cipher.final
|
46
|
-
end
|
17
|
+
@cipher.reset
|
18
|
+
@cipher.decrypt
|
19
|
+
@cipher.key = opts[:key]
|
20
|
+
@cipher.iv, ctxt = ciphertext.unpack("a#{@cipher.iv_len}a*")
|
21
|
+
ptxt = @cipher.update(ctxt)
|
22
|
+
ptxt + @cipher.final
|
47
23
|
end
|
48
|
-
|
24
|
+
|
49
25
|
def random_iv
|
50
26
|
@cipher.random_iv
|
51
27
|
end
|
52
|
-
|
28
|
+
|
53
29
|
def random_key
|
54
30
|
@cipher.random_key
|
55
31
|
end
|
56
|
-
|
57
|
-
private
|
58
|
-
# return tag, iv, ctext
|
59
|
-
def unpack msg
|
60
|
-
msg.unpack "aa#{TAG_LENGTH}a#{@cipher.iv_len}a*"
|
61
|
-
end
|
62
32
|
end
|
63
33
|
end
|
data/lib/slosilo/version.rb
CHANGED
@@ -1,22 +1,3 @@
|
|
1
|
-
# Copyright 2013-2021 Conjur Inc.
|
2
|
-
#
|
3
|
-
# Permission is hereby granted, free of charge, to any person obtaining a copy of
|
4
|
-
# this software and associated documentation files (the "Software"), to deal in
|
5
|
-
# the Software without restriction, including without limitation the rights to
|
6
|
-
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
7
|
-
# the Software, and to permit persons to whom the Software is furnished to do so,
|
8
|
-
# subject to the following conditions:
|
9
|
-
#
|
10
|
-
# The above copyright notice and this permission notice shall be included in all
|
11
|
-
# copies or substantial portions of the Software.
|
12
|
-
#
|
13
|
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
15
|
-
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
16
|
-
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
17
|
-
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
18
|
-
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
19
|
-
|
20
1
|
module Slosilo
|
21
|
-
VERSION =
|
22
|
-
end
|
2
|
+
VERSION = "0.1.2"
|
3
|
+
end
|
data/lib/slosilo.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
require "slosilo/jwt"
|
2
1
|
require "slosilo/version"
|
3
2
|
require "slosilo/keystore"
|
4
3
|
require "slosilo/symmetric"
|
5
4
|
require "slosilo/attr_encrypted"
|
6
5
|
require "slosilo/random"
|
7
|
-
require "slosilo/
|
6
|
+
require "slosilo/rack/middleware"
|
7
|
+
require "slosilo/http_request"
|
8
8
|
|
9
9
|
if defined? Sequel
|
10
10
|
require 'slosilo/adapters/sequel_adapter'
|
data/lib/tasks/slosilo.rake
CHANGED
@@ -19,14 +19,4 @@ namespace :slosilo do
|
|
19
19
|
Slosilo[args[:name]] = key
|
20
20
|
puts key
|
21
21
|
end
|
22
|
-
|
23
|
-
desc "Migrate to a new database schema"
|
24
|
-
task :migrate => :environment do |t|
|
25
|
-
Slosilo.adapter.migrate!
|
26
|
-
end
|
27
|
-
|
28
|
-
desc "Recalculate fingerprints in keystore"
|
29
|
-
task :recalculate_fingerprints => :environment do |t|
|
30
|
-
Slosilo.adapter.recalculate_fingerprints
|
31
|
-
end
|
32
22
|
end
|
data/slosilo.gemspec
CHANGED
@@ -1,20 +1,12 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
-
|
3
|
-
require File.expand_path('lib/slosilo/version.rb', __FILE__)
|
4
|
-
rescue LoadError
|
5
|
-
# so that bundle can be run without the app code
|
6
|
-
module Slosilo
|
7
|
-
VERSION = '0.0.0'
|
8
|
-
end
|
9
|
-
end
|
2
|
+
require File.expand_path('../lib/slosilo/version', __FILE__)
|
10
3
|
|
11
4
|
Gem::Specification.new do |gem|
|
12
5
|
gem.authors = ["Rafa\305\202 Rzepecki"]
|
13
6
|
gem.email = ["divided.mind@gmail.com"]
|
14
7
|
gem.description = %q{This gem provides an easy way of storing and retrieving encryption keys in the database.}
|
15
8
|
gem.summary = %q{Store SSL keys in a database}
|
16
|
-
gem.homepage = "
|
17
|
-
gem.license = "MIT"
|
9
|
+
gem.homepage = ""
|
18
10
|
|
19
11
|
gem.files = `git ls-files`.split($\)
|
20
12
|
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
@@ -22,17 +14,12 @@ Gem::Specification.new do |gem|
|
|
22
14
|
gem.name = "slosilo"
|
23
15
|
gem.require_paths = ["lib"]
|
24
16
|
gem.version = Slosilo::VERSION
|
25
|
-
gem.required_ruby_version = '
|
26
|
-
|
17
|
+
gem.required_ruby_version = '~> 1.9.3'
|
18
|
+
|
27
19
|
gem.add_development_dependency 'rake'
|
28
|
-
gem.add_development_dependency 'rspec'
|
29
|
-
gem.add_development_dependency '
|
20
|
+
gem.add_development_dependency 'rspec'
|
21
|
+
gem.add_development_dependency 'ci_reporter'
|
30
22
|
gem.add_development_dependency 'simplecov'
|
31
|
-
gem.add_development_dependency 'simplecov-cobertura'
|
32
|
-
gem.add_development_dependency 'io-grab', '~> 0.0.1'
|
33
23
|
gem.add_development_dependency 'sequel' # for sequel tests
|
34
24
|
gem.add_development_dependency 'sqlite3' # for sequel tests
|
35
|
-
gem.add_development_dependency 'bigdecimal' # for activesupport
|
36
|
-
gem.add_development_dependency 'activesupport' # for convenience in specs
|
37
25
|
end
|
38
|
-
|
@@ -0,0 +1,107 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Slosilo::HTTPRequest do
|
4
|
+
let(:keyname) { :bacon }
|
5
|
+
let(:encrypt) { subject.encrypt! }
|
6
|
+
subject { Hash.new }
|
7
|
+
before do
|
8
|
+
subject.extend Slosilo::HTTPRequest
|
9
|
+
subject.keyname = keyname
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "#sign!" do
|
13
|
+
let(:own_key) { double "own key" }
|
14
|
+
before { Slosilo.stub(:[]).with(:own).and_return own_key }
|
15
|
+
|
16
|
+
let(:signed_data) { "this is the truest truth" }
|
17
|
+
before { subject.stub signed_data: signed_data }
|
18
|
+
let(:timestamp) { "long time ago" }
|
19
|
+
let(:signature) { "seal of approval" }
|
20
|
+
let(:token) { { "data" => signed_data, "timestamp" => timestamp, "signature" => signature } }
|
21
|
+
|
22
|
+
it "makes a token out of the data to sign and inserts headers" do
|
23
|
+
own_key.stub(:signed_token).with(signed_data).and_return token
|
24
|
+
subject.should_receive(:[]=).with 'Timestamp', timestamp
|
25
|
+
subject.should_receive(:[]=).with 'X-Slosilo-Signature', signature
|
26
|
+
subject.sign!
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "#signed_data" do
|
31
|
+
before { subject.stub path: :path, body: 'body' }
|
32
|
+
context "when X-Slosilo-Key not present" do
|
33
|
+
its(:signed_data) { should == { "path" => :path, "body" => "Ym9keQ==" } }
|
34
|
+
end
|
35
|
+
|
36
|
+
context "when X-Slosilo-Key is present" do
|
37
|
+
before { subject.merge! 'X-Slosilo-Key' => :key }
|
38
|
+
its(:signed_data) { should == { "path" => :path, "body" => "Ym9keQ==", "key" => :key } }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe "#encrypt!" do
|
43
|
+
context "when key not set" do
|
44
|
+
before { subject.keyname = nil }
|
45
|
+
it "does nothing" do
|
46
|
+
subject.should_not_receive(:body=)
|
47
|
+
encrypt
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context "when requested key does not exist" do
|
52
|
+
before { Slosilo.stub(:[]).and_return nil }
|
53
|
+
it "raises error" do
|
54
|
+
expect{ encrypt }.to raise_error
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context "when the key exists" do
|
59
|
+
let(:key) { double "key" }
|
60
|
+
context "when the body is not empty" do
|
61
|
+
let(:plaintext) { "Keep your solutions close, and your problems closer." }
|
62
|
+
let(:ciphertext) { "And, when you want something, all the universe conspires in helping you to achieve it." }
|
63
|
+
let(:skey) { "make me sound like a fool instead" }
|
64
|
+
before do
|
65
|
+
subject.stub body: plaintext
|
66
|
+
key.stub(:encrypt).with(plaintext).and_return([ciphertext, skey])
|
67
|
+
Slosilo.stub(:[]).with(keyname).and_return key
|
68
|
+
end
|
69
|
+
|
70
|
+
it "encrypts the message body and adds the X-Slosilo-Key header" do
|
71
|
+
subject.should_receive(:body=).with ciphertext
|
72
|
+
subject.should_receive(:[]=).with 'X-Slosilo-Key', Base64::urlsafe_encode64(skey)
|
73
|
+
encrypt
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context "when the body is empty" do
|
78
|
+
before { subject.stub body: "" }
|
79
|
+
it "doesn't set the key header" do
|
80
|
+
subject.should_not_receive(:[]=).with 'X-Slosilo-Key'
|
81
|
+
encrypt
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
describe "#exec" do
|
88
|
+
class Subject
|
89
|
+
def exec *a
|
90
|
+
"ok, got it"
|
91
|
+
end
|
92
|
+
|
93
|
+
def initialize keyname
|
94
|
+
extend Slosilo::HTTPRequest
|
95
|
+
self.keyname = keyname
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
subject { Subject.new keyname }
|
100
|
+
|
101
|
+
it "encrypts, then signs and delegates to the superclass" do
|
102
|
+
subject.should_receive(:encrypt!).once.ordered
|
103
|
+
subject.should_receive(:sign!).once.ordered
|
104
|
+
subject.exec(:foo).should == "ok, got it"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "http request stack" do
|
4
|
+
include_context "with example key"
|
5
|
+
include_context "with mock adapter"
|
6
|
+
before { Slosilo[:own] = key }
|
7
|
+
|
8
|
+
class MockRequest < Hash
|
9
|
+
def exec *a
|
10
|
+
end
|
11
|
+
|
12
|
+
def [] name
|
13
|
+
name = name.sub(/^HTTP_/,'').gsub('_', '-').split(/(\W)/).map(&:capitalize).join
|
14
|
+
result = super name
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize
|
18
|
+
extend Slosilo::HTTPRequest
|
19
|
+
self['Authorization'] = "Simon says it's fine"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
subject { MockRequest.new }
|
24
|
+
let(:path) { '/some/path' }
|
25
|
+
|
26
|
+
context "with authorization header" do
|
27
|
+
it "works" do
|
28
|
+
mw = Slosilo::Rack::Middleware.new lambda{|_|:ok}, signature_required: true
|
29
|
+
subject.stub path: path, body: ''
|
30
|
+
mw.stub path: path
|
31
|
+
subject.send :exec
|
32
|
+
mw.call(subject).should == :ok
|
33
|
+
end
|
34
|
+
|
35
|
+
it "detects tampering" do
|
36
|
+
mw = Slosilo::Rack::Middleware.new lambda{|_|:ok}, signature_required: true
|
37
|
+
subject.stub path: path, body: ''
|
38
|
+
mw.stub path: path
|
39
|
+
subject.send :exec
|
40
|
+
subject['Authorization'] = "Simon changed his mind"
|
41
|
+
mw.call(subject).should_not == :ok
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|