X-Git-Url: https://code.grnet.gr/git/ganeti-local/blobdiff_plain/2d54e29cd20fa61ff4337f5332a77075cb9fb7b5..c4929a8bcca4a43dc6434394a91a8ea67d854844:/lib/http/auth.py diff --git a/lib/http/auth.py b/lib/http/auth.py index a13c60f..5a7bfac 100644 --- a/lib/http/auth.py +++ b/lib/http/auth.py @@ -27,17 +27,11 @@ import re import base64 import binascii -from ganeti import utils +from ganeti import compat from ganeti import http from cStringIO import StringIO -try: - from hashlib import md5 -except ImportError: - from md5 import new as md5 - - # Digest types from RFC2617 HTTP_BASIC_AUTH = "Basic" HTTP_DIGEST_AUTH = "Digest" @@ -78,7 +72,7 @@ def _FormatAuthHeader(scheme, params): class HttpServerRequestAuthentication(object): # Default authentication realm - AUTH_REALM = None + AUTH_REALM = "Unspecified" # Schemes for passwords _CLEARTEXT_SCHEME = "{CLEARTEXT}" @@ -87,21 +81,34 @@ class HttpServerRequestAuthentication(object): def GetAuthRealm(self, req): """Returns the authentication realm for a request. - MAY be overridden by a subclass, which then can return different realms for - different paths. Returning "None" means no authentication is needed for a - request. + May be overridden by a subclass, which then can return different realms for + different paths. @type req: L{http.server._HttpServerRequest} @param req: HTTP request context - @rtype: str or None + @rtype: string @return: Authentication realm """ # today we don't have per-request filtering, but we might want to # add it in the future - # pylint: disable-msg=W0613 + # pylint: disable=W0613 return self.AUTH_REALM + def AuthenticationRequired(self, req): + """Determines whether authentication is required for a request. + + To enable authentication, override this function in a subclass and return + C{True}. L{AUTH_REALM} must be set. + + @type req: L{http.server._HttpServerRequest} + @param req: HTTP request context + + """ + # Unused argument, method could be a function + # pylint: disable=W0613,R0201 + return False + def PreHandleRequest(self, req): """Called before a request is handled. @@ -109,15 +116,16 @@ class HttpServerRequestAuthentication(object): @param req: HTTP request context """ - realm = self.GetAuthRealm(req) - # Authentication not required, and no credentials given? - if realm is None and http.HTTP_AUTHORIZATION not in req.request_headers: + if not (self.AuthenticationRequired(req) or + (req.request_headers and + http.HTTP_AUTHORIZATION in req.request_headers)): return - if realm is None: # in case we don't require auth but someone - # passed the crendentials anyway - realm = "Unspecified" + realm = self.GetAuthRealm(req) + + if not realm: + raise AssertionError("No authentication realm") # Check "Authorization" header if self._CheckAuthorization(req): @@ -190,7 +198,7 @@ class HttpServerRequestAuthentication(object): """ try: - creds = base64.b64decode(in_data.encode('ascii')).decode('ascii') + creds = base64.b64decode(in_data.encode("ascii")).decode("ascii") except (TypeError, binascii.Error, UnicodeError): logging.exception("Error when decoding Basic authentication credentials") return False @@ -255,9 +263,9 @@ class HttpServerRequestAuthentication(object): realm = self.GetAuthRealm(req) if not realm: # There can not be a valid password for this case - return False + raise AssertionError("No authentication realm") - expha1 = md5() + expha1 = compat.md5_hash() expha1.update("%s:%s:%s" % (username, realm, password)) return (expected_password.lower() == expha1.hexdigest().lower()) @@ -278,8 +286,8 @@ class PasswordFileUser(object): self.options = options -def ReadPasswordFile(file_name): - """Reads a password file. +def ParsePasswordFile(contents): + """Parses the contents of a password file. Lines in the password file are of the following format:: @@ -289,15 +297,15 @@ def ReadPasswordFile(file_name): options are optional and separated by comma (','). Empty lines and comments ('#') are ignored. - @type file_name: str - @param file_name: Path to password file + @type contents: str + @param contents: Contents of password file @rtype: dict @return: Dictionary containing L{PasswordFileUser} instances """ users = {} - for line in utils.ReadFile(file_name).splitlines(): + for line in contents.splitlines(): line = line.strip() # Ignore empty lines and comments