Class: Reedb::ReeVault

Inherits:
Object
  • Object
show all
Defined in:
lib/reedb/reevault.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name, path, encprytion, header_override = nil) ⇒ ReeVault

Constructor for a vault with name, path and encryption enum. Valid encryption parameters are :aes, :twofish, :multi and :auto_fill



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/reedb/reevault.rb', line 57

def initialize(name, path, encprytion, header_override = nil)
	@already_logging = false

	# Header maps
	@headers = {}
	@hgroups = {}

	# Fileset
	@locked = false
	@locks = []

	# Defines the default (and boring vanilla) header set
	# TODO: Get the header set via config and init that instead!
	@header_set = header_override ? header_override : { 'urls' => 'list', 'tags' => 'list' }

	# Make the path available as an object variable
	construct_path("#{name}", "#{path}")

	# Init ecnryption module. So @crypt must not be nil after this
	init_encryption(encprytion)

	# Setup the secure config to false by default. Change this?
	self.secure_config(false)
	return self
end

Instance Attribute Details

#cryptObject (readonly)

Encryption handler to be used by the vault files



41
42
43
# File 'lib/reedb/reevault.rb', line 41

def crypt
  @crypt
end

#header_setObject (readonly)

Holds a hash of possible values that header files in this vault can have. Fields need to be specified by name and a type. To choose from ‘single’, ‘list’ and ‘tree’ (var, list, dict)



52
53
54
# File 'lib/reedb/reevault.rb', line 52

def header_set
  @header_set
end

#pathObject (readonly)

Returns the value of attribute path.



37
38
39
# File 'lib/reedb/reevault.rb', line 37

def path
  @path
end

#sizeObject (readonly)

Indicates the size of the vault as per data file entries. Is updated with every header cache and write cycle.



46
47
48
# File 'lib/reedb/reevault.rb', line 46

def size
  @size
end

Instance Method Details

#add_header_field(name, type) ⇒ Object

Fields can be added to a vault header BUT NOT REMOVED AGAIN! So be careful what you put in your header. (aka upgrade yes, downgrade noooo)

Unused fields can remain blank but need to stay in a vaults header list for backwards compatiblity



95
96
97
# File 'lib/reedb/reevault.rb', line 95

def add_header_field(name, type)
	@header_set[name] = type unless @header_set[name]
end

#closeObject



356
357
358
359
360
361
362
363
364
# File 'lib/reedb/reevault.rb', line 356

def close
	VaultLogger.write('Force closing the vault. Check parent logs for details', 'debug')
	# puts "Crypto module is: #{@crypt}"
	@crypt.stop_encryption if @crypt && @crypt.init

	# Removing class variables for cleanup
	remove_instance_variable(:@crypt)
	remove_instance_variable(:@headers)
end

#countObject

Counts the vault contents and returns an Integer WITHOUT HAVING TO UNLOCK THE VAULT!



108
109
110
111
112
113
114
# File 'lib/reedb/reevault.rb', line 108

def count
	counter = 0
	Dir.glob("#{@path}/data/*.ree") do |f|
		counter += 1
	end
	return counter
end

#create(password = :failed) ⇒ Object



124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
# File 'lib/reedb/reevault.rb', line 124

