trocla-ruby2 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/.document +4 -0
  3. data/.rspec +1 -0
  4. data/.travis.yml +10 -0
  5. data/CHANGELOG.md +71 -0
  6. data/Gemfile +51 -0
  7. data/LICENSE.txt +15 -0
  8. data/README.md +351 -0
  9. data/Rakefile +53 -0
  10. data/bin/trocla +148 -0
  11. data/ext/redhat/rubygem-trocla.spec +120 -0
  12. data/lib/VERSION +4 -0
  13. data/lib/trocla.rb +162 -0
  14. data/lib/trocla/default_config.yaml +47 -0
  15. data/lib/trocla/encryptions.rb +54 -0
  16. data/lib/trocla/encryptions/none.rb +10 -0
  17. data/lib/trocla/encryptions/ssl.rb +51 -0
  18. data/lib/trocla/formats.rb +54 -0
  19. data/lib/trocla/formats/bcrypt.rb +7 -0
  20. data/lib/trocla/formats/md5crypt.rb +6 -0
  21. data/lib/trocla/formats/mysql.rb +6 -0
  22. data/lib/trocla/formats/pgsql.rb +7 -0
  23. data/lib/trocla/formats/plain.rb +7 -0
  24. data/lib/trocla/formats/sha1.rb +7 -0
  25. data/lib/trocla/formats/sha256crypt.rb +6 -0
  26. data/lib/trocla/formats/sha512crypt.rb +6 -0
  27. data/lib/trocla/formats/ssha.rb +9 -0
  28. data/lib/trocla/formats/sshkey.rb +46 -0
  29. data/lib/trocla/formats/x509.rb +197 -0
  30. data/lib/trocla/store.rb +80 -0
  31. data/lib/trocla/stores.rb +39 -0
  32. data/lib/trocla/stores/memory.rb +56 -0
  33. data/lib/trocla/stores/moneta.rb +58 -0
  34. data/lib/trocla/util.rb +71 -0
  35. data/lib/trocla/version.rb +22 -0
  36. data/spec/data/.keep +0 -0
  37. data/spec/spec_helper.rb +290 -0
  38. data/spec/trocla/encryptions/none_spec.rb +22 -0
  39. data/spec/trocla/encryptions/ssl_spec.rb +26 -0
  40. data/spec/trocla/formats/x509_spec.rb +375 -0
  41. data/spec/trocla/store/memory_spec.rb +6 -0
  42. data/spec/trocla/store/moneta_spec.rb +6 -0
  43. data/spec/trocla/util_spec.rb +54 -0
  44. data/spec/trocla_spec.rb +248 -0
  45. data/trocla-ruby2.gemspec +104 -0
  46. metadata +202 -0
