user-panel #9
|
@ -0,0 +1,3 @@
|
||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
# Register your models here.
|
|
@ -0,0 +1,6 @@
|
||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class ApiConfig(AppConfig):
|
||||||
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
|
name = 'api'
|
|
@ -0,0 +1 @@
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
# Generated by Django 5.0.6 on 2024-09-19 15:09
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="Token",
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
"id",
|
||||||
|
models.BigAutoField(
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("token", models.UUIDField()),
|
||||||
|
(
|
||||||
|
"owner",
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
to=settings.AUTH_USER_MODEL,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,9 @@
|
||||||
|
from django.db import models
|
||||||
|
from user.models import User
|
||||||
|
|
||||||
|
# Create your models here.
|
||||||
|
|
||||||
|
|
||||||
|
class Token(models.Model):
|
||||||
|
token = models.UUIDField()
|
||||||
|
owner = models.ForeignKey(User, on_delete=models.CASCADE)
|
|
@ -0,0 +1,67 @@
|
||||||
|
import django_tables2 as tables
|
||||||
|
from django.utils.html import format_html
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
from api.models import Token
|
||||||
|
|
||||||
|
|
||||||
|
class ButtonColumn(tables.Column):
|
||||||
|
attrs = {
|
||||||
|
"a": {
|
||||||
|
"type": "button",
|
||||||
|
"class": "text-danger",
|
||||||
|
"title": "Remove",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
# it makes no sense to order a column of buttons
|
||||||
|
orderable = False
|
||||||
|
# django_tables will only call the render function if it doesn't find
|
||||||
|
# any empty values in the data, so we stop it from matching the data
|
||||||
|
# to any value considered empty
|
||||||
|
empty_values = ()
|
||||||
|
|
||||||
|
def render(self):
|
||||||
|
return format_html('<i class="bi bi-trash"></i>')
|
||||||
|
|
||||||
|
|
||||||
|
class TokensTable(tables.Table):
|
||||||
|
delete = ButtonColumn(
|
||||||
|
verbose_name=_("Delete"),
|
||||||
|
linkify={
|
||||||
|
"viewname": "api:delete_token",
|
||||||
|
"args": [tables.A("pk")]
|
||||||
|
},
|
||||||
|
orderable=False
|
||||||
|
)
|
||||||
|
|
||||||
|
token = tables.Column(verbose_name=_("Token"), empty_values=())
|
||||||
|
|
||||||
|
def render_view_user(self):
|
||||||
|
return format_html('<i class="bi bi-eye"></i>')
|
||||||
|
|
||||||
|
# def render_token(self, record):
|
||||||
|
# return record.get_memberships()
|
||||||
|
|
||||||
|
# def order_membership(self, queryset, is_descending):
|
||||||
|
# # TODO: Test that this doesn't return more rows than it should
|
||||||
|
# queryset = queryset.order_by(
|
||||||
|
# ("-" if is_descending else "") + "memberships__type"
|
||||||
|
# )
|
||||||
|
|
||||||
|
# return (queryset, True)
|
||||||
|
|
||||||
|
# def render_role(self, record):
|
||||||
|
# return record.get_roles()
|
||||||
|
|
||||||
|
# def order_role(self, queryset, is_descending):
|
||||||
|
# queryset = queryset.order_by(
|
||||||
|
# ("-" if is_descending else "") + "roles"
|
||||||
|
# )
|
||||||
|
|
||||||
|
# return (queryset, True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Token
|
||||||
|
template_name = "custom_table.html"
|
||||||
|
fields = ("token", "view_user")
|
||||||
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
{% load django_tables2 %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% block table-wrapper %}
|
||||||
|
<div class="table-container">
|
||||||
|
{% block table %}
|
||||||
|
<table class= "table table-striped table-sm">
|
||||||
|
{% block table.thead %}
|
||||||
|
{% if table.show_header %}
|
||||||
|
<thead {{ table.attrs.thead.as_html }}>
|
||||||
|
<tr>
|
||||||
|
{% for column in table.columns %}
|
||||||
|
<th scope="col" {{ column.attrs.th.as_html }}>
|
||||||
|
{% if column.orderable %}
|
||||||
|
<a type="button" class="btn btn-grey border border-dark"
|
||||||
|
href="{% querystring table.prefixed_order_by_field=column.order_by_alias.next %}">{{ column.header }}</a>
|
||||||
|
{% else %}
|
||||||
|
{{ column.header }}
|
||||||
|
{% endif %}
|
||||||
|
</th>
|
||||||
|
{% endfor %}
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock table.thead %}
|
||||||
|
{% block table.tbody %}
|
||||||
|
<tbody {{ table.attrs.tbody.as_html }}>
|
||||||
|
{% for row in table.paginated_rows %}
|
||||||
|
{% block table.tbody.row %}
|
||||||
|
<tr {{ row.attrs.as_html }}>
|
||||||
|
{% for column, cell in row.items %}
|
||||||
|
<td {{ column.attrs.td.as_html }}>{% if column.localize == None %}{{ cell }}{% else %}{% if column.localize %}{{ cell|localize }}{% else %}{{ cell|unlocalize }}{% endif %}{% endif %}</td>
|
||||||
|
{% endfor %}
|
||||||
|
</tr>
|
||||||
|
{% endblock table.tbody.row %}
|
||||||
|
{% empty %}
|
||||||
|
{% if table.empty_text %}
|
||||||
|
{% block table.tbody.empty_text %}
|
||||||
|
<tr><td colspan="{{ table.columns|length }}">{{ table.empty_text }}</td></tr>
|
||||||
|
{% endblock table.tbody.empty_text %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
{% endblock table.tbody %}
|
||||||
|
{% block table.tfoot %}
|
||||||
|
{% if table.has_footer %}
|
||||||
|
<tfoot {{ table.attrs.tfoot.as_html }}>
|
||||||
|
<tr>
|
||||||
|
{% for column in table.columns %}
|
||||||
|
<td {{ column.attrs.tf.as_html }}>{{ column.footer }}</td>
|
||||||
|
{% endfor %}
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock table.tfoot %}
|
||||||
|
</table>
|
||||||
|
{% endblock table %}
|
||||||
|
|
||||||
|
{% block pagination %}
|
||||||
|
{% if table.page and table.paginator.num_pages > 1 %}
|
||||||
|
<ul class="pagination">
|
||||||
|
{% if table.page.has_previous %}
|
||||||
|
{% block pagination.previous %}
|
||||||
|
<li class="previous">
|
||||||
|
<a type="button" class="btn btn-grey border border-dark" href="{% querystring table.prefixed_page_field=table.page.previous_page_number %}">
|
||||||
|
{% trans 'Previous' %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endblock pagination.previous %}
|
||||||
|
{% endif %}
|
||||||
|
{% if table.page.has_previous or table.page.has_next %}
|
||||||
|
{% block pagination.range %}
|
||||||
|
{% for p in table.page|table_page_range:table.paginator %}
|
||||||
|
<li {% if p == table.page.number %}class="active"{% endif %}>
|
||||||
|
<a type="button" class="btn btn-grey{% if p == table.page.number %}-selected{% endif %}
|
||||||
|
border border-dark"
|
||||||
|
{% if p == '...' %}
|
||||||
|
href="#">
|
||||||
|
{% else %}
|
||||||
|
href="{% querystring table.prefixed_page_field=p %}">
|
||||||
|
{% endif %}
|
||||||
|
{{ p }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
{% endblock pagination.range %}
|
||||||
|
{% endif %}
|
||||||
|
{% if table.page.has_next %}
|
||||||
|
{% block pagination.next %}
|
||||||
|
<li class="next">
|
||||||
|
<a type="button" class="btn btn-grey border border-dark" href="{% querystring table.prefixed_page_field=table.page.next_page_number %}">
|
||||||
|
{% trans 'Next' %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endblock pagination.next %}
|
||||||
|
{% endif %}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock pagination %}
|
||||||
|
</div>
|
||||||
|
{% endblock table-wrapper %}
|
|
@ -0,0 +1,14 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% load render_table from django_tables2 %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h3>
|
||||||
|
<i class="{{ icon }}"></i>
|
||||||
|
{{ subtitle }}
|
||||||
|
</h3>
|
||||||
|
{% render_table table %}
|
||||||
|
<div class="form-actions-no-box">
|
||||||
|
<a class="btn btn-green-admin" href="{% url 'api:new_token' %}">{% translate "Generate a new token" %} <i class="bi bi-plus"></i></a>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,13 @@
|
||||||
|
from api import views
|
||||||
|
|
||||||
|
from django.urls import path
|
||||||
|
|
||||||
|
|
||||||
|
app_name = 'api'
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path('snapshot/', views.NewSnapshot, name='new_snapshot'),
|
||||||
|
path('tokens/', views.TokenView.as_view(), name='tokens'),
|
||||||
|
path('tokens/new', views.TokenNewView.as_view(), name='new_token'),
|
||||||
|
path('tokens/<int:pk>/del', views.TokenDeleteView.as_view(), name='delete_token'),
|
||||||
|
]
|
|
@ -0,0 +1,116 @@
|
||||||
|
import json
|
||||||
|
|
||||||
|
from django.shortcuts import get_object_or_404, redirect
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
|
from django.views.generic.edit import DeleteView
|
||||||
|
from django.views.generic.base import View
|
||||||
|
from django.http import JsonResponse
|
||||||
|
from django_tables2 import SingleTableView
|
||||||
|
from uuid import uuid4
|
||||||
|
|
||||||
|
from dashboard.mixins import DashboardView
|
||||||
|
from evidence.models import Annotation
|
||||||
|
from evidence.parse import Build
|
||||||
|
from user.models import User
|
||||||
|
from api.models import Token
|
||||||
|
from api.tables import TokensTable
|
||||||
|
|
||||||
|
|
||||||
|
def save_in_disk(data, user):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@csrf_exempt
|
||||||
|
def NewSnapshot(request):
|
||||||
|
# Accept only posts
|
||||||
|
if request.method != 'POST':
|
||||||
|
return JsonResponse({'error': 'Invalid request method'}, status=400)
|
||||||
|
|
||||||
|
# Authentication
|
||||||
|
# auth_header = request.headers.get('Authorization')
|
||||||
|
# if not auth_header or not auth_header.startswith('Bearer '):
|
||||||
|
# return JsonResponse({'error': 'Invalid or missing token'}, status=401)
|
||||||
|
|
||||||
|
# token = auth_header.split(' ')[1]
|
||||||
|
# tk = Token.objects.filter(token=token).first()
|
||||||
|
# if not tk:
|
||||||
|
# return JsonResponse({'error': 'Invalid or missing token'}, status=401)
|
||||||
|
|
||||||
|
# Validation snapshot
|
||||||
|
try:
|
||||||
|
data = json.loads(request.body)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
return JsonResponse({'error': 'Invalid JSON'}, status=400)
|
||||||
|
|
||||||
|
# try:
|
||||||
|
# Build(data, None, check=True)
|
||||||
|
# except Exception:
|
||||||
|
# return JsonResponse({'error': 'Invalid Snapshot'}, status=400)
|
||||||
|
|
||||||
|
exist_annotation = Annotation.objects.filter(
|
||||||
|
uuid=data['uuid']
|
||||||
|
).first()
|
||||||
|
|
||||||
|
if exist_annotation:
|
||||||
|
raise ValidationError("error: the snapshot {} exist".format(data['uuid']))
|
||||||
|
|
||||||
|
# Process snapshot
|
||||||
|
# save_in_disk(data, tk.user)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Build(data, tk.user)
|
||||||
|
user = User.objects.get(email="user@example.org")
|
||||||
|
Build(data, user)
|
||||||
|
except Exception:
|
||||||
|
return JsonResponse({'status': 'fail'}, status=200)
|
||||||
|
|
||||||
|
return JsonResponse({'status': 'success'}, status=200)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class TokenView(DashboardView, SingleTableView):
|
||||||
|
template_name = "token.html"
|
||||||
|
title = _("Credential management")
|
||||||
|
section = "Credential"
|
||||||
|
subtitle = _('Managament Tokens')
|
||||||
|
icon = 'bi bi-key'
|
||||||
|
model = Token
|
||||||
|
table_class = TokensTable
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
"""
|
||||||
|
Override the get_queryset method to filter events based on the user type.
|
||||||
|
"""
|
||||||
|
return Token.objects.filter().order_by("-id")
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
context.update({
|
||||||
|
'tokens': Token.objects,
|
||||||
|
})
|
||||||
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
class TokenDeleteView(DashboardView, DeleteView):
|
||||||
|
model = Token
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
# self.check_valid_user()
|
||||||
|
self.pk = kwargs['pk']
|
||||||
|
self.object = get_object_or_404(self.model, pk=self.pk)
|
||||||
|
self.object.delete()
|
||||||
|
|
||||||
|
return redirect('api:tokens')
|
||||||
|
|
||||||
|
|
||||||
|
class TokenNewView(DashboardView, View):
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
# self.check_valid_user()
|
||||||
|
Token.objects.create(token=uuid4())
|
||||||
|
|
||||||
|
return redirect('api:tokens')
|
||||||
|
|
Loading…
Reference in New Issue