htauth 2.2.0 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a392e544654ec732a878871166ba48129a72dc8e7d2b3b9e1ed37d99117546b9
4
- data.tar.gz: fb244f0b94603e5829d36d10e1309d98608a584c1bc8ea3a3d95f60f9950b7fb
3
+ metadata.gz: ad5523ccfeacb957acb9cf2fae40e19e51bc92e10d093a950949fbe00bb50b1a
4
+ data.tar.gz: 280ed439110fd03ca5510ae81d8b17a4f0db21f4f0aed7360238bbcef83fe6c1
5
5
  SHA512:
6
- metadata.gz: 3350108830c0ce7bb7e406ec2d449c89eaf3ce1047f095cab78cffe8c197fb03eb9a5ffe7ad1b782e6d15c1ed88017ef96cb48e87cd8e91993454e00bc1fffda
7
- data.tar.gz: 3b4183cd67ab796c5de2c2fcee847a94ab7d83e925b5283640bd7718274667e141d1618a10f927cf5c6e70cc5a667c284577d83d449a7566770ea60e5a2bc20e
6
+ metadata.gz: cc5ddd224ce189eeb5d16a60059a9fcab868ff65de9aad4efac3177425004d98b22d9af29f556d001ae0c083b31cdaef4cf723c7d1342739fc01c7be12841f42
7
+ data.tar.gz: 28a12fef7f3c7a96c9bdb186a71b06b8263da2c1f6b77dfcba45239129a17311d9fbcfa799aaf004efbf09876cc380de99bb91cab6be23ffefc23ce70286618b
data/HISTORY.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
- ## Version 2.2.0 - 2023-02-XX
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
3
9
 
4
10
  * Update ruby versions
5
11
  * Add to sempahore for CI
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
@@ -7,25 +7,45 @@
7
7
 
8
8
  ## DESCRIPTION
9
9
 
10
- HTAuth is a pure ruby replacement for the Apache support programs htdigest and
11
- htpasswd. Command line and API access are provided for access to htdigest and
12
- htpasswd files.
10
+ HTAuth provides an API and commandline tools for managing Apache/httpd style
11
+ htpasswd and htdigest files.
13
12
 
14
13
  ## FEATURES
15
14
 
16
- HTAuth provides to drop in commands *htdigest-ruby* and *htpasswd-ruby* that
17
- can manipulate the digest and passwd files in the same manner as Apache's
18
- original commands.
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.
19
18
 
20
- *htdigest-ruby* and *htpasswd-ruby* are command line compatible with *htdigest*
21
- and *htpasswd*. They support the same exact same command line options as the
22
- originals, and have some extras.
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.
23
22
 
