opensecret 0.0.988 → 0.0.9925
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +56 -159
- data/bin/opensecret +2 -2
- data/bin/ops +17 -2
- data/lib/extension/string.rb +14 -16
- data/lib/{interpreter.rb → interprete.rb} +53 -29
- data/lib/keytools/binary.map.rb +49 -0
- data/lib/keytools/kdf.api.rb +249 -0
- data/lib/keytools/kdf.bcrypt.rb +64 -29
- data/lib/keytools/kdf.pbkdf2.rb +92 -83
- data/lib/keytools/kdf.scrypt.rb +190 -0
- data/lib/keytools/key.64.rb +326 -0
- data/lib/keytools/key.algo.rb +109 -0
- data/lib/keytools/key.api.rb +1281 -0
- data/lib/keytools/key.db.rb +265 -0
- data/lib/keytools/{key.module.rb → key.docs.rb} +55 -0
- data/lib/keytools/key.error.rb +110 -0
- data/lib/keytools/key.id.rb +271 -0
- data/lib/keytools/key.iv.rb +107 -0
- data/lib/keytools/key.local.rb +265 -0
- data/lib/keytools/key.mach.rb +248 -0
- data/lib/keytools/key.now.rb +402 -0
- data/lib/keytools/key.pair.rb +259 -0
- data/lib/keytools/key.pass.rb +120 -0
- data/lib/keytools/key.rb +428 -298
- data/lib/keytools/keydebug.txt +295 -0
- data/lib/logging/gem.logging.rb +3 -3
- data/lib/modules/cryptology/collect.rb +20 -0
- data/lib/session/require.gem.rb +1 -1
- data/lib/usecase/cmd.rb +417 -0
- data/lib/usecase/id.rb +36 -0
- data/lib/usecase/import.rb +174 -0
- data/lib/usecase/init.rb +78 -0
- data/lib/usecase/login.rb +70 -0
- data/lib/usecase/logout.rb +30 -0
- data/lib/usecase/open.rb +126 -0
- data/lib/{interprete → usecase}/put.rb +100 -47
- data/lib/usecase/read.rb +89 -0
- data/lib/{interprete → usecase}/safe.rb +0 -0
- data/lib/{interprete → usecase}/set.rb +0 -0
- data/lib/usecase/token.rb +111 -0
- data/lib/{interprete → usecase}/use.rb +0 -0
- data/lib/version.rb +1 -1
- data/opensecret.gemspec +4 -3
- metadata +39 -33
- data/lib/exception/cli.error.rb +0 -53
- data/lib/exception/errors/cli.errors.rb +0 -31
- data/lib/interprete/begin.rb +0 -232
- data/lib/interprete/cmd.rb +0 -621
- data/lib/interprete/export.rb +0 -163
- data/lib/interprete/init.rb +0 -205
- data/lib/interprete/key.rb +0 -119
- data/lib/interprete/open.rb +0 -148
- data/lib/interprete/seal.rb +0 -129
- data/lib/keytools/digester.rb +0 -245
- data/lib/keytools/key.data.rb +0 -227
- data/lib/keytools/key.derivation.rb +0 -341
- data/lib/modules/mappers/collateral.rb +0 -282
- data/lib/modules/mappers/envelope.rb +0 -127
- data/lib/modules/mappers/settings.rb +0 -170
- data/lib/notepad/scratch.pad.rb +0 -224
- data/lib/store-commands.txt +0 -180
@@ -1,282 +0,0 @@
|
|
1
|
-
#!/usr/bin/ruby
|
2
|
-
# coding: utf-8
|
3
|
-
|
4
|
-
module OpenSecret
|
5
|
-
|
6
|
-
module Mapper
|
7
|
-
|
8
|
-
# <b>Collateral</b> fingers (locates/points to) resource paths and objects that
|
9
|
-
# are accessible on the filesystem of either the workstation or its associated
|
10
|
-
# (external) drives.
|
11
|
-
#
|
12
|
-
# Its ubiquitous language distinguishes four (4) base paths for the
|
13
|
-
#
|
14
|
-
# - <b>frontend store</b> - usually a path to USB connected devices like phones
|
15
|
-
# - <b>mid-end store</b> - location is on workstation off the user's home directory
|
16
|
-
# - <b>(local) backend store</b> - departure lounge located off the home directory
|
17
|
-
# - <b>(remote) backend store</b> - arrival gate on S3, Google Drive or SSH drive
|
18
|
-
class Collateral
|
19
|
-
include Singleton
|
20
|
-
|
21
|
-
|
22
|
-
# After constructing the <b>{Collateral}</b> object the building
|
23
|
-
# blocks that all paths are fabricated from must be injected.
|
24
|
-
attr_accessor :domain_name, :frontend_path
|
25
|
-
|
26
|
-
ENV_OPS_KEY_NAME = "OPS_KEY"
|
27
|
-
|
28
|
-
# The context name is used in various places (filesystem especially)
|
29
|
-
# in order to disambiguate the question of which software creates, reads,
|
30
|
-
# and updates the containing collateral.
|
31
|
-
CONTEXT_NAME = "opensecret.io"
|
32
|
-
|
33
|
-
# The name of the frontend directory in which encrypted
|
34
|
-
# session envelopes are stored in a tree-like structure.
|
35
|
-
SESSION_ENVELOPES_NAME = "session.envelopes"
|
36
|
-
|
37
|
-
# The name of the frontend directory (library) in which crypt
|
38
|
-
# keys are stored in a tree-like structure.
|
39
|
-
KEY_STORE_LIBRARY_NAME = "keystore.library"
|
40
|
-
|
41
|
-
# The name of the backend directory (library) in which crypt
|
42
|
-
# material is stored in a tree-like structure.
|
43
|
-
CRYPT_STORE_LIBRARY_NAME = "crypt.library"
|
44
|
-
|
45
|
-
# The name of the frontend directory (library) in which the
|
46
|
-
# index files are harboured in a flat structure.
|
47
|
-
CONFIG_LIBRARY_NAME = "ops.configuration"
|
48
|
-
|
49
|
-
# The name of the frontend directory in which the master keys
|
50
|
-
# are (or will be) stored.
|
51
|
-
MASTER_KEYS_DIR_NAME = "known.master.keys"
|
52
|
-
|
53
|
-
# The public key filename will end with this constant suffix.
|
54
|
-
PUBLIC_KEY_NAME_SUFFIX = "public.key.txt"
|
55
|
-
|
56
|
-
# The private key filename will end with this constant suffix.
|
57
|
-
PRIVATE_KEY_NAME_SUFFIX = "private.key.txt"
|
58
|
-
|
59
|
-
|
60
|
-
# Write the public key text into a file within the master keys
|
61
|
-
# folder. The directory path is created if necessary.
|
62
|
-
#
|
63
|
-
# @param public_key_text [String]
|
64
|
-
# the text totality that makes up the domain's public key
|
65
|
-
def write_public_key public_key_text
|
66
|
-
FileUtils.mkdir_p master_keys_path
|
67
|
-
File.write public_key_path, public_key_text
|
68
|
-
end
|
69
|
-
|
70
|
-
|
71
|
-
# Read the public key text from a file within the master keys
|
72
|
-
# folder. This method will fail if the public key isn't found.
|
73
|
-
#
|
74
|
-
# @return [String]
|
75
|
-
# return the text totality making up the domain's public key
|
76
|
-
def read_public_key
|
77
|
-
return File.read( public_key_path )
|
78
|
-
end
|
79
|
-
|
80
|
-
|
81
|
-
# Write the private key text into a file within the master keys
|
82
|
-
# folder. The directory path is created if necessary.
|
83
|
-
#
|
84
|
-
# @param private_key_text [String]
|
85
|
-
# the text totality that makes up the domain's private key
|
86
|
-
def write_private_key private_key_text
|
87
|
-
FileUtils.mkdir_p master_keys_path
|
88
|
-
File.write private_key_path, private_key_text
|
89
|
-
end
|
90
|
-
|
91
|
-
|
92
|
-
# Read the private key text from a file within the master keys
|
93
|
-
# folder. This method will fail if the private key isn't found.
|
94
|
-
#
|
95
|
-
# @return [String]
|
96
|
-
# return the text totality making up the domain's private key
|
97
|
-
def read_private_key
|
98
|
-
return File.read( private_key_path )
|
99
|
-
end
|
100
|
-
|
101
|
-
|
102
|
-
# Return true if the private key exists within a file under the
|
103
|
-
# auspices of the current domain.
|
104
|
-
#
|
105
|
-
# @param private_key_text [String]
|
106
|
-
# the text totality that makes up the domain's private key
|
107
|
-
def private_key_exists?
|
108
|
-
return File.file? private_key_path
|
109
|
-
end
|
110
|
-
|
111
|
-
|
112
|
-
# The path to the initial configuration file below the user's home
|
113
|
-
# directory. The directory name the configuration file sits in is
|
114
|
-
# a dot prefixed context name derived from the value inside the
|
115
|
-
# {Mapper::Collateral::CONTEXT_NAME} constant.
|
116
|
-
#
|
117
|
-
# ~/.<<context-name>>/<<context-name>>-configuration.ini
|
118
|
-
#
|
119
|
-
# You can see the filename too is derived from the context with a
|
120
|
-
# trailing string ending in <b>.ini</b>.
|
121
|
-
#
|
122
|
-
# @return [String] full path to the context configuration file
|
123
|
-
def config_file
|
124
|
-
return File.join config_directory, "#{CONTEXT_NAME}.configuration.ini"
|
125
|
-
end
|
126
|
-
|
127
|
-
|
128
|
-
# This method returns the absolute path to the directory that the
|
129
|
-
# configuration file sits in. It is basically just the dot prefixed
|
130
|
-
# context name (the {Mapper::Collateral::CONTEXT_NAME} constant).
|
131
|
-
#
|
132
|
-
# ~/.<<context-name>>
|
133
|
-
#
|
134
|
-
# @return [String] path to directory holding context configuration file
|
135
|
-
def config_directory
|
136
|
-
return File.join home_directory, ".#{CONTEXT_NAME}"
|
137
|
-
end
|
138
|
-
|
139
|
-
|
140
|
-
# The configuration file governing the files and behaviour
|
141
|
-
# of the specified domain.
|
142
|
-
def domain_config_file
|
143
|
-
return File.join config_library_path, "ops.domain.config.ini"
|
144
|
-
end
|
145
|
-
|
146
|
-
# The path within the frontend directory in which the
|
147
|
-
# data index and the index configuration files are stored.
|
148
|
-
# @return [String]
|
149
|
-
# path to the base frontend index library directory
|
150
|
-
def config_library_path
|
151
|
-
return File.join frontend_base, CONFIG_LIBRARY_NAME
|
152
|
-
end
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
# The path to the session's folder that will be situated
|
159
|
-
# within the frontend drive location so that any session
|
160
|
-
# materials can be carried away with the external drive.
|
161
|
-
def session_folder
|
162
|
-
return File.join frontend_base, SESSION_ENVELOPES_NAME
|
163
|
-
end
|
164
|
-
|
165
|
-
|
166
|
-
# The path to the session's index file is an amalgam of the
|
167
|
-
# session folder {session_folder} and a <b>lowercased</b>
|
168
|
-
# session id stamp that should be the first n characters of
|
169
|
-
# the session id.
|
170
|
-
#
|
171
|
-
# @example
|
172
|
-
#
|
173
|
-
# session id stamp = M4Tywsv3BgNJQSn2MrA3484T
|
174
|
-
# session filename = session.m4tywsv3bgnjqsn2mra3484t.txt
|
175
|
-
#
|
176
|
-
# @param id_stamp [String]
|
177
|
-
#
|
178
|
-
# the first n characters of the session id that will
|
179
|
-
# be lowercased and sandwiched between the words
|
180
|
-
# <tt>session.</tt> and <tt>.txt</tt>
|
181
|
-
#
|
182
|
-
# @return [String]
|
183
|
-
# path to the session file that sits inside the frontend
|
184
|
-
# drive in folder called <b>session.envelopes</b>
|
185
|
-
def session_index_file id_stamp
|
186
|
-
return File.join( session_folder, "session.#{id_stamp.downcase}.txt" )
|
187
|
-
end
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
# The path within the frontend directory in which the encrypted
|
193
|
-
# keystore envelopes live in a tree-like structure.
|
194
|
-
# @return [String]
|
195
|
-
# path to the base frontend keystore data directory
|
196
|
-
def frontend_keystore_path
|
197
|
-
return File.join frontend_base, KEY_STORE_LIBRARY_NAME
|
198
|
-
end
|
199
|
-
|
200
|
-
|
201
|
-
# The path within the backend directory in which the encrypted
|
202
|
-
# crypt envelopes live in a tree-like structure.
|
203
|
-
# @return [String]
|
204
|
-
# path to the backend crypt store data directory
|
205
|
-
def backend_cryptstore_path
|
206
|
-
return File.join storage_url, "#{@domain_name}.#{CRYPT_STORE_LIBRARY_NAME}"
|
207
|
-
end
|
208
|
-
|
209
|
-
|
210
|
-
# On non-windows systems the home directory is defined
|
211
|
-
# perfectly by Ruby's Dir object.
|
212
|
-
#
|
213
|
-
# On Windows we sometimes get /AppData/Roaming appended
|
214
|
-
# onto the actual home directory. In these cases this
|
215
|
-
# method removes it.
|
216
|
-
#
|
217
|
-
# @return [String] the path to the machine user's home directory
|
218
|
-
def home_directory
|
219
|
-
|
220
|
-
return Dir.home unless Gem.win_platform?
|
221
|
-
|
222
|
-
extraneous_path = "/AppData/Roaming"
|
223
|
-
if Dir.home.end_with? extraneous_path then
|
224
|
-
return Dir.home.gsub( extraneous_path, "" )
|
225
|
-
end
|
226
|
-
|
227
|
-
return Dir.home
|
228
|
-
|
229
|
-
end
|
230
|
-
|
231
|
-
|
232
|
-
# Log the configuration file to two places
|
233
|
-
#
|
234
|
-
# - the console
|
235
|
-
# - the log file below the user's home directory.
|
236
|
-
#
|
237
|
-
# The logging is done at the INFO level.
|
238
|
-
def log_config
|
239
|
-
|
240
|
-
log.info(x) { File.read(config_file).log_lines }
|
241
|
-
puts ""
|
242
|
-
puts File.read(config_file)
|
243
|
-
puts ""
|
244
|
-
|
245
|
-
end
|
246
|
-
|
247
|
-
|
248
|
-
private
|
249
|
-
|
250
|
-
|
251
|
-
def frontend_base
|
252
|
-
return File.join @frontend_path, "ops.#{@domain_name}"
|
253
|
-
end
|
254
|
-
|
255
|
-
|
256
|
-
def storage_url
|
257
|
-
return File.join config_directory, "#{CONTEXT_NAME}.storage"
|
258
|
-
end
|
259
|
-
|
260
|
-
|
261
|
-
def master_keys_path
|
262
|
-
return File.join frontend_base, MASTER_KEYS_DIR_NAME
|
263
|
-
end
|
264
|
-
|
265
|
-
|
266
|
-
def public_key_path
|
267
|
-
return File.join master_keys_path, "#{@domain_name}.#{PUBLIC_KEY_NAME_SUFFIX}"
|
268
|
-
end
|
269
|
-
|
270
|
-
|
271
|
-
def private_key_path
|
272
|
-
return File.join master_keys_path, "#{@domain_name}.#{PRIVATE_KEY_NAME_SUFFIX}"
|
273
|
-
end
|
274
|
-
|
275
|
-
|
276
|
-
end
|
277
|
-
|
278
|
-
|
279
|
-
end
|
280
|
-
|
281
|
-
|
282
|
-
end
|
@@ -1,127 +0,0 @@
|
|
1
|
-
#!/usr/bin/ruby
|
2
|
-
# coding: utf-8
|
3
|
-
|
4
|
-
module OpenSecret
|
5
|
-
|
6
|
-
module Mapper
|
7
|
-
|
8
|
-
require 'json'
|
9
|
-
|
10
|
-
# An envelope knows how to manipulate a JSON backed data structure
|
11
|
-
# (put, add etc) <b>after reading and then decrypting it</b> from a
|
12
|
-
# file and <b>before encrypting and then writing it</b> to a file.
|
13
|
-
#
|
14
|
-
# It provides behaviour to which we can create, append (add), update
|
15
|
-
# (change), read parts and delete essentially two structures
|
16
|
-
#
|
17
|
-
# - a collection of name/value pairs
|
18
|
-
# - an ordered list of values
|
19
|
-
#
|
20
|
-
# == JSON is Not Exposed in the Interface
|
21
|
-
#
|
22
|
-
# An envelope doesn't expose the data format used in the implementation
|
23
|
-
# allowing this to be changed seamlessly to YAMl or other formats.
|
24
|
-
#
|
25
|
-
# == Symmetric Encryption and Decryption
|
26
|
-
#
|
27
|
-
# An envelope supports operations to <b>read from</b> and <b>write to</b>
|
28
|
-
# a known filepath and with a symmetric key it can
|
29
|
-
#
|
30
|
-
# - decrypt <b>after reading from</b> a file and
|
31
|
-
# - encrypt <b>before writing to</b> a (the same) file
|
32
|
-
#
|
33
|
-
# == Hashes as the Primary Data Structure
|
34
|
-
#
|
35
|
-
# Envelope extends {Hash} as the core data structure for holding
|
36
|
-
#
|
37
|
-
# - strings
|
38
|
-
# - arrays
|
39
|
-
# - other hashes
|
40
|
-
# - booleans
|
41
|
-
# - integers and floats
|
42
|
-
class Envelope < Hash
|
43
|
-
|
44
|
-
|
45
|
-
# Read and inject into this envelope, the data structure found in a
|
46
|
-
# file at the path specified in the first parameter.
|
47
|
-
#
|
48
|
-
# Symmetric cryptography is mandatory for the envelope so we must
|
49
|
-
# <b>encrypt before writing</b> and <b>decrypt after reading</b>.
|
50
|
-
#
|
51
|
-
# An argument error will result if a suitable key is not provided.
|
52
|
-
#
|
53
|
-
# If the file does not exist (denoting the first read) all this method
|
54
|
-
# does is to stash the filepath as an instance variable and igore the
|
55
|
-
# decryption key which can be nil (or ommitted).
|
56
|
-
#
|
57
|
-
# @param the_filepath [String]
|
58
|
-
# absolute path to the file which acts as the persistent mirror to
|
59
|
-
# this data structure envelope.
|
60
|
-
#
|
61
|
-
# @param decryption_key [String]
|
62
|
-
# encryption at rest is a given so this mandatory parameter must
|
63
|
-
# contain a robust symmetric decryption key. The key will be used
|
64
|
-
# for decryption after the read and it will not linger (ie not cached
|
65
|
-
# as an instance variable).
|
66
|
-
#
|
67
|
-
# @raise [ArgumentError] if the decryption key is not robust enough.
|
68
|
-
def read the_filepath, decryption_key = nil
|
69
|
-
|
70
|
-
@filepath = the_filepath
|
71
|
-
return unless File.exists? @filepath
|
72
|
-
|
73
|
-
cipher_text = Base64.decode64( File.read( @filepath ).strip )
|
74
|
-
plain_text = ToolBelt::Blowfish.decryptor( cipher_text, decryption_key )
|
75
|
-
|
76
|
-
data_structure = JSON.parse plain_text
|
77
|
-
self.merge! data_structure
|
78
|
-
|
79
|
-
end
|
80
|
-
|
81
|
-
|
82
|
-
# Write the data in this envelope hash map into a file-system
|
83
|
-
# backed mirror whose path was specified in the {self.read} method.
|
84
|
-
#
|
85
|
-
# Technology for encryption at rest is supported by this dictionary
|
86
|
-
# and to this aim, please endeavour to post a robust symmetric
|
87
|
-
# encryption key.
|
88
|
-
#
|
89
|
-
# Calling this {self.write} method when the file at the prescribed path
|
90
|
-
# does not exist results in the directory structure being created
|
91
|
-
# (if necessary) and then the encrypted file being written.
|
92
|
-
#
|
93
|
-
# @param encryption_key [String]
|
94
|
-
# encryption at rest is a given so this mandatory parameter must
|
95
|
-
# contain a robust symmetric encryption key. The symmetric key will
|
96
|
-
# be used for the decryption after the read. Note that the decryption
|
97
|
-
# key does not linger meaning it isn't cached in an instance variable.
|
98
|
-
def write encryption_key
|
99
|
-
|
100
|
-
FileUtils.mkdir_p(File.dirname(@filepath))
|
101
|
-
cipher_text = Base64.encode64 ToolBelt::Blowfish.encryptor( self.to_json, encryption_key )
|
102
|
-
File.write @filepath, cipher_text
|
103
|
-
|
104
|
-
puts ""
|
105
|
-
puts "=== ============================"
|
106
|
-
puts "=== Envelope State ============="
|
107
|
-
puts "=== ============================"
|
108
|
-
|
109
|
-
a_ini_file = IniFile.new
|
110
|
-
self.each_key do |section_name|
|
111
|
-
a_ini_file[section_name] = self[section_name]
|
112
|
-
end
|
113
|
-
puts a_ini_file.to_s
|
114
|
-
|
115
|
-
puts "=== ============================"
|
116
|
-
puts ""
|
117
|
-
|
118
|
-
end
|
119
|
-
|
120
|
-
|
121
|
-
end
|
122
|
-
|
123
|
-
|
124
|
-
end
|
125
|
-
|
126
|
-
|
127
|
-
end
|
@@ -1,170 +0,0 @@
|
|
1
|
-
#!/usr/bin/ruby
|
2
|
-
# coding: utf-8
|
3
|
-
|
4
|
-
module OpenSecret
|
5
|
-
|
6
|
-
module Mapper
|
7
|
-
|
8
|
-
require 'inifile'
|
9
|
-
require 'singleton'
|
10
|
-
|
11
|
-
# This class contains basic behaviour for managing a client only
|
12
|
-
# (serverless) session. Configuration directives are read and written
|
13
|
-
# from an INI off the home directory that is created when the session
|
14
|
-
# is first initiated.
|
15
|
-
class Settings
|
16
|
-
|
17
|
-
# Stash the setting directive and its value into the configuration file
|
18
|
-
# using the default settings group.
|
19
|
-
#
|
20
|
-
# The default settings group is resolved via {Collateral::CONTEXT_NAME}
|
21
|
-
#
|
22
|
-
# @param key_name [String] the name of the key whose value is to be written
|
23
|
-
# @param key_value [String] the data item value of the key specified
|
24
|
-
def self.stash key_name, key_value
|
25
|
-
write Collateral::CONTEXT_NAME, key_name, key_value
|
26
|
-
end
|
27
|
-
|
28
|
-
|
29
|
-
# Stash the setting directive and its value into the configuration file
|
30
|
-
# using the default settings group.
|
31
|
-
#
|
32
|
-
# The default settings group is resolved via {Collateral::CONTEXT_NAME}
|
33
|
-
#
|
34
|
-
# @param key_name [String] the name of the key whose value is to be written
|
35
|
-
# @return [String]
|
36
|
-
# return the value of the configuration directive in the default group
|
37
|
-
def self.grab key_name
|
38
|
-
read Collateral::CONTEXT_NAME, key_name
|
39
|
-
end
|
40
|
-
|
41
|
-
|
42
|
-
# Write the key/value pair in the parameter into the session's
|
43
|
-
# configuration INI file that lives in a context-named folder
|
44
|
-
# off the home directory.
|
45
|
-
#
|
46
|
-
# The session file will be in a folder whose name is simply
|
47
|
-
# the dot prefixed context_name. The session file itself will
|
48
|
-
# be named using context_name + @@filename_tail
|
49
|
-
#
|
50
|
-
# @example ~/.openbox/openbox-session.ini is the filepath for context "openbox"
|
51
|
-
#
|
52
|
-
# If neither the folder nor file exist, both are created.
|
53
|
-
# If the file did not exist a new one will with the contents
|
54
|
-
# (if the key is length and the value is 2m).
|
55
|
-
#
|
56
|
-
# [openbox]
|
57
|
-
# length = 2m
|
58
|
-
#
|
59
|
-
# If the file does already exist, an appropriate merge will be
|
60
|
-
# performed to create or update the section name, key name and
|
61
|
-
# value. The file may end up looking like
|
62
|
-
#
|
63
|
-
# [closedbox]
|
64
|
-
# shape = cuboid
|
65
|
-
# color = blue
|
66
|
-
#
|
67
|
-
# [openbox]
|
68
|
-
# length = 2m
|
69
|
-
# width = 3m
|
70
|
-
#
|
71
|
-
# @param section_name [String] name grouping the section of config values
|
72
|
-
# @param key [String] the key name of config directive to be written into the file
|
73
|
-
# @param value [String] value of the config directive to be written into the file
|
74
|
-
#
|
75
|
-
def self.write section_name, key, value
|
76
|
-
|
77
|
-
config_filepath = Collateral.instance.config_file
|
78
|
-
config_directory = Collateral.instance.config_directory
|
79
|
-
|
80
|
-
FileUtils.mkdir_p config_directory unless File.exists? config_directory
|
81
|
-
|
82
|
-
config_map = IniFile.new( :filename => config_filepath, :encoding => 'UTF-8' )
|
83
|
-
config_map = IniFile.load( config_filepath ) if File.exists? config_filepath
|
84
|
-
|
85
|
-
config_map[section_name][key] = value
|
86
|
-
config_map.write
|
87
|
-
|
88
|
-
end
|
89
|
-
|
90
|
-
|
91
|
-
# Given the configuration key name and the context name, get the
|
92
|
-
# corresponding key value from the configuration file whose path
|
93
|
-
# is acquired using the {self#get_filepath} method.
|
94
|
-
#
|
95
|
-
# @param key_name [String] the key whose value is to be retrieved
|
96
|
-
#
|
97
|
-
# @return [String] the value configured for the parameter key
|
98
|
-
#
|
99
|
-
# @raise ArgumentError for any one of a long list of reasons that
|
100
|
-
# cause the key value to not be retrieved. This can range from
|
101
|
-
# non-existent directories and files, non readable files, incorrect
|
102
|
-
# configurations right down to missing keys or even missing values.
|
103
|
-
def self.read section_name, key_name
|
104
|
-
|
105
|
-
the_data = get_inifile_data
|
106
|
-
|
107
|
-
section_exists = the_data.has_section?( section_name )
|
108
|
-
raise ArgumentError.new "Section [#{section_name}] not found in INI file => #{the_data.to_s}" unless section_exists
|
109
|
-
|
110
|
-
key_exists = the_data[ section_name ].has_key?( key_name )
|
111
|
-
raise ArgumentError.new "Key [#{key_name}] not found in section [#{section_name}] => #{the_data.to_s}" unless key_exists
|
112
|
-
|
113
|
-
rawvalue = the_data[section_name][key_name]
|
114
|
-
raise ArgumentError.new "Empty value 4 key [#{section_name}][#{key_name}] => #{the_data.to_s}" if rawvalue.empty?
|
115
|
-
|
116
|
-
keyvalue = rawvalue.chomp.strip
|
117
|
-
raise ArgumentError.new "Whitespace value 4 key [#{section_name}][#{key_name}] => #{the_data.to_s}" if keyvalue.empty?
|
118
|
-
|
119
|
-
return keyvalue
|
120
|
-
|
121
|
-
end
|
122
|
-
|
123
|
-
|
124
|
-
# Return true if the settings configuration file contains the specified
|
125
|
-
# key (in the 2nd parameter) within the section name specified in the
|
126
|
-
# first parameter.
|
127
|
-
#
|
128
|
-
# This method does not check the contents (value) of the key. Even if it
|
129
|
-
# is an empty string, this method returns true so long as the section
|
130
|
-
# exists and the key exists within that.
|
131
|
-
#
|
132
|
-
# @param section_name [String] the name of the section to search under
|
133
|
-
# @param key_name [String] the name of the key to test for existence
|
134
|
-
# @return [Boolean]
|
135
|
-
# return true if both the section and the key exists within it.
|
136
|
-
# return false if the section specified does not exist.
|
137
|
-
#
|
138
|
-
# raise [ArgumentError]
|
139
|
-
# if the INI file does not exist
|
140
|
-
def self.contains_key? section_name, key_name
|
141
|
-
|
142
|
-
the_data = get_inifile_data
|
143
|
-
|
144
|
-
return false unless the_data.has_section?( section_name )
|
145
|
-
return the_data[ section_name ].has_key?( key_name )
|
146
|
-
|
147
|
-
end
|
148
|
-
|
149
|
-
|
150
|
-
private
|
151
|
-
|
152
|
-
|
153
|
-
def self.get_inifile_data
|
154
|
-
|
155
|
-
the_file = Collateral.instance.config_file
|
156
|
-
raise ArgumentError.new "No configuration file found => [ #{the_file} ]" unless File.exists? the_file
|
157
|
-
|
158
|
-
the_text = File.read the_file
|
159
|
-
raise ArgumentError.new "Configuration file is empty => [ #{the_file} ]" if the_text.empty?
|
160
|
-
|
161
|
-
return IniFile.load the_file
|
162
|
-
|
163
|
-
end
|
164
|
-
|
165
|
-
end
|
166
|
-
|
167
|
-
end
|
168
|
-
|
169
|
-
|
170
|
-
end
|