opensecret 0.0.913 → 0.0.941

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.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +3 -0
  3. data/README.md +129 -19
  4. data/Rakefile +0 -9
  5. data/bin/opensecret +1 -1
  6. data/lib/{opensecret/plugins.io/cipher/crypto.rb → crypto/amalgam.rb} +6 -8
  7. data/lib/crypto/collect.rb +139 -0
  8. data/lib/crypto/engineer.rb +201 -0
  9. data/lib/crypto/verify.rb +33 -0
  10. data/lib/extension/array.rb +133 -0
  11. data/lib/{opensecret/additions → extension}/dir.rb +0 -0
  12. data/lib/extension/file.rb +56 -0
  13. data/lib/extension/hash.rb +33 -0
  14. data/lib/extension/string.rb +349 -0
  15. data/lib/factbase/facts.opensecret.io.ini +28 -0
  16. data/lib/logging/gem.logging.rb +133 -0
  17. data/lib/opensecret.rb +102 -45
  18. data/lib/opensecret/executors/crypt.keys/crypt.keys.ini +0 -53
  19. data/lib/session/{session.rb → attributes.rb} +60 -5
  20. data/lib/session/exceptions.rb +53 -0
  21. data/lib/session/fact.finder.rb +684 -0
  22. data/lib/session/user.home.rb +49 -0
  23. data/lib/usecase/usecase.rb +245 -0
  24. data/lib/usecase/usecases/init.rb +190 -0
  25. data/lib/usecase/usecases/on.rb +33 -0
  26. data/lib/usecase/usecases/safe.rb +95 -0
  27. data/lib/version.rb +1 -1
  28. metadata +22 -17
  29. data/lib/opensecret/additions/array.rb +0 -117
  30. data/lib/opensecret/additions/string.rb +0 -312
  31. data/lib/opensecret/commons/eco.cmdline.rb +0 -446
  32. data/lib/opensecret/eco.do.rb +0 -46
  33. data/lib/opensecret/plugins.io/error/eco.exceptions.rb +0 -24
  34. data/lib/opensecret/plugins.io/facts/fact.chars.rb +0 -66
  35. data/lib/opensecret/plugins.io/facts/fact.factor.rb +0 -156
  36. data/lib/opensecret/plugins.io/facts/fact.locator.rb +0 -105
  37. data/lib/opensecret/plugins.io/facts/fact.reader.rb +0 -137
  38. data/lib/opensecret/plugins.io/facts/fact.tree.rb +0 -661
  39. data/lib/opensecret/plugins.io/logs/log.object.rb +0 -89
  40. data/lib/opensecret/plugins.io/logs/logging.rb +0 -203
