trocla-ruby2 0.4.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.
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