Added latest changes to sirish branch with pagination for get tenants

This commit is contained in:
root 2011-05-02 13:26:46 +05:30
parent c6c0678a10
commit 20ecd99441
14 changed files with 405 additions and 256 deletions

View File

@ -17,6 +17,7 @@ SERVICES:
* Echo - A sample service that responds by returning call details
Also included:
* Auth_Basic - Stub for WSGI middleware that will be used to handle basic auth
* Auth_OpenID - Stub for WSGI middleware that will be used to handle openid auth protocol
* RemoteAuth - WSGI middleware that can be used in services (like Swift, Nova, and Glance) when Auth middleware is running remotely

View File

@ -14,13 +14,14 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import os
import sys
import eventlet
from eventlet import wsgi
from lxml import etree
import os
from paste.deploy import loadapp
import sys
from webob.exc import HTTPUnauthorized
# If ../echo/__init__.py exists, add ../ to Python search path, so that
# it will override what happens to be installed in /usr/(local/)lib/python...
@ -53,14 +54,27 @@ class EchoApp(object):
self.transform = etree.XSLT(etree.parse(echo_xsl))
def __iter__(self):
# We expect an X_AUTHORIZATION header to be passed in
# We assume the request is coming from a trusted source. Middleware
# is used to perform that validation.
if 'HTTP_X_AUTHORIZATION' not in self.envr:
return HTTPUnauthorized(self.envr, start_response)
self.start('401 Unauthorized', [('Content-Type',
'application/json')])
return iter(["401 Unauthorized"])
if 'HTTP_X_IDENTITY_STATUS' not in self.envr:
identity_status = "Unknown"
else:
identity_status = self.envr["HTTP_X_IDENTITY_STATUS"]
print ' Received:'
if 'HTTP_X_IDENTITY_STATUS' in self.envr: print ' Auth Status:', self.envr['HTTP_X_IDENTITY_STATUS']
if 'HTTP_X_AUTHORIZATION' in self.envr: print ' Identity :', self.envr['HTTP_X_AUTHORIZATION']
if 'HTTP_X_TENANT' in self.envr: print ' Tenant :', self.envr['HTTP_X_TENANT']
if 'HTTP_X_GROUP' in self.envr: print ' Group :', self.envr['HTTP_X_GROUP']
print ' Auth Status:', identity_status
if 'HTTP_X_AUTHORIZATION' in self.envr:
print ' Identity :', self.envr['HTTP_X_AUTHORIZATION']
if 'HTTP_X_TENANT' in self.envr:
print ' Tenant :', self.envr['HTTP_X_TENANT']
if 'HTTP_X_GROUP' in self.envr:
print ' Group :', self.envr['HTTP_X_GROUP']
accept = self.envr.get("HTTP_ACCEPT", "application/json")
if accept == "application/xml":
@ -80,8 +94,7 @@ class EchoApp(object):
echo = etree.Element("{http://docs.openstack.org/echo/api/v1.0}echo",
method=environ["REQUEST_METHOD"],
pathInfo=environ["PATH_INFO"],
queryString=environ.get('QUERY_STRING', ""),
caller_identity=self.envr['HTTP_X_AUTHORIZATION'])
queryString=environ.get('QUERY_STRING', ""))
content = etree.Element(
"{http://docs.openstack.org/echo/api/v1.0}content")
content.set("type", environ["CONTENT_TYPE"])
@ -97,25 +110,54 @@ def app_factory(global_conf, **local_conf):
return EchoApp
if __name__ == "__main__":
remote_auth = False
if len(sys.argv) > 1:
remote_auth = sys.argv[1] == '--remote'
def usage():
print "Runs Echo, the canonical OpenStack service, " \
"with auth middleware"
print "Options:"
print "-h, --help : show this usage information"
print "-b, --basic : run with basic auth (uses echo_basic.ini)"
print "-r, --remote: run with remote auth on port 8100" \
"(uses echo_remote.ini)"
print "-i, --ini filename: run with specified ini file"
print "-p, --port: specifies port to listen on (default is 8090)"
print "by default will run with local, token auth (uses echo.ini)"
if remote_auth:
# running auth remotely
print "Running for use with remote auth"
import getopt
try:
opts, args = getopt.getopt(sys.argv[1:],
"hbrp:i:",
["help", "basic", "remote", "port", "ini"])
except getopt.GetoptError:
usage()
sys.exit()
app = loadapp("config:" + \
os.path.join(os.path.abspath(os.path.dirname(__file__)),
"echo_remote.ini"), global_conf={"log_name": "echo.log"})
port = 0
ini = "echo.ini"
auth_name = "local Token Auth"
wsgi.server(eventlet.listen(('', 8100)), app)
for opt, arg in opts:
if opt in ["-h", "--help"]:
usage()
sys.exit()
elif opt in ["-p", "--port"]:
port = int(arg)
elif opt in ["-i", "--ini"]:
auth_name = "with custom ini: %s" % arg
ini = arg
elif opt in ["-b", "--basic"]:
auth_name = "Basic Auth"
ini = "echo_basic.ini"
elif opt in ["-r", "--remote"]:
auth_name = "remote Token Auth"
ini = "echo_remote.ini"
if not port:
port = 8100
else:
print "Running all components locally."
print "Use --remote option to run with remote auth proxy"
app = loadapp("config:" + \
os.path.join(os.path.abspath(os.path.dirname(__file__)),
"echo.ini"), global_conf={"log_name": "echo.log"})
if not port:
port = 8090
print "Running with", auth_name
app = loadapp("config:" + \
os.path.join(os.path.abspath(os.path.dirname(__file__)),
ini), global_conf={"log_name": "echo.log"})
wsgi.server(eventlet.listen(('', 8090)), app)
wsgi.server(eventlet.listen(('', port)), app)

