htauth 2.1.1 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/HISTORY.md +12 -0
- data/Manifest.txt +2 -0
- data/README.md +55 -33
- data/Rakefile +7 -4
- data/bin/htdigest-ruby +1 -1
- data/bin/htpasswd-ruby +1 -1
- data/lib/htauth/algorithm.rb +15 -2
- data/lib/htauth/argon2.rb +77 -0
- data/lib/htauth/bcrypt.rb +5 -0
- data/lib/htauth/cli/digest.rb +1 -4
- data/lib/htauth/cli/passwd.rb +9 -7
- data/lib/htauth/cli.rb +1 -2
- data/lib/htauth/digest_entry.rb +1 -0
- data/lib/htauth/passwd_entry.rb +4 -15
- data/lib/htauth/passwd_file.rb +6 -6
- data/lib/htauth/sha1.rb +1 -1
- data/lib/htauth/version.rb +1 -1
- data/lib/htauth.rb +1 -1
- data/spec/algorithm_spec.rb +0 -1
- data/spec/argon2_spec.rb +28 -0
- data/spec/bcrypt_spec.rb +0 -1
- data/spec/cli/passwd_spec.rb +17 -1
- data/spec/crypt_spec.rb +0 -1
- data/spec/digest_entry_spec.rb +0 -1
- data/spec/digest_file_spec.rb +0 -1
- data/spec/md5_spec.rb +1 -3
- data/spec/passwd_entry_spec.rb +15 -2
- data/spec/sha1_spec.rb +0 -1
- data/spec/spec_helper.rb +3 -6
- data/tasks/default.rake +22 -14
- data/tasks/this.rb +2 -2
- metadata +64 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ad5523ccfeacb957acb9cf2fae40e19e51bc92e10d093a950949fbe00bb50b1a
|
4
|
+
data.tar.gz: 280ed439110fd03ca5510ae81d8b17a4f0db21f4f0aed7360238bbcef83fe6c1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cc5ddd224ce189eeb5d16a60059a9fcab868ff65de9aad4efac3177425004d98b22d9af29f556d001ae0c083b31cdaef4cf723c7d1342739fc01c7be12841f42
|
7
|
+
data.tar.gz: 28a12fef7f3c7a96c9bdb186a71b06b8263da2c1f6b77dfcba45239129a17311d9fbcfa799aaf004efbf09876cc380de99bb91cab6be23ffefc23ce70286618b
|
data/HISTORY.md
CHANGED
@@ -1,4 +1,16 @@
|
|
1
1
|
# Changelog
|
2
|
+
## Version 2.3.0 - 2024-02-03
|
3
|
+
|
4
|
+
* Add support for argon2 encryption [#18](https://github.com/copiousfreetime/htauth/pull/18)
|
5
|
+
* Update supported ruby version, now supporting ruby 3.x only
|
6
|
+
* Semaphore updates
|
7
|
+
|
8
|
+
## Version 2.2.0 - 2023-02-06
|
9
|
+
|
10
|
+
* Update ruby versions
|
11
|
+
* Add to sempahore for CI
|
12
|
+
* fix various spelling issues
|
13
|
+
|
2
14
|
## Version 2.1.1 - 2020-04-02
|
3
15
|
|
4
16
|
* Update minimum ruby versions to modern versions
|
data/Manifest.txt
CHANGED
@@ -8,6 +8,7 @@ bin/htdigest-ruby
|
|
8
8
|
bin/htpasswd-ruby
|
9
9
|
lib/htauth.rb
|
10
10
|
lib/htauth/algorithm.rb
|
11
|
+
lib/htauth/argon2.rb
|
11
12
|
lib/htauth/bcrypt.rb
|
12
13
|
lib/htauth/cli.rb
|
13
14
|
lib/htauth/cli/digest.rb
|
@@ -27,6 +28,7 @@ lib/htauth/plaintext.rb
|
|
27
28
|
lib/htauth/sha1.rb
|
28
29
|
lib/htauth/version.rb
|
29
30
|
spec/algorithm_spec.rb
|
31
|
+
spec/argon2_spec.rb
|
30
32
|
spec/bcrypt_spec.rb
|
31
33
|
spec/cli/digest_spec.rb
|
32
34
|
spec/cli/passwd_spec.rb
|
data/README.md
CHANGED
@@ -1,29 +1,51 @@
|
|
1
1
|
## HTAuth
|
2
2
|
|
3
|
-
|
4
|
-
|
3
|
+
[](https://copiousfreetime.semaphoreci.com/projects/htauth)
|
4
|
+
|
5
|
+
* [Homepage](http://github.com/copiousfreetime/htauth)
|
6
|
+
* [Github](http://github.com/copiousfreetime/htauth)
|
5
7
|
|
6
8
|
## DESCRIPTION
|
7
9
|
|
8
|
-
HTAuth
|
9
|
-
htpasswd
|
10
|
-
htpasswd files.
|
10
|
+
HTAuth provides an API and commandline tools for managing Apache/httpd style
|
11
|
+
htpasswd and htdigest files.
|
11
12
|
|
12
13
|
## FEATURES
|
13
14
|
|
14
|
-
HTAuth provides
|
15
|
-
|
16
|
-
|
15
|
+
HTAuth provides an API allowing direct manipulation of Apache/httpd style
|
16
|
+
`htdigest` and `htpasswd` files. Supporting full programmatic manipulation and
|
17
|
+
authentication of user credentials.
|
17
18
|
|
18
|
-
|
19
|
-
|
20
|
-
|
19
|
+
HTAuth also includes drop-in, commandline compatible replacements for the Apache
|
20
|
+
utilities `htpasswd` and `htdigest` with the respective `htpasswd-ruby` and
|
21
|
+
`htdigest-ruby` commands.
|
21
22
|
|
22
|
-
Additionally,
|
23
|
-
|
23
|
+
Additionally, support for the [argon2](https://github.com/technion/ruby-argon2)
|
24
|
+
password hashing algorithm is provided for most platforms.
|
24
25
|
|
25
26
|
## SYNOPSIS
|
26
27
|
|
28
|
+
### API Usage
|
29
|
+
|
30
|
+
HTAuth::DigestFile.open("some.htdigest") do |df|
|
31
|
+
df.add_or_update('someuser', 'myrealm', 'a password')
|
32
|
+
df.delete('someolduser', 'myotherrealm')
|
33
|
+
end
|
34
|
+
|
35
|
+
HTAuth::PasswdFile.open("some.htpasswd", HTAuth::File::CREATE) do |pf|
|
36
|
+
pf.add('someuser', 'a password', 'md5')
|
37
|
+
pf.add('someotheruser', 'a different password', 'sha1')
|
38
|
+
end
|
39
|
+
|
40
|
+
HTAuth::PasswdFile.open("some.htpasswd", HTAuth::File::ALTER) do |pf|
|
41
|
+
pf.update('someuser', 'a password', 'bcrypt')
|
42
|
+
end
|
43
|
+
|
44
|
+
HTAuth::PasswdFile.open("some.htpasswd") do |pf|
|
45
|
+
pf.authenticated?('someuser', 'a password')
|
46
|
+
end
|
47
|
+
|
48
|
+
|
27
49
|
### htpasswd-ruby command line application
|
28
50
|
|
29
51
|
Usage:
|
@@ -33,7 +55,8 @@ Additionally, you can access all the functionality of *htdigest-ruby* and
|
|
33
55
|
htpasswd-ruby -n[imBdps] [-C cost] username
|
34
56
|
htpasswd-ruby -nb[mBdps] [-C cost] username password
|
35
57
|
|
36
|
-
|
58
|
+
--argon2 Force argon2 encryption of the password.
|
59
|
+
-b, --batch Batch mode, get the password from the command line, rather than prompt.
|
37
60
|
-B, --bcrypt Force bcrypt encryption of the password.
|
38
61
|
-C, --cost COST Set the computing time used for the bcrypt algorithm
|
39
62
|
(higher is more secure but slower, default: 5, valid: 4 to 31).
|
@@ -47,9 +70,7 @@ Additionally, you can access all the functionality of *htdigest-ruby* and
|
|
47
70
|
-p, --plaintext Do not encrypt the password (plaintext).
|
48
71
|
-s, --sha1 Force SHA encryption of the password.
|
49
72
|
-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.
|
73
|
+
--verify Verify password for the specified user.
|
53
74
|
|
54
75
|
### htdigest-ruby command line application
|
55
76
|
|
@@ -59,30 +80,31 @@ Additionally, you can access all the functionality of *htdigest-ruby* and
|
|
59
80
|
-h, --help Display this help.
|
60
81
|
-v, --version Show version info.
|
61
82
|
|
62
|
-
|
83
|
+
## Supported Hash Algorithms
|
63
84
|
|
64
|
-
|
65
|
-
|
66
|
-
df.delete('someolduser', 'myotherrealm')
|
67
|
-
end
|
85
|
+
Out of the box, `htauth` supports the classic algorithms that ship with Apache
|
86
|
+
`htpasswd`.
|
68
87
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
88
|
+
- Built in
|
89
|
+
- Generally accepted
|
90
|
+
- MD5 (default for compatibility reasons)
|
91
|
+
- bcrypt (probably the better choice)
|
73
92
|
|
74
|
-
|
75
|
-
|
76
|
-
|
93
|
+
- **Not Recommended** - available only for backwards compatibility with `htpasswd`
|
94
|
+
- SHA1
|
95
|
+
- crypt
|
96
|
+
- plaintext
|
77
97
|
|
78
|
-
|
79
|
-
|
80
|
-
|
98
|
+
- Available with the installation of additional libraries:
|
99
|
+
- argon2 - to use, add `gem 'argon2'` to your `Gemfile`. `argon2` will
|
100
|
+
now be a valid algorithm to use in `HTAuth::PasswdFile` API. Currently
|
101
|
+
argon2 is not supported on windows as the upstream `argon2` gem does not
|
102
|
+
support windows.
|
81
103
|
|
82
104
|
## CREDITS
|
83
105
|
|
84
106
|
* [The Apache Software Foundation](http://www.apache.org/)
|
85
|
-
* all the folks who contributed to htdigest and
|
107
|
+
* all the folks who contributed to htdigest and htpasswd
|
86
108
|
|
87
109
|
## MIT LICENSE
|
88
110
|
|
data/Rakefile
CHANGED
@@ -8,11 +8,14 @@ This.homepage = "http://github.com/copiousfreetime/#{ This.name }"
|
|
8
8
|
|
9
9
|
This.ruby_gemspec do |spec|
|
10
10
|
spec.add_dependency( 'bcrypt', '~> 3.1' )
|
11
|
+
spec.add_dependency( 'base64', '~> 0.2' )
|
11
12
|
|
12
|
-
spec.add_development_dependency( '
|
13
|
-
spec.add_development_dependency( '
|
14
|
-
spec.add_development_dependency( '
|
15
|
-
spec.add_development_dependency( '
|
13
|
+
spec.add_development_dependency( 'argon2' , '~> 2.3')
|
14
|
+
spec.add_development_dependency( 'rake' , '~> 13.1')
|
15
|
+
spec.add_development_dependency( 'minitest' , '~> 5.21' )
|
16
|
+
spec.add_development_dependency( 'minitest-junit' , '~> 1.1' )
|
17
|
+
spec.add_development_dependency( 'rdoc' , '~> 6.6' )
|
18
|
+
spec.add_development_dependency( 'simplecov', '~> 0.21' )
|
16
19
|
|
17
20
|
spec.metadata = {
|
18
21
|
"bug_tracker_uri" => "https://github.com/copiousfreetime/htauth/issues",
|
data/bin/htdigest-ruby
CHANGED
data/bin/htpasswd-ruby
CHANGED
data/lib/htauth/algorithm.rb
CHANGED
@@ -13,6 +13,8 @@ module HTAuth
|
|
13
13
|
SALT_CHARS = (%w[ . / ] + ("0".."9").to_a + ('A'..'Z').to_a + ('a'..'z').to_a).freeze
|
14
14
|
SALT_LENGTH = 8
|
15
15
|
|
16
|
+
# Public: flag for the argon2 algorithm
|
17
|
+
ARGON2 = "argon2".freeze
|
16
18
|
# Public: flag for the bcrypt algorithm
|
17
19
|
BCRYPT = "bcrypt".freeze
|
18
20
|
# Public: flag for the md5 algorithm
|
@@ -84,7 +86,17 @@ module HTAuth
|
|
84
86
|
end
|
85
87
|
|
86
88
|
# Internal
|
87
|
-
def encode(password)
|
89
|
+
def encode(password)
|
90
|
+
raise NotImplementedError, "#{self.class.name} must implement #{self.class.name}.encode(password)"
|
91
|
+
end
|
92
|
+
|
93
|
+
# Internal: Does the given password match the digest, the default just
|
94
|
+
# encodes and secure compares the result, different algorithms may overide
|
95
|
+
# this method
|
96
|
+
def verify_password?(password, digest)
|
97
|
+
encoded = encode(password)
|
98
|
+
self.class.secure_compare(encoded, digest)
|
99
|
+
end
|
88
100
|
|
89
101
|
# Internal: 8 bytes of random items from SALT_CHARS
|
90
102
|
def gen_salt(length = SALT_LENGTH)
|
@@ -92,7 +104,8 @@ module HTAuth
|
|
92
104
|
end
|
93
105
|
|
94
106
|
# Internal: this is not the Base64 encoding, this is the to64()
|
95
|
-
# method from the
|
107
|
+
# method from the Apache Portable Runtime (APR) library
|
108
|
+
# https://github.com/apache/apr/blob/trunk/crypto/apr_md5.c#L493-L502
|
96
109
|
def to_64(number, rounds)
|
97
110
|
r = StringIO.new
|
98
111
|
rounds.times do |x|
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'htauth/algorithm'
|
2
|
+
begin
|
3
|
+
require 'argon2'
|
4
|
+
rescue LoadError
|
5
|
+
end
|
6
|
+
|
7
|
+
module HTAuth
|
8
|
+
# Internal: Support of the argon2id algorithm and password format.
|
9
|
+
|
10
|
+
class Argon2 < Algorithm
|
11
|
+
class NotSupportedError < ::HTAuth::InvalidAlgorithmError
|
12
|
+
def message
|
13
|
+
"Unfortunately Argon2 passwords are not supported on `#{RUBY_PLATFORM} at this time. This because the upstream argon2 gem does not support windows."
|
14
|
+
end
|
15
|
+
end
|
16
|
+
class NotInstalledError < ::HTAuth::InvalidAlgorithmError
|
17
|
+
def message
|
18
|
+
"Argon2 passwords are supported if the `argon2' gem is installed. Add `gem 'argon2', '~> 2.3'` to your Gemfile"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# from upstream, used to help make a nice error message if its not installed
|
23
|
+
# https://github.com/technion/ruby-argon2/blob/3388d7e05e8b486ea4ba8bd2aeb1e9988f025f13/lib/argon2/hash_format.rb#L45
|
24
|
+
PREFIX = /^\$argon2(id?|d).{,113}/.freeze
|
25
|
+
ARGON2_GEM_INSTALLED = defined?(::Argon2)
|
26
|
+
|
27
|
+
def self.supported?
|
28
|
+
!::Gem.win_platform?
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.ensure_available!
|
32
|
+
raise NotSupportedError unless supported?
|
33
|
+
raise NotInstalledError unless ARGON2_GEM_INSTALLED
|
34
|
+
end
|
35
|
+
|
36
|
+
attr_accessor :options
|
37
|
+
|
38
|
+
def self.handles?(password_entry)
|
39
|
+
return false unless PREFIX.match?(password_entry)
|
40
|
+
ensure_available!
|
41
|
+
|
42
|
+
return ::Argon2::Password.valid_hash?(password_entry)
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.extract_options_from_existing_password_field(existing)
|
46
|
+
hash_format = ::Argon2::HashFormat.new(existing)
|
47
|
+
|
48
|
+
# m_cost on the input is the 2**m_cost, but in the hash its the number of
|
49
|
+
# bytes, so need to convert it back to a power of 2, which is the
|
50
|
+
# log2(m_cost)
|
51
|
+
|
52
|
+
{
|
53
|
+
t_cost: hash_format.t_cost,
|
54
|
+
m_cost: ::Math.log2(hash_format.m_cost).floor,
|
55
|
+
p_cost: hash_format.p_cost,
|
56
|
+
}
|
57
|
+
end
|
58
|
+
|
59
|
+
def initialize(params = { profile: :rfc_9106_low_memory })
|
60
|
+
self.class.ensure_available!
|
61
|
+
if existing = (params['existing'] || params[:existing]) then
|
62
|
+
@options = self.class.extract_options_from_existing_password_field(existing)
|
63
|
+
else
|
64
|
+
@options = params
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def encode(password)
|
69
|
+
argon2 = ::Argon2::Password.new(options)
|
70
|
+
argon2.create(password)
|
71
|
+
end
|
72
|
+
|
73
|
+
def verify_password?(password, digest)
|
74
|
+
::Argon2::Password.verify_password(password, digest)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
data/lib/htauth/bcrypt.rb
CHANGED
data/lib/htauth/cli/digest.rb
CHANGED
data/lib/htauth/cli/passwd.rb
CHANGED
@@ -1,6 +1,4 @@
|
|
1
|
-
require 'htauth/
|
2
|
-
require 'htauth/passwd_file'
|
3
|
-
require 'htauth/console'
|
1
|
+
require 'htauth/cli'
|
4
2
|
|
5
3
|
require 'ostruct'
|
6
4
|
require 'optparse'
|
@@ -44,8 +42,8 @@ module HTAuth
|
|
44
42
|
@option_parser = OptionParser.new(nil, 16) do |op|
|
45
43
|
op.banner = <<-EOB
|
46
44
|
Usage:
|
47
|
-
#{op.program_name} [-
|
48
|
-
#{op.program_name} -b[
|
45
|
+
#{op.program_name} [-acimBdpsD] [--verify] [-C cost] passwordfile username
|
46
|
+
#{op.program_name} -b[acmBdpsD] [--verify] [-C cost] passwordfile username password
|
49
47
|
|
50
48
|
#{op.program_name} -n[imBdps] [-C cost] username
|
51
49
|
#{op.program_name} -nb[mBdps] [-C cost] username password
|
@@ -53,7 +51,11 @@ Usage:
|
|
53
51
|
|
54
52
|
op.separator ""
|
55
53
|
|
56
|
-
op.on("
|
54
|
+
op.on("--argon2", "Force argon2 encryption of the password.") do |a|
|
55
|
+
options.algorithm = Algorithm::ARGON2
|
56
|
+
end
|
57
|
+
|
58
|
+
op.on("-b", "--batch", "Batch mode, get the password from the command line, rather than prompt.") do |b|
|
57
59
|
options.batch_mode = b
|
58
60
|
end
|
59
61
|
|
@@ -118,7 +120,7 @@ Usage:
|
|
118
120
|
options.show_version = v
|
119
121
|
end
|
120
122
|
|
121
|
-
op.on("--verify", "Verify password for the specified user") do |v|
|
123
|
+
op.on("--verify", "Verify password for the specified user.") do |v|
|
122
124
|
options.operation << :verify
|
123
125
|
end
|
124
126
|
|
data/lib/htauth/cli.rb
CHANGED
data/lib/htauth/digest_entry.rb
CHANGED
data/lib/htauth/passwd_entry.rb
CHANGED
@@ -62,7 +62,7 @@ module HTAuth
|
|
62
62
|
# Internal: Create a new Entry with the given user, password, and algorithm
|
63
63
|
def initialize(user, password = nil, alg = Algorithm::DEFAULT, alg_params = {} )
|
64
64
|
@user = user
|
65
|
-
alg = Algorithm::DEFAULT if alg == Algorithm::EXISTING
|
65
|
+
alg = Algorithm::DEFAULT if alg == Algorithm::EXISTING
|
66
66
|
@algorithm = Algorithm.algorithm_from_name(alg, alg_params)
|
67
67
|
@digest = calc_digest(password)
|
68
68
|
end
|
@@ -106,25 +106,14 @@ module HTAuth
|
|
106
106
|
end
|
107
107
|
|
108
108
|
# Public: Check if the given password is the password of this entry
|
109
|
-
#
|
110
|
-
# tries all of the ones that it thinks it could be, and marks the algorithm if it matches
|
111
|
-
# when looking for a matche, we always compare all of them, no short
|
112
|
-
# circuiting
|
109
|
+
#
|
113
110
|
def authenticated?(check_password)
|
114
|
-
|
115
|
-
if algorithm.kind_of?(Bcrypt) then
|
116
|
-
bc = ::BCrypt::Password.new(digest)
|
117
|
-
authed = bc.is_password?(check_password)
|
118
|
-
else
|
119
|
-
encoded = algorithm.encode(check_password)
|
120
|
-
authed = Algorithm.secure_compare(encoded, digest)
|
121
|
-
end
|
122
|
-
return authed
|
111
|
+
algorithm.verify_password?(check_password, @digest)
|
123
112
|
end
|
124
113
|
|
125
114
|
# Internal: Returns the key of this entry
|
126
115
|
def key
|
127
|
-
|
116
|
+
"#{user}"
|
128
117
|
end
|
129
118
|
|
130
119
|
# Internal: Returns the file line for this entry
|
data/lib/htauth/passwd_file.rb
CHANGED
@@ -11,9 +11,9 @@ module HTAuth
|
|
11
11
|
# Examples
|
12
12
|
#
|
13
13
|
# ::HTAuth::PasswdFile.open("my.passwd") do |pf|
|
14
|
-
# pf.has_entry?('myuser'
|
15
|
-
# pf.add_or_update('someuser', '
|
16
|
-
# pf.delete('someolduser'
|
14
|
+
# pf.has_entry?('myuser')
|
15
|
+
# pf.add_or_update('someuser', 'a password')
|
16
|
+
# pf.delete('someolduser')
|
17
17
|
# end
|
18
18
|
#
|
19
19
|
class PasswdFile < HTAuth::File
|
@@ -68,7 +68,7 @@ module HTAuth
|
|
68
68
|
# username - the username of the entry
|
69
69
|
# password - the password of the entry
|
70
70
|
# algorithm - the algorithm to use (default: "md5"). Valid options are:
|
71
|
-
# "md5", "bcrypt", "sha1", "plaintext", or "crypt"
|
71
|
+
# "md5", "bcrypt", "argon2", "sha1", "plaintext", or "crypt"
|
72
72
|
# algorithm_args - key-value pairs of arguments that are passed to the
|
73
73
|
# algorithm, currently this is only used to pass the cost
|
74
74
|
# to the bcrypt algorithm
|
@@ -96,7 +96,7 @@ module HTAuth
|
|
96
96
|
# username - the username of the entry
|
97
97
|
# password - the password of the entry
|
98
98
|
# algorithm - the algorithm to use (default: "md5"). Valid options are:
|
99
|
-
# "md5", "bcrypt", "sha1", "plaintext", or "crypt"
|
99
|
+
# "md5", "bcrypt", "argon2", "sha1", "plaintext", or "crypt"
|
100
100
|
# algorithm_args - key-value pairs of arguments that are passed to the
|
101
101
|
# algorithm, currently this is only used to pass the cost
|
102
102
|
# to the bcrypt algorithm
|
@@ -133,7 +133,7 @@ module HTAuth
|
|
133
133
|
# username - the username of the entry
|
134
134
|
# password - the password of the entry
|
135
135
|
# algorithm - the algorithm to use (default: "existing"). Valid options are:
|
136
|
-
# "existing", "md5", "bcrypt", "sha1", "plaintext", or "crypt"
|
136
|
+
# "existing", "md5", "bcrypt", "argon2", "sha1", "plaintext", or "crypt"
|
137
137
|
# algorithm_args - key-value pairs of arguments that are passed to the
|
138
138
|
# algorithm, currently this is only used to pass the cost
|
139
139
|
# to the bcrypt algorithm
|
data/lib/htauth/sha1.rb
CHANGED
data/lib/htauth/version.rb
CHANGED
data/lib/htauth.rb
CHANGED
data/spec/algorithm_spec.rb
CHANGED
data/spec/argon2_spec.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'argon2'
|
3
|
+
|
4
|
+
describe HTAuth::Argon2 do
|
5
|
+
|
6
|
+
it "decodes the hash back to the original options" do
|
7
|
+
hash = '$argon2id$v=19$m=65536,t=3,p=4$V1ln1M4o1RS7SzWHAtqyWQ$jEHi1Qo2FSBgLAPpOa1mPx6OD/twtjj8M1AlVZwamPg'
|
8
|
+
options = HTAuth::Argon2.extract_options_from_existing_password_field(hash)
|
9
|
+
_(options).must_equal ::Argon2::Profiles[:rfc_9106_low_memory]
|
10
|
+
end
|
11
|
+
|
12
|
+
it "encrypts the same way that argon2 does by default" do
|
13
|
+
argon2 = HTAuth::Argon2.new
|
14
|
+
hash = argon2.encode("a secret")
|
15
|
+
_(::Argon2::Password.verify_password('a secret', hash)).must_equal true
|
16
|
+
end
|
17
|
+
|
18
|
+
it "allow changing the parameters directly" do
|
19
|
+
hash = '$argon2id$v=19$m=262144,t=3,p=4$7DRAuE1yIHPHqISmcyaJTg$M0EErpbxqv8dvrMrQMoGDEA7KQCw67jGdXwtCbRINFs'
|
20
|
+
options = HTAuth::Argon2.extract_options_from_existing_password_field(hash)
|
21
|
+
|
22
|
+
options[:m_cost] = 11
|
23
|
+
|
24
|
+
argon2 = HTAuth::Argon2.new(options)
|
25
|
+
local_hash = argon2.encode("a secret")
|
26
|
+
_(::Argon2::Password.verify_password('a secret', local_hash)).must_equal true
|
27
|
+
end
|
28
|
+
end
|
data/spec/bcrypt_spec.rb
CHANGED
data/spec/cli/passwd_spec.rb
CHANGED
@@ -129,7 +129,23 @@ describe HTAuth::CLI::Passwd do
|
|
129
129
|
_(@stderr.string).must_match( /ERROR:/m )
|
130
130
|
_(se.status).must_equal 1
|
131
131
|
end
|
132
|
-
|
132
|
+
end
|
133
|
+
|
134
|
+
it "creates a new file with one argon2 entry" do
|
135
|
+
begin
|
136
|
+
@stdin.puts "a secret"
|
137
|
+
@stdin.puts "a secret"
|
138
|
+
@stdin.rewind
|
139
|
+
@htauth.run([ "--argon", "-c", @new_file, "agatha" ])
|
140
|
+
rescue SystemExit => se
|
141
|
+
_(se.status).must_equal 0
|
142
|
+
l = IO.readlines(@new_file)
|
143
|
+
fields = l.first.split(':')
|
144
|
+
_(fields.first).must_equal "agatha"
|
145
|
+
argon2_hash = fields.last
|
146
|
+
|
147
|
+
_(::Argon2::Password.valid_hash?(argon2_hash)).wont_be_nil
|
148
|
+
end
|
133
149
|
end
|
134
150
|
|
135
151
|
it "does not verify the password from stdin on -i option" do
|
data/spec/crypt_spec.rb
CHANGED
data/spec/digest_entry_spec.rb
CHANGED
data/spec/digest_file_spec.rb
CHANGED
data/spec/md5_spec.rb
CHANGED
data/spec/passwd_entry_spec.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
require 'htauth/passwd_entry'
|
3
2
|
|
4
3
|
describe HTAuth::PasswdEntry do
|
5
4
|
before(:each) do
|
@@ -32,6 +31,14 @@ describe HTAuth::PasswdEntry do
|
|
32
31
|
_(bob.digest).must_equal "b secret"
|
33
32
|
end
|
34
33
|
|
34
|
+
it "encypts correctly for argon2" do
|
35
|
+
# Don't do this in real life, do not pass in a salt, let the algorithm generate it internally
|
36
|
+
salt = ";L\xCDMRO\v\x13;\x012\x9B'\xEE\\i"
|
37
|
+
agatha = HTAuth::PasswdEntry.new("agatha","ag secret", "argon2", {salt_do_not_supply: salt} )
|
38
|
+
expected = "$argon2id$v=19$m=65536,t=3,p=4$O0zNTVJPCxM7ATKbJ+5caQ$e7wIsl7AY+uIbN+1StYOKkVCJhOrvX7BxAlQ+sPC+Nc"
|
39
|
+
_(agatha.digest).must_equal expected
|
40
|
+
end
|
41
|
+
|
35
42
|
it "encrypts with crypt as a default, when parsed from crypt()'d line" do
|
36
43
|
bob2 = HTAuth::PasswdEntry.from_line(@bob.to_s)
|
37
44
|
_(bob2.algorithm).must_be_instance_of(HTAuth::Crypt)
|
@@ -97,6 +104,12 @@ describe HTAuth::PasswdEntry do
|
|
97
104
|
_(s2.authenticated?("b secret")).must_equal true
|
98
105
|
end
|
99
106
|
|
107
|
+
it "authenticates correctly against argon2" do
|
108
|
+
s = HTAuth::PasswdEntry.new("agatha", "ag secret", "argon2")
|
109
|
+
s2 = HTAuth::PasswdEntry.from_line(s.to_s)
|
110
|
+
_(s2.authenticated?("ag secret")).must_equal true
|
111
|
+
end
|
112
|
+
|
100
113
|
it "can update the cost of an entry after initialization before encoding password" do
|
101
114
|
s = HTAuth::PasswdEntry.new("brenda", "b secret", "bcrypt")
|
102
115
|
_(s.algorithm.cost).must_equal(::HTAuth::Bcrypt::DEFAULT_APACHE_COST)
|
@@ -108,7 +121,7 @@ describe HTAuth::PasswdEntry do
|
|
108
121
|
_(s2.algorithm.cost).must_equal(12)
|
109
122
|
end
|
110
123
|
|
111
|
-
it "raises an error if
|
124
|
+
it "raises an error if assigning an invalid algorithm" do
|
112
125
|
b = HTAuth::PasswdEntry.new("brenda", "b secret", "bcrypt")
|
113
126
|
_ { b.algorithm = 42 }.must_raise(HTAuth::InvalidAlgorithmError)
|
114
127
|
end
|
data/spec/sha1_spec.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -1,10 +1,6 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
puts "Using coverage!"
|
4
|
-
SimpleCov.start if ENV['COVERAGE']
|
5
|
-
end
|
1
|
+
require 'simplecov'
|
2
|
+
SimpleCov.start if ENV['COVERAGE']
|
6
3
|
|
7
|
-
gem 'minitest'
|
8
4
|
require 'minitest/autorun'
|
9
5
|
require 'minitest/pride'
|
10
6
|
|
@@ -26,3 +22,4 @@ class ConsoleIO < StringIO
|
|
26
22
|
yield self
|
27
23
|
end
|
28
24
|
end
|
25
|
+
require 'htauth'
|
data/tasks/default.rake
CHANGED
@@ -36,11 +36,13 @@ task :develop => "develop:default"
|
|
36
36
|
# Minitest - standard TestTask
|
37
37
|
#------------------------------------------------------------------------------
|
38
38
|
begin
|
39
|
-
require '
|
40
|
-
|
41
|
-
t.
|
42
|
-
t.libs
|
43
|
-
t.
|
39
|
+
require 'minitest/test_task'
|
40
|
+
Minitest::TestTask.create( :test) do |t|
|
41
|
+
t.libs << "lib"
|
42
|
+
t.libs << "spec"
|
43
|
+
t.libs << "test"
|
44
|
+
t.warning = true
|
45
|
+
t.test_globs = "{test,spec}/**/{test_*,*_spec}.rb"
|
44
46
|
end
|
45
47
|
|
46
48
|
task :test_requirements
|
@@ -143,14 +145,20 @@ namespace :fixme do
|
|
143
145
|
end
|
144
146
|
|
145
147
|
def local_fixme_files
|
146
|
-
This.manifest.select { |p| p =~ %r|^tasks/| }
|
148
|
+
local_files = This.manifest.select { |p| p =~ %r|^tasks/| }
|
149
|
+
local_files << ".semaphore/semaphore.yml"
|
147
150
|
end
|
148
151
|
|
149
152
|
def outdated_fixme_files
|
150
153
|
local_fixme_files.select do |local|
|
151
154
|
upstream = fixme_project_path( local )
|
152
|
-
upstream.exist?
|
153
|
-
|
155
|
+
if upstream.exist? then
|
156
|
+
if File.exist?( local ) then
|
157
|
+
( Digest::SHA256.file( local ) != Digest::SHA256.file( upstream ) )
|
158
|
+
else
|
159
|
+
true
|
160
|
+
end
|
161
|
+
end
|
154
162
|
end
|
155
163
|
end
|
156
164
|
|
@@ -201,7 +209,7 @@ task :gemspec do
|
|
201
209
|
end
|
202
210
|
|
203
211
|
# .rbc files from ruby 2.0
|
204
|
-
CLOBBER <<
|
212
|
+
CLOBBER << "**/*.rbc"
|
205
213
|
|
206
214
|
# The standard gem packaging task, everyone has it.
|
207
215
|
require 'rubygems/package_task'
|
@@ -213,19 +221,19 @@ end
|
|
213
221
|
# Release - the steps we go through to do a final release, this is pulled from
|
214
222
|
# a compbination of mojombo's rakegem, hoe and hoe-git
|
215
223
|
#
|
216
|
-
# 1) make sure we are on the
|
224
|
+
# 1) make sure we are on the main branch
|
217
225
|
# 2) make sure there are no uncommitted items
|
218
226
|
# 3) check the manifest and make sure all looks good
|
219
227
|
# 4) build the gem
|
220
228
|
# 5) do an empty commit to have the commit message of the version
|
221
229
|
# 6) tag that commit as the version
|
222
|
-
# 7) push
|
230
|
+
# 7) push main
|
223
231
|
# 8) push the tag
|
224
232
|
# 7) pus the gem
|
225
233
|
#------------------------------------------------------------------------------
|
226
234
|
task :release_check do
|
227
|
-
unless `git branch` =~ /^\*
|
228
|
-
abort "You must be on the
|
235
|
+
unless `git branch` =~ /^\* main/
|
236
|
+
abort "You must be on the main branch to release!"
|
229
237
|
end
|
230
238
|
unless `git status` =~ /^nothing to commit/m
|
231
239
|
abort "Nope, sorry, you have unfinished business"
|
@@ -236,7 +244,7 @@ desc "Create tag v#{This.version}, build and push #{This.platform_gemspec.full_n
|
|
236
244
|
task :release => [ :release_check, 'manifest:check', :gem ] do
|
237
245
|
sh "git commit --allow-empty -a -m 'Release #{This.version}'"
|
238
246
|
sh "git tag -a -m 'v#{This.version}' v#{This.version}"
|
239
|
-
sh "git push origin
|
247
|
+
sh "git push origin main"
|
240
248
|
sh "git push origin v#{This.version}"
|
241
249
|
sh "gem push pkg/#{This.platform_gemspec.full_name}.gem"
|
242
250
|
end
|
data/tasks/this.rb
CHANGED
@@ -25,7 +25,7 @@ class ThisProject
|
|
25
25
|
#
|
26
26
|
# Yields self
|
27
27
|
def initialize(&block)
|
28
|
-
@exclude_from_manifest = Regexp.union(/\.(git|DS_Store)/,
|
28
|
+
@exclude_from_manifest = Regexp.union(/\.(git|DS_Store|semaphore)/,
|
29
29
|
/^(doc|coverage|pkg|tmp|Gemfile(\.lock)?)/,
|
30
30
|
/^[^\/]+\.gemspec/,
|
31
31
|
/\.(swp|jar|bundle|so|rvmrc|travis.yml|byebug_history|fossa.yml|ruby-version)$/,
|
@@ -146,7 +146,7 @@ class ThisProject
|
|
146
146
|
spec.rdoc_options = [ "--main" , 'README.md',
|
147
147
|
"--markup", "tomdoc" ]
|
148
148
|
|
149
|
-
spec.required_ruby_version = '>= 2.
|
149
|
+
spec.required_ruby_version = '>= 2.3.0'
|
150
150
|
end
|
151
151
|
end
|
152
152
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: htauth
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeremy Hinegardner
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-02-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bcrypt
|
@@ -24,65 +24,106 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '3.1'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: base64
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.2'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.2'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: argon2
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '2.3'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '2.3'
|
27
55
|
- !ruby/object:Gem::Dependency
|
28
56
|
name: rake
|
29
57
|
requirement: !ruby/object:Gem::Requirement
|
30
58
|
requirements:
|
31
59
|
- - "~>"
|
32
60
|
- !ruby/object:Gem::Version
|
33
|
-
version: '13.
|
61
|
+
version: '13.1'
|
34
62
|
type: :development
|
35
63
|
prerelease: false
|
36
64
|
version_requirements: !ruby/object:Gem::Requirement
|
37
65
|
requirements:
|
38
66
|
- - "~>"
|
39
67
|
- !ruby/object:Gem::Version
|
40
|
-
version: '13.
|
68
|
+
version: '13.1'
|
41
69
|
- !ruby/object:Gem::Dependency
|
42
70
|
name: minitest
|
43
71
|
requirement: !ruby/object:Gem::Requirement
|
44
72
|
requirements:
|
45
73
|
- - "~>"
|
46
74
|
- !ruby/object:Gem::Version
|
47
|
-
version: '5.
|
75
|
+
version: '5.21'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '5.21'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: minitest-junit
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '1.1'
|
48
90
|
type: :development
|
49
91
|
prerelease: false
|
50
92
|
version_requirements: !ruby/object:Gem::Requirement
|
51
93
|
requirements:
|
52
94
|
- - "~>"
|
53
95
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
96
|
+
version: '1.1'
|
55
97
|
- !ruby/object:Gem::Dependency
|
56
98
|
name: rdoc
|
57
99
|
requirement: !ruby/object:Gem::Requirement
|
58
100
|
requirements:
|
59
101
|
- - "~>"
|
60
102
|
- !ruby/object:Gem::Version
|
61
|
-
version: '6.
|
103
|
+
version: '6.6'
|
62
104
|
type: :development
|
63
105
|
prerelease: false
|
64
106
|
version_requirements: !ruby/object:Gem::Requirement
|
65
107
|
requirements:
|
66
108
|
- - "~>"
|
67
109
|
- !ruby/object:Gem::Version
|
68
|
-
version: '6.
|
110
|
+
version: '6.6'
|
69
111
|
- !ruby/object:Gem::Dependency
|
70
112
|
name: simplecov
|
71
113
|
requirement: !ruby/object:Gem::Requirement
|
72
114
|
requirements:
|
73
115
|
- - "~>"
|
74
116
|
- !ruby/object:Gem::Version
|
75
|
-
version: '0.
|
117
|
+
version: '0.21'
|
76
118
|
type: :development
|
77
119
|
prerelease: false
|
78
120
|
version_requirements: !ruby/object:Gem::Requirement
|
79
121
|
requirements:
|
80
122
|
- - "~>"
|
81
123
|
- !ruby/object:Gem::Version
|
82
|
-
version: '0.
|
83
|
-
description: HTAuth
|
84
|
-
|
85
|
-
htpasswd files.
|
124
|
+
version: '0.21'
|
125
|
+
description: HTAuth provides an API and commandline tools for managing Apache/httpd
|
126
|
+
style htpasswd and htdigest files.
|
86
127
|
email: jeremy@copiousfreetime.org
|
87
128
|
executables:
|
88
129
|
- htdigest-ruby
|
@@ -104,6 +145,7 @@ files:
|
|
104
145
|
- bin/htpasswd-ruby
|
105
146
|
- lib/htauth.rb
|
106
147
|
- lib/htauth/algorithm.rb
|
148
|
+
- lib/htauth/argon2.rb
|
107
149
|
- lib/htauth/bcrypt.rb
|
108
150
|
- lib/htauth/cli.rb
|
109
151
|
- lib/htauth/cli/digest.rb
|
@@ -123,6 +165,7 @@ files:
|
|
123
165
|
- lib/htauth/sha1.rb
|
124
166
|
- lib/htauth/version.rb
|
125
167
|
- spec/algorithm_spec.rb
|
168
|
+
- spec/argon2_spec.rb
|
126
169
|
- spec/bcrypt_spec.rb
|
127
170
|
- spec/cli/digest_spec.rb
|
128
171
|
- spec/cli/passwd_spec.rb
|
@@ -153,7 +196,7 @@ metadata:
|
|
153
196
|
changelog_uri: https://github.com/copiousfreetime/htauth/blob/master/HISTORY.md
|
154
197
|
homepage_uri: https://github.com/copiousfreetime/htauth
|
155
198
|
source_code_uri: https://github.com/copiousfreetime/htauth
|
156
|
-
post_install_message:
|
199
|
+
post_install_message:
|
157
200
|
rdoc_options:
|
158
201
|
- "--main"
|
159
202
|
- README.md
|
@@ -165,21 +208,21 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
165
208
|
requirements:
|
166
209
|
- - ">="
|
167
210
|
- !ruby/object:Gem::Version
|
168
|
-
version: 2.
|
211
|
+
version: 2.3.0
|
169
212
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
170
213
|
requirements:
|
171
214
|
- - ">="
|
172
215
|
- !ruby/object:Gem::Version
|
173
216
|
version: '0'
|
174
217
|
requirements: []
|
175
|
-
rubygems_version: 3.
|
176
|
-
signing_key:
|
218
|
+
rubygems_version: 3.5.3
|
219
|
+
signing_key:
|
177
220
|
specification_version: 4
|
178
|
-
summary: HTAuth
|
179
|
-
|
180
|
-
htpasswd files.
|
221
|
+
summary: HTAuth provides an API and commandline tools for managing Apache/httpd style
|
222
|
+
htpasswd and htdigest files.
|
181
223
|
test_files:
|
182
224
|
- spec/algorithm_spec.rb
|
225
|
+
- spec/argon2_spec.rb
|
183
226
|
- spec/bcrypt_spec.rb
|
184
227
|
- spec/cli/digest_spec.rb
|
185
228
|
- spec/cli/passwd_spec.rb
|