Skip to content

Commit fe84e69

Browse files
author
Davide Arcuri
committed
1 parent fb6d942 commit fe84e69

File tree

9 files changed

+346
-184
lines changed

9 files changed

+346
-184
lines changed

examples/local_api.ipynb

+54-3
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,8 @@
7070
"dumps = session.get(f\"{url}/api/dumps/\", verify=False).json()\n",
7171
"print(f\"{len(dumps)} dumps found\")\n",
7272
"if dumps: \n",
73-
" pprint(dumps[0])"
73+
" pprint(dumps[0])\n",
74+
" dump_pk = dumps[0]['index']"
7475
]
7576
},
7677
{
@@ -173,6 +174,7 @@
173174
"if res.status_code == 200:\n",
174175
" if example := res.json():\n",
175176
" print(example[0])\n",
177+
" plugin_pk = example[0]\n",
176178
"else:\n",
177179
" print(res.status_code, res.text) "
178180
]
@@ -190,11 +192,13 @@
190192
"metadata": {},
191193
"outputs": [],
192194
"source": [
195+
"\"\"\" TODO\n",
193196
"res = session.get(f\"{url}/api/dumps/{dump_pk}/plugins/{plugin_pk}/\", verify=False)\n",
194197
"if res.status_code == 200:\n",
195198
" pprint(res.json())\n",
196199
" result_pk = [x['pk'] for x in res.json() if x['plugin'] == 'linux.pslist.PsList'][0]\n",
197-
" print(res.status_code)\n"
200+
" print(res.status_code)\n",
201+
"\"\"\""
198202
]
199203
},
200204
{
@@ -287,6 +291,53 @@
287291
"walker = pyg.walk(df)\n",
288292
"\"\"\""
289293
]
294+
},
295+
{
296+
"cell_type": "markdown",
297+
"metadata": {},
298+
"source": [
299+
"# EDIT DUMP"
300+
]
301+
},
302+
{
303+
"cell_type": "code",
304+
"execution_count": null,
305+
"metadata": {},
306+
"outputs": [],
307+
"source": [
308+
"data = {\n",
309+
" \"name\": \"string\",\n",
310+
" \"folder\": {\n",
311+
" \"name\": \"string\"\n",
312+
" },\n",
313+
" \"color\": \"#7218EE\",\n",
314+
" \"status\": \"1\",\n",
315+
" \"comment\": \"stringghjny\"\n",
316+
"}\n",
317+
"res = session.patch(f\"{url}/api/dumps/{dump_pk}\", json=data, cookies=first.cookies, headers=headers, verify=False)\n",
318+
"if res.status_code == 200:\n",
319+
" pprint(res.json())\n",
320+
" dump_pk = res.json()[\"index\"]\n",
321+
"else:\n",
322+
" print(res.status_code, res.text)"
323+
]
324+
},
325+
{
326+
"cell_type": "markdown",
327+
"metadata": {},
328+
"source": [
329+
"# DELETE DUMP"
330+
]
331+
},
332+
{
333+
"cell_type": "code",
334+
"execution_count": null,
335+
"metadata": {},
336+
"outputs": [],
337+
"source": [
338+
"res = session.delete(f\"{url}/api/dumps/{dump_pk}\", cookies=first.cookies, headers=headers, verify=False)\n",
339+
"print(res.status_code, res.text) "
340+
]
290341
}
291342
],
292343
"metadata": {
@@ -305,7 +356,7 @@
305356
"name": "python",
306357
"nbconvert_exporter": "python",
307358
"pygments_lexer": "ipython3",
308-
"version": "3.10.8"
359+
"version": "3.10.15"
309360
}
310361
},
311362
"nbformat": 4,

orochi/api/models.py

+9
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,15 @@ class Meta:
175175
]
176176

177177

178+
class DumpEditIn(ModelSchema):
179+
folder: Optional[FolderSchema] = None
180+
authorized_users: Optional[List[int]] = None
181+
182+
class Meta:
183+
model = Dump
184+
fields = ["comment", "name", "color", "status"]
185+
186+
178187
class DumpSchema(ModelSchema):
179188
folder: Optional[FolderSchema] = None
180189
author: UserOutSchema = None

orochi/api/routers/dumps.py

+124-3
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,24 @@
44
from uuid import UUID, uuid1
55

66
from django.conf import settings
7+
from django.contrib.auth import get_user_model
78
from django.db import transaction
89
from django.http import HttpResponse
910
from django.shortcuts import get_object_or_404
10-
from guardian.shortcuts import get_objects_for_user
11-
from ninja import File, Query, Router, UploadedFile
11+
from guardian.shortcuts import assign_perm, get_objects_for_user, get_perms, remove_perm
12+
from ninja import File, PatchDict, Query, Router, UploadedFile
1213
from ninja.security import django_auth
1314

