slosilo 0.1.2 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,34 @@
1
+ require 'slosilo/adapters/abstract_adapter'
2
+
3
+ module Slosilo
4
+ module Adapters
5
+ class FileAdapter < AbstractAdapter
6
+ attr_reader :dir
7
+
8
+ def initialize(dir)
9
+ @dir = dir
10
+ @keys = {}
11
+ Dir[File.join(@dir, "*.key")].each do |f|
12
+ key = Slosilo::EncryptedAttributes.decrypt File.read(f)
13
+ @keys[File.basename(f, '.key')] = key
14
+ end
15
+ end
16
+
17
+ def put_key id, value
18
+ raise "id should not contain a period" if id.index('.')
19
+ fname = File.join(dir, "#{id}.key")
20
+ File.write(fname, Slosilo::EncryptedAttributes.encrypt(value))
21
+ File.chmod(0400, fname)
22
+ @keys[id] = value
23
+ end
24
+
25
+ def get_key id
26
+ @keys[id]
27
+ end
28
+
29
+ def each(&block)
30
+ @keys.each(&block)
31
+ end
32
+ end
33
+ end
34
+ end
@@ -15,7 +15,10 @@ module Slosilo
15
15
 
16
16
  # Create the table for holding keys
17
17
  def create_keystore_table
18
- create_table keystore_table do
18
+ # docs say to not use create_table? in migration;
19
+ # but we really want this to be robust in case there are any previous installs
20
+ # and we can't use table_exists? because it rolls back
21
+ create_table? keystore_table do
19
22
  String :id, primary_key: true
20
23
  # Note: currently only postgres is supported
21
24
  bytea :key, null: false
@@ -56,4 +56,4 @@ module Slosilo
56
56
  end
57
57
  end
58
58
 
59
- Object.send:include, Slosilo::EncryptedAttributes
59
+ Object.send :include, Slosilo::EncryptedAttributes
data/lib/slosilo/key.rb CHANGED
@@ -58,12 +58,15 @@ module Slosilo
58
58
  def signed_token data
59
59
  token = { "data" => data, "timestamp" => Time.new.utc.to_s }
60
60
  token["signature"] = Base64::urlsafe_encode64(sign token)
61
+ token["key"] = fingerprint
61
62
  token
62
63
  end
63
64
 
64
65
  def token_valid? token, expiry = 8 * 60
65
66
  token = token.clone
66
67
  signature = Base64::urlsafe_decode64(token.delete "signature")
68
+ expected_key = token.delete "key"
69
+ return false if expected_key and expected_key != fingerprint
67
70
  (Time.parse(token["timestamp"]) + expiry > Time.now) && verify_signature(token, signature)
68
71
  end
69
72
 
@@ -72,6 +75,10 @@ module Slosilo
72
75
  key.private_encrypt(hash_function.digest(_salt + value)) + _salt
73
76
  end
74
77
 
78
+ def fingerprint
79
+ OpenSSL::Digest::MD5.hexdigest key.public_key.to_der
80
+ end
81
+
75
82
  private
76
83
  def stringify value
77
84
  case value
@@ -15,18 +15,15 @@ module Slosilo
15
15
  key && Key.new(key)
16
16
  end
17
17
 
18
- def each(&block)
19
- adapter.each(&block)
18
+ def each &_
19
+ adapter.each { |k, v| yield k, Key.new(v) }
20
20
  end
21
21
 
22
22
  def any? &block
23
- catch :found do
24
- adapter.each do |id, k|
25
- throw :found if block.call(Key.new(k))
26
- end
27
- return false
23
+ each do |_, k|
24
+ return true if yield k
28
25
  end
29
- true
26
+ return false
30
27
  end
31
28
  end
32
29
 
@@ -51,6 +48,13 @@ module Slosilo
51
48
  keystore.any? { |k| k.token_valid? token }
52
49
  end
53
50
 
51
+ def token_signer token
52
+ each do |id, key|
53
+ return id if key.token_valid? token
54
+ end
55
+ return nil
56
+ end
57
+
54
58
  attr_accessor :adapter
55
59
 
56
60
  private
@@ -1,3 +1,3 @@
1
1
  module Slosilo
2
- VERSION = "0.1.2"
2
+ VERSION = "0.2.2"
3
3
  end
