blob: 2862ecb91a40f38267b21970fd0c818cb76264d2 [file] [log] [blame]
#
# Copyright (C) 2018 The Android Open Source Project
#
# 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.
#
import datetime
import httplib
import logging
import os
import urlparse
from google.appengine.api import users
import stripe
import webapp2
from webapp2_extras import jinja2 as wa2_jinja2
from webapp2_extras import sessions
import errors
from webapp.src.utils import datetime_util
class BaseHandler(webapp2.RequestHandler):
"""BaseHandler for all requests."""
def initialize(self, request, response):
"""Initializes this request handler."""
webapp2.RequestHandler.initialize(self, request, response)
self.session_backend = 'datastore'
def verify_origin(self):
"""This function will check the request is comming from the same domain."""
server_host = os.environ.get('ENDPOINTS_SERVICE_NAME')
request_host = self.request.headers.get('Host')
request_referer = self.request.headers.get('Referer')
if request_referer:
request_referer = urlparse.urlsplit(request_referer)[1]
else:
request_referer = request_host
logging.info('server: %s, request: %s', server_host, request_referer)
if server_host and request_referer and server_host != request_referer:
raise errors.Error(httplib.FORBIDDEN)
def dispatch(self):
"""Dispatch the request.
This will first check if there's a handler_method defined
in the matched route, and if not it'll use the method correspondent to
the request method (get(), post() etc).
"""
self.session_store = sessions.get_store(request=self.request)
# Forwards the method for RESTful support.
self.forward_method()
# Security headers.
# https://www.owasp.org/index.php/List_of_useful_HTTP_headers
self.response.headers['x-content-type-options'] = 'nosniff'
self.response.headers['x-frame-options'] = 'SAMEORIGIN'
self.response.headers['x-xss-protection'] = '1; mode=block'
try:
webapp2.RequestHandler.dispatch(self)
finally:
self.session_store.save_sessions(self.response)
# Disabled for now because host is appspot.com in production.
#self.verify_origin()
@webapp2.cached_property
def session(self):
# Returns a session using the default cookie key.
return self.session_store.get_session()
def handle_exception(self, exception, debug=False):
"""Render the exception as HTML."""
logging.exception(exception)
# Create response dictionary and status defaults.
tpl = 'error.html'
status = httplib.INTERNAL_SERVER_ERROR
resp_dict = {
'message': 'A server error occurred.',
}
url_parts = self.urlsplit()
redirect_url = '%s?%s' % (url_parts[2], url_parts[4])
# Use error code if a HTTPException, or generic 500.
if isinstance(exception, webapp2.HTTPException):
status = exception.code
resp_dict['message'] = exception.detail
elif isinstance(exception, errors.FormValidationError):
status = exception.code
resp_dict['message'] = exception.msg
resp_dict['errors'] = exception.errors
self.session['form_errors'] = exception.errors
# Redirect user to current view URL.
return self.redirect(redirect_url)
elif isinstance(exception, stripe.StripeError):
status = exception.http_status
resp_dict['errors'] = exception.json_body['error']['message']
self.session['form_errors'] = [
exception.json_body['error']['message']
]
return self.redirect(redirect_url)
elif isinstance(exception, (errors.Error, errors.AclError)):
status = exception.code
resp_dict['message'] = exception.msg
resp_dict['status'] = status
# Render output.
self.response.status_int = status
self.response.status_message = httplib.responses[status]
# Render the exception response into the error template.
self.response.write(self.jinja2.render_template(tpl, **resp_dict))
# @Override
def get(self, *args, **kwargs):
self.abort(httplib.NOT_IMPLEMENTED)
# @Override
def post(self, *args, **kwargs):
self.abort(httplib.NOT_IMPLEMENTED)
# @Override
def put(self, *args, **kwargs):
self.abort(httplib.NOT_IMPLEMENTED)
# @Override
def delete(self, *args, **kwargs):
self.abort(httplib.NOT_IMPLEMENTED)
# @Override
def head(self, *args, **kwargs):
pass
def urlsplit(self):
"""Return a tuple of the URL."""
return urlparse.urlsplit(self.request.url)
def path(self):
"""Returns the path of the current URL."""
return self.urlsplit()[2]
def forward_method(self):
"""Check for a method override param and change in the request."""
valid = (None, 'get', 'post', 'put', 'delete', 'head', 'options')
method = self.request.POST.get('__method__')
if not method: # Backbone's _method parameter.
method = self.request.POST.get('_method')
if method not in valid:
logging.debug('Invalid method %s requested!', method)
method = None
logging.debug('Method being changed from %s to %s by request',
self.request.route.handler_method, method)
self.request.route.handler_method = method
def render(self, resp, status=httplib.OK):
"""Render the response as HTML."""
user = users.get_current_user()
if user:
url = users.create_logout_url(self.request.uri)
url_linktext = "Logout"
else:
url = users.create_login_url(self.request.uri)
url_linktext = "Login"
resp.update({
# Defaults go here.
'now': datetime.datetime.now(),
'dest_url': str(self.request.get('dest_url', '')),
'form_errors': self.session.pop('form_errors', []),
'user': user,
'url': url,
'url_linktext': url_linktext,
"convert_time": datetime_util.GetTimeWithTimezone
})
if 'preload' not in resp:
resp['preload'] = {}
self.response.status_int = status
self.response.status_message = httplib.responses[status]
self.response.write(self.jinja2.render_template(self.template, **resp))
@webapp2.cached_property
def jinja2(self):
"""Returns a Jinja2 renderer cached in the app registry."""
jinja_config = {
'template_path':
os.path.join(os.path.dirname(__file__), "../../static"),
'compiled_path':
None,
'force_compiled':
False,
'environment_args': {
'autoescape': True,
'extensions': [
'jinja2.ext.autoescape',
'jinja2.ext.with_',
],
},
'globals':
None,
'filters':
None,
}
return wa2_jinja2.Jinja2(app=self.app, config=jinja_config)