36
echo/echo/echo_basic.ini Normal file
View File

@ -0,0 +1,36 @@
[DEFAULT]
;delegated means we still allow unauthenticated requests through so the
;service can make the final decision on authentication
delay_auth_decision = 0
;where to find the OpenStack service (if not in local WSGI chain)
service_protocol = http
service_host = 127.0.0.1
service_port = 8090
;used to verify this component with the OpenStack service (or PAPIAuth)
service_pass = dTpw
[app:echo]
paste.app_factory = echo:app_factory
[pipeline:main]
pipeline =
basicauth
echo
[filter:tokenauth]
paste.filter_factory = keystone:tokenauth_factory
;where to find the token auth service
auth_host = 127.0.0.1
auth_port = 8080
auth_protocol = http
;how to authenticate to the auth service for priviledged operations
;like validate token
admin_token = 999888777666
[filter:basicauth]
paste.filter_factory = keystone:basicauth_factory
[filter:openidauth]
paste.filter_factory = keystone:openidauth_factory

View File

@ -19,6 +19,7 @@ Implement a client for Echo service using Identity service
import httplib
import json
import sys
def get_auth_token(username, password, tenant):
@ -46,6 +47,23 @@ def call_service(token):
ret = data
return ret
def hack_attempt(token):
# Injecting headers in the request
headers = {"X-Auth-Token": token,
"Content-type": "application/json",
"Accept": "text/json\nX_AUTHORIZATION: someone else\n"
"X_IDENTITY_STATUS: Confirmed\nINJECTED_HEADER: aha!"}
params = '{"ping": "abcdefg"}'
conn = httplib.HTTPConnection("localhost:8090")
print headers
conn.request("POST", "/", params, headers=headers)
response = conn.getresponse()
data = response.read()
ret = data
return ret
if __name__ == '__main__':
# Call the keystone service to get a token
# NOTE: assumes the test_setup.sql script has loaded this user
@ -60,6 +78,12 @@ if __name__ == '__main__':
print "Response received:", data
print
# Use the valid token, but inject some headers
print "\033[91mInjecting some headers >:-/ \033[0m"
data = hack_attempt(token)
print "Response received:", data
print
# Use bad token to call an OpenStack service (echo)
print "\033[91mTrying with bad token...\033[0m"
data = call_service("xxxx_invalid_token_xxxx")

View File

@ -0,0 +1,13 @@
[DEFAULT]
[app:main]
paste.app_factory = auth_basic:app_factory
delay_auth_decision = 0
service_protocol = http
service_host = 127.0.0.1
service_port = 8100
service_pass = dTpw

View File

