Skip to content

Commit

Permalink
List tokens for a specific plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
Xpirix committed Dec 11, 2023
1 parent 4d78069 commit d832116
Show file tree
Hide file tree
Showing 8 changed files with 204 additions and 37 deletions.
32 changes: 16 additions & 16 deletions qgis-app/plugins/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,25 @@
def has_valid_token(function):
@wraps(function)
def wrap(request, *args, **kwargs):
auth_token = request.META.get("HTTP_AUTHORIZATION")
package_name = kwargs.get('package_name')
if request.user.is_authenticated or not str(auth_token).startswith('Bearer'):
return function(request, *args, **kwargs)
auth_token = request.META.get("HTTP_AUTHORIZATION")
package_name = kwargs.get('package_name')
if request.user.is_authenticated or not str(auth_token).startswith('Bearer'):
return function(request, *args, **kwargs)

# Validate JWT token
authentication = JWTAuthentication()
validated_token = authentication.get_validated_token(auth_token[7:])
# Validate JWT token
authentication = JWTAuthentication()
validated_token = authentication.get_validated_token(auth_token[7:])

user = authentication.get_user(validated_token)
plugin_id = validated_token.payload.get('plugin_id')
if not plugin_id or not user:
return HttpResponseForbidden("Invalid token")
request.user = user
user = authentication.get_user(validated_token)
plugin_id = validated_token.payload.get('plugin_id')
if not plugin_id or not user:
return HttpResponseForbidden("Invalid token")
request.user = user

plugin = Plugin.objects.get(pk=plugin_id)
if not plugin or plugin.package_name != package_name:
return HttpResponseForbidden("Invalid token")
plugin = Plugin.objects.get(pk=plugin_id)
if not plugin or plugin.package_name != package_name:
return HttpResponseForbidden("Invalid token")

return function(request, *args, **kwargs)
return function(request, *args, **kwargs)

return wrap
116 changes: 116 additions & 0 deletions qgis-app/plugins/templates/plugins/outstandingtoken_list.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
{% extends 'plugins/plugin_base.html' %}{% load i18n %}
{% load local_timezone %}
{% block content %}
<h2>{% trans "Tokens for" %} {{ plugin.name }}</h2>
<div>
<table class="table table-striped plugins">
<thead>
<tr>
<th>{% trans "User" %}</th>
<th>{% trans "Token" %}</th>
<th>{% trans "Created at" %}</th>
<th>{% trans "Expires at" %}</th>
<th>{% trans "Manage" %}</th>
</tr>
</thead>
<tbody>
{% for token in object_list %}
<tr class="{% cycle "even" "odd" %}">
<td>{{ token.user }}</td>
<td style="max-width:200px">
<div style="display:flex">
<span class="truncate">
{{ token.token }}
</span>
</div>
</td>
<td>{{ token.created_at|local_timezone }}</td>
<td>{{ token.expires_at|local_timezone }}</td>
<td>
<div class="tooltip">
<button
class="btn btn-primary btn-mini"
onclick="copyToClipBoard('{{ token.token }}')"
>
<span class="tooltiptext" id="copyTooltip">{% trans "Copy token to clipboard" %}</span>
<i class="icon-copy icon-white"></i>
</button>
</div>
&nbsp;
<a
class="btn btn-danger btn-mini delete"
href="{% url "token_delete" plugin.package_name token.id %}"
title="{% trans "Delete" %}"><i class="icon-remove icon-white"></i>
</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock %}

{% block extracss %}
{{ block.super }}
<style>
.truncate {
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 1;
line-clamp: 1;
-webkit-box-orient: vertical;
}

.tooltip {
position: relative;
display: inline-block;
opacity: 1 !important;
}

.tooltip .tooltiptext {
visibility: hidden;
width: 140px;
background-color: #555;
color: #fff;
text-align: center;
border-radius: 6px;
padding: 5px;
position: absolute;
z-index: 1;
bottom: 150%;
left: 50%;
margin-left: -75px;
opacity: 0;
transition: opacity 0.3s;
}

.tooltip .tooltiptext::after {
content: "";
position: absolute;
top: 100%;
left: 50%;
margin-left: -5px;
border-width: 5px;
border-style: solid;
border-color: #555 transparent transparent transparent;
}

.tooltip:hover .tooltiptext {
visibility: visible;
opacity: 1;
}
</style>
{% endblock %}

