diff --git a/daemons/ganeti-rapi b/daemons/ganeti-rapi index 8ef936502c643c70a173774c53d57165bb243ff9..a5092e227b29b9a4d5c8ed9e7e167c6c438fae52 100755 --- a/daemons/ganeti-rapi +++ b/daemons/ganeti-rapi @@ -118,17 +118,18 @@ class RemoteApiHttpServer(http.auth.HttpServerRequestAuthentication, req.private = ctx + # Check for expected attributes + assert req.private.handler + assert req.private.handler_fn + assert req.private.handler_access is not None + return req.private - def GetAuthRealm(self, req): - """Override the auth realm for queries. + def AuthenticationRequired(self, req): + """Determine whether authentication is required. """ - ctx = self._GetRequestContext(req) - if ctx.handler_access: - return self.AUTH_REALM - else: - return None + return bool(self._GetRequestContext(req).handler_access) def Authenticate(self, req, username, password): """Checks whether a user can access a resource. diff --git a/lib/http/auth.py b/lib/http/auth.py index a13c60f5d05849761f05c54abdd5fb0af416d47d..69633d0e7b84dfbb846c8a68e0d9320d060befcd 100644 --- a/lib/http/auth.py +++ b/lib/http/auth.py @@ -78,7 +78,7 @@ def _FormatAuthHeader(scheme, params): class HttpServerRequestAuthentication(object): # Default authentication realm - AUTH_REALM = None + AUTH_REALM = "Unspecified" # Schemes for passwords _CLEARTEXT_SCHEME = "{CLEARTEXT}" @@ -87,13 +87,12 @@ 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 """ @@ -102,6 +101,18 @@ class HttpServerRequestAuthentication(object): # pylint: disable-msg=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 + + """ + return False + def PreHandleRequest(self, req): """Called before a request is handled. @@ -109,15 +120,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): @@ -255,7 +267,7 @@ 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.update("%s:%s:%s" % (username, realm, password)) diff --git a/test/ganeti.http_unittest.py b/test/ganeti.http_unittest.py index 606fc741689002b854b669fa4dea9e35b7ccf235..ebb00a923a551a9d27e9ea4afe80e0edd19eb70f 100755 --- a/test/ganeti.http_unittest.py +++ b/test/ganeti.http_unittest.py @@ -154,8 +154,8 @@ class TestAuth(unittest.TestCase): self.assert_(tvbap("This is only a test", "user", "pw", "{HA1}92ea58ae804481498c257b2f65561a17")) - self.failIf(tvbap(None, "user", "pw", - "{HA1}92ea58ae804481498c257b2f65561a17")) + self.failUnlessRaises(AssertionError, tvbap, None, "user", "pw", + "{HA1}92ea58ae804481498c257b2f65561a17") self.failIf(tvbap("Admin area", "user", "pw", "{HA1}92ea58ae804481498c257b2f65561a17")) self.failIf(tvbap("This is only a test", "someone", "pw",