Class: Aspera::Cli::Plugins::Config

Inherits:
Base
  • Object
show all
Includes:
SyncActions
Defined in:
lib/aspera/cli/plugins/config.rb

Overview

Manage the CLI config file

Constant Summary collapse

PRESET_GBL_ACTIONS =

Legacy actions available globally

%i[list overview lookup secure].freeze
PRESET_EXIST_ACTIONS =

Operations requiring that preset exists

%i[show delete get unset].freeze
PRESET_INSTANCE_ACTIONS =

require id

%i[initialize update ask set].concat(PRESET_EXIST_ACTIONS).freeze
PRESET_ALL_ACTIONS =
(PRESET_GBL_ACTIONS + PRESET_INSTANCE_ACTIONS).freeze
ACTIONS =
%i[
  preset
  open
  documentation
  genkey
  pubkey
  remote_certificate
  gem
  plugins
  tokens
  echo
  download
  wizard
  detect
  coffee
  image
  ascp
  sync
  transferd
  email_test
  smtp_settings
  proxy_check
  folder
  file
  check_update
  initdemo
  vault
  test
  platform
  completion
].freeze
EXTEND_ARGS =
:''

Constants included from SyncActions

SyncActions::STATE_STR

Instance Attribute Summary collapse

Attributes inherited from Base

#context

Class Method Summary collapse

Instance Method Summary collapse

Methods included from SyncActions

#async_info_from_args, #db_from_args, declare_options, #execute_sync_action, #execute_sync_admin

Methods inherited from Base

#add_manual_header, #config, declare_options, #do_bulk_operation, #entity_execute, #formatter, #http_config, #options, #persistency, #presets, #query_read_delete, #transfer, #value_create_modify

Constructor Details

#initialize(**_) ⇒ Config

Returns a new instance of Config.



83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/aspera/cli/plugins/config.rb', line 83

def initialize(**_)
  # We need to defer parsing of options until we have the config file, so we can use @extend with @preset
  super
  @vault_instance = nil
  @pac_exec = nil
  @sdk_default_location = false
  @option_cache_tokens = true
  @main_folder = nil
  @option_config_file = nil
  @progress_bar = nil
  # Option to set main folder
  options.declare(
    :home, 'Home folder for tool',
    handler: {o: self, m: :main_folder},
    default: self.class.default_app_main_folder(app_name: Info::CMD_NAME)
  )
  options.parse_options!
  Log.log.debug{"#{Info::CMD_NAME} folder: #{@main_folder}"}
  setup_persistency_and_plugin_folders
  # Option to set config file
  options.declare(
    :config_file, 'Path to YAML file with preset configuration',
    handler: {o: self, m: :option_config_file},
    default: File.join(@main_folder, DEFAULT_CONFIG_FILENAME)
  )
  options.parse_options!
  # Instantiate PresetManager (reads config file) and inject into context
  context.presets = PresetManager.new(config_file: @option_config_file)
  # Instantiate Http and inject into context
  context.http_config = Http.new
  setup_extended_value_handlers
  # Vault options
  options.declare(:secret, 'Secret for access keys')
  options.declare(:vault, 'Vault for secrets', allowed: Hash)
  options.declare(:vault_password, 'Vault password')
  options.parse_options!
  # Declare generic plugin options only after handlers are declared
  Base.declare_options(options)
  # Configuration options
  options.declare(:no_default, 'Do not load default configuration for plugin', allowed: Allowed::TYPES_NONE, short: 'N'){ presets.use_plugin_defaults = false }
  options.declare(:preset, 'Load the named option preset from current config file', short: 'P', handler: {o: self, m: :option_preset})
  options.declare(:version_check_days, 'Period in days to check new version (zero to disable)', allowed: Allowed::TYPES_INTEGER, default: DEFAULT_CHECK_NEW_VERSION_DAYS)
  options.declare(:plugin_folder, 'Folder where to find additional plugins', handler: {o: self, m: :option_plugin_folder})
  # Declare wizard options
  @wizard = Wizard.new(self, @main_folder)
  # Transfer SDK options
  options.declare(:sdk_url, 'Ascp: URL to get Aspera Transfer Executables', default: SpecialValues::DEF)
  options.parse_options!
  set_sdk_dir
  options.declare(:locations_url, 'Ascp: URL to get download locations of Aspera Transfer Daemon', handler: {o: Ascp::Installation.instance, m: :transferd_urls})
  options.declare(:sdk_folder, 'Ascp: Path to folder with ascp (or product with "product:")', handler: {o: Products::Transferd, m: :sdk_directory})
  options.declare(:progress_bar, 'Display progress bar', allowed: Allowed::TYPES_BOOLEAN, default: Environment.terminal?)
  # Email options
  options.declare(:smtp, 'Email: SMTP configuration', allowed: Hash)
  options.declare(:notify_to, 'Email: Recipient for notification of transfers')
  options.declare(:notify_template, 'Email: ERB template for notification of transfers')
  # HTTP options — declared by HttpConfig itself
  context.http_config.declare_options(options)
  options.declare(:cache_tokens, 'Save and reuse OAuth tokens', allowed: Allowed::TYPES_BOOLEAN, handler: {o: self, m: :option_cache_tokens})
  options.declare(:fpac, 'Proxy auto configuration script')
  options.declare(:proxy_credentials, 'HTTP proxy credentials for fpac: user, password', allowed: [Array, NilClass])
  options.parse_options!
  @progress_bar = TransferProgress.new if options.get_option(:progress_bar)
  setup_pac_executor
  setup_rest_and_transfer_runtime
