Class: Keybox::Storage::Container

Inherits:
Record
  • Object
show all
Defined in:
lib/keybox/storage/container.rb

Overview

The container of the Keybox records. The Container itself is a Keybox::Storage::Record with a few extra methods.

A instance of a Container is created with a assprhase and a path to a file. The passphrase can be anything that has a to_s method.

container = Keybox::Storage::Container.new("i love ruby", "/tmp/database.yml")

This will load from the given file with the given passphrase if the file exists, or it will initalize the container to accept records.

The records are held decrypted in memory, so keep that in mind if that is a concern.

Add Records

record = Keybox::Storage::Record.new
record.field1 = "data"
record.field1 = "some more data"

container << record

Delete Records

container.delete(record)

There is no ‘update’ record, just delete it and add it.

Find a record accepts a string and will look in all the records it contains for anything that matches it. It coerces strings into Regexp so any regex can be used here too.

container.find("data")

Report if the container or any of its records have been modified:

container.modified?

Save the container to its default location:

container.save

Or to some other location

container.save("/some/other/path.yml")

Direct access to the decrypted records is also available through the records accessor.

container.records #=> Array of Keybox::Storage::Record

Constant Summary collapse

ITERATIONS =
2048

Constants inherited from Record

Record::PROTECTED_METHODS

Instance Attribute Summary collapse

Attributes inherited from Record

#creation_time, #data_members, #last_access_time, #modification_time, #uuid

Instance Method Summary collapse

Methods inherited from Record

#==, #eql?, #method_missing, #modified=, #to_yaml_properties

Constructor Details

#initialize(passphrase, path) ⇒ Container

Returns a new instance of Container.



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/keybox/storage/container.rb', line 72

def initialize(passphrase,path)
    super()

    @path        = path
    @passphrase  = passphrase
    @records     = []

    if not load_from_file then
        self.version                    = Keybox::VERSION
        
        self.key_calc_iterations        = ITERATIONS
        self.key_digest_salt            = Keybox::RandomDevice.random_bytes(32)
        self.key_digest_algorithm       = Keybox::Digest::DEFAULT_ALGORITHM
        self.key_digest                 = calculated_key_digest(passphrase)

        self.records_init_vector        = Keybox::RandomDevice.random_bytes(16)
        self.records_cipher_algorithm   = Keybox::Cipher::DEFAULT_ALGORITHM

        self.records_encrypted_data     = ""
        self.records_digest_salt        = Keybox::RandomDevice.random_bytes(32)
        self.records_digest_algorithm   = Keybox::Digest::DEFAULT_ALGORITHM
        self.records_digest             = ""
    end
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method in the class Keybox::Storage::Record

Instance Attribute Details

#recordsObject (readonly)

Returns the value of attribute records.



69
70
71
# File 'lib/keybox/storage/container.rb', line 69

def records
  @records
end

Instance Method Details

#<<(obj) ⇒ Object

Add a record to the system



167
168
169
170
171
172
# File 'lib/keybox/storage/container.rb', line 167

def <<(obj)
    if obj.respond_to?("needs_container_passphrase?") and obj.needs_container_passphrase? then
        obj.container_passphrase = @passphrase
    end
    @records << obj
end

#calculated_key(passphrase = @passphrase) ⇒ Object

calculate the encryption key from the initial passphrase



149
150
151
152
153
154
155
# File 'lib/keybox/storage/container.rb', line 149

def calculated_key(passphrase = @passphrase)
    key = self.key_digest_salt + passphrase
    self.key_calc_iterations.times do 
        key = Keybox::Digest::CLASSES[self.key_digest_algorithm].digest(key)
    end
    return key
end

#calculated_key_digest(passphrase) ⇒ Object

calculate the key digest of the encryption key



160
161
162
# File 'lib/keybox/storage/container.rb', line 160

def calculated_key_digest(passphrase)
    Keybox::Digest::CLASSES[self.key_digest_algorithm].hexdigest(calculated_key(passphrase))
end

#delete(obj) ⇒ Object

Delete a record from the system, we force a modified flag here since the underlying Record wasn’t ‘assigned to’ we have to force modification notification.



193
194
195
196
# File 'lib/keybox/storage/container.rb', line 193

def delete(obj)
    @records.delete(obj)
    @modified = true
end

#find(search_string, restricted_to = nil) ⇒ Object

Search all the records in the database finding any that match the search string passed in. The Search string is converted to a Regexp before beginning.

A list of restricted fields can also be passed in and the regexp will only be matched against those fields



213
214
215
216
217
218
219
220
221
222
223
224
225
226
# File 'lib/keybox/storage/container.rb', line 213

def find(search_string,restricted_to = nil)
    regex = Regexp.new(search_string)
    matches = []
    @records.each do |record|
        restricted_to = restricted_to || ( record.fields - %w(password) )
        record.data_members.each_pair do |k,v|
            if regex.match(v) and restricted_to.include?(k.to_s) then
                matches << record
                break
            end
        end
    end
    return matches
end

#find_by_url(url) ⇒ Object

Search only records that have a url field



201
202
203
# File 'lib/keybox/storage/container.rb', line 201

def find_by_url(url)
    find(url,%w(url))
end

#lengthObject

The number of Records in the Container



177
178
179
# File 'lib/keybox/storage/container.rb', line 177

def length
    @records.size
end

#load_from_fileObject

load from file, if this is successful then replace the existing member fields on this instance with the data from the file



113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/keybox/storage/container.rb', line 113

def load_from_file
    return false unless File.exists?(@path)
    return false unless tmp = YAML.load_file(@path)
    @creation_time      = tmp.creation_time
    @modification_time  = tmp.modification_time
    @last_access_time   = tmp.last_access_time
    @data_members       = tmp.data_members
    @uuid               = tmp.uuid
    validate_passphrase
    decrypt_records
    validate_decryption
    load_records
    @modified = false
    true
end

#modified?Boolean

See if we are modified, or if any of the records are modified

Returns:

  • (Boolean)


232
233
234
235
236
237
238
# File 'lib/keybox/storage/container.rb', line 232

def modified?
    return true if super
    @records.each do |record|
        return true if record.modified?
    end
    return false
end

#passphrase=(new_passphrase) ⇒ Object

Change the master password of the container



100
101
102
103
104
105
106
# File 'lib/keybox/storage/container.rb', line 100

def passphrase=(new_passphrase)
    @passphrase                 = new_passphrase
    self.key_digest_salt        = Keybox::RandomDevice.random_bytes(32)
    self.key_digest             = calculated_key_digest(new_passphrase)
    self.records_init_vector    = Keybox::RandomDevice.random_bytes(16)
    self.records_digest_salt    = Keybox::RandomDevice.random_bytes(32)
end

#save(path = @path) ⇒ Object

save the current container to a file



132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/keybox/storage/container.rb', line 132

def save(path = @path)
    calculate_records_digest 
    encrypt_records
    File.open(path,"w") do |f|
        f.write(self.to_yaml)
    end

    # mark everything as not modified
    @records.each do |record|
        record.modified = false
    end
    @modified = false
end

#sizeObject

The number of Records in the Container



184
185
186
# File 'lib/keybox/storage/container.rb', line 184

def size
    @records.size
end