/python/google/appengine/tools/devappserver2/url_handler.py

https://gitlab.com/gregtyka/frankenserver · Python · 158 lines · 85 code · 15 blank · 58 comment · 15 complexity · fa2da1875b300bab4b3e9da545c16ca3 MD5 · raw file

  1. #!/usr/bin/env python
  2. #
  3. # Copyright 2007 Google Inc.
  4. #
  5. # Licensed under the Apache License, Version 2.0 (the "License");
  6. # you may not use this file except in compliance with the License.
  7. # You may obtain a copy of the License at
  8. #
  9. # http://www.apache.org/licenses/LICENSE-2.0
  10. #
  11. # Unless required by applicable law or agreed to in writing, software
  12. # distributed under the License is distributed on an "AS IS" BASIS,
  13. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. # See the License for the specific language governing permissions and
  15. # limitations under the License.
  16. #
  17. """Base functionality for handling HTTP requests."""
  18. import logging
  19. import wsgiref.util
  20. from google.appengine.api import appinfo
  21. from google.appengine.tools.devappserver2 import constants
  22. from google.appengine.tools.devappserver2 import login
  23. class URLHandler(object):
  24. """Abstract base class for subclasses that handle HTTP requests for a URL."""
  25. def __init__(self, url_pattern):
  26. """Initializer for URLHandler.
  27. Args:
  28. url_pattern: A re.RegexObject that matches URLs that should be handled by
  29. this handler. It may also optionally bind groups.
  30. """
  31. self._url_pattern = url_pattern
  32. def match(self, url):
  33. """Tests whether a given URL string matches this handler.
  34. Args:
  35. url: A URL string to match.
  36. Returns:
  37. A re.MatchObject containing the result of the match, if the URL string
  38. matches this handler. None, otherwise.
  39. """
  40. return self._url_pattern.match(url)
  41. def handle_authorization(self, environ, start_response):
  42. """Handles the response if the user is not authorized to access this URL.
  43. If the user is authorized, this method returns None without side effects.
  44. The default behaviour is to always authorize the user.
  45. If the user is not authorized, this method acts as a WSGI handler, calling
  46. the start_response function and returning the message body. The response
  47. will either redirect to the login page, or contain an error message, as
  48. specified by the 'auth_fail_action' setting.
  49. Args:
  50. environ: An environ dict for the current request as defined in PEP-333.
  51. start_response: A function with semantics defined in PEP-333.
  52. Returns:
  53. An iterable over strings containing the body of an HTTP response, if the
  54. authorization check fails or the login UI must be displayed. None if the
  55. user is authorized to access the resource.
  56. """
  57. return None
  58. def handle(self, match, environ, start_response):
  59. """Serves the content associated with this handler.
  60. Args:
  61. match: The re.MatchObject containing the result of matching the URL
  62. against this handler's URL pattern.
  63. environ: An environ dict for the current request as defined in PEP-333.
  64. start_response: A function with semantics defined in PEP-333.
  65. Returns:
  66. An iterable over strings containing the body of the HTTP response, or
  67. None if this handler is not applicable to this URL.
  68. """
  69. raise NotImplementedError()
  70. class UserConfiguredURLHandler(URLHandler):
  71. """Abstract base class for handlers configured by the user.
  72. This provides common functionality for handlers that need to obey
  73. authorization restrictions.
  74. """
  75. def __init__(self, url_map, url_pattern):
  76. """Initializer for UserConfiguredURLHandler.
  77. Args:
  78. url_map: An appinfo.URLMap instance containing the configuration for this
  79. handler.
  80. url_pattern: A re.RegexObject that matches URLs that should be handled by
  81. this handler. It may also optionally bind groups.
  82. """
  83. super(UserConfiguredURLHandler, self).__init__(url_pattern)
  84. self._url_map = url_map
  85. def handle_authorization(self, environ, start_response):
  86. """Handles the response if the user is not authorized to access this URL.
  87. The authorization check is based on the 'login' setting for this handler,
  88. configured by the supplied url_map.
  89. Args:
  90. environ: An environ dict for the current request as defined in PEP-333.
  91. start_response: A function with semantics defined in PEP-333.
  92. Returns:
  93. An iterable over strings containing the body of an HTTP response, if the
  94. authorization check fails or the login UI must be displayed. None if the
  95. user is authorized to access the resource.
  96. """
  97. admin_only = self._url_map.login == appinfo.LOGIN_ADMIN
  98. requires_login = self._url_map.login == appinfo.LOGIN_REQUIRED or admin_only
  99. auth_fail_action = self._url_map.auth_fail_action
  100. cookies = environ.get('HTTP_COOKIE')
  101. email_addr, admin, _ = login.get_user_info(cookies)
  102. if constants.FAKE_IS_ADMIN_HEADER in environ:
  103. admin = True
  104. if constants.FAKE_LOGGED_IN_HEADER in environ:
  105. email_addr = 'Fake User'
  106. # admin has an effect only with login: admin (not login: required).
  107. if requires_login and not email_addr and not (admin and admin_only):
  108. if auth_fail_action == appinfo.AUTH_FAIL_ACTION_REDIRECT:
  109. logging.debug('login required, redirecting user')
  110. return login.login_redirect(wsgiref.util.application_uri(environ),
  111. wsgiref.util.request_uri(environ),
  112. start_response)
  113. elif auth_fail_action == appinfo.AUTH_FAIL_ACTION_UNAUTHORIZED:
  114. logging.debug('login required, user unauthorized')
  115. start_response('401 Not authorized', [('Content-Type', 'text/html'),
  116. ('Cache-Control', 'no-cache')])
  117. return ['Login required to view page.']
  118. elif admin_only and not admin:
  119. logging.debug('admin required, user unauthorized')
  120. start_response('401 Not authorized', [('Content-Type', 'text/html'),
  121. ('Cache-Control', 'no-cache')])
  122. return ['Current logged in user %s is not '
  123. 'authorized to view this page.'
  124. % email_addr]
  125. # Authorization check succeeded
  126. return None