import base64
import binascii
-from ganeti import utils
+from ganeti import compat
from ganeti import http
+from ganeti import utils
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"
class HttpServerRequestAuthentication(object):
# Default authentication realm
- AUTH_REALM = None
+ AUTH_REALM = "Unspecified"
# Schemes for passwords
_CLEARTEXT_SCHEME = "{CLEARTEXT}"
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.
@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):
"""
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
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())
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::
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():
- line = line.strip()
-
- # Ignore empty lines and comments
- if not line or line.startswith("#"):
- continue
-
+ for line in utils.FilterEmptyLinesAndComments(contents):
parts = line.split(None, 2)
if len(parts) < 2:
# Invalid line
+ # TODO: Return line number from FilterEmptyLinesAndComments
+ logging.warning("Ignoring non-comment line with less than two fields")
continue
name = parts[0]
if len(parts) >= 3:
for part in parts[2].split(","):
options.append(part.strip())
+ else:
+ logging.warning("Ignoring values for user '%s': %s", name, parts[3:])
users[name] = PasswordFileUser(name, password, options)