Skip to content

Commit

Permalink
Closes #12325: Disable the Django admin UI by default (#15008)
Browse files Browse the repository at this point in the history
* Disable the Django admin UI by default

* Remove outdated references to the admin UI

* Update tests
  • Loading branch information
jeremystretch authored Feb 5, 2024
1 parent 93b77cb commit 5d9311e
Show file tree
Hide file tree
Showing 19 changed files with 67 additions and 34 deletions.
2 changes: 1 addition & 1 deletion docs/administration/authentication/microsoft-azure-ad.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ You should be redirected to Microsoft's authentication portal. Enter the usernam

If successful, you will be redirected back to the NetBox UI, and will be logged in as the AD user. You can verify this by navigating to your profile (using the button at top right).

This user account has been replicated locally to NetBox, and can now be assigned groups and permissions within the NetBox admin UI.
This user account has been replicated locally to NetBox, and can now be assigned groups and permissions within NetBox.

## Troubleshooting

Expand Down
2 changes: 1 addition & 1 deletion docs/administration/authentication/okta.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,4 @@ You should be redirected to Okta's authentication portal. Enter the username/ema

If successful, you will be redirected back to the NetBox UI, and will be logged in as the Okta user. You can verify this by navigating to your profile (using the button at top right).

This user account has been replicated locally to NetBox, and can now be assigned groups and permissions within the NetBox admin UI.
This user account has been replicated locally to NetBox, and can now be assigned groups and permissions within NetBox.
2 changes: 1 addition & 1 deletion docs/administration/authentication/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

Local user accounts and groups can be created in NetBox under the "Authentication and Authorization" section of the administrative user interface. This interface is available only to users with the "staff" permission enabled.

At a minimum, each user account must have a username and password set. User accounts may also denote a first name, last name, and email address. [Permissions](../permissions.md) may also be assigned to users and/or groups within the admin UI.
At a minimum, each user account must have a username and password set. User accounts may also denote a first name, last name, and email address. [Permissions](../permissions.md) may also be assigned to individual users and/or groups as needed.

## Remote Authentication

