lib/sconb.rb in sconb-1.0.0 vs lib/sconb.rb in sconb-1.1.0

- old
+ new

@@ -1,208 +1,8 @@ -require "sconb/version" -require "thor" -require "net/ssh" -require "json" +require 'net/ssh' +require 'sconb/version' +require 'sconb/ext' +require 'sconb/ssh_config' +require 'sconb/cli' module Sconb - class CLI < Thor - - method_option :all, :type => :boolean, :aliases => '-a', :default => false, :banner => 'dump .ssh/config and private keys.' - method_option :config, :type => :string, :aliases => '-c', :default => '~/.ssh/config', :banner => '.ssh/config path' - desc "dump > dump.json", "Dump .ssh/config to JSON" - def dump(regexp_str = '.*') - regexp = Regexp.new regexp_str - path = options[:config] - file = File.expand_path(path) - configs = {} - unless File.readable?(file) - puts configs - return - end - - allconfig = config_load(path, '*') - configs['*'] = allconfig unless allconfig.size <= 1 - IO.foreach(file) do |line| - next if line =~ /^\s*(?:#.*)?$/ - if line =~ /^\s*(\S+)\s*=(.*)$/ - key, value = $1, $2 - else - key, value = line.strip.split(/\s+/, 2) - end - next if value.nil? - - # Host - if key.downcase == 'host' - negative_hosts, positive_hosts = value.to_s.split(/\s+/).partition { |h| h.start_with?('!') } - positive_hosts.each do | host | - next if host == '*' - next unless host.match regexp - config = config_load(path, host) - - allconfig.each do |key, value| - next unless config.key? key - config.delete key if config[key] == allconfig[key] - end - - configs[host] = config - end - end - - # Match - if key.downcase == 'match' - match_key = key + ' ' + value - next unless match_key.match regexp - configs[match_key] = config_load(path, value) - end - - end - puts JSON.pretty_generate configs - end - - desc "restore < dump.json > .ssh/config", "Restore .ssh/config from JSON" - def restore() - ssh_configs = [] - json = stdin_read - configs = JSON.parse(json) - configs.each do |host, config| - ssh_config = '' - unless host.match(/^Match /) - ssh_config << 'Host ' + host + "\n" - else - ssh_config << host + "\n" - end - config.each do |key, value| - next if key.downcase == 'host' || key.downcase == 'match' || key.downcase == 'identityfilecontent' - if key.downcase == 'identityfile' - value.each_with_index do |keyfile,i| - ssh_config << ' ' + key + ' ' + keyfile + "\n" - end - else - ssh_config << ' ' + key + ' ' + value + "\n" - end - end - ssh_configs.push ssh_config - end - puts ssh_configs.join("\n") - end - - method_option :force, :type => :boolean, :aliases => '-f', :default => false, :banner => 'force generate' - desc "keyregen < dump.json", "Regenerate private keys from JSON" - def keyregen() - json = stdin_read - configs = JSON.parse(json) - configs.each do |host, config| - config.each do |key, value| - next unless key.downcase == 'identityfilecontent' - identity_files = config['IdentityFile'] - value.each_with_index do |keycontent,i| - identity_file = File.expand_path(identity_files[i]) - if File.exists?(identity_file) and !options[:force] - raise Thor::Error, "Error: " + identity_files[i] + " is exists. If you want to overwrite, use --force option." - end - puts 'Regenerate ' + identity_files[i] + ' ...' - File.open(identity_file, 'w') do |file| - file.write keycontent - end - File.chmod(0600, identity_file) - end - end - end - end - - private - def stdin_read() - return $stdin.read - end - - # Original code is Net::SSH::Config.load (https://github.com/net-ssh/net-ssh/blob/master/LICENSE.txt) - private - def config_load(path, host) - settings = {} - file = File.expand_path(path) - return settings unless File.readable?(file) - - globals = {} - matched_host = nil - multi_host = [] - seen_host = false - IO.foreach(file) do |line| - next if line =~ /^\s*(?:#.*)?$/ - - if line =~ /^\s*(\S+)\s*=(.*)$/ - key, value = $1, $2 - else - key, value = line.strip.split(/\s+/, 2) - end - - # silently ignore malformed entries - next if value.nil? - - value = $1 if value =~ /^"(.*)"$/ - - if key.downcase == 'host' - # Support "Host host1 host2 hostN". - # See http://github.com/net-ssh/net-ssh/issues#issue/6 - negative_hosts, positive_hosts = value.to_s.split(/\s+/).partition { |h| h.start_with?('!') } - - # Check for negative patterns first. If the host matches, that overrules any other positive match. - # The host substring code is used to strip out the starting "!" so the regexp will be correct. - negative_match = negative_hosts.select { |h| host =~ pattern2regex(h[1..-1]) }.first - - if negative_match - matched_host = nil - else - matched_host = positive_hosts.select { |h| host =~ pattern2regex(h) }.first - end - settings[key] = host unless matched_host.nil? - seen_host = true - elsif key.downcase == 'match' - if host == value - matched_host = true - else - matched_host = nil - end - settings[key] = host unless matched_host.nil? - seen_host = true - elsif !seen_host - if key.downcase == 'identityfile' - (globals[key] ||= []) << value - - # Read IdentityFile Content - identity_file = File.expand_path(value) - if options[:all] and File.readable?(identity_file) - (globals['IdentityFileContent'] ||= []) << File.open(identity_file).read - end - else - globals[key] = value unless settings.key?(key) - end - elsif !matched_host.nil? - if key.downcase == 'identityfile' - (settings[key] ||= []) << value - - # Read IdentityFile Content - identity_file = File.expand_path(value) - if options[:all] and File.readable?(identity_file) - (settings['IdentityFileContent'] ||= []) << File.open(identity_file).read - end - else - settings[key] = value unless settings.key?(key) - end - end - end - - settings = globals.merge(settings) if globals - - return settings - end - - private - # Original code is Net::SSH::Config.pattern2regex (https://github.com/net-ssh/net-ssh/blob/master/LICENSE.txt) - def pattern2regex(pattern) - pattern = "^" + pattern.to_s.gsub(/\./, "\\."). - gsub(/\?/, '.'). - gsub(/([+\/])/, '\\\\\\0'). - gsub(/\*/, '.*') + "$" - Regexp.new(pattern, true) - end - end end