1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 '''
32 HttxLib authcache object to enable cached (or partially cached) authentication
33 answer to authentication requests
34
35 A namedtuple I{AuthCache} is defined for storage in the cache with the
36 following fields:
37
38 - {headername} if WWW-Authenticate or Proxy-Authenticate was sent
39 - {scheme} if Basic, Digest or other
40 - {answer} Previous answer
41 - {cachedata} Specific data cached to be used if a new auth request is seen
42 '''
43
44 from collections import defaultdict, namedtuple
45 from copy import deepcopy
46
47 from httxobject import HttxObject
48
49
50 AuthCache = namedtuple('AuthCache', 'headername, scheme, answer, cachedata')
51
52
54 '''
55 An object that caches data from and for authentication requests
56
57 @ivar cache: holds the cache entries
58 @type cache: dict
59 @ivar noncecache: holds specifically sequestial nonce values for
60 digest authentication
61 @type noncecache: defaultdict(int)
62 '''
63
65 '''
66 Constructor. It delegates construction to the base class
67 L{HttxObject} and initializes the member variables
68 '''
69 HttxObject.__init__(self)
70 self.cache = dict()
71 self.noncecache = defaultdict(int)
72
73
75 '''
76 Deepcopy support.
77
78 @param memo: standard __deepcopy__ parameter to avoid circular references
79 @type memo: dict
80 @return: a cloned object
81 @rtype: L{HttxAuthCache}
82 '''
83 clone = self.__class__()
84 with self.lock:
85 clone.cache = deepcopy(self.cache, memo)
86 clone.noncecache = deepcopy(self.noncecache, memo)
87
88 return clone
89
90
92 '''
93 Get a new nonce_count value for nonce
94
95 This will increment the cached value and return and cache the
96 incremented value
97
98 @param nonce: nonce value in digest auth requests
99 @type nonce: str
100 @return: a sequentally incremented nonce_count
101 @rtype: int
102 '''
103 with self.lock:
104 self.noncecache[nonce] += 1
105 return self.noncecache[nonce]
106
107
108 - def set(self, url, headername, scheme, answer, cachedata):
109 '''
110 Set a new entry in the cache
111
112 @param headername: WWW-Authenticate or Proxy-Authenticate as sent
113 @type headername: str
114 @param scheme: Basic, Digest or other types of auth
115 @type scheme: str
116 @param answer: Answer sent to the server
117 @type answer: str
118 @param cachedata: Specific data cached to be used if a new auth request is seen
119 @type cachedata: str
120 '''
121 with self.lock:
122 self.cache[url] = AuthCache(headername, scheme, answer, cachedata)
123
124
125 - def get(self, url, opaque=False):
126 '''
127 Get a cache entry for a url
128
129 The search is done hierarchically, because once authorized in an url
130 all sub-urls (same domain and same initial) path will likely send
131 the same authorization request
132
133 @param url: url for the http request
134 @type url: str
135 @param opaque: if no assumption has to be made about the stored data
136 and just cachedata has to be returned
137 @type opaque: bool (Default: False)
138 @return: A tuple with the headername to be sent and the header content
139 Both values can be None to indicate that no entry was found
140 @rtype: tuple
141 '''
142 urllen = len(url)
143 authcache = None
144
145 with self.lock:
146 cachekeys = self.cache.keys()
147 cachekeys.sort(reverse=True)
148
149 for cachekey in cachekeys:
150 if len(cachekey) > urllen or not url.startswith(cachekey):
151 continue
152
153 with self.lock:
154 authcache = self.cache[cachekey]
155 break
156
157 headername = None
158 headerval = None
159
160 if authcache:
161 if opaque:
162
163 return authcache.cachedata
164
165 elif authcache.scheme == 'basic':
166 authanswer = authcache.answer
167
168 elif authcache.scheme == 'digest':
169 authchallenge = parse_keqv_list(parse_http_list(authcache.answer))
170
171 nonce = authchallenge['nonce']
172 nonce_count = self.getnoncecount(nonce)
173 authanswer = authdigest(None, authcache.cachedata, authchallenge, httxreq, nonce_count)
174
175 headerval = '%s %s' % (authcache.scheme, authanswer)
176 headername = authcache.headername
177
178 if opaque:
179 return None
180
181 return (headername, headerval)
182