@@ -0,0 +1,28 @@
1
+
2
+ [global]
3
+
4
+ min.passwd.len = rb>> 16
5
+ nickname = godzilla
6
+ root.domain = devopswiki.co.uk
7
+ env.var.name = SECRET_MATERIAL
8
+ ratio = rb>> 3
9
+ bit.key.size = rb>> 8192
10
+ key.cipher = rb>> OpenSSL::Cipher.new 'AES-256-CBC'
11
+ secret.keyname = master.private.key.crypt.txt
12
+ secret.keydir = rb>> OpenSession::Attributes.instance.get_value "opensecret", "safe"
13
+ secret.keypath = rb>> File.join @s[:secret_keydir], @s[:secret_keyname]
14
+
15
+ repo.name = material_data
16
+
17
+ ## local.gitrepo = rb>> File.join @i[:dir], @s[:repo_name]
18
+
19
+ ## public.gitrepo = https://www.eco-platform.co.uk/content/material.data.git
20
+ ## public.dirname = public_keys
21
+ ## public.keyroute = rb>> File.join @s[:root_domain], @s[:public_dirname]
22
+ ## public.keydir = rb>> File.join @s[:local_gitrepo], @s[:public_keyroute]
23
+ ## public.keyname = rb>> "public_key." + @s[:nickname] + dot + @s[:root_domain] + ".txt"
24
+ ## public.keypath = rb>> File.join @s[:public_keydir], @s[:public_keyname]
25
+
26
+ prompt.1 = Enter a Robust Password
27
+ prompt.2 = Re-enter that Password
28
+
@@ -0,0 +1,133 @@
1
+ require "logger"
2
+ require "session/user.home"
3
+
4
+ # [MIXIN] magic is deployed to hand out DevOps quality logging
5
+ # features to any class that includes the logging module.
6
+ #
7
+ # When logging facilities are not ready we need to log just to
8
+ # stdout but when they are we need to use them.
9
+ #
10
+ # mixin power enables one class to give the logfile path and all
11
+ # classes will suddenly retrieve a another logger and use that.
12
+ #
13
+ # include Logging
14
+ # def doImportant
15
+ # log.warn(ere) "unhappy about doing this"
16
+ # do_anyway
17
+ # log.debug(ere) "all good it was okay"
18
+ # end
19
+ #
20
+ # So what are Mixins?
21
+ #
22
+ # Refer to the below link for excellent coverage of mixins.
23
+ # @see http://ruby-doc.com/docs/ProgrammingRuby/html/tut_modules.html
24
+ #
25
+ module OpenLogger
26
+
27
+ @@gem_name = "opensecret"
28
+ @@gem_base = File.join OpenSession::Home.dir, ".#{@@gem_name}"
29
+ FileUtils.mkdir_p @@gem_base unless File.exists? @@gem_base
30
+ @@log_path = File.join @@gem_base, "opensecret-cli-activity.log"
31
+
32
+
33
+
34
+ # Classes that include (MIXIN) this logging module will
35
+ # have access to this logger method.
36
+ #
37
+ # [memoization] is implemented here for performance and
38
+ # will only initiate a logger under 2 circumstances
39
+ #
40
+ # [1] - the first call (returns a STDOUT logger)
41
+ # [2] - the call after the logfile path is set
42
+ # (returns a more sophisticated logger)
43
+ def log
44
+
45
+ @@log_class ||= get_logger
46
+
47
+ end
48
+
49
+
50
+ # This Ruby behavioural snippet allows the logger to print 3 crucial
51
+ # pieces of information for the troubleshooter (detective) so that they
52
+ # may ascertain
53
+ #
54
+ # - the [module] the logging call came from
55
+ # - the [method] the logging call came from
56
+ # - the [line number] the logging call is at
57
+ #
58
+ # To use this method you can make calls like this
59
+ #
60
+ # - log.info(x) { "Log many things about where I am now." }
61
+ # - log.warn(x) { "Log many things about where I am now." }
62
+ #
63
+ def x
64
+
65
+ module_name = File.basename caller_locations(1,1).first.absolute_path, ".rb"
66
+ method_name = caller_locations(1,1).first.base_label
67
+ line_number = caller_locations(1,1).first.lineno
68
+
69
+ "#{module_name} | #{method_name} | (line #{line_number}) "
70
+
71
+ end
72
+
73
+
74
+ # This method returns an initialized logger.
75
+ #
76
+ # The logger returned may write to
77
+ #
78
+ # - a simple file
79
+ # - a service like fluentd
80
+ # - a message queue
81
+ # - a nosql database
82
+ # - all of the above
83
+ #
84
+ # Not that [memoization] should be used so that this method
85
+ # gets called ideally just once although in practise it may
86
+ # turn out to be a handful of times.
87
+ #
88
+ # @return [Logger] return an initialized logger object
89
+ def get_logger
90
+
91
+ file_logger = Logger.new @@log_path
92
+ original_formatter = Logger::Formatter.new
93
+
94
+ file_logger.formatter = proc { |severity, datetime, progname, msg|
95
+ original_formatter.call( severity, datetime, progname, msg.dump.chomp.strip )
96
+ }
97
+
98
+ return file_logger
99
+
100
+ end
101
+
102
+
103
+ # Overtly long file paths in the log files sometimes hamper readability
104
+ # and this method improves the situation by returning just the two
105
+ # immediate ancestors of the file (or folder) path.
106
+ #
107
+ # @example A really long input like
108
+ # <tt>/home/joe/project/degrees/math/2020</tt>
109
+ # is reduced to
110
+ # <tt>degrees/math/2020</tt>
111
+ #
112
+ # So this method returns the name of the grandparent folder then parent folder
113
+ # and then the most significant file (or folder) name.
114
+ #
115
+ # When this is not possible due to the filepath being colisively near the
116
+ # filesystem's root, it returns the parameter name.
117
+ #
118
+ # @param object_path [String] overtly long path that will be made more readable
119
+ # @return [String] the (separated) three most significant path name segments
120
+ def nickname object_path
121
+
122
+ object_name = File.basename object_path
123
+ parent_folder = File.dirname object_path
124
+ parent_name = File.basename parent_folder
125
+ granny_folder = File.dirname parent_folder
126
+ granny_name = File.basename granny_folder
127
+
128
+ return [granny_name,parent_name,object_name].join("/")
129
+
130
+ end
131
+
132
+
133
+ end
@@ -1,10 +1,19 @@
1
1
  require "thor"