{% block extrajs %}
<script type="text/javascript">
{{ block.super }}
function copyToClipBoard(token) {
navigator.clipboard.writeText(token);

var tooltip = document.getElementById("copyTooltip");
tooltip.innerHTML = "Token copied!";
}
</script>
{% endblock %}
9 changes: 9 additions & 0 deletions qgis-app/plugins/templates/plugins/token_delete_confirm.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{% extends 'plugins/plugin_base.html' %}{% 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 />The token 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 %}
4 changes: 4 additions & 0 deletions qgis-app/plugins/templates/plugins/token_permission_deny.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{% extends 'plugins/plugin_base.html' %}{% load i18n %}
{% block content %}
<div class="error">{% trans "You cannot see tokens for this plugin." %}</div>
{% endblock %}
9 changes: 7 additions & 2 deletions qgis-app/plugins/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,15 @@
),
url(
r"^(?P<package_name>[A-Za-z][A-Za-z0-9-_]+)/token/$",
plugin_token,
{},
PluginTokenListView.as_view(),
name="plugin_token",
),
url(
r"^(?P<package_name>[A-Za-z][A-Za-z0-9-_]+)/token/(?P<token_id>[^\/]+)/delete$",
token_delete,
{},
name="token_delete",
),
url(
r"^(?P<package_name>[A-Za-z][A-Za-z0-9-_]+)/set_featured/$",
plugin_set_featured,
Expand Down
67 changes: 48 additions & 19 deletions qgis-app/plugins/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@
from plugins.models import Plugin, PluginVersion, PluginVersionDownload, vjust
from plugins.validator import PLUGIN_REQUIRED_METADATA

from rest_framework_simplejwt.token_blacklist.models import OutstandingToken, BlacklistedToken
from rest_framework_simplejwt.tokens import RefreshToken

try:
from urllib import unquote, urlencode

Expand Down Expand Up @@ -594,29 +597,55 @@ def plugin_update(request, package_name):
{"form": form, "form_title": _("Edit plugin"), "plugin": plugin},
)

from rest_framework_simplejwt.tokens import RefreshToken

@login_required
def plugin_token(request, package_name):

class PluginTokenListView(ListView):
"""
Plugin token management
Plugin token list
"""
model = OutstandingToken
queryset = OutstandingToken.objects.all()
template_name = "plugins/outstandingtoken_list.html"

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

def get_context_data(self, **kwargs):
package_name = self.kwargs.get('package_name')
plugin = get_object_or_404(Plugin, package_name=package_name)
if not check_plugin_access(self.request.user, plugin):
context = {}
self.template_name = "plugins/token_permission_deny.html"
return context
context = super(PluginTokenListView, self).get_context_data(**kwargs)
context.update(
{
"plugin": plugin
}
)
return context

@login_required
def token_delete(request, package_name, token_id):
plugin = get_object_or_404(Plugin, package_name=package_name)
user = request.user
if not check_plugin_access(user, plugin):
return render(request, "plugins/plugin_permission_deny.html", {})

refresh = RefreshToken.for_user(user)
refresh['plugin_id'] = plugin.pk

token = {
'refresh': str(refresh),
'access': str(refresh.access_token)
}

print(token, '#############')

return render(request, "plugins/plugin_permission_deny.html", {})
outstanting_token = get_object_or_404(OutstandingToken, pk=token_id)
token = RefreshToken(outstanting_token.token)
if not check_plugin_access(request.user, plugin):
return render(request, "plugins/version_permission_deny.html", {})
if "delete_confirm" in request.POST:
token.blacklist()

msg = _("The token has been successfully deleted.")
messages.success(request, msg, fail_silently=True)
return HttpResponseRedirect(
reverse("plugin_token", args=(plugin.package_name,))
)
return render(
request,
"plugins/token_delete_confirm.html",
{"plugin": plugin, "username": outstanting_token.user},
)


class PluginsList(ListView):
Expand Down
3 changes: 3 additions & 0 deletions qgis-app/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,9 @@
"leaflet",
"bootstrapform",
"rest_framework",
'rest_framework.authtoken',
'rest_framework_simplejwt',
'rest_framework_simplejwt.token_blacklist',
"rest_framework_gis",
"preferences",
# styles:
Expand Down
1 change: 1 addition & 0 deletions qgis-app/settings_docker.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
"rest_framework",
'rest_framework.authtoken',
'rest_framework_simplejwt',
'rest_framework_simplejwt.token_blacklist',
"sorl_thumbnail_serializer", # serialize image
"drf_multiple_model",
"drf_yasg",
Expand Down

0 comments on commit d832116

Please sign in to comment.