@ -31,10 +31,20 @@ This is an Auth component as per: http://wiki.openstack.org/openstack-authn
"""
from paste.deploy import loadapp
import eventlet
from eventlet import wsgi
import os
from webob.exc import HTTPUnauthorized, HTTPInternalServerError
PROTOCOL_NAME = "Basic Authentication"
def _decorate_request_headers(header, value, proxy_headers, env):
proxy_headers[header] = value
env["HTTP_%s" % header] = value
class AuthProtocol(object):
"""Auth Middleware that handles authenticating client calls"""
@ -65,33 +75,93 @@ class AuthProtocol(object):
def __call__(self, env, start_response):
def custom_start_response(status, headers):
if self.delay_auth_decision:
headers.append(('WWW-Authenticate', "Basic realm='API Realm'"))
headers.append(('WWW-Authenticate',
"Basic realm='Use guest/guest'"))
return start_response(status, headers)
#TODO(Ziad): PERFORM BASIC AUTH
#Prep headers to proxy request to remote service
proxy_headers = env.copy()
user = ''
#Auth processed, headers added now decide how to pass on the call
if self.app:
# Pass to downstream WSGI component
env['HTTP_AUTHORIZATION'] = "Basic %s" % self.service_pass
return self.app(env, custom_start_response)
#Look for authentication
if 'HTTP_AUTHORIZATION' not in env:
#No credentials were provided
if self.delay_auth_decision:
_decorate_request_headers("X_IDENTITY_STATUS", "Invalid",
proxy_headers, env)
else:
# If the user isn't authenticated, we reject the request and
# return 401 indicating we need Basic Auth credentials.
return HTTPUnauthorized("Authentication required",
[('WWW-Authenticate',
'Basic realm="Use guest/guest"')]
)(env, start_response)
else:
# Claims were provided - validate them
import base64
auth_header = env['HTTP_AUTHORIZATION']
auth_type, encoded_creds = auth_header.split(None, 1)
user, password = base64.b64decode(encoded_creds).split(':', 1)
if not self.validateCreds(user, password):
#Claims were rejected
if not self.delay_auth_decision:
# Reject request (or ask for valid claims)
return HTTPUnauthorized("Authentication required",
[('WWW-Authenticate',
'Basic realm="Use guest/guest"')]
)(env, start_response)
else:
# Claims are valid, forward request
_decorate_request_headers("X_IDENTITY_STATUS", "Invalid",
proxy_headers, env)
proxy_headers['AUTHORIZATION'] = "Basic %s" % self.service_pass
# We are forwarding to a remote service (no downstream WSGI app)
req = Request(proxy_headers)
parsed = urlparse(req.url)
conn = http_connect(self.service_host, self.service_port, \
req.method, parsed.path, \
proxy_headers,\
ssl=(self.service_protocol == 'https'))
resp = conn.getresponse()
data = resp.read()
#TODO: use a more sophisticated proxy
# we are rewriting the headers now
return Response(status=resp.status, body=data)(env, start_response)
# TODO(Ziad): add additional details we may need,
# like tenant and group info
_decorate_request_headers('X_AUTHORIZATION', "Proxy %s" % user,
proxy_headers, env)
_decorate_request_headers("X_IDENTITY_STATUS", "Confirmed",
proxy_headers, env)
_decorate_request_headers('X_TENANT', 'blank',
proxy_headers, env)
_decorate_request_headers('X_GROUP', 'Blank',
proxy_headers, env)
#Auth processed, headers added now decide how to pass on the call
if self.app:
# Pass to downstream WSGI component
env['HTTP_AUTHORIZATION'] = "Basic %s" % self.service_pass
return self.app(env, custom_start_response)
proxy_headers['AUTHORIZATION'] = "Basic %s" % self.service_pass
# We are forwarding to a remote service (no downstream WSGI app)
req = Request(proxy_headers)
parsed = urlparse(req.url)
conn = http_connect(self.service_host, self.service_port, \
req.method, parsed.path, \
proxy_headers, \
ssl=(self.service_protocol == 'https'))
resp = conn.getresponse()
data = resp.read()
#TODO: use a more sophisticated proxy
# we are rewriting the headers now
return Response(status=resp.status, body=data)(env, start_response)
def validateCreds(self, username, password):
#stub for password validation.
import ConfigParser
import hashlib
#usersConfig = ConfigParser.ConfigParser()
#usersConfig.readfp(open('/etc/openstack/users.ini'))
#password = hashlib.sha1(password).hexdigest()
#for un, pwd in usersConfig.items('users'):
#TODO(Ziad): add intelligent credential validation (instead of hard
# coded)
if username == 'guest' and password == 'guest':
return True
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."""
conf = global_conf.copy()
conf.update(local_conf)
@ -101,14 +171,14 @@ def filter_factory(global_conf, **local_conf):
return auth_filter
def app_factory(global_conf, **local_conf):
def app_factory(global_conf, ** local_conf):
conf = global_conf.copy()
conf.update(local_conf)
return AuthProtocol(None, conf)
if __name__ == "__main__":
app = loadapp("config:" + \
os.path.join(os.path.abspath(os.path.dirname(__file__)),
"auth_basic.ini"),
global_conf={"log_name": "auth_basic.log"})
os.path.join(os.path.abspath(os.path.dirname(__file__)),
"auth_basic.ini"),
global_conf={"log_name": "auth_basic.log"})
wsgi.server(eventlet.listen(('', 8090)), app)

