Class: SafeDb::KeyDb
Overview
A Key/Value database knows how to manipulate a JSON backed data structure (put, add etc) after reading and then decrypting it from a file and before encrypting and then writing it to a file.
It provides behaviour to which we can create, append (add), update (change), read parts and delete essentially two structures
-
a collection of name/value pairs
-
an ordered list of values
JSON is Not Exposed in the Interface
A key/value database doesn’t expose the data format used in the implementation allowing this to be changed seamlessly to YAMl or other formats.
Symmetric Encryption and Decryption
A key/value database supports operations to read from and write to a known filepath and with a symmetric key it can
-
decrypt after reading from a file and
-
encrypt before writing to a (the same) file
Hashes as the Primary Data Structure
The key/value database openly extends Hash as the data structure for holding
-
strings
-
arrays
-
other hashes
-
booleans
-
integers and floats
Class Method Summary collapse
-
.from_json(db_json_string) ⇒ KeyDb
Return a key database data structure that is instantiated from the parameter JSON string.
Instance Method Summary collapse
-
#create_entry(dictionary_name, key_name, value) ⇒ Object
Create a new key value entry inside a dictionary with the specified name at the root of this database.
-
#create_map_entry(outer_keyname, inner_keyname, entry_keyname, entry_value) ⇒ Object
Create a new secondary tier map key value entry inside a primary tier map at the map_key_name location.
-
#delete_entry(dictionary_name, key_name) ⇒ Object
Delete an existing key value entry inside the dictionary with the specified name at the root of this database.
-
#get_entry(dictionary_name, key_name) ⇒ Object
Get the entry with the key name in a dictionary that is itself inside another dictionary (named in the first parameter) which thankfully is at the root of this database.
-
#has_entry?(dictionary_name, key_name) ⇒ Boolean
Does this database have an entry in the root dictionary named with the key_name parameter?.
-
#read(the_filepath, decryption_key = nil) ⇒ Object
Read and inject into this envelope, the data structure found in a file at the path specified in the first parameter.
-
#write(encryption_key) ⇒ Object
Write the data in this envelope hash map into a file-system backed mirror whose path was specified in the selfself.read method.
Methods inherited from Hash
Class Method Details
.from_json(db_json_string) ⇒ KeyDb
Return a key database data structure that is instantiated from the parameter JSON string.
52 53 54 55 56 57 58 |
# File 'lib/keytools/key.db.rb', line 52 def self.from_json( db_json_string ) data_db = KeyDb.new() data_db.merge!( JSON.parse( db_json_string ) ) return data_db end |
Instance Method Details
#create_entry(dictionary_name, key_name, value) ⇒ Object
Create a new key value entry inside a dictionary with the specified name at the root of this database. Successful completion means the named dictionary will contain one more entry than it need even if it did not previously exist.
84 85 86 87 88 89 90 91 92 93 |
# File 'lib/keytools/key.db.rb', line 84 def create_entry( dictionary_name, key_name, value ) KeyError.not_new( dictionary_name, self ) KeyError.not_new( key_name, self ) KeyError.not_new( value, self ) self[ dictionary_name ] = {} unless self.has_key?( dictionary_name ) self[ dictionary_name ][ key_name ] = value end |
#create_map_entry(outer_keyname, inner_keyname, entry_keyname, entry_value) ⇒ Object
Create a new secondary tier map key value entry inside a primary tier map at the map_key_name location.
A failure will occur if either the outer or inner keys already exist without their values being map objects.
If this method is called against a new empty map, the resulting map structure will look like the below.
{ outer_keyname ~> { inner_keyname ~> { entry_keyname, entry_value } } }
137 138 139 140 141 142 143 144 145 146 147 148 |
# File 'lib/keytools/key.db.rb', line 137 def create_map_entry( outer_keyname, inner_keyname, entry_keyname, entry_value ) KeyError.not_new( outer_keyname, self ) KeyError.not_new( inner_keyname, self ) KeyError.not_new( entry_keyname, self ) KeyError.not_new( entry_value, self ) self[ outer_keyname ] = {} unless self.has_key?( outer_keyname ) self[ outer_keyname ][ inner_keyname ] = {} unless self[ outer_keyname ].has_key?( inner_keyname ) self[ outer_keyname ][ inner_keyname ][ entry_keyname ] = entry_value end |
#delete_entry(dictionary_name, key_name) ⇒ Object
Delete an existing key value entry inside the dictionary with the specified name at the root of this database. Successful completion means the named dictionary will contain one less entry if that key existed.
216 217 218 219 220 221 222 223 |
# File 'lib/keytools/key.db.rb', line 216 def delete_entry( dictionary_name, key_name ) KeyError.not_new( dictionary_name, self ) KeyError.not_new( key_name, self ) self[ dictionary_name ].delete( key_name ) end |
#get_entry(dictionary_name, key_name) ⇒ Object
Get the entry with the key name in a dictionary that is itself inside another dictionary (named in the first parameter) which thankfully is at the root of this database.
Only call this method if #has_entry? returns true for the same dictionary and key name parameters.
194 195 196 197 198 |
# File 'lib/keytools/key.db.rb', line 194 def get_entry( dictionary_name, key_name ) return self[ dictionary_name ][ key_name ] end |
#has_entry?(dictionary_name, key_name) ⇒ Boolean
Does this database have an entry in the root dictionary named with the key_name parameter?
164 165 166 167 168 169 170 171 172 |
# File 'lib/keytools/key.db.rb', line 164 def has_entry?( dictionary_name, key_name ) KeyError.not_new( dictionary_name, self ) KeyError.not_new( key_name, self ) return false unless self.has_key?( dictionary_name ) return self[ dictionary_name ].has_key?( key_name ) end |
#read(the_filepath, decryption_key = nil) ⇒ Object
Read and inject into this envelope, the data structure found in a file at the path specified in the first parameter.
Symmetric cryptography is mandatory for the envelope so we must encrypt before writing and decrypt after reading.
An argument error will result if a suitable key is not provided.
If the file does not exist (denoting the first read) all this method does is to stash the filepath as an instance variable and igore the decryption key which can be nil (or ommitted).
249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 |
# File 'lib/keytools/key.db.rb', line 249 def read the_filepath, decryption_key = nil # @todo -> this is confused - it uses INI but above methods use JSON # @todo -> this is confused - it uses INI but above methods use JSON # @todo -> this is confused - it uses INI but above methods use JSON # @todo -> this is confused - it uses INI but above methods use JSON # @todo -> this is confused - it uses INI but above methods use JSON # @todo -> this is confused - it uses INI but above methods use JSON # @todo -> this is confused - it uses INI but above methods use JSON # @todo -> this is confused - it uses INI but above methods use JSON # @todo -> this is confused - it uses INI but above methods use JSON # @todo -> this is confused - it uses INI but above methods use JSON raise RuntimeError, "This KeyDb.read() software is never called so how can I be here?" @filepath = the_filepath return unless File.exists? @filepath cipher_text = Base64.decode64( File.read( @filepath ).strip ) plain_text = ToolBelt::Blowfish.decryptor( cipher_text, decryption_key ) data_structure = JSON.parse plain_text self.merge! data_structure end |
#write(encryption_key) ⇒ Object
Write the data in this envelope hash map into a file-system backed mirror whose path was specified in the SafeDb::KeyDb.selfself.read method.
Technology for encryption at rest is supported by this dictionary and to this aim, please endeavour to post a robust symmetric encryption key.
Calling this SafeDb::KeyDb.selfself.write method when the file at the prescribed path does not exist results in the directory structure being created (if necessary) and then the encrypted file being written.
292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 |
# File 'lib/keytools/key.db.rb', line 292 def write encryption_key # @todo -> this is confused - it uses INI but above methods use JSON # @todo -> this is confused - it uses INI but above methods use JSON # @todo -> this is confused - it uses INI but above methods use JSON # @todo -> this is confused - it uses INI but above methods use JSON # @todo -> this is confused - it uses INI but above methods use JSON # @todo -> this is confused - it uses INI but above methods use JSON # @todo -> this is confused - it uses INI but above methods use JSON # @todo -> this is confused - it uses INI but above methods use JSON # @todo -> this is confused - it uses INI but above methods use JSON raise RuntimeError, "This KeyDb.write( key ) software is never called so how can I be here?" FileUtils.mkdir_p(File.dirname(@filepath)) cipher_text = Base64.encode64 ToolBelt::Blowfish.encryptor( self.to_json, encryption_key ) File.write @filepath, cipher_text puts "" puts "=== ============================" puts "=== Envelope State =============" puts "=== ============================" a_ini_file = IniFile.new self.each_key do |section_name| a_ini_file[section_name] = self[section_name] end puts a_ini_file.to_s puts "=== ============================" puts "" end |