blob: 1470ff9b8b537f79ac2f48d0ac462324e5aa9b35 [file] [log] [blame]
#!/usr/bin/env python
#
# Copyright 2007 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
"""A base class for all Admin UI request handlers and related utilities."""
import os.path
import random
import string
import urllib
import google
import jinja2
import webapp2
from google.appengine.tools import sdk_update_checker
def _urlencode_filter(value):
if isinstance(value, basestring):
return urllib.quote(value)
else:
return urllib.urlencode(value)
def _byte_size_format(value):
byte_count = int(value)
if byte_count == 1:
return '1 Byte'
elif byte_count < 1024:
return '%d Bytes' % byte_count
elif byte_count < 1024 ** 2:
return '%.1f KiB (%d Bytes)' % (byte_count/1024.0, byte_count)
elif byte_count < 1024 ** 3:
return '%.1f MiB (%d Bytes)' % (byte_count/1024.0 ** 2, byte_count)
else:
return '%.1f GiB (%d Bytes)' % (byte_count/1024.0 ** 3, byte_count)
TEMPLATE_PATH = os.path.abspath(
os.path.join(os.path.dirname(__file__), 'templates'))
admin_template_environment = jinja2.Environment(
loader=jinja2.FileSystemLoader(TEMPLATE_PATH),
autoescape=True)
admin_template_environment.filters['urlencode'] = _urlencode_filter
admin_template_environment.filters['bytesizeformat'] = _byte_size_format
_DEFAULT_SDK_VERSION = '(Internal)'
def _get_sdk_version():
version_object = sdk_update_checker.GetVersionObject()
if version_object:
return version_object['release']
else:
return _DEFAULT_SDK_VERSION
class AdminRequestHandler(webapp2.RequestHandler):
"""Base class for all admin UI request handlers."""
_SDK_VERSION = _get_sdk_version()
@classmethod
def init_xsrf(cls, xsrf_path):
"""Load the XSRF token from the given path."""
if os.path.exists(xsrf_path):
with open(xsrf_path, 'r') as token_file:
cls.xsrf_token = token_file.read().strip()
else:
cls.xsrf_token = ''.join(random.choice(string.ascii_letters)
for _ in range(10))
with open(xsrf_path, 'w') as token_file:
token_file.write(cls.xsrf_token)
def dispatch(self):
if self.request.method in ['PATCH', 'POST', 'PUT', 'DELETE'] and (
self.request.get('xsrf_token') != self.xsrf_token):
self.response.set_status(403, 'Invalid XSRF token')
self.response.out.write('<h1>Invalid XSRF token</h1>')
else:
super(AdminRequestHandler, self).dispatch()
def render(self, template, context):
"""Returns a rendered version of the given jinja2 template.
Args:
template: The file name of the template file to use e.g.
"memcache_viewer.html".
context: A dict of values to use when rendering the template.
Returns:
A Unicode object containing the rendered template.
"""
template = admin_template_environment.get_template(template)
values = {
'app_id': self.configuration.app_id,
'request': self.request,
'sdk_version': self._SDK_VERSION,
'xsrf_token': self.xsrf_token,
}
values.update(context)
return template.render(values)
def _construct_url(self, remove=None, add=None):
"""Returns a URL referencing the current resource with the same params.
For example, if the request URL is
"http://foo/bar?animal=cat&color=redirect" then
_construct_url(['animal'], {'vehicle': 'car'}) will return
"http://foo/bar?vehicle=car&color=redirect"
Args:
remove: A sequence of query parameters to remove from the query string.
add: A mapping of query parameters to add to the query string.
Returns:
A new query string suitable for use in "GET" requests.
"""
remove = remove or []
add = add or {}
params = dict(self.request.params)
for arg in remove:
if arg in params:
del params[arg]
params.update(add)
return str('%s?%s' % (self.request.path,
urllib.urlencode(sorted(params.iteritems()))))
@property
def dispatcher(self):
return self.request.app.dispatcher
@property
def configuration(self):
return self.request.app.configuration