Esempio n. 1
0
    def create_item(self, path):
        """Creates a new Location object for the site.

        The location path should be canonical and should not contain
        parts that are not used for access control (query, fragment,
        parameters). Location should not contain non-ascii characters.

        Raises:
            ValidationError if the path is invalid or if a site
            already has a location with such path.
            LimitExceeded if the site defines a maximum number of
            locations and adding a new one would exceed this number.
        """

        locations_limit = self.site.locations_limit
        if (locations_limit is not None and self.count() >= locations_limit):
            raise LimitExceeded('Locations limit exceeded')

        if not url_utils.is_canonical(path):
            raise ValidationError(
                'Path should be absolute and normalized (starting with / '\
                    'without /../ or /./ or //).')
        if len(path) > self.PATH_LEN_LIMIT:
            raise ValidationError('Path too long')
        if url_utils.contains_fragment(path):
            raise ValidationError(
                "Path should not contain fragment ('#' part).")
        if url_utils.contains_query(path):
            raise ValidationError(
                "Path should not contain query ('?' part).")
        if url_utils.contains_params(path):
            raise ValidationError(
                "Path should not contain parameters (';' part).")
        try:
            path.encode('ascii')
        except UnicodeError:
            raise ValidationError(
                'Path should contain only ascii characters.')

        if self.get_unique(lambda item: item.path == path) is not None:
            raise ValidationError('Location already exists.')

        return self._do_create_item(path=path)
    def create_item(self, path):
        """Creates a new Location object for the site.

        The location path should be canonical and should not contain
        parts that are not used for access control (query, fragment,
        parameters). Location should not contain non-ascii characters.

        Raises:
            ValidationError if the path is invalid or if a site
            already has a location with such path.
            LimitExceeded if the site defines a maximum number of
            locations and adding a new one would exceed this number.
        """

        locations_limit = self.site.locations_limit
        if (locations_limit is not None and self.count() >= locations_limit):
            raise LimitExceeded('Locations limit exceeded')

        if not url_utils.is_canonical(path):
            raise ValidationError(
                'Path should be absolute and normalized (starting with / '\
                    'without /../ or /./ or //).')
        if len(path) > self.PATH_LEN_LIMIT:
            raise ValidationError('Path too long')
        if url_utils.contains_fragment(path):
            raise ValidationError(
                "Path should not contain fragment ('#' part).")
        if url_utils.contains_query(path):
            raise ValidationError("Path should not contain query ('?' part).")
        if url_utils.contains_params(path):
            raise ValidationError(
                "Path should not contain parameters (';' part).")
        try:
            path.encode('ascii')
        except UnicodeError:
            raise ValidationError('Path should contain only ascii characters.')

        if self.get_unique(lambda item: item.path == path) is not None:
            raise ValidationError('Location already exists.')

        return self._do_create_item(path=path)
Esempio n. 3
0
    def get(self, request):
        """Invoked by the HTTP server with a single path argument.

        The HTTP server should pass the path argument verbatim,
        without any transformations or decoding. Access control
        mechanism should work on user visible paths, not paths after
        internal rewrites performed by the server.

        At the moment, the path is allowed to contain a query part,
        which is ignored (this is because nginx does not expose
        encoded path without the query part).

        The method follows be conservative in what you accept
        principle. The path should be absolute and normalized, without
        fragment id, otherwise access is denied. Browsers in normal
        operations perform path normalization and do not send fragment
        id. Multiple consecutive '/' separators are permitted, because
        these are not normalized by browsers, and are used by
        legitimate applications.  Paths with '/./' and '/../', should
        not be normally sent by browsers and can be a sign of
        something suspicious happening. It is extremely important that
        wwwhisper does not perform any path transformations that are
        not be compatible with transformations done by the HTTP
        server.
       """
        encoded_path = self._extract_encoded_path_argument(request)
        if encoded_path is None:
            return http.HttpResponseBadRequest(
                "Auth request should have 'path' argument.")

        # Do not allow requests that contain the 'User' header. The
        # header is passed to backends and must be guaranteed to be
        # set by wwwhisper.
        # This check should already be performed by HTTP server.
        if 'HTTP_USER' in request.META:
            return http.HttpResponseBadRequest(
                "Client can not set the 'User' header")

        debug_msg = "Auth request to '%s'" % (encoded_path)

        path_validation_error = None
        if url_utils.contains_fragment(encoded_path):
            path_validation_error = "Path should not include fragment ('#')"
        else:
            stripped_path = url_utils.strip_query(encoded_path)
            decoded_path = url_utils.decode(stripped_path)
            decoded_path = url_utils.collapse_slashes(decoded_path)
            if not url_utils.is_canonical(decoded_path):
                path_validation_error = 'Path should be absolute and ' \
                    'normalized (starting with / without /../ or /./ or //).'
        if path_validation_error is not None:
            logger.debug('%s: incorrect path.' % (debug_msg))
            return http.HttpResponseBadRequest(path_validation_error)

        user = _get_user(request)
        location = request.site.locations.find_location(decoded_path)
        if user is not None:

            debug_msg += " by '%s'" % (user.email)
            respone = None

            if location is not None and location.can_access(user):
                logger.debug('%s: access granted.' % (debug_msg))
                response = http.HttpResponseOK('Access granted.')
            else:
                logger.debug('%s: access denied.' % (debug_msg))
                response = http.HttpResponseNotAuthorized(
                    _html_or_none(request, 'not_authorized.html',
                                  {'email': user.email}))
            response['User'] = user.email
            return response

        if (location is not None and location.open_access_granted()
                and not location.open_access_requires_login()):
            logger.debug('%s: authentication not required, access granted.' %
                         (debug_msg))
            return http.HttpResponseOK('Access granted.')
        logger.debug('%s: user not authenticated.' % (debug_msg))
        return http.HttpResponseNotAuthenticated(
            _html_or_none(request, 'login.html', request.site.skin()))
