htauth 2.0.0 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/HISTORY.md +7 -0
- data/Manifest.txt +4 -0
- data/README.md +36 -22
- data/Rakefile +8 -4
- data/lib/htauth.rb +1 -0
- data/lib/htauth/algorithm.rb +32 -29
- data/lib/htauth/bcrypt.rb +35 -0
- data/lib/htauth/cli/digest.rb +1 -1
- data/lib/htauth/cli/passwd.rb +90 -30
- data/lib/htauth/console.rb +5 -1
- data/lib/htauth/crypt.rb +15 -4
- data/lib/htauth/descendant_tracker.rb +46 -0
- data/lib/htauth/md5.rb +27 -7
- data/lib/htauth/passwd_entry.rb +22 -17
- data/lib/htauth/passwd_file.rb +46 -12
- data/lib/htauth/plaintext.rb +11 -4
- data/lib/htauth/sha1.rb +8 -5
- data/lib/htauth/version.rb +1 -1
- data/spec/algorithm_spec.rb +8 -0
- data/spec/bcrypt_spec.rb +33 -0
- data/spec/cli/digest_spec.rb +22 -23
- data/spec/cli/passwd_spec.rb +160 -36
- data/spec/crypt_spec.rb +1 -5
- data/spec/digest_entry_spec.rb +19 -19
- data/spec/digest_file_spec.rb +10 -10
- data/spec/md5_spec.rb +1 -5
- data/spec/passwd_entry_spec.rb +63 -42
- data/spec/passwd_file_spec.rb +31 -13
- data/spec/plaintext_spec.rb +1 -5
- data/spec/sha1_spec.rb +1 -5
- data/tasks/default.rake +3 -3
- data/tasks/this.rb +2 -2
- metadata +32 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 1a0330f64106269682197c83a695b2efbb98b05e367ba56a90aef1794cdc27dd
|
4
|
+
data.tar.gz: 4fd5c4452211b4747543941477c65eb83a33ed075d4dd352b1763f00daaabcb5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0f5cee2bcf691c57abf009a02a5d383839c1b5ae9f38345785398a258f72936b593e1672f74e62bc10f87124f12b2783b0587c4dfc5d73b413391d996824d0b1
|
7
|
+
data.tar.gz: c583805c7ee4a2f33d7297df69dc118d8abc60b91ece304c04b84c2e873bed1c8abd8dff31624a8fffd32fe008e22dcc38aa47677dd7bae143f32eba73a02287
|
data/HISTORY.md
CHANGED
@@ -1,4 +1,11 @@
|
|
1
1
|
# Changelog
|
2
|
+
## Version 2.1.0 - 2020-04-02
|
3
|
+
|
4
|
+
* Update minimum ruby versions to modern versions
|
5
|
+
* Support bcrypt password entries [#12](https://github.com/copiousfreetime/htauth/issues/12)
|
6
|
+
* Support authentication at the password file level
|
7
|
+
* implement --verify commandline option (-v in apache htpasswd)
|
8
|
+
* implement --stdin commandline option (-i in apache htpasswd)
|
2
9
|
|
3
10
|
## Version 2.0.0 - 2015-09-13
|
4
11
|
|
data/Manifest.txt
CHANGED
@@ -8,11 +8,13 @@ bin/htdigest-ruby
|
|
8
8
|
bin/htpasswd-ruby
|
9
9
|
lib/htauth.rb
|
10
10
|
lib/htauth/algorithm.rb
|
11
|
+
lib/htauth/bcrypt.rb
|
11
12
|
lib/htauth/cli.rb
|
12
13
|
lib/htauth/cli/digest.rb
|
13
14
|
lib/htauth/cli/passwd.rb
|
14
15
|
lib/htauth/console.rb
|
15
16
|
lib/htauth/crypt.rb
|
17
|
+
lib/htauth/descendant_tracker.rb
|
16
18
|
lib/htauth/digest_entry.rb
|
17
19
|
lib/htauth/digest_file.rb
|
18
20
|
lib/htauth/entry.rb
|
@@ -24,6 +26,8 @@ lib/htauth/passwd_file.rb
|
|
24
26
|
lib/htauth/plaintext.rb
|
25
27
|
lib/htauth/sha1.rb
|
26
28
|
lib/htauth/version.rb
|
29
|
+
spec/algorithm_spec.rb
|
30
|
+
spec/bcrypt_spec.rb
|
27
31
|
spec/cli/digest_spec.rb
|
28
32
|
spec/cli/passwd_spec.rb
|
29
33
|
spec/crypt_spec.rb
|
data/README.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
## HTAuth
|
2
2
|
|
3
3
|
* [Github](http://github.com/copiousfreetime/htauth/tree/master)
|
4
|
-
*
|
4
|
+
* [![Build Status](https://travis-ci.org/copiousfreetime/htauth.svg?branch=master)](https://travis-ci.org/copiousfreetime/htauth)
|
5
5
|
|
6
6
|
## DESCRIPTION
|
7
7
|
|
@@ -26,32 +26,38 @@ Additionally, you can access all the functionality of *htdigest-ruby* and
|
|
26
26
|
|
27
27
|
### htpasswd-ruby command line application
|
28
28
|
|
29
|
-
|
30
29
|
Usage:
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
30
|
+
htpasswd-ruby [-cimBdpsD] [-C cost] passwordfile username
|
31
|
+
htpasswd-ruby -b[cmBdpsD] [-C cost] passwordfile username password
|
32
|
+
|
33
|
+
htpasswd-ruby -n[imBdps] [-C cost] username
|
34
|
+
htpasswd-ruby -nb[mBdps] [-C cost] username password
|
35
|
+
|
36
|
+
-b, --batch Batch mode, get the password from the command line, rather than prompt
|
37
|
+
-B, --bcrypt Force bcrypt encryption of the password.
|
38
|
+
-C, --cost COST Set the computing time used for the bcrypt algorithm
|
39
|
+
(higher is more secure but slower, default: 5, valid: 4 to 31).
|
40
|
+
-c, --create Create a new file; this overwrites an existing file.
|
41
|
+
-d, --crypt Force CRYPT encryption of the password.
|
42
|
+
-D, --delete Delete the specified user.
|
43
|
+
-h, --help Display this help.
|
44
|
+
-i, --stdin Read the passwod from stdin without verivication (for script usage).
|
45
|
+
-m, --md5 Force MD5 encryption of the password (default).
|
46
|
+
-n, --stdout Do not update the file; Display the results on stdout instead.
|
47
|
+
-p, --plaintext Do not encrypt the password (plaintext).
|
48
|
+
-s, --sha1 Force SHA encryption of the password.
|
49
|
+
-v, --version Show version info.
|
50
|
+
--verify Verify password for the specified user
|
51
|
+
|
52
|
+
The SHA algorihtm does not use a salt and is less secure than the MD5 algorithm.
|
47
53
|
|
48
54
|
### htdigest-ruby command line application
|
49
55
|
|
50
56
|
Usage: htdigest-ruby [options] passwordfile realm username
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
57
|
+
-c, --create Create a new digest password file; this overwrites an existing file.
|
58
|
+
-D, --delete Delete the specified user.
|
59
|
+
-h, --help Display this help.
|
60
|
+
-v, --version Show version info.
|
55
61
|
|
56
62
|
### API Usage
|
57
63
|
|
@@ -65,6 +71,14 @@ Additionally, you can access all the functionality of *htdigest-ruby* and
|
|
65
71
|
pf.add('someotheruser', 'a different password', 'sha1')
|
66
72
|
end
|
67
73
|
|
74
|
+
HTAuth::PasswdFile.open("some.htpasswd", HTAuth::File::ALTER) do |pf|
|
75
|
+
pf.update('someuser', 'a password', 'bcrypt')
|
76
|
+
end
|
77
|
+
|
78
|
+
HTAuth::PasswdFile.open("some.htpasswd") do |pf|
|
79
|
+
pf.authenticated?('someuser', 'a password')
|
80
|
+
end
|
81
|
+
|
68
82
|
## CREDITS
|
69
83
|
|
70
84
|
* [The Apache Software Foundation](http://www.apache.org/)
|
data/Rakefile
CHANGED
@@ -7,10 +7,14 @@ This.email = "jeremy@copiousfreetime.org"
|
|
7
7
|
This.homepage = "http://github.com/copiousfreetime/#{ This.name }"
|
8
8
|
|
9
9
|
This.ruby_gemspec do |spec|
|
10
|
-
spec.
|
11
|
-
|
12
|
-
spec.add_development_dependency( '
|
13
|
-
spec.add_development_dependency( '
|
10
|
+
spec.add_dependency( 'bcrypt', '~> 3.1' )
|
11
|
+
|
12
|
+
spec.add_development_dependency( 'rake' , '~> 13.0')
|
13
|
+
spec.add_development_dependency( 'minitest' , '~> 5.5' )
|
14
|
+
spec.add_development_dependency( 'rdoc' , '~> 6.2' )
|
15
|
+
spec.add_development_dependency( 'simplecov', '~> 0.17' )
|
16
|
+
|
17
|
+
spec.license = "MIT"
|
14
18
|
end
|
15
19
|
|
16
20
|
load 'tasks/default.rake'
|
data/lib/htauth.rb
CHANGED
data/lib/htauth/algorithm.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'htauth/error'
|
2
|
+
require 'htauth/descendant_tracker'
|
2
3
|
require 'securerandom'
|
3
4
|
module HTAuth
|
4
5
|
class InvalidAlgorithmError < Error; end
|
@@ -7,8 +8,13 @@ module HTAuth
|
|
7
8
|
#
|
8
9
|
class Algorithm
|
9
10
|
|
11
|
+
extend DescendantTracker
|
12
|
+
|
10
13
|
SALT_CHARS = (%w[ . / ] + ("0".."9").to_a + ('A'..'Z').to_a + ('a'..'z').to_a).freeze
|
14
|
+
SALT_LENGTH = 8
|
11
15
|
|
16
|
+
# Public: flag for the bcrypt algorithm
|
17
|
+
BCRYPT = "bcrypt".freeze
|
12
18
|
# Public: flag for the md5 algorithm
|
13
19
|
MD5 = "md5".freeze
|
14
20
|
# Public: flag for the sha1 algorithm
|
@@ -26,34 +32,36 @@ module HTAuth
|
|
26
32
|
|
27
33
|
|
28
34
|
class << self
|
29
|
-
def
|
30
|
-
|
31
|
-
sub_klasses[a_name.downcase].new(params)
|
35
|
+
def algorithm_name
|
36
|
+
self.name.split("::").last.downcase
|
32
37
|
end
|
33
38
|
|
34
|
-
def
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
elsif password_field.index(sub_klasses[MD5].new.prefix) then
|
40
|
-
p = password_field.split("$")
|
41
|
-
matches << sub_klasses[MD5].new( :salt => p[2] )
|
42
|
-
else
|
43
|
-
matches << sub_klasses[PLAINTEXT].new
|
44
|
-
matches << sub_klasses[CRYPT].new( :salt => password_field[0,2] )
|
39
|
+
def algorithm_from_name(a_name, params = {})
|
40
|
+
found = children.find { |c| c.algorithm_name == a_name }
|
41
|
+
if !found then
|
42
|
+
names = children.map { |c| c.algorithm_name }
|
43
|
+
raise InvalidAlgorithmError, "`#{a_name}' is an unknown encryption algorithm, use one of #{names.join(', ')}"
|
45
44
|
end
|
46
|
-
|
47
|
-
return matches
|
45
|
+
return found.new(params)
|
48
46
|
end
|
49
47
|
|
50
|
-
|
51
|
-
|
52
|
-
|
48
|
+
# NOTE: if it is plaintext, and the length is 13 - it may matched crypt
|
49
|
+
# and be tested that way. If that is the case - this is explicitly
|
50
|
+
# siding with crypt() as you shouldn't be using plaintext. Or
|
51
|
+
# crypt for that matter.
|
52
|
+
def algorithm_from_field(password_field)
|
53
|
+
match = find_child(:handles?, password_field)
|
54
|
+
match = ::HTAuth::Plaintext if match.nil? && ::HTAuth::Plaintext.entry_matches?(password_field)
|
55
|
+
|
56
|
+
raise InvalidAlgorithmError, "unknown encryption algorithm used for `#{password_field}`" if match.nil?
|
57
|
+
|
58
|
+
return match.new(:existing => password_field)
|
53
59
|
end
|
54
60
|
|
55
|
-
|
56
|
-
|
61
|
+
# Internal: Does this class handle this type of password entry
|
62
|
+
#
|
63
|
+
def handles?(password_entry)
|
64
|
+
raise NotImplementedError, "#{self.name} must implement #{self.name}.handles?(password_entry)"
|
57
65
|
end
|
58
66
|
|
59
67
|
# Internal: Constant time string comparison.
|
@@ -75,20 +83,15 @@ module HTAuth
|
|
75
83
|
end
|
76
84
|
end
|
77
85
|
|
78
|
-
# Internal
|
79
|
-
def prefix ; end
|
80
|
-
|
81
86
|
# Internal
|
82
87
|
def encode(password) ; end
|
83
88
|
|
84
89
|
# Internal: 8 bytes of random items from SALT_CHARS
|
85
|
-
def gen_salt
|
86
|
-
|
87
|
-
8.times { chars << SALT_CHARS[SecureRandom.random_number(SALT_CHARS.size)] }
|
88
|
-
chars.join('')
|
90
|
+
def gen_salt(length = SALT_LENGTH)
|
91
|
+
Array.new(length) { SALT_CHARS.sample }.join('')
|
89
92
|
end
|
90
93
|
|
91
|
-
# Internal: this is not the Base64 encoding, this is the to64()
|
94
|
+
# Internal: this is not the Base64 encoding, this is the to64()
|
92
95
|
# method from the apache protable runtime library
|
93
96
|
def to_64(number, rounds)
|
94
97
|
r = StringIO.new
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'htauth/algorithm'
|
2
|
+
require 'bcrypt'
|
3
|
+
|
4
|
+
module HTAuth
|
5
|
+
# Internal: an implementation of the Bcrypt based encoding algorithm
|
6
|
+
# as used in the apache htpasswd -B option
|
7
|
+
|
8
|
+
class Bcrypt < Algorithm
|
9
|
+
|
10
|
+
attr_accessor :cost
|
11
|
+
|
12
|
+
DEFAULT_APACHE_COST = 5 # this is the default cost from htpasswd
|
13
|
+
|
14
|
+
def self.handles?(password_entry)
|
15
|
+
return ::BCrypt::Password.valid_hash?(password_entry)
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.extract_cost_from_existing_password_field(existing)
|
19
|
+
password = ::BCrypt::Password.new(existing)
|
20
|
+
password.cost
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(params = {})
|
24
|
+
if existing = (params['existing'] || params[:existing]) then
|
25
|
+
@cost = self.class.extract_cost_from_existing_password_field(existing)
|
26
|
+
else
|
27
|
+
@cost = params['cost'] || params[:cost] || DEFAULT_APACHE_COST
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def encode(password)
|
32
|
+
::BCrypt::Password.create(password, :cost => cost)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/htauth/cli/digest.rb
CHANGED
@@ -37,7 +37,7 @@ module HTAuth
|
|
37
37
|
|
38
38
|
def option_parser
|
39
39
|
if not @option_parser then
|
40
|
-
@option_parser = OptionParser.new do |op|
|
40
|
+
@option_parser = OptionParser.new(nil, 14) do |op|
|
41
41
|
op.banner = "Usage: #{op.program_name} [options] passwordfile realm username"
|
42
42
|
op.on("-c", "--create", "Create a new digest password file; this overwrites an existing file.") do |c|
|
43
43
|
options.file_mode = DigestFile::CREATE
|
data/lib/htauth/cli/passwd.rb
CHANGED
@@ -27,26 +27,28 @@ module HTAuth
|
|
27
27
|
@options.file_mode = File::ALTER
|
28
28
|
@options.passwdfile = nil
|
29
29
|
@options.algorithm = Algorithm::EXISTING
|
30
|
+
@options.algorithm_args = {}
|
31
|
+
@options.read_stdin_once= false
|
30
32
|
@options.send_to_stdout = false
|
31
33
|
@options.show_version = false
|
32
34
|
@options.show_help = false
|
33
35
|
@options.username = nil
|
34
|
-
@options.delete_entry = false
|
35
36
|
@options.password = ""
|
37
|
+
@options.operation = []
|
36
38
|
end
|
37
39
|
@options
|
38
40
|
end
|
39
41
|
|
40
42
|
def option_parser
|
41
43
|
if not @option_parser then
|
42
|
-
@option_parser = OptionParser.new do |op|
|
44
|
+
@option_parser = OptionParser.new(nil, 16) do |op|
|
43
45
|
op.banner = <<-EOB
|
44
|
-
Usage:
|
45
|
-
|
46
|
-
|
46
|
+
Usage:
|
47
|
+
#{op.program_name} [-cimBdpsD] [-C cost] passwordfile username
|
48
|
+
#{op.program_name} -b[cmBdpsD] [-C cost] passwordfile username password
|
47
49
|
|
48
|
-
|
49
|
-
|
50
|
+
#{op.program_name} -n[imBdps] [-C cost] username
|
51
|
+
#{op.program_name} -nb[mBdps] [-C cost] username password
|
50
52
|
EOB
|
51
53
|
|
52
54
|
op.separator ""
|
@@ -55,8 +57,27 @@ Usage:
|
|
55
57
|
options.batch_mode = b
|
56
58
|
end
|
57
59
|
|
60
|
+
op.on("-B", "--bcrypt", "Force bcrypt encryption of the password.") do |b|
|
61
|
+
options.algorithm = Algorithm::BCRYPT
|
62
|
+
end
|
63
|
+
|
64
|
+
op.on("-CCOST", "--cost COST", "Set the computing time used for the bcrypt algorithm",
|
65
|
+
"(higher is more secure but slower, default: 5, valid: 4 to 31).") do |c|
|
66
|
+
if c !~ /\A\d+\z/ then
|
67
|
+
raise ::OptionParser::ParseError, "the bcrypt cost must be an integer from 4 to 31, `#{c}` is invalid"
|
68
|
+
end
|
69
|
+
|
70
|
+
cost = c.to_i
|
71
|
+
if (4..31).include?(cost)
|
72
|
+
options.algorithm_args = { :cost => cost }
|
73
|
+
else
|
74
|
+
raise ::OptionParser::ParseError, "the bcrypt cost must be an integer from 4 to 31, `#{c}` is invalid"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
58
78
|
op.on("-c", "--create", "Create a new file; this overwrites an existing file.") do |c|
|
59
79
|
options.file_mode = HTAuth::File::CREATE
|
80
|
+
options.operation << :add_or_update
|
60
81
|
end
|
61
82
|
|
62
83
|
op.on("-d", "--crypt", "Force CRYPT encryption of the password.") do |c|
|
@@ -64,13 +85,17 @@ Usage:
|
|
64
85
|
end
|
65
86
|
|
66
87
|
op.on("-D", "--delete", "Delete the specified user.") do |d|
|
67
|
-
options.
|
88
|
+
options.operation << :delete
|
68
89
|
end
|
69
90
|
|
70
91
|
op.on("-h", "--help", "Display this help.") do |h|
|
71
92
|
options.show_help = h
|
72
93
|
end
|
73
94
|
|
95
|
+
op.on("-i", "--stdin", "Read the passwod from stdin without verivication (for script usage).") do |i|
|
96
|
+
options.read_stdin_once = true
|
97
|
+
end
|
98
|
+
|
74
99
|
op.on("-m", "--md5", "Force MD5 encryption of the password (default).") do |m|
|
75
100
|
options.algorithm = Algorithm::MD5
|
76
101
|
end
|
@@ -78,6 +103,7 @@ Usage:
|
|
78
103
|
op.on("-n", "--stdout", "Do not update the file; Display the results on stdout instead.") do |n|
|
79
104
|
options.send_to_stdout = true
|
80
105
|
options.passwdfile = HTAuth::File::STDOUT_FLAG
|
106
|
+
options.operation << :stdout
|
81
107
|
end
|
82
108
|
|
83
109
|
op.on("-p", "--plaintext", "Do not encrypt the password (plaintext).") do |p|
|
@@ -92,9 +118,13 @@ Usage:
|
|
92
118
|
options.show_version = v
|
93
119
|
end
|
94
120
|
|
121
|
+
op.on("--verify", "Verify password for the specified user") do |v|
|
122
|
+
options.operation << :verify
|
123
|
+
end
|
124
|
+
|
95
125
|
op.separator ""
|
96
126
|
|
97
|
-
op.separator "The SHA algorihtm does not use a salt and is less secure than the MD5 algorithm"
|
127
|
+
op.separator "The SHA algorihtm does not use a salt and is less secure than the MD5 algorithm."
|
98
128
|
end
|
99
129
|
end
|
100
130
|
@option_parser
|
@@ -114,14 +144,17 @@ Usage:
|
|
114
144
|
begin
|
115
145
|
option_parser.parse!(argv)
|
116
146
|
show_version if options.show_version
|
117
|
-
show_help if options.show_help
|
147
|
+
show_help if options.show_help
|
118
148
|
|
149
|
+
raise ::OptionParser::ParseError, "only one of --create, --stdout, --verify, --delete may be specified" if options.operation.size > 1
|
119
150
|
raise ::OptionParser::ParseError, "Unable to send to stdout AND create a new file" if options.send_to_stdout and (options.file_mode == File::CREATE)
|
120
151
|
raise ::OptionParser::ParseError, "a username is needed" if options.send_to_stdout and argv.size < 1
|
121
152
|
raise ::OptionParser::ParseError, "a username and password are needed" if options.send_to_stdout and options.batch_mode and ( argv.size < 2 )
|
122
153
|
raise ::OptionParser::ParseError, "a passwordfile, username and password are needed " if not options.send_to_stdout and options.batch_mode and ( argv.size < 3 )
|
123
154
|
raise ::OptionParser::ParseError, "a passwordfile and username are needed" if argv.size < 2
|
155
|
+
raise ::OptionParser::ParseError, "options -i and -b are mutually exclusive" if options.batch_mode && options.read_stdin_once
|
124
156
|
|
157
|
+
options.operation = options.operation.shift || :add_or_update
|
125
158
|
options.passwdfile = argv.shift unless options.send_to_stdout
|
126
159
|
options.username = argv.shift
|
127
160
|
options.password = argv.shift if options.batch_mode
|
@@ -133,33 +166,60 @@ Usage:
|
|
133
166
|
end
|
134
167
|
end
|
135
168
|
|
169
|
+
def fetch_password(width=20)
|
170
|
+
return options.password if options.batch_mode
|
171
|
+
console = Console.new
|
172
|
+
if options.read_stdin_once then
|
173
|
+
pw_in = console.read_answer
|
174
|
+
return pw_in
|
175
|
+
end
|
176
|
+
|
177
|
+
case options.operation
|
178
|
+
when :verify
|
179
|
+
pw_in = console.ask("Enter password: ".rjust(width))
|
180
|
+
raise PasswordError, "password '#{pw_in}' too long" if pw_in.length >= MAX_PASSWD_LENGTH
|
181
|
+
when :add_or_update
|
182
|
+
pw_in = console.ask("New password: ".rjust(width))
|
183
|
+
raise PasswordError, "password '#{pw_in}' too long" if pw_in.length >= MAX_PASSWD_LENGTH
|
184
|
+
|
185
|
+
pw_validate = console.ask("Re-type new password: ".rjust(width))
|
186
|
+
raise PasswordError, "They don't match, sorry." unless pw_in == pw_validate
|
187
|
+
end
|
188
|
+
|
189
|
+
return pw_in
|
190
|
+
end
|
191
|
+
|
136
192
|
def run(argv, env = ENV)
|
137
193
|
begin
|
138
194
|
parse_options(argv)
|
195
|
+
console = Console.new
|
139
196
|
passwd_file = PasswdFile.new(options.passwdfile, options.file_mode)
|
140
|
-
|
141
|
-
|
197
|
+
case options.operation
|
198
|
+
when :delete
|
142
199
|
passwd_file.delete(options.username)
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
pw_validate = console.ask("Re-type new password: ")
|
155
|
-
raise PasswordError, "They don't match, sorry." unless pw_in == pw_validate
|
156
|
-
options.password = pw_in
|
200
|
+
passwd_file.save!
|
201
|
+
when :verify
|
202
|
+
if passwd_file.has_entry?(options.username) then
|
203
|
+
pw_in = fetch_password
|
204
|
+
if passwd_file.authenticated?(options.username, pw_in) then
|
205
|
+
$stderr.puts "Password for user #{options.username} correct."
|
206
|
+
else
|
207
|
+
raise HTAuth::Error, "Password verification for user #{options.username} failed."
|
208
|
+
end
|
209
|
+
else
|
210
|
+
raise HTAuth::Error, "User #{options.username} not found"
|
157
211
|
end
|
158
|
-
|
212
|
+
when :add_or_update
|
213
|
+
options.password = fetch_password
|
214
|
+
action = passwd_file.has_entry?(options.username) ? "Changing" : "Adding"
|
215
|
+
console.say "#{action} password for #{options.username}."
|
216
|
+
passwd_file.add_or_update(options.username, options.password, options.algorithm, options.algorithm_args)
|
217
|
+
passwd_file.save!
|
218
|
+
when :stdout
|
219
|
+
options.password = fetch_password
|
220
|
+
passwd_file.add_or_update(options.username, options.password, options.algorithm, options.algorithm_args)
|
221
|
+
passwd_file.save!
|
159
222
|
end
|
160
|
-
|
161
|
-
passwd_file.save!
|
162
|
-
|
163
223
|
rescue HTAuth::FileAccessError => fae
|
164
224
|
msg = "Password file failure (#{options.passwdfile}) "
|
165
225
|
$stderr.puts "#{msg}: #{fae.message}"
|