@@ -0,0 +1,80 @@
1
+ # implements the default store behavior
2
+ class Trocla::Store
3
+ attr_reader :store_config, :trocla
4
+ def initialize(config,trocla)
5
+ @store_config = config
6
+ @trocla = trocla
7
+ end
8
+
9
+ # closes the store
10
+ # when called do whatever "closes" your
11
+ # store, e.g. close database connections.
12
+ def close
13
+ end
14
+
15
+ # should return value for key & format
16
+ # returns nil if nothing or a nil value
17
+ # was found.
18
+ # If a key is expired it must return nil.
19
+ def get(key,format)
20
+ raise 'not implemented'
21
+ end
22
+
23
+ # sets value for key & format
24
+ # setting the plain format must invalidate
25
+ # all other formats as they should either
26
+ # be derived from plain or set directly.
27
+ # options is a hash containing further
28
+ # information for the store. e.g. expiration
29
+ # of a key. Keys can have an expiration /
30
+ # timeout by setting `expires` within
31
+ # the options hashs. Value of `expires`
32
+ # must be an integer indicating the
33
+ # amount of seconds a key can live with.
34
+ # This mechanism is expected to be
35
+ # be implemented by the backend.
36
+ def set(key,format,value,options={})
37
+ if format == 'plain'
38
+ set_plain(key,value,options)
39
+ else
40
+ set_format(key,format,value,options)
41
+ end
42
+ end
43
+
44
+ # deletes the value for format
45
+ # if format is nil everything is deleted
46
+ # returns value of format or hash of
47
+ # format => value # if everything is
48
+ # deleted.
49
+ def delete(key,format=nil)
50
+ format.nil? ? (delete_all(key)||{}) : delete_format(key,format)
51
+ end
52
+
53
+ private
54
+ # sets a new plain value
55
+ # *must* invalidate all
56
+ # other formats
57
+ def set_plain(key,value,options)
58
+ raise 'not implemented'
59
+ end
60
+
61
+ # sets a value of a format
62
+ def set_format(key,format,value,options)
63
+ raise 'not implemented'
64
+ end
65
+
66
+ # deletes all entries of this key
67
+ # and returns a hash with all
68
+ # formats and values
69
+ # or nil if nothing is found
70
+ def delete_all(key)
71
+ raise 'not implemented'
72
+ end
73
+
74
+ # deletes the value of the passed
75
+ # key & format and returns the
76
+ # value.
77
+ def delete_format(key,format)
78
+ raise 'not implemented'
79
+ end
80
+ end
@@ -0,0 +1,39 @@
1
+ require 'trocla/store'
2
+ # store management
3
+ class Trocla::Stores
4
+ class << self
5
+ def [](store)
6
+ stores[store.to_s.downcase]
7
+ end
8
+
9
+ def all
10
+ @all ||= Dir[ path '*' ].collect do |store|
11
+ File.basename(store, '.rb').downcase
12
+ end
13
+ end
14
+
15
+ def available?(store)
16
+ all.include?(store.to_s.downcase)
17
+ end
18
+
19
+ private
20
+ def stores
21
+ @@stores ||= Hash.new do |hash, store|
22
+ store = store.to_s.downcase
23
+ if File.exists?(path(store))
24
+ require "trocla/stores/#{store}"
25
+ class_name = "Trocla::Stores::#{store.capitalize}"
26
+ hash[store] = (eval class_name)
27
+ else
28
+ raise "Store #{store} is not supported!"
29
+ end
30
+ end
31
+ end
32
+
33
+ def path(store)
34
+ File.expand_path(
35
+ File.join(File.dirname(__FILE__), 'stores', "#{store}.rb")
36
+ )
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,56 @@
1
+ # a simple in memory store just as an example
2
+ class Trocla::Stores::Memory < Trocla::Store
3
+ attr_reader :memory
4
+ def initialize(config,trocla)
5
+ super(config,trocla)
6
+ @memory = Hash.new({})
7
+ end
8
+
9
+ def get(key,format)
10
+ unless expired?(key)
11
+ memory[key][format]
12
+ else
13
+ delete_all(key)
14
+ nil
15
+ end
16
+ end
17
+ def set(key,format,value,options={})
18
+ super(key,format,value,options)
19
+ set_expires(key,options['expires'])
20
+ end
21
+
22
+ private
23
+ def set_plain(key,value,options)
24
+ memory[key] = { 'plain' => value }
25
+ end
26
+
27
+ def set_format(key,format,value,options)
28
+ memory[key].merge!({ format => value })
29
+ end
30
+
31
+ def delete_all(key)
32
+ memory.delete(key)
33
+ end
34
+ def delete_format(key,format)
35
+ old_val = (h = memory[key]).delete(format)
36
+ h.empty? ? memory.delete(key) : memory[key] = h
37
+ set_expires(key,nil)
38
+ old_val
39
+ end
40
+ private
41
+ def set_expires(key,expires)
42
+ expires = memory[key]['_expires'] if expires.nil?
43
+ if expires && expires > 0
44
+ memory[key]['_expires'] = expires
45
+ memory[key]['_expires_at'] = Time.now + expires
46
+ else
47
+ memory[key].delete('_expires')
48
+ memory[key].delete('_expires_at')
49
+ end
50
+ end
51
+ def expired?(key)
52
+ memory.key?(key) &&
53
+ (a = memory[key]['_expires_at']).is_a?(Time) && \
54
+ (a < Time.now)
55
+ end
56
+ end
@@ -0,0 +1,58 @@
1
+ # the default moneta based store
2
+ class Trocla::Stores::Moneta < Trocla::Store
3
+ attr_reader :moneta
4
+ def initialize(config,trocla)
5
+ super(config,trocla)
6
+ require 'moneta'
7
+ # load expire support by default
8
+ adapter_options = { :expires => true }.merge(
9
+ store_config['adapter_options']||{})
10
+ @moneta = Moneta.new(store_config['adapter'],adapter_options)
11
+ end
12
+
13
+ def close
14
+ moneta.close
15
+ end
16
+
17
+ def get(key,format)
18
+ moneta.fetch(key, {})[format]
19
+ end
20
+
21
+ private
22
+ def set_plain(key,value,options)
23
+ h = { 'plain' => value }
24
+ mo = moneta_options(key,options)
25
+ if options['expires'] && options['expires'] > 0
26
+ h['_expires'] = options['expires']
27
+ else
28
+ # be sure that we disable the existing
29
+ # expires if nothing is set.
30
+ mo[:expires] = false
31
+ end
32
+ moneta.store(key,h,mo)
33
+ end
34
+
35
+ def set_format(key,format,value,options)
36
+ moneta.store(key,
37
+ moneta.fetch(key,{}).merge({ format => value }),
38
+ moneta_options(key,options))
39
+ end
40
+
41
+ def delete_all(key)
42
+ moneta.delete(key)
43
+ end
44
+ def delete_format(key,format)
45
+ old_val = (h = moneta.fetch(key,{})).delete(format)
46
+ h.empty? ? moneta.delete(key) : moneta.store(key,h,moneta_options(key,{}))
47
+ old_val
48
+ end
49
+ def moneta_options(key,options)
50
+ res = {}
51
+ if options.key?('expires')
52
+ res[:expires] = options['expires']
53
+ elsif e = moneta.fetch(key, {})['_expires']
54
+ res[:expires] = e
55
+ end
56
+ res
57
+ end
58
+ end
@@ -0,0 +1,71 @@
1
+ require 'securerandom'
2
+ class Trocla
3
+ class Util
4
+ class << self
5
+ def random_str(length=12, charset='default')
6
+ _charsets = charsets[charset] || charsets['default']
7
+ (1..length).collect{|a| _charsets[SecureRandom.random_number(_charsets.size)] }.join.to_s
8
+ end
9
+
10
+ def salt(length=8)
11
+ alphanumeric_size = alphanumeric.size
12
+ (1..length).collect{|a| alphanumeric[SecureRandom.random_number(alphanumeric_size)] }.join.to_s
13
+ end
14
+
15
+ private
16
+
17
+ def charsets
18
+ @charsets ||= begin
19
+ h = {
20
+ 'default' => chars,
21
+ 'alphanumeric' => alphanumeric,
22
+ 'shellsafe' => shellsafe,
23
+ 'windowssafe' => windowssafe,
24
+ 'numeric' => numeric,
25
+ 'hexadecimal' => hexadecimal,
26
+ 'consolesafe' => consolesafe,
27
+ 'typesafe' => typesafe,
28
+ }
29
+ h.each { |k, v| h[k] = v.uniq }
30
+ end
31
+ end
32
+
33
+ def chars
34
+ @chars ||= shellsafe + special_chars
35
+ end
36
+ def shellsafe
37
+ @shellsafe ||= alphanumeric + shellsafe_chars
38
+ end
39
+ def windowssafe
40
+ @windowssafe ||= alphanumeric + windowssafe_chars
41
+ end
42
+ def consolesafe
43
+ @consolesafe ||= alphanumeric + consolesafe_chars
44
+ end
45
+ def hexadecimal
46
+ @hexadecimal ||= numeric + ('a'..'f').to_a
47
+ end
48
+ def alphanumeric
49
+ @alphanumeric ||= ('a'..'z').to_a + ('A'..'Z').to_a + numeric
50
+ end
51
+ def numeric
52
+ @numeric ||= ('0'..'9').to_a
53
+ end
54
+ def typesafe
55
+ @typesafe ||= ('a'..'x').to_a - ['i'] - ['l'] + ('A'..'X').to_a - ['I'] - ['L'] + ('1'..'9').to_a
56
+ end
57
+ def special_chars
58
+ @special_chars ||= "*()&![]{}-".split(//)
59
+ end
60
+ def shellsafe_chars
61
+ @shellsafe_chars ||= "+%/@=?_.,:".split(//)
62
+ end
63
+ def windowssafe_chars
64
+ @windowssafe_chars ||= "+%/@=?_.,".split(//)
65
+ end
66
+ def consolesafe_chars
67
+ @consolesafe_chars ||= '+.-,_'.split(//)
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,22 @@
1
+ # encoding: utf-8
2
+ class Trocla
3
+ class VERSION
4
+ version = {}
5
+ File.read(File.join(File.dirname(__FILE__), '../', 'VERSION')).each_line do |line|
6
+ type, value = line.chomp.split(":")
7
+ next if type =~ /^\s+$/ || value =~ /^\s+$/
8
+ version[type] = value
9
+ end
10
+
11
+ MAJOR = version['major']
12
+ MINOR = version['minor']
13
+ PATCH = version['patch']
14
+ BUILD = version['build']
15
+
16
+ STRING = [MAJOR, MINOR, PATCH, BUILD].compact.join('.')
17
+
18
+ def self.version
19
+ STRING
20
+ end
21
+ end
22
+ end
data/spec/data/.keep ADDED
File without changes
@@ -0,0 +1,290 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+ require 'rspec'
4
+ require 'rspec/pending_for'
5
+ require 'yaml'
6
+ require 'trocla'
7
+
8
+ # Requires supporting files with custom matchers and macros, etc,
9
+ # in ./support/ and its subdirectories.
10
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
11
+
12
+ RSpec.shared_examples "encryption_basics" do
13
+ describe 'storing' do
14
+ it "random passwords" do
15
+ expect(@trocla.password('random1', 'plain').length).to eql(16)
16
+ end
17
+
18
+ it "long random passwords" do
19
+ expect(@trocla.set_password('random1_long','plain',4096.times.collect{|s| 'x' }.join('')).length).to eql(4096)
20
+ end
21
+ end
22
+
23
+ describe 'retrieve' do
24
+ it "random passwords" do
25
+ stored = @trocla.password('random1', 'plain')
26
+ retrieved = @trocla.password('random1', 'plain')
27
+ retrieved_again = @trocla.password('random1', 'plain')
28
+ expect(retrieved).to eql(stored)
29
+ expect(retrieved_again).to eql(stored)
30
+ expect(retrieved_again).to eql(retrieved)
31
+ end
32
+
33
+ it "encrypted passwords" do
34
+ @trocla.set_password('some_pass', 'plain', 'super secret')
35
+ expect(@trocla.get_password('some_pass', 'plain')).to eql('super secret')
36
+ end
37
+
38
+ end
39
+ describe 'deleting' do
40
+ it "plain" do
41
+ @trocla.set_password('some_pass', 'plain', 'super secret')
42
+ expect(@trocla.delete_password('some_pass', 'plain')).to eql('super secret')
43
+ end
44
+ it "delete formats" do
45
+ plain = @trocla.password('some_mysqlpass', 'plain')
46
+ mysql = @trocla.password('some_mysqlpass', 'mysql')
47
+ expect(@trocla.delete_password('some_mysqlpass', 'mysql')).to eql(mysql)
48
+ expect(@trocla.delete_password('some_mysqlpass', 'plain')).to eql(plain)
49
+ expect(@trocla.get_password('some_mysqlpass','plain')).to be_nil
50
+ expect(@trocla.get_password('some_mysqlpass','mysql')).to be_nil
51
+ end
52
+
53
+ it "all passwords" do
54
+ plain = @trocla.password('some_mysqlpass', 'plain')
55
+ mysql = @trocla.password('some_mysqlpass', 'mysql')
56
+ deleted = @trocla.delete_password('some_mysqlpass')
57
+ expect(deleted).to be_a_kind_of(Hash)
58
+ expect(deleted['plain']).to eql(plain)
59
+ expect(deleted['mysql']).to eql(mysql)
60
+ end
61
+ end
62
+ end
63
+ RSpec.shared_examples "verify_encryption" do
64
+ it "does not store plaintext passwords" do
65
+ @trocla.set_password('noplain', 'plain', 'plaintext_password')
66
+ expect(File.readlines(trocla_yaml_file).grep(/plaintext_password/)).to be_empty
67
+ end
68
+
69
+ it "makes sure identical passwords do not match when stored" do
70
+ @trocla.set_password('one_key', 'plain', 'super secret')
71
+ @trocla.set_password('another_key', 'plain', 'super secret')
72
+ yaml = YAML.load_file(trocla_yaml_file)
73
+ expect(yaml['one_key']['plain']).not_to eq(yaml['another_key']['plain'])
74
+ end
75
+ end
76
+
77
+ RSpec.shared_examples 'store_validation' do |store|
78
+ describe '.get' do
79
+ it { expect(store.get('some_key','plain')).to be_nil }
80
+ end
81
+ describe '.set' do
82
+ it 'stores nil values' do
83
+ store.set('some_nil_value','plain',nil)
84
+ expect(store.get('some_nil_value','plain')).to be_nil
85
+ end
86
+ it 'stores plain format' do
87
+ store.set('some_value','plain','value')
88
+ expect(store.get('some_value','plain')).to eql('value')
89
+ end
90
+ it 'stores other formats' do
91
+ store.set('some_value','foo','bla')
92
+ expect(store.get('some_value','foo')).to eql('bla')
93
+ end
94
+ it 'resets other formats on setting plain' do
95
+ store.set('some_value','foo','bla')
96
+ store.set('some_value','plain','value')
97
+ expect(store.get('some_value','plain')).to eql('value')
98
+ expect(store.get('some_value','foo')).to be_nil
99
+ end
100
+ end
101
+ describe '.delete' do
102
+ it { expect(store.delete('something','foo')).to be_nil }
103
+ it { expect(store.delete('something')).to be_empty }
104
+ it 'deletes the value of a format' do
105
+ store.set('some_value','foo','bla')
106
+ expect(store.delete('some_value','foo')).to eql('bla')
107
+ expect(store.get('some_value','foo')).to be_nil
108
+ end
109
+ it 'deletes only the value of a format' do
110
+ store.set('some_value','plain','value')
111
+ store.set('some_value','foo','bla')
112
+ expect(store.delete('some_value','plain')).to eql('value')
113
+ expect(store.get('some_value','plain')).to be_nil
114
+ expect(store.get('some_value','foo')).to eql('bla')
115
+ end
116
+ it 'deletes all values without a format' do
117
+ store.set('some_value','plain','value')
118
+ store.set('some_value','foo','bla')
119
+ hash = store.delete('some_value')
120
+ expect(hash).to be_a_kind_of(Hash)
121
+ expect(hash['plain']).to eql('value')
122
+ expect(hash['foo']).to eql('bla')
123
+ expect(store.get('some_value','plain')).to be_nil
124
+ expect(store.get('some_value','foo')).to be_nil
125
+ end
126
+ end
127
+ describe 'expiration' do
128
+ it 'will not return an expired key' do
129
+ store.set('some_expiring_value','plain','to_be_expired',{ 'expires' => 2 })
130
+ expect(store.get('some_expiring_value','plain')).to eql('to_be_expired')
131
+ sleep 3
132
+ expect(store.get('some_expiring_value','plain')).to be_nil
133
+ end
134
+ it 'increases expiration when setting anything for that key' do
135
+ store.set('some_expiring_value','plain','to_be_expired',{ 'expires' => 2 })
136
+ expect(store.get('some_expiring_value','plain')).to eql('to_be_expired')
137
+ sleep 1
138
+ store.set('some_expiring_value','bla','bla_to_be_expired',{ 'expires' => 3 })
139
+ sleep 2
140
+ expect(store.get('some_expiring_value','plain')).to eql('to_be_expired')
141
+ sleep 2
142
+ expect(store.get('some_expiring_value','plain')).to be_nil
143
+ end
144
+ it 'keeps expiration when setting another value' do
145
+ store.set('some_expiring_value','plain','to_be_expired',{ 'expires' => 2 })
146
+ store.set('some_expiring_value','foo','to_be_expired_foo')
147
+ expect(store.get('some_expiring_value','plain')).to eql('to_be_expired')
148
+ sleep 3
149
+ expect(store.get('some_expiring_value','plain')).to be_nil
150
+ expect(store.get('some_expiring_value','foo')).to be_nil
151
+ end
152
+ it 'setting plain clears everything including expiration' do
153
+ store.set('some_expiring_value','plain','to_be_expired',{ 'expires' => 2 })
154
+ sleep 1
155
+ store.set('some_expiring_value','plain','to_be_expired2')
156
+ expect(store.get('some_expiring_value','plain')).to eql('to_be_expired2')
157
+ sleep 3
158
+ expect(store.get('some_expiring_value','plain')).to eql('to_be_expired2')
159
+ end
160
+ it 'extends expiration when setting another value' do
161
+ store.set('some_expiring_value','plain','to_be_expired',{ 'expires' => 4 })
162
+ sleep 2
163
+ store.set('some_expiring_value','foo','to_be_expired_foo')
164
+ expect(store.get('some_expiring_value','plain')).to eql('to_be_expired')
165
+ sleep 3
166
+ expect(store.get('some_expiring_value','plain')).to eql('to_be_expired')
167
+ sleep 2
168
+ expect(store.get('some_expiring_value','plain')).to be_nil
169
+ end
170
+ it 'extends expiration when deleting a format' do
171
+ store.set('some_expiring_value','plain','to_be_expired',{ 'expires' => 4 })
172
+ store.set('some_expiring_value','foo','to_be_expired2')
173
+ sleep 2
174
+ expect(store.delete('some_expiring_value','foo')).to eql('to_be_expired2')
175
+ sleep 3
176
+ expect(store.get('some_expiring_value','plain')).to eql('to_be_expired')
177
+ sleep 2
178
+ expect(store.get('some_expiring_value','plain')).to be_nil
179
+ end
180
+ it 'keeps expiration although we\'re fetching a value' do
181
+ store.set('some_expiring_value','plain','to_be_expired',{ 'expires' => 3 })
182
+ sleep 2
183
+ expect(store.get('some_expiring_value','plain')).to eql('to_be_expired')
184
+ sleep 2
185
+ expect(store.get('some_expiring_value','plain')).to be_nil
186
+ end
187
+ it 'readding a value with an expiration makes it expiring in the future' do
188
+ store.set('some_expiring_value','plain','to_be_expired')
189
+ store.set('some_expiring_value','plain','to_be_expired2',{ 'expires' => 2 })
190
+ expect(store.get('some_expiring_value','plain')).to eql('to_be_expired2')
191
+ sleep 3
192
+ expect(store.get('some_expiring_value','plain')).to be_nil
193
+ end
194
+ it 'setting an expires of false removes expiration' do
195
+ store.set('some_expiring_value','plain','to_be_expired2',{ 'expires' => 2 })
196
+ expect(store.get('some_expiring_value','plain')).to eql('to_be_expired2')
197
+ store.set('some_expiring_value','plain','to_be_expired',{ 'expires' => false })
198
+ sleep 3
199
+ expect(store.get('some_expiring_value','plain')).to eql('to_be_expired')
200
+ end
201
+ it 'setting an expires of 0 removes expiration' do
202
+ store.set('some_expiring_value','plain','to_be_expired2',{ 'expires' => 2 })
203
+ expect(store.get('some_expiring_value','plain')).to eql('to_be_expired2')
204
+ store.set('some_expiring_value','plain','to_be_expired',{ 'expires' => 0 })
205
+ sleep 3
206
+ expect(store.get('some_expiring_value','plain')).to eql('to_be_expired')
207
+ end
208
+ it 'setting an expires of false removes expiration even if it\'s for a different format' do
209
+ store.set('some_expiring_value','plain','to_be_expired2',{ 'expires' => 2 })
210
+ expect(store.get('some_expiring_value','plain')).to eql('to_be_expired2')
211
+ store.set('some_expiring_value','foo','to_be_expired_foo',{ 'expires' => false })
212
+ sleep 3
213
+ expect(store.get('some_expiring_value','plain')).to eql('to_be_expired2')
214
+ expect(store.get('some_expiring_value','foo')).to eql('to_be_expired_foo')
215
+ end
216
+ it 'setting an expires of 0 removes expiration even if it\'s for a different format' do
217
+ store.set('some_expiring_value','plain','to_be_expired2',{ 'expires' => 2 })
218
+ expect(store.get('some_expiring_value','plain')).to eql('to_be_expired2')
219
+ store.set('some_expiring_value','foo','to_be_expired_foo',{ 'expires' => 0 })
220
+ sleep 3
221
+ expect(store.get('some_expiring_value','plain')).to eql('to_be_expired2')
222
+ expect(store.get('some_expiring_value','foo')).to eql('to_be_expired_foo')
223
+ end
224
+ end
225
+ end
226
+
227
+ def default_config
228
+ @default_config ||= YAML.load(File.read(File.expand_path(base_dir+'/lib/trocla/default_config.yaml')))
229
+ end
230
+
231
+ def test_config
232
+ @config ||= default_config.merge({
233
+ 'store' => :memory,
234
+ })
235
+ end
236
+
237
+ def test_config_persistent
238
+ @config ||= default_config.merge({
239
+ 'store_options' => {
240
+ 'adapter' => :YAML,
241
+ 'adapter_options' => {
242
+ :file => trocla_yaml_file
243
+ },
244
+ },
245
+ })
246
+ end
247
+
248
+ def ssl_test_config
249
+ @ssl_config ||= test_config_persistent.merge({
250
+ 'encryption' => :ssl,
251
+ 'encryption_options' => {
252
+ :private_key => data_dir('trocla.key'),
253
+ :public_key => data_dir('trocla.pub'),
254
+ },
255
+ })
256
+ end
257
+
258
+ def base_dir
259
+ File.dirname(__FILE__)+'/../'
260
+ end
261
+
262
+ def data_dir(file = nil)
263
+ File.expand_path(File.join(base_dir, 'spec/data', file))
264
+ end
265
+
266
+ def trocla_yaml_file
267
+ data_dir('trocla_store.yaml')
268
+ end
269
+
270
+ def generate_ssl_keys
271
+ require 'openssl'
272
+ rsa_key = OpenSSL::PKey::RSA.new(4096)
273
+ File.open(data_dir('trocla.key'), 'w') { |f| f.write(rsa_key.to_pem) }
274
+ File.open(data_dir('trocla.pub'), 'w') { |f| f.write(rsa_key.public_key.to_pem) }
275
+ end
276
+
277
+ def remove_ssl_keys
278
+ File.unlink(data_dir('trocla.key'))
279
+ File.unlink(data_dir('trocla.pub'))
280
+ end
281
+
282
+ def remove_yaml_store
283
+ File.unlink(trocla_yaml_file)
284
+ end
285
+ class Trocla::Formats::Sleep < Trocla::Formats::Base
286
+ def format(plain_password,options={})
287
+ sleep options['sleep'] ||= 0
288
+ (options['sleep'] + 1 ).times.collect{ plain_password }.join(' ')
289
+ end
290
+ end