Class: RoadForest::HTTP::Keychain

Inherits:
Object
  • Object
show all
Defined in:
lib/roadforest/http/keychain.rb

Overview

Manages user credentials for HTTP Basic auth

Constant Summary collapse

ATTEMPT_LIMIT =
5
BASIC_SCHEME =
/basic\s+realm=(?<q>['"])(?<realm>(?:(?!['"]).)*)\k<q>/i

Instance Method Summary collapse

Constructor Details

#initializeKeychain

Returns a new instance of Keychain.



51
52
53
54
55
56
57
# File 'lib/roadforest/http/keychain.rb', line 51

def initialize
  @realm_for_url = {}
  @with_realm = {}
  @sources = []
  @source_enums = Hash.new{|h,k| @sources.each}
  @attempt_enums = Hash.new{|h,k| (0...ATTEMPT_LIMIT).each}
end

Instance Method Details

#add_source(source) ⇒ Object



59
60
61
62
63
# File 'lib/roadforest/http/keychain.rb', line 59

def add_source(source)
  @sources << source
  @source_enums.clear
  @attempt_enums.clear
end

#cached_response(url, realm) ⇒ Object



101
102
103
104
105
# File 'lib/roadforest/http/keychain.rb', line 101

def cached_response(url, realm)
  creds = credentials(url, realm)
  return nil if creds.nil?
  return creds.header_value
end

#canonical_root(url) ⇒ Object



65
66
67
68
69
70
71
# File 'lib/roadforest/http/keychain.rb', line 65

def canonical_root(url)
  url = Addressable::URI.parse(url)
  url.path = "/"
  url.fragment = nil
  url.query = nil
  url.to_s
end

#challenge_response(url, challenge) ⇒ Object



82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/roadforest/http/keychain.rb', line 82

def challenge_response(url, challenge)
  url = stripped_url(url).to_s
  #Future note: the RFC means that the creds selection mechanics are
  #valid for all HTTP WWW-Authenticate reponses
  if (match = BASIC_SCHEME.match(challenge)).nil?
    return nil
  end
  realm = match[:realm]
  @realm_for_url[url] = realm

  cached_response(canonical_root(url), realm) || missing_credentials(url, realm)
end

#credentials(url, realm = nil) ⇒ Object



113
114
115
# File 'lib/roadforest/http/keychain.rb', line 113

def credentials(url, realm = nil)
  @with_realm[[url, realm]]
end

#credentials_for(url) ⇒ Object



107
108
109
110
111
# File 'lib/roadforest/http/keychain.rb', line 107

def credentials_for(url)
  realm = realm_for_url(url)
  url = canonical_root(url)
  credentials(url, realm)
end

#current_source(url, realm) ⇒ Object



137
138
139
# File 'lib/roadforest/http/keychain.rb', line 137

def current_source(url, realm)
  @source_enums[[url, realm]].peek
end

#forget(url, realm) ⇒ Object



133
134
135
# File 'lib/roadforest/http/keychain.rb', line 133

def forget(url, realm)
  @with_realm.delete([url, realm])
end

#missing_credentials(url, realm) ⇒ Object



117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/roadforest/http/keychain.rb', line 117

def missing_credentials(url, realm)
  loop do
    attempt = next_attempt(url, realm)
    creds = current_source(url, realm).respond_to_challenge(url, realm, attempt)
    if creds.nil?
      next_source(url, realm)
    else
      @with_realm[[canonical_root(url), realm]] = creds
      return creds.header_value
    end
  end
  return nil
rescue StopIteration
  nil
end

#next_attempt(url, realm) ⇒ Object



149
150
151
152
153
154
# File 'lib/roadforest/http/keychain.rb', line 149

def next_attempt(url, realm)
  @attempt_enums[[url, realm]].next
rescue StopIteration
  next_source(url, realm)
  retry
end

#next_source(url, realm) ⇒ Object



141
142
143
144
145
146
147
# File 'lib/roadforest/http/keychain.rb', line 141

def next_source(url, realm)
  @attempt_enums.delete([url, realm])
  @source_enums[[url, realm]].next
rescue StopIteration
  @source_enums.delete([url, realm])
  raise
end

#preemptive_response(url) ⇒ Object



95
96
97
98
99
# File 'lib/roadforest/http/keychain.rb', line 95

def preemptive_response(url)
  realm = realm_for_url(url)
  url = canonical_root(url)
  return cached_response(url, realm)
end

#realm_for_url(url) ⇒ Object



156
157
158
159
160
161
162
163
164
# File 'lib/roadforest/http/keychain.rb', line 156

def realm_for_url(url)
  url = stripped_url(url)
  while (realm = @realm_for_url[url.to_s]).nil?
    new_url = url.join("..")
    return realm if new_url == url
    url = new_url
  end
  return realm || :default
end

#stripped_url(url) ⇒ Object



73
74
75
76
77
78
# File 'lib/roadforest/http/keychain.rb', line 73

def stripped_url(url)
  url = Addressable::URI.parse(url)
  url.fragment = nil
  url.query = nil
  url
end