Fix admin site - Custom admin URL (#5766)

* Update config template file

* Add entry to docs

* Add INVENTREE_ADMIN_ENABLED to settings.py

* Add helper functions for improving admin links

* Refactor existing admin links

* remove debug statements

* Fix custom admin URL

* Expand documentation

* Fix URL

* Improve wording in config_template.yaml

* Extend admin_url tag

- Allow lookup without pk
- Handle case where pk not found
This commit is contained in:
Oliver 2023-10-21 22:12:14 +11:00 committed by GitHub
parent 388b722de1
commit b335728e29
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 130 additions and 53 deletions

View File

@ -197,7 +197,18 @@ if DBBACKUP_STORAGE_OPTIONS is None:
'location': config.get_backup_dir(), 'location': config.get_backup_dir(),
} }
# Application definition INVENTREE_ADMIN_ENABLED = get_boolean_setting(
'INVENTREE_ADMIN_ENABLED',
config_key='admin_enabled',
default_value=True
)
# Base URL for admin pages (default="admin")
INVENTREE_ADMIN_URL = get_setting(
'INVENTREE_ADMIN_URL',
config_key='admin_url',
default_value='admin'
)
INSTALLED_APPS = [ INSTALLED_APPS = [
# Admin site integration # Admin site integration
@ -378,14 +389,6 @@ if DEBUG:
INSTALLED_APPS.append('sslserver') INSTALLED_APPS.append('sslserver')
# InvenTree URL configuration # InvenTree URL configuration
# Base URL for admin pages (default="admin")
INVENTREE_ADMIN_URL = get_setting(
'INVENTREE_ADMIN_URL',
config_key='admin_url',
default_value='admin'
)
ROOT_URLCONF = 'InvenTree.urls' ROOT_URLCONF = 'InvenTree.urls'
TEMPLATES = [ TEMPLATES = [

View File

@ -209,11 +209,14 @@ classic_frontendpatterns = [
new_frontendpatterns = platform_urls new_frontendpatterns = platform_urls
urlpatterns = [ urlpatterns = []
# admin sites
re_path(f'^{settings.INVENTREE_ADMIN_URL}/error_log/', include('error_report.urls')), if settings.INVENTREE_ADMIN_ENABLED:
re_path(f'^{settings.INVENTREE_ADMIN_URL}/', admin.site.urls, name='inventree-admin'), admin_url = settings.INVENTREE_ADMIN_URL,
] urlpatterns += [
path(f'{admin_url}/error_log/', include('error_report.urls')),
path(f'{admin_url}/', admin.site.urls, name='inventree-admin'),
]
urlpatterns += backendpatterns urlpatterns += backendpatterns

View File

@ -29,10 +29,9 @@ src="{% static 'img/blank_image.png' %}"
{% block actions %} {% block actions %}
<!-- Admin Display --> <!-- Admin Display -->
{% if user.is_staff and roles.build.change %} {% admin_url user "build.build" build.pk as url %}
{% url 'admin:build_build_change' build.pk as url %}
{% include "admin_button.html" with url=url %} {% include "admin_button.html" with url=url %}
{% endif %}
{% if barcodes %} {% if barcodes %}
<!-- Barcode actions menu --> <!-- Barcode actions menu -->
<div class='btn-group' role='group'> <div class='btn-group' role='group'>

View File

@ -14,10 +14,9 @@
{% block actions %} {% block actions %}
<!-- Admin View --> <!-- Admin View -->
{% if user.is_staff and perms.company.change_company %} {% admin_url user "company.company" company.pk as url %}
{% url 'admin:company_company_change' company.pk as url %}
{% include "admin_button.html" with url=url %} {% include "admin_button.html" with url=url %}
{% endif %}
{% if company.is_supplier and roles.purchase_order.add %} {% if company.is_supplier and roles.purchase_order.add %}
<button type='button' class='btn btn-outline-primary' id='company-order-2' title='{% trans "Create Purchase Order" %}'> <button type='button' class='btn btn-outline-primary' id='company-order-2' title='{% trans "Create Purchase Order" %}'>
<span class='fas fa-shopping-cart'/> <span class='fas fa-shopping-cart'/>

View File

@ -26,10 +26,10 @@
{% endblock heading %} {% endblock heading %}
{% block actions %} {% block actions %}
{% if user.is_staff and perms.company.change_company %}
{% url 'admin:company_manufacturerpart_change' part.pk as url %} {% admin_url user 'company.manufacturerpart' part.pk as url %}
{% include "admin_button.html" with url=url %} {% include "admin_button.html" with url=url %}
{% endif %}
{% if roles.purchase_order.change %} {% if roles.purchase_order.change %}
{% if roles.purchase_order.add and part.part.purchaseable %} {% if roles.purchase_order.add and part.part.purchaseable %}
<button type='button' class='btn btn-outline-secondary' id='order-part' title='{% trans "Order part" %}'> <button type='button' class='btn btn-outline-secondary' id='order-part' title='{% trans "Order part" %}'>

View File

@ -26,10 +26,9 @@
{% endblock heading %} {% endblock heading %}
{% block actions %} {% block actions %}
{% if user.is_staff and perms.company.change_company %} {% admin_url user "company.supplierpart" part.pk as url %}
{% url 'admin:company_supplierpart_change' part.pk as url %}
{% include "admin_button.html" with url=url %} {% include "admin_button.html" with url=url %}
{% endif %}
{% if barcodes %} {% if barcodes %}
<!-- Barcode actions menu --> <!-- Barcode actions menu -->
<div class='btn-group' role='group'> <div class='btn-group' role='group'>

View File

@ -58,6 +58,14 @@ database:
# Use the environment variable INVENTREE_DEBUG # Use the environment variable INVENTREE_DEBUG
debug: True debug: True
# Set to False to disable the admin interface (default = True)
# Or, use the environment variable INVENTREE_ADMIN_ENABLED
#admin_enabled: True
# Set the admin URL (default is 'admin')
# Or, use the environment variable INVENTREE_ADMIN_URL
#admin_url: 'admin'
# Set enabled frontends # Set enabled frontends
# Use the environment variable INVENTREE_CLASSIC_FRONTEND # Use the environment variable INVENTREE_CLASSIC_FRONTEND
# classic_frontend: True # classic_frontend: True

View File

@ -19,10 +19,10 @@
{% endblock heading %} {% endblock heading %}
{% block actions %} {% block actions %}
{% if user.is_staff and roles.purchase_order.change %}
{% url 'admin:order_purchaseorder_change' order.pk as url %} {% admin_url user "order.purchaseorder" order.pk as url %}
{% include "admin_button.html" with url=url %} {% include "admin_button.html" with url=url %}
{% endif %}
{% if barcodes %} {% if barcodes %}
<!-- Barcode actions menu --> <!-- Barcode actions menu -->
<div class='btn-group' role='group'> <div class='btn-group' role='group'>

View File

@ -29,10 +29,9 @@ src="{% static 'img/blank_image.png' %}"
{% endblock heading %} {% endblock heading %}
{% block actions %} {% block actions %}
{% if user.is_staff and roles.return_order.change %} {% admin_url user "order.returnorder" order.pk as url %}
{% url 'admin:order_returnorder_change' order.pk as url %}
{% include "admin_button.html" with url=url %} {% include "admin_button.html" with url=url %}
{% endif %}
{% if barcodes %} {% if barcodes %}
<!-- Barcode actions menu --> <!-- Barcode actions menu -->
<div class='btn-group' role='group'> <div class='btn-group' role='group'>

View File

@ -29,10 +29,9 @@ src="{% static 'img/blank_image.png' %}"
{% endblock heading %} {% endblock heading %}
{% block actions %} {% block actions %}
{% if user.is_staff and roles.sales_order.change %} {% admin_url user "order.salesorder" order.pk as url %}
{% url 'admin:order_salesorder_change' order.pk as url %}
{% include "admin_button.html" with url=url %} {% include "admin_button.html" with url=url %}
{% endif %}
{% if barcodes %} {% if barcodes %}
<!-- Barcode actions menu --> <!-- Barcode actions menu -->
<div class='btn-group' role='group'> <div class='btn-group' role='group'>

View File

@ -25,10 +25,11 @@
{% endblock heading %} {% endblock heading %}
{% block actions %} {% block actions %}
{% if category and user.is_staff and roles.part_category.change %} {% if category %}
{% url 'admin:part_partcategory_change' category.pk as url %} {% admin_url user "part.partcategory" category.pk as url %}
{% include "admin_button.html" with url=url %} {% include "admin_button.html" with url=url %}
{% endif %} {% endif %}
{% settings_value "STOCKTAKE_ENABLE" as stocktake_enable %} {% settings_value "STOCKTAKE_ENABLE" as stocktake_enable %}
{% if stocktake_enable and roles.stocktake.add %} {% if stocktake_enable and roles.stocktake.add %}
<button type='button' class='btn btn-outline-secondary' id='category-stocktake' title='{% trans "Perform stocktake for this part category" %}'> <button type='button' class='btn btn-outline-secondary' id='category-stocktake' title='{% trans "Perform stocktake for this part category" %}'>

View File

@ -18,10 +18,8 @@
{% block actions %} {% block actions %}
<!-- Admin View --> <!-- Admin View -->
{% if user.is_staff and roles.part.change %} {% admin_url user "part.part" part.pk as url %}
{% url 'admin:part_part_change' part.pk as url %}
{% include "admin_button.html" with url=url %} {% include "admin_button.html" with url=url %}
{% endif %}
{% if starred_directly %} {% if starred_directly %}
<button type='button' class='btn btn-outline-secondary' id='toggle-starred' title='{% trans "You are subscribed to notifications for this part" %}'> <button type='button' class='btn btn-outline-secondary' id='toggle-starred' title='{% trans "You are subscribed to notifications for this part" %}'>

View File

@ -8,7 +8,7 @@ from datetime import date, datetime
from django import template from django import template
from django.conf import settings as djangosettings from django.conf import settings as djangosettings
from django.templatetags.static import StaticNode from django.templatetags.static import StaticNode
from django.urls import reverse from django.urls import NoReverseMatch, reverse
from django.utils.html import format_html from django.utils.html import format_html
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
@ -626,3 +626,52 @@ else: # pragma: no cover
token.contents = ' '.join(bits) token.contents = ' '.join(bits)
return I18nStaticNode.handle_token(parser, token) return I18nStaticNode.handle_token(parser, token)
@register.simple_tag()
def admin_index(user):
"""Return a URL for the admin interface"""
if not djangosettings.INVENTREE_ADMIN_ENABLED:
return ''
if not user.is_staff:
return ''
return reverse('admin:index')
@register.simple_tag()
def admin_url(user, table, pk):
"""Generate a link to the admin site for the given model instance.
- If the admin site is disabled, an empty URL is returned
- If the user is not a staff user, an empty URL is returned
- If the user does not have the correct permission, an empty URL is returned
"""
app, model = table.strip().split('.')
from django.urls import reverse
if not djangosettings.INVENTREE_ADMIN_ENABLED:
return ""
if not user.is_staff:
return ""
# Check the user has the correct permission
perm_string = f"{app}.change_{model}"
if not user.has_perm(perm_string):
return ''
# Fallback URL
url = reverse(f"admin:{app}_{model}_changelist")
if pk:
try:
url = reverse(f'admin:{app}_{model}_change', args=(pk,))
except NoReverseMatch:
pass
return url

View File

@ -17,7 +17,7 @@ from django.apps import apps
from django.conf import settings from django.conf import settings
from django.contrib import admin from django.contrib import admin
from django.db.utils import IntegrityError, OperationalError, ProgrammingError from django.db.utils import IntegrityError, OperationalError, ProgrammingError
from django.urls import clear_url_caches, re_path from django.urls import clear_url_caches, path
from django.utils.text import slugify from django.utils.text import slugify
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
@ -620,13 +620,17 @@ class PluginsRegistry:
app_name = getattr(url, 'app_name', None) app_name = getattr(url, 'app_name', None)
admin_url = settings.INVENTREE_ADMIN_URL
if app_name == 'admin': if app_name == 'admin':
urlpatterns[index] = re_path(r'^admin/', admin.site.urls, name='inventree-admin') urlpatterns[index] = path(f'{admin_url}/', admin.site.urls, name='inventree-admin')
if app_name == 'plugin': if app_name == 'plugin':
urlpatterns[index] = get_plugin_urls() urlpatterns[index] = get_plugin_urls()
# Refresh the URL cache # Refresh the URL cache
clear_url_caches() clear_url_caches()
# endregion # endregion
# region plugin registry hash calculations # region plugin registry hash calculations

View File

@ -25,10 +25,9 @@
{% block actions %} {% block actions %}
{% if user.is_staff and roles.stock.change %} {% admin_url user "stock.stockitem" item.pk as url %}
{% url 'admin:stock_stockitem_change' item.pk as url %}
{% include "admin_button.html" with url=url %} {% include "admin_button.html" with url=url %}
{% endif %}
{% mixin_available "locate" as locate_available %} {% mixin_available "locate" as locate_available %}
{% if plugins_enabled and locate_available %} {% if plugins_enabled and locate_available %}
<button id='locate-item-button' title='{% trans "Locate stock item" %}' class='btn btn-outline-secondary' typy='button'> <button id='locate-item-button' title='{% trans "Locate stock item" %}' class='btn btn-outline-secondary' typy='button'>

View File

@ -28,10 +28,11 @@
{% block actions %} {% block actions %}
<!-- Admin view --> <!-- Admin view -->
{% if location and user.is_staff and roles.stock_location.change %} {% if location %}
{% url 'admin:stock_stocklocation_change' location.pk as url %} {% admin_url user "stock.stocklocation" location.pk as url %}
{% include "admin_button.html" with url=url %} {% include "admin_button.html" with url=url %}
{% endif %} {% endif %}
{% settings_value "STOCKTAKE_ENABLE" as stocktake_enable %} {% settings_value "STOCKTAKE_ENABLE" as stocktake_enable %}
{% if stocktake_enable and roles.stocktake.add %} {% if stocktake_enable and roles.stocktake.add %}
<button type='button' class='btn btn-outline-secondary' id='location-stocktake' title='{% trans "Perform stocktake for this stock location" %}'> <button type='button' class='btn btn-outline-secondary' id='location-stocktake' title='{% trans "Perform stocktake for this stock location" %}'>

View File

@ -35,7 +35,7 @@
<h4>{% trans "Plugins" %}</h4> <h4>{% trans "Plugins" %}</h4>
{% include "spacer.html" %} {% include "spacer.html" %}
<div class='btn-group' role='group'> <div class='btn-group' role='group'>
{% url 'admin:plugin_pluginconfig_changelist' as url %} {% admin_url user "plugin.pluginconfig" None as url %}
{% include "admin_button.html" with url=url %} {% include "admin_button.html" with url=url %}
{% if plug %} {% if plug %}
<button class="btn btn-success" id="install-plugin" title="{% trans 'Install Plugin' %}"><span class='fas fa-plus-circle'></span> {% trans "Install Plugin" %}</button> <button class="btn btn-success" id="install-plugin" title="{% trans 'Install Plugin' %}"><span class='fas fa-plus-circle'></span> {% trans "Install Plugin" %}</button>

View File

@ -3,7 +3,7 @@
{% inventree_customize 'hide_admin_link' as hidden %} {% inventree_customize 'hide_admin_link' as hidden %}
{% if not hidden and user.is_staff %} {% if url and not hidden and user.is_staff %}
<a href='{{ url }}'> <a href='{{ url }}'>
<button id='admin-button' href='{{ url }}' title='{% trans "View in administration panel" %}' type='button' class='btn btn-primary admin-button'> <button id='admin-button' href='{{ url }}' title='{% trans "View in administration panel" %}' type='button' class='btn btn-primary admin-button'>
<span class='fas fa-user-shield'></span> <span class='fas fa-user-shield'></span>

View File

@ -139,7 +139,10 @@
<ul class='dropdown-menu dropdown-menu-end inventree-navbar-menu'> <ul class='dropdown-menu dropdown-menu-end inventree-navbar-menu'>
{% if user.is_authenticated %} {% if user.is_authenticated %}
{% if user.is_staff and not hide_admin_link %} {% if user.is_staff and not hide_admin_link %}
<li><a class='dropdown-item' href="{% url 'admin:index' %}"><span class="fas fa-user-shield"></span> {% trans "Admin" %}</a></li> {% admin_index user as admin_idx %}
{% if admin_idx %}
<li><a class='dropdown-item' href="{{ admin_idx }}"><span class="fas fa-user-shield"></span> {% trans "Admin" %}</a></li>
{% endif %}
{% endif %} {% endif %}
<li><a class='dropdown-item' href="{% url 'settings' %}"><span class="fas fa-cog"></span> {% trans "Settings" %}</a></li> <li><a class='dropdown-item' href="{% url 'settings' %}"><span class="fas fa-cog"></span> {% trans "Settings" %}</a></li>
<li><a class='dropdown-item' href="{% url 'account_logout' %}"><span class="fas fa-sign-out-alt"></span> {% trans "Logout" %}</a></li> <li><a class='dropdown-item' href="{% url 'account_logout' %}"><span class="fas fa-sign-out-alt"></span> {% trans "Logout" %}</a></li>

View File

@ -1,10 +1,11 @@
{% extends "registration/logged_out.html" %} {% extends "registration/logged_out.html" %}
{% load i18n %} {% load i18n %}
{% load inventree_extras %}
{% block content %} {% block content %}
<p>{% translate "You were logged out successfully." %}</p> <p>{% translate "You were logged out successfully." %}</p>
<p><a href="{% url 'admin:index' %}">{% translate 'Log in again' %}</a></p> <p><a href="{% url 'index' %}">{% translate 'Log in again' %}</a></p>
{% endblock content %} {% endblock content %}

View File

@ -55,10 +55,22 @@ The following basic options are available:
| INVENTREE_LOG_LEVEL | log_level | Set level of logging to terminal | WARNING | | INVENTREE_LOG_LEVEL | log_level | Set level of logging to terminal | WARNING |
| INVENTREE_DB_LOGGING | db_logging | Enable logging of database messages | False | | INVENTREE_DB_LOGGING | db_logging | Enable logging of database messages | False |
| INVENTREE_TIMEZONE | timezone | Server timezone | UTC | | INVENTREE_TIMEZONE | timezone | Server timezone | UTC |
| ADMIN_URL | admin_url | URL for accessing [admin interface](../settings/admin.md) | admin | | INVENTREE_ADMIN_ENABLED | admin_enabled | Enable the [django administrator interface](https://docs.djangoproject.com/en/4.2/ref/contrib/admin/) | True |
| INVENTREE_ADMIN_URL | admin_url | URL for accessing [admin interface](../settings/admin.md) | admin |
| INVENTREE_LANGUAGE | language | Default language | en-us | | INVENTREE_LANGUAGE | language | Default language | en-us |
| INVENTREE_BASE_URL | base_url | Server base URL | *Not specified* | | INVENTREE_BASE_URL | base_url | Server base URL | *Not specified* |
### Admin Site
Django provides a powerful [administrator interface](https://docs.djangoproject.com/en/4.2/ref/contrib/admin/) which can be used to manage the InvenTree database. This interface is enabled by default, but can be disabled by setting `INVENTREE_ADMIN_ENABLED` to `False`.
#### Custom Admin URL
By default, the admin interface is available at the `/admin/` URL. This can be changed by setting the `INVENTREE_ADMIN_URL` environment variable.
!!! warning "Security"
Changing the admin URL is a simple way to improve security, but it is not a substitute for proper security practices.
### Base URL Configuration ### Base URL Configuration
The base URL of the InvenTree site is required for constructing absolute URLs in a number of circumstances. To construct a URL, the InvenTree iterates through the following options in decreasing order of importance: The base URL of the InvenTree site is required for constructing absolute URLs in a number of circumstances. To construct a URL, the InvenTree iterates through the following options in decreasing order of importance: