opensecret 0.0.988 → 0.0.9925
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.
- 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
|