safedb 0.01.0001

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 (90) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/.yardopts +3 -0
  4. data/Gemfile +10 -0
  5. data/LICENSE +21 -0
  6. data/README.md +793 -0
  7. data/Rakefile +16 -0
  8. data/bin/safe +5 -0
  9. data/lib/configs/README.md +58 -0
  10. data/lib/extension/array.rb +162 -0
  11. data/lib/extension/dir.rb +35 -0
  12. data/lib/extension/file.rb +123 -0
  13. data/lib/extension/hash.rb +33 -0
  14. data/lib/extension/string.rb +572 -0
  15. data/lib/factbase/facts.safedb.net.ini +38 -0
  16. data/lib/interprete.rb +462 -0
  17. data/lib/keytools/PRODUCE_RAND_SEQ_USING_DEV_URANDOM.txt +0 -0
  18. data/lib/keytools/kdf.api.rb +243 -0
  19. data/lib/keytools/kdf.bcrypt.rb +265 -0
  20. data/lib/keytools/kdf.pbkdf2.rb +262 -0
  21. data/lib/keytools/kdf.scrypt.rb +190 -0
  22. data/lib/keytools/key.64.rb +326 -0
  23. data/lib/keytools/key.algo.rb +109 -0
  24. data/lib/keytools/key.api.rb +1391 -0
  25. data/lib/keytools/key.db.rb +330 -0
  26. data/lib/keytools/key.docs.rb +195 -0
  27. data/lib/keytools/key.error.rb +110 -0
  28. data/lib/keytools/key.id.rb +271 -0
  29. data/lib/keytools/key.ident.rb +243 -0
  30. data/lib/keytools/key.iv.rb +107 -0
  31. data/lib/keytools/key.local.rb +259 -0
  32. data/lib/keytools/key.now.rb +402 -0
  33. data/lib/keytools/key.pair.rb +259 -0
  34. data/lib/keytools/key.pass.rb +120 -0
  35. data/lib/keytools/key.rb +585 -0
  36. data/lib/logging/gem.logging.rb +132 -0
  37. data/lib/modules/README.md +43 -0
  38. data/lib/modules/cryptology/aes-256.rb +154 -0
  39. data/lib/modules/cryptology/amalgam.rb +70 -0
  40. data/lib/modules/cryptology/blowfish.rb +130 -0
  41. data/lib/modules/cryptology/cipher.rb +207 -0
  42. data/lib/modules/cryptology/collect.rb +138 -0
  43. data/lib/modules/cryptology/crypt.io.rb +225 -0
  44. data/lib/modules/cryptology/engineer.rb +99 -0
  45. data/lib/modules/mappers/dictionary.rb +288 -0
  46. data/lib/modules/storage/coldstore.rb +186 -0
  47. data/lib/modules/storage/git.store.rb +399 -0
  48. data/lib/session/fact.finder.rb +334 -0
  49. data/lib/session/require.gem.rb +112 -0
  50. data/lib/session/time.stamp.rb +340 -0
  51. data/lib/session/user.home.rb +49 -0
  52. data/lib/usecase/cmd.rb +487 -0
  53. data/lib/usecase/config/README.md +57 -0
  54. data/lib/usecase/docker/README.md +146 -0
  55. data/lib/usecase/docker/docker.rb +49 -0
  56. data/lib/usecase/edit/README.md +43 -0
  57. data/lib/usecase/edit/delete.rb +46 -0
  58. data/lib/usecase/export.rb +40 -0
  59. data/lib/usecase/files/README.md +37 -0
  60. data/lib/usecase/files/eject.rb +56 -0
  61. data/lib/usecase/files/file_me.rb +78 -0
  62. data/lib/usecase/files/read.rb +169 -0
  63. data/lib/usecase/files/write.rb +89 -0
  64. data/lib/usecase/goto.rb +57 -0
  65. data/lib/usecase/id.rb +36 -0
  66. data/lib/usecase/import.rb +157 -0
  67. data/lib/usecase/init.rb +63 -0
  68. data/lib/usecase/jenkins/README.md +146 -0
  69. data/lib/usecase/jenkins/jenkins.rb +208 -0
  70. data/lib/usecase/login.rb +71 -0
  71. data/lib/usecase/logout.rb +28 -0
  72. data/lib/usecase/open.rb +71 -0
  73. data/lib/usecase/print.rb +40 -0
  74. data/lib/usecase/put.rb +81 -0
  75. data/lib/usecase/set.rb +44 -0
  76. data/lib/usecase/show.rb +138 -0
  77. data/lib/usecase/terraform/README.md +91 -0
  78. data/lib/usecase/terraform/terraform.rb +121 -0
  79. data/lib/usecase/token.rb +35 -0
  80. data/lib/usecase/update/README.md +55 -0
  81. data/lib/usecase/update/rename.rb +180 -0
  82. data/lib/usecase/use.rb +41 -0
  83. data/lib/usecase/verse.rb +20 -0
  84. data/lib/usecase/view.rb +71 -0
  85. data/lib/usecase/vpn/README.md +150 -0
  86. data/lib/usecase/vpn/vpn.ini +31 -0
  87. data/lib/usecase/vpn/vpn.rb +54 -0
  88. data/lib/version.rb +3 -0
  89. data/safedb.gemspec +34 -0
  90. metadata +193 -0
