Class: HTAuth::CLI::Passwd

Inherits:
Object
  • Object
show all
Defined in:
lib/htauth/cli/passwd.rb

Overview

Internal: Implemenation of the commandline htpasswd-ruby

Constant Summary collapse

MAX_PASSWD_LENGTH =
255

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializePasswd

Returns a new instance of Passwd.



15
16
17
18
19
# File 'lib/htauth/cli/passwd.rb', line 15

def initialize
  @passwd_file = nil
  @option_parser = nil
  @options = nil
end

Instance Attribute Details

#passwd_fileObject

Returns the value of attribute passwd_file.



13
14
15
# File 'lib/htauth/cli/passwd.rb', line 13

def passwd_file
  @passwd_file
end

Instance Method Details

#fetch_password(width = 20) ⇒ Object



171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
# File 'lib/htauth/cli/passwd.rb', line 171

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_parserObject



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
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
# File 'lib/htauth/cli/passwd.rb', line 40

def option_parser
  if not @option_parser then
    @option_parser = OptionParser.new(nil, 16) do |op|
      op.banner = <<-EOB
Usage:
  #{op.program_name} [-acimBdpsD] [--verify] [-C cost] passwordfile username
  #{op.program_name} -b[acmBdpsD] [--verify] [-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("--argon2", "Force argon2 encryption of the password.") do |a|
        options.algorithm = Algorithm::ARGON2
      end

      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

#optionsObject



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/htauth/cli/passwd.rb', line 21

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) ⇒ Object



145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/htauth/cli/passwd.rb', line 145

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) ⇒ Object



194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
# File 'lib/htauth/cli/passwd.rb', line 194

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_helpObject



135
136
137
138
# File 'lib/htauth/cli/passwd.rb', line 135

def show_help
  $stdout.puts option_parser
  exit 1
end

#show_versionObject



140
141
142
143
# File 'lib/htauth/cli/passwd.rb', line 140

def show_version
  $stdout.puts "#{option_parser.program_name}: version #{HTAuth::VERSION}"
  exit 1
end