def test_parameter_subdomain_redirect(self):
     """Verify that we redirect a host-based subdomain properly."""
     self.init('/?subdomain=japan', 'personfinder.appspot.com')
     legacy_redirect.redirect(self.handler)
     self.assertEquals(301, self.handler.response.status_int)
     self.assertEquals('http://google.org/personfinder/japan/',
                       self.handler.response.headers['Location'])
 def test_parameter_subdomain_redirect(self):
     """Verify that we redirect a host-based subdomain properly."""
     self.init('/?subdomain=japan', 'personfinder.appspot.com')
     legacy_redirect.redirect(self.handler)
     self.assertEquals(301, self.handler.response.status)
     self.assertEquals('http://google.org/personfinder/japan/',
                       self.handler.response.headers['Location'])
 def test_dotorg_redirect(self):
     """Verify that personfinder.google.org redirects work."""
     self.init('/view?given_name=&id=turkey-2011.person-finder.appspot.com'
               '%2Fperson.1141073&family_name=&query=ahmet&role=seek',
               host='turkey-2011.personfinder.google.org')
     legacy_redirect.redirect(self.handler)
     self.assertEquals(301, self.handler.response.status)
     self.assertEquals(
         'http://google.org/personfinder/turkey-2011/view?'
         'id=turkey-2011.person-finder.appspot.com'
         '%2Fperson.1141073&query=ahmet&role=seek',
         self.handler.response.headers['Location'])
 def test_subdomain_action(self):
     """Verify that a random action gets redirected properly."""
     self.init('/view?given_name=&id=turkey-2011.person-finder.appspot.com'
               '%2Fperson.1141073&family_name=&query=ahmet&role=seek',
               host='turkey-2011.googlepersonfinder.appspot.com')
     legacy_redirect.redirect(self.handler)
     self.assertEquals(301, self.handler.response.status)
     # note that we stripped out the empty params here.
     self.assertEquals(
         'http://google.org/personfinder/turkey-2011'
         '/view?id=turkey-2011.person-finder.appspot.com'
         '%2Fperson.1141073&query=ahmet&role=seek',
         self.handler.response.headers['Location'])
 def test_dotorg_redirect(self):
     """Verify that personfinder.google.org redirects work."""
     self.init(
         '/view?given_name=&id=turkey-2011.person-finder.appspot.com'
         '%2Fperson.1141073&family_name=&query=ahmet&role=seek',
         host='turkey-2011.personfinder.google.org')
     legacy_redirect.redirect(self.handler)
     self.assertEquals(301, self.handler.response.status_int)
     self.assertEquals(
         'http://google.org/personfinder/turkey-2011/view?'
         'id=turkey-2011.person-finder.appspot.com'
         '%2Fperson.1141073&query=ahmet&role=seek',
         self.handler.response.headers['Location'])
 def test_subdomain_action(self):
     """Verify that a random action gets redirected properly."""
     self.init(
         '/view?given_name=&id=turkey-2011.person-finder.appspot.com'
         '%2Fperson.1141073&family_name=&query=ahmet&role=seek',
         host='turkey-2011.googlepersonfinder.appspot.com')
     legacy_redirect.redirect(self.handler)
     self.assertEquals(301, self.handler.response.status_int)
     # note that we stripped out the empty params here.
     self.assertEquals(
         'http://google.org/personfinder/turkey-2011'
         '/view?id=turkey-2011.person-finder.appspot.com'
         '%2Fperson.1141073&query=ahmet&role=seek',
         self.handler.response.headers['Location'])
 def test_dotorg_redirect(self):
     """Verify that personfinder.google.org redirects work."""
     self.init(
         "/view?given_name=&id=turkey-2011.person-finder.appspot.com"
         "%2Fperson.1141073&family_name=&query=ahmet&role=seek",
         host="turkey-2011.personfinder.google.org",
     )
     legacy_redirect.redirect(self.handler)
     self.assertEquals(301, self.handler.response.status_int)
     self.assertEquals(
         "http://google.org/personfinder/turkey-2011/view?"
         "id=turkey-2011.person-finder.appspot.com"
         "%2Fperson.1141073&query=ahmet&role=seek",
         self.handler.response.headers["Location"],
     )
