Przeglądaj źródła

Merge pull request #20577 from netbox-community/20492-disable-token-plaintext-retrieval

Closes #20492: Disable API token plaintext retrieval
bctiemann 4 miesięcy temu
rodzic
commit
5ad6bd88f6

+ 0 - 11
docs/configuration/security.md

@@ -1,16 +1,5 @@
 # Security & Authentication Parameters
 # Security & Authentication Parameters
 
 
-## ALLOW_TOKEN_RETRIEVAL
-
-Default: `False`
-
-!!! note
-    The default value of this parameter changed from `True` to `False` in NetBox v4.3.0.
-
-If disabled, the values of API tokens will not be displayed after each token's initial creation. A user **must** record the value of a token prior to its creation, or it will be lost. Note that this affects _all_ users, regardless of assigned permissions.
-
----
-
 ## ALLOWED_URL_SCHEMES
 ## ALLOWED_URL_SCHEMES
 
 
 !!! tip "Dynamic Configuration Parameter"
 !!! tip "Dynamic Configuration Parameter"

+ 5 - 2
docs/integrations/rest-api.md

@@ -80,7 +80,7 @@ Likewise, the site, rack, and device objects are located under the "DCIM" applic
 
 
 The full hierarchy of available endpoints can be viewed by navigating to the API root in a web browser.
 The full hierarchy of available endpoints can be viewed by navigating to the API root in a web browser.
 
 
-Each model generally has two views associated with it: a list view and a detail view. The list view is used to retrieve a list of multiple objects and to create new objects. The detail view is used to retrieve, update, or delete an single existing object. All objects are referenced by their numeric primary key (`id`).
+Each model generally has two views associated with it: a list view and a detail view. The list view is used to retrieve a list of multiple objects and to create new objects. The detail view is used to retrieve, update, or delete a single existing object. All objects are referenced by their numeric primary key (`id`).
 
 
 * `/api/dcim/devices/` - List existing devices or create a new device
 * `/api/dcim/devices/` - List existing devices or create a new device
 * `/api/dcim/devices/123/` - Retrieve, update, or delete the device with ID 123
 * `/api/dcim/devices/123/` - Retrieve, update, or delete the device with ID 123
@@ -655,6 +655,9 @@ The NetBox REST API primarily employs token-based authentication. For convenienc
 
 
 A token is a secret, unique identifier mapped to a NetBox user account. Each user may have one or more tokens which he or she can use for authentication when making REST API requests. To create a token, navigate to the API tokens page under your user profile. When creating a token, NetBox will automatically populate a randomly-generated token value.
 A token is a secret, unique identifier mapped to a NetBox user account. Each user may have one or more tokens which he or she can use for authentication when making REST API requests. To create a token, navigate to the API tokens page under your user profile. When creating a token, NetBox will automatically populate a randomly-generated token value.
 
 
