getting pep8-y with it
This commit is contained in:
parent
4006479c24
commit
f606f6b4f5
|
@ -43,6 +43,7 @@ if os.path.exists(os.path.join(POSSIBLE_TOPDIR, 'echo', '__init__.py')):
|
||||||
Echo: a dummy service for OpenStack auth testing. It returns request info.
|
Echo: a dummy service for OpenStack auth testing. It returns request info.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class EchoApp(object):
|
class EchoApp(object):
|
||||||
def __init__(self, environ, start_response):
|
def __init__(self, environ, start_response):
|
||||||
self.envr = environ
|
self.envr = environ
|
||||||
|
@ -57,9 +58,10 @@ class EchoApp(object):
|
||||||
# We assume the request is coming from a trusted source. Middleware
|
# We assume the request is coming from a trusted source. Middleware
|
||||||
# is used to perform that validation.
|
# is used to perform that validation.
|
||||||
if 'HTTP_X_AUTHORIZATION' not in self.envr:
|
if 'HTTP_X_AUTHORIZATION' not in self.envr:
|
||||||
self.start('401 Unauthorized', [('Content-Type', 'application/json')])
|
self.start('401 Unauthorized', [('Content-Type',
|
||||||
|
'application/json')])
|
||||||
return iter(["401 Unauthorized"])
|
return iter(["401 Unauthorized"])
|
||||||
|
|
||||||
if 'HTTP_X_IDENTITY_STATUS' not in self.envr:
|
if 'HTTP_X_IDENTITY_STATUS' not in self.envr:
|
||||||
identity_status = "Unknown"
|
identity_status = "Unknown"
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -47,11 +47,13 @@ def call_service(token):
|
||||||
ret = data
|
ret = data
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
def hack_attempt(token):
|
def hack_attempt(token):
|
||||||
# Injecting headers in the request
|
# Injecting headers in the request
|
||||||
headers = {"X-Auth-Token": token,
|
headers = {"X-Auth-Token": token,
|
||||||
"Content-type": "application/json",
|
"Content-type": "application/json",
|
||||||
"Accept": "text/json\nX_AUTHORIZATION: someone else\nX_IDENTITY_STATUS: Confirmed\nINJECTED_HEADER: aha!"}
|
"Accept": "text/json\nX_AUTHORIZATION: someone else\n"
|
||||||
|
"X_IDENTITY_STATUS: Confirmed\nINJECTED_HEADER: aha!"}
|
||||||
params = '{"ping": "abcdefg"}'
|
params = '{"ping": "abcdefg"}'
|
||||||
conn = httplib.HTTPConnection("localhost:8090")
|
conn = httplib.HTTPConnection("localhost:8090")
|
||||||
print headers
|
print headers
|
||||||
|
@ -70,24 +72,24 @@ if __name__ == '__main__':
|
||||||
obj = json.loads(auth)
|
obj = json.loads(auth)
|
||||||
token = obj["auth"]["token"]["id"]
|
token = obj["auth"]["token"]["id"]
|
||||||
print "Token obtained:", token
|
print "Token obtained:", token
|
||||||
|
|
||||||
# Use that token to call an OpenStack service (echo)
|
# Use that token to call an OpenStack service (echo)
|
||||||
data = call_service(token)
|
data = call_service(token)
|
||||||
print "Response received:", data
|
print "Response received:", data
|
||||||
print
|
print
|
||||||
|
|
||||||
# Use the valid token, but inject some headers
|
# Use the valid token, but inject some headers
|
||||||
print "\033[91mInjecting some headers >:-/ \033[0m"
|
print "\033[91mInjecting some headers >:-/ \033[0m"
|
||||||
data = hack_attempt(token)
|
data = hack_attempt(token)
|
||||||
print "Response received:", data
|
print "Response received:", data
|
||||||
print
|
print
|
||||||
|
|
||||||
# Use bad token to call an OpenStack service (echo)
|
# Use bad token to call an OpenStack service (echo)
|
||||||
print "\033[91mTrying with bad token...\033[0m"
|
print "\033[91mTrying with bad token...\033[0m"
|
||||||
data = call_service("xxxx_invalid_token_xxxx")
|
data = call_service("xxxx_invalid_token_xxxx")
|
||||||
print "Response received:", data
|
print "Response received:", data
|
||||||
print
|
print
|
||||||
|
|
||||||
#Supply bad credentials
|
#Supply bad credentials
|
||||||
print "\033[91mTrying with bad credentials...\033[0m"
|
print "\033[91mTrying with bad credentials...\033[0m"
|
||||||
auth = get_auth_token("joeuser", "wrongpass", "1")
|
auth = get_auth_token("joeuser", "wrongpass", "1")
|
||||||
|
|
|
@ -75,7 +75,8 @@ class AuthProtocol(object):
|
||||||
def __call__(self, env, start_response):
|
def __call__(self, env, start_response):
|
||||||
def custom_start_response(status, headers):
|
def custom_start_response(status, headers):
|
||||||
if self.delay_auth_decision:
|
if self.delay_auth_decision:
|
||||||
headers.append(('WWW-Authenticate', "Basic realm='Use guest/guest'"))
|
headers.append(('WWW-Authenticate',
|
||||||
|
"Basic realm='Use guest/guest'"))
|
||||||
return start_response(status, headers)
|
return start_response(status, headers)
|
||||||
|
|
||||||
#Prep headers to proxy request to remote service
|
#Prep headers to proxy request to remote service
|
||||||
|
@ -91,16 +92,16 @@ class AuthProtocol(object):
|
||||||
else:
|
else:
|
||||||
# If the user isn't authenticated, we reject the request and
|
# If the user isn't authenticated, we reject the request and
|
||||||
# return 401 indicating we need Basic Auth credentials.
|
# return 401 indicating we need Basic Auth credentials.
|
||||||
return HTTPUnauthorized(
|
return HTTPUnauthorized("Authentication required",
|
||||||
"Authentication required",
|
[('WWW-Authenticate',
|
||||||
[('WWW-Authenticate', 'Basic realm="Use guest/guest"')]
|
'Basic realm="Use guest/guest"')]
|
||||||
)(env,start_response)
|
)(env, start_response)
|
||||||
else:
|
else:
|
||||||
# Claims were provided - validate them
|
# Claims were provided - validate them
|
||||||
import base64
|
import base64
|
||||||
auth_header = env['HTTP_AUTHORIZATION']
|
auth_header = env['HTTP_AUTHORIZATION']
|
||||||
auth_type, encoded_creds = auth_header.split(None, 1)
|
auth_type, encoded_creds = auth_header.split(None, 1)
|
||||||
user, password = base64.b64decode(encoded_creds).split(':', 1)
|
user, password = base64.b64decode(encoded_creds).split(':', 1)
|
||||||
if not self.validateCreds(user, password):
|
if not self.validateCreds(user, password):
|
||||||
#Claims were rejected
|
#Claims were rejected
|
||||||
if not self.delay_auth_decision:
|
if not self.delay_auth_decision:
|
||||||
|
@ -125,13 +126,12 @@ class AuthProtocol(object):
|
||||||
_decorate_request_headers('X_GROUP', 'Blank',
|
_decorate_request_headers('X_GROUP', 'Blank',
|
||||||
proxy_headers, env)
|
proxy_headers, env)
|
||||||
|
|
||||||
|
|
||||||
#Auth processed, headers added now decide how to pass on the call
|
#Auth processed, headers added now decide how to pass on the call
|
||||||
if self.app:
|
if self.app:
|
||||||
# Pass to downstream WSGI component
|
# Pass to downstream WSGI component
|
||||||
env['HTTP_AUTHORIZATION'] = "Basic %s" % self.service_pass
|
env['HTTP_AUTHORIZATION'] = "Basic %s" % self.service_pass
|
||||||
return self.app(env, custom_start_response)
|
return self.app(env, custom_start_response)
|
||||||
|
|
||||||
proxy_headers['AUTHORIZATION'] = "Basic %s" % self.service_pass
|
proxy_headers['AUTHORIZATION'] = "Basic %s" % self.service_pass
|
||||||
# We are forwarding to a remote service (no downstream WSGI app)
|
# We are forwarding to a remote service (no downstream WSGI app)
|
||||||
req = Request(proxy_headers)
|
req = Request(proxy_headers)
|
||||||
|
@ -146,9 +146,6 @@ class AuthProtocol(object):
|
||||||
# we are rewriting the headers now
|
# we are rewriting the headers now
|
||||||
return Response(status=resp.status, body=data)(env, start_response)
|
return Response(status=resp.status, body=data)(env, start_response)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def validateCreds(self, username, password):
|
def validateCreds(self, username, password):
|
||||||
#stub for password validation.
|
#stub for password validation.
|
||||||
import ConfigParser
|
import ConfigParser
|
||||||
|
@ -163,6 +160,7 @@ class AuthProtocol(object):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def filter_factory(global_conf, ** local_conf):
|
def filter_factory(global_conf, ** local_conf):
|
||||||
"""Returns a WSGI filter app for use with paste.deploy."""
|
"""Returns a WSGI filter app for use with paste.deploy."""
|
||||||
conf = global_conf.copy()
|
conf = global_conf.copy()
|
||||||
|
|
|
@ -117,14 +117,13 @@ class AuthProtocol(object):
|
||||||
self._init_protocol_common(app, conf) # Applies to all protocols
|
self._init_protocol_common(app, conf) # Applies to all protocols
|
||||||
self._init_protocol(app, conf) # Specific to this protocol
|
self._init_protocol(app, conf) # Specific to this protocol
|
||||||
|
|
||||||
|
|
||||||
def __call__(self, env, start_response):
|
def __call__(self, env, start_response):
|
||||||
""" Handle incoming request. Authenticate. And send downstream. """
|
""" Handle incoming request. Authenticate. And send downstream. """
|
||||||
|
|
||||||
self.start_response = start_response
|
self.start_response = start_response
|
||||||
self.env = env
|
self.env = env
|
||||||
|
|
||||||
#Prep headers to forward request to downstream service (local or remote)
|
#Prep headers to forward request to local or remote downstream service
|
||||||
self.proxy_headers = env.copy()
|
self.proxy_headers = env.copy()
|
||||||
for header in self.proxy_headers.iterkeys():
|
for header in self.proxy_headers.iterkeys():
|
||||||
if header[0:5] == 'HTTP_':
|
if header[0:5] == 'HTTP_':
|
||||||
|
@ -158,23 +157,21 @@ class AuthProtocol(object):
|
||||||
|
|
||||||
#Collect information about valid claims
|
#Collect information about valid claims
|
||||||
if valid:
|
if valid:
|
||||||
verified_claims = self._expound_claims()
|
claims = self._expound_claims()
|
||||||
if verified_claims:
|
if claims:
|
||||||
# TODO(Ziad): add additional details we may need,
|
# TODO(Ziad): add additional details we may need,
|
||||||
# like tenant and group info
|
# like tenant and group info
|
||||||
self._decorate_request('X_AUTHORIZATION',
|
self._decorate_request('X_AUTHORIZATION',
|
||||||
"Proxy %s" % verified_claims['user'])
|
"Proxy %s" % claims['user'])
|
||||||
self._decorate_request('X_TENANT',
|
self._decorate_request('X_TENANT',
|
||||||
verified_claims['tenant'])
|
claims['tenant'])
|
||||||
self._decorate_request('X_GROUP',
|
self._decorate_request('X_GROUP',
|
||||||
verified_claims['group'])
|
claims['group'])
|
||||||
self.expanded = True
|
self.expanded = True
|
||||||
|
|
||||||
|
|
||||||
#Send request downstream
|
#Send request downstream
|
||||||
return self._forward_request()
|
return self._forward_request()
|
||||||
|
|
||||||
|
|
||||||
def get_admin_auth_token(self, username, password, tenant):
|
def get_admin_auth_token(self, username, password, tenant):
|
||||||
"""
|
"""
|
||||||
This function gets an admin auth token to be used by this service to
|
This function gets an admin auth token to be used by this service to
|
||||||
|
@ -198,25 +195,25 @@ class AuthProtocol(object):
|
||||||
return claims
|
return claims
|
||||||
|
|
||||||
def _reject_request(self):
|
def _reject_request(self):
|
||||||
# Redirect client to auth server
|
# Redirect client to auth server
|
||||||
return HTTPUseProxy(location=self.auth_location)(self.env,
|
return HTTPUseProxy(location=self.auth_location)(self.env,
|
||||||
self.start_response)
|
self.start_response)
|
||||||
|
|
||||||
def _reject_claims(self):
|
def _reject_claims(self):
|
||||||
# Client sent bad claims
|
# Client sent bad claims
|
||||||
return HTTPUnauthorized()(self.env,
|
return HTTPUnauthorized()(self.env,
|
||||||
self.start_response)
|
self.start_response)
|
||||||
|
|
||||||
def _validate_claims(self, claims):
|
def _validate_claims(self, claims):
|
||||||
"""Validate claims, and provide identity information isf applicable """
|
"""Validate claims, and provide identity information isf applicable """
|
||||||
|
|
||||||
# Step 1: We need to auth with the keystone service, so get an
|
# Step 1: We need to auth with the keystone service, so get an
|
||||||
# admin token
|
# admin token
|
||||||
#TODO: Need to properly implement this, where to store creds
|
#TODO: Need to properly implement this, where to store creds
|
||||||
# for now using token from ini
|
# for now using token from ini
|
||||||
#auth = self.get_admin_auth_token("admin", "secrete", "1")
|
#auth = self.get_admin_auth_token("admin", "secrete", "1")
|
||||||
#admin_token = json.loads(auth)["auth"]["token"]["id"]
|
#admin_token = json.loads(auth)["auth"]["token"]["id"]
|
||||||
|
|
||||||
# Step 2: validate the user's token with the auth service
|
# Step 2: validate the user's token with the auth service
|
||||||
# since this is a priviledged op,m we need to auth ourselves
|
# since this is a priviledged op,m we need to auth ourselves
|
||||||
# by using an admin token
|
# by using an admin token
|
||||||
|
@ -268,10 +265,11 @@ class AuthProtocol(object):
|
||||||
first_group = token_info['auth']['user']['groups']['group'][0]
|
first_group = token_info['auth']['user']['groups']['group'][0]
|
||||||
verified_claims = {'user': token_info['auth']['user']['username'],
|
verified_claims = {'user': token_info['auth']['user']['username'],
|
||||||
'tenant': token_info['auth']['user']['tenantId'],
|
'tenant': token_info['auth']['user']['tenantId'],
|
||||||
'group': '%s/%s' % (first_group['id'], first_group['tenantId'])}
|
'group': '%s/%s' % (first_group['id'],
|
||||||
|
first_group['tenantId'])}
|
||||||
return verified_claims
|
return verified_claims
|
||||||
|
|
||||||
def _decorate_request(self, index, value):
|
def _decorate_request(self, index, value):
|
||||||
self.proxy_headers[index] = value
|
self.proxy_headers[index] = value
|
||||||
self.env["HTTP_%s" % index] = value
|
self.env["HTTP_%s" % index] = value
|
||||||
|
|
||||||
|
@ -282,15 +280,18 @@ class AuthProtocol(object):
|
||||||
#now decide how to pass on the call
|
#now decide how to pass on the call
|
||||||
if self.app:
|
if self.app:
|
||||||
# Pass to downstream WSGI component
|
# Pass to downstream WSGI component
|
||||||
return self.app(self.env, self.start_response) #.custom_start_response)
|
return self.app(self.env, self.start_response)
|
||||||
|
#.custom_start_response)
|
||||||
else:
|
else:
|
||||||
# We are forwarding to a remote service (no downstream WSGI app)
|
# We are forwarding to a remote service (no downstream WSGI app)
|
||||||
req = Request(self.proxy_headers)
|
req = Request(self.proxy_headers)
|
||||||
parsed = urlparse(req.url)
|
parsed = urlparse(req.url)
|
||||||
conn = http_connect(self.service_host, self.service_port, \
|
conn = http_connect(self.service_host,
|
||||||
req.method, parsed.path, \
|
self.service_port,
|
||||||
self.proxy_headers,\
|
req.method,
|
||||||
ssl=(self.service_protocol == 'https'))
|
parsed.path,
|
||||||
|
self.proxy_headers,
|
||||||
|
ssl=(self.service_protocol == 'https'))
|
||||||
resp = conn.getresponse()
|
resp = conn.getresponse()
|
||||||
data = resp.read()
|
data = resp.read()
|
||||||
#TODO: use a more sophisticated proxy
|
#TODO: use a more sophisticated proxy
|
||||||
|
|
Loading…
Reference in New Issue