From 9a1eeb57e947cf47823cc4a2ff39eac9fb26ce95 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Tue, 26 Apr 2016 14:34:49 +0200 Subject: [PATCH] Controll of the project directory in the controller --- gns3server/compute/project.py | 3 -- gns3server/controller/project.py | 51 +++++++++++++++++++++++++++++++- tests/compute/test_project.py | 7 ----- tests/controller/test_project.py | 29 ++++++++++++++++-- 4 files changed, 77 insertions(+), 13 deletions(-) diff --git a/gns3server/compute/project.py b/gns3server/compute/project.py index 5140bbd2..dd771cf5 100644 --- a/gns3server/compute/project.py +++ b/gns3server/compute/project.py @@ -127,9 +127,6 @@ class Project: if path != self._path and self.is_local() is False: raise aiohttp.web.HTTPForbidden(text="You are not allowed to modify the project directory path") - if '"' in path: - raise aiohttp.web.HTTPForbidden(text="You are not allowed to use \" in the project directory path. It's not supported by Dynamips.") - self._path = path self._update_temporary_file() diff --git a/gns3server/controller/project.py b/gns3server/controller/project.py index 51ecc5e7..f273b8e1 100644 --- a/gns3server/controller/project.py +++ b/gns3server/controller/project.py @@ -15,6 +15,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +import os import asyncio import aiohttp from uuid import UUID, uuid4 @@ -23,6 +24,7 @@ from contextlib import contextmanager from .vm import VM from .udp_link import UDPLink from ..notification_queue import NotificationQueue +from ..config import Config class Project: @@ -45,7 +47,13 @@ class Project: except ValueError: raise aiohttp.web.HTTPBadRequest(text="{} is not a valid UUID".format(project_id)) self._id = project_id - self._path = path + + #TODO: Security check if not locale + if path is None: + location = self._config().get("project_directory", self._get_default_project_directory()) + path = os.path.join(location, self._id) + self.path = path + self._temporary = temporary self._computes = set() self._vms = {} @@ -68,6 +76,30 @@ class Project: def path(self): return self._path + @path.setter + def path(self, path): + try: + os.makedirs(path, exist_ok=True) + except OSError as e: + raise aiohttp.web.HTTPInternalServerError(text="Could not create project directory: {}".format(e)) + + if '"' in path: + raise aiohttp.web.HTTPForbidden(text="You are not allowed to use \" in the project directory path. It's not supported by Dynamips.") + + self._path = path + + def _config(self): + return Config.instance().get_section_config("Server") + + @property + def captures_directory(self): + """ + Location of the captures file + """ + path = os.path.join(self._path, "project-files", "captures") + os.makedirs(path, exist_ok=True) + return path + @asyncio.coroutine def addCompute(self, compute): self._computes.add(compute) @@ -142,6 +174,7 @@ class Project: def delete(self): for compute in self._computes: yield from compute.delete("/projects/{}".format(self._id)) + shutil.rmtree(self.path) @contextmanager def queue(self): @@ -166,6 +199,22 @@ class Project: for listener in self._listeners: listener.put_nowait((action, event, kwargs)) + @classmethod + def _get_default_project_directory(cls): + """ + Return the default location for the project directory + depending of the operating system + """ + + server_config = Config.instance().get_section_config("Server") + path = os.path.expanduser(server_config.get("projects_path", "~/GNS3/projects")) + path = os.path.normpath(path) + try: + os.makedirs(path, exist_ok=True) + except OSError as e: + raise aiohttp.web.HTTPInternalServerError(text="Could not create project directory: {}".format(e)) + return path + def __json__(self): return { diff --git a/tests/compute/test_project.py b/tests/compute/test_project.py index d7254fef..8f171308 100644 --- a/tests/compute/test_project.py +++ b/tests/compute/test_project.py @@ -118,13 +118,6 @@ def test_changing_path_not_allowed(tmpdir): p.path = str(tmpdir) -def test_changing_path_with_quote_not_allowed(tmpdir): - with patch("gns3server.compute.project.Project.is_local", return_value=True): - with pytest.raises(aiohttp.web.HTTPForbidden): - p = Project(project_id=str(uuid4())) - p.path = str(tmpdir / "project\"53") - - def test_json(tmpdir): p = Project(project_id=str(uuid4())) assert p.__json__() == {"name": p.name, "project_id": p.id, "temporary": False} diff --git a/tests/controller/test_project.py b/tests/controller/test_project.py index 27dac1e6..94119754 100644 --- a/tests/controller/test_project.py +++ b/tests/controller/test_project.py @@ -16,13 +16,16 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +import os import pytest import aiohttp from unittest.mock import MagicMock from tests.utils import AsyncioMagicMock - +from unittest.mock import patch +from uuid import uuid4 from gns3server.controller.project import Project +from gns3server.config import Config def test_affect_uuid(): @@ -35,7 +38,29 @@ def test_affect_uuid(): def test_json(tmpdir): p = Project() - assert p.__json__() == {"name": p.name, "project_id": p.id, "temporary": False, "path": None} + assert p.__json__() == {"name": p.name, "project_id": p.id, "temporary": False, "path": p.path} + + +def test_path(tmpdir): + + directory = Config.instance().get_section_config("Server").get("project_directory") + + with patch("gns3server.compute.project.Project._get_default_project_directory", return_value=directory): + p = Project(project_id=str(uuid4())) + assert p.path == os.path.join(directory, p.id) + assert os.path.exists(os.path.join(directory, p.id)) + + +def test_init_path(tmpdir): + + p = Project(path=str(tmpdir), project_id=str(uuid4())) + assert p.path == str(tmpdir) + + +def test_changing_path_with_quote_not_allowed(tmpdir): + with pytest.raises(aiohttp.web.HTTPForbidden): + p = Project(project_id=str(uuid4())) + p.path = str(tmpdir / "project\"53") def test_addVM(async_run):