class OpenSecret::UseCase
The parent OpenSecret
use case is designed to be extended by the cli (command line) use cases like {OpenSecret::Open}, {OpenSecret::Put} and {OpenSecret::Lock} because it describes behaviour common to at least two (but usually more) of the use cases.
Common Use
Case Behaviour¶ ↑
This {OpenSecret::UseCase} use case is designed to be extended and does preparatory work to create favourable and useful conditions to make use cases readable, less repetitive, simpler and concise.
Machine (Workstation) Configuration File
¶ ↑
The global configuration filepath is found off the home directory using {Dir.home}.
~/.opensecret.io/opensecret.io.configuration.ini
The global configuration file in INI format is managed through the methods
-
{grab} read the value at key_name from the default section
-
{stash} put directive key/value pair in default section
-
{read} read the value at key_name from the parameter section
-
{write} put directive key/value pair in parameter section
Constants
- APPLICATION_GEM_NAME
- APPLICATION_GEM_WEBSITE
- APPLICATION_GITHUB_URL
- APP_DIR_NAME
If and when this command line credentials management app needs to write any configuration directives to the machine's userspace it will use this constant to denote the (off-home) directory name.
- COMMANDMENT
This is the root command typed into the shell to invoke one of the opensecret use cases.
- ENVELOPE_ID_NAME
- ENVELOPE_ID_SIZE
- ENVELOPE_KEY_NAME
- ENVELOPE_KEY_PREFIX
- ENVELOPE_KEY_SIZE
- ENV_PATH
- ENV_VAR_KEY_NAME
The name of the environment variable that will hold the session token generated by {self.generate_session_token}. This environment variable is typically instantiated either manually (for ad-hoc use) or through media such as dot profile.
- ENV_VAR_PREFIX_A
This prefix denotes keys and their values should be posted as environment variables within the context (for example terraform) before instigating the main action like terraform apply.
- ENV_VAR_PREFIX_B
- FILE_CONTENT_KEY
The base64 encoded representation of the file content is placed into a map with this keyname.
- FILE_KEY_PREFIX
This prefix precedes keynames whose map value represents a file object.
- FILE_NAME_KEY
The file base name is placed into a map with this keyname.
- KEY_PATH
- LAST_ACCESSED
- MASTER_LOCK_KEY_NAME
- SESSION_DICT_LOCK_NAME
- SESSION_DICT_LOCK_SIZE
- SESSION_FILENAME_ID_SIZE
- SESSION_ID_SIZE
- SESSION_START_TIMESTAMP_NAME
Attributes
This variable should be set to true if the use case call originates from a shell different from the one through which the login ocurred.
To proceed, the shell that hosted the safe login must be a parent or at least an ancestor of this shell.
This variable need not be set if the login shell is the direct parent of this one (the every day manual usage scenario).
If however the login occurred from a grand or great grandparent shell (as is the case when nested scripts make an agent-like call), this variable must be set to true.
Public Class Methods
This use case is initialized primary by resolving the configured +general and use case specific facts+. To access the general facts, a domain name is expected in the parameter delegated by the extension use case classes.
# File lib/usecase/cmd.rb, line 238 def initialize class_name = self.class.name.split(":").last.downcase is_no_token_usecase = [ "token", "init", "id" ].include? class_name return if is_no_token_usecase exit(100) unless ops_key_exists? fact_filepath = File.sister_filepath( self, "ini", :execute ) log.info(x) { "Search location for INI factfile is [#{fact_filepath}]" } return unless File.exists?( fact_filepath ) @facts = FactFind.new() add_secret_facts @facts @facts.assimilate_ini_file( fact_filepath ) @dictionary = @facts.f[ @facts.to_symbol( class_name ) ] end
Public Instance Methods
After the main flow of events certain state conditions must hold true thus demonstrating that the observable value has indeed ben delivered.
Child classes should subclass this method and place any post execution (post condition) checks in it and then make a call to this method through the “super” keyword.
# File lib/usecase/cmd.rb, line 186 def check_post_conditions begin post_validation rescue OpenError::CliError => e puts "" puts "Your command did not complete successfully." puts "Post validation checks failed." puts "" puts " => #{e.message}" #### puts " => #{e.culprit}" puts "" abort e.message end end
Validate the input parameters and check that the current state is perfect for executing the use case.
If either of the above fail - the validation function should set a human readable string and then throw an exception.
# File lib/usecase/cmd.rb, line 153 def check_pre_conditions begin pre_validation rescue OpenError::CliError => e puts "" puts "Your command did not complete successfully." puts "Pre validation checks failed." puts "" puts " => #{e.message}" puts "" abort e.message end end
If the use case validation went well, the memorization went well the
# File lib/usecase/cmd.rb, line 229 def cleanup end
This method returns the absolute path to the directory that the configuration file sits in. It is basically just the dot prefixed context name (the {APP_DIR_NAME} constant).
~/.<<context-name>>
@return [String] path to directory holding context configuration file
# File lib/usecase/cmd.rb, line 130 def config_directory return File.join( Dir.home, ".#{APP_DIR_NAME}" ) end
The path to the initial configuration file below the user's home directory. The directory name the configuration file sits in is a dot prefixed context name derived from the value inside the {APP_DIR_NAME} constant.
~/.<<context-name>>/<<context-name>>-configuration.ini
You can see the filename too is derived from the context with a trailing string ending in .ini.
@return [String] full path to the context configuration file
# File lib/usecase/cmd.rb, line 118 def config_file return File.join config_directory(), "#{APP_DIR_NAME}.configuration.ini" end
Execute the main flow of events of the use case. Any exceptions thrown are captured and if the instance variale [@human_readable_message] is set - tell the user about it. Without any message - just tell the user something went wrong and tell them where the logs are that might carry more information.
# File lib/usecase/cmd.rb, line 222 def execute end
Execute the use cases's flow from beginning when you validate the input and parameters through the memorize, execute and the final cleanup.
# File lib/usecase/cmd.rb, line 138 def flow_of_events check_pre_conditions execute cleanup check_post_conditions end
Get the master database. This behaviour can only complete correctly if a successful login precedes this call either in this or an ancestral shell environment.
@return [Hash]
the hash data structure returned represents the master database.
# File lib/usecase/cmd.rb, line 88 def get_master_database begin log.info(x) { "Request for master db with from_script set to #{@from_script}" } return OpenKey::KeyApi.read_master_db( @from_script ) rescue OpenSSL::Cipher::CipherError => e log.fatal(x) { "Exception getting master db for the safe book." } log.fatal(x) { "The from_script parameter came set as [ #{@from_script} ]" } log.fatal(x) { "The exception message is ~> [[ #{e.message} ]]" } e.backtrace.log_lines abort e.message end end
Child classes should subclass this method and place any post execution (post condition) checks in it and then make a call to this method through the “super” keyword if this method gets any global behaviour in it worth calling.
# File lib/usecase/cmd.rb, line 211 def post_validation end
Override me if you need to
# File lib/usecase/cmd.rb, line 174 def pre_validation end
Private Instance Methods
# File lib/usecase/cmd.rb, line 292 def add_secret_facts fact_db master_db = get_master_database() raise ArgumentError.new "There is no open chapter here." if unopened_envelope?( master_db ) chapter_id = ENVELOPE_KEY_PREFIX + master_db[ ENV_PATH ] verse_id = master_db[ KEY_PATH ] chapter_data = OpenKey::KeyDb.from_json( OpenKey::KeyApi.content_unlock( master_db[ chapter_id ] ) ) mini_dictionary = chapter_data[ master_db[ KEY_PATH ] ] mini_dictionary.each do | key_str, value_str| fact_db.assimilate_fact( "secrets", key_str, value_str ) end end
# File lib/usecase/cmd.rb, line 308 def create_header() return OpenKey::KeyApi.format_header( OpenSecret::VERSION, APPLICATION_GEM_NAME, APPLICATION_GITHUB_URL, @domain_name ) end
# File lib/usecase/cmd.rb, line 345 def log_env() log.info(x) { "Gem Root Folder => #{Gem.dir()}" } log.info(x) { "Gem Config File => #{Gem.config_file()}" } log.info(x) { "Gem Binary Path => #{Gem.default_bindir()}" } log.info(x) { "Gem Host Path => #{Gem.host()}" } log.info(x) { "Gem Spec Info => #{Gem.latest_spec_for(APPLICATION_GEM_NAME)}" } log.info(x) { "Gem Spec Name => #{Gem.latest_spec_for(APPLICATION_GEM_NAME).name}" } log.info(x) { "Gem Spec Version => #{Gem.latest_spec_for(APPLICATION_GEM_NAME).version}" } log.info(x) { "Gem Caller Folder => #{Gem.location_of_caller()}" } log.info(x) { "Gem Paths List => #{Gem.path()}" } log.info(x) { "Gem Platforms => #{Gem.platforms()}" } log.info(x) { "Gem Ruby Version X => #{Gem.ruby()}" } log.info(x) { "Gem Ruby Version Y => #{Gem::VERSION}" } log.info(x) { "Gem Ruby Version Z => #{Gem.latest_rubygems_version()}" } log.info(x) { "Gem User Folder => #{Gem.user_dir()}" } log.info(x) { "Gem User Home => #{Gem.user_home()}" } return end
# File lib/usecase/cmd.rb, line 321 def ops_key_exists? log_env() if ( ENV.has_key? ENV_VAR_KEY_NAME ) return true end puts "" puts "opensecret needs you to create a session key." puts "To automate this step see the documentation." puts "To create the key run the below command." puts "" puts " export #{ENV_VAR_KEY_NAME}=`#{COMMANDMENT} token`" puts "" puts "Those are backticks surrounding `#{COMMANDMENT} token`" puts "Not apostrophes." puts "" return false end
# File lib/usecase/cmd.rb, line 419 def print_already_initialized puts "" puts "You can go ahead and login." puts "Your domain [#{@domain_name}] is already setup." puts "You should already know the password." puts "" puts " #{COMMANDMENT} login #{@domain_name}" puts "" end
# File lib/usecase/cmd.rb, line 404 def print_already_logged_in puts "" puts "We are already logged in. Open a secret envelope, put, then seal." puts "" puts " #{COMMANDMENT} open aws.credentials:s3reader" puts " #{COMMANDMENT} put access_key ABCD1234" puts " #{COMMANDMENT} put secret_key FGHIJ56789" puts " #{COMMANDMENT} put region_key eu-central-1" puts " #{COMMANDMENT} seal" puts "" end
# File lib/usecase/cmd.rb, line 432 def print_domain_initialized puts "" puts "It is time to login." puts "The protector keys for [#{@domain_name}] have been setup." puts "From now on you simply login to use this domain." puts "" puts " #{COMMANDMENT} login #{@domain_name}" puts "" end
# File lib/usecase/cmd.rb, line 472 def print_login_success puts "" puts "Success - you are logged in." puts "" puts " #{COMMANDMENT} open aws.credentials:s3reader" puts " #{COMMANDMENT} put access_key ABCD1234" puts " #{COMMANDMENT} put secret_key FGHIJ56789" puts " #{COMMANDMENT} put region_key eu-central-1" puts " #{COMMANDMENT} seal" puts "" end
# File lib/usecase/cmd.rb, line 445 def print_not_initialized puts "" puts "Please initialize the app domain on this machine." puts "Give a domain name and a folder for key storage." puts "" puts " #{COMMANDMENT} init <domain_name> \"$HOME/open.world\"" puts "" end
# File lib/usecase/cmd.rb, line 457 def print_success_initializing puts "" puts "Success - now open a secret envelope, put, then seal." puts "" puts " #{COMMANDMENT} open aws.credentials:s3reader" puts " #{COMMANDMENT} put access_key ABCD1234" puts " #{COMMANDMENT} put secret_key FGHIJ56789" puts " #{COMMANDMENT} put region_key eu-central-1" puts " #{COMMANDMENT} seal" puts "" end
# File lib/usecase/cmd.rb, line 377 def print_unopened_envelope() puts "" puts "Problem - before creating, reading or changing data you" puts "must first open a path to it like this." puts "" puts " #{COMMANDMENT} open email.accounts joe@gmail.com" puts "" puts " then you put data at that path" puts "" puts " #{COMMANDMENT} put username joebloggs" puts " #{COMMANDMENT} put password jo3s-s3cr3t" puts " #{COMMANDMENT} put phone-no 07123456789" puts " #{COMMANDMENT} put question \"Mums maiden name\"" puts "" puts " and why not read it back" puts "" puts " #{COMMANDMENT} get password" puts "" puts " then close the path." puts "" puts " #{COMMANDMENT} close" puts "" end
# File lib/usecase/cmd.rb, line 368 def unopened_envelope?( key_database ) return false if key_database.has_key?( ENV_PATH ) print_unopened_envelope() return true end