Employ standard naming for Token view, form, table classes

This commit is contained in:
Jeremy Stretch 2023-07-25 10:53:26 -04:00
parent d0c3c3f93b
commit 5064bd4574
13 changed files with 52 additions and 139 deletions

View File

@ -469,6 +469,7 @@ EXEMPT_EXCLUDE_MODELS = (
('auth', 'group'), ('auth', 'group'),
('auth', 'user'), ('auth', 'user'),
('users', 'objectpermission'), ('users', 'objectpermission'),
('users', 'token'),
) )
# All URLs starting with a string listed here are exempt from login enforcement # All URLs starting with a string listed here are exempt from login enforcement

View File

@ -34,7 +34,7 @@
</a> </a>
</li> </li>
<li> <li>
<a class="dropdown-item" href="{% url 'users:token_list' %}"> <a class="dropdown-item" href="{% url 'users:usertoken_list' %}">
<i class="mdi mdi-key"></i> API Tokens <i class="mdi mdi-key"></i> API Tokens
</a> </a>
</li> </li>

View File

@ -3,6 +3,10 @@
{% load helpers %} {% load helpers %}
{% load plugins %} {% load plugins %}
{% block breadcrumbs %}
<li class="breadcrumb-item"><a href="{% url 'users:usertoken_list' %}">Tokens</a></li>
{% endblock breadcrumbs %}
{% block content %} {% block content %}
<div class="row"> <div class="row">
<div class="col col-md-12"> <div class="col col-md-12">
@ -50,7 +54,7 @@
</div> </div>
</div> </div>
<div class="col col-md-12 text-center"> <div class="col col-md-12 text-center">
<a href="{% url 'users:token_add' %}" class="btn btn-outline-primary">Add Another</a> <a href="{% url 'users:usertoken_add' %}" class="btn btn-outline-primary">Add Another</a>
<a href="{{ return_url }}" class="btn btn-outline-danger">Cancel</a> <a href="{{ return_url }}" class="btn btn-outline-danger">Cancel</a>
</div> </div>
</div> </div>

View File

@ -7,7 +7,7 @@
{% block content %} {% block content %}
<div class="row"> <div class="row">
<div class="col col-md-12 text-end"> <div class="col col-md-12 text-end">
<a href="{% url 'users:token_add' %}" class="btn btn-sm btn-primary my-3"> <a href="{% url 'users:usertoken_add' %}" class="btn btn-sm btn-primary my-3">
<span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add a Token <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add a Token
</a> </a>
</div> </div>

View File

@ -18,7 +18,7 @@
</li> </li>
{% endif %} {% endif %}
<li role="presentation" class="nav-item"> <li role="presentation" class="nav-item">
<a class="nav-link{% if active_tab == 'api-tokens' %} active{% endif %}" href="{% url 'users:token_list' %}">{% trans "API Tokens" %}</a> <a class="nav-link{% if active_tab == 'api-tokens' %} active{% endif %}" href="{% url 'users:usertoken_list' %}">{% trans "API Tokens" %}</a>
</li> </li>
</ul> </ul>
{% endblock %} {% endblock %}

View File

