slosilo 0.1.2 → 0.2.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.
@@ -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