Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

conversion to different project layout

  • Loading branch information...
commit e0e59bf11e9dd19afcd38d276de15a26673259c5 1 parent 372f6a5
@copiousfreetime authored
View
5 .gitignore
@@ -0,0 +1,5 @@
+*~
+pkg/
+coverage/
+*.swp
+*.swo
View
4 CHANGES → HISTORY
@@ -1,5 +1,9 @@
= HTAuth
+== Version 1.0.2 (2008-11-30)
+
+* Change project layout
+
== Version 1.0.1 (2008-02-06)
=== Changes
View
3  README
@@ -2,7 +2,8 @@
* Homepage[http://copiousfreetime.rubyforge.org/htauth]
* {Rubyforge Project}[http://rubyforge.org/projects/copiousfreetime/]
-* email jeremy at hinegardner dot org
+* Github[http://github.com/copiousfreetime/htauth/tree/master]
+* email jeremy at copiousfreetime dot org
== DESCRIPTION
View
61 Rakefile
@@ -1,14 +1,61 @@
-# make sure our project's ./lib directory is added to the ruby search path
-$: << File.join(File.dirname(__FILE__),"lib")
+#--
+# Copyright (c) 2008 Jeremy Hinegardner
+# All rights reserved. See LICENSE and/or COPYING for details.
+#++
+#-------------------------------------------------------------------------------
+# make sure our project's top level directory and the lib directory are added to
+# the ruby search path.
+#-------------------------------------------------------------------------------
+$: << File.expand_path(File.join(File.dirname(__FILE__),"lib"))
+$: << File.expand_path(File.dirname(__FILE__))
+
+#-------------------------------------------------------------------------------
+# load the global project configuration and add in the top level clean and
+# clobber tasks so that other tasks can utilize those constants if necessary
+# This loads up the defaults for the whole project configuration
+#-------------------------------------------------------------------------------
require 'rubygems'
-require 'rake/gempackagetask'
+require 'tasks/config.rb'
+require 'gemspec'
require 'rake/clean'
+require 'rake/gempackagetask'
require 'rake/rdoctask'
-require 'rake/contrib/sshpublisher'
-
+require 'spec/rake/spectask'
+#-------------------------------------------------------------------------------
+# Main configuration for the project, these overwrite the items that are in
+# tasks/config.rb
+#-------------------------------------------------------------------------------
require 'htauth'
+Configuration.for("project") {
+ name "htauth"
+ version HTAuth::VERSION
+ author "Jeremy Hinegardner"
+ email "jeremy@copiousfreetime.org"
+ homepage "http://copiousfreetime.rubyforge.org/htauth/"
+}
-load 'tasks/setup.rb'
-
+#-------------------------------------------------------------------------------
+# load up all the project tasks and setup the default task to be the
+# test:default task.
+#-------------------------------------------------------------------------------
+Configuration.for("packaging").files.tasks.each do |tasklib|
+ import tasklib
+end
+task :default => 'test:default'
+#-------------------------------------------------------------------------------
+# Finalize the loading of all pending imports and update the top level clobber
+# task to depend on all possible sub-level tasks that have a name like
+# ':clobber' in other namespaces. This allows us to say:
+#
+# rake clobber
+#
+# and it will get everything.
+#-------------------------------------------------------------------------------
+Rake.application.load_imports
+Rake.application.tasks.each do |t|
+ if t.name =~ /:clobber/ then
+ task :clobber => [t.name]
+ end
+end
View
19 bin/htdigest-ruby 100644 → 100755
@@ -1,12 +1,19 @@
#!/usr/bin/env ruby
+begin
+ require 'highline'
+rescue LoadError
+ require 'rubygems'
+ require 'highline'
+end
+
begin
- require 'htauth'
+ require 'htauth'
rescue LoadError
- path = File.expand_path(File.join(File.dirname(__FILE__),"..","lib"))
- raise if $:.include?(path)
- $: << path
- retry
+ path = File.expand_path(File.join(File.dirname(__FILE__),"..","lib"))
+ raise if $:.include?(path)
+ $: << path
+ retry
end
-HTAuth::Digest.new.run(ARGV)
+HTAuth::Digest.new.run(ARGV, ENV)
View
9 bin/htpasswd-ruby 100644 → 100755
@@ -1,6 +1,13 @@
#!/usr/bin/env ruby
begin
+ require 'highline'
+rescue LoadError
+ require 'rubygems'
+ require 'highline'
+end
+
+begin
require 'htauth'
rescue LoadError
path = File.expand_path(File.join(File.dirname(__FILE__),"..","lib"))
@@ -9,4 +16,4 @@ rescue LoadError
retry
end
-HTAuth::Passwd.new.run(ARGV)
+HTAuth::Passwd.new.run(ARGV, ENV)
View
43 gemspec.rb
@@ -0,0 +1,43 @@
+require 'rubygems'
+require 'htauth/version'
+require 'tasks/config'
+
+HTAuth::GEM_SPEC = Gem::Specification.new do |spec|
+ proj = Configuration.for('project')
+ spec.name = proj.name
+ spec.version = HTAuth::VERSION
+
+ spec.author = proj.author
+ spec.email = proj.email
+ spec.homepage = proj.homepage
+ spec.summary = proj.summary
+ spec.description = proj.description
+ spec.platform = Gem::Platform::RUBY
+
+
+ pkg = Configuration.for('packaging')
+ spec.files = pkg.files.all
+ spec.executables = pkg.files.bin.collect { |b| File.basename(b) }
+
+ # add dependencies here
+ # spec.add_dependency("rake", ">= 0.8.1")
+ spec.add_dependency("highline", "~> 1.4.0")
+
+
+ if rdoc = Configuration.for_if_exist?('rdoc') then
+ spec.has_rdoc = true
+ spec.extra_rdoc_files = pkg.files.rdoc
+ spec.rdoc_options = rdoc.options + [ "--main" , rdoc.main_page ]
+ else
+ spec.has_rdoc = false
+ end
+
+ if test = Configuration.for_if_exist?('testing') then
+ spec.test_files = test.files
+ end
+
+ if rf = Configuration.for_if_exist?('rubyforge') then
+ spec.rubyforge_project = rf.project
+ end
+
+end
View
36 lib/htauth.rb
@@ -1,14 +1,36 @@
+#--
+# Copyrigth (c) 2008 Jeremy Hinegardner
+# All rights reserved. See LICENSE and/or COPYING for details
+#++
+
module HTAuth
-
- ROOT_DIR = ::File.expand_path(::File.join(::File.dirname(__FILE__),".."))
- LIB_DIR = ::File.join(ROOT_DIR,"lib").freeze
- class FileAccessError < StandardError ; end
- class TempFileError < StandardError ; end
- class PasswordError < StandardError ; end
+ # The root directory of the project is considered to be the parent directory
+ # of the 'lib' directory.
+ #
+ def self.root_dir
+ unless @root_dir
+ path_parts = ::File.expand_path( __FILE__ ).split( ::File::SEPARATOR )
+ lib_index = path_parts.rindex( 'lib' )
+ @root_dir = path_parts[ 0...lib_index].join( ::File::SEPARATOR ) + ::File::SEPARATOR
+ end
+ return @root_dir
+ end
+
+ def self.lib_path( *args )
+ self.sub_path( "lib", *args )
+ end
+
+ def self.sub_path( sub, *args )
+ sp = ::File.join( root_dir, sub ) + ::File::SEPARATOR
+ sp = ::File.join( sp, *args ) if args
+ end
+
+ class FileAccessError < StandardError ; end
+ class TempFileError < StandardError ; end
+ class PasswordError < StandardError ; end
end
require 'htauth/version'
-require 'htauth/gemspec'
require 'htauth/passwd'
require 'htauth/digest'
View
101 lib/htauth/algorithm.rb
@@ -1,67 +1,68 @@
require 'htauth'
module HTAuth
- class InvalidAlgorithmError < StandardError ; end
- # base class all the Passwd algorithms derive from
- class Algorithm
+ class InvalidAlgorithmError < StandardError ; end
+ # base class all the Passwd algorithms derive from
+ class Algorithm
- SALT_CHARS = (%w[ . / ] + ("0".."9").to_a + ('A'..'Z').to_a + ('a'..'z').to_a).freeze
- DEFAULT = ( RUBY_PLATFORM !~ /mswin32/ ) ? "crypt" : "md5"
- EXISTING = "existing"
+ SALT_CHARS = (%w[ . / ] + ("0".."9").to_a + ('A'..'Z').to_a + ('a'..'z').to_a).freeze
+ DEFAULT = ( RUBY_PLATFORM !~ /mswin32/ ) ? "crypt" : "md5"
+ EXISTING = "existing"
- class << self
- def algorithm_from_name(a_name, params = {})
- raise InvalidAlgorithmError, "`#{a_name}' is an invalid encryption algorithm, use one of #{sub_klasses.keys.join(', ')}" unless sub_klasses[a_name.downcase]
- sub_klasses[a_name.downcase].new(params)
- end
+ class << self
+ def algorithm_from_name(a_name, params = {})
+ raise InvalidAlgorithmError, "`#{a_name}' is an invalid encryption algorithm, use one of #{sub_klasses.keys.join(', ')}" unless sub_klasses[a_name.downcase]
+ sub_klasses[a_name.downcase].new(params)
+ end
- def algorithms_from_field(password_field)
- matches = []
+ def algorithms_from_field(password_field)
+ matches = []
- if password_field.index(sub_klasses['sha1'].new.prefix) then
- matches << sub_klasses['sha1'].new
- elsif password_field.index(sub_klasses['md5'].new.prefix) then
- p = password_field.split("$")
- matches << sub_klasses['md5'].new( :salt => p[2] )
- else
- matches << sub_klasses['plaintext'].new
- matches << sub_klasses['crypt'].new( :salt => password_field[0,2] )
- end
+ if password_field.index(sub_klasses['sha1'].new.prefix) then
+ matches << sub_klasses['sha1'].new
+ elsif password_field.index(sub_klasses['md5'].new.prefix) then
+ p = password_field.split("$")
+ matches << sub_klasses['md5'].new( :salt => p[2] )
+ else
+ matches << sub_klasses['plaintext'].new
+ matches << sub_klasses['crypt'].new( :salt => password_field[0,2] )
+ end
- return matches
- end
+ return matches
+ end
- def inherited(sub_klass)
- k = sub_klass.name.split("::").last.downcase
- sub_klasses[k] = sub_klass
- end
+ def inherited(sub_klass)
+ k = sub_klass.name.split("::").last.downcase
+ sub_klasses[k] = sub_klass
+ end
- def sub_klasses
- @sub_klasses ||= {}
- end
- end
+ def sub_klasses
+ @sub_klasses ||= {}
+ end
+ end
- def prefix ; end
- def encode(password) ; end
-
- # 8 bytes of random items from SALT_CHARS
- def gen_salt
- chars = []
- 8.times { chars << SALT_CHARS[rand(SALT_CHARS.size)] }
- chars.join('')
- end
+ def prefix ; end
+ def encode(password) ; end
- # this is not the Base64 encoding, this is the to64() method from apr
- def to_64(number, rounds)
- r = StringIO.new
- rounds.times do |x|
- r.print(SALT_CHARS[number % 64])
- number >>= 6
- end
- return r.string
- end
+ # 8 bytes of random items from SALT_CHARS
+ def gen_salt
+ chars = []
+ 8.times { chars << SALT_CHARS[rand(SALT_CHARS.size)] }
+ chars.join('')
+ end
+
+ # this is not the Base64 encoding, this is the to64() method from apr
+ def to_64(number, rounds)
+ r = StringIO.new
+ rounds.times do |x|
+ r.print(SALT_CHARS[number % 64])
+ number >>= 6
+ end
+ return r.string
end
+ end
end
+
require 'htauth/md5'
require 'htauth/sha1'
require 'htauth/crypt'
View
22 lib/htauth/crypt.rb
@@ -2,19 +2,19 @@
module HTAuth
- # The basic crypt algorithm
- class Crypt < Algorithm
-
- def initialize(params = {})
- @salt = params[:salt] || params['salt'] || gen_salt
- end
+ # The basic crypt algorithm
+ class Crypt < Algorithm
- def prefix
+ def initialize(params = {})
+ @salt = params[:salt] || params['salt'] || gen_salt
+ end
+
+ def prefix
""
- end
+ end
- def encode(password)
- password.crypt(@salt)
- end
+ def encode(password)
+ password.crypt(@salt)
end
+ end
end
View
215 lib/htauth/digest.rb
@@ -4,127 +4,126 @@
require 'ostruct'
require 'optparse'
-require 'rubygems'
require 'highline'
module HTAuth
- class Digest
+ class Digest
- MAX_PASSWD_LENGTH = 255
+ MAX_PASSWD_LENGTH = 255
- attr_accessor :digest_file
+ attr_accessor :digest_file
- def initialize
- @digest_file = nil
- end
+ def initialize
+ @digest_file = nil
+ end
- def options
- if @options.nil? then
- @options = ::OpenStruct.new
- @options.show_version = false
- @options.show_help = false
- @options.file_mode = DigestFile::ALTER
- @options.passwdfile = nil
- @options.realm = nil
- @options.username = nil
- @options.delete_entry = false
- end
- @options
- end
+ def options
+ if @options.nil? then
+ @options = ::OpenStruct.new
+ @options.show_version = false
+ @options.show_help = false
+ @options.file_mode = DigestFile::ALTER
+ @options.passwdfile = nil
+ @options.realm = nil
+ @options.username = nil
+ @options.delete_entry = false
+ end
+ @options
+ end
- def option_parser
- if not @option_parser then
- @option_parser = OptionParser.new do |op|
- op.banner = "Usage: #{op.program_name} [options] passwordfile realm username"
- op.on("-c", "--create", "Create a new digest password file; this overwrites an existing file.") do |c|
- options.file_mode = DigestFile::CREATE
- end
-
- op.on("-D", "--delete", "Delete the specified user.") do |d|
- options.delete_entry = d
- end
-
- op.on("-h", "--help", "Display this help.") do |h|
- options.show_help = h
- end
-
- op.on("-v", "--version", "Show version info.") do |v|
- options.show_version = v
- end
- end
- end
- @option_parser
+ def option_parser
+ if not @option_parser then
+ @option_parser = OptionParser.new do |op|
+ op.banner = "Usage: #{op.program_name} [options] passwordfile realm username"
+ op.on("-c", "--create", "Create a new digest password file; this overwrites an existing file.") do |c|
+ options.file_mode = DigestFile::CREATE
+ end
+
+ op.on("-D", "--delete", "Delete the specified user.") do |d|
+ options.delete_entry = d
+ end
+
+ op.on("-h", "--help", "Display this help.") do |h|
+ options.show_help = h
+ end
+
+ op.on("-v", "--version", "Show version info.") do |v|
+ options.show_version = v
+ end
end
+ end
+ @option_parser
+ end
- def show_help
- $stdout.puts option_parser
- exit 1
- end
+ def show_help
+ $stdout.puts option_parser
+ exit 1
+ end
- def show_version
- $stdout.puts "#{option_parser.program_name}: version #{HTAuth::VERSION}"
- exit 1
- end
+ def show_version
+ $stdout.puts "#{option_parser.program_name}: version #{HTAuth::VERSION}"
+ exit 1
+ end
- def parse_options(argv)
- begin
- option_parser.parse!(argv)
- show_version if options.show_version
- show_help if options.show_help or argv.size < 3
-
- options.passwdfile = argv.shift
- options.realm = argv.shift
- options.username = argv.shift
- rescue ::OptionParser::ParseError => pe
- $stderr.puts "ERROR: #{option_parser.program_name} - #{pe}"
- $stderr.puts "Try `#{option_parser.program_name} --help` for more information"
- exit 1
- end
- end
+ def parse_options(argv)
+ begin
+ option_parser.parse!(argv)
+ show_version if options.show_version
+ show_help if options.show_help or argv.size < 3
+
+ options.passwdfile = argv.shift
+ options.realm = argv.shift
+ options.username = argv.shift
+ rescue ::OptionParser::ParseError => pe
+ $stderr.puts "ERROR: #{option_parser.program_name} - #{pe}"
+ $stderr.puts "Try `#{option_parser.program_name} --help` for more information"
+ exit 1
+ end
+ end
+
+ def run(argv, env)
+ begin
+ parse_options(argv)
+ digest_file = DigestFile.new(options.passwdfile, options.file_mode)
+
+ if options.delete_entry then
+ digest_file.delete(options.username, options.realm)
+ else
+ # initialize here so that if $stdin is overwritten it gets picked up
+ hl = ::HighLine.new
+
+ action = digest_file.has_entry?(options.username, options.realm) ? "Changing" : "Adding"
- def run(argv)
- begin
- parse_options(argv)
- digest_file = DigestFile.new(options.passwdfile, options.file_mode)
-
- if options.delete_entry then
- digest_file.delete(options.username, options.realm)
- else
- # initialize here so that if $stdin is overwritten it gest picked up
- hl = ::HighLine.new
-
- action = digest_file.has_entry?(options.username, options.realm) ? "Changing" : "Adding"
-
- $stdout.puts "#{action} password for #{options.username} in realm #{options.realm}."
-
- pw_in = hl.ask(" New password: ") { |q| q.echo = '*' }
- raise PasswordError, "password '#{pw_in}' too long" if pw_in.length >= MAX_PASSWD_LENGTH
-
- pw_validate = hl.ask("Re-type new password: ") { |q| q.echo = '*' }
- raise PasswordError, "They don't match, sorry." unless pw_in == pw_validate
-
- digest_file.add_or_update(options.username, options.realm, pw_in)
- end
-
- digest_file.save!
-
- rescue HTAuth::FileAccessError => fae
- msg = "Could not open password file #{options.passwdfile} "
- $stderr.puts "#{msg}: #{fae.message}"
- $stderr.puts fae.backtrace.join("\n")
- exit 1
- rescue HTAuth::PasswordError => pe
- $stderr.puts "#{pe.message}"
- exit 1
- rescue HTAuth::DigestFileError => fe
- $stderr.puts "#{fe.message}"
- exit 1
- rescue SignalException => se
- $stderr.puts
- $stderr.puts "Interrupted"
- exit 1
- end
- exit 0
+ $stdout.puts "#{action} password for #{options.username} in realm #{options.realm}."
+
+ pw_in = hl.ask(" New password: ") { |q| q.echo = '*' }
+ raise PasswordError, "password '#{pw_in}' too long" if pw_in.length >= MAX_PASSWD_LENGTH
+
+ pw_validate = hl.ask("Re-type new password: ") { |q| q.echo = '*' }
+ raise PasswordError, "They don't match, sorry." unless pw_in == pw_validate
+
+ digest_file.add_or_update(options.username, options.realm, pw_in)
end
+
+ digest_file.save!
+
+ rescue HTAuth::FileAccessError => fae
+ msg = "Could not open password file #{options.passwdfile} "
+ $stderr.puts "#{msg}: #{fae.message}"
+ $stderr.puts fae.backtrace.join("\n")
+ exit 1
+ rescue HTAuth::PasswordError => pe
+ $stderr.puts "#{pe.message}"
+ exit 1
+ rescue HTAuth::DigestFileError => fe
+ $stderr.puts "#{fe.message}"
+ exit 1
+ rescue SignalException => se
+ $stderr.puts
+ $stderr.puts "Interrupted"
+ exit 1
+ end
+ exit 0
end
+ end
end
View
106 lib/htauth/digest_entry.rb
@@ -2,72 +2,72 @@
require 'digest/md5'
module HTAuth
- class InvalidDigestEntry < StandardError ; end
+ class InvalidDigestEntry < StandardError ; end
- # A single record in an htdigest file.
- class DigestEntry
+ # A single record in an htdigest file.
+ class DigestEntry
- attr_accessor :user
- attr_accessor :realm
- attr_accessor :digest
+ attr_accessor :user
+ attr_accessor :realm
+ attr_accessor :digest
- class << self
- def from_line(line)
- parts = is_entry!(line)
- d = DigestEntry.new(parts[0], parts[1])
- d.digest = parts[2]
- return d
- end
+ class << self
+ def from_line(line)
+ parts = is_entry!(line)
+ d = DigestEntry.new(parts[0], parts[1])
+ d.digest = parts[2]
+ return d
+ end
- # test if a line is an entry, raise InvalidDigestEntry if it is not.
- # an entry must be composed of 3 parts, username:realm:md5sum
- # where username, and realm do not contain the ':' character
- # and the md5sum must be 32 characters long.
- def is_entry!(line)
- raise InvalidDigestEntry, "line commented out" if line =~ /\A#/
- parts = line.strip.split(":")
- raise InvalidDigestEntry, "line must be of the format username:realm:md5checksum" if parts.size != 3
- raise InvalidDigestEntry, "md5 checksum is not 32 characters long" if parts.last.size != 32
- raise InvalidDigestEntry, "md5 checksum has invalid characters" if parts.last !~ /\A[[:xdigit:]]{32}\Z/
- return parts
- end
+ # test if a line is an entry, raise InvalidDigestEntry if it is not.
+ # an entry must be composed of 3 parts, username:realm:md5sum
+ # where username, and realm do not contain the ':' character
+ # and the md5sum must be 32 characters long.
+ def is_entry!(line)
+ raise InvalidDigestEntry, "line commented out" if line =~ /\A#/
+ parts = line.strip.split(":")
+ raise InvalidDigestEntry, "line must be of the format username:realm:md5checksum" if parts.size != 3
+ raise InvalidDigestEntry, "md5 checksum is not 32 characters long" if parts.last.size != 32
+ raise InvalidDigestEntry, "md5 checksum has invalid characters" if parts.last !~ /\A[[:xdigit:]]{32}\Z/
+ return parts
+ end
- # test if a line is an entry and return true or false
- def is_entry?(line)
- begin
- is_entry!(line)
- return true
- rescue InvalidDigestEntry
- return false
- end
- end
+ # test if a line is an entry and return true or false
+ def is_entry?(line)
+ begin
+ is_entry!(line)
+ return true
+ rescue InvalidDigestEntry
+ return false
end
+ end
+ end
- def initialize(user, realm, password = "")
- @user = user
- @realm = realm
- @digest = calc_digest(password)
- end
+ def initialize(user, realm, password = "")
+ @user = user
+ @realm = realm
+ @digest = calc_digest(password)
+ end
- def password=(new_password)
- @digest = calc_digest(new_password)
- end
+ def password=(new_password)
+ @digest = calc_digest(new_password)
+ end
- def calc_digest(password)
- ::Digest::MD5.hexdigest("#{user}:#{realm}:#{password}")
- end
+ def calc_digest(password)
+ ::Digest::MD5.hexdigest("#{user}:#{realm}:#{password}")
+ end
- def authenticated?(check_password)
- hd = ::Digest::MD5.hexdigest("#{user}:#{realm}:#{check_password}")
- return hd == digest
- end
+ def authenticated?(check_password)
+ hd = ::Digest::MD5.hexdigest("#{user}:#{realm}:#{check_password}")
+ return hd == digest
+ end
- def key
+ def key
"#{user}:#{realm}"
- end
+ end
- def to_s
+ def to_s
"#{user}:#{realm}:#{digest}"
- end
end
+ end
end
View
125 lib/htauth/digest_file.rb
@@ -1,81 +1,80 @@
require 'stringio'
-require 'tempfile'
require 'htauth/file'
require 'htauth/digest_entry'
module HTAuth
- class DigestFileError < StandardError ; end
- class DigestFile < HTAuth::File
+ class DigestFileError < StandardError ; end
+ class DigestFile < HTAuth::File
- ENTRY_KLASS = HTAuth::DigestEntry
+ ENTRY_KLASS = HTAuth::DigestEntry
- # does the entry the the specified username and realm exist in the file
- def has_entry?(username, realm)
- test_entry = DigestEntry.new(username, realm)
- @entries.has_key?(test_entry.key)
- end
+ # does the entry the the specified username and realm exist in the file
+ def has_entry?(username, realm)
+ test_entry = DigestEntry.new(username, realm)
+ @entries.has_key?(test_entry.key)
+ end
+
+ # remove an entry from the file
+ def delete(username, realm)
+ if has_entry?(username, realm) then
+ ir = internal_record(username, realm)
+ line_index = ir['line_index']
+ @entries.delete(ir['entry'].key)
+ @lines[line_index] = nil
+ dirty!
+ end
+ nil
+ end
- # remove an entry from the file
- def delete(username, realm)
- if has_entry?(username, realm) then
- ir = internal_record(username, realm)
- line_index = ir['line_index']
- @entries.delete(ir['entry'].key)
- @lines[line_index] = nil
- dirty!
- end
- nil
- end
+ # add or update an entry as appropriate
+ def add_or_update(username, realm, password)
+ if has_entry?(username, realm) then
+ update(username, realm, password)
+ else
+ add(username, realm, password)
+ end
+ end
- # add or update an entry as appropriate
- def add_or_update(username, realm, password)
- if has_entry?(username, realm) then
- update(username, realm, password)
- else
- add(username, realm, password)
- end
- end
+ # add an new record. raises an error if the entry exists.
+ def add(username, realm, password)
+ raise DigestFileError, "Unable to add already existing user #{username} in realm #{realm}" if has_entry?(username, realm)
- # add an new record. raises an error if the entry exists.
- def add(username, realm, password)
- raise DigestFileError, "Unable to add already existing user #{username} in realm #{realm}" if has_entry?(username, realm)
-
- new_entry = DigestEntry.new(username, realm, password)
- new_index = @lines.size
- @lines << new_entry.to_s
- @entries[new_entry.key] = { 'entry' => new_entry, 'line_index' => new_index }
- dirty!
- return nil
- end
+ new_entry = DigestEntry.new(username, realm, password)
+ new_index = @lines.size
+ @lines << new_entry.to_s
+ @entries[new_entry.key] = { 'entry' => new_entry, 'line_index' => new_index }
+ dirty!
+ return nil
+ end
- # update an already existing entry with a new password. raises an error if the entry does not exist
- def update(username, realm, password)
- raise DigestFileError, "Unable to update non-existent user #{username} in realm #{realm}" unless has_entry?(username, realm)
- ir = internal_record(username, realm)
- ir['entry'].password = password
- @lines[ir['line_index']] = ir['entry'].to_s
- dirty!
- return nil
- end
+ # update an already existing entry with a new password. raises an error if the entry does not exist
+ def update(username, realm, password)
+ raise DigestFileError, "Unable to update non-existent user #{username} in realm #{realm}" unless has_entry?(username, realm)
+ ir = internal_record(username, realm)
+ ir['entry'].password = password
+ @lines[ir['line_index']] = ir['entry'].to_s
+ dirty!
+ return nil
+ end
- # fetches a copy of an entry from the file. Updateing the entry returned from fetch will NOT
- # propogate back to the file.
- def fetch(username, realm)
- return nil unless has_entry?(username, realm)
- ir = internal_record(username, realm)
- return ir['entry'].dup
- end
+ # fetches a copy of an entry from the file. Updateing the entry returned from fetch will NOT
+ # propogate back to the file.
+ def fetch(username, realm)
+ return nil unless has_entry?(username, realm)
+ ir = internal_record(username, realm)
+ return ir['entry'].dup
+ end
- def entry_klass
- ENTRY_KLASS
- end
+ def entry_klass
+ ENTRY_KLASS
+ end
- private
+ private
- def internal_record(username, realm)
- e = DigestEntry.new(username, realm)
- @entries[e.key]
- end
+ def internal_record(username, realm)
+ e = DigestEntry.new(username, realm)
+ @entries[e.key]
end
+ end
end
View
10 lib/htauth/entry.rb
@@ -1,9 +1,9 @@
module HTAuth
- # base class from which all entries are derived
- class Entry
- def dup
- self.class.from_line(self.to_s)
- end
+ # base class from which all entries are derived
+ class Entry
+ def dup
+ self.class.from_line(self.to_s)
end
+ end
end
View
168 lib/htauth/file.rb
@@ -2,102 +2,102 @@
require 'htauth'
module HTAuth
- class FileAccessError < StandardError ; end
- class File
- ALTER = "alter"
- CREATE = "create"
- STDOUT_FLAG = "-"
+ class FileAccessError < StandardError ; end
+ class File
+ ALTER = "alter"
+ CREATE = "create"
+ STDOUT_FLAG = "-"
- attr_reader :filename
- attr_reader :file
+ attr_reader :filename
+ attr_reader :file
- class << self
- # open a file yielding the the file object for use. The file is saved when
- # the block exists, if the file has had alterations made.
- def open(filename, mode = ALTER)
- f = self.new(filename, mode)
- if block_given?
- begin
- yield f
- ensure
- f.save! if f and f.dirty?
- end
- end
- return f
- end
+ class << self
+ # open a file yielding the the file object for use. The file is saved when
+ # the block exists, if the file has had alterations made.
+ def open(filename, mode = ALTER)
+ f = self.new(filename, mode)
+ if block_given?
+ begin
+ yield f
+ ensure
+ f.save! if f and f.dirty?
+ end
end
+ return f
+ end
+ end
- # Create or Alter a password file.
- #
- # Altering a non-existent file is an error. Creating an existing file results in
- # a truncation and overwrite of the existing file.
- def initialize(filename, mode = ALTER)
- @filename = filename
- @mode = mode
- @dirty = false
-
- raise FileAccessError, "Invalid mode #{mode}" unless [ ALTER, CREATE ].include?(mode)
+ # Create or Alter a password file.
+ #
+ # Altering a non-existent file is an error. Creating an existing file results in
+ # a truncation and overwrite of the existing file.
+ def initialize(filename, mode = ALTER)
+ @filename = filename
+ @mode = mode
+ @dirty = false
- if (filename != STDOUT_FLAG) and (mode == ALTER) and (not ::File.exist?(filename)) then
- raise FileAccessError, "Could not open passwd file #{filename} for reading."
- end
+ raise FileAccessError, "Invalid mode #{mode}" unless [ ALTER, CREATE ].include?(mode)
- begin
- @entries = {}
- @lines = []
- load_entries if (@mode == ALTER) and (filename != STDOUT_FLAG)
- rescue => e
- raise FileAccessError, e.message
- end
- end
+ if (filename != STDOUT_FLAG) and (mode == ALTER) and (not ::File.exist?(filename)) then
+ raise FileAccessError, "Could not open passwd file #{filename} for reading."
+ end
- # return whether or not an alteration to the file has happened
- def dirty?
- @dirty
- end
+ begin
+ @entries = {}
+ @lines = []
+ load_entries if (@mode == ALTER) and (filename != STDOUT_FLAG)
+ rescue => e
+ raise FileAccessError, e.message
+ end
+ end
- # mark the file as dirty
- def dirty!
- @dirty = true
- end
+ # return whether or not an alteration to the file has happened
+ def dirty?
+ @dirty
+ end
- # update the original file with the new contents
- def save!
- begin
- case filename
- when STDOUT_FLAG
- $stdout.write(contents)
- else
- ::File.open(@filename,"w") do |f|
- f.write(contents)
- end
- end
- @dirty = false
- rescue => e
- raise FileAccessError, "Error saving file #{@filename} : #{e.message}"
- end
- end
+ # mark the file as dirty
+ def dirty!
+ @dirty = true
+ end
- # return what should be the contents of the file
- def contents
- c = StringIO.new
- @lines.each do |l|
- c.puts l if l
- end
- c.string
+ # update the original file with the new contents
+ def save!
+ begin
+ case filename
+ when STDOUT_FLAG
+ $stdout.write(contents)
+ else
+ ::File.open(@filename,"w") do |f|
+ f.write(contents)
+ end
end
+ @dirty = false
+ rescue => e
+ raise FileAccessError, "Error saving file #{@filename} : #{e.message}"
+ end
+ end
+
+ # return what should be the contents of the file
+ def contents
+ c = StringIO.new
+ @lines.each do |l|
+ c.puts l if l
+ end
+ c.string
+ end
- # load up entries, keep items in the same order and do not trim out any
- # items in the file, like commented out lines or empty space
- def load_entries
- @lines = IO.readlines(@filename)
- @lines.each_with_index do |line,idx|
- if entry_klass.is_entry?(line) then
- entry = entry_klass.from_line(line)
- v = { 'entry' => entry, 'line_index' => idx }
- @entries[entry.key] = v
- end
- end
+ # load up entries, keep items in the same order and do not trim out any
+ # items in the file, like commented out lines or empty space
+ def load_entries
+ @lines = IO.readlines(@filename)
+ @lines.each_with_index do |line,idx|
+ if entry_klass.is_entry?(line) then
+ entry = entry_klass.from_line(line)
+ v = { 'entry' => entry, 'line_index' => idx }
+ @entries[entry.key] = v
end
+ end
end
+ end
end
View
53 lib/htauth/gemspec.rb
@@ -1,53 +0,0 @@
-require 'rubygems'
-require 'htauth/specification'
-require 'htauth/version'
-require 'rake'
-
-# The Gem Specification plus some extras for htauth.
-module HTAuth
- SPEC = HTAuth::Specification.new do |spec|
- spec.name = "htauth"
- spec.version = HTAuth::VERSION
- spec.rubyforge_project = "copiousfreetime"
- spec.author = "Jeremy Hinegardner"
- spec.email = "jeremy@hinegardner.org"
- spec.homepage = "http://copiousfreetime.rubyforge.org/htauth"
-
- spec.summary = "Pure Ruby replacement for the htdigest and htpasswd programs."
- spec.description = <<-DESC
- HTAuth is a pure ruby replacement for the Apache support programs htdigest
- and htpasswd. Command line and API access are provided for access to
- htdigest and htpasswd files.
- DESC
-
- spec.extra_rdoc_files = FileList["CHANGES", "LICENSE", "README"]
- spec.has_rdoc = true
- spec.rdoc_main = "README"
- spec.rdoc_options = [ "--line-numbers" , "--inline-source" ]
-
- spec.test_files = FileList["spec/**/*"]
- spec.executables << "htdigest-ruby"
- spec.executables << "htpasswd-ruby"
- spec.files = spec.test_files + spec.extra_rdoc_files +
- FileList["lib/**/*.rb"]
-
- spec.add_dependency("rake")
- spec.add_dependency("highline", ">= 1.4.0")
-
- spec.platform = Gem::Platform::RUBY
-
- spec.remote_user = "jjh"
- spec.local_rdoc_dir = "doc/rdoc"
- spec.remote_rdoc_dir = ""
- spec.local_coverage_dir = "doc/coverage"
-
- spec.remote_site_dir = "#{spec.name}/"
-
- spec.post_install_message = <<EOM
-Try out 'htpasswd-ruby' or 'htdigest-ruby' to get started.
-EOM
-
- end
-end
-
-
View
144 lib/htauth/md5.rb
@@ -3,80 +3,80 @@
module HTAuth
- # an implementation of the MD5 based encoding algorithm
- # as used in the apache htpasswd -m option
- class Md5 < Algorithm
-
- DIGEST_LENGTH = 16
-
- def initialize(params = {})
- @salt = params['salt'] || params[:salt] || gen_salt
- end
+ # an implementation of the MD5 based encoding algorithm
+ # as used in the apache htpasswd -m option
+ class Md5 < Algorithm
- def prefix
- "$apr1$"
- end
+ DIGEST_LENGTH = 16
+
+ def initialize(params = {})
+ @salt = params['salt'] || params[:salt] || gen_salt
+ end
- # this algorigthm pulled straight from apr_md5_encode() and converted to ruby syntax
- def encode(password)
- primary = ::Digest::MD5.new
- primary << password
- primary << prefix
- primary << @salt
-
- md5_t = ::Digest::MD5.digest("#{password}#{@salt}#{password}")
-
- l = password.length
- while l > 0 do
- slice_size = ( l > DIGEST_LENGTH ) ? DIGEST_LENGTH : l
- primary << md5_t[0, slice_size]
- l -= DIGEST_LENGTH
- end
-
- # weirdness
- l = password.length
- while l != 0
- case (l & 1)
- when 1
- primary << 0.chr
- when 0
- primary << password[0,1]
- end
- l >>= 1
- end
-
- pd = primary.digest
-
- encoded_password = "#{prefix}#{@salt}$"
-
- # apr_md5_encode has this comment about a 60Mhz Pentium above this loop.
- 1000.times do |x|
- ctx = ::Digest::MD5.new
- ctx << (( ( x & 1 ) == 1 ) ? password : pd[0,DIGEST_LENGTH])
- (ctx << @salt) unless ( x % 3 ) == 0
- (ctx << password) unless ( x % 7 ) == 0
- ctx << (( ( x & 1 ) == 0 ) ? password : pd[0,DIGEST_LENGTH])
- pd = ctx.digest
- end
-
-
- l = (pd[ 0]<<16) | (pd[ 6]<<8) | pd[12]
- encoded_password << to_64(l, 4)
-
- l = (pd[ 1]<<16) | (pd[ 7]<<8) | pd[13]
- encoded_password << to_64(l, 4)
-
- l = (pd[ 2]<<16) | (pd[ 8]<<8) | pd[14]
- encoded_password << to_64(l, 4)
-
- l = (pd[ 3]<<16) | (pd[ 9]<<8) | pd[15]
- encoded_password << to_64(l, 4)
-
- l = (pd[ 4]<<16) | (pd[10]<<8) | pd[ 5]
- encoded_password << to_64(l, 4)
- encoded_password << to_64(pd[11],2)
-
- return encoded_password
+ def prefix
+ "$apr1$"
+ end
+
+ # this algorigthm pulled straight from apr_md5_encode() and converted to ruby syntax
+ def encode(password)
+ primary = ::Digest::MD5.new
+ primary << password
+ primary << prefix
+ primary << @salt
+
+ md5_t = ::Digest::MD5.digest("#{password}#{@salt}#{password}")
+
+ l = password.length
+ while l > 0 do
+ slice_size = ( l > DIGEST_LENGTH ) ? DIGEST_LENGTH : l
+ primary << md5_t[0, slice_size]
+ l -= DIGEST_LENGTH
+ end
+
+ # weirdness
+ l = password.length
+ while l != 0
+ case (l & 1)
+ when 1
+ primary << 0.chr
+ when 0
+ primary << password[0,1]
end
+ l >>= 1
+ end
+
+ pd = primary.digest
+
+ encoded_password = "#{prefix}#{@salt}$"
+
+ # apr_md5_encode has this comment about a 60Mhz Pentium above this loop.
+ 1000.times do |x|
+ ctx = ::Digest::MD5.new
+ ctx << (( ( x & 1 ) == 1 ) ? password : pd[0,DIGEST_LENGTH])
+ (ctx << @salt) unless ( x % 3 ) == 0
+ (ctx << password) unless ( x % 7 ) == 0
+ ctx << (( ( x & 1 ) == 0 ) ? password : pd[0,DIGEST_LENGTH])
+ pd = ctx.digest
+ end
+
+
+ l = (pd[ 0]<<16) | (pd[ 6]<<8) | pd[12]
+ encoded_password << to_64(l, 4)
+
+ l = (pd[ 1]<<16) | (pd[ 7]<<8) | pd[13]
+ encoded_password << to_64(l, 4)
+
+ l = (pd[ 2]<<16) | (pd[ 8]<<8) | pd[14]
+ encoded_password << to_64(l, 4)
+
+ l = (pd[ 3]<<16) | (pd[ 9]<<8) | pd[15]
+ encoded_password << to_64(l, 4)
+
+ l = (pd[ 4]<<16) | (pd[10]<<8) | pd[ 5]
+ encoded_password << to_64(l, 4)
+ encoded_password << to_64(pd[11],2)
+
+ return encoded_password
end
+ end
end
View
299 lib/htauth/passwd.rb
@@ -4,173 +4,172 @@
require 'ostruct'
require 'optparse'
-require 'rubygems'
require 'highline'
module HTAuth
- class Passwd
+ class Passwd
- MAX_PASSWD_LENGTH = 255
+ MAX_PASSWD_LENGTH = 255
- attr_accessor :passwd_file
+ attr_accessor :passwd_file
- def initialize
- @passwd_file = nil
- end
+ def initialize
+ @passwd_file = nil
+ end
- def options
- if @options.nil? then
- @options = ::OpenStruct.new
- @options.batch_mode = false
- @options.file_mode = File::ALTER
- @options.passwdfile = nil
- @options.algorithm = Algorithm::EXISTING
- @options.send_to_stdout = false
- @options.show_version = false
- @options.show_help = false
- @options.username = nil
- @options.delete_entry = false
- @options.password = ""
- end
- @options
- end
+ def options
+ if @options.nil? then
+ @options = ::OpenStruct.new
+ @options.batch_mode = false
+ @options.file_mode = File::ALTER
+ @options.passwdfile = nil
+ @options.algorithm = Algorithm::EXISTING
+ @options.send_to_stdout = false
+ @options.show_version = false
+ @options.show_help = false
+ @options.username = nil
+ @options.delete_entry = false
+ @options.password = ""
+ end
+ @options
+ end
- def option_parser
- if not @option_parser then
- @option_parser = OptionParser.new do |op|
- op.banner = <<EOB
+ def option_parser
+ if not @option_parser then
+ @option_parser = OptionParser.new do |op|
+ op.banner = <<EOB
Usage:
- #{op.program_name} [-cmdpsD] passwordfile username
- #{op.program_name} -b[cmdpsD] passwordfile username password
+ #{op.program_name} [-cmdpsD] passwordfile username
+ #{op.program_name} -b[cmdpsD] passwordfile username password
- #{op.program_name} -n[mdps] username
- #{op.program_name} -nb[mdps] username password
+ #{op.program_name} -n[mdps] username
+ #{op.program_name} -nb[mdps] username password
EOB
- op.separator ""
-
- op.on("-b", "--batch", "Batch mode, get the password from the command line, rather than prompt") do |b|
- options.batch_mode = b
- end
-
- op.on("-c", "--create", "Create a new file; this overwrites an existing file.") do |c|
- options.file_mode = HTAuth::File::CREATE
- end
-
- op.on("-d", "--crypt", "Force CRYPT encryption of the password (default).") do |c|
- options.algorithm = "crypt"
- end
-
- op.on("-D", "--delete", "Delete the specified user.") do |d|
- options.delete_entry = d
- end
-
- op.on("-h", "--help", "Display this help.") do |h|
- options.show_help = h
- end
-
- op.on("-m", "--md5", "Force MD5 encryption of the password (default on Windows).") do |m|
- options.algorithm = "md5"
- end
-
- op.on("-n", "--stdout", "Do not update the file; Display the results on stdout instead.") do |n|
- options.send_to_stdout = true
- options.passwdfile = HTAuth::File::STDOUT_FLAG
- end
-
- op.on("-p", "--plaintext", "Do not encrypt the password (plaintext).") do |p|
- options.algorithm = "plaintext"
- end
-
- op.on("-s", "--sha1", "Force SHA encryption of the password.") do |s|
- options.algorithm = "sha1"
- end
-
- op.on("-v", "--version", "Show version info.") do |v|
- options.show_version = v
- end
- end
- end
- @option_parser
- end
+ op.separator ""
- def show_help
- $stdout.puts option_parser
- exit 1
- end
+ op.on("-b", "--batch", "Batch mode, get the password from the command line, rather than prompt") do |b|
+ options.batch_mode = b
+ end
- def show_version
- $stdout.puts "#{option_parser.program_name}: version #{HTAuth::VERSION}"
- exit 1
- end
+ op.on("-c", "--create", "Create a new file; this overwrites an existing file.") do |c|
+ options.file_mode = HTAuth::File::CREATE
+ end
+
+ op.on("-d", "--crypt", "Force CRYPT encryption of the password (default).") do |c|
+ options.algorithm = "crypt"
+ end
+
+ op.on("-D", "--delete", "Delete the specified user.") do |d|
+ options.delete_entry = d
+ end
+
+ op.on("-h", "--help", "Display this help.") do |h|
+ options.show_help = h
+ end
+
+ op.on("-m", "--md5", "Force MD5 encryption of the password (default on Windows).") do |m|
+ options.algorithm = "md5"
+ end
+
+ op.on("-n", "--stdout", "Do not update the file; Display the results on stdout instead.") do |n|
+ options.send_to_stdout = true
+ options.passwdfile = HTAuth::File::STDOUT_FLAG
+ end
+
+ op.on("-p", "--plaintext", "Do not encrypt the password (plaintext).") do |p|
+ options.algorithm = "plaintext"
+ end
+
+ op.on("-s", "--sha1", "Force SHA encryption of the password.") do |s|
+ options.algorithm = "sha1"
+ end
- def parse_options(argv)
- begin
- option_parser.parse!(argv)
- show_version if options.show_version
- show_help if options.show_help
-
- raise ::OptionParser::ParseError, "Unable to send to stdout AND create a new file" if options.send_to_stdout and (options.file_mode == File::CREATE)
- raise ::OptionParser::ParseError, "a username is needed" if options.send_to_stdout and argv.size < 1
- raise ::OptionParser::ParseError, "a username and password are needed" if options.send_to_stdout and options.batch_mode and ( argv.size < 2 )
- raise ::OptionParser::ParseError, "a passwordfile, username and password are needed " if not options.send_to_stdout and options.batch_mode and ( argv.size < 3 )
- raise ::OptionParser::ParseError, "a passwordfile and username are needed" if argv.size < 2
-
- options.passwdfile = argv.shift unless options.send_to_stdout
- options.username = argv.shift
- options.password = argv.shift if options.batch_mode
-
- rescue ::OptionParser::ParseError => pe
- $stderr.puts "ERROR: #{option_parser.program_name} - #{pe}"
- show_help
- exit 1
- end
+ op.on("-v", "--version", "Show version info.") do |v|
+ options.show_version = v
+ end
end
+ end
+ @option_parser
+ end
+
+ def show_help
+ $stdout.puts option_parser
+ exit 1
+ end
+
+ def show_version
+ $stdout.puts "#{option_parser.program_name}: version #{HTAuth::VERSION}"
+ exit 1
+ end
+
+ def parse_options(argv)
+ begin
+ option_parser.parse!(argv)
+ show_version if options.show_version
+ show_help if options.show_help
+
+ raise ::OptionParser::ParseError, "Unable to send to stdout AND create a new file" if options.send_to_stdout and (options.file_mode == File::CREATE)
+ raise ::OptionParser::ParseError, "a username is needed" if options.send_to_stdout and argv.size < 1
+ raise ::OptionParser::ParseError, "a username and password are needed" if options.send_to_stdout and options.batch_mode and ( argv.size < 2 )
+ raise ::OptionParser::ParseError, "a passwordfile, username and password are needed " if not options.send_to_stdout and options.batch_mode and ( argv.size < 3 )
+ raise ::OptionParser::ParseError, "a passwordfile and username are needed" if argv.size < 2
+
+ options.passwdfile = argv.shift unless options.send_to_stdout
+ options.username = argv.shift
+ options.password = argv.shift if options.batch_mode
+
+ rescue ::OptionParser::ParseError => pe
+ $stderr.puts "ERROR: #{option_parser.program_name} - #{pe}"
+ show_help
+ exit 1
+ end
+ end
- def run(argv)
- begin
- parse_options(argv)
- passwd_file = PasswdFile.new(options.passwdfile, options.file_mode)
-
- if options.delete_entry then
- passwd_file.delete(options.username)
- else
- unless options.batch_mode
- # initialize here so that if $stdin is overwritten it gest picked up
- hl = ::HighLine.new
-
- action = passwd_file.has_entry?(options.username) ? "Changing" : "Adding"
-
- $stdout.puts "#{action} password for #{options.username}."
-
- pw_in = hl.ask(" New password: ") { |q| q.echo = '*' }
- raise PasswordError, "password '#{pw_in}' too long" if pw_in.length >= MAX_PASSWD_LENGTH
-
- pw_validate = hl.ask("Re-type new password: ") { |q| q.echo = '*' }
- raise PasswordError, "They don't match, sorry." unless pw_in == pw_validate
- options.password = pw_in
- end
- passwd_file.add_or_update(options.username, options.password, options.algorithm)
- end
-
- passwd_file.save!
-
- rescue HTAuth::FileAccessError => fae
- msg = "Password file failure (#{options.passwdfile}) "
- $stderr.puts "#{msg}: #{fae.message}"
- exit 1
- rescue HTAuth::PasswordError => pe
- $stderr.puts "#{pe.message}"
- exit 1
- rescue HTAuth::PasswdFileError => fe
- $stderr.puts "#{fe.message}"
- exit 1
- rescue SignalException => se
- $stderr.puts
- $stderr.puts "Interrupted"
- exit 1
- end
- exit 0
+ def run(argv, env)
+ begin
+ parse_options(argv)
+ passwd_file = PasswdFile.new(options.passwdfile, options.file_mode)
+
+ if options.delete_entry then
+ passwd_file.delete(options.username)
+ else
+ unless options.batch_mode
+ # initialize here so that if $stdin is overwritten it gest picked up
+ hl = ::HighLine.new
+
+ action = passwd_file.has_entry?(options.username) ? "Changing" : "Adding"
+
+ $stdout.puts "#{action} password for #{options.username}."
+
+ pw_in = hl.ask(" New password: ") { |q| q.echo = '*' }
+ raise PasswordError, "password '#{pw_in}' too long" if pw_in.length >= MAX_PASSWD_LENGTH
+
+ pw_validate = hl.ask("Re-type new password: ") { |q| q.echo = '*' }
+ raise PasswordError, "They don't match, sorry." unless pw_in == pw_validate
+ options.password = pw_in
+ end
+ passwd_file.add_or_update(options.username, options.password, options.algorithm)
end
+
+ passwd_file.save!
+
+ rescue HTAuth::FileAccessError => fae
+ msg = "Password file failure (#{options.passwdfile}) "
+ $stderr.puts "#{msg}: #{fae.message}"
+ exit 1
+ rescue HTAuth::PasswordError => pe
+ $stderr.puts "#{pe.message}"
+ exit 1
+ rescue HTAuth::PasswdFileError => fe
+ $stderr.puts "#{fe.message}"
+ exit 1
+ rescue SignalException => se
+ $stderr.puts
+ $stderr.puts "Interrupted"
+ exit 1
+ end
+ exit 0
end
+ end
end
View
154 lib/htauth/passwd_entry.rb
@@ -2,96 +2,96 @@
require 'htauth/algorithm'
module HTAuth
- class InvalidPasswdEntry < StandardError ; end
+ class InvalidPasswdEntry < StandardError ; end
- # A single record in an htdigest file.
- class PasswdEntry < Entry
+ # A single record in an htdigest file.
+ class PasswdEntry < Entry
- attr_accessor :user
- attr_accessor :digest
- attr_reader :algorithm
+ attr_accessor :user
+ attr_accessor :digest
+ attr_reader :algorithm
- class << self
- def from_line(line)
- parts = is_entry!(line)
- d = PasswdEntry.new(parts[0])
- d.digest = parts[1]
- d.algorithm = Algorithm.algorithms_from_field(parts[1])
- return d
- end
+ class << self
+ def from_line(line)
+ parts = is_entry!(line)
+ d = PasswdEntry.new(parts[0])
+ d.digest = parts[1]
+ d.algorithm = Algorithm.algorithms_from_field(parts[1])
+ return d
+ end
- # test if a line is an entry, raise InvalidPasswdEntry if it is not.
- # an entry must be composed of 2 parts, username:encrypted_password
- # where username, and password do not contain the ':' character
- def is_entry!(line)
- raise InvalidPasswdEntry, "line commented out" if line =~ /\A#/
- parts = line.strip.split(":")
- raise InvalidPasswdEntry, "line must be of the format username:pssword" if parts.size != 2
- return parts
- end
+ # test if a line is an entry, raise InvalidPasswdEntry if it is not.
+ # an entry must be composed of 2 parts, username:encrypted_password
+ # where username, and password do not contain the ':' character
+ def is_entry!(line)
+ raise InvalidPasswdEntry, "line commented out" if line =~ /\A#/
+ parts = line.strip.split(":")
+ raise InvalidPasswdEntry, "line must be of the format username:pssword" if parts.size != 2
+ return parts
+ end
- # test if a line is an entry and return true or false
- def is_entry?(line)
- begin
- is_entry!(line)
- return true
- rescue InvalidPasswdEntry
- return false
- end
- end
+ # test if a line is an entry and return true or false
+ def is_entry?(line)
+ begin
+ is_entry!(line)
+ return true
+ rescue InvalidPasswdEntry
+ return false
end
+ end
+ end
- def initialize(user, password = "", alg = Algorithm::DEFAULT, alg_params = {} )
- @user = user
- alg = Algorithm::DEFAULT if alg == Algorithm::EXISTING
- @algorithm = Algorithm.algorithm_from_name(alg, alg_params)
- @digest = algorithm.encode(password)
- end
+ def initialize(user, password = "", alg = Algorithm::DEFAULT, alg_params = {} )
+ @user = user
+ alg = Algorithm::DEFAULT if alg == Algorithm::EXISTING
+ @algorithm = Algorithm.algorithm_from_name(alg, alg_params)
+ @digest = algorithm.encode(password)
+ end
- def algorithm=(alg)
- if alg.kind_of?(Array) then
- if alg.size == 1 then
- @algorithm = alg.first
- else
- @algorithm = alg
- end
- else
- @algorithm = Algorithm.algorithm_from_name(alg) unless Algorithm::EXISTING == alg
- end
- return @algorithm
+ def algorithm=(alg)
+ if alg.kind_of?(Array) then
+ if alg.size == 1 then
+ @algorithm = alg.first
+ else
+ @algorithm = alg
end
+ else
+ @algorithm = Algorithm.algorithm_from_name(alg) unless Algorithm::EXISTING == alg
+ end
+ return @algorithm
+ end
- def password=(new_password)
- if algorithm.kind_of?(Array) then
- @algorithm = Algorithm.algorithm_from_name("crypt")
- end
- @digest = algorithm.encode(new_password)
- end
+ def password=(new_password)
+ if algorithm.kind_of?(Array) then
+ @algorithm = Algorithm.algorithm_from_name("crypt")
+ end
+ @digest = algorithm.encode(new_password)
+ end
- # check the password and make sure it works, in the case that the algorithm is unknown it
- # tries all of the ones that it thinks it could be, and marks the algorithm if it matches
- def authenticated?(check_password)
- authed = false
- if algorithm.kind_of?(Array) then
- algorithm.each do |alg|
- if alg.encode(check_password) == digest then
- @algorithm = alg
- authed = true
- break
- end
- end
- else
- authed = digest == algorithm.encode(check_password)
- end
- return authed
+ # check the password and make sure it works, in the case that the algorithm is unknown it
+ # tries all of the ones that it thinks it could be, and marks the algorithm if it matches
+ def authenticated?(check_password)
+ authed = false
+ if algorithm.kind_of?(Array) then
+ algorithm.each do |alg|
+ if alg.encode(check_password) == digest then
+ @algorithm = alg
+ authed = true
+ break
+ end
end
+ else
+ authed = digest == algorithm.encode(check_password)
+ end
+ return authed
+ end
- def key
- return "#{user}"
- end
+ def key
+ return "#{user}"
+ end
- def to_s
- "#{user}:#{digest}"
- end
+ def to_s
+ "#{user}:#{digest}"
end
+ end
end
View
129 lib/htauth/specification.rb
@@ -1,129 +0,0 @@
-require 'rubygems'
-require 'rubygems/specification'
-require 'rake'
-require 'htauth'
-
-module HTAuth
- # Add some additional items to Gem::Specification
- # A HTAuth::Specification adds additional pieces of information the
- # typical gem specification
- class Specification
-
- RUBYFORGE_ROOT = "/var/www/gforge-projects/"
-
- # user that accesses remote site
- attr_accessor :remote_user
-
- # remote host, default 'rubyforge.org'
- attr_accessor :remote_host
-
- # name the rdoc main
- attr_accessor :rdoc_main
-
- # local directory in development holding the generated rdoc
- # default 'doc'
- attr_accessor :local_rdoc_dir
-
- # remote directory for storing rdoc, default 'doc'
- attr_accessor :remote_rdoc_dir
-
- # local directory for coverage report
- attr_accessor :local_coverage_dir
-
- # remote directory for storing coverage reports
- # This defaults to 'coverage'
- attr_accessor :remote_coverage_dir
-
- # local directory for generated website, default +site/public+
- attr_accessor :local_site_dir
-
- # remote directory relative to +remote_root+ for the website.
- # website.
- attr_accessor :remote_site_dir
-
- # is a .tgz to be created?, default 'true'
- attr_accessor :need_tar
-
- # is a .zip to be created, default 'true'
- attr_accessor :need_zip
-
-
- def initialize
- @remote_user = nil
- @remote_host = "rubyforge.org"
-
- @rdoc_main = "README"
- @local_rdoc_dir = "doc"
- @remote_rdoc_dir = "doc"
- @local_coverage_dir = "coverage"
- @remote_coverage_dir = "coverage"
- @local_site_dir = "site/public"
- @remote_site_dir = "."
-
- @need_tar = true
- @need_zip = true
-
- @spec = Gem::Specification.new
-
- yield self if block_given?
-
- # update rdoc options to take care of the rdoc_main if it is
- # there, and add a default title if one is not given
- if not @spec.rdoc_options.include?("--main") then
- @spec.rdoc_options.concat(["--main", rdoc_main])
- end
-
- if not @spec.rdoc_options.include?("--title") then
- @spec.rdoc_options.concat(["--title","'#{name} -- #{summary}'"])
- end
- end
-
- # if this gets set then it overwrites what would be the
- # rubyforge default. If rubyforge project is not set then use
- # name. If rubyforge project and name are set, but they are
- # different then assume that name is a subproject of the
- # rubyforge project
- def remote_root
- if rubyforge_project.nil? or
- rubyforge_project == name then
- return RUBYFORGE_ROOT + "#{name}/"
- else
- return RUBYFORGE_ROOT + "#{rubyforge_project}/#{name}/"
- end
- end
-
- # rdoc files is the same as what would be generated during gem
- # installation. That is, everything in the require paths plus
- # the rdoc_extra_files
- #
- def rdoc_files
- flist = extra_rdoc_files.dup
- @spec.require_paths.each do |rp|
- flist << FileList["#{rp}/**/*.rb"]
- end
- flist.flatten.uniq
- end
-
- # calculate the remote directories
- def remote_root_location
- "#{remote_user}@#{remote_host}:#{remote_root}"
- end
-
- def remote_rdoc_location
- remote_root_location + @remote_rdoc_dir
- end
-
- def remote_coverage_location
- remote_root_loation + @remote_coverage_dir
- end
-
- def remote_site_location
- remote_root_location + @remote_site_dir
- end
-
- # we delegate any other calls to spec
- def method_missing(method_id,*params,&block)
- @spec.send method_id, *params, &block
- end
- end
-end
View
29 lib/htauth/version.rb
@@ -1,19 +1,22 @@
require 'htauth'
module HTAuth
- class Version
- MAJOR = 1
- MINOR = 0
- BUILD = 1
+ module Version
+ MAJOR = 1
+ MINOR = 0
+ BUILD = 2
- class << self
- def to_a
- [MAJOR, MINOR, BUILD]
- end
+ def to_a
+ [MAJOR, MINOR, BUILD]
+ end
- def to_s
- to_a.join(".")
- end
- end
+ def to_s
+ to_a.join(".")
end
- VERSION = Version.to_s
+
+ module_function :to_a
+ module_function :to_s
+
+ STRING = Version.to_s
+ end
+ VERSION = Version.to_s
end
View
90 tasks/announce.rake
@@ -1,58 +1,38 @@
-#-----------------------------------------------------------------------
-# Announcement tasks
-# - create a basic email template that can be used to send email to
-# rubytalk.
-#-----------------------------------------------------------------------
-def changes
- change_file = File.expand_path(File.join(HTAuth::ROOT_DIR,"CHANGES"))
- sections = File.read(change_file).split(/^(?=== )/)
-end
-
-def last_changeset
- changes[1]
-end
-
-def announcement
- puts changes.size
- urls = " #{HTAuth::SPEC.homepage}"
- subject = "#{HTAuth::SPEC.name} #{HTAuth::VERSION} Released"
- title = "#{HTAuth::SPEC.name} version #{HTAuth::VERSION} has been released."
- body = <<BODY
-#{HTAuth::SPEC.description.rstrip}
-
-{{ Changelog for Version #{HTAuth::VERSION} }}
-
-#{last_changeset.rstrip}
-
-BODY
-
- return subject, title, body, urls
-end
+require 'tasks/config'
+#-------------------------------------------------------------------------------
+# announcement methods
+#-------------------------------------------------------------------------------
+proj_config = Configuration.for('project')
namespace :announce do
- desc "create email for ruby-talk"
- task :email do
- subject, title, body, urls = announcement
-
- File.open("email.txt", "w") do |mail|
- mail.puts "From: #{HTAuth::SPEC.author} <#{HTAuth::SPEC.email}>"
- mail.puts "To: ruby-talk@ruby-lang.org"
- mail.puts "Date: #{Time.now.rfc2822}"
- mail.puts "Subject: [ANN] #{subject}"
- mail.puts
- mail.puts title
- mail.puts
- mail.puts urls
- mail.puts
- mail.puts body
- mail.puts
- mail.puts urls
- end
- puts "Created the following as email.txt:"
- puts "-" * 72
- puts File.read("email.txt")
- puts "-" * 72
- end
-
- CLOBBER << "email.txt"
+ desc "create email for ruby-talk"
+ task :email do
+ info = Utils.announcement
+
+ File.open("email.txt", "w") do |mail|
+ mail.puts "From: #{proj_config.author} <#{proj_config.email}>"
+ mail.puts "To: ruby-talk@ruby-lang.org"
+ mail.puts "Date: #{Time.now.rfc2822}"
+ mail.puts "Subject: [ANN] #{info[:subject]}"
+ mail.puts
+ mail.puts info[:title]
+ mail.puts
+ mail.puts info[:urls]
+ mail.puts
+ mail.puts info[:description]
+ mail.puts
+ mail.puts "{{ Release notes for Version #{HTAuth::VERSION} }}"
+ mail.puts
+ mail.puts info[:release_notes]
+ mail.puts
+ mail.puts info[:urls]
+ end
+ puts "Created the following as email.txt:"
+ puts "-" * 72
+ puts File.read("email.txt")
+ puts "-" * 72
+ end
+
+ CLOBBER << "email.txt"
end
+
View
98 tasks/config.rb
@@ -0,0 +1,98 @@
+require 'configuration'
+
+require 'rake'
+require 'tasks/utils'
+
+#-----------------------------------------------------------------------
+# General project configuration
+#-----------------------------------------------------------------------
+Configuration.for('project') {
+ name "htauth"
+ version "1.0.1"
+ author "Jeremy Hinegardner"
+ email "jeremy@copiousfreetime.org"
+ homepage "http://copiousfreetime.rubyforge.org/htauth"
+ description Utils.section_of("README", "description")
+ summary description.split(".").first
+ history "HISTORY"
+ license FileList["LICENSE", ]
+ readme "README"
+}
+
+#-----------------------------------------------------------------------
+# Packaging
+#-----------------------------------------------------------------------
+Configuration.for('packaging') {
+ # files in the project
+ proj_conf = Configuration.for('project')
+ files {
+ bin FileList["bin/*"]
+ lib FileList["lib/**/*.rb"]
+ test FileList["spec/**/*.rb", "test/**/*.rb"]
+ data FileList["data/**/*"]
+ tasks FileList["tasks/**/*.r{ake,b}"]
+ rdoc FileList[proj_conf.readme, proj_conf.history,
+ proj_conf.license] + lib
+ all bin + lib + test + data + rdoc + tasks
+ }
+
+ # ways to package the results
+ formats {
+ tgz true
+ zip true
+ rubygem Configuration::Table.has_key?('rubygem')
+ }
+}
+
+#-----------------------------------------------------------------------
+# Gem packaging
+#-----------------------------------------------------------------------
+Configuration.for("rubygem") {
+ spec "gemspec.rb"
+ Configuration.for('packaging').files.all << spec
+}
+
+#-----------------------------------------------------------------------
+# Testing
+# - change mode to 'testunit' to use unit testing
+#-----------------------------------------------------------------------
+Configuration.for('test') {
+ mode "spec"
+ files Configuration.for("packaging").files.test
+ options %w[ --format specdoc --color ]
+ ruby_opts %w[ ]
+}
+
+#-----------------------------------------------------------------------
+# Rcov
+#-----------------------------------------------------------------------
+Configuration.for('rcov') {
+ output_dir "coverage"
+ libs %w[ lib ]
+ rcov_opts %w[ --html ]
+ ruby_opts %w[ ]
+ test_files Configuration.for('packaging').files.test
+}
+
+#-----------------------------------------------------------------------
+# Rdoc
+#-----------------------------------------------------------------------
+Configuration.for('rdoc') {
+ files Configuration.for('packaging').files.rdoc
+ main_page files.first
+ title Configuration.for('project').name
+ options %w[ --line-numbers --inline-source ]
+ output_dir "doc"
+}
+
+#-----------------------------------------------------------------------
+# Rubyforge
+#-----------------------------------------------------------------------
+Configuration.for('rubyforge') {
+ project "copiousfreetime"
+ user "jjh"
+ host "rubyforge.org"
+ rdoc_location "#{user}@#{host}:/var/www/gforge-projects/#{project}/htauth"
+}
+
+
View
39 tasks/distribution.rake
@@ -1,39 +1,38 @@
-#-----------------------------------------------------------------------
+require 'tasks/config'
+
+#-------------------------------------------------------------------------------
# Distribution and Packaging
-#-----------------------------------------------------------------------
-namespace :dist do
+#-------------------------------------------------------------------------------
+if pkg_config = Configuration.for_if_exist?("packaging") then
+
+ require 'gemspec'
+ require 'rake/gempackagetask'
+ require 'rake/contrib/sshpublisher'
- GEM_SPEC = eval(HTAuth::SPEC.to_ruby)
+ namespace :dist do
- Rake::GemPackageTask.new(GEM_SPEC) do |pkg|
- pkg.need_tar = HTAuth::SPEC.need_tar
- pkg.need_zip = HTAuth::SPEC.need_zip
+ Rake::GemPackageTask.new(HTAuth::GEM_SPEC) do |pkg|
+ pkg.need_tar = pkg_config.formats.tgz
+ pkg.need_zip = pkg_config.formats.zip
end
desc "Install as a gem"
task :install => [:clobber, :package] do
- sh "sudo gem install pkg/#{HTAuth::SPEC.full_name}.gem"
+ sh "sudo gem install pkg/#{HTAuth::GEM_SPEC.full_name}.gem"
end
- # uninstall the gem and all executables
desc "Uninstall gem"
task :uninstall do
- sh "sudo gem uninstall #{HTAuth::SPEC.name} -x"
+ sh "sudo gem uninstall -x #{HTAuth::GEM_SPEC.name}"
end
desc "dump gemspec"
task :gemspec do
- puts HTAuth::SPEC.to_ruby
+ puts HTAuth::GEM_SPEC.to_ruby
end
desc "reinstall gem"
- task :reinstall => [:uninstall, :install]
-
- desc "distribute copiously"
- task :copious => [:package] do
- Rake::SshFilePublisher.new('jeremy@copiousfreetime.org',
- '/var/www/vhosts/www.copiousfreetime.org/htdocs/gems/gems',
- 'pkg',"#{HTAuth::SPEC.full_name}.gem").upload
- sh "ssh jeremy@copiousfreetime.org rake -f /var/www/vhosts/www.copiousfreetime.org/htdocs/gems/Rakefile"
- end
+ task :reinstall => [:uninstall, :repackage, :install]
+
+ end
end
View
42 tasks/documentation.rake
@@ -1,25 +1,31 @@
+require 'tasks/config'
+
#-----------------------------------------------------------------------
# Documentation
#-----------------------------------------------------------------------
-namespace :doc do
-
+if rdoc_config = Configuration.for_if_exist?('rdoc') then
+
+ namespace :doc do
+
+ require 'rake/rdoctask'
+
# generating documentation locally
Rake::RDocTask.new do |rdoc|
- rdoc.rdoc_dir = HTAuth::SPEC.local_rdoc_dir
- rdoc.options = HTAuth::SPEC.rdoc_options
- rdoc.rdoc_files = HTAuth::SPEC.rdoc_files
- end
-
- desc "Deploy the RDoc documentation to #{HTAuth::SPEC.remote_rdoc_location}"
- task :deploy => :rerdoc do
- sh "rsync -zav --delete #{HTAuth::SPEC.local_rdoc_dir}/ #{HTAuth::SPEC.remote_rdoc_location}"
- end
-
- if HAVE_HEEL then
- desc "View the RDoc documentation locally"
- task :view => :rdoc do
- sh "heel --root #{HTAuth::SPEC.local_rdoc_dir}"
- end
- end
+ rdoc.rdoc_dir = rdoc_config.output_dir
+ rdoc.options = rdoc_config.options
+ rdoc.rdoc_files = rdoc_config.files
+ rdoc.title = rdoc_config.title
+ rdoc.main = rdoc_config.main_page
+ end
+
+ if rubyforge_config = Configuration.for_if_exist?('rubyforge') then
+ desc "Deploy the RDoc documentation to #{rubyforge_config.rdoc_location}"
+ task :deploy => :rerdoc do
+ sh "rsync -zav --delete #{rdoc_config.output_dir}/ #{rubyforge_config.rdoc_location}"
+ end
+ end
+
+ end
end
+
View
41 tasks/rspec.rake
@@ -1,24 +1,29 @@
-require 'spec/rake/spectask'
-#-----------------------------------------------------------------------
-# Testing - this is either test or spec, include the appropriate one
-#-----------------------------------------------------------------------
-namespace :test do
+require 'tasks/config'
- task :default => :spec
+#--------------------------------------------------------------------------------
+# configuration for running rspec. This shows up as the test:default task
+#--------------------------------------------------------------------------------
+if spec_config = Configuration.for_if_exist?("test") then
+ if spec_config.mode == "spec" then
+ namespace :test do
- Spec::Rake::SpecTask.new do |r|
- r.rcov = true
- r.rcov_dir = HTAuth::SPEC.local_coverage_dir
- r.libs = HTAuth::SPEC.require_paths
- r.spec_opts = %w(--format specdoc --color)
- end
+ task :default => :spec
+
+ require 'spec/rake/spectask'
+ Spec::Rake::SpecTask.new do |r|
+ r.ruby_opts = spec_config.ruby_opts
+ r.libs = [ HTAuth.lib_path,
+ HTAuth.root_dir ]
+ r.spec_files = spec_config.files
+ r.spec_opts = spec_config.options
- if HAVE_HEEL then
- desc "View the code coverage report locally"
- task :coverage => [:spec] do
- sh "heel --root #{HTAuth::SPEC.local_coverage_dir}"
- end
+ if rcov_config = Configuration.for_if_exist?('rcov') then
+ r.rcov = true
+ r.rcov_dir = rcov_config.output_dir
+ r.rcov_opts = rcov_config.rcov_opts
+ end
+ end
end
-
+ end
end
View
43 tasks/rubyforge.rake
@@ -1,43 +0,0 @@
-if HAVE_RUBYFORGE then
- require 'rubyforge'
-
- #-----------------------------------------------------------------------
- # Rubyforge additions to the task library
- #-----------------------------------------------------------------------
- namespace :dist do
- desc "Release files to rubyforge"
- task :rubyforge => [:clean, :package] do
-
- rubyforge = RubyForge.new
-
- # make sure this release doesn't already exist