24
- Additionally, you can access all the functionality of *htdigest-ruby* and
25
- *htpasswd-ruby* through an API.
23
+ Additionally, support for the [argon2](https://github.com/technion/ruby-argon2)
24
+ password hashing algorithm is provided for most platforms.
26
25
 
27
26
  ## SYNOPSIS
28
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
+
29
49
  ### htpasswd-ruby command line application
30
50
 
31
51
  Usage:
@@ -35,7 +55,8 @@ Additionally, you can access all the functionality of *htdigest-ruby* and
35
55
  htpasswd-ruby -n[imBdps] [-C cost] username
36
56
  htpasswd-ruby -nb[mBdps] [-C cost] username password
37
57
 
38
- -b, --batch Batch mode, get the password from the command line, rather than prompt
58
+ --argon2 Force argon2 encryption of the password.
59
+ -b, --batch Batch mode, get the password from the command line, rather than prompt.
39
60
  -B, --bcrypt Force bcrypt encryption of the password.
40
61
  -C, --cost COST Set the computing time used for the bcrypt algorithm
41
62
  (higher is more secure but slower, default: 5, valid: 4 to 31).
@@ -49,9 +70,7 @@ Additionally, you can access all the functionality of *htdigest-ruby* and
49
70
  -p, --plaintext Do not encrypt the password (plaintext).
50
71
  -s, --sha1 Force SHA encryption of the password.
51
72
  -v, --version Show version info.
52
- --verify Verify password for the specified user
53
-
54
- The SHA algorithm does not use a salt and is less secure than the MD5 algorithm.
73
+ --verify Verify password for the specified user.
55
74
 
56
75
  ### htdigest-ruby command line application
57
76
 
@@ -61,30 +80,31 @@ Additionally, you can access all the functionality of *htdigest-ruby* and
61
80
  -h, --help Display this help.
62
81
  -v, --version Show version info.
63
82
 
64
- ### API Usage
83
+ ## Supported Hash Algorithms
65
84
 
66
- HTAuth::DigestFile.open("some.htdigest") do |df|
67
- df.add_or_update('someuser', 'myrealm', 'a password')
68
- df.delete('someolduser', 'myotherrealm')
69
- end
85
+ Out of the box, `htauth` supports the classic algorithms that ship with Apache
86
+ `htpasswd`.
70
87
 
71
- HTAuth::PasswdFile.open("some.htpasswd", HTAuth::File::CREATE) do |pf|
72
- pf.add('someuser', 'a password', 'md5')
73
- pf.add('someotheruser', 'a different password', 'sha1')
74
- end
88
+ - Built in
89
+ - Generally accepted
90
+ - MD5 (default for compatibility reasons)
91
+ - bcrypt (probably the better choice)
75
92
 
76
- HTAuth::PasswdFile.open("some.htpasswd", HTAuth::File::ALTER) do |pf|
77
- pf.update('someuser', 'a password', 'bcrypt')
78
- end
93
+ - **Not Recommended** - available only for backwards compatibility with `htpasswd`
94
+ - SHA1
95
+ - crypt
96
+ - plaintext
79
97
 
80
- HTAuth::PasswdFile.open("some.htpasswd") do |pf|
81
- pf.authenticated?('someuser', 'a password')
82
- end
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.
83
103
 
84
104
  ## CREDITS
85
105
 
86
106
  * [The Apache Software Foundation](http://www.apache.org/)
87
- * all the folks who contributed to htdigest and htpassword
107
+ * all the folks who contributed to htdigest and htpasswd
88
108
 
89
109
  ## MIT LICENSE
90
110
 
data/Rakefile CHANGED
@@ -8,12 +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( 'rake' , '~> 13.0')
13
- spec.add_development_dependency( 'minitest' , '~> 5.11' )
14
- spec.add_development_dependency( 'minitest-junit' , '~> 1.0' )
15
- spec.add_development_dependency( 'rdoc' , '~> 6.4' )
16
- spec.add_development_dependency( 'simplecov', '~> 0.17' )
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' )
17
19
 
18
20
  spec.metadata = {
19
21
  "bug_tracker_uri" => "https://github.com/copiousfreetime/htauth/issues",
data/bin/htdigest-ruby CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  begin
4
- require 'htauth/cli'
4
+ require 'htauth/cli/digest'
5
5
  rescue LoadError
6
6
  path = File.expand_path(File.join(File.dirname(__FILE__),"..","lib"))
7
7
  raise if $:.include?(path)
data/bin/htpasswd-ruby CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  begin
4
- require 'htauth/cli'
4
+ require 'htauth/cli/passwd'
5
5
  rescue LoadError
6
6
  path = File.expand_path(File.join(File.dirname(__FILE__),"..","lib"))
7
7
  raise if $:.include?(path)
@@ -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) ; end
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)
@@ -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
@@ -31,5 +31,10 @@ module HTAuth
31
31
  def encode(password)
32
32
  ::BCrypt::Password.create(password, :cost => cost)
33
33
  end
34
+
35
+ def verify_password?(password, digest)
36
+ bc = ::BCrypt::Password.new(digest)
37
+ bc.is_password?(password)
38
+ end
34
39
  end
35
40
  end
@@ -1,7 +1,4 @@
1
- require 'htauth/version'
2
- require 'htauth/error'
3
- require 'htauth/digest_file'
4
- require 'htauth/console'
1
+ require 'htauth/cli'
5
2
 
6
3
  require 'ostruct'
7
4
  require 'optparse'
@@ -1,6 +1,4 @@
1
- require 'htauth/error'
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} [-cimBdpsD] [-C cost] passwordfile username
48
- #{op.program_name} -b[cmBdpsD] [-C cost] passwordfile username password
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("-b", "--batch", "Batch mode, get the password from the command line, rather than prompt") do |b|
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
@@ -1,8 +1,7 @@
1
1
  require 'htauth'
2
+ require 'htauth/console'
2
3
  module HTAuth
