Skip to content

Commit

Permalink
CRUD of user token
Browse files Browse the repository at this point in the history
  • Loading branch information
Xpirix committed Sep 17, 2024
1 parent 8b57d4c commit 2f5fe2a
Show file tree
Hide file tree
Showing 10 changed files with 175 additions and 59 deletions.
14 changes: 14 additions & 0 deletions qgis-app/api/forms.py
Original file line number Diff line number Diff line change
@@ -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",
)
2 changes: 1 addition & 1 deletion qgis-app/api/migrations/0001_initial.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)),
Expand Down
2 changes: 1 addition & 1 deletion qgis-app/api/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
"""
Expand Down
9 changes: 9 additions & 0 deletions qgis-app/api/templates/user_token_delete.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{% extends BASE_TEMPLATE %}{% load i18n %}
{% block content %}
<h3>Delete token of "{{ username }}"</h3>
<form action="" method="post">{% csrf_token %}
<p class="alert alert-danger">{% trans "You asked to delete a token.<br />It will be permanently deleted and this action cannot be undone.<br />Please confirm." %}</p>
<p><input type="submit" class="btn btn-danger" name="delete_confirm" value="{% trans "Ok" %}" /> <a class="btn btn-default" href="javascript:history.back()">{% trans "Cancel" %}</a></p>
</form>

{% endblock %}
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,6 @@ <h2>{% trans "Token for" %} {{ hub.name }}</h2>
is lost, you can generate a new one at any time.
</div>
<dl class="dl-horizontal">
<dt>{% trans "User"%}</dt>
<dd>
<a href="{% url "user_details" object.user %}">{{ object.user }}</a>
</dd>
<dt>{% trans "Created at"%}</dt>
<dd>
{{ object.created_at|local_timezone }}
</dd>
<dt>{% trans "Expires at"%}</dt>
<dd>
{{ object.expires_at|local_timezone }}
</dd>
<dt>{% trans "Access token"%}</dt>
<dd>
<textarea
Expand All @@ -43,8 +31,8 @@ <h2>{% trans "Token for" %} {{ hub.name }}</h2>

</dl>
<div>
<a class="btn" href="{% url "hub_token_list" %}?v={{ timestamp_from_last_edit }}">{% trans "Back to the list" %}</a>
{% comment %} <a class="btn btn-primary" href="{% url "hub_token_update" object.pk %}">{% trans "Edit description" %}</a> {% endcomment %}
<a class="btn" href="{% url "user_token_list" %}?v={{ timestamp_from_last_edit }}">{% trans "Back to the list" %}</a>
<a class="btn btn-primary" href="{% url "user_token_update" object.pk %}">{% trans "Edit description" %}</a>
</div>
{% endblock %}
{% block extracss %}
Expand Down
26 changes: 26 additions & 0 deletions qgis-app/api/templates/user_token_form.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{% extends BASE_TEMPLATE %}{% load i18n %}
{% load local_timezone %}
{% block content %}
<h2>{% trans "Edit token description " %} {{ token.jti }}</h2>

{% if form.errors %}
<div class="alert alert-error">
<button type="button" class="close" data-dismiss="alert">&times;</button>
<p>{% trans "The form contains errors and cannot be submitted, please check the fields highlighted in red." %}</p>
</div>
{% endif %}
{% if form.non_field_errors %}
<div class="alert alert-error">
<button type="button" class="close" data-dismiss="alert">&times;</button>
{% for error in form.non_field_errors %}
<p>{{ error }}</p>
{% endfor %}
</div>
{% endif %}
<form action="" method="post" class="horizontal" enctype="multipart/form-data">{% csrf_token %}
{% include "base/form_snippet.html" %}
<div class="form-actions">
<button class="btn btn-primary" type="submit">{% trans "Save" %}</button>
</div>
</form>
{% endblock %}
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{% extends BASE_TEMPLATE %}{% load i18n %}
{% load local_timezone %}
{% block content %}
<h2>{% trans "Tokens" %}</h2>
<form method="post" action="{% url "hub_token_create"%}">{% csrf_token %}
<h2>{% trans "My Tokens" %}</h2>
<form method="post" action="{% url "user_token_create"%}">{% csrf_token %}
<div style="margin:0; width:fit-content;">
<h2>
<button type="submit" name="hub_token_create" id="hub_token_create"
<button type="submit" name="user_token_create" id="user_token_create"
value="{% trans "Generate a New Token" %}" class="btn btn-block btn-primary" style="padding: 10px">
<i class="icon-plus icon-white icon-2x" style=" vertical-align: middle;"></i>
&nbsp;{% trans "Generate a New Token" %}
Expand All @@ -20,24 +20,25 @@ <h2>
<tr>
<th>{% trans "Description" %}</th>
<th>{% trans "Created at" %}</th>
{% comment %} <th>{% trans "Expires at" %}</th> {% endcomment %}
<th>{% trans "Last used at" %}</th>
<th>{% trans "Manage" %}</th>
</tr>
</thead>
<tbody>
{% for hub_token in object_list %}
{% for user_token in object_list %}
<tr class="{% cycle "even" "odd" %}">
<td>{{ hub_token.token.user }}</td>
<td>{{ hub_token.description|default:"-" }}</td>
<td>{{ hub_token.token.created_at|local_timezone }}</td>
<td>{{ hub_token.last_used_on|default:"-"|local_timezone }}</td>
<td>{{ user_token.description|default:"-" }}</td>
<td>{{ user_token.token.created_at|local_timezone }}</td>
{% comment %} <td>{{ user_token.token.expires_at|local_timezone }}</td> {% endcomment %}
<td>{{ user_token.last_used_on|default:"-"|local_timezone }}</td>
<td>
{% comment %} <a class="btn btn-primary btn-mini" href="{% url "hub_token_update" hub_token.token.id %}"
<a class="btn btn-primary btn-mini" href="{% url "user_token_update" user_token.pk %}"
title="{% trans "Edit description" %}"><i class="icon-pencil icon-white"></i></a>&nbsp;
<a class="btn btn-danger btn-mini delete"
href="{% url "hub_token_delete" hub_token.token.id %}"
href="{% url "user_token_delete" user_token.pk %}"
title="{% trans "Delete" %}"><i class="icon-remove icon-white"></i>
</a> {% endcomment %}
</a>
</td>
</tr>
{% endfor %}
Expand Down
26 changes: 19 additions & 7 deletions qgis-app/api/urls.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,39 @@
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
from api.views import UserTokenDetailView, UserTokenListView, user_token_create, user_token_update, user_token_delete
urlpatterns = [
path("resources/", ResourceAPIList.as_view(), name="resource-list"),
path(
"resource/<uuid:uuid>/", ResourceAPIDownload.as_view(), name="resource-download"
),
url(
r"^tokens/$",
HubTokenListView.as_view(),
name="hub_token_list",
UserTokenListView.as_view(),
name="user_token_list",
),
url(
r"^tokens/(?P<pk>\d+)/$",
HubTokenDetailView.as_view(),
name="hub_token_detail",
UserTokenDetailView.as_view(),
name="user_token_detail",
),
url(
r"^tokens/create/$",
hub_token_create,
user_token_create,
{},
name="hub_token_create",
name="user_token_create",
),
url(
r"^tokens/(?P<token_id>\d+)/update$",
user_token_update,
{},
name="user_token_update",
),
url(
r"^tokens/(?P<token_id>[^\/]+)/delete/$",
user_token_delete,
{},
name="user_token_delete",
),

]
114 changes: 90 additions & 24 deletions qgis-app/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
from django.urls import reverse
from rest_framework_simplejwt.exceptions import InvalidToken, TokenError
import time
from django.utils.translation import gettext_lazy as _
from api.forms import UserTokenForm
from django.contrib import messages

# models
from geopackages.models import Geopackage
Expand All @@ -28,7 +31,7 @@
from styles.models import Style
from layerdefinitions.models import LayerDefinition
from wavefronts.models import Wavefront
from api.models import HubOutstandingToken
from api.models import UserOutstandingToken
from rest_framework_simplejwt.token_blacklist.models import OutstandingToken

def filter_resource_type(queryset, request, *args, **kwargs):
Expand Down Expand Up @@ -181,95 +184,158 @@ def get(self, request, *args, **kwargs):
return response


class HubTokenDetailView(DetailView):
class UserTokenDetailView(DetailView):
"""
Hub token detail
"""
model = OutstandingToken
queryset = OutstandingToken.objects.all()
template_name = "hub_token_detail.html"
template_name = "user_token_detail.html"

@method_decorator(ensure_csrf_cookie)
def dispatch(self, *args, **kwargs):
return super(HubTokenDetailView, self).dispatch(*args, **kwargs)
return super(UserTokenDetailView, self).dispatch(*args, **kwargs)

def get_context_data(self, **kwargs):
context = super(HubTokenDetailView, self).get_context_data(**kwargs)
context = super(UserTokenDetailView, self).get_context_data(**kwargs)
token_id = self.kwargs.get('pk')
hub_token = get_object_or_404(
HubOutstandingToken,
user_token = get_object_or_404(
UserOutstandingToken,
pk=token_id,
is_blacklisted=False,
is_newly_created=True
)
outstanding_token = get_object_or_404(
OutstandingToken,
pk=hub_token.token.pk,
pk=user_token.token.pk,
user=self.request.user
)
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"
self.template_name = "user_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,
"object": user_token,
'timestamp_from_last_edit': timestamp_from_last_edit
}
)
# hub_token.is_newly_created = False
# hub_token.save()
user_token.is_newly_created = False
user_token.save()
return context


class HubTokenListView(ListView):
class UserTokenListView(ListView):
"""
Hub token list
"""
model = HubOutstandingToken
queryset = HubOutstandingToken.objects.all().order_by("-token__created_at")
template_name = "hub_token_list.html"
model = UserOutstandingToken
queryset = UserOutstandingToken.objects.all().order_by("-token__created_at")
template_name = "user_token_list.html"

@method_decorator(ensure_csrf_cookie)
def dispatch(self, *args, **kwargs):
return super(HubTokenListView, self).dispatch(*args, **kwargs)
return super(UserTokenListView, 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 = super(UserTokenListView, self).get_queryset()
qs = self.get_filtered_queryset(qs)
return qs



@login_required
@transaction.atomic
def hub_token_create(request):
def user_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_token = UserOutstandingToken.objects.create(
user=user,
token=outstanding_token,
is_blacklisted=False,
is_newly_created=True
)

return HttpResponseRedirect(
reverse("hub_token_detail", args=[hub_token.pk])
reverse("user_token_detail", args=[user_token.pk])
)


@login_required
@transaction.atomic
def user_token_update(request, token_id):
print(token_id)
user_token = get_object_or_404(
UserOutstandingToken,
pk=token_id,
is_blacklisted=False
)
outstanding_token = get_object_or_404(
OutstandingToken,
pk=user_token.token.pk,
user=request.user
)
if request.method == "POST":
form = UserTokenForm(request.POST, instance=user_token)
if form.is_valid():
form.save()
msg = _("The token description has been successfully updated.")
messages.success(request, msg, fail_silently=True)
return HttpResponseRedirect(
reverse("user_token_list")
)
else:
form = UserTokenForm(instance=user_token)

return render(
request,
"user_token_form.html",
{"form": form, "token": user_token}
)


@login_required
@transaction.atomic
def user_token_delete(request, token_id):
user_token = get_object_or_404(
UserOutstandingToken,
pk=token_id,
is_blacklisted=False
)
outstanding_token = get_object_or_404(
OutstandingToken,
pk=user_token.token.pk,
user=request.user
)
if "delete_confirm" in request.POST:
try:
token = RefreshToken(outstanding_token.token)
token.blacklist()
user_token.is_blacklisted = True
except (InvalidToken, TokenError) as e:
user_token.is_blacklisted = True
user_token.save()

msg = _("The token has been successfully deleted.")
messages.success(request, msg, fail_silently=True)
return HttpResponseRedirect(
reverse("user_token_list")
)
return render(
request,
"user_token_delete.html",
{"description": user_token.description, "username": outstanding_token.user},
)
Loading

0 comments on commit 2f5fe2a

Please sign in to comment.