From 9f4135a23a67c7a3573b1c49696dc30b7b71fa1b Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Wed, 2 Nov 2022 10:00:57 -0400 Subject: [PATCH] Clean up display of tokens in views --- netbox/netbox/configuration_example.py | 7 ++- netbox/templates/generic/object_edit.html | 2 - netbox/templates/users/api_token.html | 58 ++++++++++++++++------- netbox/users/forms.py | 12 ++--- netbox/users/models.py | 11 +++-- netbox/users/tables.py | 17 +++---- netbox/users/views.py | 5 +- 7 files changed, 60 insertions(+), 52 deletions(-) diff --git a/netbox/netbox/configuration_example.py b/netbox/netbox/configuration_example.py index 76fdaaf02..b3b6fbb6c 100644 --- a/netbox/netbox/configuration_example.py +++ b/netbox/netbox/configuration_example.py @@ -72,6 +72,9 @@ ADMINS = [ # ('John Doe', 'jdoe@example.com'), ] +# Permit the retrieval of API tokens after their creation. +ALLOW_TOKEN_RETRIEVAL = False + # Enable any desired validators for local account passwords below. For a list of included validators, please see the # Django documentation at https://docs.djangoproject.com/en/stable/topics/auth/passwords/#password-validation. AUTH_PASSWORD_VALIDATORS = [ @@ -224,7 +227,3 @@ TIME_FORMAT = 'g:i a' SHORT_TIME_FORMAT = 'H:i:s' DATETIME_FORMAT = 'N j, Y g:i a' SHORT_DATETIME_FORMAT = 'Y-m-d H:i' - -# Allow API Tokens to be viewed after creation. Before NetBox 3.4 the default was to allow viewing of the tokens -# so this flag was created for backwards compatability. -ALLOW_TOKEN_RETRIEVAL = False diff --git a/netbox/templates/generic/object_edit.html b/netbox/templates/generic/object_edit.html index 47a7c24fb..c61fb723f 100644 --- a/netbox/templates/generic/object_edit.html +++ b/netbox/templates/generic/object_edit.html @@ -109,11 +109,9 @@ Context: - {% if not disable_addanother %} - {% endif %} {% endif %} Cancel {% endblock buttons %} diff --git a/netbox/templates/users/api_token.html b/netbox/templates/users/api_token.html index effbe6cf1..1a9296704 100644 --- a/netbox/templates/users/api_token.html +++ b/netbox/templates/users/api_token.html @@ -6,31 +6,55 @@ {% block content %}
+ {% if not settings.ALLOW_TOKEN_RETRIEVAL %} + + {% endif %}
-
Token Created
+
Token
-

The token has been created, you will need to copy the key as it will no longer be displayed for security purposes.

- - + + + + + + + + + + + + + + + + + +
Key{{ object.key }}Key +
+ + + +
+
{{ key }}
+
Description{{ object.description|placeholder }}
User{{ object.user }}
Created{{ object.created|annotated_date }}
Expires + {% if object.expires %} + {{ object.expires|annotated_date }} + {% else %} + Never + {% endif %} +
-
- {% csrf_token %} - {% render_form form %} -
-
- - Cancel -
-
-
- {% plugin_left_page object %} +
{% endblock %} diff --git a/netbox/users/forms.py b/netbox/users/forms.py index e6ef32da2..048005f13 100644 --- a/netbox/users/forms.py +++ b/netbox/users/forms.py @@ -121,13 +121,7 @@ class TokenForm(BootstrapMixin, forms.ModelForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - instance = getattr(self, 'instance', None) - if instance and instance.id and not settings.ALLOW_TOKEN_RETRIEVAL: - keyfield = self.fields['key'] - keyfield.disabled = True - keyfield.required = False - keyfield.widget = forms.HiddenInput() - -class TokenViewForm(BootstrapMixin, forms.Form): - view_token = forms.BooleanField(widget=forms.HiddenInput(), required=False) + # Omit the key field if token retrieval is not permitted + if self.instance.pk and not settings.ALLOW_TOKEN_RETRIEVAL: + del self.fields['key'] diff --git a/netbox/users/models.py b/netbox/users/models.py index e6a86be80..441ed2eee 100644 --- a/netbox/users/models.py +++ b/netbox/users/models.py @@ -1,6 +1,7 @@ import binascii import os +from django.conf import settings from django.contrib.auth.models import Group, User from django.contrib.contenttypes.models import ContentType from django.contrib.postgres.fields import ArrayField @@ -230,12 +231,12 @@ class Token(models.Model): 'Ex: "10.1.1.0/24, 192.168.10.16/32, 2001:DB8:1::/64"', ) - class Meta: - pass - def __str__(self): - # Only display the last 24 bits of the token to avoid accidental exposure. - return f"{self.description or self.key[-6:]} ({self.user})" + return self.key if settings.ALLOW_TOKEN_RETRIEVAL else self.partial + + @property + def partial(self): + return f'**********************************{self.key[-6:]}' if self.key else '' def save(self, *args, **kwargs): if not self.key: diff --git a/netbox/users/tables.py b/netbox/users/tables.py index 61307fe3d..8fbe9e8b3 100644 --- a/netbox/users/tables.py +++ b/netbox/users/tables.py @@ -1,4 +1,3 @@ -from django.conf import settings from .models import Token from netbox.tables import NetBoxTable, columns @@ -7,14 +6,16 @@ __all__ = ( ) -TOKEN = """{{ value }}""" +TOKEN = """{{ record }}""" ALLOWED_IPS = """{{ value|join:", " }}""" COPY_BUTTON = """ - - - +{% if settings.ALLOW_TOKEN_RETRIEVAL %} + + + +{% endif %} """ @@ -41,9 +42,3 @@ class TokenTable(NetBoxTable): fields = ( 'pk', 'description', 'key', 'write_enabled', 'created', 'expires', 'last_used', 'allowed_ips', ) - - def render_key(self, value): - if settings.ALLOW_TOKEN_RETRIEVAL: - return value - else: - return "****************************************" diff --git a/netbox/users/views.py b/netbox/users/views.py index bc3614e5e..0cda966ed 100644 --- a/netbox/users/views.py +++ b/netbox/users/views.py @@ -20,7 +20,7 @@ from extras.tables import ObjectChangeTable from netbox.authentication import get_auth_backend_display, get_saml_idps from netbox.config import get_config from utilities.forms import ConfirmationForm -from .forms import LoginForm, PasswordChangeForm, TokenForm, TokenViewForm, UserConfigForm +from .forms import LoginForm, PasswordChangeForm, TokenForm, UserConfigForm from .models import Token, UserConfig from .tables import TokenTable @@ -261,7 +261,6 @@ class TokenEditView(LoginRequiredMixin, View): 'object': token, 'form': form, 'return_url': reverse('users:token_list'), - 'disable_addanother': not settings.ALLOW_TOKEN_RETRIEVAL }) def post(self, request, pk=None): @@ -288,10 +287,8 @@ class TokenEditView(LoginRequiredMixin, View): messages.success(request, msg) if not pk and not settings.ALLOW_TOKEN_RETRIEVAL: - form = TokenViewForm(initial={'view_token': True}) return render(request, 'users/api_token.html', { 'object': token, - 'form': form, 'key': token.key, 'return_url': reverse('users:token_list'), })