end

Instance Attribute Details

#gem_urlObject (readonly)

Returns the value of attribute gem_url.



332
333
334
# File 'lib/aspera/cli/plugins/config.rb', line 332

def gem_url
  @gem_url
end

#main_folderObject

Returns the value of attribute main_folder.



210
211
212
# File 'lib/aspera/cli/plugins/config.rb', line 210

def main_folder
  @main_folder
end

#option_cache_tokensObject

Returns the value of attribute option_cache_tokens.



210
211
212
# File 'lib/aspera/cli/plugins/config.rb', line 210

def option_cache_tokens
  @option_cache_tokens
end

#option_config_fileObject

Returns the value of attribute option_config_file.



333
334
335
# File 'lib/aspera/cli/plugins/config.rb', line 333

def option_config_file
  @option_config_file
end

#progress_barObject (readonly)

Returns the value of attribute progress_bar.



211
212
213
# File 'lib/aspera/cli/plugins/config.rb', line 211

def progress_bar
  @progress_bar
end

Class Method Details

.deep_clone(val) ⇒ Object

Deep clone hash so that it does not get modified in case of display and secret hide



64
65
66
# File 'lib/aspera/cli/plugins/config.rb', line 64

def deep_clone(val)
  return Marshal.load(Marshal.dump(val))
end

.default_app_main_folder(app_name:) ⇒ String

Returns Product config folder (~/.aspera/).

Returns:

  • (String)

    Product config folder (~/.aspera/)



76
77
78
79
80
# File 'lib/aspera/cli/plugins/config.rb', line 76

def default_app_main_folder(app_name:)
  Aspera.assert_type(app_name, String)
  Aspera.assert(!app_name.empty?, 'app_name must not be empty')
  return File.join(module_family_folder, app_name)
end

.gem_plugins_folderObject

Folder containing plugins in the gem's main folder



52
53
54
# File 'lib/aspera/cli/plugins/config.rb', line 52

def gem_plugins_folder
  File.dirname(File.expand_path(__FILE__))
end

.gem_src_rootObject

Go up as many times as englobing modules (not counting class, as it is a file)

Returns:

  • main folder where code is, i.e. .../lib



58
59
60
61
# File 'lib/aspera/cli/plugins/config.rb', line 58

def gem_src_root
  # Module.nesting[2] is Cli::Plugins
  File.expand_path(Module.nesting[2].to_s.gsub('::', '/').gsub(%r{[^/]+}, '..'), gem_plugins_folder)
end

.module_family_folderObject

Returns product family folder (~/.aspera).

Returns:

  • product family folder (~/.aspera)



69
70
71
72
73
# File 'lib/aspera/cli/plugins/config.rb', line 69

def module_family_folder
  user_home_folder = Dir.home
  Aspera.assert(Dir.exist?(user_home_folder), type: Cli::Error){"Home folder does not exist: #{user_home_folder}. Check your user environment."}
  return File.join(user_home_folder, ASPERA_HOME_FOLDER_NAME)
end

Instance Method Details

#add_plugin_default_preset(plugin_name_sym) ⇒ Object

Delegation to PresetManager: loads default preset options for a plugin



297
298
299
300
301
# File 'lib/aspera/cli/plugins/config.rb', line 297

def add_plugin_default_preset(plugin_name_sym)
  default_config_name = presets.plugin_default_name(plugin_name_sym)
  Log.log.debug{"add_plugin_default_preset:#{plugin_name_sym}:#{default_config_name}"}
  options.add_option_preset(presets.by_name(default_config_name), 'default_plugin', override: false) unless default_config_name.nil?
end

#check_gem_versionObject



247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
# File 'lib/aspera/cli/plugins/config.rb', line 247

def check_gem_version
  latest_version =
    begin
      Rest.new(base_url: 'https://rubygems.org/api/v1').read("versions/#{Info::GEM_NAME}/latest.json")['version']
    rescue StandardError
      Log.log.warn('Could not retrieve latest gem version on rubygems.')
      '0'
    end
  if Gem::Version.new(Environment.ruby_version) < Gem::Version.new(Info::RUBY_FUTURE_MINIMUM_VERSION)
    Log.log.warn do
      "Note that a future version will require Ruby version #{Info::RUBY_FUTURE_MINIMUM_VERSION} at minimum, " \
        "you are using #{Environment.ruby_version}"
    end
  end
  return {
    name:        Info::GEM_NAME,
    current:     Cli::VERSION,
    latest:      latest_version,
    need_update: Gem::Version.new(Cli::VERSION) < Gem::Version.new(latest_version)
  }