Esempio n. 4
0
 def test_contains_fragment(self):
     self.assertTrue(contains_fragment('/foo#123'))
     self.assertTrue(contains_fragment('/foo#'))
     # Encoded '#' should not be treated as fragment separator.
     self.assertFalse(contains_fragment('/foo%23'))
Esempio n. 5
0
    def get(self, request):
        """Invoked by the HTTP server with a single path argument.

        The HTTP server should pass the path argument verbatim,
        without any transformations or decoding. Access control
        mechanism should work on user visible paths, not paths after
        internal rewrites performed by the server.

        At the moment, the path is allowed to contain a query part,
        which is ignored (this is because nginx does not expose
        encoded path without the query part).

        The method follows be conservative in what you accept
        principle. The path should be absolute and normalized, without
        fragment id, otherwise access is denied. Browsers in normal
        operations perform path normalization and do not send fragment
        id. Multiple consecutive '/' separators are permitted, because
        these are not normalized by browsers, and are used by
        legitimate applications.  Paths with '/./' and '/../', should
        not be normally sent by browsers and can be a sign of
        something suspicious happening. It is extremely important that
        wwwhisper does not perform any path transformations that are
        not be compatible with transformations done by the HTTP
        server.
       """
        encoded_path = self._extract_encoded_path_argument(request)
        if encoded_path is None:
            return http.HttpResponseBadRequest(
                "Auth request should have 'path' argument.")

        # Do not allow requests that contain the 'User' header. The
        # header is passed to backends and must be guaranteed to be
        # set by wwwhisper.
        # This check should already be performed by HTTP server.
        if 'HTTP_USER' in request.META:
            return http.HttpResponseBadRequest(
                "Client can not set the 'User' header")

        debug_msg = "Auth request to '%s'" % (encoded_path)

        path_validation_error = None
        if url_utils.contains_fragment(encoded_path):
            path_validation_error = "Path should not include fragment ('#')"
        else:
            stripped_path = url_utils.strip_query(encoded_path)
            decoded_path = url_utils.decode(stripped_path)
            decoded_path = url_utils.collapse_slashes(decoded_path)
            if not url_utils.is_canonical(decoded_path):
                path_validation_error = 'Path should be absolute and ' \
                    'normalized (starting with / without /../ or /./ or //).'
        if path_validation_error is not None:
            logger.debug('%s: incorrect path.' % (debug_msg))
            return http.HttpResponseBadRequest(path_validation_error)

        user = _get_user(request)
        location = request.site.locations.find_location(decoded_path)
        if user is not None:

            debug_msg += " by '%s'" % (user.email)
            respone = None

            if location is not None and location.can_access(user):
                logger.debug('%s: access granted.' % (debug_msg))
                response =  http.HttpResponseOK('Access granted.')
            else:
                logger.debug('%s: access denied.' % (debug_msg))
                response = http.HttpResponseNotAuthorized(
                    _html_or_none(request, 'not_authorized.html',
                                  {'email' : user.email}))
            response['User'] = user.email
            return response

        if (location is not None and location.open_access_granted() and
            not location.open_access_requires_login()):
            logger.debug('%s: authentication not required, access granted.'
                         % (debug_msg))
            return http.HttpResponseOK('Access granted.')
        logger.debug('%s: user not authenticated.' % (debug_msg))
        return http.HttpResponseNotAuthenticated(
            _html_or_none(request, 'login.html', request.site.skin()))