diff --git a/netbox/account/views.py b/netbox/account/views.py
index da4aa6d74..7dd423919 100644
--- a/netbox/account/views.py
+++ b/netbox/account/views.py
@@ -25,10 +25,12 @@ from extras.models import Bookmark
from extras.tables import BookmarkTable, NotificationTable, SubscriptionTable
from netbox.authentication import get_auth_backend_display, get_saml_idps
from netbox.config import get_config
+from netbox.ui import layout
from netbox.views import generic
from users import forms
from users.models import UserConfig
from users.tables import TokenTable
+from users.ui.panels import TokenExamplePanel, TokenPanel
from utilities.request import safe_for_redirect
from utilities.string import remove_linebreaks
from utilities.views import register_model_view
@@ -342,12 +344,21 @@ class UserTokenListView(LoginRequiredMixin, View):
@register_model_view(UserToken)
class UserTokenView(LoginRequiredMixin, View):
+ layout = layout.SimpleLayout(
+ left_panels=[
+ TokenPanel(),
+ ],
+ right_panels=[
+ TokenExamplePanel(),
+ ],
+ )
def get(self, request, pk):
token = get_object_or_404(UserToken.objects.filter(user=request.user), pk=pk)
return render(request, 'account/token.html', {
'object': token,
+ 'layout': self.layout,
})
diff --git a/netbox/templates/users/panels/token_example.html b/netbox/templates/users/panels/token_example.html
new file mode 100644
index 000000000..ca70eac28
--- /dev/null
+++ b/netbox/templates/users/panels/token_example.html
@@ -0,0 +1,9 @@
+{% extends 'ui/panels/_base.html' %}
+
+{% block panel_content %}
+
curl -X GET \
+-H "Authorization: {{ object.get_auth_header_prefix }}<TOKEN>" \
+-H "Content-Type: application/json" \
+-H "Accept: application/json; indent=4" \
+{{ request.scheme }}://{{ request.get_host }}{% url "api-status" %}
+{% endblock panel_content %}
diff --git a/netbox/templates/users/token.html b/netbox/templates/users/token.html
index 7d8ee1a2f..06e79dc84 100644
--- a/netbox/templates/users/token.html
+++ b/netbox/templates/users/token.html
@@ -1,73 +1,4 @@
{% extends 'generic/object.html' %}
-{% load helpers %}
{% load i18n %}
-{% load render_table from django_tables2 %}
{% block title %}{% trans "Token" %} {{ object }}{% endblock %}
-
-{% block subtitle %}{% endblock %}
-
-{% block content %}
-
-
-
-
-
-
- | {% trans "Version" %} |
- {{ object.version }} |
-
- {% if object.version == 1 %}
-
- | {% trans "Token" %} |
- {{ object.partial }} |
-
- {% else %}
-
- | {% trans "Key" %} |
- {{ object }} |
-
-
- | {% trans "Pepper ID" %} |
- {{ object.pepper_id }} |
-
- {% endif %}
-
- | {% trans "User" %} |
-
- {{ object.user }}
- |
-
-
- | {% trans "Description" %} |
- {{ object.description|placeholder }} |
-
-
- | {% trans "Enabled" %} |
- {% checkmark object.enabled %} |
-
-
- | {% trans "Write enabled" %} |
- {% checkmark object.write_enabled %} |
-
-
- | {% trans "Created" %} |
- {{ object.created|isodatetime }} |
-
-
- | {% trans "Expires" %} |
- {{ object.expires|isodatetime|placeholder }} |
-
-
- | {% trans "Last used" %} |
- {{ object.last_used|isodatetime|placeholder }} |
-
-
- | {% trans "Allowed IPs" %} |
- {{ object.allowed_ips|join:", "|placeholder }} |
-
-
-
-
-
-{% endblock %}
diff --git a/netbox/users/models/tokens.py b/netbox/users/models/tokens.py
index f5b9f461c..bf51d6ef8 100644
--- a/netbox/users/models/tokens.py
+++ b/netbox/users/models/tokens.py
@@ -201,6 +201,15 @@ class Token(models.Model):
"""
return self.enabled and not self.is_expired
+ def get_auth_header_prefix(self):
+ """
+ Return the HTTP Authorization header prefix for this token.
+ """
+ if self.v1:
+ return 'Token '
+ if self.v2:
+ return f'Bearer {TOKEN_PREFIX}{self.key}.'
+
def clean(self):
super().clean()
diff --git a/netbox/users/ui/__init__.py b/netbox/users/ui/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/netbox/users/ui/panels.py b/netbox/users/ui/panels.py
new file mode 100644
index 000000000..92a4dd46a
--- /dev/null
+++ b/netbox/users/ui/panels.py
@@ -0,0 +1,25 @@
+from django.utils.translation import gettext_lazy as _
+
+from netbox.ui import actions, attrs, panels
+
+
+class TokenPanel(panels.ObjectAttributesPanel):
+ version = attrs.NumericAttr('version')
+ key = attrs.TextAttr('key')
+ token = attrs.TextAttr('partial')
+ pepper_id = attrs.NumericAttr('pepper_id')
+ user = attrs.RelatedObjectAttr('user', linkify=True)
+ description = attrs.TextAttr('description')
+ enabled = attrs.BooleanAttr('enabled')
+ write_enabled = attrs.BooleanAttr('write_enabled')
+ expires = attrs.TextAttr('expires')
+ last_used = attrs.TextAttr('last_used')
+ allowed_ips = attrs.TextAttr('allowed_ips')
+
+
+class TokenExamplePanel(panels.Panel):
+ template_name = 'users/panels/token_example.html'
+ title = _('Example Usage')
+ actions = [
+ actions.CopyContent('token-example')
+ ]
diff --git a/netbox/users/views.py b/netbox/users/views.py
index 17b1e7f7d..09a88a14b 100644
--- a/netbox/users/views.py
+++ b/netbox/users/views.py
@@ -3,7 +3,9 @@ from django.db.models import Count
from core.models import ObjectChange
from core.tables import ObjectChangeTable
from netbox.object_actions import AddObject, BulkDelete, BulkEdit, BulkExport, BulkImport, BulkRename
+from netbox.ui import layout
from netbox.views import generic
+from users.ui import panels
from utilities.query import count_related
from utilities.views import GetRelatedModelsMixin, register_model_view
from . import filtersets, forms, tables
@@ -26,6 +28,14 @@ class TokenListView(generic.ObjectListView):
@register_model_view(Token)
class TokenView(generic.ObjectView):
queryset = Token.objects.all()
+ layout = layout.SimpleLayout(
+ left_panels=[
+ panels.TokenPanel(),
+ ],
+ right_panels=[
+ panels.TokenExamplePanel(),
+ ],
+ )
@register_model_view(Token, 'add', detail=False)