end

#defaults_set(plugin_name, preset_name, preset_values, option_default, option_override) ⇒ Object

Delegations to PresetManager kept for backward compatibility



304
305
306
# File 'lib/aspera/cli/plugins/config.rb', line 304

def defaults_set(plugin_name, preset_name, preset_values, option_default, option_override)
  presets.defaults_set(plugin_name, preset_name, preset_values, option_default, option_override)
end

#email_settingsHash

Returns email server setting with defaults if not defined.

Returns:

  • (Hash)

    email server setting with defaults if not defined

Raises:



753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
# File 'lib/aspera/cli/plugins/config.rb', line 753

def email_settings
  smtp = options.get_option(:smtp, mandatory: true)
  # Change keys from string into symbol
  smtp = smtp.symbolize_keys
  unsupported = smtp.keys - SMTP_CONF_PARAMS
  raise Cli::Error, "Unsupported SMTP parameter: #{unsupported.join(', ')}, use: #{SMTP_CONF_PARAMS.join(', ')}" unless unsupported.empty?
  # Defaults
  # smtp[:ssl] = nil (false)
  smtp[:tls] = !smtp[:ssl] unless smtp.key?(:tls)
  smtp[:port] ||= if smtp[:tls]
    587
  elsif smtp[:ssl]
    465
  else
    25
  end
  smtp[:from_email] ||= smtp[:username] if smtp.key?(:username)
  smtp[:from_name] ||= smtp[:from_email].sub(/@.*$/, '').gsub(/[^a-zA-Z]/, ' ').capitalize if smtp.key?(:username)
  smtp[:domain] ||= smtp[:from_email].sub(/^.*@/, '') if smtp.key?(:from_email)
  # Check minimum required
  %i[server port domain].each do |n|
    Aspera.assert(smtp.key?(n)){"Missing mandatory smtp parameter: #{n}"}
  end
  Log.log.debug{"smtp=#{smtp}"}
  return smtp
end

#execute_actionObject

Main action procedure for plugin



563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
# File 'lib/aspera/cli/plugins/config.rb', line 563

