mirror of
https://github.com/GNS3/gns3-server.git
synced 2025-01-31 05:13:49 +02:00
API for editing files on the controller
Ref https://github.com/GNS3/gns3-gui/issues/1401
This commit is contained in:
parent
bc8a319142
commit
e557ccd078
@ -15,6 +15,7 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import os
|
||||||
import aiohttp
|
import aiohttp
|
||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
@ -249,3 +250,85 @@ class ProjectHandler:
|
|||||||
yield from response.drain()
|
yield from response.drain()
|
||||||
|
|
||||||
yield from response.write_eof()
|
yield from response.write_eof()
|
||||||
|
|
||||||
|
@Route.get(
|
||||||
|
r"/projects/{project_id}/files/{path:.+}",
|
||||||
|
description="Get a file from a project. Beware you have warranty to be able to access only to file global to the project (for example README.txt)",
|
||||||
|
parameters={
|
||||||
|
"project_id": "Project UUID",
|
||||||
|
},
|
||||||
|
status_codes={
|
||||||
|
200: "File returned",
|
||||||
|
403: "Permission denied",
|
||||||
|
404: "The file doesn't exist"
|
||||||
|
})
|
||||||
|
def get_file(request, response):
|
||||||
|
|
||||||
|
controller = Controller.instance()
|
||||||
|
project = controller.get_project(request.match_info["project_id"])
|
||||||
|
path = request.match_info["path"]
|
||||||
|
path = os.path.normpath(path)
|
||||||
|
|
||||||
|
# Raise error if user try to escape
|
||||||
|
if path[0] == ".":
|
||||||
|
raise aiohttp.web.HTTPForbidden
|
||||||
|
path = os.path.join(project.path, path)
|
||||||
|
|
||||||
|
response.content_type = "application/octet-stream"
|
||||||
|
response.set_status(200)
|
||||||
|
response.enable_chunked_encoding()
|
||||||
|
# Very important: do not send a content length otherwise QT closes the connection (curl can consume the feed)
|
||||||
|
response.content_length = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(path, "rb") as f:
|
||||||
|
response.start(request)
|
||||||
|
while True:
|
||||||
|
data = f.read(4096)
|
||||||
|
if not data:
|
||||||
|
break
|
||||||
|
yield from response.write(data)
|
||||||
|
|
||||||
|
except FileNotFoundError:
|
||||||
|
raise aiohttp.web.HTTPNotFound()
|
||||||
|
except PermissionError:
|
||||||
|
raise aiohttp.web.HTTPForbidden()
|
||||||
|
|
||||||
|
@Route.post(
|
||||||
|
r"/projects/{project_id}/files/{path:.+}",
|
||||||
|
description="Write a file to a project",
|
||||||
|
parameters={
|
||||||
|
"project_id": "Project UUID",
|
||||||
|
},
|
||||||
|
raw=True,
|
||||||
|
status_codes={
|
||||||
|
200: "File returned",
|
||||||
|
403: "Permission denied",
|
||||||
|
404: "The path doesn't exist"
|
||||||
|
})
|
||||||
|
def write_file(request, response):
|
||||||
|
|
||||||
|
controller = Controller.instance()
|
||||||
|
project = controller.get_project(request.match_info["project_id"])
|
||||||
|
path = request.match_info["path"]
|
||||||
|
path = os.path.normpath(path)
|
||||||
|
|
||||||
|
# Raise error if user try to escape
|
||||||
|
if path[0] == ".":
|
||||||
|
raise aiohttp.web.HTTPForbidden
|
||||||
|
path = os.path.join(project.path, path)
|
||||||
|
|
||||||
|
response.set_status(200)
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(path, 'wb+') as f:
|
||||||
|
while True:
|
||||||
|
packet = yield from request.content.read(512)
|
||||||
|
if not packet:
|
||||||
|
break
|
||||||
|
f.write(packet)
|
||||||
|
|
||||||
|
except FileNotFoundError:
|
||||||
|
raise aiohttp.web.HTTPNotFound()
|
||||||
|
except PermissionError:
|
||||||
|
raise aiohttp.web.HTTPForbidden()
|
||||||
|
@ -167,6 +167,21 @@ def test_get_file(http_compute, tmpdir):
|
|||||||
assert response.status == 403
|
assert response.status == 403
|
||||||
|
|
||||||
|
|
||||||
|
def test_write_file(http_compute, tmpdir):
|
||||||
|
|
||||||
|
with patch("gns3server.config.Config.get_section_config", return_value={"projects_path": str(tmpdir)}):
|
||||||
|
project = ProjectManager.instance().create_project(project_id="01010203-0405-0607-0809-0a0b0c0d0e0b")
|
||||||
|
|
||||||
|
response = http_compute.post("/projects/{project_id}/files/hello".format(project_id=project.id), body="world", raw=True)
|
||||||
|
assert response.status == 200
|
||||||
|
|
||||||
|
with open(os.path.join(project.path, "hello")) as f:
|
||||||
|
assert f.read() == "world"
|
||||||
|
|
||||||
|
response = http_compute.post("/projects/{project_id}/files/../hello".format(project_id=project.id), raw=True)
|
||||||
|
assert response.status == 403
|
||||||
|
|
||||||
|
|
||||||
def test_stream_file(http_compute, tmpdir):
|
def test_stream_file(http_compute, tmpdir):
|
||||||
|
|
||||||
with patch("gns3server.config.Config.get_section_config", return_value={"projects_path": str(tmpdir)}):
|
with patch("gns3server.config.Config.get_section_config", return_value={"projects_path": str(tmpdir)}):
|
||||||
|
@ -175,3 +175,30 @@ def test_export(http_controller, tmpdir, loop, project):
|
|||||||
with myzip.open("a") as myfile:
|
with myzip.open("a") as myfile:
|
||||||
content = myfile.read()
|
content = myfile.read()
|
||||||
assert content == b"hello"
|
assert content == b"hello"
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_file(http_controller, tmpdir, loop, project):
|
||||||
|
os.makedirs(project.path, exist_ok=True)
|
||||||
|
with open(os.path.join(project.path, 'hello'), 'w+') as f:
|
||||||
|
f.write('world')
|
||||||
|
|
||||||
|
response = http_controller.get("/projects/{project_id}/files/hello".format(project_id=project.id), raw=True)
|
||||||
|
assert response.status == 200
|
||||||
|
assert response.body == b"world"
|
||||||
|
|
||||||
|
response = http_controller.get("/projects/{project_id}/files/false".format(project_id=project.id), raw=True)
|
||||||
|
assert response.status == 404
|
||||||
|
|
||||||
|
response = http_controller.get("/projects/{project_id}/files/../hello".format(project_id=project.id), raw=True)
|
||||||
|
assert response.status == 403
|
||||||
|
|
||||||
|
|
||||||
|
def test_write_file(http_controller, tmpdir, project):
|
||||||
|
response = http_controller.post("/projects/{project_id}/files/hello".format(project_id=project.id), body="world", raw=True)
|
||||||
|
assert response.status == 200
|
||||||
|
|
||||||
|
with open(os.path.join(project.path, "hello")) as f:
|
||||||
|
assert f.read() == "world"
|
||||||
|
|
||||||
|
response = http_controller.post("/projects/{project_id}/files/../hello".format(project_id=project.id), raw=True)
|
||||||
|
assert response.status == 403
|
||||||
|
Loading…
Reference in New Issue
Block a user