From d48846343d72947928e38e2f13166efe2d5daf5c Mon Sep 17 00:00:00 2001
From: Lova ANDRIARIMALALA <43842786+Xpirix@users.noreply.github.com>
Date: Thu, 12 Sep 2024 14:18:36 +0200
Subject: [PATCH 01/17] Create user token model
---
qgis-app/api/migrations/0001_initial.py | 30 ++++++++++++++++++++++
qgis-app/api/models.py | 33 ++++++++++++++++++++++++-
2 files changed, 62 insertions(+), 1 deletion(-)
create mode 100644 qgis-app/api/migrations/0001_initial.py
diff --git a/qgis-app/api/migrations/0001_initial.py b/qgis-app/api/migrations/0001_initial.py
new file mode 100644
index 00000000..f2f51797
--- /dev/null
+++ b/qgis-app/api/migrations/0001_initial.py
@@ -0,0 +1,30 @@
+# Generated by Django 4.2.16 on 2024-09-12 07:16
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ initial = True
+
+ dependencies = [
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ('token_blacklist', '0012_alter_outstandingtoken_user'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='HubOutstandingToken',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('is_blacklisted', models.BooleanField(default=False)),
+ ('is_newly_created', models.BooleanField(default=False)),
+ ('description', models.CharField(blank=True, help_text="Describe this token so that it's easier to remember where you're using it.", max_length=512, null=True, verbose_name='Description')),
+ ('last_used_on', models.DateTimeField(blank=True, null=True, verbose_name='Last used on')),
+ ('token', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='token_blacklist.outstandingtoken')),
+ ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
+ ],
+ ),
+ ]
diff --git a/qgis-app/api/models.py b/qgis-app/api/models.py
index 6b202199..c63b5454 100644
--- a/qgis-app/api/models.py
+++ b/qgis-app/api/models.py
@@ -1 +1,32 @@
-# Create your models here.
+from base.models.processing_models import Resource
+from django.db import models
+from django.utils.translation import gettext_lazy as _
+from rest_framework_simplejwt.token_blacklist.models import OutstandingToken
+from django.contrib.auth.models import User
+
+class HubOutstandingToken(models.Model):
+ """
+ Hub outstanding token
+ """
+ user = models.ForeignKey(
+ User,
+ on_delete=models.CASCADE
+ )
+ token = models.ForeignKey(
+ OutstandingToken,
+ on_delete=models.CASCADE
+ )
+ is_blacklisted = models.BooleanField(default=False)
+ is_newly_created = models.BooleanField(default=False)
+ description = models.CharField(
+ verbose_name=_("Description"),
+ help_text=_("Describe this token so that it's easier to remember where you're using it."),
+ max_length=512,
+ blank=True,
+ null=True,
+ )
+ last_used_on = models.DateTimeField(
+ verbose_name=_("Last used on"),
+ blank=True,
+ null=True
+ )
\ No newline at end of file
From f4c7b8981962d72bf15209a344f4aea6a1813765 Mon Sep 17 00:00:00 2001
From: Lova ANDRIARIMALALA <43842786+Xpirix@users.noreply.github.com>
Date: Thu, 12 Sep 2024 16:32:18 +0200
Subject: [PATCH 02/17] Token list and create token view
---
qgis-app/api/templates/hub_token_list.html | 75 ++++++++++++++
qgis-app/api/urls.py | 20 +++-
qgis-app/api/views.py | 110 ++++++++++++++++++++-
qgis-app/templates/base.html | 5 +-
4 files changed, 205 insertions(+), 5 deletions(-)
create mode 100644 qgis-app/api/templates/hub_token_list.html
diff --git a/qgis-app/api/templates/hub_token_list.html b/qgis-app/api/templates/hub_token_list.html
new file mode 100644
index 00000000..c5604736
--- /dev/null
+++ b/qgis-app/api/templates/hub_token_list.html
@@ -0,0 +1,75 @@
+{% extends BASE_TEMPLATE %}{% load i18n %}
+{% load local_timezone %}
+{% block content %}
+
{% trans "Tokens" %}
+
+{% if object_list.count %}
+
+
+
+
+ {% trans "Description" %}
+ {% trans "Created at" %}
+ {% trans "Last used at" %}
+ {% trans "Manage" %}
+
+
+
+ {% for hub_token in object_list %}
+
+ {{ hub_token.token.user }}
+ {{ hub_token.description|default:"-" }}
+
+
+ {{ hub_token.token.jti }}
+
+
+ {{ hub_token.token.created_at|local_timezone }}
+ {{ hub_token.last_used_on|default:"-"|local_timezone }}
+
+
+
+
+
+
+ {% endfor %}
+
+
+
+{% else %}
+
+ ×
+ {% trans "This list is empty!" %}
+
+{% endif %}
+
+{% endblock %}
+
+{% block extracss %}
+{{ block.super }}
+
+{% endblock %}
diff --git a/qgis-app/api/urls.py b/qgis-app/api/urls.py
index c2048c34..db44fe6a 100644
--- a/qgis-app/api/urls.py
+++ b/qgis-app/api/urls.py
@@ -1,9 +1,27 @@
from api.views import ResourceAPIDownload, ResourceAPIList
from django.urls import path
-
+from django.urls import re_path as url
+from api.views import HubTokenDetailView, HubTokenListView, hub_token_create
urlpatterns = [
path("resources/", ResourceAPIList.as_view(), name="resource-list"),
path(
"resource//", ResourceAPIDownload.as_view(), name="resource-download"
),
+ url(
+ r"^tokens/$",
+ HubTokenListView.as_view(),
+ name="hub_token_list",
+ ),
+ url(
+ r"^tokens/(?P\d+)/$",
+ HubTokenDetailView.as_view(),
+ name="hub_token_detail",
+ ),
+ url(
+ r"^tokens/create/$",
+ hub_token_create,
+ {},
+ name="hub_token_create",
+ ),
+
]
diff --git a/qgis-app/api/views.py b/qgis-app/api/views.py
index f62375d2..e2f02dcc 100644
--- a/qgis-app/api/views.py
+++ b/qgis-app/api/views.py
@@ -3,12 +3,22 @@
from api.serializers import GeopackageSerializer, ModelSerializer, StyleSerializer, LayerDefinitionSerializer, WavefrontSerializer
from base.license import zip_a_file_if_not_zipfile
from django.contrib.postgres.search import SearchVector
-from django.http import Http404, HttpResponse
+from django.http import Http404, HttpResponse, HttpResponseRedirect
from django.utils.decorators import method_decorator
from django.utils.text import slugify
from django.views.decorators.cache import cache_page
from drf_multiple_model.pagination import MultipleModelLimitOffsetPagination
from drf_multiple_model.views import FlatMultipleModelAPIView
+from django.views.decorators.csrf import ensure_csrf_cookie
+from django.shortcuts import get_object_or_404, render
+from django.contrib.auth.decorators import login_required
+from django.db import transaction
+
+from django.views.generic import ListView, DetailView
+from rest_framework_simplejwt.tokens import RefreshToken, api_settings
+from django.urls import reverse
+from rest_framework_simplejwt.exceptions import InvalidToken, TokenError
+import time
# models
from geopackages.models import Geopackage
@@ -18,7 +28,8 @@
from styles.models import Style
from layerdefinitions.models import LayerDefinition
from wavefronts.models import Wavefront
-
+from api.models import HubOutstandingToken
+from rest_framework_simplejwt.token_blacklist.models import OutstandingToken
def filter_resource_type(queryset, request, *args, **kwargs):
resource_type = request.query_params["resource_type"]
@@ -168,3 +179,98 @@ def get(self, request, *args, **kwargs):
slugify(object.name, allow_unicode=True)
)
return response
+
+
+class HubTokenDetailView(DetailView):
+ """
+ Hub token detail
+ """
+ model = OutstandingToken
+ queryset = OutstandingToken.objects.all()
+ template_name = "hub_token_detail.html"
+
+ @method_decorator(ensure_csrf_cookie)
+ def dispatch(self, *args, **kwargs):
+ return super(HubTokenDetailView, self).dispatch(*args, **kwargs)
+
+ def get_context_data(self, **kwargs):
+ context = super(HubTokenDetailView, self).get_context_data(**kwargs)
+ token_id = self.kwargs.get('pk')
+
+ outstanding_token = get_object_or_404(
+ OutstandingToken,
+ pk=token_id,
+ user=self.request.user
+ )
+ hub_token = get_object_or_404(
+ HubOutstandingToken,
+ token__pk=outstanding_token.pk,
+ is_blacklisted=False,
+ is_newly_created=True
+ )
+ try:
+ token = RefreshToken(outstanding_token.token)
+ token['refresh_jti'] = token[api_settings.JTI_CLAIM]
+ except (InvalidToken, TokenError) as e:
+ context = {}
+ self.template_name = "hub_token_invalid_or_expired.html"
+ return context
+ timestamp_from_last_edit = int(time.time())
+ context.update(
+ {
+ "access_token": str(token.access_token),
+ "object": outstanding_token,
+ 'timestamp_from_last_edit': timestamp_from_last_edit
+ }
+ )
+ hub_token.is_newly_created = False
+ hub_token.save()
+ return context
+
+
+class HubTokenListView(ListView):
+ """
+ Hub token list
+ """
+ model = HubOutstandingToken
+ queryset = HubOutstandingToken.objects.all().order_by("-token__created_at")
+ template_name = "hub_token_list.html"
+
+ @method_decorator(ensure_csrf_cookie)
+ def dispatch(self, *args, **kwargs):
+ return super(HubTokenListView, self).dispatch(*args, **kwargs)
+
+ def get_filtered_queryset(self, qs):
+ return qs.filter(
+ is_blacklisted=False
+ )
+
+ def get_queryset(self):
+ qs = super(HubTokenListView, self).get_queryset()
+ qs = self.get_filtered_queryset(qs)
+ return qs
+
+
+
+@login_required
+@transaction.atomic
+def hub_token_create(request):
+ if request.method == "POST":
+ user = request.user
+ refresh = RefreshToken.for_user(user)
+
+ jti = refresh[api_settings.JTI_CLAIM]
+
+ outstanding_token = OutstandingToken.objects.get(jti=jti)
+
+ hub_token = HubOutstandingToken.objects.create(
+ user=user,
+ token=outstanding_token,
+ is_blacklisted=False,
+ is_newly_created=True
+ )
+
+ return HttpResponseRedirect(
+ reverse("hub_token_detail", args=[hub_token.pk])
+ )
+
diff --git a/qgis-app/templates/base.html b/qgis-app/templates/base.html
index 74b94bd1..75378665 100644
--- a/qgis-app/templates/base.html
+++ b/qgis-app/templates/base.html
@@ -71,8 +71,9 @@
+ {% endfor %}
+ API
+
{% if user.is_authenticated %}
From 8b57d4c97eb6e8e84e600621f1768e8efa1826e5 Mon Sep 17 00:00:00 2001
From: Lova ANDRIARIMALALA <43842786+Xpirix@users.noreply.github.com>
Date: Tue, 17 Sep 2024 13:58:23 +0300
Subject: [PATCH 03/17] Add token detail page, fix token create
---
qgis-app/api/templates/hub_token_detail.html | 110 +++++++++++++++++++
qgis-app/api/templates/hub_token_list.html | 9 +-
qgis-app/api/views.py | 17 ++-
3 files changed, 120 insertions(+), 16 deletions(-)
create mode 100644 qgis-app/api/templates/hub_token_detail.html
diff --git a/qgis-app/api/templates/hub_token_detail.html b/qgis-app/api/templates/hub_token_detail.html
new file mode 100644
index 00000000..ae60dab5
--- /dev/null
+++ b/qgis-app/api/templates/hub_token_detail.html
@@ -0,0 +1,110 @@
+{% extends BASE_TEMPLATE %}{% load i18n %}
+{% load local_timezone %}
+{% block content %}
+{% trans "Token for" %} {{ hub.name }}
+
+ ×
+ To enhance the security of your token,
+ it will be displayed only once. Please ensure
+ to save it in a secure location. If the token
+ is lost, you can generate a new one at any time.
+
+
+ {% trans "User"%}
+
+ {{ object.user }}
+
+ {% trans "Created at"%}
+
+ {{ object.created_at|local_timezone }}
+
+ {% trans "Expires at"%}
+
+ {{ object.expires_at|local_timezone }}
+
+ {% trans "Access token"%}
+
+
+
+
+ {% trans "Copy token to clipboard" %}
+
+
+
+
+
+
+
+
+{% endblock %}
+{% block extracss %}
+{{ block.super }}
+
+{% endblock %}
+
+{% block extrajs %}
+
+{% endblock %}
\ No newline at end of file
diff --git a/qgis-app/api/templates/hub_token_list.html b/qgis-app/api/templates/hub_token_list.html
index c5604736..6bc8b162 100644
--- a/qgis-app/api/templates/hub_token_list.html
+++ b/qgis-app/api/templates/hub_token_list.html
@@ -29,20 +29,15 @@
{{ hub_token.token.user }}
{{ hub_token.description|default:"-" }}
-
-
- {{ hub_token.token.jti }}
-
-
{{ hub_token.token.created_at|local_timezone }}
{{ hub_token.last_used_on|default:"-"|local_timezone }}
-
-
+ {% endcomment %}
{% endfor %}
diff --git a/qgis-app/api/views.py b/qgis-app/api/views.py
index e2f02dcc..1a936695 100644
--- a/qgis-app/api/views.py
+++ b/qgis-app/api/views.py
@@ -196,18 +196,17 @@ def dispatch(self, *args, **kwargs):
def get_context_data(self, **kwargs):
context = super(HubTokenDetailView, self).get_context_data(**kwargs)
token_id = self.kwargs.get('pk')
-
- outstanding_token = get_object_or_404(
- OutstandingToken,
- pk=token_id,
- user=self.request.user
- )
hub_token = get_object_or_404(
HubOutstandingToken,
- token__pk=outstanding_token.pk,
+ pk=token_id,
is_blacklisted=False,
is_newly_created=True
)
+ outstanding_token = get_object_or_404(
+ OutstandingToken,
+ pk=hub_token.token.pk,
+ user=self.request.user
+ )
try:
token = RefreshToken(outstanding_token.token)
token['refresh_jti'] = token[api_settings.JTI_CLAIM]
@@ -223,8 +222,8 @@ def get_context_data(self, **kwargs):
'timestamp_from_last_edit': timestamp_from_last_edit
}
)
- hub_token.is_newly_created = False
- hub_token.save()
+ # hub_token.is_newly_created = False
+ # hub_token.save()
return context
From 2f5fe2a017ec2d4c818b1bf449f94548c38dbddb Mon Sep 17 00:00:00 2001
From: Lova ANDRIARIMALALA <43842786+Xpirix@users.noreply.github.com>
Date: Tue, 17 Sep 2024 16:14:05 +0300
Subject: [PATCH 04/17] CRUD of user token
---
qgis-app/api/forms.py | 14 +++
qgis-app/api/migrations/0001_initial.py | 2 +-
qgis-app/api/models.py | 2 +-
qgis-app/api/templates/user_token_delete.html | 9 ++
...ken_detail.html => user_token_detail.html} | 16 +--
qgis-app/api/templates/user_token_form.html | 26 ++++
...b_token_list.html => user_token_list.html} | 23 ++--
qgis-app/api/urls.py | 26 ++--
qgis-app/api/views.py | 114 ++++++++++++++----
qgis-app/templates/base.html | 2 +-
10 files changed, 175 insertions(+), 59 deletions(-)
create mode 100644 qgis-app/api/forms.py
create mode 100644 qgis-app/api/templates/user_token_delete.html
rename qgis-app/api/templates/{hub_token_detail.html => user_token_detail.html} (78%)
create mode 100644 qgis-app/api/templates/user_token_form.html
rename qgis-app/api/templates/{hub_token_list.html => user_token_list.html} (67%)
diff --git a/qgis-app/api/forms.py b/qgis-app/api/forms.py
new file mode 100644
index 00000000..1431e358
--- /dev/null
+++ b/qgis-app/api/forms.py
@@ -0,0 +1,14 @@
+from django.forms import CharField, ModelForm
+from api.models import UserOutstandingToken
+
+
+class UserTokenForm(ModelForm):
+ """
+ Form for token description editing
+ """
+
+ class Meta:
+ model = UserOutstandingToken
+ fields = (
+ "description",
+ )
\ No newline at end of file
diff --git a/qgis-app/api/migrations/0001_initial.py b/qgis-app/api/migrations/0001_initial.py
index f2f51797..950f36bf 100644
--- a/qgis-app/api/migrations/0001_initial.py
+++ b/qgis-app/api/migrations/0001_initial.py
@@ -16,7 +16,7 @@ class Migration(migrations.Migration):
operations = [
migrations.CreateModel(
- name='HubOutstandingToken',
+ name='UserOutstandingToken',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('is_blacklisted', models.BooleanField(default=False)),
diff --git a/qgis-app/api/models.py b/qgis-app/api/models.py
index c63b5454..6db86a94 100644
--- a/qgis-app/api/models.py
+++ b/qgis-app/api/models.py
@@ -4,7 +4,7 @@
from rest_framework_simplejwt.token_blacklist.models import OutstandingToken
from django.contrib.auth.models import User
-class HubOutstandingToken(models.Model):
+class UserOutstandingToken(models.Model):
"""
Hub outstanding token
"""
diff --git a/qgis-app/api/templates/user_token_delete.html b/qgis-app/api/templates/user_token_delete.html
new file mode 100644
index 00000000..481c1ca4
--- /dev/null
+++ b/qgis-app/api/templates/user_token_delete.html
@@ -0,0 +1,9 @@
+{% extends BASE_TEMPLATE %}{% load i18n %}
+{% block content %}
+ Delete token of "{{ username }}"
+
+
+{% endblock %}
diff --git a/qgis-app/api/templates/hub_token_detail.html b/qgis-app/api/templates/user_token_detail.html
similarity index 78%
rename from qgis-app/api/templates/hub_token_detail.html
rename to qgis-app/api/templates/user_token_detail.html
index ae60dab5..2735de0a 100644
--- a/qgis-app/api/templates/hub_token_detail.html
+++ b/qgis-app/api/templates/user_token_detail.html
@@ -10,18 +10,6 @@ {% trans "Token for" %} {{ hub.name }}
is lost, you can generate a new one at any time.
- {% trans "User"%}
-
- {{ object.user }}
-
- {% trans "Created at"%}
-
- {{ object.created_at|local_timezone }}
-
- {% trans "Expires at"%}
-
- {{ object.expires_at|local_timezone }}
-
{% trans "Access token"%}
{% endblock %}
{% block extracss %}
diff --git a/qgis-app/api/templates/user_token_form.html b/qgis-app/api/templates/user_token_form.html
new file mode 100644
index 00000000..a1f04cf3
--- /dev/null
+++ b/qgis-app/api/templates/user_token_form.html
@@ -0,0 +1,26 @@
+{% extends BASE_TEMPLATE %}{% load i18n %}
+{% load local_timezone %}
+{% block content %}
+{% trans "Edit token description " %} {{ token.jti }}
+
+{% if form.errors %}
+
+
×
+
{% trans "The form contains errors and cannot be submitted, please check the fields highlighted in red." %}
+
+{% endif %}
+{% if form.non_field_errors %}
+
+
×
+ {% for error in form.non_field_errors %}
+
{{ error }}
+ {% endfor %}
+
+{% endif %}
+
+{% endblock %}
\ No newline at end of file
diff --git a/qgis-app/api/templates/hub_token_list.html b/qgis-app/api/templates/user_token_list.html
similarity index 67%
rename from qgis-app/api/templates/hub_token_list.html
rename to qgis-app/api/templates/user_token_list.html
index 6bc8b162..3552e405 100644
--- a/qgis-app/api/templates/hub_token_list.html
+++ b/qgis-app/api/templates/user_token_list.html
@@ -1,11 +1,11 @@
{% extends BASE_TEMPLATE %}{% load i18n %}
{% load local_timezone %}
{% block content %}
-{% trans "Tokens" %}
-