def execute_action
  action = options.get_next_command(ACTIONS)
  case action
  when :preset # Newer syntax
    return execute_preset
  when :open
    Environment.instance.open_editor(@option_config_file.to_s)
    return Result::Nothing.new
  when :documentation
    section = options.get_next_argument('private key file path', mandatory: false)
    section = "##{section}" unless section.nil?
    Environment.instance.open_uri("#{Info::DOC_URL}#{section}")
    return Result::Nothing.new
  when :genkey # Generate new rsa key
    private_key_path = options.get_next_argument('private key file path')
    private_key_length = options.get_next_argument('size in bits', mandatory: false, validation: Integer, default: OAuth::Jwt::DEFAULT_PRIV_KEY_LENGTH)
    OAuth::Jwt.generate_rsa_private_key(path: private_key_path, length: private_key_length)
    return Result::Status.new("Generated #{private_key_length} bit RSA key: #{private_key_path}")
  when :pubkey # Get pub key
    private_key_pem = options.get_next_argument('private key PEM value')
    return Result::Text.new(OpenSSL::PKey::RSA.new(private_key_pem).public_key.to_s)
  when :remote_certificate
    cert_action = options.get_next_command(%i[chain only name])
    remote_url = options.get_next_argument('remote URL')
    remote_chain = Rest.remote_certificate_chain(remote_url, as_string: false)
    raise "No certificate found for #{remote_url}" unless remote_chain&.first
    case cert_action
    when :chain
      return Result::Text.new(remote_chain.map(&:to_pem).join("\n"))
    when :only
      return Result::Text.new(remote_chain.first.to_pem)
    when :name
      return Result::Text.new(remote_chain.first.subject.to_a.find{ |name, _, _| name == 'CN'}[1])
    end
  when :echo # Display the content of a value given on command line
    return Result.auto(options.get_next_argument('value', validation: nil))
  when :download
    file_url = options.get_next_argument('source URL').chomp
    file_dest = options.get_next_argument('file path', mandatory: false)
    file_dest = File.join(transfer.destination_folder(Transfer::Spec::DIRECTION_RECEIVE), file_url.gsub(%r{.*/}, '')) if file_dest.nil?
    Log.log.info("Downloading: #{file_url}")
    Rest.new(base_url: file_url).call(operation: 'GET', save_to_file: file_dest)
    return Result::Status.new("Saved to: #{file_dest}")
  when :tokens
    require 'aspera/api/node'
    case options.get_next_command(%i{flush list show})
    when :flush
      return Result::ValueList.new(OAuth::Factory.instance.flush_tokens, name: 'file')
    when :list
      return Result::ObjectList.new(OAuth::Factory.instance.persisted_tokens)
    when :show
      data = OAuth::Factory.instance.get_token_info(options.instance_identifier)
      raise Cli::Error, 'Unknown identifier' if data.nil?
      return Result::SingleObject.new(data)
    end
  when :plugins
    case options.get_next_command(%i[list create])
    when :list
      result = []
      Plugins::Factory.instance.plugin_list.each do |name|
        plugin_class = Plugins::Factory.instance.plugin_class(name)
        result.push({
          plugin: name,
          detect: TerminalFormatter.tick(plugin_class.respond_to?(:detect)),
          wizard: TerminalFormatter.tick(plugin_class.method_defined?(:wizard)),
          path:   Plugins::Factory.instance.plugin_source(name)
        })
      end
      return Result::ObjectList.new(result, fields: %w[plugin detect wizard path])
    when :create
      plugin_name = options.get_next_argument('name').downcase
      destination_folder = options.get_next_argument('folder', mandatory: false) || File.join(@main_folder, ASPERA_PLUGINS_FOLDERNAME)
      plugin_file = File.join(destination_folder, "#{plugin_name}.rb")
      content = <<~END_OF_PLUGIN_CODE
        require 'aspera/cli/plugins/base'
        module Aspera
          module Cli
            module Plugins
              class #{plugin_name.snake_to_capital} < Base
                ACTIONS=[]
                def execute_action
                  return Result::Status.new('You called plugin #{plugin_name}')
                end
              end
            end
          end
        end
      END_OF_PLUGIN_CODE
      File.write(plugin_file, content)
      return Result::Status.new("Created #{plugin_file}")
    end
  when :detect, :wizard
    # Interactive mode
    options.ask_missing_mandatory = true
    # Detect plugins by url and optional query
    apps = @wizard.identify_plugins_for_url.freeze
    return Result::ObjectList.new(apps) if action.eql?(:detect)
    return @wizard.find(apps)
  when :coffee
    return Result::Image.new(COFFEE_IMAGE_URL)
  when :image
    return Result::Image.new(options.get_next_argument('image URI or blob'))
  when :ascp
    execute_action_ascp
  when :sync
    SyncActions.declare_options(options)
    case options.get_next_command(%i[spec admin translate])
    when :spec
      builder = Schema::Documentation.new(TerminalFormatter, Sync::Operations::CONF_SCHEMA, include_option: true).build
      return Result::ObjectList.new(builder.rows, fields: builder.columns)
    when :admin
      return execute_sync_admin
    when :translate
      return Result::SingleObject.new(Sync::Operations.args_to_conf(options.get_next_argument('async arguments', multiple: true)))
    else Aspera.error_unreachable_line
    end
  when :transferd
    execute_action_transferd
  when :gem
    case options.get_next_command(%i[path version name])
    when :path then return Result::Text.new(self.class.gem_src_root)
    when :version then return Result::Text.new(Cli::VERSION)
    when :name then return Result::Text.new(Info::GEM_NAME)
    else Aspera.error_unreachable_line
    end
  when :folder
    return Result::Text.new(@main_folder)
  when :file
    return Result::Text.new(@option_config_file)
  when :email_test
    send_email_template(email_template_default: EMAIL_TEST_TEMPLATE)
    return Result::Nothing.new
  when :smtp_settings
    return Result::SingleObject.new(email_settings)
  when :proxy_check
    # Ensure fpac was provided
    options.get_option(:fpac, mandatory: true)
    server_url = options.get_next_argument('server url')
    return Result::ValueList.new(@pac_exec.get_proxies(server_url), name: 'proxy')
  when :check_update
    return Result::SingleObject.new(check_gem_version)
  when :initdemo
    cp = presets.config_presets
    if cp.key?(DEMO_PRESET)
      Log.log.warn{"Demo server preset already present: #{DEMO_PRESET}"}
    else
      Log.log.info{"Creating Demo server preset: #{DEMO_PRESET}"}
      cp[DEMO_PRESET] = {
        'url'                                    => "ssh://#{DEMO_SERVER}.asperasoft.com:33001",
        'username'                               => ASPERA,
        'ssAP'.downcase.reverse + 'drow'.reverse => DEMO_SERVER + ASPERA # cspell:disable-line
      }
    end
    cp[PresetManager::Key::DEFAULTS] ||= {}
    if cp[PresetManager::Key::DEFAULTS].key?(SERVER_COMMAND)
      Log.log.warn{"Server default preset already set to: #{cp[PresetManager::Key::DEFAULTS][SERVER_COMMAND]}"}
      Log.log.warn{"Use #{DEMO_PRESET} for demo: -P#{DEMO_PRESET}"} unless
        DEMO_PRESET.eql?(cp[PresetManager::Key::DEFAULTS][SERVER_COMMAND])
    else
      cp[PresetManager::Key::DEFAULTS][SERVER_COMMAND] = DEMO_PRESET
      Log.log.info{"Setting server default preset to : #{DEMO_PRESET}"}
    end
    return Result::Status.new('Done')
  when :vault then execute_vault
  when :test then return execute_test
  when :platform
    return Result::Text.new(Environment.instance.architecture)
  when :completion
    return execute_completion
  else Aspera.error_unreachable_line
  end
end

#execute_action_ascpObject



366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
# File 'lib/aspera/cli/plugins/config.rb', line 366

