# frozen_string_literal: truerequire_relative'abstract/handler'require_relative'abstract/request'require'digest/md5'require'base64'moduleRackwarn"Rack::Auth::Digest is deprecated and will be removed in Rack 3.1",uplevel: 1moduleAuthmoduleDigest# Rack::Auth::Digest::Nonce is the default nonce generator for the# Rack::Auth::Digest::MD5 authentication handler.## +private_key+ needs to set to a constant string.## +time_limit+ can be optionally set to an integer (number of seconds),# to limit the validity of the generated nonces.classNonceclass<<selfattr_accessor:private_key,:time_limitenddefself.parse(string)new(*Base64.decode64(string).split(' ',2))enddefinitialize(timestamp=Time.now,given_digest=nil)@timestamp,@given_digest=timestamp.to_i,given_digestenddefto_sBase64.encode64("#{@timestamp}#{digest}").stripenddefdigest::Digest::MD5.hexdigest("#{@timestamp}:#{self.class.private_key}")enddefvalid?digest==@given_digestenddefstale?!self.class.time_limit.nil?&&(Time.now.to_i-@timestamp)>self.class.time_limitenddeffresh?!stale?endendclassParams<Hashdefself.parse(str)Params[*split_header_value(str).mapdo|param|k,v=param.split('=',2)[k,dequote(v)]end.flatten]enddefself.dequote(str)# From WEBrick::HTTPUtilsret=(/\A"(.*)"\Z/=~str)?$1:str.dupret.gsub!(/\\(.)/,"\\1")retenddefself.split_header_value(str)str.scan(/\w+\=(?:"[^\"]+"|[^,]+)/n)enddefinitializesuper()yieldselfifblock_given?enddef[](k)superk.to_senddef[]=(k,v)superk.to_s,v.to_sendUNQUOTED=['nc','stale']defto_smapdo|k,v|"#{k}=#{(UNQUOTED.include?(k)?v.to_s:quote(v))}"end.join(', ')enddefquote(str)# From WEBrick::HTTPUtils'"'+str.gsub(/[\\\"]/o,"\\\1")+'"'endendclassRequest<Auth::AbstractRequestdefmethod@env[RACK_METHODOVERRIDE_ORIGINAL_METHOD]||@env[REQUEST_METHOD]enddefdigest?"digest"==schemeenddefcorrect_uri?request.fullpath==urienddefnonce@nonce||=Nonce.parse(params['nonce'])enddefparams@params||=Params.parse(parts.last)enddefrespond_to?(sym,*)superorparams.has_key?sym.to_senddefmethod_missing(sym,*args)returnsuperunlessparams.has_key?(key=sym.to_s)returnparams[key]ifargs.size==0raiseArgumentError,"wrong number of arguments (#{args.size} for 0)"endend# 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.#classMD5<AbstractHandlerattr_accessor:opaqueattr_writer:passwords_hasheddefinitialize(app,realm=nil,opaque=nil,&authenticator)@passwords_hashed=nilifopaque.nil?andrealm.respond_to?:values_atrealm,opaque,@passwords_hashed=realm.values_at:realm,:opaque,:passwords_hashedendsuper(app,realm,&authenticator)@opaque=opaqueenddefpasswords_hashed?!!@passwords_hashedenddefcall(env)auth=Request.new(env)unlessauth.provided?returnunauthorizedendif!auth.digest?||!auth.correct_uri?||!valid_qop?(auth)returnbad_requestendifvalid?(auth)ifauth.nonce.stale?returnunauthorized(challenge(stale: true))elseenv['REMOTE_USER']=auth.usernamereturn@app.call(env)endendunauthorizedendprivateQOP='auth'defparams(hash={})Params.newdo|params|params['realm']=realmparams['nonce']=Nonce.new.to_sparams['opaque']=H(opaque)params['qop']=QOPhash.each{|k,v|params[k]=v}endenddefchallenge(hash={})"Digest #{params(hash)}"enddefvalid?(auth)valid_opaque?(auth)&&valid_nonce?(auth)&&valid_digest?(auth)enddefvalid_qop?(auth)QOP==auth.qopenddefvalid_opaque?(auth)H(opaque)==auth.opaqueenddefvalid_nonce?(auth)auth.nonce.valid?enddefvalid_digest?(auth)pw=@authenticator.call(auth.username)pw&&Rack::Utils.secure_compare(digest(auth,pw),auth.response)enddefmd5(data)::Digest::MD5.hexdigest(data)endalias:H:md5defKD(secret,data)H"#{secret}:#{data}"enddefA1(auth,password)"#{auth.username}:#{auth.realm}:#{password}"enddefA2(auth)"#{auth.method}:#{auth.uri}"enddefdigest(auth,password)password_hash=passwords_hashed??password:H(A1(auth,password))KDpassword_hash,"#{auth.nonce}:#{auth.nc}:#{auth.cnonce}:#{QOP}:#{HA2(auth)}"endendendendend