3
4
  module CLI
4
5
 
5
6
  end
6
7
  end
7
- require 'htauth/cli/digest'
8
- require 'htauth/cli/passwd'
@@ -1,5 +1,6 @@
1
1
  require 'htauth/error'
2
2
  require 'htauth/entry'
3
+ require 'htauth/algorithm'
3
4
  require 'digest/md5'
4
5
 
5
6
  module HTAuth
@@ -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
- # check the password and make sure it works, in the case that the algorithm is unknown it
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
- authed = false
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
- return "#{user}"
116
+ "#{user}"
128
117
  end
129
118
 
130
119
  # Internal: Returns the file line for this entry
@@ -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', 'myrealm')
15
- # pf.add_or_update('someuser', 'myrealm', 'a password')
16
- # pf.delete('someolduser', 'myotherrealm')
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
@@ -16,7 +16,7 @@ module HTAuth
16
16
  end
17
17
 
18
18
  # ignore the params
19
- def initialize(params = {})
19
+ def initialize(params = {})
20
20
  end
21
21
 
22
22
  def encode(password)
@@ -1,4 +1,4 @@
1
1
  module HTAuth
2
2
  # Public: The version of the htauth library
3
- VERSION = "2.2.0"
3
+ VERSION = "2.3.0"
4
4
  end
data/lib/htauth.rb CHANGED
@@ -30,7 +30,7 @@ end
30
30
 
31
31
  require 'htauth/version'
32
32
  require 'htauth/algorithm'
33
- require 'htauth/console'
33
+ require 'htauth/argon2'
34
34
  require 'htauth/bcrypt'
35
35
  require 'htauth/crypt'
36
36
  require 'htauth/digest_entry'
@@ -1,5 +1,4 @@
1
1
  require 'spec_helper'
2
- require 'htauth/algorithm'
3
2
 
4
3
  describe HTAuth::Algorithm do
5
4
  it "raises an error if it encouners an unknown algorithm" do
@@ -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
@@ -1,5 +1,4 @@
1
1
  require 'spec_helper'
2
- require 'htauth/bcrypt'
3
2
 
4
3
  describe HTAuth::Bcrypt do
5
4
  it "encrypts the same way that apache does by default" do
@@ -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
@@ -1,5 +1,4 @@
1
1
  require 'spec_helper'
2
- require 'htauth/crypt'
3
2
 
4
3
  describe HTAuth::Crypt do
5
4
  it "encrypts the same way that apache does" do
@@ -1,5 +1,4 @@
1
1
  require 'spec_helper'
2
- require 'htauth/digest_entry'
3
2
 
4
3
  describe HTAuth::DigestEntry do
5
4
  before(:each) do
@@ -1,5 +1,4 @@
1
1
  require 'spec_helper'
2
- require 'htauth/digest_file'
3
2
  require 'tempfile'
4
3
 
5
4
  describe HTAuth::DigestFile do
data/spec/md5_spec.rb CHANGED
@@ -1,6 +1,4 @@
1
- require File.join(File.dirname(__FILE__),"spec_helper.rb")
2
-
3
- require 'htauth/md5'
1
+ require 'spec_helper'
4
2
 
5
3
  describe HTAuth::Md5 do
6
4
  it "encrypts the same way that apache does" do
@@ -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 assinging an invalid algorithm" do
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
@@ -1,5 +1,4 @@
1
1
  require 'spec_helper'
2
- require 'htauth/sha1'
3
2
 
4
3
  describe HTAuth::Sha1 do
5
4
  it "encrypts the same way that apache does" do
data/spec/spec_helper.rb CHANGED
@@ -1,10 +1,6 @@
1
- if RUBY_VERSION >= '1.9.2' then
2
- require 'simplecov'
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 'rake/testtask'
40
- Rake::TestTask.new( :test ) do |t|
41
- t.ruby_opts = %w[ -w ]
42
- t.libs = %w[ lib spec test ]
43
- t.pattern = "{test,spec}/**/{test_*,*_spec}.rb"
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
- ( Digest::SHA256.file( local ) != Digest::SHA256.file( upstream ) )
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 << FileList["**/*.rbc"]
212
+ CLOBBER << "**/*.rbc"
205
213
 
206
214
  # The standard gem packaging task, everyone has it.