def execute_action_ascp
  command = options.get_next_command(%i[show products info install spec schema errors])
  case command
  when :show
    return Result::Text.new(Ascp::Installation.instance.path(:ascp))
  when :info
    # Collect info from ascp executable
    data = Ascp::Installation.instance.ascp_info
    # Add command line transfer spec
    data['ts'] = transfer.user_transfer_spec
    # Add keys
    DataRepository::ELEMENTS.each_with_object(data){ |i, h| h[i.to_s] = DataRepository.instance.item(i)}
    # Declare those as secrets
    SecretHider::ADDITIONAL_KEYS_TO_HIDE.concat(DataRepository::ELEMENTS.map(&:to_s))
    return Result::SingleObject.new(data)
  when :products
    command = options.get_next_command(%i[list])
    case command
    when :list
      return Result::ObjectList.new(Ascp::Installation.instance.installed_products, fields: %w[name app_root])
    end
  when :install
    return install_transfer_sdk
  when :spec
    builder = Schema::Documentation.new(TerminalFormatter, Transfer::Spec::SCHEMA, include_option: true, agent_columns: true).build
    return Result::ObjectList.new(builder.rows, fields: builder.columns)
  when :schema
    schema = Transfer::Spec::SCHEMA.current.merge({'$comment'=>'DO NOT EDIT, this file was generated from the YAML.'})
    agent = options.get_next_argument('transfer agent name', mandatory: false)
    schema['properties'] = schema['properties'].select{ |_k, v| CommandLineBuilder.supported_by_agent(agent, v)} unless agent.nil?
    schema['properties'] = schema['properties'].sort.to_h
    return Result::SingleObject.new(schema)
  when :errors
    error_data = []
    Ascp::Management::ERRORS.each_pair do |code, prop|
      error_data.push(code: code, mnemonic: prop[:c], retry: prop[:r], info: prop[:a])
    end
    return Result::ObjectList.new(error_data)
  else Aspera.error_unexpected_value(command)
  end
  Aspera.error_unreachable_line
end

#execute_action_transferdObject



409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
# File 'lib/aspera/cli/plugins/config.rb', line 409

def execute_action_transferd
  command = options.get_next_command(%i[list install])
  case command
  when :install
    return install_transfer_sdk
  when :list
    sdk_list = Ascp::Installation.instance.sdk_locations
    return Result::ObjectList.new(
      sdk_list,
      fields: sdk_list.first.keys - ['url']
    )
  else Aspera.error_unexpected_value(command)
  end
  Aspera.error_unreachable_line
end

#execute_completionResult

Generate shell completion

Returns:

  • (Result)

    completion result



738
739
740
741
742
743
744
745
746
747
748
749
750
# File 'lib/aspera/cli/plugins/config.rb', line 738

def execute_completion
  shell_type = options.get_next_command(%i[bash])
  case shell_type
  when :bash
    if options.get_next_argument('', multiple: true, mandatory: false).nil?
      Plugins::Factory.instance.plugin_list.each{ |p| puts p}
    else
      Log.log.warn('only first level completion so far')
    end
    Process.exit(0)
  else Aspera.error_unreachable_line
  end
end

#execute_preset(action: nil, name: nil) ⇒ Object



433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
# File 'lib/aspera/cli/plugins/config.rb', line 433