@@ -0,0 +1,72 @@
1
+ require 'spec_helper'
2
+
3
+ require 'slosilo/adapters/file_adapter'
4
+
5
+ describe Slosilo::Adapters::FileAdapter do
6
+ let(:dir) {
7
+ require 'tmpdir'
8
+ Dir.mktmpdir
9
+ }
10
+ let(:adapter) { Slosilo::Adapters::FileAdapter.new dir }
11
+ subject { adapter }
12
+
13
+ describe "#get_key" do
14
+ context "when given key does not exist" do
15
+ it "returns nil" do
16
+ subject.get_key(:whatever).should_not be
17
+ end
18
+ end
19
+ end
20
+
21
+ describe "#put_key" do
22
+ let(:key) { "key" }
23
+ context "unacceptable id" do
24
+ let(:id) { "foo.bar" }
25
+ it "isn't accepted" do
26
+ lambda { subject.put_key id, key }.should raise_error
27
+ end
28
+ end
29
+ context "acceptable id" do
30
+ let(:id) { "id" }
31
+ let(:key_encrypted) { "encrypted key" }
32
+ let(:fname) { "#{dir}/#{id}.key" }
33
+ it "creates the key" do
34
+ Slosilo::EncryptedAttributes.should_receive(:encrypt).with(key).and_return key_encrypted
35
+ File.should_receive(:write).with(fname, key_encrypted)
36
+ File.should_receive(:chmod).with(0400, fname)
37
+ subject.put_key id, key
38
+ subject.instance_variable_get("@keys")[id].should == key
39
+ end
40
+ end
41
+ end
42
+
43
+ describe "#each" do
44
+ before { adapter.instance_variable_set("@keys", one: :onek, two: :twok) }
45
+
46
+ it "iterates over each key" do
47
+ results = []
48
+ adapter.each { |id,k| results << { id => k } }
49
+ results.should == [ { one: :onek}, {two: :twok } ]
50
+ end
51
+ end
52
+
53
+ describe 'key store' do
54
+ let(:key) { 'fake key' }
55
+ let(:id) { 'some id' }
56
+
57
+ before do
58
+ Slosilo::encryption_key = Slosilo::Symmetric.new.random_key
59
+ pre_adapter = Slosilo::Adapters::FileAdapter.new dir
60
+ pre_adapter.put_key(id, key)
61
+ end
62
+
63
+ it "loads and decrypts the key" do
64
+ adapter.get_key(id).should == key
65
+ end
66
+ it "enumerates the keys" do
67
+ results = []
68
+ adapter.each { |id,k| results << { id => k } }
69
+ results.should == [ { id => key } ]
70
+ end
71
+ end
72
+ end
data/spec/key_spec.rb CHANGED
@@ -6,6 +6,7 @@ describe Slosilo::Key do
6
6
  subject { key }
7
7
  its(:to_der) { should == rsa.to_der }
8
8
  its(:to_s) { should == rsa.public_key.to_pem }
9
+ its(:fingerprint) { should == key_fingerprint }
9
10
 
10
11
  let(:plaintext) { 'quick brown fox jumped over the lazy dog' }
11
12
  describe '#encrypt' do
@@ -79,7 +80,7 @@ describe Slosilo::Key do
79
80
  let(:signature) { "signature" }
80
81
  let(:salt) { 'a pinch of salt' }
81
82
  let(:expected_signature) { Base64::urlsafe_encode64 "\xB0\xCE{\x9FP\xEDV\x9C\xE7b\x8B[\xFAil\x87^\x96\x17Z\x97\x1D\xC2?B\x96\x9C\x8Ep-\xDF_\x8F\xC21\xD9^\xBC\n\x16\x04\x8DJ\xF6\xAF-\xEC\xAD\x03\xF9\xEE:\xDF\xB5\x8F\xF9\xF6\x81m\xAB\x9C\xAB1\x1E\x837\x8C\xFB\xA8P\xA8<\xEA\x1Dx\xCEd\xED\x84f\xA7\xB5t`\x96\xCC\x0F\xA9t\x8B\x9Fo\xBF\x92K\xFA\xFD\xC5?\x8F\xC68t\xBC\x9F\xDE\n$\xCA\xD2\x8F\x96\x0EtX2\x8Cl\x1E\x8Aa\r\x8D\xCAi\x86\x1A\xBD\x1D\xF7\xBC\x8561j\x91YlO\xFA(\x98\x10iq\xCC\xAF\x9BV\xC6\v\xBC\x10Xm\xCD\xFE\xAD=\xAA\x95,\xB4\xF7\xE8W\xB8\x83;\x81\x88\xE6\x01\xBA\xA5F\x91\x17\f\xCE\x80\x8E\v\x83\x9D<\x0E\x83\xF6\x8D\x03\xC0\xE8A\xD7\x90i\x1D\x030VA\x906D\x10\xA0\xDE\x12\xEF\x06M\xD8\x8B\xA9W\xC8\x9DTc\x8AJ\xA4\xC0\xD3!\xFA\x14\x89\xD1p\xB4J7\xA5\x04\xC2l\xDC8<\x04Y\xD8\xA4\xFB[\x89\xB1\xEC\xDA\xB8\xD7\xEA\x03Ja pinch of salt" }