def create(password = :failed)
	# If keygen was used to set a user password then fetch it
	# and remove the variable from memory!
	return nil unless password?(password)
	return nil unless encryption?(password)

	# puts "This is the password: #{password}"

	# => Encryption now active and key available under @crypt.key
	@conf_path = "#{@path}/config"

	needs_creation = true

	if self.includes?('config')
		raise VaultExistsAtLocationError.new, "Vault already exists at location #{@path}. Aborting operation..."

		# => This rules out lots of code to be run
		needs_creation = false
	else
		FileUtils::mkdir_p(File.expand_path("#{@path}/data")) # => Data dir
		FileUtils::mkdir(File.expand_path("#{@path}/shasums")) # => Checksum dir
		FileUtils::mkdir(File.expand_path("#{@path}/logs")) # => Logs dir

		# On *nix devices change permissions.
		if Reedb::archos == :linux || Reedb::archos == :osx || Reedb::archos == :vars
			FileUtils::chmod_R(0744, "#{@path}")
		end
	end

	# Now that the vault directory exists logs can be opened.
	init_logger(true)

	if needs_creation
		# Code that will only be run if the vault was just created on the system
		time = Reedb::Utilities.get_time(false)
		VaultLogger.write("Vault created on #{time}. All directories created successfully!")

		# => Now creating configuration file
		@config = {}
		@config['vault_name'] = "#{@name}"
		@config['creation_date'] = "#{Utilities.get_time}"
		@config['last_updated'] = "#{Utilities.get_time}"
		@config['creation_machine'] = "#{Socket.gethostname}"
		@config['updating_machine'] = "#{Socket.gethostname}"
		@config['creation_user'] = "#{Etc.getlogin}"
		@config['updating_user'] = "#{Etc.getlogin}"

		# Convert the header set to JSON, then write it into the config
		hset = JSON.dump(@header_set)
		@config['header_set'] = "#{hset}"

		# Add the Reedb version this vault was created with for upgradability
		@config['creation_version'] = "#{Reedb::VERSION}"
		save_config

		# Now writing encrypted key to file with ASCII armour
		update_secure_info('cey', @encrypted_key)
		# remove_instance_variable(:@encrypted_key)
	end
	self.load(password)
end

#includes?(file) ⇒ Boolean

Quickly returns if a file exists in the vault or it’s children.

Returns:

  • (Boolean)


367
368
369
# File 'lib/reedb/reevault.rb', line 367

def includes?(file)
	return File.exists?("#{@path}/#{file}")
end

#list_headers(search) ⇒ Object

Returns headers according to a search queury

{ ‘name’ => ‘__name__’, ‘url’ => ‘__url__’, ‘tags’ => ‘__tags__’, ‘generic_field’ => ‘generic_information’ }

‘tags=search engines, internet#urls=www.poodle.com



310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
# File 'lib/reedb/reevault.rb', line 310

def list_headers search
	query = {}
	cache_headers # => This fills @headers and @hfields

	return @headers unless search

	begin
		splat = search.split('#')
		splat.each do |target|
			slice = target.split('=')
			query["#{slice[0]}"] = slice[1..-1]
		end

			# Rescue the query in case it was bad
	rescue
		raise MalformedSearchError.new, 'Malformed search data'
	end

	log_query = {}
	candidates = []

	query.each do |cat, data|
		data.each do |val|
			log_query["#{cat}"] = @hgroups["#{cat}"]["#{val}"] if @hgroups["#{cat}"].include?(val)
			log_query["#{cat}"].each { |c| candidates << c unless candidates.include?(c) }
		end
	end
	return_buffer = candidates
	candidates.each do |can|
		log_query.each do |cat, data|
			return_buffer.delete(can) unless log_query["#{cat}"].include?(can)
		end
	end

	return return_buffer
end

#load(password) ⇒ Object



186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
# File 'lib/reedb/reevault.rb', line 186

def load(password)
	unless self.includes?('config')
		raise VaultDoesNotExistError.new("Loading vault failed because it couldn't be found at the specified path!")
	end
	init_logger(false)

	# Check if the config needs to be read via ASCII64 or YAML
	if self.includes?('pom')
		# Config is stored with ASCII Armour
		@config = read_secure_info('config')
	else
		@config = YAML.load_file("#{@path}/config")
	end


	return nil unless unlock_vault("#{password}")
	VaultLogger.write('Finished loading vault', 'debug')
	cache_headers

	return self
end

#locked?Boolean

Little helper method to determine if a vault is in the middle of a write cycle. Which would cause horrible crashes on other applications and errors on the file system if things are moved around inside

Returns:

  • (Boolean)