@ -12,7 +12,6 @@ __all__ = (
'ObjectPermissionFilterSet', 'ObjectPermissionFilterSet',
'TokenFilterSet', 'TokenFilterSet',
'UserFilterSet', 'UserFilterSet',
'UserTokenFilterSet',
) )
@ -112,54 +111,6 @@ class TokenFilterSet(BaseFilterSet):
) )
class UserTokenFilterSet(BaseFilterSet):
q = django_filters.CharFilter(
method='search',
label=_('Search'),
)
user_id = django_filters.ModelMultipleChoiceFilter(
field_name='user',
queryset=get_user_model().objects.all(),
label=_('User'),
)
user = django_filters.ModelMultipleChoiceFilter(
field_name='user__username',
queryset=get_user_model().objects.all(),
to_field_name='username',
label=_('User (name)'),
)
created = django_filters.DateTimeFilter()
created__gte = django_filters.DateTimeFilter(
field_name='created',
lookup_expr='gte'
)
created__lte = django_filters.DateTimeFilter(
field_name='created',
lookup_expr='lte'
)
expires = django_filters.DateTimeFilter()
expires__gte = django_filters.DateTimeFilter(
field_name='expires',
lookup_expr='gte'
)
expires__lte = django_filters.DateTimeFilter(
field_name='expires',
lookup_expr='lte'
)
class Meta:
model = Token
fields = ['id', 'key', 'write_enabled', 'description']
def search(self, queryset, name, value):
if not value.strip():
return queryset
return queryset.filter(
Q(user__username__icontains=value) |
Q(description__icontains=value)
)
class ObjectPermissionFilterSet(BaseFilterSet): class ObjectPermissionFilterSet(BaseFilterSet):
q = django_filters.CharFilter( q = django_filters.CharFilter(
method='search', method='search',

View File

@ -8,7 +8,7 @@ from utilities.forms.widgets import BulkEditNullBooleanSelect
__all__ = ( __all__ = (
'ObjectPermissionBulkEditForm', 'ObjectPermissionBulkEditForm',
'UserBulkEditForm', 'UserBulkEditForm',
'UserTokenBulkEditForm', 'TokenBulkEditForm',
) )
@ -73,7 +73,7 @@ class ObjectPermissionBulkEditForm(BootstrapMixin, forms.Form):
nullable_fields = ('description',) nullable_fields = ('description',)
class UserTokenBulkEditForm(BulkEditForm): class TokenBulkEditForm(BulkEditForm):
pk = forms.ModelMultipleChoiceField( pk = forms.ModelMultipleChoiceField(
queryset=Token.objects.all(), queryset=Token.objects.all(),
widget=forms.MultipleHiddenInput widget=forms.MultipleHiddenInput

View File

@ -7,7 +7,7 @@ from utilities.forms import CSVModelForm
__all__ = ( __all__ = (
'GroupImportForm', 'GroupImportForm',
'UserImportForm', 'UserImportForm',
'UserTokenImportForm', 'TokenImportForm',
) )
@ -36,7 +36,7 @@ class UserImportForm(CSVModelForm):
return super().save(*args, **kwargs) return super().save(*args, **kwargs)
class UserTokenImportForm(CSVModelForm): class TokenImportForm(CSVModelForm):
key = forms.CharField( key = forms.CharField(
label=_('Key'), required=False, help_text=_("If no key is provided, one will be generated automatically.") label=_('Key'), required=False, help_text=_("If no key is provided, one will be generated automatically.")
) )

View File

@ -16,7 +16,7 @@ __all__ = (
'GroupFilterForm', 'GroupFilterForm',
'ObjectPermissionFilterForm', 'ObjectPermissionFilterForm',
'UserFilterForm', 'UserFilterForm',
'UserTokenFilterForm', 'TokenFilterForm',
) )
@ -116,7 +116,7 @@ class ObjectPermissionFilterForm(NetBoxModelFilterSetForm):
) )
class UserTokenFilterForm(SavedFiltersMixin, FilterForm): class TokenFilterForm(SavedFiltersMixin, FilterForm):
model = Token model = Token
fieldsets = ( fieldsets = (
(None, ('q', 'filter_id',)), (None, ('q', 'filter_id',)),

View File

@ -20,12 +20,13 @@ from utilities.permissions import qs_filter_from_constraints
from utilities.utils import flatten_dict from utilities.utils import flatten_dict
__all__ = ( __all__ = (
'UserTokenForm',
'GroupForm', 'GroupForm',
'ObjectPermissionForm', 'ObjectPermissionForm',
'TokenForm', 'TokenForm',
'UserConfigForm', 'UserConfigForm',
'UserForm', 'UserForm',
'UserTokenForm', 'TokenForm',
) )
@ -108,9 +109,11 @@ class UserConfigForm(BootstrapMixin, forms.ModelForm, metaclass=UserConfigFormMe
] ]
class TokenForm(BootstrapMixin, forms.ModelForm): class UserTokenForm(BootstrapMixin, forms.ModelForm):
key = forms.CharField( key = forms.CharField(
label=_('Key'), required=False, help_text=_("If no key is provided, one will be generated automatically.") label=_('Key'),
required=False,
help_text=_("If no key is provided, one will be generated automatically.")
) )
allowed_ips = SimpleArrayField( allowed_ips = SimpleArrayField(
base_field=IPNetworkFormField(validators=[prefix_validator]), base_field=IPNetworkFormField(validators=[prefix_validator]),
@ -139,25 +142,13 @@ class TokenForm(BootstrapMixin, forms.ModelForm):
del self.fields['key'] del self.fields['key']
class UserTokenForm(BootstrapMixin, forms.ModelForm): class TokenForm(UserTokenForm):
key = forms.CharField(
label=_('Key'), required=False, help_text=_("If no key is provided, one will be generated automatically.")
)
user = forms.ModelChoiceField( user = forms.ModelChoiceField(
queryset=get_user_model().objects.order_by( queryset=get_user_model().objects.order_by(
'username' 'username'
), ),
required=False required=False
) )
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: class Meta:
model = Token model = Token
@ -168,13 +159,6 @@ class UserTokenForm(BootstrapMixin, forms.ModelForm):
'expires': DateTimePicker(), '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']
class UserForm(BootstrapMixin, forms.ModelForm): class UserForm(BootstrapMixin, forms.ModelForm):
password = forms.CharField( password = forms.CharField(

View File

@ -8,7 +8,6 @@ __all__ = (
'ObjectPermissionTable', 'ObjectPermissionTable',
'TokenTable', 'TokenTable',
'UserTable', 'UserTable',
'UserTokenTable',
) )
@ -32,31 +31,6 @@ class TokenActionsColumn(columns.ActionsColumn):
class TokenTable(NetBoxTable): class TokenTable(NetBoxTable):
key = columns.TemplateColumn(
template_code=TOKEN
)
write_enabled = columns.BooleanColumn(
verbose_name='Write'
)
created = columns.DateColumn()
expired = columns.DateColumn()
last_used = columns.DateTimeColumn()
allowed_ips = columns.TemplateColumn(
template_code=ALLOWED_IPS
)
actions = TokenActionsColumn(
actions=('edit', 'delete'),
extra_buttons=COPY_BUTTON
)
class Meta(NetBoxTable.Meta):
model = Token
fields = (
'pk', 'description', 'key', 'write_enabled', 'created', 'expires', 'last_used', 'allowed_ips',
)
class UserTokenTable(NetBoxTable):
key = columns.TemplateColumn( key = columns.TemplateColumn(
verbose_name='Key', verbose_name='Key',
template_code=TOKEN, template_code=TOKEN,

View File

@ -11,16 +11,17 @@ urlpatterns = [
path('bookmarks/', views.BookmarkListView.as_view(), name='bookmarks'), path('bookmarks/', views.BookmarkListView.as_view(), name='bookmarks'),
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'),
path('api-tokens/', views.TokenListView.as_view(), name='token_list'), path('api-tokens/', views.UserTokenListView.as_view(), name='usertoken_list'),
path('api-tokens/add/', views.TokenEditView.as_view(), name='token_add'), path('api-tokens/add/', views.UserTokenEditView.as_view(), name='usertoken_add'),
path('api-tokens/<int:pk>/', include(get_model_urls('users', 'token'))), path('api-tokens/<int:pk>/edit/', views.UserTokenEditView.as_view(), name='usertoken_edit'),
path('api-tokens/<int:pk>/delete/', views.UserTokenDeleteView.as_view(), name='usertoken_delete'),
# Tokens # Tokens
path('tokens/', views.UserTokenListView.as_view(), name='token_list'), path('tokens/', views.TokenListView.as_view(), name='token_list'),
path('tokens/add/', views.UserTokenEditView.as_view(), name='token_add'), path('tokens/add/', views.TokenEditView.as_view(), name='token_add'),
path('tokens/import/', views.UserTokenBulkImportView.as_view(), name='token_import'), path('tokens/import/', views.TokenBulkImportView.as_view(), name='token_import'),
path('tokens/edit/', views.UserTokenBulkEditView.as_view(), name='token_bulk_edit'), path('tokens/edit/', views.TokenBulkEditView.as_view(), name='token_bulk_edit'),
path('tokens/delete/', views.UserTokenBulkDeleteView.as_view(), name='token_bulk_delete'), path('tokens/delete/', views.TokenBulkDeleteView.as_view(), name='token_bulk_delete'),
path('tokens/<int:pk>/', include(get_model_urls('users', 'token'))), path('tokens/<int:pk>/', include(get_model_urls('users', 'token'))),
# Users # Users

View File

@ -252,7 +252,7 @@ class BookmarkListView(LoginRequiredMixin, generic.ObjectListView):
# API tokens # API tokens
# #
class TokenListView(LoginRequiredMixin, View): class UserTokenListView(LoginRequiredMixin, View):
def get(self, request): def get(self, request):
@ -267,8 +267,7 @@ class TokenListView(LoginRequiredMixin, View):
}) })
@register_model_view(Token, 'edit') class UserTokenEditView(LoginRequiredMixin, View):
class TokenEditView(LoginRequiredMixin, View):
def get(self, request, pk=None): def get(self, request, pk=None):
@ -277,7 +276,7 @@ class TokenEditView(LoginRequiredMixin, View):
else: else:
token = Token(user=request.user) token = Token(user=request.user)
form = forms.TokenForm(instance=token) form = forms.UserTokenForm(instance=token)
return render(request, 'generic/object_edit.html', { return render(request, 'generic/object_edit.html', {
'object': token, 'object': token,
@ -289,10 +288,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 = forms.TokenForm(request.POST, instance=token) form = forms.UserTokenForm(request.POST, instance=token)
else: else:
token = Token(user=request.user) token = Token(user=request.user)
form = forms.TokenForm(request.POST) form = forms.UserTokenForm(request.POST)
if form.is_valid(): if form.is_valid():
@ -322,8 +321,7 @@ class TokenEditView(LoginRequiredMixin, View):
}) })
@register_model_view(Token, 'delete') class UserTokenDeleteView(LoginRequiredMixin, View):
class TokenDeleteView(LoginRequiredMixin, View):
def get(self, request, pk): def get(self, request, pk):
@ -356,18 +354,18 @@ class TokenDeleteView(LoginRequiredMixin, View):
# #
# User Token # Tokens
# #
class UserTokenListView(generic.ObjectListView): class TokenListView(generic.ObjectListView):
queryset = Token.objects.all() queryset = Token.objects.all()
filterset = filtersets.UserTokenFilterSet filterset = filtersets.TokenFilterSet
filterset_form = forms.UserTokenFilterForm filterset_form = forms.TokenFilterForm
table = tables.UserTokenTable table = tables.TokenTable
@register_model_view(Token) @register_model_view(Token)
class UserTokenView(generic.ObjectView): class TokenView(generic.ObjectView):
queryset = Token.objects.all() queryset = Token.objects.all()
def get_extra_context(self, request, instance): def get_extra_context(self, request, instance):
@ -375,30 +373,30 @@ class UserTokenView(generic.ObjectView):
@register_model_view(Token, 'edit') @register_model_view(Token, 'edit')
class UserTokenEditView(generic.ObjectEditView): class TokenEditView(generic.ObjectEditView):
queryset = Token.objects.all() queryset = Token.objects.all()
form = forms.UserTokenForm form = forms.TokenForm
@register_model_view(Token, 'delete') @register_model_view(Token, 'delete')
class UserTokenDeleteView(generic.ObjectDeleteView): class TokenDeleteView(generic.ObjectDeleteView):
queryset = Token.objects.all() queryset = Token.objects.all()
class UserTokenBulkImportView(generic.BulkImportView): class TokenBulkImportView(generic.BulkImportView):
queryset = Token.objects.all() queryset = Token.objects.all()
model_form = forms.UserTokenImportForm model_form = forms.TokenImportForm
class UserTokenBulkEditView(generic.BulkEditView): class TokenBulkEditView(generic.BulkEditView):
queryset = Token.objects.all() queryset = Token.objects.all()
table = tables.TokenTable table = tables.TokenTable
form = forms.UserTokenBulkEditForm form = forms.TokenBulkEditForm
class UserTokenBulkDeleteView(generic.BulkDeleteView): class TokenBulkDeleteView(generic.BulkDeleteView):
queryset = Token.objects.all() queryset = Token.objects.all()
table = tables.UserTokenTable table = tables.TokenTable
# #