+!!! note "Tokens cannot be retrieved once created"
+    Once a token has been created, its plaintext value cannot be retrieved. For this reason, you must take care to securely record the token locally immediately upon its creation. If a token plaintext is lost, it cannot be recovered: A new token must be created.
+
 By default, all users can create and manage their own REST API tokens under the user control panel in the UI or via the REST API. This ability can be disabled by overriding the [`DEFAULT_PERMISSIONS`](../configuration/security.md#default_permissions) configuration parameter.
 By default, all users can create and manage their own REST API tokens under the user control panel in the UI or via the REST API. This ability can be disabled by overriding the [`DEFAULT_PERMISSIONS`](../configuration/security.md#default_permissions) configuration parameter.
 
 
 Additionally, a token can be set to expire at a specific time. This can be useful if an external client needs to be granted temporary access to NetBox.
 Additionally, a token can be set to expire at a specific time. This can be useful if an external client needs to be granted temporary access to NetBox.
@@ -663,7 +666,7 @@ Additionally, a token can be set to expire at a specific time. This can be usefu
 
 
 Beginning with NetBox v4.5, two versions of API token are supported, denoted as v1 and v2. Users are strongly encouraged to create only v2 tokens and to discontinue the use of v1 tokens. Support for v1 tokens will be removed in a future NetBox release.
 Beginning with NetBox v4.5, two versions of API token are supported, denoted as v1 and v2. Users are strongly encouraged to create only v2 tokens and to discontinue the use of v1 tokens. Support for v1 tokens will be removed in a future NetBox release.
 
 
-v2 API tokens offer much stronger security. The token plaintext given at creation time is hashed together with a configured [cryptographic pepper](../configuration/required-parameters.md#api_token_peppers) to generate a unique checksum. This checksum is irreversible; the token plaintext is never stored on the server and thus cannot be retrieved.
+v2 API tokens offer much stronger security. The token plaintext given at creation time is hashed together with a configured [cryptographic pepper](../configuration/required-parameters.md#api_token_peppers) to generate a unique checksum. This checksum is irreversible; the token plaintext is never stored on the server and thus cannot be retrieved even with database-level access.
 
 
 #### Restricting Write Operations
 #### Restricting Write Operations
 
 

+ 0 - 3
netbox/netbox/configuration_example.py

@@ -91,9 +91,6 @@ ADMINS = [
     # ('John Doe', 'jdoe@example.com'),
     # ('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
 # 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.
 # Django documentation at https://docs.djangoproject.com/en/stable/topics/auth/passwords/#password-validation.
 AUTH_PASSWORD_VALIDATORS = [
 AUTH_PASSWORD_VALIDATORS = [

+ 0 - 2
netbox/netbox/configuration_testing.py

@@ -43,8 +43,6 @@ SECRET_KEY = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
 
 
 DEFAULT_PERMISSIONS = {}
 DEFAULT_PERMISSIONS = {}
 
 
-ALLOW_TOKEN_RETRIEVAL = True
-
 API_TOKEN_PEPPERS = {
 API_TOKEN_PEPPERS = {
     1: 'TEST-VALUE-DO-NOT-USE-TEST-VALUE-DO-NOT-USE-TEST-VALUE-DO-NOT-USE',
     1: 'TEST-VALUE-DO-NOT-USE-TEST-VALUE-DO-NOT-USE-TEST-VALUE-DO-NOT-USE',
 }
 }

+ 0 - 1
netbox/netbox/settings.py

@@ -76,7 +76,6 @@ elif hasattr(configuration, 'DATABASE') and hasattr(configuration, 'DATABASES'):
 
 
 # Set static config parameters
 # Set static config parameters
 ADMINS = getattr(configuration, 'ADMINS', [])
 ADMINS = getattr(configuration, 'ADMINS', [])
-ALLOW_TOKEN_RETRIEVAL = getattr(configuration, 'ALLOW_TOKEN_RETRIEVAL', False)
 ALLOWED_HOSTS = getattr(configuration, 'ALLOWED_HOSTS')  # Required
 ALLOWED_HOSTS = getattr(configuration, 'ALLOWED_HOSTS')  # Required
 API_TOKEN_PEPPERS = getattr(configuration, 'API_TOKEN_PEPPERS', {})
 API_TOKEN_PEPPERS = getattr(configuration, 'API_TOKEN_PEPPERS', {})
 AUTH_PASSWORD_VALIDATORS = getattr(configuration, 'AUTH_PASSWORD_VALIDATORS', [
 AUTH_PASSWORD_VALIDATORS = getattr(configuration, 'AUTH_PASSWORD_VALIDATORS', [

+ 1 - 8
netbox/templates/users/token.html

@@ -20,14 +20,7 @@
           {% if object.version == 1 %}
           {% if object.version == 1 %}
             <tr>
             <tr>
               <th scope="row">{% trans "Token" %}</th>
               <th scope="row">{% trans "Token" %}</th>
-              <td>
-                {% if settings.ALLOW_TOKEN_RETRIEVAL %}
-                  <span id="secret" class="font-monospace" data-secret="{{ object.plaintext }}">{{ object.plaintext }}</span>
-                  <button type="button" class="btn btn-primary toggle-secret float-end" data-bs-toggle="button">{% trans "Show Secret" %}</button>
-                {% else %}
-                  {{ object.partial }}
-                {% endif %}
-              </td>
+              <td>{{ object.partial }}</td>
             </tr>
             </tr>
           {% else %}
           {% else %}
             <tr>
             <tr>

+ 3 - 7
netbox/users/forms/model_forms.py

@@ -1,7 +1,6 @@
 import json
 import json
 
 
 from django import forms
 from django import forms
-from django.conf import settings
 from django.contrib.auth import password_validation
 from django.contrib.auth import password_validation
 from django.contrib.postgres.forms import SimpleArrayField
 from django.contrib.postgres.forms import SimpleArrayField
 from django.core.exceptions import FieldError
 from django.core.exceptions import FieldError
@@ -115,7 +114,7 @@ class UserTokenForm(forms.ModelForm):
         label=_('Token'),
         label=_('Token'),
         help_text=_(
         help_text=_(
             'Tokens must be at least 40 characters in length. <strong>Be sure to record your key</strong> prior to '
             'Tokens must be at least 40 characters in length. <strong>Be sure to record your key</strong> prior to '
-            'submitting this form, as it may no longer be accessible once the token has been created.'
+            'submitting this form, as it will no longer be accessible once the token has been created.'
         ),
         ),
         widget=forms.TextInput(
         widget=forms.TextInput(
             attrs={'data-clipboard': 'true'}
             attrs={'data-clipboard': 'true'}
@@ -148,11 +147,8 @@ class UserTokenForm(forms.ModelForm):
             self.fields['version'].disabled = True
             self.fields['version'].disabled = True
             self.fields['user'].disabled = True
             self.fields['user'].disabled = True
 
 
-            # Omit the key field when editing an existing token if token retrieval is not permitted
-            if self.instance.v1 and settings.ALLOW_TOKEN_RETRIEVAL:
-                self.initial['token'] = self.instance.plaintext
-            else:
-                del self.fields['token']
+            # Omit the key field when editing an existing Token
+            del self.fields['token']
 
 
         # Generate an initial random key if none has been specified
         # Generate an initial random key if none has been specified
         elif self.instance._state.adding and not self.initial.get('token'):
         elif self.instance._state.adding and not self.initial.get('token'):

+ 1 - 8
netbox/users/tables.py

@@ -11,13 +11,7 @@ __all__ = (
     'UserTable',
     'UserTable',
 )
 )
 
 
-TOKEN = """<samp><a href="{{ record.get_absolute_url }}" id="token_{{ record.pk }}">{{ record }}</a></samp>"""
-
-COPY_BUTTON = """
-{% if settings.ALLOW_TOKEN_RETRIEVAL %}
-  {% copy_content record.pk prefix="token_" color="success" %}
-{% endif %}
-"""
+TOKEN = """<samp><a href="{{ record.get_absolute_url }}">{{ record }}</a></samp>"""
 
 
 
 
 class TokenTable(NetBoxTable):
 class TokenTable(NetBoxTable):
@@ -48,7 +42,6 @@ class TokenTable(NetBoxTable):
     )
     )
     actions = columns.ActionsColumn(
     actions = columns.ActionsColumn(
         actions=('edit', 'delete'),
         actions=('edit', 'delete'),
-        extra_buttons=COPY_BUTTON
     )
     )
 
 
     class Meta(NetBoxTable.Meta):
     class Meta(NetBoxTable.Meta):