@@ -0,0 +1,330 @@
1
+ #!/usr/bin/ruby
2
+ # coding: utf-8
3
+
4
+ module SafeDb
5
+
6
+ require 'json'
7
+
8
+ # A Key/Value database knows how to manipulate a JSON backed data structure
9
+ # (put, add etc) <b>after reading and then decrypting it</b> from a
10
+ # file and <b>before encrypting and then writing it</b> to a file.
11
+ #
12
+ # It provides behaviour to which we can create, append (add), update
13
+ # (change), read parts and delete essentially two structures
14
+ #
15
+ # - a collection of name/value pairs
16
+ # - an ordered list of values
17
+ #
18
+ # == JSON is Not Exposed in the Interface
19
+ #
20
+ # A key/value database doesn't expose the data format used in the implementation
21
+ # allowing this to be changed seamlessly to YAMl or other formats.
22
+ #
23
+ # == Symmetric Encryption and Decryption
24
+ #
25
+ # A key/value database supports operations to <b>read from</b> and <b>write to</b>
26
+ # a known filepath and with a symmetric key it can
27
+ #
28
+ # - decrypt <b>after reading from</b> a file and
29
+ # - encrypt <b>before writing to</b> a (the same) file
30
+ #
31
+ # == Hashes as the Primary Data Structure
32
+ #
33
+ # The key/value database openly extends {Hash} as the data structure for holding
34
+ #
35
+ # - strings
36
+ # - arrays
37
+ # - other hashes
38
+ # - booleans
39
+ # - integers and floats
40
+ class KeyDb < Hash
41
+
42
+ # Return a key database data structure that is instantiated from
43
+ # the parameter JSON string.
44
+ #
45
+ # @param db_json_string [String]
46
+ # this json formatted data structure will be converted into a
47
+ # a Ruby hash (map) data structure and returned.
48
+ #
49
+ # @return [KeyDb]
50
+ # a hash data structure that has been instantiated as per the
51
+ # parameter json string content.
52
+ def self.from_json( db_json_string )
53
+
54
+ data_db = KeyDb.new()
55
+ data_db.merge!( JSON.parse( db_json_string ) )
56
+ return data_db
57
+
58
+ end
59
+
60
+
61
+
62
+ # Create a new key value entry inside a dictionary with the specified
63
+ # name at the root of this database. Successful completion means the
64
+ # named dictionary will contain one more entry than it need even if it
65
+ # did not previously exist.
66
+ #
67
+ # @param dictionary_name [String]
68
+ #
69
+ # if a dictionary with this name exists at the root of the
70
+ # database add the parameter key value pair into it.
71
+ #
72
+ # if no dictionary exists then create one first before adding
73
+ # the key value pair as the first entry into it.
74
+ #
75
+ # @param key_name [String]
76
+ #
77
+ # the key part of the key value pair that will be added into the
78
+ # dictionary whose name was provided in the first parameter.
79
+ #
80
+ # @param value [String]
81
+ #
82
+ # the value part of the key value pair that will be added into the
83
+ # dictionary whose name was provided in the first parameter.
84
+ def create_entry( dictionary_name, key_name, value )
85
+
86
+ KeyError.not_new( dictionary_name, self )
87
+ KeyError.not_new( key_name, self )
88
+ KeyError.not_new( value, self )
89
+
90
+ self[ dictionary_name ] = {} unless self.has_key?( dictionary_name )
91
+ self[ dictionary_name ][ key_name ] = value
92
+
93
+ end
94
+
95
+
96
+ # Create a new secondary tier map key value entry inside a primary tier
97
+ # map at the map_key_name location.
98
+ #
99
+ # A failure will occur if either the outer or inner keys already exist
100
+ # without their values being map objects.
101
+ #
102
+ # If this method is called against a new empty map, the resulting map
103
+ # structure will look like the below.
104
+ #
105
+ # { outer_keyname ~> { inner_keyname ~> { entry_keyname, entry_value } } }
106
+ #
107
+ # @param outer_keyname [String]
108
+ #
109
+ # if a dictionary with this name exists at the root of the
110
+ # database add the parameter key value pair into it.
111
+ #
112
+ # if no dictionary exists then create one first before adding
113
+ # the key value pair as the first entry into it.
114
+ #
115
+ # @param inner_keyname [String]
116
+ #
117
+ # if a map exists at this key name then an entry comprising of
118
+ # a map_entry_key and a entry_value may either be added
119
+ # (if the map_entry_key does not already exist), or updated if
120
+ # it does.
121
+ #
122
+ # if the map does not exist it will be created and its first and
123
+ # only entry will be a key with inner_keyname along with a new
124
+ # single entry map consisting of the entry_keyname and the
125
+ # entry_value.
126
+ #
127
+ # @param entry_keyname [String]
128
+ #
129
+ # this key will exist in the second tier map after this operation.
130
+ #
131
+ # @param entry_value [String]
132
+ #
133
+ # this value will exist in the second tier map after this operation
134
+ # and if the entry_keyname already existed its value is overwritten
135
+ # with this one.
136
+ #
137
+ def create_map_entry( outer_keyname, inner_keyname, entry_keyname, entry_value )
138
+
139
+ KeyError.not_new( outer_keyname, self )
140
+ KeyError.not_new( inner_keyname, self )
141
+ KeyError.not_new( entry_keyname, self )
142
+ KeyError.not_new( entry_value, self )
143
+
144
+ self[ outer_keyname ] = {} unless self.has_key?( outer_keyname )
145
+ self[ outer_keyname ][ inner_keyname ] = {} unless self[ outer_keyname ].has_key?( inner_keyname )
146
+ self[ outer_keyname ][ inner_keyname ][ entry_keyname ] = entry_value
147
+
148
+ end
149
+
150
+
151
+ # Does this database have an entry in the root dictionary named with
152
+ # the key_name parameter?
153
+ #
154
+ # @param dictionary_name [String]
155
+ #
156
+ # immediately return false if a dictionary with this name does
157
+ # <b>not exist</b> at the root of this database.
158
+ #
159
+ # @param key_name [String]
160
+ #
161
+ # test whether a key/value pair answering to this name exists inside
162
+ # the specified dictionary at the root of this database.
163
+ #
164
+ def has_entry?( dictionary_name, key_name )
165
+
166
+ KeyError.not_new( dictionary_name, self )
167
+ KeyError.not_new( key_name, self )
168
+
169
+ return false unless self.has_key?( dictionary_name )
170
+ return self[ dictionary_name ].has_key?( key_name )
171
+
172
+ end
173
+
174
+
175
+ # Get the entry with the key name in a dictionary that is itself
176
+ # inside another dictionary (named in the first parameter) which
177
+ # thankfully is at the root of this database.
178
+ #
179
+ # Only call this method if {has_entry?} returns true for the same
180
+ # dictionary and key name parameters.
181
+ #
182
+ # @param dictionary_name [String]
183
+ #
184
+ # get the entry inside a dictionary which is itself inside a
185
+ # dictionary (with this dictionary name) which is itself at the
186
+ # root of this database.
187
+ #
188
+ # @param key_name [String]
189
+ #
190
+ # get the value part of the key value pair that is inside a
191
+ # dictionary (with the above dictionary name) which is itself
192
+ # at the root of this database.
193
+ #
194
+ def get_entry( dictionary_name, key_name )
195
+
196
+ return self[ dictionary_name ][ key_name ]
197
+
198
+ end
199
+
200
+
201
+ # Delete an existing key value entry inside the dictionary with the specified
202
+ # name at the root of this database. Successful completion means the
203
+ # named dictionary will contain one less entry if that key existed.
204
+ #
205
+ # @param dictionary_name [String]
206
+ #
207
+ # if a dictionary with this name exists at the root of the
208
+ # database add the parameter key value pair into it.
209
+ #
210
+ # if no dictionary exists throw an error
211
+ #
212
+ # @param key_name [String]
213
+ #
214
+ # the key part of the key value pair that will be deleted in the
215
+ # dictionary whose name was provided in the first parameter.
216
+ def delete_entry( dictionary_name, key_name )
217
+
218
+ KeyError.not_new( dictionary_name, self )
219
+ KeyError.not_new( key_name, self )
220
+
221
+ self[ dictionary_name ].delete( key_name )
222
+
223
+ end
224
+
225
+
226
+ # Read and inject into this envelope, the data structure found in a
227
+ # file at the path specified in the first parameter.
228
+ #
229
+ # Symmetric cryptography is mandatory for the envelope so we must
230
+ # <b>encrypt before writing</b> and <b>decrypt after reading</b>.
231
+ #
232
+ # An argument error will result if a suitable key is not provided.
233
+ #
234
+ # If the file does not exist (denoting the first read) all this method
235
+ # does is to stash the filepath as an instance variable and igore the
236
+ # decryption key which can be nil (or ommitted).
237
+ #
238
+ # @param the_filepath [String]
239
+ # absolute path to the file which acts as the persistent mirror to
240
+ # this data structure envelope.
241
+ #
242
+ # @param decryption_key [String]
243
+ # encryption at rest is a given so this mandatory parameter must
244
+ # contain a robust symmetric decryption key. The key will be used
245
+ # for decryption after the read and it will not linger (ie not cached
246
+ # as an instance variable).
247
+ #
248
+ # @raise [ArgumentError] if the decryption key is not robust enough.
249
+ def read the_filepath, decryption_key = nil
250
+
251
+ # @todo -> this is confused - it uses INI but above methods use JSON
252
+ # @todo -> this is confused - it uses INI but above methods use JSON
253
+ # @todo -> this is confused - it uses INI but above methods use JSON
254
+ # @todo -> this is confused - it uses INI but above methods use JSON
255
+ # @todo -> this is confused - it uses INI but above methods use JSON
256
+ # @todo -> this is confused - it uses INI but above methods use JSON
257
+ # @todo -> this is confused - it uses INI but above methods use JSON
258
+ # @todo -> this is confused - it uses INI but above methods use JSON
259
+ # @todo -> this is confused - it uses INI but above methods use JSON
260
+ # @todo -> this is confused - it uses INI but above methods use JSON
261
+
262
+ raise RuntimeError, "This KeyDb.read() software is never called so how can I be here?"
263
+
264
+ @filepath = the_filepath
265
+ return unless File.exists? @filepath
266
+
267
+ cipher_text = Base64.decode64( File.read( @filepath ).strip )
268
+ plain_text = ToolBelt::Blowfish.decryptor( cipher_text, decryption_key )
269
+
270
+ data_structure = JSON.parse plain_text
271
+ self.merge! data_structure
272
+
273
+ end
274
+
275
+
276
+ # Write the data in this envelope hash map into a file-system
277
+ # backed mirror whose path was specified in the {self.read} method.
278
+ #
279
+ # Technology for encryption at rest is supported by this dictionary
280
+ # and to this aim, please endeavour to post a robust symmetric
281
+ # encryption key.
282
+ #
283
+ # Calling this {self.write} method when the file at the prescribed path
284
+ # does not exist results in the directory structure being created
285
+ # (if necessary) and then the encrypted file being written.
286
+ #
287
+ # @param encryption_key [String]
288
+ # encryption at rest is a given so this mandatory parameter must
289
+ # contain a robust symmetric encryption key. The symmetric key will
290
+ # be used for the decryption after the read. Note that the decryption
291
+ # key does not linger meaning it isn't cached in an instance variable.
292
+ def write encryption_key
293
+
294
+ # @todo -> this is confused - it uses INI but above methods use JSON
295
+ # @todo -> this is confused - it uses INI but above methods use JSON
296
+ # @todo -> this is confused - it uses INI but above methods use JSON
297
+ # @todo -> this is confused - it uses INI but above methods use JSON
298
+ # @todo -> this is confused - it uses INI but above methods use JSON
299
+ # @todo -> this is confused - it uses INI but above methods use JSON
300
+ # @todo -> this is confused - it uses INI but above methods use JSON
301
+ # @todo -> this is confused - it uses INI but above methods use JSON
302
+ # @todo -> this is confused - it uses INI but above methods use JSON
303
+
304
+ raise RuntimeError, "This KeyDb.write( key ) software is never called so how can I be here?"
305
+
306
+ FileUtils.mkdir_p(File.dirname(@filepath))
307
+ cipher_text = Base64.encode64 ToolBelt::Blowfish.encryptor( self.to_json, encryption_key )
308
+ File.write @filepath, cipher_text
309
+
310
+ puts ""
311
+ puts "=== ============================"
312
+ puts "=== Envelope State ============="
313
+ puts "=== ============================"
314
+
315
+ a_ini_file = IniFile.new
316
+ self.each_key do |section_name|
317
+ a_ini_file[section_name] = self[section_name]
318
+ end
319
+ puts a_ini_file.to_s
320
+
321
+ puts "=== ============================"
322
+ puts ""
323
+
324
+ end
325
+
326
+
327
+ end
328
+
329
+
330
+ end
@@ -0,0 +1,195 @@
1
+ #!/usr/bin/ruby
2
+
3
+ # The open key library generates keys, it stores their salts, it produces differing
4
+ # representations of the keys (like base64 for storage and binary for encrypting).
5
+ #
6
+ # == Key Class Names their and Responsibility
7
+ #
8
+ # The 5 core key classes in the open key library are
9
+ #
10
+ # - {Key} represents keys in bits, binary and base 64 formats
11
+ # - {Key64} for converting from to and between base 64 characters
12
+ # - {Key256} uses key derivation functions to produce high entropy keys
13
+ # - {KeyIO} reads and writes key metadata (like salts) from/to persistent storage
14
+ # - {KeyCycle} for creating and locking the keys that underpin the security
15
+ #
16
+ # == The 5 Core Key Classes
17
+ #
18
+ # Key To initialize with a 264 binary bit string. To hold the
19
+ # key and represent it when requested
20
+ # - as a 264 bit binary bit string
21
+ # - as a 256 bit binary bit string
22
+ # - as a 256 bit raw bytes encryption key
23
+ # - as a YACHT64 formatted string
24
+ #
25
+ # Key64 To map in and out of the Yacht64 character set - from and to
26
+ # - a binary bit string sequence
27
+ # - a Base64 character encoding
28
+ # - a UrlSafe Base64 character encoding
29
+ # - a Radix64 character encoding
30
+ #
31
+ # Key256 It generates a key in 3 different and important ways. It can
32
+ # generate
33
+ #
34
+ # (a) from_password
35
+ # (b) from_random (or it can)
36
+ # (c) regenerate
37
+ #
38
+ # When generating from a password it takes a dictionary with
39
+ # a pre-tailored "section" and writes BCrypt and Pbkdf2 salts
40
+ # into it.
41
+ #
42
+ # When generating random it kicks of by creating a 55 byte
43
+ # random key fo BCrypt and a 64 byte random key for Pbkdf2.
44
+ # It then calls upon generate_from_password.
45
+ #
46
+ # When regenerating it queries the dictionary provided at the
47
+ # pre-tailored "section" for the BCrypt and Pbkdf2 salts and
48
+ # then uses input passwords (be they human randomly sourced)
49
+ # and regenerates the keys it produced at an earlier sitting.
50
+ #
51
+ # KeyIO KeyIO is instantiated with a folder path and a "key reference".
52
+ # KeyIO will then manage writing to and rereading from the structure
53
+ # hel inside th efile. The file is named (primarily) by the
54
+ # reference string.
55
+ #
56
+ # KeyCycle KeyLifeCycle implements the merry go round that palms off
57
+ # responsibility to the intra-session cycle and then back again
58
+ # to ever rotary inter-session(ary) cycle.
59
+ ########### Maybe think of a method where we pass in
60
+ ########### 2 secrets - 1 human and 1 55 random bytes (session)
61
+ ###########
62
+ ########### 1 another 55 random key is created (the actual encryption key)
63
+ ########### 2 then the above key is encrypted TWICE (2 diff salts and keys)
64
+ ########### 3 Once by key from human password
65
+ ########### 4 Once by key from machine password
66
+ ########### 5 then the key from 1 is returned
67
+ ########### 6 caller encrypts file .................... (go 4 it)
68
+
69
+
70
+ # Generates a 256 bit symmetric encryption key derived from a random
71
+ # seed sequence of 55 bytes. These 55 bytes are then fed into the
72
+ # {from_password} key derivation function and processed in a similar
73
+ # way to if a human had generated the string.
74
+ #
75
+
76
+
77
+ # <b>Key derivation functions</b> exist to convert <b>low entropy</b> human
78
+ # created passwords into a high entropy key that is computationally difficult
79
+ # to acquire through brute force.
80
+ #
81
+ # == SafeDb's Underlying Security Strategy
82
+ #
83
+ # <b>Randomly generate a 256 bit encryption key and encrypt it</b> with a key
84
+ # derived from a human password and generated by at least two cryptographic
85
+ # workhorses known as <b>key derivation functions</b>.
86
+ #
87
+ # The encryption key (encrypted by the one derived from a human password) sits
88
+ # at the beginning of a long chain of keys and encryption - so much so that the
89
+ # crypt material being outputted for storage is all but worthless to anyone but
90
+ # its rightful owner.
91
+ #
92
+ # == Key Size vs Crack Time
93
+ #
94
+ # Cracking a 256 bit key would need roughly 2^255 iterations (half the space)
95
+ # and this is akin to the number of atoms in the known universe.
96
+ #
97
+ # <b>The human key can put security at risk.</b>
98
+ #
99
+ # The rule of thumb is that a 40 character password with a good spread of the
100
+ # roughly 90 typable characters, would produce security equivalent to that of
101
+ # an AES 256bit key. As the password size and entropy drop, so does the security,
102
+ # exponentially.
103
+ #
104
+ # As human generated passwords have a relatively small key space, key derivation
105
+ # functions must be slow to compute with any implementation.
106
+ #
107
+ # == Key Derivation Functions for Command Line Apps
108
+ #
109
+ # A command line app (with no recourse to a central server) uses a Key
110
+ # Derivation Function (like BCrypt, Aaron2 or PBKD2) in a manner different
111
+ # to that employed by server side software.
112
+ #
113
+ # - server side passwords are hashed then both salt and hash are persisted
114
+ # - command line apps do not store the key - they only store the salt
115
+ # - both throw away the original password
116
+ #
117
+ # == One Key | One Session | One Crypt
118
+ #
119
+ # Command line apps use the derived key to <b>symmetrically encrypt and decrypt</b>
120
+ # one and only one 48 character key and a new key is derived at the beginning
121
+ # of every session.
122
+ #
123
+ # At the end of the session <b>all material encrypted by the outgoing key</b>
124
+ # is removed. This aggressive key rotation strategy leaves no stone unturned in
125
+ # the quest for ultimate security.
126
+ #
127
+ # == SafeDb's CLI Key Derivation Architecture
128
+ #
129
+ # SafeDb never accesses another server and giving its users total control
130
+ # of their secret crypted materials. It strengthens the key derivation process
131
+ # in three important ways.
132
+ #
133
+ # - [1] it does not store the key nor does it store the password
134
+ #
135
+ # - [2] a new master key is generated for every session only to hold the master index file
136
+ #
137
+ # - [3] it uses both <b>BCrypt</b> (Blowfish Crypt) and the indefatigable <b>PBKD2</b>
138
+
139
+
140
+ # After a successful initialization, the application instance is linked to a keystore
141
+ # whose contents are responsible for securing the application instance database.
142
+ #
143
+ # To ascertain what needs to be done to bridge the gap to full initialization the
144
+ # app needs to know 3 things from the KeyApi. These things are
145
+ #
146
+ # - the ID of this app instance on the machine
147
+ # - if a keystore been associated with this ID
148
+ # - whether the keystore secures the app database
149
+ #
150
+ # The answers dictate the steps that need to be undertaken to bring the database of
151
+ # the application instance under the secure wing of the KeyApi.
152
+ #
153
+ #
154
+ # == 1. What is the App Instance ID on this Machine?
155
+ #
156
+ # The KeyApi uses the "just given" application reference and the machine environment to
157
+ # respond with a <b>digested identifier</b> binding the application instance to the
158
+ # present machine (workstation).
159
+ #
160
+ #
161
+ # == 2. Has a Keystore been associated with this ID?
162
+ #
163
+ # The application's configuration manager is asked to find an associated KeyStore ID
164
+ # mapped against the app/machine id garnered by question 1.
165
+ #
166
+ # <b>No it has not!</b>
167
+ #
168
+ # If <b>NO</b> then a KeyStore ID is acquired <b>either from the init command's parameter</b>,
169
+ # or a <b>suitable default</b>. This new association between the app/machine ID and the
170
+ # KeyStore ID is then stored so the answer next time will be <b>YES</b>.
171
+ #
172
+ # <b>Yes it has!</b>
173
+ #
174
+ # Great - we now submit the KeyStore ID to the KeyApi so that it may answer question 3.
175
+ #
176
+ #
177
+ # == 3. Does the keystore secure the app instance database?
178
+ #
179
+ # For the KeyApi to answer, it needs the App's Instance ID and the KeyStore ID.
180
+ #
181
+ # <b>Not Yet!</b> Now <b>NO</b> means this application instance's database has not been
182
+ # brought under the protection of the KeyApi's multi-layered security net. For this it
183
+ # needs
184
+ #
185
+ # - the KeyStore ID
186
+ # - the application instance reference
187
+ # - the plaintext secret from which nothing of the host survives
188
+ # - the current application database plaintext
189
+ #
190
+ # <b>Yes it does!</b> If the app db keys <b>have been instantiated</b> and the client app is
191
+ # <b>sitting pretty</b> in possession of the database ciphertext, no more needs doing.
192
+
193
+ module SafeDb
194
+
195
+ end
@@ -0,0 +1,110 @@
1
+ #!/usr/bin/ruby
2
+
3
+ module SafeDb
4
+
5
+
6
+ # This class is the parent to all opensession errors
7
+ # that originate from the command line.
8
+ #
9
+ # All opensession cli originating errors are about
10
+ #
11
+ # - a problem with the input or
12
+ # - a problem with the current state or
13
+ # - a predictable future problem
14
+ class KeyError < StandardError
15
+
16
+
17
+ # Initialize the error and provide a culprit
18
+ # object which will be to-stringed and given
19
+ # out as evidence (look at this)!
20
+ #
21
+ # This method will take care of loggin the error.
22
+ #
23
+ # @param message [String] human readable error message
24
+ # @param culprit [Object] object that is either pertinent, a culprit or culpable
25
+ def initialize message, culprit
26
+
27
+ super(message)
28
+
29
+ @the_culprit = culprit
30
+
31
+ log.info(x) { "An [Error] Occured => #{message}" }
32
+ log.info(x) { "Object of Interest => #{culprit.to_s}" } unless culprit.nil?
33
+ log.info(x) { "Class Name Culprit => #{culprit.class.name}" }
34
+ log.info(x) { "Error Message From => #{self.class.name}" }
35
+
36
+ thread_backtrace = Thread.current.backtrace.join("\n")
37
+ thread_backtrace.to_s.log_lines
38
+
39
+ end
40
+
41
+
42
+ # This method gives interested parties the object that
43
+ # is at the centre of the exception. This object is either
44
+ # very pertinent, culpable or at the very least, interesting.
45
+ #
46
+ # @return [String] string representation of culpable object
47
+ def culprit
48
+ return "No culprit identified." if @the_culprit.nil?
49
+ return @the_culprit.to_s
50
+ end
51
+
52
+
53
+ # Assert that the parameter string attribute is <b>not new</b> which
54
+ # means neither nil, nor empty nor consists solely of whitespace.
55
+ #
56
+ # The <b>NEW</b> acronym tells us that a bearer worthy of the name is
57
+ #
58
+ # - neither <b>N</b>il
59
+ # - nor <b>E</b>mpty
60
+ # - nor consists solely of <b>W</b>hitespace
61
+ #
62
+ # @param the_attribute [String]
63
+ # raise a {KeyError} if the attribute is not new.
64
+ #
65
+ # @param the_desc [String]
66
+ # a description of th attribute
67
+ #
68
+ # @raise [KeyError]
69
+ #
70
+ # The attribute cannot be <b>NEW</b>. The <b>NEW acronym</b> asserts
71
+ # that the attribute is
72
+ #
73
+ # - neither <b>N</b>il
74
+ # - nor <b>E</b>mpty
75
+ # - nor <b>W</b>hitespace only
76
+ #
77
+ def self.not_new the_attribute, the_desc
78
+
79
+ attribute_new = the_attribute.nil? || the_attribute.chomp.strip.empty?
80
+ return unless attribute_new
81
+
82
+ msg = "[the_desc] is either nil, empty or consists solely of whitespace."
83
+ raise KeyError.new( msg, the_desc )
84
+
85
+ end
86
+
87
+
88
+ end
89
+
90
+ =begin
91
+ # Throw this error if the configured safe directory points to a file.
92
+ class SafeDirectoryIsFile < OpenError::CliError; end;
93
+
94
+ # Throw this error if safe directory path is either nil or empty.
95
+ class SafeDirNotConfigured < OpenError::CliError; end;
96
+
97
+ # Throw this error if the email address is nil, empty or less than 5 characters.
98
+ class EmailAddrNotConfigured < OpenError::CliError; end;
99
+
100
+ # Throw this error if the store url is either nil or empty.
101
+ class StoreUrlNotConfigured < OpenError::CliError; end;
102
+
103
+ # Throw if "prime folder" name occurs 2 or more times in the path.
104
+ class SafePrimeNameRepeated < OpenError::CliError; end;
105
+
106
+ # Throw if "prime folder" name occurs 2 or more times in the path.
107
+ class SafePrimeNameNotAtEnd < OpenError::CliError; end;
108
+ =end
109
+
110
+ end