diff options
author | Sylvain Hellegouarch <sh@defuze.org> | 2006-09-11 19:53:21 +0000 |
---|---|---|
committer | Sylvain Hellegouarch <sh@defuze.org> | 2006-09-11 19:53:21 +0000 |
commit | 4cd65390c6f0b88694b6e01ed333bf702ad7cee8 (patch) | |
tree | 12dbf61d540a5d897d4c13b5b5d76e130f0858cc | |
parent | 06d8ffcd7c9e19c220bb22c0957df6df9bf56c1d (diff) | |
download | cherrypy-git-4cd65390c6f0b88694b6e01ed333bf702ad7cee8.tar.gz |
Digest and basic auth can now take a callable which must return a dict with user credentials so that it can fetch those from a database for instance.
Basic takes also an encrypt parameter which must be a callable that will encrypt the password sent back the user-agent. So that passwords can be stored encrypted on the server.
-rw-r--r-- | cherrypy/lib/auth.py | 24 | ||||
-rw-r--r-- | cherrypy/lib/httpauth.py | 10 | ||||
-rw-r--r-- | cherrypy/test/test_httpauth.py | 31 |
3 files changed, 42 insertions, 23 deletions
diff --git a/cherrypy/lib/auth.py b/cherrypy/lib/auth.py index fed8820f..4f2eb053 100644 --- a/cherrypy/lib/auth.py +++ b/cherrypy/lib/auth.py @@ -3,31 +3,41 @@ import cherrypy from httpauth import parseAuthorization, checkResponse, basicAuth, digestAuth -def check_auth(users): +def check_auth(users, encrypt=None): """If an authorization header contains credentials, return True, else False.""" if 'authorization' in cherrypy.request.headers: # make sure the provided credentials are correctly set ah = parseAuthorization(cherrypy.request.headers['authorization']) if ah is None: raise cherrypy.HTTPError(400, 'Bad Request') - + + if not encrypt: + encrypt = lambda x: x + + if callable(users): + users = users() # expect it to return a dictionary + + if not isinstance(users, dict): + raise ValueError, "Authentication users must be passed contained in a dictionary" + # fetch the user password password = users.get(ah["username"], None) # validate the authorization by re-computing it here # and compare it with what the user-agent provided - if checkResponse(ah, password, method=cherrypy.request.method): + if checkResponse(ah, password, method=cherrypy.request.method, encrypt=encrypt): return True return False -def basic_auth(realm, users):
+def basic_auth(realm, users, encrypt=None):
"""If auth fails, raise 401 with a basic authentication header.
realm: a string containing the authentication realm.
- users: a dict of the form: {username: password}.
+ users: a dict of the form: {username: password} or a callable returning a dict. + encrypt: callable used to encrypt the password returned from the user-agent. """ - if check_auth(users): + if check_auth(users, encrypt): return # inform the user-agent this path is protected @@ -39,7 +49,7 @@ def digest_auth(realm, users): """If auth fails, raise 401 with a digest authentication header. realm: a string containing the authentication realm.
- users: a dict of the form: {username: password}.
+ users: a dict of the form: {username: password} or a callable returning a dict.
""" if check_auth(users): return diff --git a/cherrypy/lib/httpauth.py b/cherrypy/lib/httpauth.py index dac20813..8aab3cc2 100644 --- a/cherrypy/lib/httpauth.py +++ b/cherrypy/lib/httpauth.py @@ -295,7 +295,7 @@ def _computeDigestResponse(auth_map, password, method = "GET", A1 = None,**kwarg return KD(H_A1, request)
-def _checkDigestResponse(auth_map, password, method = "GET", A1 = None,**kwargs):
+def _checkDigestResponse(auth_map, password, method = "GET", A1 = None, **kwargs):
"""This function is used to verify the response given by the client when
he tries to authenticate.
Optional arguments:
@@ -308,15 +308,15 @@ def _checkDigestResponse(auth_map, password, method = "GET", A1 = None,**kwargs) return response == auth_map["response"]
-def _checkBasicResponse (auth_map, password, method='GET', **kwargs):
- return auth_map["password"] == password
+def _checkBasicResponse (auth_map, password, method='GET', encrypt=None, **kwargs):
+ return encrypt(auth_map["password"]) == password
AUTH_RESPONSES = {
"basic": _checkBasicResponse,
"digest": _checkDigestResponse,
}
-def checkResponse (auth_map, password, method = "GET", **kwargs):
+def checkResponse (auth_map, password, method = "GET", encrypt=None, **kwargs):
"""'checkResponse' compares the auth_map with the password and optionally
other arguments that each implementation might need.
@@ -335,7 +335,7 @@ def checkResponse (auth_map, password, method = "GET", **kwargs): """
global AUTH_RESPONSES
checker = AUTH_RESPONSES[auth_map["auth_scheme"]]
- return checker (auth_map, password, method, **kwargs)
+ return checker (auth_map, password, method=method, encrypt=encrypt, **kwargs)
diff --git a/cherrypy/test/test_httpauth.py b/cherrypy/test/test_httpauth.py index 0341eed9..83e4e125 100644 --- a/cherrypy/test/test_httpauth.py +++ b/cherrypy/test/test_httpauth.py @@ -1,6 +1,8 @@ from cherrypy.test import test test.prefer_parent_path() +import md5 + import cherrypy from cherrypy.lib import httpauth @@ -20,12 +22,19 @@ def setup_server(): return "This is protected by Basic auth." index.exposed = True + def md5_encrypt(data): + return md5.new(data).hexdigest() + + def fetch_users(): + return {'test': 'test'} + conf = {'/digest': {'tools.digestauth.on': True, 'tools.digestauth.realm': 'localhost', - 'tools.digestauth.users': {'test': 'test'}}, + 'tools.digestauth.users': fetch_users}, '/basic': {'tools.basicauth.on': True, 'tools.basicauth.realm': 'localhost', - 'tools.basicauth.users': {'test': 'test'}}} + 'tools.basicauth.users': {'test': md5_encrypt('test')}, + 'tools.basicauth.encrypt': md5_encrypt}} root = Root() root.digest = DigestProtected() root.basic = BasicProtected() @@ -96,18 +105,18 @@ class HTTPAuthTest(helper.CPWebCase): self._handlewebError(bad_value_msg % ('qop', '"auth"', tokens['qop'])) # now let's see if what - base_auth = 'Digest username="test", realm="localhost", nonce="%s", uri="/digest/", algorithm=MD5, response="%s", qop=auth, nc=%s, cnonce="1522e61005789929"' + base_auth = 'Digest username="test", realm="localhost", nonce="%s", uri="/digest/", algorithm=MD5, response="%s", qop=auth, nc=%s, cnonce="1522e61005789929"' - auth = base_auth % (nonce, '', '00000001') + auth = base_auth % (nonce, '', '00000001') - params = httpauth.parseAuthorization(auth) - response = httpauth._computeDigestResponse(params, 'test') + params = httpauth.parseAuthorization(auth) + response = httpauth._computeDigestResponse(params, 'test') + + auth = base_auth % (nonce, response, '00000001') + self.getPage('/digest/', [('Authorization', auth)]) + self.assertStatus('200 OK') + self.assertBody('This is protected by Digest auth.') - auth = base_auth % (nonce, response, '00000001') - self.getPage('/digest/', [('Authorization', auth)]) - self.assertStatus('200 OK') - self.assertBody('This is protected by Digest auth.') - if __name__ == "__main__": setup_server() helper.testmain() |