mirror of
https://github.com/netbox-community/netbox.git
synced 2025-08-25 16:56:10 -06:00
125890 first working user list
This commit is contained in:
parent
4208b79514
commit
773a4f9896
@ -344,6 +344,19 @@ OPERATIONS_MENU = Menu(
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ADMIN_MENU = Menu(
|
||||||
|
label=_('Admin'),
|
||||||
|
icon_class='mdi mdi-account-multiple',
|
||||||
|
groups=(
|
||||||
|
MenuGroup(
|
||||||
|
label=_('Users'),
|
||||||
|
items=(
|
||||||
|
get_model_item('users', 'user', _('Users'), actions=['add']),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
MENUS = [
|
MENUS = [
|
||||||
ORGANIZATION_MENU,
|
ORGANIZATION_MENU,
|
||||||
@ -358,6 +371,7 @@ MENUS = [
|
|||||||
PROVISIONING_MENU,
|
PROVISIONING_MENU,
|
||||||
CUSTOMIZATION_MENU,
|
CUSTOMIZATION_MENU,
|
||||||
OPERATIONS_MENU,
|
OPERATIONS_MENU,
|
||||||
|
ADMIN_MENU,
|
||||||
]
|
]
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
import django_filters
|
import django_filters
|
||||||
|
from django.conf import settings
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
from django.contrib.auth.models import Group, User
|
from django.contrib.auth.models import Group, User
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from netbox.filtersets import BaseFilterSet
|
from netbox.filtersets import BaseFilterSet
|
||||||
from users.models import ObjectPermission, Token
|
from users.models import ObjectPermission, Token, NetBoxUser
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'GroupFilterSet',
|
'GroupFilterSet',
|
||||||
@ -47,7 +49,7 @@ class UserFilterSet(BaseFilterSet):
|
|||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = User
|
model = NetBoxUser
|
||||||
fields = ['id', 'username', 'first_name', 'last_name', 'email', 'is_staff', 'is_active']
|
fields = ['id', 'username', 'first_name', 'last_name', 'email', 'is_staff', 'is_active']
|
||||||
|
|
||||||
def search(self, queryset, name, value):
|
def search(self, queryset, name, value):
|
||||||
|
@ -1,130 +0,0 @@
|
|||||||
from django import forms
|
|
||||||
from django.conf import settings
|
|
||||||
from django.contrib.auth.forms import AuthenticationForm, PasswordChangeForm as DjangoPasswordChangeForm
|
|
||||||
from django.contrib.postgres.forms import SimpleArrayField
|
|
||||||
from django.utils.html import mark_safe
|
|
||||||
from django.utils.translation import gettext as _
|
|
||||||
|
|
||||||
from ipam.formfields import IPNetworkFormField
|
|
||||||
from ipam.validators import prefix_validator
|
|
||||||
from netbox.preferences import PREFERENCES
|
|
||||||
from utilities.forms import BootstrapMixin
|
|
||||||
from utilities.forms.widgets import DateTimePicker
|
|
||||||
from utilities.utils import flatten_dict
|
|
||||||
from .models import Token, UserConfig
|
|
||||||
|
|
||||||
|
|
||||||
class LoginForm(BootstrapMixin, AuthenticationForm):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class PasswordChangeForm(BootstrapMixin, DjangoPasswordChangeForm):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class UserConfigFormMetaclass(forms.models.ModelFormMetaclass):
|
|
||||||
|
|
||||||
def __new__(mcs, name, bases, attrs):
|
|
||||||
|
|
||||||
# Emulate a declared field for each supported user preference
|
|
||||||
preference_fields = {}
|
|
||||||
for field_name, preference in PREFERENCES.items():
|
|
||||||
description = f'{preference.description}<br />' if preference.description else ''
|
|
||||||
help_text = f'{description}<code>{field_name}</code>'
|
|
||||||
field_kwargs = {
|
|
||||||
'label': preference.label,
|
|
||||||
'choices': preference.choices,
|
|
||||||
'help_text': mark_safe(help_text),
|
|
||||||
'coerce': preference.coerce,
|
|
||||||
'required': False,
|
|
||||||
'widget': forms.Select,
|
|
||||||
}
|
|
||||||
preference_fields[field_name] = forms.TypedChoiceField(**field_kwargs)
|
|
||||||
attrs.update(preference_fields)
|
|
||||||
|
|
||||||
return super().__new__(mcs, name, bases, attrs)
|
|
||||||
|
|
||||||
|
|
||||||
class UserConfigForm(BootstrapMixin, forms.ModelForm, metaclass=UserConfigFormMetaclass):
|
|
||||||
fieldsets = (
|
|
||||||
('User Interface', (
|
|
||||||
'pagination.per_page',
|
|
||||||
'pagination.placement',
|
|
||||||
'ui.colormode',
|
|
||||||
)),
|
|
||||||
('Miscellaneous', (
|
|
||||||
'data_format',
|
|
||||||
)),
|
|
||||||
)
|
|
||||||
# List of clearable preferences
|
|
||||||
pk = forms.MultipleChoiceField(
|
|
||||||
choices=[],
|
|
||||||
required=False
|
|
||||||
)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = UserConfig
|
|
||||||
fields = ()
|
|
||||||
|
|
||||||
def __init__(self, *args, instance=None, **kwargs):
|
|
||||||
|
|
||||||
# Get initial data from UserConfig instance
|
|
||||||
initial_data = flatten_dict(instance.data)
|
|
||||||
kwargs['initial'] = initial_data
|
|
||||||
|
|
||||||
super().__init__(*args, instance=instance, **kwargs)
|
|
||||||
|
|
||||||
# Compile clearable preference choices
|
|
||||||
self.fields['pk'].choices = (
|
|
||||||
(f'tables.{table_name}', '') for table_name in instance.data.get('tables', [])
|
|
||||||
)
|
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
|
||||||
|
|
||||||
# Set UserConfig data
|
|
||||||
for pref_name, value in self.cleaned_data.items():
|
|
||||||
if pref_name == 'pk':
|
|
||||||
continue
|
|
||||||
self.instance.set(pref_name, value, commit=False)
|
|
||||||
|
|
||||||
# Clear selected preferences
|
|
||||||
for preference in self.cleaned_data['pk']:
|
|
||||||
self.instance.clear(preference)
|
|
||||||
|
|
||||||
return super().save(*args, **kwargs)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def plugin_fields(self):
|
|
||||||
return [
|
|
||||||
name for name in self.fields.keys() if name.startswith('plugins.')
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class TokenForm(BootstrapMixin, forms.ModelForm):
|
|
||||||
key = forms.CharField(
|
|
||||||
required=False,
|
|
||||||
help_text=_("If no key is provided, one will be generated automatically.")
|
|
||||||
)
|
|
||||||
allowed_ips = SimpleArrayField(
|
|
||||||
base_field=IPNetworkFormField(validators=[prefix_validator]),
|
|
||||||
required=False,
|
|
||||||
label=_('Allowed IPs'),
|
|
||||||
help_text=_('Allowed IPv4/IPv6 networks from where the token can be used. Leave blank for no restrictions. '
|
|
||||||
'Example: <code>10.1.1.0/24,192.168.10.16/32,2001:db8:1::/64</code>'),
|
|
||||||
)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = Token
|
|
||||||
fields = [
|
|
||||||
'key', 'write_enabled', 'expires', 'description', 'allowed_ips',
|
|
||||||
]
|
|
||||||
widgets = {
|
|
||||||
'expires': DateTimePicker(),
|
|
||||||
}
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
# Omit the key field if token retrieval is not permitted
|
|
||||||
if self.instance.pk and not settings.ALLOW_TOKEN_RETRIEVAL:
|
|
||||||
del self.fields['key']
|
|
28
netbox/users/migrations/0004_netboxuser.py
Normal file
28
netbox/users/migrations/0004_netboxuser.py
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
# Generated by Django 4.1.9 on 2023-06-02 16:55
|
||||||
|
|
||||||
|
import django.contrib.auth.models
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
('auth', '0012_alter_user_first_name_max_length'),
|
||||||
|
('users', '0003_token_allowed_ips_last_used'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='NetBoxUser',
|
||||||
|
fields=[],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'User',
|
||||||
|
'proxy': True,
|
||||||
|
'indexes': [],
|
||||||
|
'constraints': [],
|
||||||
|
},
|
||||||
|
bases=('auth.user',),
|
||||||
|
managers=[
|
||||||
|
('objects', django.contrib.auth.models.UserManager()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
@ -20,6 +20,7 @@ from utilities.utils import flatten_dict
|
|||||||
from .constants import *
|
from .constants import *
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
|
'NetBoxUser',
|
||||||
'ObjectPermission',
|
'ObjectPermission',
|
||||||
'Token',
|
'Token',
|
||||||
'UserConfig',
|
'UserConfig',
|
||||||
@ -30,6 +31,7 @@ __all__ = (
|
|||||||
# Proxy models for admin
|
# Proxy models for admin
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
class AdminGroup(Group):
|
class AdminGroup(Group):
|
||||||
"""
|
"""
|
||||||
Proxy contrib.auth.models.Group for the admin UI
|
Proxy contrib.auth.models.Group for the admin UI
|
||||||
@ -48,10 +50,19 @@ class AdminUser(User):
|
|||||||
proxy = True
|
proxy = True
|
||||||
|
|
||||||
|
|
||||||
|
class NetBoxUser(User):
|
||||||
|
"""
|
||||||
|
Proxy contrib.auth.models.User for the UI
|
||||||
|
"""
|
||||||
|
class Meta:
|
||||||
|
verbose_name = 'User'
|
||||||
|
proxy = True
|
||||||
|
|
||||||
#
|
#
|
||||||
# User preferences
|
# User preferences
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
class UserConfig(models.Model):
|
class UserConfig(models.Model):
|
||||||
"""
|
"""
|
||||||
This model stores arbitrary user-specific preferences in a JSON data structure.
|
This model stores arbitrary user-specific preferences in a JSON data structure.
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
|
import django_tables2 as tables
|
||||||
from .models import Token
|
from .models import Token
|
||||||
from netbox.tables import NetBoxTable, columns
|
from netbox.tables import NetBoxTable, columns
|
||||||
|
from users.models import NetBoxUser
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'TokenTable',
|
'TokenTable',
|
||||||
|
'UserTable',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -50,3 +53,17 @@ class TokenTable(NetBoxTable):
|
|||||||
fields = (
|
fields = (
|
||||||
'pk', 'description', 'key', 'write_enabled', 'created', 'expires', 'last_used', 'allowed_ips',
|
'pk', 'description', 'key', 'write_enabled', 'created', 'expires', 'last_used', 'allowed_ips',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class UserTable(NetBoxTable):
|
||||||
|
username = tables.Column()
|
||||||
|
actions = columns.ActionsColumn(
|
||||||
|
actions=('edit', 'delete'),
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta(NetBoxTable.Meta):
|
||||||
|
model = NetBoxUser
|
||||||
|
fields = (
|
||||||
|
'pk', 'id', 'username', 'email', 'first_name', 'last_name'
|
||||||
|
)
|
||||||
|
default_columns = ('pk', 'username', 'email', 'first_name', 'last_name')
|
||||||
|
@ -11,6 +11,14 @@ urlpatterns = [
|
|||||||
path('preferences/', views.UserConfigView.as_view(), name='preferences'),
|
path('preferences/', views.UserConfigView.as_view(), name='preferences'),
|
||||||
path('password/', views.ChangePasswordView.as_view(), name='change_password'),
|
path('password/', views.ChangePasswordView.as_view(), name='change_password'),
|
||||||
|
|
||||||
|
# Users
|
||||||
|
path('users/', views.NetBoxUserListView.as_view(), name='user_list'),
|
||||||
|
path('users/add/', views.NetBoxUserEditView.as_view(), name='user_add'),
|
||||||
|
path('users/import/', views.NetBoxUserBulkImportView.as_view(), name='netboxuser_import'),
|
||||||
|
path('users/edit/', views.NetBoxUserBulkEditView.as_view(), name='netboxuser_bulk_edit'),
|
||||||
|
path('users/delete/', views.NetBoxUserBulkDeleteView.as_view(), name='netboxuser_bulk_delete'),
|
||||||
|
path('users/<int:pk>/', include(get_model_urls('users', 'netboxuser'))),
|
||||||
|
|
||||||
# API tokens
|
# API tokens
|
||||||
path('api-tokens/', views.TokenListView.as_view(), name='token_list'),
|
path('api-tokens/', views.TokenListView.as_view(), name='token_list'),
|
||||||
path('api-tokens/add/', views.TokenEditView.as_view(), name='token_add'),
|
path('api-tokens/add/', views.TokenEditView.as_view(), name='token_add'),
|
||||||
|
@ -2,7 +2,7 @@ import logging
|
|||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth import login as auth_login, logout as auth_logout, update_session_auth_hash
|
from django.contrib.auth import login as auth_login, logout as auth_logout, update_session_auth_hash, get_user_model
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
from django.contrib.auth.models import update_last_login
|
from django.contrib.auth.models import update_last_login
|
||||||
from django.contrib.auth.signals import user_logged_in
|
from django.contrib.auth.signals import user_logged_in
|
||||||
@ -19,11 +19,11 @@ from extras.models import ObjectChange
|
|||||||
from extras.tables import ObjectChangeTable
|
from extras.tables import ObjectChangeTable
|
||||||
from netbox.authentication import get_auth_backend_display, get_saml_idps
|
from netbox.authentication import get_auth_backend_display, get_saml_idps
|
||||||
from netbox.config import get_config
|
from netbox.config import get_config
|
||||||
|
from netbox.views import generic
|
||||||
from utilities.forms import ConfirmationForm
|
from utilities.forms import ConfirmationForm
|
||||||
from utilities.views import register_model_view
|
from utilities.views import register_model_view
|
||||||
from .forms import LoginForm, PasswordChangeForm, TokenForm, UserConfigForm
|
from . import filtersets, forms, tables
|
||||||
from .models import Token, UserConfig
|
from .models import Token, UserConfig, NetBoxUser
|
||||||
from .tables import TokenTable
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -69,7 +69,7 @@ class LoginView(View):
|
|||||||
return auth_backends
|
return auth_backends
|
||||||
|
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
form = LoginForm(request)
|
form = forms.LoginForm(request)
|
||||||
|
|
||||||
if request.user.is_authenticated:
|
if request.user.is_authenticated:
|
||||||
logger = logging.getLogger('netbox.auth.login')
|
logger = logging.getLogger('netbox.auth.login')
|
||||||
@ -82,7 +82,7 @@ class LoginView(View):
|
|||||||
|
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
logger = logging.getLogger('netbox.auth.login')
|
logger = logging.getLogger('netbox.auth.login')
|
||||||
form = LoginForm(request, data=request.POST)
|
form = forms.LoginForm(request, data=request.POST)
|
||||||
|
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
logger.debug("Login form validation was successful")
|
logger.debug("Login form validation was successful")
|
||||||
@ -175,7 +175,7 @@ class UserConfigView(LoginRequiredMixin, View):
|
|||||||
|
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
userconfig = request.user.config
|
userconfig = request.user.config
|
||||||
form = UserConfigForm(instance=userconfig)
|
form = forms.UserConfigForm(instance=userconfig)
|
||||||
|
|
||||||
return render(request, self.template_name, {
|
return render(request, self.template_name, {
|
||||||
'form': form,
|
'form': form,
|
||||||
@ -184,7 +184,7 @@ class UserConfigView(LoginRequiredMixin, View):
|
|||||||
|
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
userconfig = request.user.config
|
userconfig = request.user.config
|
||||||
form = UserConfigForm(request.POST, instance=userconfig)
|
form = forms.UserConfigForm(request.POST, instance=userconfig)
|
||||||
|
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
form.save()
|
form.save()
|
||||||
@ -207,7 +207,7 @@ class ChangePasswordView(LoginRequiredMixin, View):
|
|||||||
messages.warning(request, "LDAP-authenticated user credentials cannot be changed within NetBox.")
|
messages.warning(request, "LDAP-authenticated user credentials cannot be changed within NetBox.")
|
||||||
return redirect('users:profile')
|
return redirect('users:profile')
|
||||||
|
|
||||||
form = PasswordChangeForm(user=request.user)
|
form = forms.PasswordChangeForm(user=request.user)
|
||||||
|
|
||||||
return render(request, self.template_name, {
|
return render(request, self.template_name, {
|
||||||
'form': form,
|
'form': form,
|
||||||
@ -215,7 +215,7 @@ class ChangePasswordView(LoginRequiredMixin, View):
|
|||||||
})
|
})
|
||||||
|
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
form = PasswordChangeForm(user=request.user, data=request.POST)
|
form = forms.PasswordChangeForm(user=request.user, data=request.POST)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
form.save()
|
form.save()
|
||||||
update_session_auth_hash(request, form.user)
|
update_session_auth_hash(request, form.user)
|
||||||
@ -237,7 +237,7 @@ class TokenListView(LoginRequiredMixin, View):
|
|||||||
def get(self, request):
|
def get(self, request):
|
||||||
|
|
||||||
tokens = Token.objects.filter(user=request.user)
|
tokens = Token.objects.filter(user=request.user)
|
||||||
table = TokenTable(tokens)
|
table = tables.TokenTable(tokens)
|
||||||
table.configure(request)
|
table.configure(request)
|
||||||
|
|
||||||
return render(request, 'users/api_tokens.html', {
|
return render(request, 'users/api_tokens.html', {
|
||||||
@ -257,7 +257,7 @@ class TokenEditView(LoginRequiredMixin, View):
|
|||||||
else:
|
else:
|
||||||
token = Token(user=request.user)
|
token = Token(user=request.user)
|
||||||
|
|
||||||
form = TokenForm(instance=token)
|
form = forms.TokenForm(instance=token)
|
||||||
|
|
||||||
return render(request, 'generic/object_edit.html', {
|
return render(request, 'generic/object_edit.html', {
|
||||||
'object': token,
|
'object': token,
|
||||||
@ -269,10 +269,10 @@ class TokenEditView(LoginRequiredMixin, View):
|
|||||||
|
|
||||||
if pk:
|
if pk:
|
||||||
token = get_object_or_404(Token.objects.filter(user=request.user), pk=pk)
|
token = get_object_or_404(Token.objects.filter(user=request.user), pk=pk)
|
||||||
form = TokenForm(request.POST, instance=token)
|
form = forms.TokenForm(request.POST, instance=token)
|
||||||
else:
|
else:
|
||||||
token = Token(user=request.user)
|
token = Token(user=request.user)
|
||||||
form = TokenForm(request.POST)
|
form = forms.TokenForm(request.POST)
|
||||||
|
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
|
|
||||||
@ -333,3 +333,48 @@ class TokenDeleteView(LoginRequiredMixin, View):
|
|||||||
'form': form,
|
'form': form,
|
||||||
'return_url': reverse('users:token_list'),
|
'return_url': reverse('users:token_list'),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
#
|
||||||
|
# Users
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
class NetBoxUserListView(generic.ObjectListView):
|
||||||
|
queryset = NetBoxUser.objects.all()
|
||||||
|
filterset = filtersets.UserFilterSet
|
||||||
|
filterset_form = forms.UserFilterForm
|
||||||
|
table = tables.UserTable
|
||||||
|
|
||||||
|
|
||||||
|
@register_model_view(get_user_model())
|
||||||
|
class NetBoxUserView(generic.ObjectView):
|
||||||
|
queryset = get_user_model().objects.all()
|
||||||
|
|
||||||
|
|
||||||
|
@register_model_view(NetBoxUser, 'edit')
|
||||||
|
class NetBoxUserEditView(generic.ObjectEditView):
|
||||||
|
queryset = get_user_model().objects.all()
|
||||||
|
form = forms.UserForm
|
||||||
|
|
||||||
|
|
||||||
|
@register_model_view(NetBoxUser, 'delete')
|
||||||
|
class NetBoxUserDeleteView(generic.ObjectDeleteView):
|
||||||
|
queryset = get_user_model().objects.all()
|
||||||
|
|
||||||
|
|
||||||
|
class NetBoxUserBulkImportView(generic.BulkImportView):
|
||||||
|
queryset = get_user_model().objects.all()
|
||||||
|
model_form = forms.UserImportForm
|
||||||
|
|
||||||
|
|
||||||
|
class NetBoxUserBulkEditView(generic.BulkEditView):
|
||||||
|
queryset = get_user_model().objects.all()
|
||||||
|
filterset = filtersets.UserFilterSet
|
||||||
|
table = tables.UserTable
|
||||||
|
form = forms.UserBulkEditForm
|
||||||
|
|
||||||
|
|
||||||
|
class NetBoxUserBulkDeleteView(generic.BulkDeleteView):
|
||||||
|
queryset = get_user_model().objects.all()
|
||||||
|
filterset = filtersets.UserFilterSet
|
||||||
|
table = tables.UserTable
|
||||||
|
@ -5,6 +5,7 @@ from django.urls.exceptions import NoReverseMatch
|
|||||||
|
|
||||||
from netbox.registry import registry
|
from netbox.registry import registry
|
||||||
from .permissions import resolve_permission
|
from .permissions import resolve_permission
|
||||||
|
from .querysets import RestrictedQuerySet
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'ContentTypePermissionRequiredMixin',
|
'ContentTypePermissionRequiredMixin',
|
||||||
@ -93,7 +94,7 @@ class ObjectPermissionRequiredMixin(AccessMixin):
|
|||||||
'a base queryset'.format(self.__class__.__name__)
|
'a base queryset'.format(self.__class__.__name__)
|
||||||
)
|
)
|
||||||
|
|
||||||
if not self.has_permission():
|
if isinstance(self.queryset, RestrictedQuerySet) and not self.has_permission():
|
||||||
return self.handle_no_permission()
|
return self.handle_no_permission()
|
||||||
|
|
||||||
return super().dispatch(request, *args, **kwargs)
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
Loading…
Reference in New Issue
Block a user