| # |
| # 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) |