opensecret 0.0.913 → 0.0.941

Sign up to get free protection for your applications and to get access to all the features.
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?