Esempio n. 8
0
    def initialize(self, request, response):
        webapp.RequestHandler.initialize(self, request, response)

        # If requested, set the clock before doing anything clock-related.
        # Only works on localhost for testing.  Specify ?utcnow=1293840000 to
        # set the clock to 2011-01-01, or ?utcnow=real to revert to real time.
        utcnow = request.get('utcnow')
        if request.remote_addr == '127.0.0.1' and utcnow:
            if utcnow == 'real':
                utils.set_utcnow_for_test(None)
            else:
                utils.set_utcnow_for_test(float(utcnow))

        # If requested, flush caches before we touch anything that uses them.
        flush_caches(*request.get('flush', '').split(','))

        # check for legacy redirect:
        # TODO(lschumacher|kpy): remove support for legacy URLS Q1 2012.
        if legacy_redirect.do_redirect(self):
            # stub out get/head to prevent failures.
            self.get = self.head = lambda *args: None
            return legacy_redirect.redirect(self)

        # Gather commonly used information into self.env.
        self.env = setup_env(request)

        # Force a redirect if requested, except where https is not supported:
        # - for cron jobs
        # - for task queue jobs
        # - in development
        if (self.env.force_https and self.env.scheme == 'http'
            and not is_cron_task(self.request)
            and not is_task_queue_task(self.request)
            and not is_development_server()):
            self.redirect(self.env.url.replace('http:', 'https:'))

        # Activate the selected language.
        response.headers['Content-Language'] = self.env.lang
        response.headers['Set-Cookie'] = \
            'django_language=%s; path=/' % self.env.lang
        django_setup.activate(self.env.lang)

        # Activate the appropriate resource bundle.
        resources.set_active_bundle_name(self.env.resource_bundle)
Esempio n. 9
0
    def initialize(self, request, response):
        webapp.RequestHandler.initialize(self, request, response)

        # If requested, set the clock before doing anything clock-related.
        # Only works on localhost for testing.  Specify ?utcnow=1293840000 to
        # set the clock to 2011-01-01, or ?utcnow=real to revert to real time.
        utcnow = request.get('utcnow')
        if request.remote_addr == '127.0.0.1' and utcnow:
            if utcnow == 'real':
                utils.set_utcnow_for_test(None)
            else:
                utils.set_utcnow_for_test(float(utcnow))

        # If requested, flush caches before we touch anything that uses them.
        flush_caches(*request.get('flush', '').split(','))

        # check for legacy redirect:
        # TODO(lschumacher|kpy): remove support for legacy URLS Q1 2012.
        if legacy_redirect.do_redirect(self):
            # stub out get/head to prevent failures.
            self.get = self.head = lambda *args: None
            return legacy_redirect.redirect(self)

        # Gather commonly used information into self.env.
        self.env = setup_env(request)

        # Force a redirect if requested, except where https is not supported:
        # - for cron jobs
        # - for task queue jobs
        # - in development
        if (self.env.force_https and self.env.scheme == 'http'
                and not is_cron_task(self.request)
                and not is_task_queue_task(self.request)
                and not is_development_server()):
            self.redirect(self.env.url.replace('http:', 'https:'))

        # Activate the selected language.
        response.headers['Content-Language'] = self.env.lang
        response.headers['Set-Cookie'] = \
            'django_language=%s; path=/' % self.env.lang
        django_setup.activate(self.env.lang)

        # Activate the appropriate resource bundle.
        resources.set_active_bundle_name(self.env.resource_bundle)
