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. Not normalized paths can be a sign of something suspicious happening. It is extremely important that wwwhisper does not perform any tricky path transformations that may 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.") debug_msg = "Auth request to '%s'" % (encoded_path) path_validation_error = None if url_path.contains_fragment(encoded_path): path_validation_error = "Path should not include fragment ('#')" else: stripped_path = url_path.strip_query(encoded_path) decoded_path = url_path.decode(stripped_path) if not url_path.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 = request.user location = self.locations_collection.find_location(decoded_path) if user and user.is_authenticated(): debug_msg += " by '%s'" % (user.email) if location is not None and location.can_access(user.uuid): logger.debug('%s: access granted.' % (debug_msg)) return http.HttpResponseOK('Access granted.') logger.debug('%s: access denied.' % (debug_msg)) return http.HttpResponseNotAuthorized() if location is not None and location.open_access: 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()
def create_item(self, path): """Creates a new Location object. 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. Args: path: A canonical path to the location. Raises: CreationException if the path is invalid or if a location with such path already exists. """ if not url_path.is_canonical(path): raise CreationException( 'Path should be absolute and normalized (starting with / '\ 'without /../ or /./ or //).') if url_path.contains_fragment(path): raise CreationException( "Path should not contain fragment ('#' part).") if url_path.contains_query(path): raise CreationException( "Path should not contain query ('?' part).") if url_path.contains_params(path): raise CreationException( "Path should not contain parameters (';' part).") try: if path.encode('utf-8', 'strict') != path: raise CreationException( 'Path should contain only ascii characters.') except UnicodeError: raise CreationException('Invalid path encoding') if _find(Location, path=path) is not None: raise CreationException('Location already exists.') location = Location.objects.create(path=path) location.save() return location
def test_is_canonical(self): self.assertTrue(is_canonical("/")) self.assertTrue(is_canonical("/foo/bar")) self.assertTrue(is_canonical("/foo/bar/")) self.assertTrue(is_canonical("/foo/bar/ ")) self.assertFalse(is_canonical("")) self.assertFalse(is_canonical("foo")) self.assertFalse(is_canonical("//")) self.assertFalse(is_canonical(" /")) self.assertFalse(is_canonical(" //")) self.assertFalse(is_canonical("//foo")) self.assertFalse(is_canonical("/foo/bar/..")) self.assertFalse(is_canonical("/foo//bar")) self.assertFalse(is_canonical("/foo/bar//")) self.assertFalse(is_canonical("/foo/bar/./foo"))