120
121
122
# File 'lib/reedb/reevault.rb', line 120

def locked?;
	@locked
end

#read_file(name, history = false) ⇒ Object

Read a single file from the vault in secure mode Returns the entire file or only it’s current set in hashes.



211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
# File 'lib/reedb/reevault.rb', line 211

def read_file(name, history = false)

	# Loads the file data into a local variable if it exists
	file_data = load_file_data(name)
	if file_data == nil
		raise FileNotFoundError.new("#{name} could not be read: File not found!")
		# return VAULT_FILE_NOT_FOUND_ERROR # If the exception isn't handled correctly
	else
		# This code is executed if the file was found (thus data is in file_data)
		compiled = {}
		compiled['header'] = {}

		# Removes the latest version from the header because it is insignificant.
		file_data['header'].each do |key, value|
			compiled['header']["#{key}"] = value unless key == 'latest'
		end

		if history
			compiled['body'] = file_data['body']
		else
			body_list = []
			file_data['body'].each do |key, _|
				body_list << key
			end

			compiled['body'] = {}

			# Now sort the list of body versions
			body_list.heapsort!

			# Then compile the data together into one data hash
			body_list.each do |version|
				file_data['body']["#{version}"].each do |key, value|
					compiled['body']["#{key}"] = value
				end
			end
		end

		# Then return that hash. Huzza!
		return compiled
	end
end

#remove_file(name) ⇒ Object



290
291
292
293
294
295
296
297
298
# File 'lib/reedb/reevault.rb', line 290

def remove_file(name)
	path_to_file = load_file_hash(name)
	if path_to_file
		FileUtils.rm(path_to_file)
		VaultLogger.write("Removed file #{name} from vault.", 'debug')
	else
		raise FileNotFoundError.new("#{name} could not be removed: File not found!")
	end
end

#secure_config(boolean = true) ⇒ Object



83
84
85
86
# File 'lib/reedb/reevault.rb', line 83

def secure_config(boolean = true)
	@secure_config = boolean
	return self
end

#to_sObject



371
372
373
# File 'lib/reedb/reevault.rb', line 371

def to_s
	return "Vault: #{@name}, Path: #{@path}, File count: #{@headers.length}"
end

#try?Boolean

Pokes if a vault exists

Returns:

  • (Boolean)


101
102
103
# File 'lib/reedb/reevault.rb', line 101

def try?
	return self.includes?('config')
end

#unload(time) ⇒ Object

Dump headers and files from memory in times of inactivity for security reasons



349
350
351
352
353
354
# File 'lib/reedb/reevault.rb', line 349

def unload(time)
	remove_instance_variable(:@headers)
	@headers = {}

	VaultLogger.write("It has been #{time*60} minutes since the last interaction. Unloading vault contents for security reasons.", 'debug')
end

#update(name, data) ⇒ Object

Check the file API or the wiki to learn how this function works. This function is also used to delete fields from header space.



257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
# File 'lib/reedb/reevault.rb', line 257

def update(name, data)

	# Cache headers first to be sure we're up to date
	cache_headers

	# Raises exception and [returns] in case exception isn't properly being handled
	(raise FileBusyError.new, "File #{name} busy"; return) if @locks.include?(name)
	@locks << name

	if @headers.key?(name)
		# Creates file object from existing file object.
		df = DataFile.new(name, self, load_file_data(name, :secure))
		df.insertv2(data, :hard) # Default cache mode
	else
		df = DataFile.new(name, self)
		df.insertv2(data, :hard) # Default cache mode
	end

	# => Make sure that everything is up to date.
	cache_headers
	@config['updating_user'] = "#{Etc.getlogin}"
	@config['updating_machine'] = "#{Socket.gethostname}"
	@config['last_updated'] = "#{Utilities.get_time}"
	@config['last_version'] = "#{Reedb::VERSION}"
	save_config

	# Sync and close the file.
	df.sync.close

	# Unlocks the file again for other processes to edit.
	@locks.delete(name)
end