1415
from orochi.api.filters import DumpFilters, OperatingSytemFilters
15-
from orochi.api.models import DumpIn, DumpInfoSchema, DumpSchema, ResultSmallOutSchema
16+
from orochi.api.models import (
17+
DumpEditIn,
18+
DumpIn,
19+
DumpInfoSchema,
20+
DumpSchema,
21+
ErrorsOut,
22+
ResultSmallOutSchema,
23+
SuccessResponse,
24+
)
1625
from orochi.website.defaults import RESULT_STATUS_NOT_STARTED, RESULT_STATUS_RUNNING
1726
from orochi.website.models import Dump, Folder, Result, UserPlugin
1827
from orochi.website.views import index_f_and_f
@@ -46,6 +55,44 @@ def list_dumps(request, filters: Query[OperatingSytemFilters]):
4655
return dumps
4756

4857

58+
@router.delete(
59+
"/{pk}",
60+
auth=django_auth,
61+
url_name="delete_dump",
62+
response={200: SuccessResponse, 400: ErrorsOut},
63+
)
64+
def delete_dump(request, pk: UUID):
65+
"""
66+
Deletes a dump identified by its primary key (pk). This function ensures that the user has permission to delete the dump before proceeding with the deletion.
67+
68+
Args:
69+
request: The HTTP request object.
70+
pk (UUID): The primary key of the dump to be deleted.
71+
72+
Returns:
73+
SuccessResponse: A response indicating the successful deletion of the dump.
74+
75+
Raises:
76+
Http404: If the dump with the specified primary key does not exist.
77+
ErrorsOut: If the user does not have permission to delete the dump.
78+
79+
Examples:
80+
DELETE /dumps/{pk}
81+
"""
82+
try:
83+
dump = get_object_or_404(Dump, index=pk)
84+
name = dump.name
85+
if dump not in get_objects_for_user(request.user, "website.can_see"):
86+
return 400, {"errors": "Error during index deletion."}
87+
dump.delete()
88+
shutil.rmtree(f"{settings.MEDIA_ROOT}/{dump.index}")
89+
return 200, {"message": f"Index {name} has been deleted successfully."}
90+
except Exception as excp:
91+
return 400, {
92+
"errors": str(excp) if excp else "Generic error during dump deletion"
93+
}
94+
95+
4996
@router.get("/{pk}", response=DumpInfoSchema, auth=django_auth)
5097
def get_dump_info(request, pk: UUID):
5198
"""
@@ -152,6 +199,65 @@ def create_dump(request, payload: DumpIn, upload: Optional[UploadedFile] = File(
152199
return HttpResponse(f"Bad Request ({excp})", status=400)
153200

154201

202+
@router.patch("/{pk}", url_name="edit_index", response=DumpSchema, auth=django_auth)
203+
def edit_dump(request, pk: UUID, payload: PatchDict[DumpEditIn]):
204+
"""
205+
Edits an existing dump based on the provided payload. This function updates the dump's attributes and manages user permissions for accessing the dump.
206+
207+
Args:
208+
request: The HTTP request object.
209+
payload (PatchDict[DumpEditIn]): The data containing the updates to be applied to the dump.
210+
211+
Returns:
212+
DumpSchema: The updated dump object.
213+
214+
Raises:
215+
Http404: If the dump with the specified index does not exist.
216+
HttpResponse: If there is an error during the update process.
217+
218+
Examples:
219+
PATCH /dumps/{pk}
220+
"""
221+
222+
try:
223+
dump = get_object_or_404(Dump, index=pk)
224+
if dump not in get_objects_for_user(request.user, "website.can_see"):
225+
return 403, {"message": "Unauthorized"}
226+
227+
auth_users = [
228+
user.pk
229+
for user in get_user_model().objects.all()
230+
if "can_see" in get_perms(user, dump) and user != request.user
231+
]
232+
233+
if payload["folder"]:
234+
folder, _ = Folder.objects.get_or_create(
235+
name=payload["folder"]["name"], user=request.user
236+
)
237+
dump.folder = folder
238+
239+
for attr, value in payload.items():
240+
if attr not in ["authorized_users", "folder"]:
241+
setattr(dump, attr, value)
242+
else:
243+
for user_pk in payload.get("authorized_users", []):
244+
user = get_user_model().objects.get(pk=user_pk)
245+
if user.pk not in auth_users:
246+
assign_perm(
247+
"can_see",
248+
user,
249+
dump,
250+
)
251+
for user_pk in auth_users:
252+
if user_pk not in payload.get("authorized_users", []):
253+
user = get_user_model().objects.get(pk=user_pk)
254+
remove_perm("can_see", user, dump)
255+
dump.save()
256+
return dump
257+
except Exception as excp:
258+
return HttpResponse(f"Bad Request ({excp})", status=400)
259+
260+
155261
@router.get(
156262
"/{idxs:pks}/plugins",
157263
url_name="dumps_plugins",
@@ -188,3 +294,18 @@ def get_dump_plugins(request, pks: List[UUID], filters: Query[DumpFilters] = Non
188294
if filters and filters.result:
189295
res = res.filter(result=filters.result)
190296
return res
297+
298+
299+
@router.get(
300+
"/{idxs:pks}/plugin/{str:plugin_name}",
301+
url_name="dumps_plugin_status",
302+
auth=django_auth,
303+
)
304+
def get_dump_plugin_status(request, pks: List[UUID], plugin_name: int):
305+
dumps_ok = get_objects_for_user(request.user, "website.can_see")
306+
dumps = [
307+
dump.index for dump in Dump.objects.filter(index__in=pks) if dump in dumps_ok
308+
]
309+
return Result.objects.select_related("dump", "plugin").filter(
310+
dump__index__in=dumps, plugin__name=plugin_name
311+
)

orochi/templates/website/index.html

+47-17
Original file line numberDiff line numberDiff line change
@@ -846,21 +846,47 @@ <h5 class="offcanvas-title" id="leftNoteLabel">History Log</h5>
846846
$(document).on("submit", "#edit-index", function (e) {
847847
e.preventDefault();
848848
var form = $(this);
849+
let formData = form.serializeArray();
850+
let obj = {};
851+
formData.forEach(item => {
852+
if (['csrfmiddlewaretoken', 'folder'].indexOf(item.name) == -1) {
853+
obj[item.name] = item.value;
854+
}else if(item.name == 'folder'){
855+
if(item.value != null && item.value != ''){
856+
obj['folder'] = {'name': $("#id_folder").find(":selected").text()}
857+
}
858+
}
859+
});
860+
$.ajaxSetup({
861+
headers: { 'X-CSRFToken': $('input[name="csrfmiddlewaretoken"]').val() }
862+
});
863+
849864
$.ajax({
850865
url: form.attr("action"),
851-
data: form.serialize(),
866+
data: JSON.stringify(obj),
867+
contentType: "application/json",
852868
type: form.attr("method"),
853869
dataType: 'json',
854-
success: function (data) {
855-
if (data.form_is_valid) {
856-
$("#index-list").html(data.dumps);
857-
$("#modal-update").modal('hide');
858-
selected_plugin = null;
859-
update_sidebar();
860-
} else {
861-
$("#modal-update .modal-content").html(data.html_form);
862-
}
863-
}
870+
success: function () {
871+
$("#modal-update").modal('hide');
872+
selected_plugin = null;
873+
update_sidebar();
874+
$.toast({
875+
title: 'Index updated!',
876+
content: 'Index updated successfully!',
877+
type: 'success',
878+
delay: 5000
879+
});
880+
},
881+
error: function () {
882+
$("#modal-update").modal('hide');
883+
$.toast({
884+
title: 'Index delete!',
885+
content: 'Error in index update!',
886+
type: 'error',
887+
delay: 5000
888+
});
889+
},
864890
});
865891
});
866892

@@ -870,10 +896,14 @@ <h5 class="offcanvas-title" id="leftNoteLabel">History Log</h5>
870896
var index = btn.data('index');
871897
bootbox.confirm("Are you sure??", function (result) {
872898
if (result === true) {
899+
900+
$.ajaxSetup({
901+
headers: { 'X-CSRFToken': $('input[name="csrfmiddlewaretoken"]').val() }
902+
});
903+
873904
$.ajax({
874-
url: "{% url 'website:index_delete' %}",
875-
data: { 'index': index },
876-
method: 'get',
905+
url: "{% url 'api:delete_dump' pk=111111111122222222223333333333444444 %}".replace(/111111111122222222223333333333444444/, index),
906+
method: 'delete',
877907
dataType: 'json',
878908
success: function (data) {
879909
btn.parent().parent().parent().remove();
@@ -885,15 +915,15 @@ <h5 class="offcanvas-title" id="leftNoteLabel">History Log</h5>
885915
update_sidebar();
886916
$.toast({
887917
title: 'Index delete!',
888-
content: 'Index has been deleted successfully.',
918+
content: data.message,
889919
type: 'success',
890920
delay: 5000
891921
});
892922
},
893-
error: function () {
923+
error: function (data) {
894924
$.toast({
895925
title: 'Index delete!',
896-
content: 'Error during index deletion.',
926+
content: data.errors,
897927
type: 'error',
898928
delay: 5000
899929
});

orochi/templates/website/partial_index_edit.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{% load widget_tweaks %}
22

3-
<form method="post" action="{% url 'website:index_edit' %}" id="edit-index">
3+
<form method="patch" action="{% url 'api:edit_index' pk=index %}" id="edit-index">
44
{{ form.media }}
55
{% csrf_token %}
66
<div class="modal-header">

0 commit comments

Comments
 (0)