Your IP : 18.216.57.57
# frozen_string_literal: true
require_relative '../abstract/handler'
require_relative 'request'
require_relative 'params'
require_relative 'nonce'
require 'digest/md5'
module Rack
module Auth
module Digest
# Rack::Auth::Digest::MD5 implements the MD5 algorithm version of
# HTTP Digest Authentication, as per RFC 2617.
#
# Initialize with the [Rack] application that you want protecting,
# and a block that looks up a plaintext password for a given username.
#
# +opaque+ needs to be set to a constant base64/hexadecimal string.
#
class MD5 < AbstractHandler
attr_accessor :opaque
attr_writer :passwords_hashed
def initialize(app, realm = nil, opaque = nil, &authenticator)
@passwords_hashed = nil
if opaque.nil? and realm.respond_to? :values_at
realm, opaque, @passwords_hashed = realm.values_at :realm, :opaque, :passwords_hashed
end
super(app, realm, &authenticator)
@opaque = opaque
end
def passwords_hashed?
!!@passwords_hashed
end
def call(env)
auth = Request.new(env)
unless auth.provided?
return unauthorized
end
if !auth.digest? || !auth.correct_uri? || !valid_qop?(auth)
return bad_request
end
if valid?(auth)
if auth.nonce.stale?
return unauthorized(challenge(stale: true))
else
env['REMOTE_USER'] = auth.username
return @app.call(env)
end
end
unauthorized
end
private
QOP = 'auth'
def params(hash = {})
Params.new do |params|
params['realm'] = realm
params['nonce'] = Nonce.new.to_s
params['opaque'] = H(opaque)
params['qop'] = QOP
hash.each { |k, v| params[k] = v }
end
end
def challenge(hash = {})
"Digest #{params(hash)}"
end
def valid?(auth)
valid_opaque?(auth) && valid_nonce?(auth) && valid_digest?(auth)
end
def valid_qop?(auth)
QOP == auth.qop
end
def valid_opaque?(auth)
H(opaque) == auth.opaque
end
def valid_nonce?(auth)
auth.nonce.valid?
end
def valid_digest?(auth)
pw = @authenticator.call(auth.username)
pw && Rack::Utils.secure_compare(digest(auth, pw), auth.response)
end
def md5(data)
::Digest::MD5.hexdigest(data)
end
alias :H :md5
def KD(secret, data)
H "#{secret}:#{data}"
end
def A1(auth, password)
"#{auth.username}:#{auth.realm}:#{password}"
end
def A2(auth)
"#{auth.method}:#{auth.uri}"
end
def digest(auth, password)
password_hash = passwords_hashed? ? password : H(A1(auth, password))
KD password_hash, "#{auth.nonce}:#{auth.nc}:#{auth.cnonce}:#{QOP}:#{H A2(auth)}"
end
end
end
end
end