2
+ require "fileutils"
2
3
  require "session/time.stamp"
3
- require "session/session"
4
+ require "session/attributes"
5
+ require "logging/gem.logging"
4
6
 
5
- #
6
- # This command line processor extends Thor's functionality in
7
- # order to
7
+ require "usecase/usecases/safe"
8
+ require "usecase/usecases/init"
9
+
10
+ include OpenLogger
11
+
12
+ # This standard out sync command flushes text destined for STDOUT immediately,
13
+ # without waiting either for a full cache or script completion.
14
+ $stdout.sync = true
15
+
16
+ # This command line processor extends the Thor gem CLI tools in order to
8
17
  #
9
18
  # - read the posted commands, options and switches
10
19
  # - maps the incoming string data to objects
@@ -13,7 +22,9 @@ require "session/session"
13
22
  # - ensure that the parameter values are in range
14
23
  # - delegate processing to the registered handlers
15
24
  #
16
- class CommandProcessor < Thor
25
+ class CliInterpreter < Thor
26
+
27
+ log.info(x) {"Wake up loggers."}
17
28
 
18
29
  #
19
30
  # This class option allows every CLI call the option to include
@@ -22,11 +33,21 @@ class CommandProcessor < Thor
22
33
  #
23
34
  class_option :debug, :type => :boolean
24
35
 
36
+
37
+
38
+ # Description of the init configuration call.
39
+ desc "init", "initialize secret keys and check access to the crypt store"
40
+
41
+ # Initialize secret keys and check access to the crypt store.
25
42
  #
26
- # Thor method describing the mandatory directory path parameter
27
- # in the keydir cli interface call.
28
- #
29
- desc "keydir KEYPATH", "KEYPATH path to USB key for storing private keys"
43
+ # - checks the installed configuration.
44
+ def init
45
+ OpenSecret::Init.new.flow_of_events
46
+ end
47
+
48
+
49
+ # Description of the mandatory safe and (safe directory) configuration.
50
+ desc "safe SAFE_DIR", "SAFE_DIR full path to the (ideally USB key) storage location"
30
51
 
31
52
  #
32
53
  # A USB key drive is the ideal store for the encrypted private
@@ -37,59 +58,95 @@ class CommandProcessor < Thor
37
58
  # - if not, it attempts to create the path
38
59
  # - if successful it's written into HOME/.opensecret/opensecret.keydir.txt
39
60
  #
40
- # @example opensecret keydir /path/to/usb/key/dir
61
+ # @param safe_dir [String] the path to USB key for storing encrypted keys
62
+ #
63
+ def safe safe_dir
64
+
65
+ configure_safe_uc = OpenSecret::Safe.new
66
+ configure_safe_uc.safe_path = safe_dir
67
+ configure_safe_uc.flow_of_events
68
+
69
+ end
70
+
71
+
72
+ #
73
+ # Description of the email-address that is unique
74
+ # for the domain in question.
41
75
  #
42
- # Alternate Use Case Flows
76
+ desc "email EMAIL_ADDRESS", "EMAIL_ADDRESS Your email address unique for the domain."
77
+
43
78
  #
