safedb 0.01.0001
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/.yardopts +3 -0
- data/Gemfile +10 -0
- data/LICENSE +21 -0
- data/README.md +793 -0
- data/Rakefile +16 -0
- data/bin/safe +5 -0
- data/lib/configs/README.md +58 -0
- data/lib/extension/array.rb +162 -0
- data/lib/extension/dir.rb +35 -0
- data/lib/extension/file.rb +123 -0
- data/lib/extension/hash.rb +33 -0
- data/lib/extension/string.rb +572 -0
- data/lib/factbase/facts.safedb.net.ini +38 -0
- data/lib/interprete.rb +462 -0
- data/lib/keytools/PRODUCE_RAND_SEQ_USING_DEV_URANDOM.txt +0 -0
- data/lib/keytools/kdf.api.rb +243 -0
- data/lib/keytools/kdf.bcrypt.rb +265 -0
- data/lib/keytools/kdf.pbkdf2.rb +262 -0
- 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 +1391 -0
- data/lib/keytools/key.db.rb +330 -0
- data/lib/keytools/key.docs.rb +195 -0
- data/lib/keytools/key.error.rb +110 -0
- data/lib/keytools/key.id.rb +271 -0
- data/lib/keytools/key.ident.rb +243 -0
- data/lib/keytools/key.iv.rb +107 -0
- data/lib/keytools/key.local.rb +259 -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 +585 -0
- data/lib/logging/gem.logging.rb +132 -0
- data/lib/modules/README.md +43 -0
- data/lib/modules/cryptology/aes-256.rb +154 -0
- data/lib/modules/cryptology/amalgam.rb +70 -0
- data/lib/modules/cryptology/blowfish.rb +130 -0
- data/lib/modules/cryptology/cipher.rb +207 -0
- data/lib/modules/cryptology/collect.rb +138 -0
- data/lib/modules/cryptology/crypt.io.rb +225 -0
- data/lib/modules/cryptology/engineer.rb +99 -0
- data/lib/modules/mappers/dictionary.rb +288 -0
- data/lib/modules/storage/coldstore.rb +186 -0
- data/lib/modules/storage/git.store.rb +399 -0
- data/lib/session/fact.finder.rb +334 -0
- data/lib/session/require.gem.rb +112 -0
- data/lib/session/time.stamp.rb +340 -0
- data/lib/session/user.home.rb +49 -0
- data/lib/usecase/cmd.rb +487 -0
- data/lib/usecase/config/README.md +57 -0
- data/lib/usecase/docker/README.md +146 -0
- data/lib/usecase/docker/docker.rb +49 -0
- data/lib/usecase/edit/README.md +43 -0
- data/lib/usecase/edit/delete.rb +46 -0
- data/lib/usecase/export.rb +40 -0
- data/lib/usecase/files/README.md +37 -0
- data/lib/usecase/files/eject.rb +56 -0
- data/lib/usecase/files/file_me.rb +78 -0
- data/lib/usecase/files/read.rb +169 -0
- data/lib/usecase/files/write.rb +89 -0
- data/lib/usecase/goto.rb +57 -0
- data/lib/usecase/id.rb +36 -0
- data/lib/usecase/import.rb +157 -0
- data/lib/usecase/init.rb +63 -0
- data/lib/usecase/jenkins/README.md +146 -0
- data/lib/usecase/jenkins/jenkins.rb +208 -0
- data/lib/usecase/login.rb +71 -0
- data/lib/usecase/logout.rb +28 -0
- data/lib/usecase/open.rb +71 -0
- data/lib/usecase/print.rb +40 -0
- data/lib/usecase/put.rb +81 -0
- data/lib/usecase/set.rb +44 -0
- data/lib/usecase/show.rb +138 -0
- data/lib/usecase/terraform/README.md +91 -0
- data/lib/usecase/terraform/terraform.rb +121 -0
- data/lib/usecase/token.rb +35 -0
- data/lib/usecase/update/README.md +55 -0
- data/lib/usecase/update/rename.rb +180 -0
- data/lib/usecase/use.rb +41 -0
- data/lib/usecase/verse.rb +20 -0
- data/lib/usecase/view.rb +71 -0
- data/lib/usecase/vpn/README.md +150 -0
- data/lib/usecase/vpn/vpn.ini +31 -0
- data/lib/usecase/vpn/vpn.rb +54 -0
- data/lib/version.rb +3 -0
- data/safedb.gemspec +34 -0
- metadata +193 -0
@@ -0,0 +1,259 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
# coding: utf-8
|
3
|
+
|
4
|
+
module SafeDb
|
5
|
+
|
6
|
+
require 'inifile'
|
7
|
+
|
8
|
+
# KeyPair is a <b>key-value</b> store backed by a plain-text file in
|
9
|
+
# an <b>INI format</b> that sits on an accessible file-system.
|
10
|
+
#
|
11
|
+
#
|
12
|
+
# == Example Data Exchange
|
13
|
+
#
|
14
|
+
# Issue the below ruby calls and specify a /path/to/file
|
15
|
+
#
|
16
|
+
# keymap = KeyPair.new "/path/to/file"
|
17
|
+
#
|
18
|
+
# keymap.use "phone_numbers"
|
19
|
+
# keymap.set "joe", "0044 7500 123456"
|
20
|
+
# keymap.set "amy", "0044 7678 123456"
|
21
|
+
#
|
22
|
+
# Now visit the file to see your exchanged data.
|
23
|
+
#
|
24
|
+
# [phone_numbers]
|
25
|
+
# joe = 0044 7500 123456
|
26
|
+
# amy = 0044 7678 123456
|
27
|
+
#
|
28
|
+
#
|
29
|
+
# == The <em>Current</em> Section
|
30
|
+
#
|
31
|
+
# You can set the <b>current section</b> with the {use} method and then
|
32
|
+
# subsequent read, write, or query behaviour will reference the section that
|
33
|
+
# you stated.
|
34
|
+
#
|
35
|
+
# You do not need a new object to switch sections - just go ahead and
|
36
|
+
# use another another one.
|
37
|
+
#
|
38
|
+
# Remember that KeyPair is <b>two-dimensional</b> data structure so all
|
39
|
+
# key-value pairs are stored under the auspices of a section.
|
40
|
+
#
|
41
|
+
# == Key-Value Pair Exchanges
|
42
|
+
#
|
43
|
+
# Representational state transfer occurs with four methods with
|
44
|
+
#
|
45
|
+
# - custom sections referenced through {read} and {write}
|
46
|
+
# - said sections transfered via ubiquitous {get} and {set}
|
47
|
+
#
|
48
|
+
# The name given to the default group can be specified to the constructor.
|
49
|
+
# If none is provided the aptly named "default" is used.
|
50
|
+
class KeyPair
|
51
|
+
|
52
|
+
# Initialize the key value store and auto write a time stamp that
|
53
|
+
# has nano-second accuracy with a key whose name is gleened from
|
54
|
+
# the constant {KeyData::INIT_TIME_STAMP_NAME}.
|
55
|
+
#
|
56
|
+
# The path to the backing INI file is gleened from the first
|
57
|
+
# backing file path parameter.
|
58
|
+
#
|
59
|
+
# @param backing_file_path [String]
|
60
|
+
# the expected location of the file-backed key-value store.
|
61
|
+
# If the folder and/or file do not exist the folder is created
|
62
|
+
# and then the file is created along with the time stamps.
|
63
|
+
#
|
64
|
+
# @param the_default_group [String]
|
65
|
+
# the name of the default group. If none is presented this value
|
66
|
+
# will default to the aptly named "default".
|
67
|
+
def initialize backing_file_path
|
68
|
+
@file_path = backing_file_path
|
69
|
+
create_dir_if_necessary
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
# Set the section to use for future data exchanges via the ubiquitous {get}
|
74
|
+
# and {set} methods as well as the query {contains} key method.
|
75
|
+
#
|
76
|
+
# @param the_section_name [String]
|
77
|
+
# the non-nil and non whitespace only section name that will lead a
|
78
|
+
# set of key-value pairs in the INI formatted file.
|
79
|
+
def use the_section_name
|
80
|
+
raise ArgumentError, "Cannot use a Nil section name." if the_section_name.nil?
|
81
|
+
@section_to_use = the_section_name
|
82
|
+
end
|
83
|
+
|
84
|
+
# Stash the setting directive and its value into the configuration file
|
85
|
+
# using the default settings group.
|
86
|
+
#
|
87
|
+
# @param key_name [String] the name of the key whose value is to be written
|
88
|
+
# @param key_value [String] the data item value of the key specified
|
89
|
+
def set key_name, key_value
|
90
|
+
raise ArgumentError, "Cannot set a Nil section name." if @section_to_use.nil?
|
91
|
+
write @section_to_use, key_name, key_value
|
92
|
+
end
|
93
|
+
|
94
|
+
|
95
|
+
# Stash the setting directive and its value into the configuration file
|
96
|
+
# using the default settings group.
|
97
|
+
#
|
98
|
+
# @param key_name [String] the name of the key whose value is to be written
|
99
|
+
# @return [String]
|
100
|
+
# return the value of the configuration directive in the default group
|
101
|
+
def get key_name
|
102
|
+
raise ArgumentError, "Cannot get from a Nil section name." if @section_to_use.nil?
|
103
|
+
read @section_to_use, key_name
|
104
|
+
end
|
105
|
+
|
106
|
+
|
107
|
+
# Write the key/value pair in the parameter into this key/value store's
|
108
|
+
# base file-system backing INI file.
|
109
|
+
#
|
110
|
+
# This method assumes the existence of the backing configuration file at
|
111
|
+
# the @file_path instance variable that was set during initialization.
|
112
|
+
#
|
113
|
+
# Observable value is the written key/value pair within the specified
|
114
|
+
# section. The alternate flows are
|
115
|
+
#
|
116
|
+
# - if the section does not exist it is created
|
117
|
+
# - if the section and key exist the value is inserted or overwritten
|
118
|
+
#
|
119
|
+
# @param section_name [String] name grouping the section of config values
|
120
|
+
# @param key [String] the key name of config directive to be written into the file
|
121
|
+
# @param value [String] value of the config directive to be written into the file
|
122
|
+
#
|
123
|
+
def write section_name, key, value
|
124
|
+
|
125
|
+
config_map = IniFile.new( :filename => @file_path, :encoding => 'UTF-8' )
|
126
|
+
config_map = IniFile.load( @file_path ) if File.file? @file_path
|
127
|
+
config_map[section_name][key] = value
|
128
|
+
config_map.write
|
129
|
+
|
130
|
+
end
|
131
|
+
|
132
|
+
|
133
|
+
# Given the configuration key name and the context name, get the
|
134
|
+
# corresponding key value from the configuration file whose path
|
135
|
+
# is acquired using the {self#get_filepath} method.
|
136
|
+
#
|
137
|
+
# @param key_name [String] the key whose value is to be retrieved
|
138
|
+
#
|
139
|
+
# @return [String] the value configured for the parameter key
|
140
|
+
#
|
141
|
+
# @raise ArgumentError for any one of a long list of reasons that
|
142
|
+
# cause the key value to not be retrieved. This can range from
|
143
|
+
# non-existent directories and files, non readable files, incorrect
|
144
|
+
# configurations right down to missing keys or even missing values.
|
145
|
+
def read section_name, key_name
|
146
|
+
|
147
|
+
raise ArgumentError.new "No section given." if section_name.nil? || section_name.strip.empty?
|
148
|
+
raise ArgumentError.new "No parameter key given." if key_name.nil? || key_name.strip.empty?
|
149
|
+
raise ArgumentError.new "No file found at [ #{@file_path} ]" unless File.exists? @file_path
|
150
|
+
the_text = File.read @file_path
|
151
|
+
raise ArgumentError.new "This file is empty => [ #{@file_path} ]" if the_text.empty?
|
152
|
+
|
153
|
+
the_data = IniFile.load @file_path
|
154
|
+
key_exists = the_data[ section_name ].has_key?( key_name )
|
155
|
+
key_err_msg = "Key [#{key_name}] not found in section [#{section_name}]"
|
156
|
+
raise ArgumentError, key_err_msg unless key_exists
|
157
|
+
|
158
|
+
rawvalue = the_data[section_name][key_name]
|
159
|
+
key_val_msg = "Nil empty or whitespace value for key [#{section_name}][#{key_name}]"
|
160
|
+
nil_empty_or_whitespace = rawvalue.nil? || rawvalue.chomp.strip.empty?
|
161
|
+
raise ArgumentError, key_val_msg if nil_empty_or_whitespace
|
162
|
+
|
163
|
+
return rawvalue.chomp.strip
|
164
|
+
|
165
|
+
end
|
166
|
+
|
167
|
+
|
168
|
+
# Return true if the settings configuration file contains the specified
|
169
|
+
# parameter key within the current section name that has been set via
|
170
|
+
# the {use} method.
|
171
|
+
#
|
172
|
+
# This method does not check the contents (value) of the key. Even if it
|
173
|
+
# is an empty string, this method returns true so long as the section
|
174
|
+
# exists and the key exists within that.
|
175
|
+
#
|
176
|
+
# @param key_name [String]
|
177
|
+
# does a key with this name exist within the current map section.
|
178
|
+
#
|
179
|
+
# @return [Boolean]
|
180
|
+
# return true if the current section exists and a key with the parameter
|
181
|
+
# name exists within it.
|
182
|
+
# return false if <b>either</b> the section <b>or</b> the key do not exist.
|
183
|
+
#
|
184
|
+
# raise [ArgumentError]
|
185
|
+
# if the configuration file does not exist or is empty
|
186
|
+
# if the paramter key_name is nil, empty or contains only whitespace
|
187
|
+
def contains? key_name
|
188
|
+
|
189
|
+
raise ArgumentError.new "No parameter key given." if key_name.nil? || key_name.strip.empty?
|
190
|
+
raise ArgumentError.new "No file found at [ #{@file_path} ]" unless File.exists? @file_path
|
191
|
+
the_text = File.read @file_path
|
192
|
+
raise ArgumentError.new "This file is empty => [ #{@file_path} ]" if the_text.empty?
|
193
|
+
|
194
|
+
the_data = IniFile.load @file_path
|
195
|
+
return false unless the_data.has_section?( @section_to_use )
|
196
|
+
return the_data[ @section_to_use ].has_key?( key_name )
|
197
|
+
|
198
|
+
end
|
199
|
+
|
200
|
+
|
201
|
+
|
202
|
+
# Return true if the settings configuration file contains the specified
|
203
|
+
# section name. This method ignores whatever section that may or may not
|
204
|
+
# have been pointed to by the use command.
|
205
|
+
#
|
206
|
+
# @param section_name [String]
|
207
|
+
# does a section with this name exist within the file data structure
|
208
|
+
#
|
209
|
+
# @return [Boolean]
|
210
|
+
# return true if a section exists with the specified name
|
211
|
+
def has_section? section_name
|
212
|
+
|
213
|
+
KeyError.not_new( section_name, self )
|
214
|
+
|
215
|
+
raise ArgumentError.new "No file found at [ #{@file_path} ]" unless File.exists? @file_path
|
216
|
+
the_text = File.read @file_path
|
217
|
+
raise ArgumentError.new "This file is empty => [ #{@file_path} ]" if the_text.empty?
|
218
|
+
|
219
|
+
the_data = IniFile.load @file_path
|
220
|
+
return the_data.has_section?( section_name )
|
221
|
+
|
222
|
+
end
|
223
|
+
|
224
|
+
|
225
|
+
|
226
|
+
# Get the time stamp that was written to the key-value store at
|
227
|
+
# the point it was first initialized and then subsequently written
|
228
|
+
# out (serialized) onto the file-system.
|
229
|
+
#
|
230
|
+
# The time stamp returned marks the first time this key-value store
|
231
|
+
# was conceived by a use case actor and subsequently serialized.
|
232
|
+
#
|
233
|
+
# @return [String]
|
234
|
+
# the string time stamp denoting the first time this key-value
|
235
|
+
# store was first initialized and then subsequently written out
|
236
|
+
# (serialized) onto the file-system.
|
237
|
+
def time_stamp
|
238
|
+
return get INIT_TIME_STAMP_NAME
|
239
|
+
end
|
240
|
+
|
241
|
+
|
242
|
+
|
243
|
+
private
|
244
|
+
|
245
|
+
|
246
|
+
|
247
|
+
def create_dir_if_necessary
|
248
|
+
|
249
|
+
config_directory = File.dirname @file_path
|
250
|
+
return if (File.exist? config_directory) && (File.directory? config_directory)
|
251
|
+
FileUtils.mkdir_p config_directory
|
252
|
+
|
253
|
+
end
|
254
|
+
|
255
|
+
|
256
|
+
end
|
257
|
+
|
258
|
+
|
259
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
# coding: utf-8
|
3
|
+
|
4
|
+
module SafeDb
|
5
|
+
|
6
|
+
class KeyPass
|
7
|
+
|
8
|
+
|
9
|
+
# <tt>Collect something sensitive from the command line</tt> with a
|
10
|
+
# minimum length specified in the first parameter. This method can't
|
11
|
+
# know whether the information is a password, a pin number or whatever
|
12
|
+
# so it takes the integer minimum size at its word.
|
13
|
+
#
|
14
|
+
# <b>Question 5 to App Config | What is the Secret?</b>
|
15
|
+
#
|
16
|
+
# The client may need to acquire the secret if the answer to question 4 indicates the need
|
17
|
+
# to instantiate the keys and encrypt the application's plaintext database. The application
|
18
|
+
# should facilitate communication of the secret via
|
19
|
+
#
|
20
|
+
# - an environment variable
|
21
|
+
# - the system clipboard (cleared after reading)
|
22
|
+
# - a file whose path is a command parameter
|
23
|
+
# - a file in a pre-agreed location
|
24
|
+
# - a file in the present directory (with a pre-agreed name)
|
25
|
+
# - a URL from a parameter or pre-agreed
|
26
|
+
# - the shell's secure password reader
|
27
|
+
# - the DConf / GConf or GSettings configuration stores
|
28
|
+
# - a REST API
|
29
|
+
# - password managers like LastPass, KeePassX or 1Pass
|
30
|
+
# - the Amazon KMS (Key Management Store)
|
31
|
+
# - vaults from Ansible, Terraform and Kubernetes
|
32
|
+
# - credential managers like GitSecrets and Credstash
|
33
|
+
#
|
34
|
+
# @param prompt_twice [Boolean] indicate whether the user should be
|
35
|
+
# prompted twice. If true the prompt_2 text must be provided and
|
36
|
+
# converse is also true. A true value asserts that both times the
|
37
|
+
# user enters the same (case sensitive) string.
|
38
|
+
#
|
39
|
+
# @return [String] the collected string text ( watch out for non-ascii chars)
|
40
|
+
# @raise [ArgumentError] if the minimum size is less than one
|
41
|
+
def self.password_from_shell prompt_twice
|
42
|
+
|
43
|
+
assert_min_size MINIMUM_PASSWORD_SIZE
|
44
|
+
|
45
|
+
sleep(1)
|
46
|
+
puts "Password:"
|
47
|
+
first_secret = STDIN.noecho(&:gets).chomp
|
48
|
+
|
49
|
+
assert_input_text_size first_secret.length, MINIMUM_PASSWORD_SIZE
|
50
|
+
return first_secret unless prompt_twice
|
51
|
+
|
52
|
+
sleep(1)
|
53
|
+
puts "Re-enter the password:"
|
54
|
+
check_secret = STDIN.noecho(&:gets).chomp
|
55
|
+
|
56
|
+
assert_same_size_text first_secret, check_secret
|
57
|
+
|
58
|
+
return first_secret
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
# --
|
64
|
+
# -- Raise an exception if asked to collect text that is less
|
65
|
+
# -- than 3 characters in length.
|
66
|
+
# --
|
67
|
+
def self.assert_min_size min_size
|
68
|
+
|
69
|
+
min_length_msg = "\n\nCrypts with 2 (or less) characters open up exploitable holes.\n\n"
|
70
|
+
raise ArgumentError.new min_length_msg if min_size < 3
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
# --
|
76
|
+
# -- Output an error message and then exit if the entered input
|
77
|
+
# -- text size does not meet the minimum requirements.
|
78
|
+
# --
|
79
|
+
def self.assert_input_text_size input_size, min_size
|
80
|
+
|
81
|
+
if( input_size < min_size )
|
82
|
+
|
83
|
+
puts
|
84
|
+
puts "Input is too short. Please enter at least #{min_size} characters."
|
85
|
+
puts
|
86
|
+
|
87
|
+
exit
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
# --
|
95
|
+
# -- Assert that the text entered the second time is exactly (case sensitive)
|
96
|
+
# -- the same as the text entered the first time.
|
97
|
+
# --
|
98
|
+
def self.assert_same_size_text first_text, second_text
|
99
|
+
|
100
|
+
unless( first_text.eql? second_text )
|
101
|
+
|
102
|
+
puts
|
103
|
+
puts "Those two bits of text are not the same (in my book)!"
|
104
|
+
puts
|
105
|
+
|
106
|
+
exit
|
107
|
+
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
|
114
|
+
MINIMUM_PASSWORD_SIZE = 4
|
115
|
+
|
116
|
+
|
117
|
+
end
|
118
|
+
|
119
|
+
|
120
|
+
end
|