diff --git a/netbox/users/forms/model_forms.py b/netbox/users/forms/model_forms.py
index 1a91a239f..f7f026bd6 100644
--- a/netbox/users/forms/model_forms.py
+++ b/netbox/users/forms/model_forms.py
@@ -1,10 +1,11 @@
from django import forms
from django.conf import settings
from django.contrib.auth import get_user_model
-from django.contrib.auth.forms import AuthenticationForm, PasswordChangeForm as DjangoPasswordChangeForm
+from django.contrib.auth.forms import AuthenticationForm, PasswordChangeForm as DjangoPasswordChangeForm, SetPasswordForm as DjangoPasswordSetForm
from django.contrib.auth.models import Group
from django.contrib.contenttypes.models import ContentType
from django.contrib.postgres.forms import SimpleArrayField
+from django.urls import reverse
from django.utils.html import mark_safe
from django.utils.translation import gettext as _
@@ -24,6 +25,7 @@ __all__ = (
'LoginForm',
'ObjectPermissionForm',
'PasswordChangeForm',
+ 'PasswordSetForm',
'TokenForm',
'UserConfigForm',
'UserForm',
@@ -38,6 +40,10 @@ class PasswordChangeForm(BootstrapMixin, DjangoPasswordChangeForm):
pass
+class PasswordSetForm(BootstrapMixin, DjangoPasswordSetForm):
+ pass
+
+
class UserConfigFormMetaclass(forms.models.ModelFormMetaclass):
def __new__(mcs, name, bases, attrs):
@@ -192,9 +198,9 @@ class UserForm(BootstrapMixin, forms.ModelForm):
self.fields['password'].help_text = _(
"Raw passwords are not stored, so there is no way to see this "
"user’s password, but you can change the password using "
- 'this form.'
- )
-
+ 'this form.'
+ ).format(url=reverse('users:change_user_password', args=[self.instance.pk]))
+ print(self.fields['password'].help_text)
del self.fields['confirm_password']
def save(self, *args, **kwargs):
diff --git a/netbox/users/urls.py b/netbox/users/urls.py
index 6b13748ba..031c38c44 100644
--- a/netbox/users/urls.py
+++ b/netbox/users/urls.py
@@ -17,6 +17,7 @@ urlpatterns = [
path('users/edit/', views.NetBoxUserBulkEditView.as_view(), name='netboxuser_bulk_edit'),
path('users/delete/', views.NetBoxUserBulkDeleteView.as_view(), name='netboxuser_bulk_delete'),
path('users//', include(get_model_urls('users', 'netboxuser'))),
+ path('users/password//', views.NetBoxUserChangePasswordView.as_view(), name='change_user_password'),
# Groups
path('groups/', views.NetBoxGroupListView.as_view(), name='netboxgroup_list'),
diff --git a/netbox/users/views.py b/netbox/users/views.py
index 99feeef20..a316985e9 100644
--- a/netbox/users/views.py
+++ b/netbox/users/views.py
@@ -6,6 +6,7 @@ from django.contrib.auth import login as auth_login, logout as auth_logout, upda
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.models import Group, User, update_last_login
from django.contrib.auth.signals import user_logged_in
+from django.core.exceptions import ImproperlyConfigured
from django.db.models import Count
from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404, redirect, render, resolve_url
@@ -397,6 +398,52 @@ class NetBoxUserBulkDeleteView(generic.BulkDeleteView):
return get_permission_for_model(User, 'delete')
+class NetBoxUserChangePasswordView(LoginRequiredMixin, View):
+ template_name = 'users/passworduser.html'
+ queryset = User.objects.all()
+
+ def get_object(self, **kwargs):
+ """
+ Return an object for editing. If no keyword arguments have been specified, this will be a new instance.
+ """
+ if not kwargs:
+ # We're creating a new object
+ return self.queryset.model()
+ return get_object_or_404(self.queryset, **kwargs)
+
+ def get(self, request, *args, **kwargs):
+ obj = self.get_object(**kwargs)
+
+ # LDAP users cannot change their password here
+ if getattr(obj, 'ldap_username', None):
+ messages.warning(request, "LDAP-authenticated user credentials cannot be changed within NetBox.")
+ return redirect('users:netboxuser_list')
+
+ form = forms.PasswordSetForm(user=obj)
+
+ return render(request, self.template_name, {
+ 'form': form,
+ 'active_tab': 'password',
+ 'object': obj,
+ })
+
+ def post(self, request, *args, **kwargs):
+ obj = self.get_object(**kwargs)
+
+ form = forms.PasswordSetForm(user=obj, data=request.POST)
+ if form.is_valid():
+ form.save()
+ update_session_auth_hash(request, form.user)
+ messages.success(request, "The password has been changed successfully.")
+ return redirect('users:netboxuser_list')
+
+ return render(request, self.template_name, {
+ 'form': form,
+ 'active_tab': 'password',
+ 'object': obj,
+ })
+
+
#
# Groups
#