44
- # - if file exists with equal (case sensitive) content this is logged
45
- # - if file exists with different content that content is logged and changed
79
+ # This method collects the email address that is unique for the domain in
80
+ # question. For the email address to be valid it must consist of only alphanumerics,
81
+ # underscores, periods, hyphens and (at most) one @ symbol.
46
82
  #
47
- # @param keypath [String] the path to USB key for storing private keys
83
+ # Note that underscores, periods, hyphens and @ symbol are permissable if not
84
+ # at the start or end of the email address nor can they appear consecutively.
48
85
  #
49
- def keydir keypath
86
+ # <tt>a@b.cd</tt> is the minimum size of an externally addressable email
87
+ # address so 6 or more characters is enforced by this configuation method.
88
+ #
89
+ # email validation will be added to opensecret including
90
+ # - validation of the email address character array
91
+ # - proof of control and ownership of the email address
92
+ #
93
+ # If an email address already exists within the domain section of the
94
+ # configuration file, it is overwritten. If there is no configuration
95
+ # file yet, one is created within the auspices of the home directory.
96
+ #
97
+ # @param email_address [String] email address of the user (eg a@b.cd)
98
+ #
99
+ def email email_address
50
100
 
51
- if( File.exists?( keypath ) && !(File.directory? keypath) )
52
- abort "The path cannot be a file => #{keypath}"
101
+ if email_address.length < 6
102
+ abort "The tiniest (externally accessible) email address [a@b.cd] has 6 characters."
53
103
  end
54
104
 
55
- FileUtils.mkdir_p keypath unless File.exists? keypath
105
+ OpenSession::Attributes.stash "opensecret", "email", email_address
56
106
 
57
- secret_session = OpenSession::Session.new
58
- secret_session.write_keyvalue "opensecret", "key_folder", keypath
59
- session_file = secret_session.get_filepath "opensecret"
107
+ end
60
108
 
61
- puts ""
62
- puts "private key directory => [ #{keypath} ]"
63
- puts "session configuration => [ #{session_file} ]"
64
- puts "session time stamp is => [ #{OpenSession::Stamp.yyjjj_hhmm_sst} ]"
65
- puts ""
66
109
 
67
- end
110
+ desc "store STORE_URL", "STORE_URL denotes the location of the backend crypt store"
68
111
 
69
112
  #
70
- # Initialize (configure) two fundamental crypt pointers
71
- #
72
- # - an opensecret domain like &raquo; **lecturers@harvard**
73
- # - the url to a backend store like Git, S3 or an SSH accessible drive.
74
- #
75
- # The domain will be extended to cover verified internet domains.
76
- # They will also latch onto LDAP domains so when admins add, revoke
77
- # or remove users, their opensecret access is adjusted accordingly.
113
+ # Here we define the location (the URL) of the crypt store. The crypt store will hold
114
+ # the cipher text and is known as <tt>backend storage</tt>.
78
115
  #
79
- # @example opensecret user create id=joe email=joebloggs@opensecret.io
116
+ # The planned list of backend storage systems (each onlined with a plugin), is
80
117
  #
81
- # @param domain [String] the DOMAIN eg lecturers@harvard for your family or work group.
82
- # @param store_url [String] the STORE_URL for connecting to the backend storage service
118
+ # - Git (including GitHub, GitLab, BitBucket, OpenGit and private repositories.
119
+ # - S3 Buckets from the Amazon Web Services (AWS) cloud.
120
+ # - SSH, SCP, SFTP connected file-systems
121
+ # - network storage including Samba, NFS, VMWare vSAN and
122
+ # - GoogleDrive (only Windows has suitable synchronized support).
83
123
  #
84
- # ---> def init domain, store_url
124
+ # @param store_url [String] the STORE_URL identifying a filesystem or Git or S3 storage location
125
+ def store store_url
85
126
 
86
- # ---> OpenSecret::Crypto.register_domain domain, store_url
87
127
 