82
- let(:expected_token) { { "data" => data, "timestamp" => "2012-01-01 01:01:01 UTC", "signature" => expected_signature } }
83
+ let(:expected_token) { token_to_sign.merge "signature" => expected_signature, "key" => key_fingerprint }
83
84
  before do
84
85
  key.stub salt: salt
85
86
  Time.stub new: time
@@ -95,6 +96,15 @@ describe Slosilo::Key do
95
96
  before { Time.stub now: Time.new(2012,1,1,1,2,1,0) }
96
97
  subject { key.token_valid? token }
97
98
  it { should be_true }
99
+
100
+ it "doesn't check signature on the advisory key field" do
101
+ key.token_valid?(token.merge "key" => key_fingerprint).should be_true
102
+ end
103
+
104
+ it "rejects the token if the key field is present and doesn't match" do
105
+ key.token_valid?(token.merge "key" => "this is not the key you are looking for").should_not be_true
106
+ end
107
+
98
108
  context "when token is 1 hour old" do
99
109
  before { Time.stub now: Time.new(2012,1,1,2,1,1,0) }
100
110
  it { should be_false }
data/spec/slosilo_spec.rb CHANGED
@@ -55,4 +55,23 @@ describe Slosilo do
55
55
  it { should be_true }
56
56
  end
57
57
  end
58
+
59
+ describe '.token_signer' do
60
+ let(:token) { double "token" }
61
+ let(:key_one) { double "key", token_valid?: false }
62
+ let(:other_key) { double "another key", token_valid?: false }
63
+
64
+ before do
65
+ subject.stub(:each).and_yield('test', key_one).and_yield('other', other_key)
66
+ end
67
+
68
+ it "returns nil when token doesn't have a valid signature from any known key" do
69
+ subject.token_signer(token).should_not be
70
+ end
71
+
72
+ it "returns the name of the key which validates the token" do
73
+ other_key.stub(:token_valid?).with(token).and_return true
74
+ subject.token_signer(token).to_s.should == 'other'
75
+ end
76
+ end
58
77
  end
data/spec/spec_helper.rb CHANGED
@@ -41,6 +41,7 @@ Dg1ikwi8GUF4HPZe9DyhXgDhg19wM/qcpjX8bSypsUWHWP+FanhjdWU=
41
41
  -----END RSA PRIVATE KEY-----
42
42
  """ }
43
43
  let (:key) { Slosilo::Key.new rsa.to_der }
44
+ let (:key_fingerprint) { "d28e3a347e368416b3129a40c1b887fe" }
44
45
 
45
46
  def self.mock_own_key
46
47
  before { Slosilo.stub(:[]).with(:own).and_return key }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: slosilo
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-03-12 00:00:00.000000000 Z
12
+ date: 2013-04-12 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
@@ -122,6 +122,7 @@ files:
122
122
  - Rakefile
123
123
  - lib/slosilo.rb
124
124
  - lib/slosilo/adapters/abstract_adapter.rb
125
+ - lib/slosilo/adapters/file_adapter.rb
125
126
  - lib/slosilo/adapters/mock_adapter.rb
126
127
  - lib/slosilo/adapters/sequel_adapter.rb
127
128
  - lib/slosilo/adapters/sequel_adapter/migration.rb
@@ -135,6 +136,7 @@ files:
135
136
  - lib/slosilo/version.rb
136
137
  - lib/tasks/slosilo.rake
137
138
  - slosilo.gemspec
139
+ - spec/file_adapter_spec.rb
138
140
  - spec/http_request_spec.rb
139
141
  - spec/http_stack_spec.rb
140
142
  - spec/key_spec.rb
@@ -165,7 +167,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
165
167
  version: '0'
166
168
  segments:
167
169
  - 0
168
- hash: 40977695530656319
170
+ hash: 2238471852209156981
169
171
  requirements: []
170
172
  rubyforge_project:
171
173
  rubygems_version: 1.8.24
@@ -173,6 +175,7 @@ signing_key:
173
175
  specification_version: 3
174
176
  summary: Store SSL keys in a database
175
177
  test_files:
178
+ - spec/file_adapter_spec.rb
176
179
  - spec/http_request_spec.rb
177
180
  - spec/http_stack_spec.rb
178
181
  - spec/key_spec.rb