def execute_preset(action: nil, name: nil)
  cp = presets.config_presets
  action = options.get_next_command(PRESET_ALL_ACTIONS) if action.nil?
  name = options.instance_identifier if name.nil? && PRESET_INSTANCE_ACTIONS.include?(action)
  name = presets.global_default_preset if name.eql?(GLOBAL_DEFAULT_KEYWORD)
  # Those operations require existing option
  raise "no such preset: #{name}" if PRESET_EXIST_ACTIONS.include?(action) && !cp.key?(name)
  case action
  when :list
    return Result::ValueList.new(cp.keys, name: 'name')
  when :overview
    # Display process modifies the value (hide secrets): we do not want to save removed secrets
    data = PresetManager.deep_clone(cp)
    formatter.hide_secrets(data)
    result = []
    data.each do |config, preset|
      preset.each do |parameter, value|
        result.push(CONF_OVERVIEW_KEYS.zip([config, parameter, value]).to_h)
      end
    end
    return Result::ObjectList.new(result, fields: CONF_OVERVIEW_KEYS)
  when :show
    return Result::SingleObject.new(PresetManager.deep_clone(cp[name]))
  when :delete
    cp.delete(name)
    return Result::Status.new("Deleted: #{name}")
  when :get
    param_name = options.get_next_argument('parameter name')
    value = cp[name][param_name]
    raise "no such option in preset #{name} : #{param_name}" if value.nil?
    case value
    when Numeric, String then return Result::Text.new(ExtendedValue.instance.evaluate(value.to_s, context: 'preset'))
    end
    return Result::SingleObject.new(value)
  when :unset
    param_name = options.get_next_argument('parameter name')
    cp[name].delete(param_name)
    return Result::Status.new("Removed: #{name}: #{param_name}")
  when :set
    param_name = options.get_next_argument('parameter name')
    param_name = Manager.option_line_to_name(param_name)
    param_value = options.get_next_argument('parameter value', validation: nil)
    set_preset_key(name, param_name, param_value)
    return Result::Nothing.new
  when :initialize
    config_value = options.get_next_argument('extended value', validation: Hash)
    Log.log.warn{"configuration already exists: #{name}, overwriting"} if cp.key?(name)
    cp[name] = config_value
    return Result::Status.new("Modified: #{@option_config_file}")
  when :update
    unprocessed_options = options.unprocessed_options_with_value
    Log.log.debug{"opts=#{unprocessed_options}"}
    cp[name] ||= {}
    cp[name].merge!(unprocessed_options)
    return Result::Status.new("Updated: #{name}")
  when :ask
    options.ask_missing_mandatory = true
    cp[name] ||= {}
    options.get_next_argument('option names', multiple: true).each do |option_name|
      option_value = options.get_interactive(option_name, check_option: true)
      cp[name][option_name] = option_value
    end
    return Result::Status.new("Updated: #{name}")
  when :lookup
    BasicAuth.declare_options(options)
    url = options.get_option(:url, mandatory: true)
    user = options.get_option(:username, mandatory: true)
    result = lookup_preset(url: url, username: user)
    raise Error, 'no such config found' if result.nil?
    return Result::SingleObject.new(result)
  when :secure
    identifier = options.get_next_argument('config name', mandatory: false)
    preset_names = identifier.nil? ? cp.keys : [identifier]
    secret_keywords = %w[password secret].freeze
    preset_names.each do |preset_name|
      preset = cp[preset_name]
      next unless preset.is_a?(Hash)
      preset.each_key do |option_name|
        secret_keywords.each do |keyword|
          next unless option_name.end_with?(keyword)
          vault_label = preset_name
          incr = 0
          until vault.get(label: vault_label, exception: false).nil?
            vault_label = "#{preset_name}#{incr}"
            incr += 1
          end
          to_set = {label: vault_label, password: preset[option_name]}
          puts "need to encode #{preset_name}.#{option_name} -> #{vault_label} -> #{to_set}"
          vault.set(to_set)
          preset[option_name] = "@vault:#{vault_label}.password"
        end
      end
    end
    return Result::Status.new('Secrets secured in vault: Make sure to save the vault password securely.')
  end
end

#execute_testObject

Artificially raise an exception for tests



865
866
867
868
869
870
871
872
873
874
875
876
877
# File 'lib/aspera/cli/plugins/config.rb', line 865

def execute_test
  case options.get_next_command(%i[throw web])
  when :throw
    # :type [String]
    # Options
    exception_class_name = options.get_next_argument('exception class name', mandatory: true)
    exception_text = options.get_next_argument('exception text', mandatory: true)
    type = Object.const_get(exception_class_name)
    Aspera.assert(type <= Exception){"#{type} is not an exception: #{type.class}"}
    raise type, exception_text
  when :web
  end
end

#execute_vaultHash

Returns result of execution of vault command.

Returns:

  • (Hash)

    result of execution of vault command



815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
# File 'lib/aspera/cli/plugins/config.rb', line 815

def execute_vault
  command = options.get_next_command(%i[info list show create delete password])
  case command
  when :info
    return Result::SingleObject.new(vault.info)
  when :list
    # , fields: %w(label url username password description)
    return Result::ObjectList.new(vault.list)
  when :show
    return Result::SingleObject.new(vault.get(label: options.get_next_argument('label')))
  when :create
    vault.set(options.get_next_argument('info', validation: Hash).symbolize_keys)
    return Result::Status.new('Secret added')
  when :delete
    label_to_delete = options.get_next_argument('label')
    vault.delete(label: label_to_delete)
    return Result::Status.new("Secret deleted: #{label_to_delete}")
  when :password
    Aspera.assert(vault.respond_to?(:change_password), 'Vault does not support password change')
    vault.change_password(options.get_next_argument('new_password'))
    return Result::Status.new('Vault password updated')
  end
end

#get_plugin_default_config_name(plugin_name_sym) ⇒ Object



320
321
322
# File 'lib/aspera/cli/plugins/config.rb', line 320

def get_plugin_default_config_name(plugin_name_sym)
  presets.plugin_default_name(plugin_name_sym)
end

#ignore_cert?(address, port) ⇒ Boolean

Returns:

  • (Boolean)


221
# File 'lib/aspera/cli/plugins/config.rb', line 221

def ignore_cert?(address, port); context.http_config.ignore_cert?(address, port); end

#install_transfer_sdkObject



358
359
360
361
362
363
364
# File 'lib/aspera/cli/plugins/config.rb', line 358

def install_transfer_sdk
  asked_version = options.get_next_argument('transferd version', mandatory: false)
  sdk_url = options.get_option(:sdk_url, mandatory: true)
  sdk_url = nil if sdk_url.eql?(SpecialValues::DEF)
  name, version, folder = Ascp::Installation.instance.retrieve_sdk(url: sdk_url, version: asked_version)
  return Result::Status.new("Installed #{name} version #{version} in #{folder}")
