summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsirish bitra <sirish.bitra@gmail.com>2011-05-08 17:41:42 +0530
committersirish bitra <sirish.bitra@gmail.com>2011-05-08 17:41:42 +0530
commitad0cbd2e763d010d31428393aab9d2bcd787cbe0 (patch)
treebe2ab246c1105265a0d2e519d643b14a7f63d503
parentc32b81baa25c827f72bde67832b2f4e20f09f10c (diff)
downloadkeystone-ad0cbd2e763d010d31428393aab9d2bcd787cbe0.tar.gz
keystone-ad0cbd2e763d010d31428393aab9d2bcd787cbe0.tar.xz
keystone-ad0cbd2e763d010d31428393aab9d2bcd787cbe0.zip
get version implementation
s Please enter the commit message for your changes. Lines starting
-rwxr-xr-xkeystone/auth_server.py82
-rw-r--r--keystone/logic/service.py287
2 files changed, 363 insertions, 6 deletions
diff --git a/keystone/auth_server.py b/keystone/auth_server.py
index 35812f48..5ab8932f 100755
--- a/keystone/auth_server.py
+++ b/keystone/auth_server.py
@@ -56,7 +56,6 @@ if os.path.exists(os.path.join(POSSIBLE_TOPDIR, 'keystone', '__init__.py')):
sys.path.insert(0, POSSIBLE_TOPDIR)
-
from queryext import exthandler
from keystone.common import wsgi
import keystone.logic.service as serv
@@ -65,6 +64,7 @@ import keystone.logic.types.auth as auth
import keystone.logic.types.fault as fault
import keystone.logic.types.user as users
+
VERSION_STATUS = "ALPHA"
VERSION_DATE = "2011-04-23T00:00:00Z"
@@ -77,6 +77,10 @@ def is_xml_response(req):
return req.content_type == "application/xml"
+def get_app_root():
+ return os.path.abspath(os.path.dirname(__file__))
+
+
def get_auth_token(req):
auth_token = None
if "X-Auth-Token" in req.headers:
@@ -113,16 +117,19 @@ def send_result(code, req, result):
return
return content
+
class MiscController(wsgi.Controller):
def __init__(self, options):
self.options = options
- def get_version_info(self, req):
+ def get_version_info(self, req):
+
if is_xml_response(req):
resp_file = os.path.join(POSSIBLE_TOPDIR,
"keystone/content/version.xml.tpl")
else:
+
resp_file = os.path.join(POSSIBLE_TOPDIR,
"keystone/content/version.json.tpl")
@@ -131,11 +138,71 @@ class MiscController(wsgi.Controller):
#try:
tmplfile=open(resp_file);
tmplstring=tmplfile.read()
-
- send_result(200,req, tmplstring.format(HOST=hostname, PORT=port,
+
+ return serv.template(resp_file, HOST=hostname, PORT=port,
+ VERSION_STATUS=VERSION_STATUS,
+ VERSION_DATE=VERSION_DATE)
+
+ def get_pdf_contract(self, req):
+ resp = Response()
+ return serv.static_file(resp, req, "content/idmdevguide.pdf",
+ root=get_app_root(),
+ mimetype="application/pdf",)
+
+"""
+@bottle.route('/v1.0', method='GET')
+@bottle.route('/v1.0/', method='GET')
+@wrap_error
+def get_version_info():
+ if is_xml_response():
+ resp_file = os.path.join(POSSIBLE_TOPDIR, "keystone/content/version.xml.tpl")
+ response.content_type = "application/xml"
+ else:
+ resp_file = os.path.join(POSSIBLE_TOPDIR, "keystone/content/version.json.tpl")
+ response.content_type = "application/json"
+ hostname = request.environ.get("SERVER_NAME")
+ port = request.environ.get("SERVER_PORT")
+ return bottle.template(resp_file, HOST=hostname, PORT=port,
VERSION_STATUS=VERSION_STATUS,
- VERSION_DATE=VERSION_DATE))
+ VERSION_DATE=VERSION_DATE)
+
+##
+## Version links:
+##
+
+@bottle.route('/v1.0/idmdevguide.pdf', method='GET')
+@wrap_error
+def get_pdf_contract():
+ return bottle.static_file("content/idmdevguide.pdf",
+ root=get_app_root(),
+ mimetype="application/pdf")
+
+
+@bottle.route('/v1.0/identity.wadl', method='GET')
+@wrap_error
+def get_wadl_contract():
+ return bottle.static_file("identity.wadl",
+ root=get_app_root(),
+ mimetype="application/vnd.sun.wadl+xml")
+
+
+@bottle.route('/v1.0/xsd/:xsd', method='GET')
+@wrap_error
+def get_xsd_contract(xsd):
+ return bottle.static_file("/xsd/" + xsd,
+ root=get_app_root(),
+ mimetype="application/xml")
+
+
+@bottle.route('/v1.0/xsd/atom/:xsd', method='GET')
+@wrap_error
+def get_xsd_atom_contract(xsd):
+ return bottle.static_file("/xsd/atom/" + xsd,
+ root=get_app_root(),
+ mimetype="application/xml")
+
+"""
class AuthController(wsgi.Controller):
@@ -386,7 +453,10 @@ class Auth_API(wsgi.Router):
action="get_version_info",conditions=dict(method=["GET"]))
mapper.connect("/v1.0", controller=misc_controller,
action="get_version_info",conditions=dict(method=["GET"]))
-
+ mapper.connect("/v1.0/idmdevguide.pdf", controller=misc_controller,
+ action="get_pdf_contract",conditions=dict(method=["GET"]))
+
+
super(Auth_API, self).__init__(mapper)
diff --git a/keystone/logic/service.py b/keystone/logic/service.py
index cc852af0..9b38b76c 100644
--- a/keystone/logic/service.py
+++ b/keystone/logic/service.py
@@ -26,6 +26,293 @@ import keystone.db.sqlalchemy.api as db_api
import keystone.db.sqlalchemy.models as db_models
import uuid
+import cgi
+import re
+import os
+import functools
+import time
+TEMPLATES = {}
+DEBUG = False
+TEMPLATE_PATH = ['./', './views/']
+
+
+##""" this part of below code is used from bottle.py """
+class BaseTemplate(object):
+ """ Base class and minimal API for template adapters """
+ extentions = ['tpl','html','thtml','stpl']
+ settings = {} #used in prepare()
+ defaults = {} #used in render()
+
+ def __init__(self, source=None, name=None, lookup=[], encoding='utf8', **settings):
+ """ Create a new template.
+ If the source parameter (str or buffer) is missing, the name argument
+ is used to guess a template filename. Subclasses can assume that
+ self.source and/or self.filename are set. Both are strings.
+ The lookup, encoding and settings parameters are stored as instance
+ variables.
+ The lookup parameter stores a list containing directory paths.
+ The encoding parameter should be used to decode byte strings or files.
+ The settings parameter contains a dict for engine-specific settings.
+ """
+ self.name = name
+ self.source = source.read() if hasattr(source, 'read') else source
+ self.filename = source.filename if hasattr(source, 'filename') else None
+ self.lookup = map(os.path.abspath, lookup)
+ self.encoding = encoding
+ self.settings = self.settings.copy() # Copy from class variable
+ self.settings.update(settings) # Apply
+ if not self.source and self.name:
+ self.filename = self.search(self.name, self.lookup)
+ if not self.filename:
+ raise TemplateError('Template %s not found.' % repr(name))
+ if not self.source and not self.filename:
+ raise TemplateError('No template specified.')
+ self.prepare(**self.settings)
+
+ @classmethod
+ def search(cls, name, lookup=[]):
+ """ Search name in all directories specified in lookup.
+ First without, then with common extensions. Return first hit. """
+ if os.path.isfile(name): return name
+ for spath in lookup:
+ fname = os.path.join(spath, name)
+ if os.path.isfile(fname):
+ return fname
+ for ext in cls.extentions:
+ if os.path.isfile('%s.%s' % (fname, ext)):
+ return '%s.%s' % (fname, ext)
+
+ @classmethod
+ def global_config(cls, key, *args):
+ ''' This reads or sets the global settings stored in class.settings. '''
+ if args:
+ cls.settings[key] = args[0]
+ else:
+ return cls.settings[key]
+
+ def prepare(self, **options):
+ """ Run preparations (parsing, caching, ...).
+ It should be possible to call this again to refresh a template or to
+ update settings.
+ """
+ raise NotImplementedError
+
+ def render(self, **args):
+ """ Render the template with the specified local variables and return
+ a single byte or unicode string. If it is a byte string, the encoding
+ must match self.encoding. This method must be thread-safe!
+ """
+ raise NotImplementedError
+
+
+class SimpleTemplate(BaseTemplate):
+ blocks = ('if','elif','else','try','except','finally','for','while','with','def','class')
+ dedent_blocks = ('elif', 'else', 'except', 'finally')
+
+ def prepare(self, escape_func=cgi.escape, noescape=False):
+ self.cache = {}
+ if self.source:
+ self.code = self.translate(self.source)
+ self.co = compile(self.code, '<string>', 'exec')
+ else:
+ self.code = self.translate(open(self.filename).read())
+ self.co = compile(self.code, self.filename, 'exec')
+ enc = self.encoding
+ touni = functools.partial(unicode, encoding=self.encoding)
+ self._str = lambda x: touni(x, enc)
+ self._escape = lambda x: escape_func(touni(x))
+ if noescape:
+ self._str, self._escape = self._escape, self._str
+
+ def translate(self, template):
+ stack = [] # Current Code indentation
+ lineno = 0 # Current line of code
+ ptrbuffer = [] # Buffer for printable strings and token tuple instances
+ codebuffer = [] # Buffer for generated python code
+ touni = functools.partial(unicode, encoding=self.encoding)
+ multiline = dedent = False
+
+ def yield_tokens(line):
+ for i, part in enumerate(re.split(r'\{\{(.*?)\}\}', line)):
+ if i % 2:
+ if part.startswith('!'): yield 'RAW', part[1:]
+ else: yield 'CMD', part
+ else: yield 'TXT', part
+
+ def split_comment(codeline):
+ """ Removes comments from a line of code. """
+ line = codeline.splitlines()[0]
+ try:
+ tokens = list(tokenize.generate_tokens(iter(line).next))
+ except tokenize.TokenError:
+ return line.rsplit('#',1) if '#' in line else (line, '')
+ for token in tokens:
+ if token[0] == tokenize.COMMENT:
+ start, end = token[2][1], token[3][1]
+ return codeline[:start] + codeline[end:], codeline[start:end]
+ return line, ''
+
+ def flush(): # Flush the ptrbuffer
+ if not ptrbuffer: return
+ cline = ''
+ for line in ptrbuffer:
+ for token, value in line:
+ if token == 'TXT': cline += repr(value)
+ elif token == 'RAW': cline += '_str(%s)' % value
+ elif token == 'CMD': cline += '_escape(%s)' % value
+ cline += ', '
+ cline = cline[:-2] + '\\\n'
+ cline = cline[:-2]
+ if cline[:-1].endswith('\\\\\\\\\\n'):
+ cline = cline[:-7] + cline[-1] # 'nobr\\\\\n' --> 'nobr'
+ cline = '_printlist([' + cline + '])'
+ del ptrbuffer[:] # Do this before calling code() again
+ code(cline)
+
+ def code(stmt):
+ for line in stmt.splitlines():
+ codebuffer.append(' ' * len(stack) + line.strip())
+
+ for line in template.splitlines(True):
+ lineno += 1
+ line = line if isinstance(line, unicode)\
+ else unicode(line, encoding=self.encoding)
+ if lineno <= 2:
+ m = re.search(r"%.*coding[:=]\s*([-\w\.]+)", line)
+ if m: self.encoding = m.group(1)
+ if m: line = line.replace('coding','coding (removed)')
+ if line.strip()[:2].count('%') == 1:
+ line = line.split('%',1)[1].lstrip() # Full line following the %
+ cline = split_comment(line)[0].strip()
+ cmd = re.split(r'[^a-zA-Z0-9_]', cline)[0]
+ flush() ##encodig (TODO: why?)
+ if cmd in self.blocks or multiline:
+ cmd = multiline or cmd
+ dedent = cmd in self.dedent_blocks # "else:"
+ if dedent and not oneline and not multiline:
+ cmd = stack.pop()
+ code(line)
+ oneline = not cline.endswith(':') # "if 1: pass"
+ multiline = cmd if cline.endswith('\\') else False
+ if not oneline and not multiline:
+ stack.append(cmd)
+ elif cmd == 'end' and stack:
+ code('#end(%s) %s' % (stack.pop(), line.strip()[3:]))
+ elif cmd == 'include':
+ p = cline.split(None, 2)[1:]
+ if len(p) == 2:
+ code("_=_include(%s, _stdout, %s)" % (repr(p[0]), p[1]))
+ elif p:
+ code("_=_include(%s, _stdout)" % repr(p[0]))
+ else: # Empty %include -> reverse of %rebase
+ code("_printlist(_base)")
+ elif cmd == 'rebase':
+ p = cline.split(None, 2)[1:]
+ if len(p) == 2:
+ code("globals()['_rebase']=(%s, dict(%s))" % (repr(p[0]), p[1]))
+ elif p:
+ code("globals()['_rebase']=(%s, {})" % repr(p[0]))
+ else:
+ code(line)
+ else: # Line starting with text (not '%') or '%%' (escaped)
+ if line.strip().startswith('%%'):
+ line = line.replace('%%', '%', 1)
+ ptrbuffer.append(yield_tokens(line))
+ flush()
+ return '\n'.join(codebuffer) + '\n'
+
+ def subtemplate(self, _name, _stdout, **args):
+ if _name not in self.cache:
+ self.cache[_name] = self.__class__(name=_name, lookup=self.lookup)
+ return self.cache[_name].execute(_stdout, **args)
+
+ def execute(self, _stdout, **args):
+ env = self.defaults.copy()
+ env.update({'_stdout': _stdout, '_printlist': _stdout.extend,
+ '_include': self.subtemplate, '_str': self._str,
+ '_escape': self._escape})
+ env.update(args)
+ eval(self.co, env)
+ if '_rebase' in env:
+ subtpl, rargs = env['_rebase']
+ subtpl = self.__class__(name=subtpl, lookup=self.lookup)
+ rargs['_base'] = _stdout[:] #copy stdout
+ del _stdout[:] # clear stdout
+ return subtpl.execute(_stdout, **rargs)
+ return env
+
+ def render(self, **args):
+ """ Render the template using keyword arguments as local variables. """
+ stdout = []
+ self.execute(stdout, **args)
+ return ''.join(stdout)
+
+def static_file(resp, req, filename, root, guessmime=True, mimetype=None, download=False):
+ """ Opens a file in a safe way and returns a HTTPError object with status
+ code 200, 305, 401 or 404. Sets Content-Type, Content-Length and
+ Last-Modified header. Obeys If-Modified-Since header and HEAD requests.
+ """
+ root = os.path.abspath(root) + os.sep
+ filename = os.path.abspath(os.path.join(root, filename.strip('/\\')))
+ header = dict()
+ if not filename.startswith(root):
+ #return HTTPError(403, "Access denied.")
+ return ForbiddenFault("Access denied.")
+ if not os.path.exists(filename) or not os.path.isfile(filename):
+ #return HTTPError(404, "File does not exist.")
+ return fault.ItemNotFoundFault("File does not exist.")
+ if not os.access(filename, os.R_OK):
+ #return HTTPError(403, "You do not have permission to access this file.")
+ return ForbiddenFault("You do not have permission to access this file.")
+
+ if not mimetype and guessmime:
+ resp.headers['Content-Type'] = mimetypes.guess_type(filename)[0]
+ else:
+ resp.headers['Content-Type'] = mimetype if mimetype else 'text/plain'
+
+ if download == True:
+ download = os.path.basename(filename)
+ if download:
+ resp.headers['Content-Disposition'] = 'attachment; filename="%s"' % download
+
+ stats = os.stat(filename)
+ lm = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(stats.st_mtime))
+ resp.headers['Last-Modified'] = lm
+ ims = req.environ.get('HTTP_IF_MODIFIED_SINCE')
+ if ims:
+ ims = ims.split(";")[0].strip() # IE sends "<date>; length=146"
+ ims = parse_date(ims)
+ if ims is not None and ims >= int(stats.st_mtime):
+ resp.headers['Date'] = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime())
+ return Response(status=304, header=header)
+ resp.headers['Content-Length'] = stats.st_size
+ if req.method == 'HEAD':
+ return resp('', header=header)
+ else:
+ return resp(open(filename, 'rb'), header=header)
+
+
+def template(tpl, template_adapter=SimpleTemplate, **kwargs):
+ '''
+ Get a rendered template as a string iterator.
+ You can use a name, a filename or a template string as first parameter.
+ '''
+ if tpl not in TEMPLATES or DEBUG:
+ settings = kwargs.get('template_settings',{})
+ lookup = kwargs.get('template_lookup', TEMPLATE_PATH)
+ if isinstance(tpl, template_adapter):
+ TEMPLATES[tpl] = tpl
+ if settings: TEMPLATES[tpl].prepare(**settings)
+ elif "\n" in tpl or "{" in tpl or "%" in tpl or '$' in tpl:
+ TEMPLATES[tpl] = template_adapter(source=tpl, lookup=lookup, **settings)
+ else:
+ TEMPLATES[tpl] = template_adapter(name=tpl, lookup=lookup, **settings)
+ if not TEMPLATES[tpl]:
+ abort(500, 'Template (%s) not found' % tpl)
+ return TEMPLATES[tpl].render(**kwargs)
+
+
+##""" this part of above code is used from bottle.py """
class IDMService(object):