Esempio n. 10
0
class BaseHandler(webapp.RequestHandler):
    # Handlers that don't need a repository name can set this to False.
    repo_required = True

    # Handlers that require HTTPS can set this to True.
    https_required = False

    # Set this to True to enable a handler even for deactivated repositories.
    ignore_deactivation = False

    # List all accepted query parameters here with their associated validators.
    auto_params = {
        'add_note': validate_yes,
        'age': validate_age,
        'alternate_family_names': strip,
        'alternate_given_names': strip,
        'author_email': strip,
        'author_made_contact': validate_yes,
        'author_name': strip,
        'author_phone': strip,
        'believed_dead_permission': validate_checkbox_as_bool,
        'cache_seconds': validate_cache_seconds,
        'clone': validate_yes,
        'confirm': validate_yes,
        'contact_email': strip,
        'contact_name': strip,
        'content_id': strip,
        'cursor': strip,
        'date_of_birth': validate_approximate_date,
        'description': strip,
        'domain_write_permission': strip,
        'dupe_notes': validate_yes,
        'email_of_found_person': strip,
        'error': strip,
        'expiry_option': validate_expiry,
        'family_name': strip,
        'full_read_permission': validate_checkbox_as_bool,
        'given_name': strip,
        'home_city': strip,
        'home_country': strip,
        'home_neighborhood': strip,
        'home_postal_code': strip,
        'home_state': strip,
        'home_street': strip,
        'id': strip,
        'id1': strip,
        'id2': strip,
        'id3': strip,
        'is_valid': validate_checkbox_as_bool,
        'key': strip,
        'lang': validate_lang,
        'last_known_location': strip,
        'mark_notes_reviewed': validate_checkbox_as_bool,
        'max_results': validate_int,
        'min_entry_date': validate_datetime,
        'new_repo': validate_repo,
        'note_photo': validate_image,
        'note_photo_url': strip,
        'omit_notes': validate_yes,
        'operation': strip,
        'organization_name': strip,
        'person_record_id': strip,
        'phone_of_found_person': strip,
        'photo': validate_image,
        'photo_url': strip,
        'profile_url1': strip,
        'profile_url2': strip,
        'profile_url3': strip,
        'query': strip,
        'query_type': strip,
        'read_permission': validate_checkbox_as_bool,
        'referrer': strip,
        'resource_bundle': validate_resource_name,
        'resource_bundle_default': validate_resource_name,
        'resource_bundle_original': validate_resource_name,
        'resource_lang': validate_lang,
        'resource_name': validate_resource_name,
        'role': validate_role,
        'search_engine_id': validate_int,
        'search_permission': validate_checkbox_as_bool,
        'sex': validate_sex,
        'signature': strip,
        'skip': validate_int,
        'small': validate_yes,
        'source': strip,
        'source_date': strip,
        'source_name': strip,
        'source_url': strip,
        'stats_permission': validate_checkbox_as_bool,
        'status': validate_status,
        'style': strip,
        'subscribe': validate_checkbox,
        'subscribe_email': strip,
        'subscribe_permission': validate_checkbox_as_bool,
        'suppress_redirect': validate_yes,
        'target': strip,
        'text': strip,
        'ui': strip_and_lower,
        'utcnow': validate_timestamp,
        'version': validate_version,
    }

    def redirect(self, path, repo=None, permanent=False, **params):
        # This will prepend the repo to the path to create a working URL,
        # unless the path has a global prefix or is an absolute URL.
        if re.match('^[a-z]+:', path) or GLOBAL_PATH_RE.match(path):
            if params:
                path += '?' + urlencode(params, self.charset)
        else:
            path = self.get_url(path, repo, **params)
        return webapp.RequestHandler.redirect(self, path, permanent=permanent)

    def render(self,
               name,
               language_override=None,
               cache_seconds=0,
               get_vars=lambda: {},
               **vars):
        """Renders a template to the output stream, passing in the variables
        specified in **vars as well as any additional variables returned by
        get_vars().  Since this is intended for use by a dynamic page handler,
        caching is off by default; if cache_seconds is positive, then
        get_vars() will be called only when cached content is unavailable."""
        self.write(
            self.render_to_string(name, language_override, cache_seconds,
                                  get_vars, **vars))

    def render_to_string(self,
                         name,
                         language_override=None,
                         cache_seconds=0,
                         get_vars=lambda: {},
                         **vars):
        """Renders a template to a string, passing in the variables specified
        in **vars as well as any additional variables returned by get_vars().
        Since this is intended for use by a dynamic page handler, caching is
        off by default; if cache_seconds is positive, then get_vars() will be
        called only when cached content is unavailable."""
        # TODO(kpy): Make the contents of extra_key overridable by callers?
        lang = language_override or self.env.lang
        extra_key = (self.env.repo, self.env.charset,
                     self.request.query_string)

        def get_all_vars():
            vars['env'] = self.env  # pass along application-wide context
            vars['config'] = self.config  # pass along the configuration
            vars['params'] = self.params  # pass along the query parameters
            vars.update(get_vars())
            return vars

        return resources.get_rendered(name, lang, extra_key, get_all_vars,
                                      cache_seconds)

    def error(self, code, message='', message_html=''):
        self.info(code, message, message_html, style='error')

    def info(self, code, message='', message_html='', style='info'):
        """Renders a simple page with a message.

        Args:
          code: HTTP status code.
          message: A message in plain text.
          message_html: A message in HTML.
          style: 'info', 'error' or 'plain'. 'info' and 'error' differs in
              appearance. 'plain' just renders the message without extra
              HTML tags. Good for API response.
        """
        is_error = 400 <= code < 600
        if is_error:
            webapp.RequestHandler.error(self, code)
        else:
            self.response.set_status(code)
        if not message and not message_html:
            message = '%d: %s' % (code, httplib.responses.get(code))
        if style == 'plain':
            self.__render_plain_message(message, message_html)
        else:
            try:
                self.render('message.html',
                            cls=style,
                            message=message,
                            message_html=message_html)
            except:
                self.__render_plain_message(message, message_html)
        self.terminate_response()

    def __render_plain_message(self, message, message_html):
        self.response.out.write(
            django.utils.html.escape(message) +
            ('<p>' if message and message_html else '') + message_html)

    def terminate_response(self):
        """Prevents any further output from being written."""
        self.response.out.write = lambda *args: None
        self.get = lambda *args: None
        self.post = lambda *args: None

    def write(self, text):
        """Sends text to the client using the charset from select_charset()."""
        self.response.out.write(text.encode(self.env.charset, 'replace'))

    def get_url(self, action, repo=None, scheme=None, **params):
        """Constructs the absolute URL for a given action and query parameters,
        preserving the current repo and the parameters listed in
        PRESERVED_QUERY_PARAM_NAMES."""
        return get_url(self.request,
                       repo or self.env.repo,
                       action,
                       charset=self.env.charset,
                       scheme=scheme,
                       **params)

    @staticmethod
    def add_task_for_repo(repo, name, action, **kwargs):
        """Queues up a task for an individual repository."""
        task_name = '%s-%s-%s' % (repo, name, int(time.time() * 1000))
        path = '/%s/%s' % (repo, action)
        taskqueue.add(name=task_name, method='GET', url=path, params=kwargs)

    def send_mail(self, to, subject, body):
        """Sends e-mail using a sender address that's allowed for this app."""
        app_id = get_app_name()
        sender = 'Do not reply <do-not-reply@%s.%s>' % (app_id, EMAIL_DOMAIN)
        logging.info('Add mail task: recipient %r, subject %r' % (to, subject))
        taskqueue.add(queue_name='send-mail',
                      url='/global/admin/send_mail',
                      params={
                          'sender': sender,
                          'to': to,
                          'subject': subject,
                          'body': body
                      })

    def get_captcha_html(self, error_code=None, use_ssl=False):
        """Generates the necessary HTML to display a CAPTCHA validation box."""

        # We use the 'custom_translations' parameter for UI messages, whereas
        # the 'lang' parameter controls the language of the challenge itself.
        # reCAPTCHA falls back to 'en' if this parameter isn't recognized.
        lang = self.env.lang.split('-')[0]

        return captcha.get_display_html(
            public_key=config.get('captcha_public_key'),
            use_ssl=use_ssl,
            error=error_code,
            lang=lang,
            custom_translations={
                # reCAPTCHA doesn't support all languages, so we treat its
                # messages as part of this app's usual translation workflow
                'instructions_visual': _('Type the two words:'),
                'instructions_audio': _('Type what you hear:'),
                'play_again': _('Play the sound again'),
                'cant_hear_this': _('Download the sound as MP3'),
                'visual_challenge': _('Get a visual challenge'),
                'audio_challenge': _('Get an audio challenge'),
                'refresh_btn': _('Get a new challenge'),
                'help_btn': _('Help'),
                'incorrect_try_again': _('Incorrect.  Try again.')
            })

    def get_captcha_response(self):
        """Returns an object containing the CAPTCHA response information for the
        given request's CAPTCHA field information."""
        challenge = self.request.get('recaptcha_challenge_field')
        response = self.request.get('recaptcha_response_field')
        remote_ip = os.environ['REMOTE_ADDR']
        return captcha.submit(challenge, response,
                              config.get('captcha_private_key'), remote_ip)

    def handle_exception(self, exception, debug_mode):
        logging.error(traceback.format_exc())
        self.error(
            500,
            _('There was an error processing your request.  Sorry for the '
              'inconvenience.  Our administrators will investigate the source '
              'of the problem, but please check that the format of your '
              'request is correct.'))

    def to_local_time(self, date):
        """Converts a datetime object to the local time configured for the
        current repository.  For convenience, returns None if date is None."""
        # TODO(kpy): This only works for repositories that have a single fixed
        # time zone offset and never use Daylight Saving Time.
        if date:
            if self.config.time_zone_offset:
                return date + timedelta(0, 3600 * self.config.time_zone_offset)
            return date

    def maybe_redirect_for_repo_alias(self, request):
        """If the specified repository name is an alias, redirects to the URL
        with the canonical repository name and returns True. Otherwise returns
        False.
        """
        # Config repo_alias is a dictionary from a repository name alias to
        # its canonical.
        # e.g., {'yol': '2013-yolanda', 'jam': '2014-jammu-kashmir-floods'}
        #
        # A repository name alias can be used instead of the canonical
        # repository name in URLs. This is especially useful combined with
        # the short URL. e.g., You can access
        # https://www.google.org/personfinder/2014-jammu-kashmir-floods
        # by http://g.co/pf/jam .
        if not self.repo:
            return False
        repo_aliases = config.get('repo_aliases', default={})
        if self.repo in repo_aliases:
            canonical_repo = repo_aliases[self.repo]
            params = {}
            for name in request.arguments():
                params[name] = request.get(name)
            # Redirects to the same URL including the query parameters, except
            # for the repository name.
            self.redirect('/' + self.env.action, repo=canonical_repo, **params)
            self.terminate_response()
            return True
        else:
            return False

    def __init__(self, request, response, env):
        webapp.RequestHandler.__init__(self, request, response)
        self.params = Struct()
        self.env = env
        self.repo = env.repo
        self.config = env.config
        self.charset = env.charset

        # Set default Content-Type header.
        self.response.headers['Content-Type'] = ('text/html; charset=%s' %
                                                 self.charset)

        # Validate query parameters.
        for name, validator in self.auto_params.items():
            try:
                value = self.request.get(name, '')
                setattr(self.params, name, validator(value))
            except Exception, e:
                setattr(self.params, name, validator(None))
                return self.error(400, 'Invalid parameter %s: %s' % (name, e))

        # Ensure referrer is in whitelist, if it exists
        if self.params.referrer and (not self.params.referrer
                                     in self.config.referrer_whitelist):
            setattr(self.params, 'referrer', '')

        # Log the User-Agent header.
        sample_rate = float(self.config and self.config.user_agent_sample_rate
                            or 0)
        if random.random() < sample_rate:
            model.UserAgentLog(
                repo=self.repo,
                sample_rate=sample_rate,
                user_agent=self.request.headers.get('User-Agent'),
                lang=lang,
                accept_charset=self.request.headers.get('Accept-Charset', ''),
                ip_address=self.request.remote_addr).put()

        # Check for SSL (unless running on localhost for development).
        if self.https_required and self.env.domain != 'localhost':
            if self.env.scheme != 'https':
                return self.error(403, 'HTTPS is required.')

        # Handles repository alias.
        if self.maybe_redirect_for_repo_alias(request):
            return

        # Check for an authorization key.
        self.auth = None
        if self.params.key:
            if self.repo:
                # check for domain specific one.
                self.auth = model.Authorization.get(self.repo, self.params.key)
            if not self.auth:
                # perhaps this is a global key ('*' for consistency with config).
                self.auth = model.Authorization.get('*', self.params.key)
        if self.auth and not self.auth.is_valid:
            self.auth = None

        # Handlers that don't need a repository configuration can skip it.
        if not self.repo:
            if self.repo_required:
                return self.error(400, 'No repository specified.')
            return
        # Everything after this requires a repo.

        # Reject requests for repositories that don't exist.
        if not model.Repo.get_by_key_name(self.repo):
            if legacy_redirect.do_redirect(self):
                return legacy_redirect.redirect(self)
            html = 'No such repository. '
            if self.env.repo_options:
                html += 'Select:<p>' + self.render_to_string('repo-menu.html')
            return self.error(404, message_html=html)

        # If this repository has been deactivated, terminate with a message.
        # The ignore_deactivation flag is for admin pages that bypass this.
        if self.config.deactivated and not self.ignore_deactivation:
            self.env.language_menu = []
            self.env.robots_ok = True
            self.render('message.html',
                        cls='deactivation',
                        message_html=self.config.deactivation_message_html)
            self.terminate_response()
 def test_parameter_subdomain_redirect(self):
     """Verify that we redirect a host-based subdomain properly."""
     self.init("/?subdomain=japan", "personfinder.appspot.com")
     legacy_redirect.redirect(self.handler)
     self.assertEquals(301, self.handler.response.status_int)
     self.assertEquals("http://google.org/personfinder/japan/", self.handler.response.headers["Location"])