end

#lookup_preset(url:, username:) ⇒ Object



328
329
330
# File 'lib/aspera/cli/plugins/config.rb', line 328

def lookup_preset(url:, username:)
  presets.lookup_preset(url: url, username: username)
end

#lookup_secret(url:, username:) ⇒ String?

Lookup the corresponding secret for the given URL and usernames

Parameters:

  • url (String)

    Server URL

  • username (String)

    Username

Returns:

  • (String, nil)

    Secret if found



883
884
885
886
887
888
889
890
891
892
893
# File 'lib/aspera/cli/plugins/config.rb', line 883

def lookup_secret(url:, username:)
  secret = options.get_option(:secret)
  if secret.eql?('PRESET')
    conf = presets.lookup_preset(url: url, username: username)
    if conf.is_a?(Hash)
      Log.log.debug{"Found preset #{conf} with URL and username"}
      secret = conf['password']
    end
  end
  return secret
end

#option_http_optionsObject



218
# File 'lib/aspera/cli/plugins/config.rb', line 218

def option_http_options;        context.http_config.http_options; end

#option_http_options=(v) ⇒ Object



219
# File 'lib/aspera/cli/plugins/config.rb', line 219

def option_http_options=(v);    context.http_config.http_options = v; end

#option_ignore_cert_host_portObject



220
# File 'lib/aspera/cli/plugins/config.rb', line 220

def option_ignore_cert_host_port; context.http_config.ignore_cert_host_port; end

#option_insecureObject

Delegations to http_config kept for backward compatibility with transfer_agent and plugins



214
# File 'lib/aspera/cli/plugins/config.rb', line 214

def option_insecure;            context.http_config.insecure; end

#option_insecure=(v) ⇒ Object



215
# File 'lib/aspera/cli/plugins/config.rb', line 215

def option_insecure=(v);        context.http_config.insecure = v; end

#option_plugin_folderObject



341
342
343
# File 'lib/aspera/cli/plugins/config.rb', line 341

def option_plugin_folder
  return Plugins::Factory.instance.lookup_folders
end

#option_plugin_folder=(value) ⇒ Object



335
336
337
338
339
# File 'lib/aspera/cli/plugins/config.rb', line 335

def option_plugin_folder=(value)
  value = [value] unless value.is_a?(Array)
  Aspera.assert_array_all(value, String){'plugin folder(s)'}
  value.each{ |f| Plugins::Factory.instance.add_lookup_folder(f)}
end

#option_presetObject



345
# File 'lib/aspera/cli/plugins/config.rb', line 345

def option_preset; 'write-only option'; end

#option_preset=(value) ⇒ Object



347
348
349
350
351
352
353
354
355
356
# File 'lib/aspera/cli/plugins/config.rb', line 347

def option_preset=(value)
  case value
  when Hash
    options.add_option_preset(value, 'set')
  when String
    options.add_option_preset(preset_by_name(value), 'set_by_name')
  else
    raise BadArgument, 'Preset definition must be a String for preset name, or Hash for set of values'
  end
end

#option_warn_insecure_certObject



216
# File 'lib/aspera/cli/plugins/config.rb', line 216

def option_warn_insecure_cert;  context.http_config.warn_insecure; end

#option_warn_insecure_cert=(v) ⇒ Object



217
# File 'lib/aspera/cli/plugins/config.rb', line 217

def option_warn_insecure_cert=(v); context.http_config.warn_insecure = v; end

#periodic_check_newer_gem_versionObject



269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
# File 'lib/aspera/cli/plugins/config.rb', line 269

def periodic_check_newer_gem_version
  # Get verification period
  delay_days = options.get_option(:version_check_days, mandatory: true).to_i
  # Check only if not zero day
  return if delay_days.eql?(0)
  # Get last date from persistency
  last_check_array = []
  check_date_persist = PersistencyActionOnce.new(
    manager: persistency,
    data:    last_check_array,
    id:      'version_last_check'
  )
  # Get persisted date or nil
  current_date = Date.today
  last_check_days = (current_date - Date.strptime(last_check_array.first, GEM_CHECK_DATE_FMT)) rescue nil
  Log.log.debug{"gem check new version: #{delay_days}, #{last_check_days}, #{current_date}, #{last_check_array}"}
  return if !last_check_days.nil? && last_check_days < delay_days
  # Generate timestamp
  last_check_array[0] = current_date.strftime(GEM_CHECK_DATE_FMT)
  check_date_persist.save
  # Compare this version and the one on internet
  check_data = check_gem_version
  Log.log.warn do
    "A new version is available: #{check_data[:latest]}. You have #{check_data[:current]}. Upgrade with: gem update #{check_data[:name]}"
  end if check_data[:need_update]
end

#preset_by_name(config_name, include_path = []) ⇒ Object



316
317
318
# File 'lib/aspera/cli/plugins/config.rb', line 316

def preset_by_name(config_name, include_path = [])
  presets.by_name(config_name, include_path)
end

#save_config_file_if_neededObject



324
325
326
# File 'lib/aspera/cli/plugins/config.rb', line 324

