htauth 1.0.1 → 1.0.2

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.
@@ -1,5 +1,9 @@
1
1
  = HTAuth
2
2
 
3
+ == Version 1.0.2 (2008-11-30)
4
+
5
+ * Change project layout
6
+
3
7
  == Version 1.0.1 (2008-02-06)
4
8
 
5
9
  === Changes
data/README CHANGED
@@ -2,17 +2,18 @@
2
2
 
3
3
  * Homepage[http://copiousfreetime.rubyforge.org/htauth]
4
4
  * {Rubyforge Project}[http://rubyforge.org/projects/copiousfreetime/]
5
- * email jeremy at hinegardner dot org
5
+ * Github[http://github.com/copiousfreetime/htauth/tree/master]
6
+ * email jeremy at copiousfreetime dot org
6
7
 
7
8
  == DESCRIPTION
8
9
 
9
- HTAuth is a pure ruby replacement for the Apache support programs +htdigest+ and
10
- +htpasswd+. Command line and API access are provided for access to htdigest and
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
11
12
  htpasswd files.
12
13
 
13
14
  == FEATURES
14
15
 
15
- Rpassword provides to drop in commands *htdigest-ruby* and *htpasswd-ruby* that
16
+ HTAuth provides to drop in commands *htdigest-ruby* and *htpasswd-ruby* that
16
17
  can manipulate the digest and passwd files in the same manner as Apache's
17
18
  original commands.
18
19
 
@@ -1,12 +1,19 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+ begin
4
+ require 'highline'
5
+ rescue LoadError
6
+ require 'rubygems'
7
+ require 'highline'
8
+ end
9
+
3
10
  begin
4
- require 'htauth'
11
+ require 'htauth'
5
12
  rescue LoadError
6
- path = File.expand_path(File.join(File.dirname(__FILE__),"..","lib"))
7
- raise if $:.include?(path)
8
- $: << path
9
- retry
13
+ path = File.expand_path(File.join(File.dirname(__FILE__),"..","lib"))
14
+ raise if $:.include?(path)
15
+ $: << path
16
+ retry
10
17
  end
11
18
 
12
- HTAuth::Digest.new.run(ARGV)
19
+ HTAuth::Digest.new.run(ARGV, ENV)
@@ -1,5 +1,12 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+ begin
4
+ require 'highline'
5
+ rescue LoadError
6
+ require 'rubygems'
7
+ require 'highline'
8
+ end
9
+
3
10
  begin
4
11
  require 'htauth'
5
12
  rescue LoadError
@@ -9,4 +16,4 @@ rescue LoadError
9
16
  retry
10
17
  end
11
18
 
12
- HTAuth::Passwd.new.run(ARGV)
19
+ HTAuth::Passwd.new.run(ARGV, ENV)
@@ -0,0 +1,43 @@
1
+ require 'rubygems'
2
+ require 'htauth/version'
3
+ require 'tasks/config'
4
+
5
+ HTAuth::GEM_SPEC = Gem::Specification.new do |spec|
6
+ proj = Configuration.for('project')
7
+ spec.name = proj.name
8
+ spec.version = HTAuth::VERSION
9
+
10
+ spec.author = proj.author
11
+ spec.email = proj.email
12
+ spec.homepage = proj.homepage
13
+ spec.summary = proj.summary
14
+ spec.description = proj.description
15
+ spec.platform = Gem::Platform::RUBY
16
+
17
+
18
+ pkg = Configuration.for('packaging')
19
+ spec.files = pkg.files.all
20
+ spec.executables = pkg.files.bin.collect { |b| File.basename(b) }
21
+
22
+ # add dependencies here
23
+ # spec.add_dependency("rake", ">= 0.8.1")
24
+ spec.add_dependency("highline", "~> 1.4.0")
25
+
26
+
27
+ if rdoc = Configuration.for_if_exist?('rdoc') then
28
+ spec.has_rdoc = true
29
+ spec.extra_rdoc_files = pkg.files.rdoc
30
+ spec.rdoc_options = rdoc.options + [ "--main" , rdoc.main_page ]
31
+ else
32
+ spec.has_rdoc = false
33
+ end
34
+
35
+ if test = Configuration.for_if_exist?('testing') then
36
+ spec.test_files = test.files
37
+ end
38
+
39
+ if rf = Configuration.for_if_exist?('rubyforge') then
40
+ spec.rubyforge_project = rf.project
41
+ end
42
+
43
+ end
@@ -1,14 +1,36 @@
1
+ #--
2
+ # Copyrigth (c) 2008 Jeremy Hinegardner
3
+ # All rights reserved. See LICENSE and/or COPYING for details
4
+ #++
5
+
1
6
  module HTAuth
2
-
3
- ROOT_DIR = ::File.expand_path(::File.join(::File.dirname(__FILE__),".."))
4
- LIB_DIR = ::File.join(ROOT_DIR,"lib").freeze
5
7
 
6
- class FileAccessError < StandardError ; end
7
- class TempFileError < StandardError ; end
8
- class PasswordError < StandardError ; end
8
+ # The root directory of the project is considered to be the parent directory
9
+ # of the 'lib' directory.
10
+ #
11
+ def self.root_dir
12
+ unless @root_dir
13
+ path_parts = ::File.expand_path( __FILE__ ).split( ::File::SEPARATOR )
14
+ lib_index = path_parts.rindex( 'lib' )
15
+ @root_dir = path_parts[ 0...lib_index].join( ::File::SEPARATOR ) + ::File::SEPARATOR
16
+ end
17
+ return @root_dir
18
+ end
19
+
20
+ def self.lib_path( *args )
21
+ self.sub_path( "lib", *args )
22
+ end
23
+
24
+ def self.sub_path( sub, *args )
25
+ sp = ::File.join( root_dir, sub ) + ::File::SEPARATOR
26
+ sp = ::File.join( sp, *args ) if args
27
+ end
28
+
29
+ class FileAccessError < StandardError ; end
30
+ class TempFileError < StandardError ; end
31
+ class PasswordError < StandardError ; end
9
32
  end
10
33
 
11
34
  require 'htauth/version'
12
- require 'htauth/gemspec'
13
35
  require 'htauth/passwd'
14
36
  require 'htauth/digest'
@@ -1,67 +1,68 @@
1
1
  require 'htauth'
2
2
 
3
3
  module HTAuth
4
- class InvalidAlgorithmError < StandardError ; end
5
- # base class all the Passwd algorithms derive from
6
- class Algorithm
4
+ class InvalidAlgorithmError < StandardError ; end
5
+ # base class all the Passwd algorithms derive from
6
+ class Algorithm
7
7
 
8
- SALT_CHARS = (%w[ . / ] + ("0".."9").to_a + ('A'..'Z').to_a + ('a'..'z').to_a).freeze
9
- DEFAULT = ( RUBY_PLATFORM !~ /mswin32/ ) ? "crypt" : "md5"
10
- EXISTING = "existing"
8
+ SALT_CHARS = (%w[ . / ] + ("0".."9").to_a + ('A'..'Z').to_a + ('a'..'z').to_a).freeze
9
+ DEFAULT = ( RUBY_PLATFORM !~ /mswin32/ ) ? "crypt" : "md5"
10
+ EXISTING = "existing"
11
11
 
12
- class << self
13
- def algorithm_from_name(a_name, params = {})
14
- raise InvalidAlgorithmError, "`#{a_name}' is an invalid encryption algorithm, use one of #{sub_klasses.keys.join(', ')}" unless sub_klasses[a_name.downcase]
15
- sub_klasses[a_name.downcase].new(params)
16
- end
12
+ class << self
13
+ def algorithm_from_name(a_name, params = {})
14
+ raise InvalidAlgorithmError, "`#{a_name}' is an invalid encryption algorithm, use one of #{sub_klasses.keys.join(', ')}" unless sub_klasses[a_name.downcase]
15
+ sub_klasses[a_name.downcase].new(params)
16
+ end
17
17
 
18
- def algorithms_from_field(password_field)
19
- matches = []
18
+ def algorithms_from_field(password_field)
19
+ matches = []
20
20
 
21
- if password_field.index(sub_klasses['sha1'].new.prefix) then
22
- matches << sub_klasses['sha1'].new
23
- elsif password_field.index(sub_klasses['md5'].new.prefix) then
24
- p = password_field.split("$")
25
- matches << sub_klasses['md5'].new( :salt => p[2] )
26
- else
27
- matches << sub_klasses['plaintext'].new
28
- matches << sub_klasses['crypt'].new( :salt => password_field[0,2] )
29
- end
21
+ if password_field.index(sub_klasses['sha1'].new.prefix) then
22
+ matches << sub_klasses['sha1'].new
23
+ elsif password_field.index(sub_klasses['md5'].new.prefix) then
24
+ p = password_field.split("$")
25
+ matches << sub_klasses['md5'].new( :salt => p[2] )
26
+ else
27
+ matches << sub_klasses['plaintext'].new
28
+ matches << sub_klasses['crypt'].new( :salt => password_field[0,2] )
29
+ end
30
30
 
31
- return matches
32
- end
31
+ return matches
32
+ end
33
33
 
34
- def inherited(sub_klass)
35
- k = sub_klass.name.split("::").last.downcase
36
- sub_klasses[k] = sub_klass
37
- end
34
+ def inherited(sub_klass)
35
+ k = sub_klass.name.split("::").last.downcase
36
+ sub_klasses[k] = sub_klass
37
+ end
38
38
 
39
- def sub_klasses
40
- @sub_klasses ||= {}
41
- end
42
- end
39
+ def sub_klasses
40
+ @sub_klasses ||= {}
41
+ end
42
+ end
43
43
 
44
- def prefix ; end
45
- def encode(password) ; end
46
-
47
- # 8 bytes of random items from SALT_CHARS
48
- def gen_salt
49
- chars = []
50
- 8.times { chars << SALT_CHARS[rand(SALT_CHARS.size)] }
51
- chars.join('')
52
- end
44
+ def prefix ; end
45
+ def encode(password) ; end
53
46
 
54
- # this is not the Base64 encoding, this is the to64() method from apr
55
- def to_64(number, rounds)
56
- r = StringIO.new
57
- rounds.times do |x|
58
- r.print(SALT_CHARS[number % 64])
59
- number >>= 6
60
- end
61
- return r.string
62
- end
47
+ # 8 bytes of random items from SALT_CHARS
48
+ def gen_salt
49
+ chars = []
50
+ 8.times { chars << SALT_CHARS[rand(SALT_CHARS.size)] }
51
+ chars.join('')
52
+ end
53
+
54
+ # this is not the Base64 encoding, this is the to64() method from apr
55
+ def to_64(number, rounds)
56
+ r = StringIO.new
57
+ rounds.times do |x|
58
+ r.print(SALT_CHARS[number % 64])
59
+ number >>= 6
60
+ end
61
+ return r.string
63
62
  end
63
+ end
64
64
  end
65
+
65
66
  require 'htauth/md5'
66
67
  require 'htauth/sha1'
67
68
  require 'htauth/crypt'
@@ -2,19 +2,19 @@ require 'htauth/algorithm'
2
2
 
3
3
  module HTAuth
4
4
 
5
- # The basic crypt algorithm
6
- class Crypt < Algorithm
7
-
8
- def initialize(params = {})
9
- @salt = params[:salt] || params['salt'] || gen_salt
10
- end
5
+ # The basic crypt algorithm
6
+ class Crypt < Algorithm
11
7
 
12
- def prefix
8
+ def initialize(params = {})
9
+ @salt = params[:salt] || params['salt'] || gen_salt
10
+ end
11
+
12
+ def prefix
13
13
  ""
14
- end
14
+ end
15
15
 
16
- def encode(password)
17
- password.crypt(@salt)
18
- end
16
+ def encode(password)
17
+ password.crypt(@salt)
19
18
  end
19
+ end
20
20
  end
@@ -4,127 +4,126 @@ require 'htauth/digest_file'
4
4
  require 'ostruct'
5
5
  require 'optparse'
6
6
 
7
- require 'rubygems'
8
7
  require 'highline'
9
8
 
10
9
  module HTAuth
11
- class Digest
10
+ class Digest
12
11
 
13
- MAX_PASSWD_LENGTH = 255
12
+ MAX_PASSWD_LENGTH = 255
14
13
 
15
- attr_accessor :digest_file
14
+ attr_accessor :digest_file
16
15
 
17
- def initialize
18
- @digest_file = nil
19
- end
16
+ def initialize
17
+ @digest_file = nil
18
+ end
20
19
 
21
- def options
22
- if @options.nil? then
23
- @options = ::OpenStruct.new
24
- @options.show_version = false
25
- @options.show_help = false
26
- @options.file_mode = DigestFile::ALTER
27
- @options.passwdfile = nil
28
- @options.realm = nil
29
- @options.username = nil
30
- @options.delete_entry = false
31
- end
32
- @options
33
- end
20
+ def options
21
+ if @options.nil? then
22
+ @options = ::OpenStruct.new
23
+ @options.show_version = false
24
+ @options.show_help = false
25
+ @options.file_mode = DigestFile::ALTER
26
+ @options.passwdfile = nil
27
+ @options.realm = nil
28
+ @options.username = nil
29
+ @options.delete_entry = false
30
+ end
31
+ @options
32
+ end
34
33
 
35
- def option_parser
36
- if not @option_parser then
37
- @option_parser = OptionParser.new do |op|
38
- op.banner = "Usage: #{op.program_name} [options] passwordfile realm username"
39
- op.on("-c", "--create", "Create a new digest password file; this overwrites an existing file.") do |c|
40
- options.file_mode = DigestFile::CREATE
41
- end
42
-
43
- op.on("-D", "--delete", "Delete the specified user.") do |d|
44
- options.delete_entry = d
45
- end
46
-
47
- op.on("-h", "--help", "Display this help.") do |h|
48
- options.show_help = h
49
- end
50
-
51
- op.on("-v", "--version", "Show version info.") do |v|
52
- options.show_version = v
53
- end
54
- end
55
- end
56
- @option_parser
34
+ def option_parser
35
+ if not @option_parser then
36
+ @option_parser = OptionParser.new do |op|
37
+ op.banner = "Usage: #{op.program_name} [options] passwordfile realm username"
38
+ op.on("-c", "--create", "Create a new digest password file; this overwrites an existing file.") do |c|
39
+ options.file_mode = DigestFile::CREATE
40
+ end
41
+
42
+ op.on("-D", "--delete", "Delete the specified user.") do |d|
43
+ options.delete_entry = d
44
+ end
45
+
46
+ op.on("-h", "--help", "Display this help.") do |h|
47
+ options.show_help = h
48
+ end
49
+
50
+ op.on("-v", "--version", "Show version info.") do |v|
51
+ options.show_version = v
52
+ end
57
53
  end
54
+ end
55
+ @option_parser
56
+ end
58
57
 
59
- def show_help
60
- $stdout.puts option_parser
61
- exit 1
62
- end
58
+ def show_help
59
+ $stdout.puts option_parser
60
+ exit 1
61
+ end
63
62
 
64
- def show_version
65
- $stdout.puts "#{option_parser.program_name}: version #{HTAuth::VERSION}"
66
- exit 1
67
- end
63
+ def show_version
64
+ $stdout.puts "#{option_parser.program_name}: version #{HTAuth::VERSION}"
65
+ exit 1
66
+ end
68
67
 
69
- def parse_options(argv)
70
- begin
71
- option_parser.parse!(argv)
72
- show_version if options.show_version
73
- show_help if options.show_help or argv.size < 3
74
-
75
- options.passwdfile = argv.shift
76
- options.realm = argv.shift
77
- options.username = argv.shift
78
- rescue ::OptionParser::ParseError => pe
79
- $stderr.puts "ERROR: #{option_parser.program_name} - #{pe}"
80
- $stderr.puts "Try `#{option_parser.program_name} --help` for more information"
81
- exit 1
82
- end
83
- end
68
+ def parse_options(argv)
69
+ begin
70
+ option_parser.parse!(argv)
71
+ show_version if options.show_version
72
+ show_help if options.show_help or argv.size < 3
73
+
74
+ options.passwdfile = argv.shift
75
+ options.realm = argv.shift
76
+ options.username = argv.shift
77
+ rescue ::OptionParser::ParseError => pe
78
+ $stderr.puts "ERROR: #{option_parser.program_name} - #{pe}"
79
+ $stderr.puts "Try `#{option_parser.program_name} --help` for more information"
80
+ exit 1
81
+ end
82
+ end
83
+
84
+ def run(argv, env = ENV)
85
+ begin
86
+ parse_options(argv)
87
+ digest_file = DigestFile.new(options.passwdfile, options.file_mode)
88
+
89
+ if options.delete_entry then
90
+ digest_file.delete(options.username, options.realm)
91
+ else
92
+ # initialize here so that if $stdin is overwritten it gets picked up
93
+ hl = ::HighLine.new
94
+
95
+ action = digest_file.has_entry?(options.username, options.realm) ? "Changing" : "Adding"
84
96
 
85
- def run(argv)
86
- begin
87
- parse_options(argv)
88
- digest_file = DigestFile.new(options.passwdfile, options.file_mode)
89
-
90
- if options.delete_entry then
91
- digest_file.delete(options.username, options.realm)
92
- else
93
- # initialize here so that if $stdin is overwritten it gest picked up
94
- hl = ::HighLine.new
95
-
96
- action = digest_file.has_entry?(options.username, options.realm) ? "Changing" : "Adding"
97
-
98
- $stdout.puts "#{action} password for #{options.username} in realm #{options.realm}."
99
-
100
- pw_in = hl.ask(" New password: ") { |q| q.echo = '*' }
101
- raise PasswordError, "password '#{pw_in}' too long" if pw_in.length >= MAX_PASSWD_LENGTH
102
-
103
- pw_validate = hl.ask("Re-type new password: ") { |q| q.echo = '*' }
104
- raise PasswordError, "They don't match, sorry." unless pw_in == pw_validate
105
-
106
- digest_file.add_or_update(options.username, options.realm, pw_in)
107
- end
108
-
109
- digest_file.save!
110
-
111
- rescue HTAuth::FileAccessError => fae
112
- msg = "Could not open password file #{options.passwdfile} "
113
- $stderr.puts "#{msg}: #{fae.message}"
114
- $stderr.puts fae.backtrace.join("\n")
115
- exit 1
116
- rescue HTAuth::PasswordError => pe
117
- $stderr.puts "#{pe.message}"
118
- exit 1
119
- rescue HTAuth::DigestFileError => fe
120
- $stderr.puts "#{fe.message}"
121
- exit 1
122
- rescue SignalException => se
123
- $stderr.puts
124
- $stderr.puts "Interrupted"
125
- exit 1
126
- end
127
- exit 0
97
+ $stdout.puts "#{action} password for #{options.username} in realm #{options.realm}."
98
+
99
+ pw_in = hl.ask(" New password: ") { |q| q.echo = '*' }
100
+ raise PasswordError, "password '#{pw_in}' too long" if pw_in.length >= MAX_PASSWD_LENGTH
101
+
102
+ pw_validate = hl.ask("Re-type new password: ") { |q| q.echo = '*' }
103
+ raise PasswordError, "They don't match, sorry." unless pw_in == pw_validate
104
+
105
+ digest_file.add_or_update(options.username, options.realm, pw_in)
128
106
  end
107
+
108
+ digest_file.save!
109
+
110
+ rescue HTAuth::FileAccessError => fae
111
+ msg = "Could not open password file #{options.passwdfile} "
112
+ $stderr.puts "#{msg}: #{fae.message}"
113
+ $stderr.puts fae.backtrace.join("\n")
114
+ exit 1
115
+ rescue HTAuth::PasswordError => pe
116
+ $stderr.puts "#{pe.message}"
117
+ exit 1
118
+ rescue HTAuth::DigestFileError => fe
119
+ $stderr.puts "#{fe.message}"
120
+ exit 1
121
+ rescue SignalException => se
122
+ $stderr.puts
123
+ $stderr.puts "Interrupted"
124
+ exit 1
125
+ end
126
+ exit 0
129
127
  end
128
+ end
130
129
  end