From c6d2272ddb370b9731e17b06fa08971e6cda5202 Mon Sep 17 00:00:00 2001 From: elijah Date: Sun, 14 Oct 2012 03:02:06 -0700 Subject: added add-user command --- lib/leap_cli/commands/compile.rb | 2 +- lib/leap_cli/commands/init.rb | 2 +- lib/leap_cli/commands/pre.rb | 4 +- lib/leap_cli/commands/user.rb | 106 ++++++++++++++++++++++++ lib/leap_cli/commands/util.rb | 125 ++++++++++++++++++++++++++++ lib/leap_cli/config/manager.rb | 18 +++-- lib/leap_cli/log.rb | 19 +++-- lib/leap_cli/path.rb | 10 --- lib/leap_cli/util.rb | 171 +++++++++++++++++++++++++++++++++++++++ 9 files changed, 425 insertions(+), 32 deletions(-) create mode 100644 lib/leap_cli/commands/user.rb create mode 100644 lib/leap_cli/commands/util.rb create mode 100644 lib/leap_cli/util.rb (limited to 'lib/leap_cli') diff --git a/lib/leap_cli/commands/compile.rb b/lib/leap_cli/commands/compile.rb index 8764e52..3e9d42d 100644 --- a/lib/leap_cli/commands/compile.rb +++ b/lib/leap_cli/commands/compile.rb @@ -5,7 +5,7 @@ module LeapCli command :compile do |c| c.action do |global_options,options,args| manager.load(Path.provider) - Path.ensure_dir(Path.hiera) + ensure_dir(Path.hiera) manager.export(Path.hiera) end end diff --git a/lib/leap_cli/commands/init.rb b/lib/leap_cli/commands/init.rb index 75cc876..de43a45 100644 --- a/lib/leap_cli/commands/init.rb +++ b/lib/leap_cli/commands/init.rb @@ -7,7 +7,7 @@ module LeapCli c.action do |global_options,options,args| directory = args.first unless directory && directory.any? - help_now! "Directory name is required." + help! "Directory name is required." end directory = File.expand_path(directory) if File.exists?(directory) diff --git a/lib/leap_cli/commands/pre.rb b/lib/leap_cli/commands/pre.rb index 2281bf6..ada6a6a 100644 --- a/lib/leap_cli/commands/pre.rb +++ b/lib/leap_cli/commands/pre.rb @@ -7,7 +7,7 @@ module LeapCli desc 'Verbosity level 0..2' arg_name 'level' - default_value '0' + default_value '1' flag [:v, :verbose] desc 'Specify the root directory' @@ -30,7 +30,7 @@ module LeapCli if Path.ok? true else - fail!("Could not find the root directory. Change current working directory or try --root") + bail!("Could not find the root directory. Change current working directory or try --root") end end diff --git a/lib/leap_cli/commands/user.rb b/lib/leap_cli/commands/user.rb new file mode 100644 index 0000000..af59074 --- /dev/null +++ b/lib/leap_cli/commands/user.rb @@ -0,0 +1,106 @@ +require 'gpgme' + +# +# notes: +# +# file ~/.gnupg/00440025.asc +# /home/elijah/.gnupg/00440025.asc: PGP public key block +# +# file ~/.ssh/id_rsa.pub +# /home/elijah/.ssh/id_rsa.pub: OpenSSH RSA public key +# + +module LeapCli + module Commands + + desc 'adds a new trusted sysadmin' + arg_name '', :optional => false, :multiple => false + command :'add-user' do |c| + + c.switch 'self', :desc => 'lets you choose among your public keys', :negatable => false + c.flag 'ssh-pub-key', :desc => 'SSH public key file for this new user' + c.flag 'pgp-pub-key', :desc => 'OpenPGP public key file for this new user' + + c.action do |global_options,options,args| + username = args.first + if !username.any? && !options[:self] + help! "Either 'username' or --self is required." + end + + ssh_pub_key = nil + pgp_pub_key = nil + + if options['ssh-pub-key'] + ssh_pub_key = read_file!(options['ssh-pub-key']) + end + if options['pgp-pub-key'] + pgp_pub_key = read_file!(options['pgp-pub-key']) + end + + if options[:self] + username ||= `whoami`.strip + ssh_pub_key ||= pick_ssh_key + pgp_pub_key ||= pick_pgp_key + end + + assert!(ssh_pub_key, 'Sorry, could not find SSH public key.') + assert!(pgp_pub_key, 'Sorry, could not find OpenPGP public key.') + + if ssh_pub_key + write_file!(:user_ssh, username, ssh_pub_key) + end + if pgp_pub_key + write_file!(:user_pgp, username, pgp_pub_key) + end + end + end + + # + # let the the user choose among the ssh public keys that we encounter, or just pick the key if there is only one. + # + def pick_ssh_key + assert_bin! 'ssh-add' + ssh_fingerprints = `ssh-add -l`.split("\n").compact + assert! ssh_fingerprints.any?, 'Sorry, could not find any SSH public key for you. Have you run ssh-keygen?' + + if ssh_fingerprints.length > 1 + key_index = numbered_choice_menu('Choose your SSH public key', ssh_fingerprints) do |key, i| + say("#{i+1}. #{key}") + end + else + key_index = 0 + end + + ssh_keys = `ssh-add -L`.split("\n").compact + return ssh_keys[key_index] + end + + # + # let the the user choose among the gpg public keys that we encounter, or just pick the key if there is only one. + # + def pick_pgp_key + secret_keys = GPGME::Key.find(:secret) + + assert_bin! 'gpg' + assert! secret_keys.any?, 'Sorry, could not find any OpenPGP keys for you.' + + if secret_keys.length > 1 + key_index = numbered_choice_menu('Choose your OpenPGP public key', secret_keys) do |key, i| + key_info = key.to_s.split("\n")[0..1].map{|line| line.sub(/^\s*(sec|uid)\s*/,'')}.join(' -- ') + say("#{i+1}. #{key_info}") + end + else + key_index = 0 + end + + key_id = secret_keys[key_index].sha + + # can't use this, it includes signatures: + #puts GPGME::Key.export(key_id, :armor => true, :export_options => :export_minimal) + + # export with signatures removed: + return `gpg --armor --export-options export-minimal --export #{key_id}`.strip + end + + end +end \ No newline at end of file diff --git a/lib/leap_cli/commands/util.rb b/lib/leap_cli/commands/util.rb new file mode 100644 index 0000000..ad4f01c --- /dev/null +++ b/lib/leap_cli/commands/util.rb @@ -0,0 +1,125 @@ +module LeapCli + module Commands + extend self + extend LeapCli::Util +# # +# # keeps prompting the user for a numbered choice, until they pick a good one or bail out. +# # +# # block is yielded and is responsible for rendering the choices. +# # + def numbered_choice_menu(msg, items, &block) + while true + say("\n" + msg + ':') + items.each_with_index &block + say("q. quit") + index = ask("number 1-#{items.length}> ") + if index.empty? + next + elsif index =~ /q/ + bail! + else + i = index.to_i - 1 + if i < 0 || i >= items.length + bail! + else + return i + end + end + end + end + +# # +# # read a file, exit if the file doesn't exist. +# # +# def read_file!(file_path) +# if !File.exists?(file_path) +# bail!("File '%s' does not exist." % file_path) +# else +# File.readfile(file_path) +# end +# end + +# ## +# ## LOGGING +# ## + +# def log0(message=nil, &block) +# if message +# puts message +# elsif block +# puts yield(block) +# end +# end + +# def log1(message=nil, &block) +# if LeapCli.log_level > 0 +# if message +# puts message +# elsif block +# puts yield(block) +# end +# end +# end + +# def log2(message=nil, &block) +# if LeapCli.log_level > 1 +# if message +# puts message +# elsif block +# puts yield(block) +# end +# end +# end + +# def progress(message) +# log1(" * " + message) +# end + +# ## +# ## QUITTING +# ## + +# # +# # quit and print help +# # +# def help!(message=nil) +# ENV['GLI_DEBUG'] = "false" +# help_now!(message) +# #say("ERROR: " + message) +# end + +# # +# # quit with a message that we are bailing out. +# # +# def bail!(message="") +# say(message) +# say("Bailing out.") +# raise SystemExit.new +# #ENV['GLI_DEBUG'] = "false" +# #exit_now!(message) +# end + +# # +# # quit with no message +# # +# def quit!(message='') +# say(message) +# raise SystemExit.new +# end + +# # +# # bails out with message if assertion is false. +# # +# def assert!(boolean, message) +# bail!(message) unless boolean +# end + +# # +# # assert that the command is available +# # +# def assert_bin!(cmd_name) +# assert! `which #{cmd_name}`.strip.any?, "Sorry, bailing out, the command '%s' is not installed." % cmd_name +# end + + end +end diff --git a/lib/leap_cli/config/manager.rb b/lib/leap_cli/config/manager.rb index 55575cf..b35251a 100644 --- a/lib/leap_cli/config/manager.rb +++ b/lib/leap_cli/config/manager.rb @@ -3,6 +3,7 @@ require 'yaml' module LeapCli module Config + # # A class to manage all the objects in all the configuration files. # @@ -32,15 +33,16 @@ module LeapCli # save compiled hiera .yaml files # def export(dir) - Dir.glob(dir + '/*.yaml').each do |f| - File.unlink(f) - end + existing_files = Dir.glob(dir + '/*.yaml') + updated_files = [] @nodes.each do |name, node| # not sure if people will approve of this change: - # File.open("#{dir}/#{name}.#{node.domain_internal}.yaml", 'w') do |f| - File.open("#{dir}/#{name}.yaml", 'w') do |f| - f.write node.to_yaml - end + filepath = "#{dir}/#{name}.yaml" + updated_files << filepath + Util::write_file!(filepath, node.to_yaml) + end + (existing_files - updated_files).each do |filepath| + Util::remove_file!(filepath) end end @@ -99,7 +101,7 @@ module LeapCli end def load_json(filename, config_type) - log2 { filename.sub(/^#{Regexp.escape(Path.root)}/,'') } + #log2 { filename.sub(/^#{Regexp.escape(Path.root)}/,'') } # # read file, strip out comments diff --git a/lib/leap_cli/log.rb b/lib/leap_cli/log.rb index fe8e5ac..ac35eae 100644 --- a/lib/leap_cli/log.rb +++ b/lib/leap_cli/log.rb @@ -1,15 +1,19 @@ module LeapCli + extend self - def self.log_level + def log_level @log_level end - def self.log_level=(value) + def log_level=(value) @log_level = value end - end +## +## LOGGING +## + def log0(message=nil, &block) if message puts message @@ -38,12 +42,7 @@ def log2(message=nil, &block) end end -def help!(message=nil) - ENV['GLI_DEBUG'] = "false" - help_now!(message) +def progress(message) + log1(" * " + message) end -def fail!(message=nil) - ENV['GLI_DEBUG'] = "false" - exit_now!(message) -end \ No newline at end of file diff --git a/lib/leap_cli/path.rb b/lib/leap_cli/path.rb index 5dc8fe8..f3cbad9 100644 --- a/lib/leap_cli/path.rb +++ b/lib/leap_cli/path.rb @@ -36,16 +36,6 @@ module LeapCli raise "No such directory '#{@root}'" unless File.directory?(@root) end - def self.ensure_dir(dir) - unless File.directory?(dir) - if File.exists?(dir) - raise 'Unable to create directory "%s", file already exists.' % dir - else - FileUtils.mkdir_p(dir) - end - end - end - def self.find_file(name, filename) path = [Path.files, filename].join('/') return path if File.exists?(path) diff --git a/lib/leap_cli/util.rb b/lib/leap_cli/util.rb new file mode 100644 index 0000000..67fca8d --- /dev/null +++ b/lib/leap_cli/util.rb @@ -0,0 +1,171 @@ +require 'md5' + +module LeapCli + module Util + extend self + + ## + ## QUITTING + ## + + # + # quit and print help + # + def help!(message=nil) + ENV['GLI_DEBUG'] = "false" + help_now!(message) + #say("ERROR: " + message) + end + + # + # quit with a message that we are bailing out. + # + def bail!(message="") + say(message) + say("Bailing out.") + raise SystemExit.new + #ENV['GLI_DEBUG'] = "false" + #exit_now!(message) + end + + # + # quit with no message + # + def quit!(message='') + say(message) + raise SystemExit.new + end + + # + # bails out with message if assertion is false. + # + def assert!(boolean, message) + bail!(message) unless boolean + end + + # + # assert that the command is available + # + def assert_bin!(cmd_name) + assert! `which #{cmd_name}`.strip.any?, "Sorry, bailing out, the command '%s' is not installed." % cmd_name + end + + ## + ## FILES AND DIRECTORIES + ## + + def relative_path(path) + path.sub(/^#{Regexp.escape(Path.provider)}\//,'') + end + + def progress_created(path) + progress 'created %s' % relative_path(path) + end + + def progress_updated(path) + progress 'updated %s' % relative_path(path) + end + + def progress_nochange(path) + progress 'no change %s' % relative_path(path) + end + + def progress_removed(path) + progress 'removed %s' % relative_path(path) + end + + # + # creates a directory if it doesn't already exist + # + def ensure_dir(dir) + unless File.directory?(dir) + if File.exists?(dir) + bail! 'Unable to create directory "%s", file already exists.' % dir + else + FileUtils.mkdir_p(dir) + unless dir =~ /\/$/ + dir = dir + '/' + end + progress_created dir + end + end + end + + NAMED_PATHS = { + :user_ssh => 'users/#{arg}/#{arg}_ssh.pub', + :user_pgp => 'users/#{arg}/#{arg}_pgp.pub' + } + + # + # read a file, exit if the file doesn't exist. + # + def read_file!(file_path) + if !File.exists?(file_path) + bail!("File '%s' does not exist." % file_path) + else + File.readfile(file_path) + end + end + + def write_file!(*args) + if args.first.is_a? Symbol + write_named_file!(*args) + else + write_to_path!(*args) + end + end + + def remove_file!(file_path) + if File.exists?(file_path) + File.unlink(file_path) + progress_removed(file_path) + end + end + + # + # saves a named file + # + def write_named_file!(name, arg, contents) + assert!(NAMED_PATHS[name], "Error, I don't know the path for #{arg}") + + filename = eval('"' + NAMED_PATHS[name] + '"') + fullpath = Path.provider + '/' + filename + + write_to_path!(fullpath, contents) + end + + def write_to_path!(filepath, contents) + ensure_dir File.dirname(filepath) + existed = File.exists?(filepath) + if existed + if file_content_is?(filepath, contents) + progress_nochange filepath + return + end + end + + File.open(filepath, 'w') do |f| + f.write contents + end + + if existed + progress_updated filepath + else + progress_created filepath + end + end + + private + + def file_content_is?(filepath, contents) + output = `md5sum '#{filepath}'`.strip + if $?.to_i == 0 + return output.split(" ").first == MD5.md5(contents).to_s + else + return false + end + end + + end +end + -- cgit v1.2.3