def save_config_file_if_needed
  presets.save_if_needed
end

#send_email_template(email_template_default: nil, values: {}) ⇒ Object

Send email using ERB template

Parameters:

  • email_template_default (String) (defaults to: nil)

    default template, can be overridden by option

  • values (Hash) (defaults to: {})

    values to be used in template, keys with default: to, from_name, from_email



783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
# File 'lib/aspera/cli/plugins/config.rb', line 783

def send_email_template(email_template_default: nil, values: {})
  values[:to] ||= options.get_option(:notify_to, mandatory: true)
  notify_template = options.get_option(:notify_template, mandatory: email_template_default.nil?) || email_template_default
  mail_conf = email_settings
  values[:from_name] ||= mail_conf[:from_name]
  values[:from_email] ||= mail_conf[:from_email]
  %i[to from_email].each do |n|
    Aspera.assert_type(values[n], String){"Missing email parameter: #{n} in config"}
  end
  start_options = [mail_conf[:domain]]
  start_options.push(mail_conf[:username], mail_conf[:password], :login) if mail_conf.key?(:username) && mail_conf.key?(:password)
  # Create a binding with only variables defined in values
  template_binding = Environment.empty_binding
  # Add variables to binding
  values.each do |k, v|
    Aspera.assert_type(k, Symbol)
    template_binding.local_variable_set(k, v)
  end
  # Execute template
  msg_with_headers = ERB.new(notify_template).result(template_binding)
  Log.dump(:msg_with_headers, msg_with_headers)
  require 'net/smtp'
  smtp = Net::SMTP.new(mail_conf[:server], mail_conf[:port])
  smtp.enable_starttls if mail_conf[:tls]
  smtp.enable_tls if mail_conf[:ssl]
  smtp.start(*start_options) do |smtp_session|
    smtp_session.send_message(msg_with_headers, values[:from_email], values[:to])
  end
  nil
end

#set_global_default(key, value) ⇒ Object



312
313
314
# File 'lib/aspera/cli/plugins/config.rb', line 312

def set_global_default(key, value)
  presets.set_global_default(key, value)
end

#set_preset_key(preset, param_name, param_value) ⇒ Object



308
309
310
# File 'lib/aspera/cli/plugins/config.rb', line 308

def set_preset_key(preset, param_name, param_value)
  presets.set_key(preset, param_name, param_value)
end

#set_sdk_dirObject



225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
# File 'lib/aspera/cli/plugins/config.rb', line 225

def set_sdk_dir
  # Check SDK folder is set or not, for compatibility, we check in two places
  sdk_dir = Products::Transferd.sdk_directory rescue nil
  if sdk_dir.nil?
    @sdk_default_location = true
    Log.log.debug('SDK folder is not set, checking default')
    # New location
    sdk_dir = self.class.default_app_main_folder(app_name: TRANSFERD_APP_NAME)
    Log.log.debug{"Checking: #{sdk_dir}"}
    if !Dir.exist?(sdk_dir)
      Log.log.debug{"No such folder: #{sdk_dir}"}
      # Former location
      former_sdk_folder = File.join(self.class.default_app_main_folder(app_name: Info::CMD_NAME), TRANSFERD_APP_NAME)
      Log.log.debug{"Checking: #{former_sdk_folder}"}
      sdk_dir = former_sdk_folder if Dir.exist?(former_sdk_folder)
    end
    Log.log.debug{"Using: #{sdk_dir}"}
    Products::Transferd.sdk_directory = sdk_dir
  end
end

#trusted_cert_locationsObject



222
# File 'lib/aspera/cli/plugins/config.rb', line 222

def trusted_cert_locations;     context.http_config.trusted_cert_locations; end

#trusted_cert_locations=(v) ⇒ Object



223
# File 'lib/aspera/cli/plugins/config.rb', line 223

def trusted_cert_locations=(v); context.http_config.trusted_cert_locations = v; end

#vaultObject

Returns vault, from options or cache.

Returns:

  • (Object)

    vault, from options or cache



851
852
853
854
855
856
857
858
859
860
861
862
# File 'lib/aspera/cli/plugins/config.rb', line 851

def vault
  return @vault_instance unless @vault_instance.nil?
  info = options.get_option(:vault).symbolize_keys
  info[:type] ||= 'file'
  require 'aspera/keychain/factory'
  @vault_instance = Keychain::Factory.create(
    info,
    Info::CMD_NAME,
    @main_folder,
    options.get_option(:vault_password)
  )
end

#vault_value(name) ⇒ String

Returns value from vault matching ..

Returns:

  • (String)

    value from vault matching .

Raises:



840
841
842
843
844
845
846
847
848
# File 'lib/aspera/cli/plugins/config.rb', line 840

def vault_value(name)
  m = name.split('.')
  raise BadArgument, 'vault name shall match <name>.<param>' unless m.length.eql?(2)
  # This raise exception if label not found:
  info = vault.get(label: m[0])
  value = info[m[1].to_sym]
  raise "no such entry value: #{m[1]}" if value.nil?
  return value
end