diff --git a/examples/echo/echo/echo.ini b/examples/echo/echo/echo.ini index aecb3f4bc9..b564760108 100644 --- a/examples/echo/echo/echo.ini +++ b/examples/echo/echo/echo.ini @@ -25,6 +25,7 @@ paste.filter_factory = keystone.middleware.auth_token:filter_factory auth_host = 127.0.0.1 auth_port = 5001 auth_protocol = http +auth_uri = http://localhost:5000/ ;how to authenticate to the auth service for priviledged operations ;like validate token admin_token = 999888777666 diff --git a/examples/echo/echo/echo_remote.ini b/examples/echo/echo/echo_remote.ini index a429485d84..ba5e46102e 100644 --- a/examples/echo/echo/echo_remote.ini +++ b/examples/echo/echo/echo_remote.ini @@ -14,6 +14,6 @@ paste.filter_factory = keystone.middleware.remoteauth:filter_factory ; (otherwise we redirect call) remote_auth_pass = dTpw ;where to redirect untrusted calls to -auth_location = http://127.0.0.1:5001/ +proxy_location = http://127.0.0.1:8090/ diff --git a/examples/paste/auth_token.ini b/examples/paste/auth_token.ini index b2899e3694..bb37241bc2 100644 --- a/examples/paste/auth_token.ini +++ b/examples/paste/auth_token.ini @@ -6,9 +6,10 @@ paste.app_factory = auth_token:app_factory auth_protocol = http auth_host = 127.0.0.1 auth_port = 5001 +auth_uri = http://127.0.0.1:5000/ admin_token = 999888777666 -delay_auth_decision = 0 +delay_auth_decision = 1 service_protocol = http service_host = 127.0.0.1 diff --git a/examples/paste/glance-api.conf b/examples/paste/glance-api.conf index 6b1d8801e8..26dea5ee3c 100644 --- a/examples/paste/glance-api.conf +++ b/examples/paste/glance-api.conf @@ -82,6 +82,7 @@ service_port = 808 auth_host = 127.0.0.1 auth_port = 5001 auth_protocol = http +auth_uri = http://127.0.0.1:5000/ admin_token = 999888777666 # Allows anonymous access diff --git a/examples/paste/glance-registry.conf b/examples/paste/glance-registry.conf index a66323cde8..378705e58d 100644 --- a/examples/paste/glance-registry.conf +++ b/examples/paste/glance-registry.conf @@ -46,6 +46,7 @@ service_port = 808 auth_host = 127.0.0.1 auth_port = 5001 auth_protocol = http +auth_uri = http://127.0.0.1:5000/ admin_token = 999888777666 # Allows anonymous access diff --git a/examples/paste/nova-api-paste.ini b/examples/paste/nova-api-paste.ini index 7cc75ffea9..bf7ae41fea 100644 --- a/examples/paste/nova-api-paste.ini +++ b/examples/paste/nova-api-paste.ini @@ -90,6 +90,7 @@ service_port = 808 auth_host = 127.0.0.1 auth_port = 5001 auth_protocol = http +auth_uri = http://127.0.0.1:5000/ admin_token = 999888777666 [filter:auth_shim] diff --git a/keystone/middleware/auth_token.py b/keystone/middleware/auth_token.py index 1dd3e21eaa..2b8fce5ef4 100755 --- a/keystone/middleware/auth_token.py +++ b/keystone/middleware/auth_token.py @@ -58,6 +58,7 @@ from paste.deploy import loadapp from urlparse import urlparse from webob.exc import HTTPUnauthorized, HTTPUseProxy from webob.exc import Request, Response +import tools.tracer # @UnusedImport # module runs on import from keystone.common.bufferedhttp import http_connect_raw as http_connect @@ -99,18 +100,22 @@ class AuthProtocol(object): self.auth_host = conf.get('auth_host') self.auth_port = int(conf.get('auth_port')) self.auth_protocol = conf.get('auth_protocol', 'https') - self.auth_location = "%s://%s:%s" % (self.auth_protocol, - self.auth_host, - self.auth_port) + + # where to tell clients to find the auth service (default to url + # constructed based on endpoint we have for the service to use) + self.auth_location = conf.get('auth_uri', + "%s://%s:%s" % (self.auth_protocol, + self.auth_host, + self.auth_port)) # Credentials used to verify this component with the Auth service since - # validating tokens is a priviledged call + # validating tokens is a privileged call self.admin_token = conf.get('admin_token') def __init__(self, app, conf): """ Common initialization code """ - #TODO(ziad): maybe we rafactor this into a superclass + #TODO(ziad): maybe we refactor this into a superclass self._init_protocol_common(app, conf) # Applies to all protocols self._init_protocol(app, conf) # Specific to this protocol @@ -174,8 +179,8 @@ class AuthProtocol(object): # NOTE(todd): unused self.expanded = True - #Send request downstream - return self._forward_request() + #Send request downstream + return self._forward_request() # NOTE(todd): unused def get_admin_auth_token(self, username, password, tenant): @@ -203,8 +208,10 @@ class AuthProtocol(object): def _reject_request(self): """Redirect client to auth server""" - return HTTPUseProxy(location=self.auth_location)(self.env, - self.start_response) + return HTTPUnauthorized("Authentication required", + [("WWW-Authenticate", + "Keystone uri='%s'" % self.auth_location)])(self.env, + self.start_response) def _reject_claims(self): """Client sent bad claims""" @@ -297,6 +304,7 @@ class AuthProtocol(object): # We are forwarding to a remote service (no downstream WSGI app) req = Request(self.proxy_headers) parsed = urlparse(req.url) + conn = http_connect(self.service_host, self.service_port, req.method, @@ -305,10 +313,20 @@ class AuthProtocol(object): ssl=(self.service_protocol == 'https')) resp = conn.getresponse() data = resp.read() + #TODO(ziad): use a more sophisticated proxy # we are rewriting the headers now - return Response(status=resp.status, body=data)(self.proxy_headers, - self.start_response) + + if resp.status == 401 or resp.status == 305: + # Add our own headers to the list + headers = [("WWW_AUTHENTICATE", + "Keystone uri='%s'" % self.auth_location)] + return Response(status=resp.status, body=data, + headerlist=headers)(self.env, + self.start_response) + else: + return Response(status=resp.status, body=data)(self.env, + self.start_response) def filter_factory(global_conf, **local_conf): diff --git a/keystone/middleware/remoteauth.py b/keystone/middleware/remoteauth.py index 5919abd67f..7050180738 100644 --- a/keystone/middleware/remoteauth.py +++ b/keystone/middleware/remoteauth.py @@ -69,8 +69,8 @@ class RemoteAuth(object): # app is the next app in WSGI chain - eventually the OpenStack service self.app = app self.conf = conf - # where to redirect untrusted requests to go and auth - self.auth_location = conf.get('auth_location') + # where to redirect untrusted requests to + self.proxy_location = conf.get('proxy_location') # secret that will tell us a request is coming from a trusted auth # component self.remote_auth_pass = conf.get('remote_auth_pass') @@ -81,7 +81,10 @@ class RemoteAuth(object): # Authenticate the Auth component itself. headers = [('www-authenticate', 'Basic realm="API Auth"')] if 'HTTP_AUTHORIZATION' not in env: - return HTTPUnauthorized(headers=headers)(env, start_response) + # Redirect to proxy (auth component) and show that basic auth is + # required + return HTTPUseProxy(location=self.proxy_location, + headers=headers)(env, start_response) else: auth_type, encoded_creds = env['HTTP_AUTHORIZATION'].split(None, 1) if encoded_creds != self.remote_auth_pass: @@ -89,8 +92,7 @@ class RemoteAuth(object): # Make sure that the user has been authenticated by the Auth Service if 'HTTP_X_AUTHORIZATION' not in env: - return HTTPUseProxy(location=self.auth_location)(env, - start_response) + return HTTPUnauthorized()(env, start_response) return self.app(env, start_response)