View File

@ -53,24 +53,20 @@ HTTP_X_AUTHORIZATION: the client identity being passed in
import eventlet
from eventlet import wsgi
import httplib
import json
import os
from paste.deploy import loadapp
import sys
from urlparse import urlparse
from webob.exc import HTTPUnauthorized
from webob.exc import HTTPUnauthorized, HTTPUseProxy
from webob.exc import Request, Response
import httplib
from keystone.common.bufferedhttp import http_connect_raw as http_connect
PROTOCOL_NAME = "Token Authentication"
def _decorate_request_headers(header, value, proxy_headers, env):
proxy_headers[header] = value
env["HTTP_%s" % header] = value
class AuthProtocol(object):
"""Auth Middleware that handles authenticating client calls"""
@ -106,8 +102,9 @@ 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)
self.auth_location = "%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
@ -120,6 +117,61 @@ class AuthProtocol(object):
self._init_protocol_common(app, conf) # Applies to all protocols
self._init_protocol(app, conf) # Specific to this protocol
def __call__(self, env, start_response):
""" Handle incoming request. Authenticate. And send downstream. """
self.start_response = start_response
self.env = env
#Prep headers to forward request to local or remote downstream service
self.proxy_headers = env.copy()
for header in self.proxy_headers.iterkeys():
if header[0:5] == 'HTTP_':
self.proxy_headers[header[5:]] = self.proxy_headers[header]
del self.proxy_headers[header]
#Look for authentication claims
self.claims = self._get_claims(env)
if not self.claims:
#No claim(s) provided
if self.delay_auth_decision:
#Configured to allow downstream service to make final decision.
#So mark status as Invalid and forward the request downstream
self._decorate_request("X_IDENTITY_STATUS", "Invalid")
else:
#Respond to client as appropriate for this auth protocol
return self._reject_request()
else:
# this request is presenting claims. Let's validate them
valid = self._validate_claims(self.claims)
if not valid:
# Keystone rejected claim
if self.delay_auth_decision:
# Downstream service will receive call still and decide
self._decorate_request("X_IDENTITY_STATUS", "Invalid")
else:
#Respond to client as appropriate for this auth protocol
return self._reject_claims()
else:
self._decorate_request("X_IDENTITY_STATUS", "Confirmed")
#Collect information about valid claims
if valid:
claims = self._expound_claims()
if claims:
# TODO(Ziad): add additional details we may need,
# like tenant and group info
self._decorate_request('X_AUTHORIZATION',
"Proxy %s" % claims['user'])
self._decorate_request('X_TENANT',
claims['tenant'])
self._decorate_request('X_GROUP',
claims['group'])
self.expanded = True
#Send request downstream
return self._forward_request()
def get_admin_auth_token(self, username, password, tenant):
"""
This function gets an admin auth token to be used by this service to
@ -136,111 +188,116 @@ class AuthProtocol(object):
headers=headers)
response = conn.getresponse()
data = response.read()
ret = data
return ret
return data
def __call__(self, env, start_response):
def custom_start_response(status, headers):
if self.delay_auth_decision:
headers.append(('WWW-Authenticate', "Basic realm='API Realm'"))
return start_response(status, headers)
def _get_claims(self, env):
claims = env.get('HTTP_X_AUTH_TOKEN', env.get('HTTP_X_STORAGE_TOKEN'))
return claims
#Prep headers to proxy request to remote service
proxy_headers = env.copy()
user = ''
def _reject_request(self):
# Redirect client to auth server
return HTTPUseProxy(location=self.auth_location)(self.env,
self.start_response)
#Look for token in request
token = env.get('HTTP_X_AUTH_TOKEN', env.get('HTTP_X_STORAGE_TOKEN'))
if not token:
#No token was provided
if self.delay_auth_decision:
_decorate_request_headers("X_IDENTITY_STATUS", "Invalid",
proxy_headers, env)
else:
# Redirect client to auth server
return HTTPUseProxy(location=self.auth_location)(env,
start_response)
def _reject_claims(self):
# Client sent bad claims
return HTTPUnauthorized()(self.env,
self.start_response)
def _validate_claims(self, claims):
"""Validate claims, and provide identity information isf applicable """
# Step 1: We need to auth with the keystone service, so get an
# admin token
#TODO: Need to properly implement this, where to store creds
# for now using token from ini
#auth = self.get_admin_auth_token("admin", "secrete", "1")
#admin_token = json.loads(auth)["auth"]["token"]["id"]
# Step 2: validate the user's token with the auth service
# since this is a priviledged op,m we need to auth ourselves
# by using an admin token
headers = {"Content-type": "application/json",
"Accept": "text/json",
"X-Auth-Token": self.admin_token}
##TODO:we need to figure out how to auth to keystone
#since validate_token is a priviledged call
#Khaled's version uses creds to get a token
# "X-Auth-Token": admin_token}
# we're using a test token from the ini file for now
conn = http_connect(self.auth_host, self.auth_port, 'GET',
'/v1.0/token/%s' % claims, headers=headers)
resp = conn.getresponse()
data = resp.read()
conn.close()
if not str(resp.status).startswith('20'):
# Keystone rejected claim
return False
else:
# this request is claiming it has a valid token, let's check
# with the auth service
# Step 1: We need to auth with the keystone service, so get an
# admin token
#TODO: Need to properly implement this, where to store creds
# for now using token from ini
#auth = self.get_admin_auth_token("admin", "secrete", "1")
#admin_token = json.loads(auth)["auth"]["token"]["id"]
#TODO(Ziad): there is an optimization we can do here. We have just
#received data from Keystone that we can use instead of making
#another call in _expound_claims
return True
# Step 2: validate the user's token with the auth service
# since this is a priviledged op,m we need to auth ourselves
# by using an admin token
headers = {"Content-type": "application/json",
"Accept": "text/json",
"X-Auth-Token": self.admin_token}
##TODO:we need to figure out how to auth to keystone
#since validate_token is a priviledged call
#Khaled's version uses creds to get a token
# "X-Auth-Token": admin_token}
# we're using a test token from the ini file for now
conn = http_connect(self.auth_host, self.auth_port, 'GET',
'/v1.0/token/%s' % token, headers=headers)
resp = conn.getresponse()
data = resp.read()
conn.close()
def _expound_claims(self):
# Valid token. Get user data and put it in to the call
# so the downstream service can use it
headers = {"Content-type": "application/json",
"Accept": "text/json",
"X-Auth-Token": self.admin_token}
##TODO:we need to figure out how to auth to keystone
#since validate_token is a priviledged call
#Khaled's version uses creds to get a token
# "X-Auth-Token": admin_token}
# we're using a test token from the ini file for now
conn = http_connect(self.auth_host, self.auth_port, 'GET',
'/v1.0/token/%s' % self.claims, headers=headers)
resp = conn.getresponse()
data = resp.read()
conn.close()
if not str(resp.status).startswith('20'):
# Keystone rejected claim
if self.delay_auth_decision:
# Downstream service will receive call still and decide
_decorate_request_headers("X_IDENTITY_STATUS", "Invalid",
proxy_headers, env)
else:
# Reject the response & send back the error
# (not delay_auth_decision)
return HTTPUnauthorized(headers=headers)(env,
start_response)
else:
# Valid token. Get user data and put it in to the call
# so the downstream service can use iot
dict_response = json.loads(data)
#TODO(Ziad): make this more robust
user = dict_response['auth']['user']['username']
tenant = dict_response['auth']['user']['tenantId']
group = '%s/%s' % (dict_response['auth']['user']['groups']['group'][0]['id'],
dict_response['auth']['user']['groups']['group'][0]['tenantId'])
if not str(resp.status).startswith('20'):
raise LookupError('Unable to locate claims: %s' % resp.status)
# TODO(Ziad): add additional details we may need,
# like tenant and group info
_decorate_request_headers('X_AUTHORIZATION', "Proxy %s" % user,
proxy_headers, env)
_decorate_request_headers("X_IDENTITY_STATUS", "Confirmed",
proxy_headers, env)
_decorate_request_headers('X_TENANT', tenant,
proxy_headers, env)
_decorate_request_headers('X_GROUP', group,
proxy_headers, env)
token_info = json.loads(data)
#TODO(Ziad): make this more robust
first_group = token_info['auth']['user']['groups']['group'][0]
verified_claims = {'user': token_info['auth']['user']['username'],
'tenant': token_info['auth']['user']['tenantId'],
'group': '%s/%s' % (first_group['id'],
first_group['tenantId'])}
return verified_claims
#Token/Auth processed, headers added now decide how to pass on the call
_decorate_request_headers('AUTHORIZATION',
"Basic %s" % self.service_pass,
proxy_headers,
env)
def _decorate_request(self, index, value):
self.proxy_headers[index] = value
self.env["HTTP_%s" % index] = value
def _forward_request(self):
#Token/Auth processed & claims added to headers
self._decorate_request('AUTHORIZATION',
"Basic %s" % self.service_pass)
#now decide how to pass on the call
if self.app:
# Pass to downstream WSGI component
return self.app(env, custom_start_response)
return self.app(self.env, self.start_response)
#.custom_start_response)
else:
# We are forwarding to a remote service (no downstream WSGI app)
req = Request(proxy_headers)
req = Request(self.proxy_headers)
parsed = urlparse(req.url)
conn = http_connect(self.service_host, self.service_port, \
req.method, parsed.path, \
proxy_headers,\
ssl=(self.service_protocol == 'https'))
conn = http_connect(self.service_host,
self.service_port,
req.method,
parsed.path,
self.proxy_headers,
ssl=(self.service_protocol == 'https'))
resp = conn.getresponse()
data = resp.read()
#TODO: use a more sophisticated proxy
# we are rewriting the headers now
return Response(status=resp.status, body=data)(proxy_headers,
start_response)
return Response(status=resp.status, body=data)(self.proxy_headers,
self.start_response)
def filter_factory(global_conf, **local_conf):

View File

@ -19,7 +19,7 @@
from session import get_session
from sqlalchemy.orm import joinedload
import models
import pprint
def tenant_create(values):
tenant_ref = models.Tenant()
@ -34,70 +34,12 @@ def tenant_get(id, session=None):
result = session.query(models.Tenant).filter_by(id=id).first()
return result
def tenant_get_all(session=None):
if not session:
session = get_session()
return session.query(models.Tenant).all()
def tenant_get_page(marker,limit,session=None):
if not session:
session = get_session()
if marker:
return session.query(models.Tenant).filter("id>=:marker").params(\
marker = '%s' % marker).order_by\
(models.Tenant.id).limit(limit).all()
else:
return session.query(models.Tenant).order_by(\
models.Tenant.id).limit(limit).all()
#return session.query(models.Tenant).all()
def tenant_get_page_markers(marker,limit,session=None):
if not session:
session = get_session()
first = session.query(models.Tenant).order_by(\
models.Tenant.id).first()
last = session.query(models.Tenant).order_by(\
models.Tenant.id.desc()).first()
if marker is None:
marker=first.id
print marker
next=session.query(models.Tenant).filter("id > :marker").params(\
marker = '%s' % marker).order_by(\
models.Tenant.id).limit(limit).all()
prev=session.query(models.Tenant).filter("id < :marker").params(\
marker = '%s' % marker).order_by(\
models.Tenant.id.desc()).limit(int(limit)).all()
if len(next) == 0:
next=last
else:
for t in next:
next=t
if len(prev) == 0:
prev=first
else:
for t in prev:
prev=t
if prev.id == marker:
prev = None
else:
prev=prev.id
if next.id == last.id:
next = None
else:
next = next.id
return (prev,next)
def tenant_is_empty(id, session=None):
if not session:

View File

@ -229,6 +229,7 @@ def delete_token(token_id):
## Tenant Operations
##
@bottle.route('/v1.0/tenants', method='POST')
@wrap_error
def create_tenant():
@ -243,15 +244,10 @@ def get_tenants():
marker = None
if "marker" in request.GET:
marker = request.GET["marker"]
limit = None
if "limit" in request.GET:
limit = request.GET["limit"]
else:
limit=10
print limit, marker
url = '%s://%s%s' % (request.environ['wsgi.url_scheme'],request.environ['HTTP_HOST'],request.environ['PATH_INFO'])
tenants = service.get_tenants(get_auth_token(), marker, limit,url)
tenants = service.get_tenants(get_auth_token(), marker, limit)
return send_result(200, tenants)

View File

@ -114,35 +114,16 @@ class IDMService(object):
return tenant
""" def get_tenants(self, admin_token, marker, limit):
self.__validate_token(admin_token)
ts = []
dtenants = db_api.tenant_get_all()
for dtenant in dtenants:
ts.append(tenants.Tenant(dtenant.id,
dtenant.desc, dtenant.enabled))
return tenants.Tenants(ts, [])
"""
def get_tenants(self, admin_token, marker, limit, url):
def get_tenants(self, admin_token, marker, limit):
self.__validate_token(admin_token)
ts = []
dtenants = db_api.tenant_get_page(marker,limit)
dtenants = db_api.tenant_get_all()
for dtenant in dtenants:
ts.append(tenants.Tenant(dtenant.id,
dtenant.desc, dtenant.enabled))
prev,next=db_api.tenant_get_page_markers(marker,limit)
links=[]
if prev:
links.append(atom.Link('prev',"%s?'marker=%s&limit=%s'" % (url,prev,limit)))
if next:
links.append(atom.Link('next',"%s?'marker=%s&limit=%s'" % (url,next,limit)))
return tenants.Tenants(ts, links)
return tenants.Tenants(ts, [])
def get_tenant(self, admin_token, tenant_id):
self.__validate_token(admin_token)

View File

@ -23,15 +23,3 @@ class Link(object):
self.link_type = link_type
self.hreflang = hreflang
self.title = title
def to_dict(self):
links = {}
if self.link_type:
links["link_type"] = self.link_type
if self.hreflang:
links["hreflang"] = self.hreflang
if self.title:
links["title"] = self.title
links["rel"] = self.rel
links["href"] = self.href
return {'links': links}

View File

@ -116,5 +116,4 @@ class Tenants(object):
def to_json(self):
values = [t.to_dict()["tenant"] for t in self.values]
links = [t.to_dict()["links"] for t in self.links]
return json.dumps({"tenants": {"values": values,"links":links}})
return json.dumps({"tenants": {"values": values}})

View File

@ -24,7 +24,7 @@ insert into tenants (id, "desc", enabled) values
-- Groups
insert into groups (id, "desc", tenant_id) values
("Admin", "Andmin users", "1234");
("Admin", "Admin users", "1234");
insert into groups (id, "desc", tenant_id) values
("Default", "Standard users", "1234");

View File

@ -1,7 +1,8 @@
import os
import sys
# Need to access identity module
sys.path.append(os.path.abspath(os.path.join(os.path.abspath( __file__ ), '..', '..', '..', '..', 'keystone')))
sys.path.append(os.path.abspath(os.path.join(os.path.abspath(__file__),
'..', '..', '..', '..', 'keystone')))
from keystone import identity
import unittest
from webtest import TestApp
@ -970,6 +971,5 @@ class delete_tenant_test(tenant_test):
self.assertEqual(204, int(resp['status']))
if __name__ == '__main__':
unittest.main()