class HTAuth::CLI::Passwd

Implemenation of the commandline htpasswd-ruby

Constants

MAX_PASSWD_LENGTH

Attributes

passwd_file[RW]

Public Class Methods

new() click to toggle source
# File lib/htauth/cli/passwd.rb, line 17
def initialize
  @passwd_file = nil
  @option_parser = nil
  @options = nil
end

Public Instance Methods

fetch_password(width=20) click to toggle source
# File lib/htauth/cli/passwd.rb, line 169
def fetch_password(width=20)
  return options.password if options.batch_mode
  console = Console.new
  if options.read_stdin_once then
    pw_in = console.read_answer
    return pw_in
  end

  case options.operation
  when :verify
    pw_in = console.ask("Enter password: ".rjust(width))
    raise PasswordError, "password '#{pw_in}' too long" if pw_in.length >= MAX_PASSWD_LENGTH
  when :add_or_update
    pw_in = console.ask("New password: ".rjust(width))
    raise PasswordError, "password '#{pw_in}' too long" if pw_in.length >= MAX_PASSWD_LENGTH

    pw_validate = console.ask("Re-type new password: ".rjust(width))
    raise PasswordError, "They don't match, sorry." unless pw_in == pw_validate
  end

  return pw_in
end
option_parser() click to toggle source
# File lib/htauth/cli/passwd.rb, line 42
      def option_parser
        if not @option_parser then
          @option_parser = OptionParser.new(nil, 16) do |op|
            op.banner = <<-EOB
Usage:
        #{op.program_name} [-cimBdpsD] [-C cost] passwordfile username
        #{op.program_name} -b[cmBdpsD] [-C cost] passwordfile username password

        #{op.program_name} -n[imBdps] [-C cost] username
        #{op.program_name} -nb[mBdps] [-C cost] username password
            EOB

            op.separator ""

            op.on("-b", "--batch", "Batch mode, get the password from the command line, rather than prompt") do |b|
              options.batch_mode = b
            end

            op.on("-B", "--bcrypt", "Force bcrypt encryption of the password.") do |b|
              options.algorithm = Algorithm::BCRYPT
            end

            op.on("-CCOST", "--cost COST", "Set the computing time used for the bcrypt algorithm",
                                           "(higher is more secure but slower, default: 5, valid: 4 to 31).") do |c|
              if c !~ /\A\d+\z/ then
                  raise ::OptionParser::ParseError, "the bcrypt cost must be an integer from 4 to 31, `#{c}` is invalid"
              end

              cost = c.to_i
              if (4..31).include?(cost)
                options.algorithm_args = { :cost => cost }
              else
                raise ::OptionParser::ParseError, "the bcrypt cost must be an integer from 4 to 31, `#{c}` is invalid"
              end
            end

            op.on("-c", "--create", "Create a new file; this overwrites an existing file.") do |c|
              options.file_mode = HTAuth::File::CREATE
              options.operation << :add_or_update
            end

            op.on("-d", "--crypt", "Force CRYPT encryption of the password.") do |c|
              options.algorithm = Algorithm::CRYPT
            end

            op.on("-D", "--delete", "Delete the specified user.") do |d|
              options.operation << :delete
            end

            op.on("-h", "--help", "Display this help.") do |h|
              options.show_help = h
            end

            op.on("-i", "--stdin", "Read the passwod from stdin without verivication (for script usage).") do |i|
              options.read_stdin_once = true
            end

            op.on("-m", "--md5", "Force MD5 encryption of the password (default).") do |m|
              options.algorithm = Algorithm::MD5
            end

            op.on("-n", "--stdout", "Do not update the file; Display the results on stdout instead.") do |n|
              options.send_to_stdout = true
              options.passwdfile     = HTAuth::File::STDOUT_FLAG
              options.operation     << :stdout
            end

            op.on("-p", "--plaintext", "Do not encrypt the password (plaintext).") do |p|
              options.algorithm = Algorithm::PLAINTEXT
            end

            op.on("-s", "--sha1", "Force SHA encryption of the password.") do |s|
              options.algorithm = Algorithm::SHA1
            end

            op.on("-v", "--version", "Show version info.") do |v|
              options.show_version = v
            end

            op.on("--verify", "Verify password for the specified user") do |v|
              options.operation << :verify
            end

            op.separator ""

            op.separator "The SHA algorihtm does not use a salt and is less secure than the MD5 algorithm."
          end
        end
        @option_parser
      end