207
215
  require 'rubygems/package_task'
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.2.0
4
+ version: 2.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Hinegardner
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-02-06 00:00:00.000000000 Z
11
+ date: 2024-02-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bcrypt
@@ -24,79 +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.0'
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.0'
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.11'
75
+ version: '5.21'
48
76
  type: :development
49
77
  prerelease: false
50
78
  version_requirements: !ruby/object:Gem::Requirement
51
79
  requirements:
52
80
  - - "~>"
53
81
  - !ruby/object:Gem::Version
54
- version: '5.11'
82
+ version: '5.21'
55
83
  - !ruby/object:Gem::Dependency
56
84
  name: minitest-junit
57
85
  requirement: !ruby/object:Gem::Requirement
58
86
  requirements:
59
87
  - - "~>"
60
88
  - !ruby/object:Gem::Version
61
- version: '1.0'
89
+ version: '1.1'
62
90
  type: :development
63
91
  prerelease: false
64
92
  version_requirements: !ruby/object:Gem::Requirement
65
93
  requirements:
66
94
  - - "~>"
67
95
  - !ruby/object:Gem::Version
68
- version: '1.0'
96
+ version: '1.1'
69
97
  - !ruby/object:Gem::Dependency
70
98
  name: rdoc
71
99
  requirement: !ruby/object:Gem::Requirement
72
100
  requirements:
73
101
  - - "~>"
74
102
  - !ruby/object:Gem::Version
75
- version: '6.4'
103
+ version: '6.6'
76
104
  type: :development
77
105
  prerelease: false
78
106
  version_requirements: !ruby/object:Gem::Requirement
79
107
  requirements:
80
108
  - - "~>"
81
109
  - !ruby/object:Gem::Version
82
- version: '6.4'
110
+ version: '6.6'
83
111
  - !ruby/object:Gem::Dependency
84
112
  name: simplecov
85
113
  requirement: !ruby/object:Gem::Requirement
86
114
  requirements:
87
115
  - - "~>"
88
116
  - !ruby/object:Gem::Version
89
- version: '0.17'
117
+ version: '0.21'
90
118
  type: :development
91
119
  prerelease: false
92
120
  version_requirements: !ruby/object:Gem::Requirement
93
121
  requirements:
94
122
  - - "~>"
95
123
  - !ruby/object:Gem::Version
96
- version: '0.17'
97
- description: HTAuth is a pure ruby replacement for the Apache support programs htdigest
98
- and htpasswd. Command line and API access are provided for access to htdigest and
99
- 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.
100
127
  email: jeremy@copiousfreetime.org
101
128
  executables:
102
129
  - htdigest-ruby
@@ -118,6 +145,7 @@ files:
118
145
  - bin/htpasswd-ruby
119
146
  - lib/htauth.rb
120
147
  - lib/htauth/algorithm.rb
148
+ - lib/htauth/argon2.rb
121
149
  - lib/htauth/bcrypt.rb
122
150
  - lib/htauth/cli.rb
123
151
  - lib/htauth/cli/digest.rb
@@ -137,6 +165,7 @@ files:
137
165
  - lib/htauth/sha1.rb
138
166
  - lib/htauth/version.rb
139
167
  - spec/algorithm_spec.rb
168
+ - spec/argon2_spec.rb
140
169
  - spec/bcrypt_spec.rb
141
170
  - spec/cli/digest_spec.rb
142
171
  - spec/cli/passwd_spec.rb
@@ -186,14 +215,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
186
215
  - !ruby/object:Gem::Version
187
216
  version: '0'
188
217
  requirements: []
189
- rubygems_version: 3.4.6
218
+ rubygems_version: 3.5.3
190
219
  signing_key:
191
220
  specification_version: 4
192
- summary: HTAuth is a pure ruby replacement for the Apache support programs htdigest
193
- and htpasswd. Command line and API access are provided for access to htdigest and
194
- htpasswd files.
221
+ summary: HTAuth provides an API and commandline tools for managing Apache/httpd style
222
+ htpasswd and htdigest files.
195
223
  test_files:
196
224
  - spec/algorithm_spec.rb
225
+ - spec/argon2_spec.rb
197
226
  - spec/bcrypt_spec.rb
198
227
  - spec/cli/digest_spec.rb
199
228
  - spec/cli/passwd_spec.rb