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
|
+
# The command line interface has a high entropy randomly generated
|
7
|
+
# key whose purpose is to <b>lock the application's data key</b> for
|
8
|
+
# the duration of the session which is between a login and a logout.
|
9
|
+
#
|
10
|
+
# These keys are unique to only one shell session on one workstation
|
11
|
+
# and they live lives that are no longer (and mostly shorter) than
|
12
|
+
# the life of the parent shell.
|
13
|
+
#
|
14
|
+
# == The 4 CLI Shell Entities
|
15
|
+
#
|
16
|
+
# The four (4) important entities within the shell session are
|
17
|
+
#
|
18
|
+
# - an obfuscator key for locking the shell key during a session
|
19
|
+
# - a high entropy randomly generated shell key for locking the app data key
|
20
|
+
# - one environment variable whose value embodies three (3) data segments
|
21
|
+
# - a session id derived by pushing the env var through a one-way function
|
22
|
+
class KeyLocal
|
23
|
+
|
24
|
+
|
25
|
+
# The number of Radix64 characters that make up a valid BCrypt salt.
|
26
|
+
# To create a BCrypt salt use
|
27
|
+
BCRYPT_SALT_LENGTH = 22
|
28
|
+
|
29
|
+
|
30
|
+
# There are two digits representing the BCrypt iteration count.
|
31
|
+
# The minimum is 10 and the maximum is 16.
|
32
|
+
BCRYPT_ITER_COUNT_SIZE = 2
|
33
|
+
|
34
|
+
|
35
|
+
# The session token comprises of 3 segments with fixed lengths.
|
36
|
+
# This triply segmented text token that can be used to decrypt
|
37
|
+
# and deliver the shell key.
|
38
|
+
SESSION_TOKEN_SIZE = 128 + 22 + BCRYPT_ITER_COUNT_SIZE
|
39
|
+
|
40
|
+
|
41
|
+
# Given a 152 character session token, what is the index that pinpoints
|
42
|
+
# the beginning of the 22 character BCrypt salt? The answer is given
|
43
|
+
# by this BCRYPT_SALT_START_INDEX constant.
|
44
|
+
BCRYPT_SALT_START_INDEX = SESSION_TOKEN_SIZE - BCRYPT_SALT_LENGTH - BCRYPT_ITER_COUNT_SIZE
|
45
|
+
|
46
|
+
|
47
|
+
# What index pinpoints the end of the BCrypt salt itself.
|
48
|
+
# This is easy as the final 2 characters are the iteration count
|
49
|
+
# so the end index is the length subtract 1 subtract 2.
|
50
|
+
BCRYPT_SALT_END_INDEX = SESSION_TOKEN_SIZE - 1
|
51
|
+
|
52
|
+
|
53
|
+
# Initialize the session by generating a random high entropy shell token
|
54
|
+
# and then generate an obfuscator key which we use to lock the shell
|
55
|
+
# key and return a triply segmented text token that can be used to decrypt
|
56
|
+
# and deliver the shell key as long as the same shell on the same machine
|
57
|
+
# is employed to make the call.
|
58
|
+
#
|
59
|
+
# <b>The 3 Session Token Segments</b>
|
60
|
+
#
|
61
|
+
# The session token is divided up into 3 segments with a total of 150
|
62
|
+
# characters.
|
63
|
+
#
|
64
|
+
# | -------- | ------------ | ------------------------------------- |
|
65
|
+
# | Segment | Length | Purpose |
|
66
|
+
# | -------- | ------------ | ------------------------------------- |
|
67
|
+
# | 1 | 16 bytes | AES Encrypt Initialization Vector(IV) |
|
68
|
+
# | 2 | 80 bytes | Cipher text from Random Key AES crypt |
|
69
|
+
# | 3 | 22 chars | Salt for obfuscator key derivation |
|
70
|
+
# | -------- | ------------ | ------------------------------------- |
|
71
|
+
# | Total | 150 chars | Session Token in Environment Variable |
|
72
|
+
# | -------- | ------------ | ------------------------------------- |
|
73
|
+
#
|
74
|
+
# Why is the <b>16 byte salt and the 80 byte BCrypt ciphertext</b> represented
|
75
|
+
# by <b>128 base64 characters</b>?
|
76
|
+
#
|
77
|
+
# 16 bytes + 80 bytes = 96 bytes
|
78
|
+
# 96 bytes x 8 bits = 768 bits
|
79
|
+
# 768 bits / 6 bits = 128 base64 characters
|
80
|
+
#
|
81
|
+
# @return [String]
|
82
|
+
# return a triply segmented text token that can be used to decrypt
|
83
|
+
# and redeliver the high entropy session shell key on the same machine
|
84
|
+
# and within the same shell on the same machine.
|
85
|
+
def self.generate_shell_key_and_token
|
86
|
+
|
87
|
+
bcrypt_salt_key = KdfBCrypt.generate_bcrypt_salt
|
88
|
+
obfuscator_key = derive_session_crypt_key( bcrypt_salt_key )
|
89
|
+
random_key_ciphertext = obfuscator_key.do_encrypt_key( Key.from_random() )
|
90
|
+
session_token = random_key_ciphertext + bcrypt_salt_key.reverse
|
91
|
+
assert_session_token_size( session_token )
|
92
|
+
|
93
|
+
return session_token
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
# Regenerate the random shell key that was instantiated and locked
|
99
|
+
# during the {instantiate_shell_key_and_generate_token} method.
|
100
|
+
#
|
101
|
+
# To successfully reacquire the randomly generated (and then locked)
|
102
|
+
# shell key we must be provided with five (5) data points, four (4)
|
103
|
+
# of which are embalmed within the 150 character session token
|
104
|
+
# parameter.
|
105
|
+
#
|
106
|
+
# <b>What we need to Regenerate the Shell Key</b>
|
107
|
+
#
|
108
|
+
# Regenerating the shell key is done in two steps when given the
|
109
|
+
# four (4) <b>session token segments</b> described below, and the
|
110
|
+
# shell identity key described in the {SafeDb::Identifier} class.
|
111
|
+
#
|
112
|
+
# The session token is divided up into 4 segments with a total of 152
|
113
|
+
# characters.
|
114
|
+
#
|
115
|
+
# | -------- | ------------ | ------------------------------------- |
|
116
|
+
# | Segment | Length | Purpose |
|
117
|
+
# | -------- | ------------ | ------------------------------------- |
|
118
|
+
# | 1 | 16 bytes | AES Encrypt Initialization Vector(IV) |
|
119
|
+
# | 2 | 80 bytes | Cipher text from Random Key AES crypt |
|
120
|
+
# | 3 | 22 chars | Salt 4 shell identity key derivation |
|
121
|
+
# | 4 | 2 chars | BCrypt iteration parameter (10 to 16) |
|
122
|
+
# | -------- | ------------ | ------------------------------------- |
|
123
|
+
# | Total | 152 chars | Session Token in Environment Variable |
|
124
|
+
# | -------- | ------------ | ------------------------------------- |
|
125
|
+
#
|
126
|
+
# @param session_token [String]
|
127
|
+
# a triply segmented (and one liner) text token instantiated by
|
128
|
+
# {self.instantiate_shell_key_and_generate_token} and provided
|
129
|
+
# here ad verbatim.
|
130
|
+
#
|
131
|
+
# @param use_grandparent_pid [Boolean]
|
132
|
+
#
|
133
|
+
# Optional boolean parameter. If set to true the PID (process ID) used
|
134
|
+
# as part of an obfuscator key and normally acquired from the parent
|
135
|
+
# process should now be acquired from the grandparent's process.
|
136
|
+
#
|
137
|
+
# Set to true when accessing the safe's credentials from a sub process
|
138
|
+
# rather than directly through the logged in shell.
|
139
|
+
#
|
140
|
+
# @return [SafeDb::Key]
|
141
|
+
# an extremely high entropy 256 bit key derived (digested) from 48
|
142
|
+
# random bytes at the beginning of the shell (cli) session.
|
143
|
+
def self.regenerate_shell_key( session_token, use_grandparent_pid = false )
|
144
|
+
|
145
|
+
assert_session_token_size( session_token )
|
146
|
+
bcrypt_salt = session_token[ BCRYPT_SALT_START_INDEX .. BCRYPT_SALT_END_INDEX ].reverse
|
147
|
+
assert_bcrypt_salt_size( bcrypt_salt )
|
148
|
+
|
149
|
+
key_ciphertext = session_token[ 0 .. ( BCRYPT_SALT_START_INDEX - 1 ) ]
|
150
|
+
obfuscator_key = derive_session_crypt_key( bcrypt_salt, use_grandparent_pid )
|
151
|
+
regenerated_key = obfuscator_key.do_decrypt_key( key_ciphertext )
|
152
|
+
|
153
|
+
return regenerated_key
|
154
|
+
|
155
|
+
end
|
156
|
+
|
157
|
+
|
158
|
+
# Derive a <b>short term (session scoped) encryption key</b> from the
|
159
|
+
# surrounding shell and workstation (machine) environment with an
|
160
|
+
# important same/different guarantee.
|
161
|
+
#
|
162
|
+
# The <b>same / different guarantee promises</b> us that the derived
|
163
|
+
# key will be
|
164
|
+
#
|
165
|
+
# - <b>the same</b> whenever called from within this executing shell
|
166
|
+
# - <b>different</b> when the shell and/or workstation are different
|
167
|
+
#
|
168
|
+
# This method uses a one-way function to return a combinatorial digested
|
169
|
+
# session identification string using a number of distinct parameters that
|
170
|
+
# deliver the important behaviours of changing in certain circumstances
|
171
|
+
# and remaining unchanged in others.
|
172
|
+
#
|
173
|
+
# <b>Change | When Should the key Change?</b>
|
174
|
+
#
|
175
|
+
# What is really important is that the <b>key changes when</b>
|
176
|
+
#
|
177
|
+
# - the <b>command shell</b> changes
|
178
|
+
# - the workstation <b>shell user is switched</b>
|
179
|
+
# - the host machine <b>workstation</b> is changed
|
180
|
+
# - the user <b>SSH's</b> into another shell
|
181
|
+
#
|
182
|
+
# A distinct workstation is identified by the first MAC address and the
|
183
|
+
# hostname of the machine.
|
184
|
+
#
|
185
|
+
# <b>Unchanged | When Should the Key Remain Unchanged?</b>
|
186
|
+
#
|
187
|
+
# Remaining <b>unchanged</b> in certain scenarios is a feature that is
|
188
|
+
# just as important as changing in others. The key must remain
|
189
|
+
# <b>unchanged</b> when
|
190
|
+
#
|
191
|
+
# - the <b>user returns to a command shell</b>
|
192
|
+
# - the user exits their <b>remote SSH session</b>
|
193
|
+
# - <b>sudo is used</b> to execute the commands
|
194
|
+
# - the user comes back to their <b>workstation</b>
|
195
|
+
# - the clock ticks into another day, month, year ...
|
196
|
+
#
|
197
|
+
# @param bcrypt_salt_key [SafeDb::Key]
|
198
|
+
#
|
199
|
+
# Either use BCrypt to generate the salt or retrieve and post in a
|
200
|
+
# previously generated salt which must hold 22 printable characters.
|
201
|
+
#
|
202
|
+
# @param use_grandparent_pid [Boolean]
|
203
|
+
#
|
204
|
+
# Optional boolean parameter. If set to true the PID (process ID) used
|
205
|
+
# as part of an obfuscator key and normally acquired from the parent
|
206
|
+
# process should now be acquired from the grandparent's process.
|
207
|
+
#
|
208
|
+
# Set to true when accessing the safe's credentials from a sub process
|
209
|
+
# rather than directly through the logged in shell.
|
210
|
+
#
|
211
|
+
# @return [SafeDb::Key]
|
212
|
+
# a digested key suitable for short term (session scoped) use with the
|
213
|
+
# guarantee that the same key will be returned whenever called from within
|
214
|
+
# the same executing shell environment and a different key when not.
|
215
|
+
def self.derive_session_crypt_key bcrypt_salt_key, use_grandparent_pid = false
|
216
|
+
|
217
|
+
shell_id_text = KeyIdent.derive_shell_identifier( use_grandparent_pid )
|
218
|
+
truncate_text = shell_id_text.length > KdfBCrypt::BCRYPT_MAX_IN_TEXT_LENGTH
|
219
|
+
shell_id_trim = shell_id_text unless truncate_text
|
220
|
+
shell_id_trim = shell_id_text[ 0 .. ( KdfBCrypt::BCRYPT_MAX_IN_TEXT_LENGTH - 1 ) ] if truncate_text
|
221
|
+
|
222
|
+
return KdfBCrypt.generate_key( shell_id_trim, bcrypt_salt_key )
|
223
|
+
|
224
|
+
end
|
225
|
+
|
226
|
+
|
227
|
+
private
|
228
|
+
|
229
|
+
|
230
|
+
# 000000000000000000000000000000000000000000000000000000000000000
|
231
|
+
# How to determine the caller.
|
232
|
+
# Better strategy would be just to print the stack trace
|
233
|
+
# That gives you much more bang for the one line buck.
|
234
|
+
# 000000000000000000000000000000000000000000000000000000000000000
|
235
|
+
# calling_module = File.basename caller_locations(1,1).first.absolute_path, ".rb"
|
236
|
+
# calling_method = caller_locations(1,1).first.base_label
|
237
|
+
# calling_lineno = caller_locations(1,1).first.lineno
|
238
|
+
# caller_details = "#{calling_module} | #{calling_method} | (line #{calling_lineno})"
|
239
|
+
# log.info(x) { "### Caller Details =>> =>> #{caller_details}" }
|
240
|
+
# 000000000000000000000000000000000000000000000000000000000000000
|
241
|
+
|
242
|
+
|
243
|
+
def self.assert_session_token_size session_token
|
244
|
+
err_msg = "Session token has #{session_token.length} and not #{SESSION_TOKEN_SIZE} chars."
|
245
|
+
raise RuntimeError, err_msg unless session_token.length == SESSION_TOKEN_SIZE
|
246
|
+
end
|
247
|
+
|
248
|
+
|
249
|
+
def self.assert_bcrypt_salt_size bcrypt_salt
|
250
|
+
amalgam_length = BCRYPT_SALT_LENGTH + BCRYPT_ITER_COUNT_SIZE
|
251
|
+
err_msg = "Expected BCrypt salt length of #{amalgam_length} not #{bcrypt_salt.length}."
|
252
|
+
raise RuntimeError, err_msg unless bcrypt_salt.length == amalgam_length
|
253
|
+
end
|
254
|
+
|
255
|
+
|
256
|
+
end
|
257
|
+
|
258
|
+
|
259
|
+
end
|
@@ -0,0 +1,402 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
module SafeDb
|
4
|
+
|
5
|
+
require 'singleton'
|
6
|
+
|
7
|
+
# This stamp sits at the centre of a fundamental DevOps pattern concerned
|
8
|
+
# with infrastructure provisioning and configuraion management.
|
9
|
+
#
|
10
|
+
# The central idea behind the pattern is to link every infrastructure
|
11
|
+
# object created during a session with a reference accurate to the nearest
|
12
|
+
# centi-second denoting the moment the software runtime (session) began.
|
13
|
+
class KeyNow
|
14
|
+
include Singleton
|
15
|
+
|
16
|
+
attr_reader :time_now
|
17
|
+
|
18
|
+
# Return two digit [mo] month index from 01 to 12.
|
19
|
+
# @example 02 => in February
|
20
|
+
#
|
21
|
+
def self.mo
|
22
|
+
return KeyNow.instance.time_now.strftime "%m"
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
# Return three character abbreviated month name.
|
27
|
+
# @example feb => in February
|
28
|
+
#
|
29
|
+
def self.mmm
|
30
|
+
return KeyNow.instance.time_now.strftime( "%b" ).downcase
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
#
|
35
|
+
# Return three character abbreviated day of week.
|
36
|
+
# @example tue => on Tuesday
|
37
|
+
#
|
38
|
+
def self.ddd
|
39
|
+
return KeyNow.instance.time_now.strftime( "%a" ).downcase
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
#
|
44
|
+
# Return two digit (character) hour of day from 00 to 23.
|
45
|
+
# @example 22 => between 22.00.00 and 22.59.59 inclusive
|
46
|
+
#
|
47
|
+
def self.hh
|
48
|
+
return KeyNow.instance.time_now.strftime "%H"
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
#
|
53
|
+
# Return two digit minute of hour from [00] to [59].
|
54
|
+
#
|
55
|
+
def self.mm
|
56
|
+
return KeyNow.instance.time_now.strftime "%M"
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
#
|
61
|
+
# Return two digit second of minute from [00] to [59].
|
62
|
+
#
|
63
|
+
def self.ss
|
64
|
+
return KeyNow.instance.time_now.strftime "%S"
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
#
|
69
|
+
# Return a [3 digit] second and tenth of second
|
70
|
+
# representation.
|
71
|
+
#
|
72
|
+
# The final digit is derived from the 1000 sliced
|
73
|
+
# millisecond of second running from 000 to 999.
|
74
|
+
#
|
75
|
+
# <tt>Truncation (Not Rounding)</tt>
|
76
|
+
#
|
77
|
+
# The [final] digit is acquired by TRUNCATING
|
78
|
+
# (chopping off) the last 2 of the 3 millisecond
|
79
|
+
# digits. No rounding is applied.
|
80
|
+
#
|
81
|
+
# The 3 returned digits comprise of the
|
82
|
+
#
|
83
|
+
# - second of minute => 2 digits | [00] to [59] (and)
|
84
|
+
# - tenth of second => 1 digit from [0] to [9]
|
85
|
+
#
|
86
|
+
# @example
|
87
|
+
#
|
88
|
+
# => The time at the 562nd millisecond of the 49th
|
89
|
+
# second of the minute.
|
90
|
+
#
|
91
|
+
# => 3 chars
|
92
|
+
# => 495
|
93
|
+
#
|
94
|
+
#
|
95
|
+
def self.sst
|
96
|
+
millisec_string = KeyNow.instance.time_now.strftime "%L"
|
97
|
+
return "#{ss}#{millisec_string[0]}"
|
98
|
+
end
|
99
|
+
|
100
|
+
|
101
|
+
#
|
102
|
+
# Return the [two] digit year (eg 19 for 2019).
|
103
|
+
# that we are currently in.
|
104
|
+
#
|
105
|
+
def self.yy
|
106
|
+
return KeyNow.instance.time_now.strftime("%Y")[2..-1]
|
107
|
+
end
|
108
|
+
|
109
|
+
|
110
|
+
#
|
111
|
+
# Return the [four] digit year (eg 2019)
|
112
|
+
# that we are currently in.
|
113
|
+
#
|
114
|
+
def self.yyyy
|
115
|
+
return KeyNow.instance.time_now.strftime("%Y")
|
116
|
+
end
|
117
|
+
|
118
|
+
|
119
|
+
# ------------------------------------------------- -- #
|
120
|
+
# Return 3 digit julian day of year [001] to [366]. -- #
|
121
|
+
# ------------------------------------------------- -- #
|
122
|
+
def self.jjj
|
123
|
+
return KeyNow.instance.time_now.strftime "%j"
|
124
|
+
end
|
125
|
+
|
126
|
+
|
127
|
+
# [yymo_mmm] returns an amalgam of
|
128
|
+
#
|
129
|
+
# => the two-digit year
|
130
|
+
# => the two-digit month index (starting at 01)
|
131
|
+
# => a period (separator)
|
132
|
+
# => the abbreviated month name
|
133
|
+
#
|
134
|
+
# @example
|
135
|
+
# => 1908.aug
|
136
|
+
# => for August 2019
|
137
|
+
#
|
138
|
+
def self.yymo_mmm
|
139
|
+
return "#{yy}#{mo}.#{mmm}"
|
140
|
+
end
|
141
|
+
|
142
|
+
|
143
|
+
#
|
144
|
+
# Given two integer parameters (month index and 4 digit year) representing
|
145
|
+
# the month in question this method returns the [PREVIOUS MONTHS] character
|
146
|
+
# amalgam in the format [yymo_mmm] where
|
147
|
+
#
|
148
|
+
# => yy | previous month's two-digit year
|
149
|
+
# => mo | previous month's two-digit month index
|
150
|
+
# => . | a period (separator)
|
151
|
+
# => mmm | previous month's abbreviated month name
|
152
|
+
#
|
153
|
+
# -------------------
|
154
|
+
# Example 1 (Simple)
|
155
|
+
# -------------------
|
156
|
+
#
|
157
|
+
# returns char => 1907.jul
|
158
|
+
# 4 parameters => 8, 2019
|
159
|
+
# representing => August, 2019
|
160
|
+
#
|
161
|
+
# ----------------------
|
162
|
+
# Example 2 (Last Year)
|
163
|
+
# ----------------------
|
164
|
+
#
|
165
|
+
# returns char => 1812.dec
|
166
|
+
# 4 parameters => 1, 2019
|
167
|
+
# representing => January, 2019
|
168
|
+
#
|
169
|
+
def self.previous_month_chars this_month_index, this_4digit_year
|
170
|
+
|
171
|
+
prev_month_index = this_month_index == 1 ? 12 : ( this_month_index - 1 )
|
172
|
+
prev_2dig_mn_pad = sprintf '%02d', prev_month_index
|
173
|
+
prev_4digit_year = this_month_index == 1 ? ( this_4digit_year - 1 ) : this_4digit_year
|
174
|
+
prev_twodigit_yr = "#{prev_4digit_year.to_s}"[2..-1]
|
175
|
+
prev_months_name = Date::ABBR_MONTHNAMES[prev_month_index].downcase
|
176
|
+
|
177
|
+
return "#{prev_twodigit_yr}#{prev_2dig_mn_pad}.#{prev_months_name}"
|
178
|
+
|
179
|
+
end
|
180
|
+
|
181
|
+
#
|
182
|
+
# Using the current class time this method returns
|
183
|
+
# the character amalgam for the [PREVIOUS MONTH] in
|
184
|
+
# the format [yymo_mmm] where
|
185
|
+
#
|
186
|
+
# => yy | last month's two-digit year
|
187
|
+
# => mo | last month's two-digit month index
|
188
|
+
# => . | a period (separator)
|
189
|
+
# => mmm | last month's abbreviated month name
|
190
|
+
#
|
191
|
+
# -------------------
|
192
|
+
# Example 1 (Simple)
|
193
|
+
# -------------------
|
194
|
+
#
|
195
|
+
# returns => 1907.jul
|
196
|
+
# if this month is => August 2019
|
197
|
+
#
|
198
|
+
# ----------------------
|
199
|
+
# Example 2 (Last Year)
|
200
|
+
# ----------------------
|
201
|
+
#
|
202
|
+
# returns => 1812.dec
|
203
|
+
# if this month is => January 2019
|
204
|
+
#
|
205
|
+
def self.yymo_mmm_prev
|
206
|
+
return previous_month_chars mo.to_i, yyyy.to_i
|
207
|
+
end
|
208
|
+
|
209
|
+
|
210
|
+
# Return 5 digit amalgam of year and julian day.
|
211
|
+
# eg [19003] for [January 3rd 2019]
|
212
|
+
def self.yyjjj
|
213
|
+
return "#{yy}#{jjj}"
|
214
|
+
end
|
215
|
+
|
216
|
+
|
217
|
+
# Return the 4 digit amalgam of the hour and minute
|
218
|
+
# using the 24 hour clock.
|
219
|
+
#
|
220
|
+
# @example
|
221
|
+
# => 1525
|
222
|
+
# => 03:25 pm
|
223
|
+
#
|
224
|
+
def self.hhmm
|
225
|
+
return "#{hh}#{mm}"
|
226
|
+
end
|
227
|
+
|
228
|
+
|
229
|
+
#
|
230
|
+
# Return the time of day to a TENTH of a second accuracy.
|
231
|
+
# [8] characters will always be returned with the 5th one
|
232
|
+
# being the (period) separator.
|
233
|
+
#
|
234
|
+
# The first (separated) segment delivers a hhmm 24 hour
|
235
|
+
# clock representation of the stamped time.
|
236
|
+
#
|
237
|
+
# The 3 digits of the second segment comprise of
|
238
|
+
#
|
239
|
+
# second of minute => 2 digits | [00] to [59]
|
240
|
+
# tenth of second => 1 digit from [0] to [9]
|
241
|
+
#
|
242
|
+
# @example
|
243
|
+
# => The time at the 562nd millisecond of the 49th
|
244
|
+
# second of the 23rd minute of the 17th hour of
|
245
|
+
# the day ( 17:23:49.562 )
|
246
|
+
#
|
247
|
+
# => 8 chars
|
248
|
+
# => 1723.495
|
249
|
+
#
|
250
|
+
def self.hhmm_sst
|
251
|
+
return "#{hhmm}.#{sst}"
|
252
|
+
end
|
253
|
+
|
254
|
+
|
255
|
+
# Return a string timestampt that is a period separated
|
256
|
+
# amalgam of the 2 digit year, 3 digit julian day, 2 digit
|
257
|
+
# hour, 2 digit minute, 2 digit second and 1 digit rounded
|
258
|
+
# down tenth of second.
|
259
|
+
#
|
260
|
+
# @example
|
261
|
+
# => 19003.1025
|
262
|
+
# => 10:25 am on January 3rd 2019
|
263
|
+
#
|
264
|
+
#
|
265
|
+
# Return the time of day to a TENTH of a second accuracy.
|
266
|
+
# [8] characters will always be returned with the 5th one
|
267
|
+
# being the (period) separator.
|
268
|
+
#
|
269
|
+
# The first (separated) segment delivers a hhmm 24 hour
|
270
|
+
# clock representation of the stamped time.
|
271
|
+
#
|
272
|
+
# The 3 digits of the second segment comprise of
|
273
|
+
#
|
274
|
+
# - second of minute => 2 digits | [00] to [59]
|
275
|
+
# - tenth of second => 1 digit from [0] to [9]
|
276
|
+
#
|
277
|
+
# @example
|
278
|
+
# => The time at the 562nd millisecond of the 49th
|
279
|
+
# second of the 23rd minute of the 17th hour of
|
280
|
+
# the day ( 17:23:49.562 )
|
281
|
+
#
|
282
|
+
# => 8 chars
|
283
|
+
# => 1723.495
|
284
|
+
#
|
285
|
+
def self.yyjjj_hhmm_sst
|
286
|
+
return "#{yyjjj}.#{hhmm}.#{sst}"
|
287
|
+
end
|
288
|
+
|
289
|
+
|
290
|
+
# Return a string timestampt that is a period separated
|
291
|
+
# amalgam of the 2 digit year, 3 digit julian day, 2 digit
|
292
|
+
# hour, 2 digit minute, 2 digit second and <b>9 digit</b>
|
293
|
+
# nanosecond.
|
294
|
+
#
|
295
|
+
# @example
|
296
|
+
# return => 19003.1725.42.836592034
|
297
|
+
# 4 time => 17:25:42 am on January 3rd 2019
|
298
|
+
#
|
299
|
+
# As per the above example, the time returned
|
300
|
+
#
|
301
|
+
# - is the 836592034 <b>nanosecond</b>
|
302
|
+
# - of the 42nd <b>second</b>
|
303
|
+
# - of the 25th <b>minute</b>
|
304
|
+
# - of the 17th <b>hour</b>
|
305
|
+
# - of the 3rd <b>day</b>
|
306
|
+
# - of the 20th <b>year</b>
|
307
|
+
# - of the 21st <b>century</b>
|
308
|
+
#
|
309
|
+
# @return [String]
|
310
|
+
# Return the time of day to nanosecond accuracy.
|
311
|
+
# 23 characters are always returned with three (3) period
|
312
|
+
# separators at the 6th, 11th and 14th positions.
|
313
|
+
def self.yyjjj_hhmm_ss_nanosec
|
314
|
+
nanosec_str = KeyNow.instance.time_now.strftime "%9N"
|
315
|
+
return "#{yyjjj}.#{hhmm}.#{ss}.#{nanosec_str}"
|
316
|
+
end
|
317
|
+
|
318
|
+
|
319
|
+
# Fetch the double barreled time stamp that is an amalgam of
|
320
|
+
# the human readable time now and a machine time representation
|
321
|
+
# from the moment this class was initialized.
|
322
|
+
#
|
323
|
+
# See the {yyjjj_hhmm_ss_nanosec} method for documentation of
|
324
|
+
# the nanosecond accurate time stamp.
|
325
|
+
#
|
326
|
+
# @return [String]
|
327
|
+
# the double barreled time stamp containing a human readable
|
328
|
+
# (right this moment) time and a <b>class initialized time</b>
|
329
|
+
# representation with nanosecond accuracy.
|
330
|
+
def self.fetch
|
331
|
+
return "#{Time.now.ctime} ( #{yyjjj_hhmm_ss_nanosec} )"
|
332
|
+
end
|
333
|
+
|
334
|
+
|
335
|
+
# Grab the double barreled time stamp that is an amalgam of
|
336
|
+
# the human readable time now and a machine time representation
|
337
|
+
# from the moment this class was initialized.
|
338
|
+
#
|
339
|
+
# On Friday June the 8th at about 6:26 pm.
|
340
|
+
# Fri Jun 8 18:26:17 2018 ( 18159.1826.138 )
|
341
|
+
#
|
342
|
+
# See the {yyjjj_hhmm_sst} method for documentation of stamp
|
343
|
+
# that is accurate to the tenth of a second.
|
344
|
+
#
|
345
|
+
# @return [String]
|
346
|
+
# the double barreled time stamp containing a human readable
|
347
|
+
# (right this moment) time and a <b>class initialized time</b>
|
348
|
+
# representation with tenth of a second accuracy.
|
349
|
+
def self.grab
|
350
|
+
time_with_consecutive_spaces = Time.now.ctime
|
351
|
+
human_readable_str = time_with_consecutive_spaces.gsub( " ", " " )
|
352
|
+
return "#{human_readable_str} ( #{yyjjj_hhmm_sst} )"
|
353
|
+
end
|
354
|
+
|
355
|
+
|
356
|
+
# Return the Rubyfied time zone being used.
|
357
|
+
def self.zone
|
358
|
+
return KeyNow.instance.time_now.zone
|
359
|
+
end
|
360
|
+
|
361
|
+
|
362
|
+
# Log segments of time pertaining to the time stamp.
|
363
|
+
# @todo
|
364
|
+
# move method contents into test class
|
365
|
+
def self.log_instance_time
|
366
|
+
|
367
|
+
log.info(x) { "[stamp] -------------- => -------------------------------- #" }
|
368
|
+
log.info(x) { "[stamp] eco time stamp => [#{KeyNow.instance.time_now.ctime}]" }
|
369
|
+
log.info(x) { "[stamp] -------------- => -------------------------------- #" }
|
370
|
+
log.info(x) { "[stamp] Univ Time Zone => #{zone}" }
|
371
|
+
log.info(x) { "[stamp] Month Index is => #{mo}" }
|
372
|
+
log.info(x) { "[stamp] Month Name is => #{mmm}" }
|
373
|
+
log.info(x) { "[stamp] Day Of Week is => #{ddd}" }
|
374
|
+
log.info(x) { "[stamp] -------------- => -------------------------------- #" }
|
375
|
+
log.info(x) { "[stamp] Two Digit Year => #{yy}" }
|
376
|
+
log.info(x) { "[stamp] Julian Cal Day => #{jjj}" }
|
377
|
+
log.info(x) { "[stamp] Yr and Jul Day => #{yyjjj}" }
|
378
|
+
log.info(x) { "[stamp] Hour of Theday => #{hh}" }
|
379
|
+
log.info(x) { "[stamp] Minute of Hour => #{mm}" }
|
380
|
+
log.info(x) { "[stamp] Hour + Minute => #{hhmm}" }
|
381
|
+
log.info(x) { "[stamp] Second of Min => #{ss}" }
|
382
|
+
log.info(x) { "[stamp] 600 Min Slices => #{sst}" }
|
383
|
+
log.info(x) { "[stamp] -------------- => -------------------------------- #" }
|
384
|
+
log.info(x) { "[stamp] The Time Stamp => #{yyjjj_hhmm_sst}" }
|
385
|
+
log.info(x) { "[stamp] -------------- => -------------------------------- #" }
|
386
|
+
|
387
|
+
end
|
388
|
+
|
389
|
+
|
390
|
+
# This singleton (one instance) class sets the time just once.
|
391
|
+
def initialize
|
392
|
+
@time_now = Time.now;
|
393
|
+
end
|
394
|
+
|
395
|
+
|
396
|
+
KeyNow.log_instance_time
|
397
|
+
|
398
|
+
|
399
|
+
end
|
400
|
+
|
401
|
+
|
402
|
+
end
|