options() click to toggle source
# File lib/htauth/cli/passwd.rb, line 23
def options
  if @options.nil? then
    @options                = ::OpenStruct.new
    @options.batch_mode     = false
    @options.file_mode      = File::ALTER
    @options.passwdfile     = nil
    @options.algorithm      = Algorithm::EXISTING
    @options.algorithm_args = {}
    @options.read_stdin_once= false
    @options.send_to_stdout = false
    @options.show_version   = false
    @options.show_help      = false
    @options.username       = nil
    @options.password       = ""
    @options.operation      = []
  end
  @options
end
parse_options(argv) click to toggle source
# File lib/htauth/cli/passwd.rb, line 143
def parse_options(argv)
  begin
    option_parser.parse!(argv)
    show_version if options.show_version
    show_help if options.show_help

    raise ::OptionParser::ParseError, "only one of --create, --stdout, --verify, --delete may be specified" if options.operation.size > 1
    raise ::OptionParser::ParseError, "Unable to send to stdout AND create a new file" if options.send_to_stdout and (options.file_mode == File::CREATE)
    raise ::OptionParser::ParseError, "a username is needed" if options.send_to_stdout and argv.size < 1
    raise ::OptionParser::ParseError, "a username and password are needed" if options.send_to_stdout and options.batch_mode  and ( argv.size < 2 ) 
    raise ::OptionParser::ParseError, "a passwordfile, username and password are needed " if not options.send_to_stdout and options.batch_mode and ( argv.size < 3 )
    raise ::OptionParser::ParseError, "a passwordfile and username are needed" if argv.size < 2
    raise ::OptionParser::ParseError, "options -i and -b are mutually exclusive" if options.batch_mode && options.read_stdin_once

    options.operation  = options.operation.shift || :add_or_update
    options.passwdfile = argv.shift unless options.send_to_stdout
    options.username   = argv.shift
    options.password   = argv.shift if options.batch_mode

  rescue ::OptionParser::ParseError => pe
    $stderr.puts "ERROR: #{option_parser.program_name} - #{pe}"
    show_help
    exit 1
  end
end
run(argv, env = ENV) click to toggle source
# File lib/htauth/cli/passwd.rb, line 192
def run(argv, env = ENV)
  begin
    parse_options(argv)
    console = Console.new
    passwd_file = PasswdFile.new(options.passwdfile, options.file_mode)
    case options.operation
    when :delete
      passwd_file.delete(options.username)
      passwd_file.save!
    when :verify
      if passwd_file.has_entry?(options.username) then
        pw_in = fetch_password
        if passwd_file.authenticated?(options.username, pw_in) then
          $stderr.puts "Password for user #{options.username} correct."
        else
          raise HTAuth::Error, "Password verification for user #{options.username} failed."
        end
      else
        raise HTAuth::Error, "User #{options.username} not found"
      end
    when :add_or_update
      options.password = fetch_password
      action = passwd_file.has_entry?(options.username) ? "Changing" : "Adding"
      console.say "#{action} password for #{options.username}."
      passwd_file.add_or_update(options.username, options.password, options.algorithm, options.algorithm_args)
      passwd_file.save!
    when :stdout
      options.password = fetch_password
      passwd_file.add_or_update(options.username, options.password, options.algorithm, options.algorithm_args)
      passwd_file.save!
    end
  rescue HTAuth::FileAccessError => fae
    msg = "Password file failure (#{options.passwdfile}) "
    $stderr.puts "#{msg}: #{fae.message}"
    exit 1
  rescue HTAuth::Error => pe
    $stderr.puts "#{pe.message}"
    exit 1
  rescue SignalException => se
    $stderr.puts
    $stderr.puts "Interrupted #{se}"
    exit 1
  end
  exit 0
end
show_help() click to toggle source
# File lib/htauth/cli/passwd.rb, line 133
def show_help
  $stdout.puts option_parser
  exit 1
end
show_version() click to toggle source
# File lib/htauth/cli/passwd.rb, line 138
def show_version
  $stdout.puts "#{option_parser.program_name}: version #{HTAuth::VERSION}"
  exit 1
end