88
- # ---> puts ""
89
- # ---> puts "New domain configured => [ #{domain} ]"
90
- # ---> puts "Crypt store configured => [ #{store_url} ]"
91
- # ---> puts ""
128
+ if store_url.strip.length < 3
129
+ abort "4 characters is the minimum domain name length."
130
+ end
131
+
132
+ OpenSession::Attributes.stash "opensecret", "store", store_url.strip
133
+
134
+ ### if( File.exists?( store_url ) && !(File.directory? store_url) )
135
+ ### abort "The store url path cannot be a file => #{store_url}"
136
+ ### end
137
+
138
+ end
139
+
140
+
141
+ desc "on", "Open a session to encrypt (lock) one or more secrets"
142
+
143
+ # The [on] message tells opensecret to prepare to lock one or more secrets.
144
+ def on
145
+
146
+ #### FileUtils.mkdir_p store_url unless File.exists? store_url
147
+ #### OpenSession::Attributes.stash "opensecret", "store.id.#{store_id}", store_url
148
+
149
+ end
92
150
 
93
- # ---> end
94
151
 
95
152
  end
@@ -24,56 +24,3 @@ public.keypath = e>> File.join @s[:public_keydir], @s[:public_keyname]
24
24
  prompt.1 = Enter a Robust Password
25
25
  prompt.2 = Re-enter that Password
26
26
 
27
- #--
28
- #-- ------------------------------------------
29
- #-- How to Add the Secret Material on Windows
30
- #-- ------------------------------------------
31
- #--
32
- #-- Check that the variable is not set.
33
- #-- $ set
34
- #--
35
- #-- Run the commands below and then acquire another
36
- #-- command prompt or emacs/cygwin window.
37
- #--
38
- #-- $ setx SECRET_MATERIAL ABC123
39
- #-- $ set
40
- #--
41
- #-- Check (with last command) on new prompt that the
42
- #-- environment variable is now set.
43
- #--
44
- #-- ----------------------------------------
45
- #-- How to Add the Secret Material (Linux)
46
- #-- ----------------------------------------
47
- #--
48
- #-- Check that the variable is not set.
49
- #-- $ printenv | sort
50
- #--
51
- #-- Run the commands below and then reboot.
52
- #-- (Ensure that the whole disk is encrypted so that the
53
- #-- /etc/environment file cannot be accessed if your desktop
54
- #-- or laptop is stolen.
55
- #--
56
- #-- $ sudo chmod 666 /etc/environment
57
- #-- $ sudo echo "SECRET_MATERIAL=ABC123" >> /etc/environment
58
- #-- $ sudo chmod 644 /etc/environment
59
- #-- $ printenv | sort
60
- #--
61
- #-- Check (with last command) after the reboot to ensure
62
- #-- that the environment variable is now set.
63
- #--
64
- #-- ---------------------------------------------------
65
- #-- How to TEMPORARILY Add the Secret Material (Linux)
66
- #-- ---------------------------------------------------
67
- #--
68
- #-- Check that the variable is not set.
69
- #-- $ printenv | sort
70
- #--
71
- #-- We are only adding for the session (perhaps to test it)
72
- #-- therefore we simply export. On closing the shell the
73
- #-- environment variable will be gone.
74
- #--
75
- #-- $ export SECRET_MATERIAL=ABC123
76
- #-- $ printenv | sort
77
- #--
78
- #-- Now the environment variable should be temporarily set.
79
- #--
@@ -17,6 +17,7 @@
17
17
  module OpenSession
18
18
 
19
19
  require 'inifile'
20
+ require 'singleton'
20
21
 
21
22
 
22
23
  ## ---> Cleaning User Input - Use Me
@@ -73,13 +74,30 @@ module OpenSession
73
74
  # This "session awakening" wipes the slate clean and starts afresh
74
75
  # with regard to the two dimensional array of configuration directive
75
76
  # pointers.
76
- class Session
77
-
77
+ class Attributes
78
+ include Singleton
78
79
 
79
80
  @@filename_tail = "-session.ini"
80
81
  attr_reader :time_stamp
81
82
 
82
83
 
