class ApacheAuthTkt

Constants

DIGEST_LENGTHS

Attributes

base64encode[RW]
conf_file[RW]
digest_type[RW]
error[RW]
ipaddr[RW]
lifetime[RW]
secret[RW]

Public Class Methods

new(args) click to toggle source
# File lib/apache_authtkt.rb, line 40
def initialize(args)

   #puts args.inspect

   # set defaults
   if (args.has_key? :ipaddr)
      @ipaddr = args[:ipaddr]
   else
      @ipaddr = '0.0.0.0'
   end

   if (args.has_key? :secret)
      @secret = args[:secret]
   elsif (args.has_key? :conf_file)
      @secret = _get_secret(args[:conf_file])
      if (!@secret.length)
         raise "Can't parse secret from " + args[:conf_file]
      end
   else
      raise "Must pass 'secret' or 'conf_file'"
   end

   if (args[:digest_type])
      @digest_type = args[:digest_type]
   else
      @digest_type = 'md5'
   end

   raise(InvalidDigestTypeError, @digest_type) unless DIGEST_LENGTHS[@digest_type.downcase]

   if (args.has_key? :base64encode)
      @base64encode = args[:base64encode]
   else
      @base64encode = true
   end

   if (args.has_key? :lifetime)
      @lifetime = args[:lifetime]
   else
      # 8 hours.
      @lifetime = 28800
   end

end

Public Instance Methods

_get_secret(filename) click to toggle source
# File lib/apache_authtkt.rb, line 85
def _get_secret(filename)
   # based on http://meso.net/mod_auth_tkt
   if (!File.file? filename)
      raise "#{filename} is not a file"
   end
   secret_str = ''
   open(filename) do |file|
      file.each do |line|
         if line.include? 'TKTAuthSecret'
            secret_str = line.gsub('TKTAuthSecret', '').strip.gsub("\"", '').gsub("'",'')
            break
         end
      end
   end
   return secret_str
end
create_ticket(user_opts={}) click to toggle source

based on meso.net/mod_auth_tkt

# File lib/apache_authtkt.rb, line 114
def create_ticket(user_opts={})
   options = {
      :user       => 'guest',
      :tokens     => '',
      :user_data  => '',
      :ignore_ip  => false,
      :ts         => Time.now.to_i
   }.merge(user_opts)

   timestamp  = options[:ts]
   ip_address = options[:ignore_ip] ? '0.0.0.0' : @ipaddr
   digest = get_digest(timestamp, ip_address, options[:user], options[:tokens], options[:user_data])
   tkt = sprintf("%s%08x%s!%s!%s", digest, timestamp, options[:user], options[:tokens], options[:user_data])

   if (@base64encode)
      tkt = Base64.encode64(tkt).gsub("\n", '').strip
   end

   return tkt

end
expired?(tkt) click to toggle source
# File lib/apache_authtkt.rb, line 199
def expired?(tkt)
   return false if @lifetime.nil?
   parsed = self.parse_ticket(tkt)
   if (!parsed)
      return false
   end
   !(parsed[:ts] + @lifetime >= Time.now.to_i)
end
get_digest(ts, ipaddr, user, tokens, data) click to toggle source
# File lib/apache_authtkt.rb, line 136
def get_digest(ts, ipaddr, user, tokens, data)
   ipts = [ip2long(ipaddr), ts].pack("NN")
   digest0 = nil
   digest  = nil
   raw     = ipts + @secret + user + "\0" + tokens + "\0" + data
   digest0 = digest_klass.hexdigest(raw)
   digest  = digest_klass.hexdigest(digest0 + @secret)
   return digest
end
ip2long(ip) click to toggle source

from meso.net/mod_auth_tkt function adapted according to php: generates an IPv4 Internet network address from its Internet standard format (dotted string) representation.

# File lib/apache_authtkt.rb, line 105
def ip2long(ip)
   long = 0
   ip.split( /\./ ).reverse.each_with_index do |x, i|
      long += x.to_i << ( i * 8 )
   end
   long
end
parse_ticket(tkt) click to toggle source
# File lib/apache_authtkt.rb, line 159
def parse_ticket(tkt)
   # strip possible lead/trail quotes
   tkt = tkt.gsub(/^"|"$/,'')

   # test if base64 encoded before decoding
   if (@base64encode && tkt.length % 4 == 0 && tkt =~ /^[A-Za-z0-9+\/=]+\Z/)
      tkt = Base64.decode64(tkt)
   end

   # sanity checks
   if (tkt.length < 40)
      @error = "ticket string too short"
      return false
   end
   if (!tkt.index('!'))
      @error = "ticket missing !"
      return false
   end

   # parse it
   parts = tkt.split(/\!/)
   parsed = {:tokens => '', :data => ''}
   digest_length = DIGEST_LENGTHS[@digest_type.downcase]
   if (packed = parts[0].match("^(.{#{digest_length}})(.{8})(.+)$"))
      parsed[:digest] = packed[1]
      parsed[:ts]     = packed[2].hex
      parsed[:user]    = packed[3]
      if (parts.length == 3)
         parsed[:tokens] = parts[1]
         parsed[:data]   = parts[2]
      elsif (parts.length == 2)
         parsed[:tokens] = parts[1]
      end
   else
      @error = "invalid ticket pattern"
      return false
   end
   return parsed
end
validate_ticket(tkt, ipaddr='0.0.0.0') click to toggle source
# File lib/apache_authtkt.rb, line 146
def validate_ticket(tkt, ipaddr='0.0.0.0')
   parsed = parse_ticket(tkt)
   if (!parsed)
      return false
   end
   expected_digest = get_digest(parsed[:ts], ipaddr, parsed[:user], parsed[:tokens], parsed[:data])
   if (expected_digest == parsed[:digest])
      return parsed
   else
      return false
   end
end

Private Instance Methods

digest_klass() click to toggle source
# File lib/apache_authtkt.rb, line 210
def digest_klass
  Digest.const_get(@digest_type.upcase)
end