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

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

from_script[W]

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

new() click to toggle source

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

check_post_conditions() click to toggle source

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
check_pre_conditions() click to toggle source

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
cleanup() click to toggle source

If the use case validation went well, the memorization went well the

# File lib/usecase/cmd.rb, line 229
def cleanup

end
config_directory() click to toggle source

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
config_file() click to toggle source

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() click to toggle source

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
flow_of_events() click to toggle source

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_master_database() click to toggle source

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
post_validation() click to toggle source

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
pre_validation() click to toggle source

Override me if you need to

# File lib/usecase/cmd.rb, line 174
def pre_validation

end

Private Instance Methods

add_secret_facts(fact_db) click to toggle source
# 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
create_header() click to toggle source
# 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
log_env() click to toggle source
# 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
ops_key_exists?() click to toggle source
# 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
print_already_initialized() click to toggle source
print_already_logged_in() click to toggle source
print_domain_initialized() click to toggle source
print_login_success() click to toggle source
print_not_initialized() click to toggle source
print_success_initializing() click to toggle source
print_unopened_envelope() click to toggle source
unopened_envelope?( key_database ) click to toggle source
# 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