84
+
85
+ # Stash the attribute within the session's configuration file and
86
+ # print out the current state of the configuration.
87
+ #
88
+ # @param section_name [String] name grouping the section of config values
89
+ # @param key_name [String] the name of the key whose value is to be written
90
+ # @param key_value [String] the data item value of the key specified
91
+ def self.stash section_name, key_name, key_value
92
+
93
+ the_session = OpenSession::Attributes.instance
94
+ the_session.write_keyvalue section_name, key_name, key_value
95
+ puts "\n" + File.read(the_session.get_filepath(section_name)) + "\n"
96
+
97
+ end
98
+
99
+
100
+
83
101
  # This singleton (one instance) class initializes by getting
84
102
  # the current timestamp.
85
103
  def initialize
@@ -138,6 +156,42 @@ module OpenSession
138
156
  end
139
157
 
140
158
 
159
+ # Given the configuration key name and the context name, get the
160
+ # corresponding key value from the configuration file whose path
161
+ # is acquired using the {self#get_filepath} method.
162
+ #
163
+ # @param context_name [String] name of program writing a session attribute
164
+ # @param key_name [String] the key whose value is to be retrieved
165
+ #
166
+ # @return [String] the value configured for the parameter key
167
+ #
168
+ # @raise ArgumentError for any one of a long list of reasons that
169
+ # cause the key value to not be retrieved. This can range from
170
+ # non-existent directories and files, non readable files, incorrect
171
+ # configurations right down to missing keys or even missing values.
172
+ def get_value context_name, key_name
173
+
174
+ the_file = get_filepath context_name
175
+ raise ArgumentError.new "No configuration file found => [ #{the_file} ]" unless File.exists? the_file
176
+
177
+ the_text = File.read the_file
178
+ raise ArgumentError.new "Configuration file is empty => [ #{the_file} ]" if the_text.empty?
179
+
180
+ the_data = IniFile.load the_file
181
+ key_exists = the_data[ context_name ].has_key?( key_name )
182
+ raise ArgumentError.new "Key [#{key_name}] not configured => #{the_data.to_s}" unless key_exists
183
+
184
+ rawvalue = the_data[context_name][key_name]
185
+ raise ArgumentError.new "Empty value 4 key [#{key_name}] => #{the_data.to_s}" if rawvalue.empty?
186
+
187
+ keyvalue = rawvalue.chomp.strip
188
+ raise ArgumentError.new "Whitespace value 4 key [#{key_name}] => #{the_data.to_s}" if keyvalue.empty?
189
+
190
+ return keyvalue
191
+
192
+ end
193
+
194
+
141
195
  #
142
196
  # Get the path to the session context file.
143
197
  # This file will be in a folder whose name is simply the dot
@@ -147,7 +201,7 @@ module OpenSession
147
201
  # @example ~/.openbox/openbox-session.ini is the filepath for context "openbox"
148
202
  #
149
203
  # @param context_name [String] name of program writing a session attribute
150
- #
204
+ # @return [String] full path to the context configuration file
151
205
  def get_filepath context_name
152
206
 
153
207
  return File.join( get_filedir(context_name), "#{context_name}#{@@filename_tail}" )
@@ -165,7 +219,7 @@ module OpenSession
165
219
  # @example ~/.openbox is the directory for context "openbox"
166
220
  #
167
221
  # @param context_name [String] name of program (or use case) context
168
- #
222
+ # @return [String] path to directory holding context configuration file
169
223
  def get_filedir context_name
170
224
 
171
225
  return File.join home_directory, ".#{context_name}"
@@ -173,7 +227,6 @@ module OpenSession
173
227
  end
174
228
 
175
229
 
176
- #
177
230
  # On non-windows systems the home directory is defined
178
231
  # perfectly by Ruby's Dir object.
179
232
  #
@@ -181,6 +234,7 @@ module OpenSession
181
234
  # onto the actual home directory. In these cases this
182
235
  # method removes it.
183
236
  #
237
+ # @return [String] the path to the machine user's home directory
184
238
  def home_directory
185
239
 
186
240
  return Dir.home unless Gem.win_platform?
@@ -205,6 +259,7 @@ module OpenSession
205
259
  # - ENV['USERNAME'] for the Windows platform
206
260
  # - ENV['USER'] for Linux (and everything else)
207
261
  #
262
+ # @return [String] the username of the machine user
208
263
  def username
209
264
 
210
265
  return ENV['USERNAME'] if Gem.win_platform?