1 import cherrypy
2 from cherrypy._cpcompat import md5, sha, ntob
3 from cherrypy.lib import httpauth
4
5 from cherrypy.test import helper
6
7
9
11 class Root:
12
13 def index(self):
14 return "This is public."
15 index.exposed = True
16
17 class DigestProtected:
18
19 def index(self):
20 return "Hello %s, you've been authorized." % (
21 cherrypy.request.login)
22 index.exposed = True
23
24 class BasicProtected:
25
26 def index(self):
27 return "Hello %s, you've been authorized." % (
28 cherrypy.request.login)
29 index.exposed = True
30
31 class BasicProtected2:
32
33 def index(self):
34 return "Hello %s, you've been authorized." % (
35 cherrypy.request.login)
36 index.exposed = True
37
38 def fetch_users():
39 return {'test': 'test'}
40
41 def sha_password_encrypter(password):
42 return sha(ntob(password)).hexdigest()
43
44 def fetch_password(username):
45 return sha(ntob('test')).hexdigest()
46
47 conf = {
48 '/digest': {
49 'tools.digest_auth.on': True,
50 'tools.digest_auth.realm': 'localhost',
51 'tools.digest_auth.users': fetch_users
52 },
53 '/basic': {
54 'tools.basic_auth.on': True,
55 'tools.basic_auth.realm': 'localhost',
56 'tools.basic_auth.users': {
57 'test': md5(ntob('test')).hexdigest()
58 }
59 },
60 '/basic2': {
61 'tools.basic_auth.on': True,
62 'tools.basic_auth.realm': 'localhost',
63 'tools.basic_auth.users': fetch_password,
64 'tools.basic_auth.encrypt': sha_password_encrypter
65 }
66 }
67
68 root = Root()
69 root.digest = DigestProtected()
70 root.basic = BasicProtected()
71 root.basic2 = BasicProtected2()
72 cherrypy.tree.mount(root, config=conf)
73 setup_server = staticmethod(setup_server)
74
80
92
104
106 self.getPage("/digest/")
107 self.assertStatus(401)
108
109 value = None
110 for k, v in self.headers:
111 if k.lower() == "www-authenticate":
112 if v.startswith("Digest"):
113 value = v
114 break
115
116 if value is None:
117 self._handlewebError(
118 "Digest authentification scheme was not found")
119
120 value = value[7:]
121 items = value.split(', ')
122 tokens = {}
123 for item in items:
124 key, value = item.split('=')
125 tokens[key.lower()] = value
126
127 missing_msg = "%s is missing"
128 bad_value_msg = "'%s' was expecting '%s' but found '%s'"
129 nonce = None
130 if 'realm' not in tokens:
131 self._handlewebError(missing_msg % 'realm')
132 elif tokens['realm'] != '"localhost"':
133 self._handlewebError(bad_value_msg %
134 ('realm', '"localhost"', tokens['realm']))
135 if 'nonce' not in tokens:
136 self._handlewebError(missing_msg % 'nonce')
137 else:
138 nonce = tokens['nonce'].strip('"')
139 if 'algorithm' not in tokens:
140 self._handlewebError(missing_msg % 'algorithm')
141 elif tokens['algorithm'] != '"MD5"':
142 self._handlewebError(bad_value_msg %
143 ('algorithm', '"MD5"', tokens['algorithm']))
144 if 'qop' not in tokens:
145 self._handlewebError(missing_msg % 'qop')
146 elif tokens['qop'] != '"auth"':
147 self._handlewebError(bad_value_msg %
148 ('qop', '"auth"', tokens['qop']))
149
150
151 base_auth = (
152 'Digest '
153 'username="test", '
154 'realm="wrong realm", '
155 'nonce="%s", '
156 'uri="/digest/", '
157 'algorithm=MD5, '
158 'response="%s", '
159 'qop=auth, '
160 'nc=%s, '
161 'cnonce="1522e61005789929"'
162 )
163
164 auth = base_auth % (nonce, '', '00000001')
165 params = httpauth.parseAuthorization(auth)
166 response = httpauth._computeDigestResponse(params, 'test')
167
168 auth = base_auth % (nonce, response, '00000001')
169 self.getPage('/digest/', [('Authorization', auth)])
170 self.assertStatus(401)
171
172
173 base_auth = (
174 'Digest '
175 'username="test", '
176 'realm="localhost", '
177 'nonce="%s", '
178 'uri="/digest/", '
179 'algorithm=MD5, '
180 'response="%s", '
181 'qop=auth, '
182 'nc=%s, '
183 'cnonce="1522e61005789929"'
184 )
185
186 auth = base_auth % (nonce, '', '00000001')
187 params = httpauth.parseAuthorization(auth)
188 response = httpauth._computeDigestResponse(params, 'test')
189
190 auth = base_auth % (nonce, response, '00000001')
191 self.getPage('/digest/', [('Authorization', auth)])
192 self.assertStatus('200 OK')
193 self.assertBody("Hello test, you've been authorized.")
194