Expand Down
2 changes: 1 addition & 1 deletion docs/configuration/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,4 @@ The configuration file may be modified at any time. However, the WSGI service (e
$ sudo systemctl restart netbox
```

Configuration parameters which are set via the admin UI (those listed under "dynamic settings") take effect immediately.
Configuration parameters which are set via the user interface (those listed under "dynamic settings") take effect immediately.
8 changes: 8 additions & 0 deletions docs/configuration/miscellaneous.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,14 @@ The maximum size (in bytes) of an incoming HTTP request (i.e. `GET` or `POST` da

---

## DJANGO_ADMIN_ENABLED

Default: False

Setting this to True installs the `django.contrib.admin` app and enables the [Django admin UI](https://docs.djangoproject.com/en/5.0/ref/contrib/admin/). This may be necessary to support older plugins which do not integrate with the native NetBox interface.

---

## ENFORCE_GLOBAL_UNIQUE

!!! tip "Dynamic Configuration Parameter"
Expand Down
2 changes: 1 addition & 1 deletion docs/customization/custom-scripts.md
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ An IPv4 or IPv6 network with a mask. Returns a `netaddr.IPNetwork` object. Two a
## Running Custom Scripts

!!! note
To run a custom script, a user must be assigned via permissions for `Extras > Script`, `Extras > ScriptModule`, and `Core > ManagedFile` objects. They must also be assigned the `extras.run_script` permission. This is achieved by assigning the user (or group) a permission on the Script object and specifying the `run` action in the admin UI as shown below.
To run a custom script, a user must be assigned via permissions for `Extras > Script`, `Extras > ScriptModule`, and `Core > ManagedFile` objects. They must also be assigned the `extras.run_script` permission. This is achieved by assigning the user (or group) a permission on the Script object and specifying the `run` action as shown below.

![Adding the run action to a permission](../media/admin_ui_run_permission.png)

Expand Down
2 changes: 1 addition & 1 deletion docs/customization/reports.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ Once you have created a report, it will appear in the reports list. Initially, r
## Running Reports

!!! note
To run a report, a user must be assigned via permissions for `Extras > Report`, `Extras > ReportModule`, and `Core > ManagedFile` objects. They must also be assigned the `extras.run_report` permission. This is achieved by assigning the user (or group) a permission on the Report object and specifying the `run` action in the admin UI as shown below.
To run a report, a user must be assigned via permissions for `Extras > Report`, `Extras > ReportModule`, and `Core > ManagedFile` objects. They must also be assigned the `extras.run_report` permission. This is achieved by assigning the user (or group) a permission on the Report object and specifying the `run` action as shown below.

![Adding the run action to a permission](../media/admin_ui_run_permission.png)

Expand Down
2 changes: 1 addition & 1 deletion docs/features/event-rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,4 @@ For more detail, see the reference documentation for NetBox's [conditional logic

## Event Rule Processing

When a change is detected, any resulting events are placed into a Redis queue for processing. This allows the user's request to complete without needing to wait for the outgoing event(s) to be processed. The events are then extracted from the queue by the `rqworker` process. The current event queue and any failed events can be inspected in the admin UI under System > Background Tasks.
When a change is detected, any resulting events are placed into a Redis queue for processing. This allows the user's request to complete without needing to wait for the outgoing event(s) to be processed. The events are then extracted from the queue by the `rqworker` process. The current event queue and any failed events can be inspected under System > Background Tasks.
6 changes: 3 additions & 3 deletions docs/integrations/webhooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,9 @@ If no body template is specified, the request body will be populated with a JSON

## Webhook Processing

Using [Event Rules](../features/event-rules.md), when a change is detected, any resulting webhooks are placed into a Redis queue for processing. This allows the user's request to complete without needing to wait for the outgoing webhook(s) to be processed. The webhooks are then extracted from the queue by the `rqworker` process and HTTP requests are sent to their respective destinations. The current webhook queue and any failed webhooks can be inspected in the admin UI under System > Background Tasks.
Using [Event Rules](../features/event-rules.md), when a change is detected, any resulting webhooks are placed into a Redis queue for processing. This allows the user's request to complete without needing to wait for the outgoing webhook(s) to be processed. The webhooks are then extracted from the queue by the `rqworker` process and HTTP requests are sent to their respective destinations. The current webhook queue and any failed webhooks can be inspected under System > Background Tasks.

A request is considered successful if the response has a 2XX status code; otherwise, the request is marked as having failed. Failed requests may be retried manually via the admin UI.
A request is considered successful if the response has a 2XX status code; otherwise, the request is marked as having failed. Failed requests may be requeued manually under System > Background Tasks.

## Troubleshooting

Expand Down Expand Up @@ -108,4 +108,4 @@ Content-Type: application/x-www-form-urlencoded

Note that `webhook_receiver` does not actually _do_ anything with the information received: It merely prints the request headers and body for inspection.

Now, when the NetBox webhook is triggered and processed, you should see its headers and content appear in the terminal where the webhook receiver is listening. If you don't, check that the `rqworker` process is running and that webhook events are being placed into the queue (visible under the NetBox admin UI).
Now, when the NetBox webhook is triggered and processed, you should see its headers and content appear in the terminal where the webhook receiver is listening. If you don't, check that the `rqworker` process is running and that webhook events are being placed into the queue.
2 changes: 2 additions & 0 deletions netbox/netbox/configuration_testing.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@

SECRET_KEY = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'

DJANGO_ADMIN_ENABLED = True

DEFAULT_PERMISSIONS = {}

LOGGING = {
Expand Down
5 changes: 4 additions & 1 deletion netbox/netbox/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@
'users.delete_token': ({'user': '$user'},),
})
DEVELOPER = getattr(configuration, 'DEVELOPER', False)
DJANGO_ADMIN_ENABLED = getattr(configuration, 'DJANGO_ADMIN_ENABLED', False)
DOCS_ROOT = getattr(configuration, 'DOCS_ROOT', os.path.join(os.path.dirname(BASE_DIR), 'docs'))
EMAIL = getattr(configuration, 'EMAIL', {})
EVENTS_PIPELINE = getattr(configuration, 'EVENTS_PIPELINE', (
Expand Down Expand Up @@ -355,7 +356,6 @@ def _setting(name, default=None):
#

INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
Expand Down Expand Up @@ -393,6 +393,9 @@ def _setting(name, default=None):
'drf_spectacular_sidecar',
]

if DJANGO_ADMIN_ENABLED:
INSTALLED_APPS.insert(0, 'django.contrib.admin')

# Middleware
MIDDLEWARE = [
'graphiql_debug_toolbar.middleware.DebugToolbarMiddleware',
Expand Down
12 changes: 6 additions & 6 deletions netbox/netbox/tests/dummy_plugin/navigation.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,23 @@

items = (
PluginMenuItem(
link='plugins:dummy_plugin:dummy_models',
link='plugins:dummy_plugin:dummy_model_list',
link_text='Item 1',
buttons=(
PluginMenuButton(
link='admin:dummy_plugin_dummymodel_add',
title='Add a new dummy model',
link='plugins:dummy_plugin:dummy_model_add',
title='Button 1',
icon_class='mdi mdi-plus-thick',
),
PluginMenuButton(
link='admin:dummy_plugin_dummymodel_add',
title='Add a new dummy model',
link='plugins:dummy_plugin:dummy_model_add',
title='Button 2',
icon_class='mdi mdi-plus-thick',
),
)
),
PluginMenuItem(
link='plugins:dummy_plugin:dummy_models',
link='plugins:dummy_plugin:dummy_model_list',
link_text='Item 2',
),
)
Expand Down
3 changes: 2 additions & 1 deletion netbox/netbox/tests/dummy_plugin/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@


urlpatterns = (
path('models/', views.DummyModelsView.as_view(), name='dummy_models'),
path('models/', views.DummyModelsView.as_view(), name='dummy_model_list'),
path('models/add/', views.DummyModelAddView.as_view(), name='dummy_model_add'),
)
17 changes: 17 additions & 0 deletions netbox/netbox/tests/dummy_plugin/views.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import random
import string

from django.http import HttpResponse
from django.views.generic import View

Expand All @@ -15,6 +18,20 @@ def get(self, request):
return HttpResponse(f"Instances: {instance_count}")


class DummyModelAddView(View):

def get(self, request):
return HttpResponse(f"Create an instance")

def post(self, request):
instance = DummyModel(
name=''.join(random.choices(string.ascii_lowercase, k=8)),
number=random.randint(1, 100000)
)
instance.save()
return HttpResponse(f"Instance created")


@register_model_view(Site, 'extra', path='other-stuff')
class ExtraCoreModelView(View):

Expand Down
2 changes: 1 addition & 1 deletion netbox/netbox/tests/test_plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def test_admin(self):
def test_views(self):

# Test URL resolution
url = reverse('plugins:dummy_plugin:dummy_models')
url = reverse('plugins:dummy_plugin:dummy_model_list')
self.assertEqual(url, '/plugins/dummy-plugin/models/')

# Test GET request
Expand Down
20 changes: 9 additions & 11 deletions netbox/netbox/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
from netbox.graphql.views import GraphQLView
from netbox.plugins.urls import plugin_patterns, plugin_api_patterns
from netbox.views import HomeView, StaticMediaFailureView, SearchView, htmx
from .admin import admin_site

_patterns = [

Expand Down Expand Up @@ -70,26 +69,25 @@
# Plugins
path('plugins/', include((plugin_patterns, 'plugins'))),
path('api/plugins/', include((plugin_api_patterns, 'plugins-api'))),

# Admin
path('admin/', admin_site.urls),
]

# Django admin UI
if settings.DJANGO_ADMIN_ENABLED:
from .admin import admin_site
_patterns.append(path('admin/', admin_site.urls))

# django-debug-toolbar
if settings.DEBUG:
import debug_toolbar
_patterns += [
path('__debug__/', include(debug_toolbar.urls)),
]
_patterns.append(path('__debug__/', include(debug_toolbar.urls)))

# Prometheus metrics
if settings.METRICS_ENABLED:
_patterns += [
path('', include('django_prometheus.urls')),
]
_patterns.append(path('', include('django_prometheus.urls')))

# Prepend BASE_PATH
urlpatterns = [
path('{}'.format(settings.BASE_PATH), include(_patterns))
path(settings.BASE_PATH, include(_patterns))
]

handler404 = 'netbox.views.errors.handler_404'
Expand Down
2 changes: 1 addition & 1 deletion netbox/templates/account/profile.html
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ <h5 class="card-header">{% trans "Account Details" %}</h5>
<td>{% checkmark request.user.is_superuser %}</td>
</tr>
<tr>
<th scope="row">{% trans "Admin Access" %}</th>
<th scope="row">{% trans "Staff" %}</th>
<td>{% checkmark request.user.is_staff %}</td>
</tr>
</table>
Expand Down
6 changes: 5 additions & 1 deletion netbox/templates/core/configrevision_restore.html
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,11 @@
<td>{{ param }}</td>
<td>{{ current }}</td>
<td>{{ new }}</td>
<td>{% if current != new %}<img src="{% static 'admin/img/icon-changelink.svg' %}" alt="*" title="{% trans "Changed" %}">{% endif %}</td>
<td>
{% if current != new %}
<i class="mdi mdi-pencil text-warning" title="{% trans "Changed" %}"></i>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
Expand Down
4 changes: 2 additions & 2 deletions netbox/templates/inc/user_menu.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
</div>
</a>
<div class="dropdown-menu dropdown-menu-end dropdown-menu-arrow">
{% if request.user.is_staff %}
{% if config.DJANGO_ADMIN_ENABLED and request.user.is_staff %}
<a class="dropdown-item" href="{% url 'admin:index' %}">
<i class="mdi mdi-cog"></i> {% trans "Admin" %}
<i class="mdi mdi-cog"></i> {% trans "Django Admin" %}
</a>
{% endif %}
<a href="{% url 'account:profile' %}" class="dropdown-item">
Expand Down

0 comments on commit 5d9311e

Please sign in to comment.