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



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 = "Usage:\n  \#{op.program_name} [-acimBdpsD] [--verify] [-C cost] passwordfile username\n  \#{op.program_name} -b[acmBdpsD] [--verify] [-C cost] passwordfile username password\n\n  \#{op.program_name} -n[imBdps] [-C cost] username\n  \#{op.program_name} -nb[mBdps] [-C cost] username password\n      EOB\n\n      op.separator \"\"\n\n      op.on(\"--argon2\", \"Force argon2 encryption of the password.\") do |a|\n        options.algorithm = Algorithm::ARGON2\n      end\n\n      op.on(\"-b\", \"--batch\", \"Batch mode, get the password from the command line, rather than prompt.\") do |b|\n        options.batch_mode = b\n      end\n\n      op.on(\"-B\", \"--bcrypt\", \"Force bcrypt encryption of the password.\") do |b|\n        options.algorithm = Algorithm::BCRYPT\n      end\n\n      op.on(\"-CCOST\", \"--cost COST\", \"Set the computing time used for the bcrypt algorithm\",\n                                     \"(higher is more secure but slower, default: 5, valid: 4 to 31).\") do |c|\n        if c !~ /\\A\\d+\\z/ then\n            raise ::OptionParser::ParseError, \"the bcrypt cost must be an integer from 4 to 31, `\#{c}` is invalid\"\n        end\n\n        cost = c.to_i\n        if (4..31).include?(cost)\n          options.algorithm_args = { :cost => cost }\n        else\n          raise ::OptionParser::ParseError, \"the bcrypt cost must be an integer from 4 to 31, `\#{c}` is invalid\"\n        end\n      end\n\n      op.on(\"-c\", \"--create\", \"Create a new file; this overwrites an existing file.\") do |c|\n        options.file_mode = HTAuth::File::CREATE\n        options.operation << :add_or_update\n      end\n\n      op.on(\"-d\", \"--crypt\", \"Force CRYPT encryption of the password.\") do |c|\n        options.algorithm = Algorithm::CRYPT\n      end\n\n      op.on(\"-D\", \"--delete\", \"Delete the specified user.\") do |d|\n        options.operation << :delete\n      end\n\n      op.on(\"-h\", \"--help\", \"Display this help.\") do |h|\n        options.show_help = h\n      end\n\n      op.on(\"-i\", \"--stdin\", \"Read the passwod from stdin without verivication (for script usage).\") do |i|\n        options.read_stdin_once = true\n      end\n\n      op.on(\"-m\", \"--md5\", \"Force MD5 encryption of the password (default).\") do |m|\n        options.algorithm = Algorithm::MD5\n      end\n\n      op.on(\"-n\", \"--stdout\", \"Do not update the file; Display the results on stdout instead.\") do |n|\n        options.send_to_stdout = true\n        options.passwdfile     = HTAuth::File::STDOUT_FLAG\n        options.operation     << :stdout\n      end\n\n      op.on(\"-p\", \"--plaintext\", \"Do not encrypt the password (plaintext).\") do |p|\n        options.algorithm = Algorithm::PLAINTEXT\n      end\n\n      op.on(\"-s\", \"--sha1\", \"Force SHA encryption of the password.\") do |s|\n        options.algorithm = Algorithm::SHA1\n      end\n\n      op.on(\"-v\", \"--version\", \"Show version info.\") do |v|\n        options.show_version = v\n      end\n\n      op.on(\"--verify\", \"Verify password for the specified user.\") do |v|\n        options.operation << :verify\n      end\n\n      op.separator \"\"\n\n      op.separator \"The SHA algorihtm does not use a salt and is less secure than the MD5 algorithm.\"\n    end\n  end\n  @option_parser\nend\n"

#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