Add support for blocking the changing of password for specify users.

Signed-off-by: Roberto Rosario <roberto.rosario.gonzalez@gmail.com>
This commit is contained in:
Roberto Rosario
2018-08-26 06:56:48 -04:00
parent c3312d964f
commit 69b80aff1d
12 changed files with 168 additions and 14 deletions

View File

@@ -118,6 +118,7 @@
- Interpret ALLOWED_HOSTS as YAML.
- Don't show the document types of an index instance.
- Add the tag created and tag edited events.
- Add support for blocking the changing of password for specify users.
3.0.3 (2018-08-17)
==================

View File

@@ -7,8 +7,11 @@ from navigation import Link
from .icons import icon_logout, icon_password_change
def has_usable_password(context):
return context['request'].user.has_usable_password
def has_usable_password_and_can_change_password(context):
return (
context['request'].user.has_usable_password and
not context['request'].user.user_options.block_password_change
)
link_logout = Link(
@@ -16,6 +19,7 @@ link_logout = Link(
text=_('Logout'), view='authentication:logout_view'
)
link_password_change = Link(
condition=has_usable_password, icon_class=icon_password_change,
text=_('Change password'), view='authentication:password_change_view'
condition=has_usable_password_and_can_change_password,
icon_class=icon_password_change, text=_('Change password'),
view='authentication:password_change_view'
)

View File

@@ -2,13 +2,14 @@ from __future__ import absolute_import, unicode_literals
from django.conf import settings
from django.contrib import messages
from django.contrib.auth import REDIRECT_FIELD_NAME
from django.contrib.auth import REDIRECT_FIELD_NAME, get_user_model
from django.contrib.auth.views import (
login, password_change, password_reset, password_reset_confirm,
password_reset_complete, password_reset_done
)
from django.http import HttpResponseRedirect
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import redirect, resolve_url
from django.template.loader import render_to_string
from django.urls import reverse
from django.utils.http import is_safe_url
from django.utils.translation import ugettext_lazy as _
@@ -77,6 +78,14 @@ def password_change_view(request):
"""
extra_context = {'title': _('Current user password change')}
if request.user.user_options.block_password_change:
messages.error(
request, _(
'Changing the password is not allowed for this account.'
)
)
return HttpResponseRedirect(reverse(settings.HOME_VIEW))
return password_change(
request, extra_context=extra_context,
template_name='appearance/generic_form.html',

View File

@@ -2,6 +2,7 @@ from __future__ import unicode_literals
from django.apps import apps
from django.contrib.auth import get_user_model
from django.db.models.signals import post_save
from django.utils.translation import ugettext_lazy as _
from acls import ModelPermission
@@ -14,12 +15,13 @@ from metadata import MetadataLookup
from navigation import SourceColumn
from rest_api.fields import DynamicSerializerField
from .handlers import handler_initialize_new_user_options
from .links import (
link_group_add, link_group_delete, link_group_edit, link_group_list,
link_group_members, link_group_setup, link_user_add, link_user_delete,
link_user_edit, link_user_groups, link_user_list,
link_user_multiple_delete, link_user_multiple_set_password,
link_user_set_password, link_user_setup
link_user_set_options, link_user_set_password, link_user_setup
)
from .permissions import (
permission_group_delete, permission_group_edit,
@@ -116,12 +118,13 @@ class UserManagementApp(MayanAppConfig):
sources=(Group,)
)
menu_object.bind_links(
links=(link_acl_list, link_group_delete,), position=99, sources=(Group,)
links=(link_acl_list, link_group_delete,), position=99,
sources=(Group,)
)
menu_object.bind_links(
links=(
link_user_edit, link_user_set_password, link_user_groups,
link_acl_list, link_user_delete
link_user_set_options, link_acl_list, link_user_delete
), sources=(User,)
)
menu_secondary.bind_links(
@@ -140,5 +143,10 @@ class UserManagementApp(MayanAppConfig):
)
menu_setup.bind_links(links=(link_user_setup, link_group_setup))
post_save.connect(
dispatch_uid='user_management_handler_initialize_new_user_options',
receiver=handler_initialize_new_user_options,
sender=User
)
registry.register(Group)
registry.register(User)

View File

@@ -6,5 +6,7 @@ from django.contrib.auth import get_user_model
class UserForm(forms.ModelForm):
class Meta:
fields = (
'username', 'first_name', 'last_name', 'email', 'is_active',
)
model = get_user_model()
fields = ('username', 'first_name', 'last_name', 'email', 'is_active',)

View File

@@ -0,0 +1,12 @@
from __future__ import unicode_literals
from django.apps import apps
def handler_initialize_new_user_options(sender, instance, **kwargs):
UserOptions = apps.get_model(
app_label='user_management', model_name='UserOptions'
)
if kwargs['created']:
UserOptions.objects.create(user=instance)

View File

@@ -63,6 +63,10 @@ link_user_multiple_set_password = Link(
permissions=(permission_user_edit,), text=_('Set password'),
view='user_management:user_multiple_set_password'
)
link_user_set_options = Link(
args='object.id', permissions=(permission_user_edit,),
text=_('User options'), view='user_management:user_options',
)
link_user_set_password = Link(
args='object.id', permissions=(permission_user_edit,),
text=_('Set password'), view='user_management:user_set_password',

View File

@@ -0,0 +1,55 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.11 on 2018-08-26 10:01
from __future__ import unicode_literals
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
def add_user_options_to_existing_users(apps, schema_editor):
User = apps.get_model(settings.AUTH_USER_MODEL)
UserOptions = apps.get_model(
app_label='user_management', model_name='UserOptions'
)
for user in User.objects.all():
UserOptions.objects.create(user=user)
def remove_user_options_from_existing_users(apps, schema_editor):
User = apps.get_model(settings.AUTH_USER_MODEL)
UserOptions = apps.get_model(
app_label='user_management', model_name='UserOptions'
)
for user in User.objects.all():
UserOptions.objects.filter(user=user).delete()
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='UserOptions',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('block_password_change', models.BooleanField(default=False, verbose_name='Forbid this user from changing their password.')),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='user_options', to=settings.AUTH_USER_MODEL, verbose_name='User')),
],
options={
'verbose_name': 'User settings',
'verbose_name_plural': 'Users settings',
},
),
migrations.RunPython(
code=add_user_options_to_existing_users,
reverse_code=remove_user_options_from_existing_users
),
]

View File

@@ -0,0 +1,24 @@
from __future__ import unicode_literals
from django.conf import settings
from django.db import models
from django.utils.translation import ugettext_lazy as _
class UserOptions(models.Model):
user = models.OneToOneField(
on_delete=models.CASCADE, related_name='user_options',
to=settings.AUTH_USER_MODEL, unique=True, verbose_name=_('User')
)
block_password_change = models.BooleanField(
default=False,
verbose_name=_('Forbid this user from changing their password.')
)
class Meta:
verbose_name = _('User settings')
verbose_name_plural = _('Users settings')
def natural_key(self):
return self.user.natural_key()
natural_key.dependencies = [settings.AUTH_USER_MODEL]

View File

@@ -9,7 +9,7 @@ from .api_views import (
from .views import (
GroupCreateView, GroupDeleteView, GroupEditView, GroupListView,
GroupMembersView, UserCreateView, UserDeleteView, UserEditView,
UserGroupsView, UserListView, UserSetPasswordView
UserGroupsView, UserListView, UserOptionsEditView, UserSetPasswordView
)
urlpatterns = [
@@ -51,6 +51,11 @@ urlpatterns = [
r'^user/(?P<pk>\d+)/groups/$', UserGroupsView.as_view(),
name='user_groups'
),
url(
r'^user/(?P<pk>\d+)/options/$',
UserOptionsEditView.as_view(),
name='user_options'
),
]
api_urls = [

View File

@@ -133,8 +133,10 @@ class UserCreateView(SingleObjectCreateView):
class UserDeleteView(MultipleObjectConfirmActionView):
model = get_user_model()
object_permission = permission_user_delete
queryset = get_user_model().objects.filter(
is_superuser=False, is_staff=False
)
success_message = _('User delete request performed on %(count)d user')
success_message_plural = _(
'User delete request performed on %(count)d users'
@@ -216,8 +218,11 @@ class UserGroupsView(AssignRemoveView):
'title': _('Groups of user: %s') % self.get_object()
}
def get_object(self):
return get_object_or_404(get_user_model(), pk=self.kwargs['pk'])
return get_object_or_404(
get_user_model().objects.filter(
is_superuser=False, is_staff=False
), pk=self.kwargs['pk']
)
def left_list(self):
return AssignRemoveView.generate_choices(
@@ -248,6 +253,31 @@ class UserListView(SingleObjectListView):
).exclude(is_staff=True).order_by('last_name', 'first_name')
class UserOptionsEditView(SingleObjectEditView):
fields = ('block_password_change',)
object_permission = permission_user_edit
def get_extra_context(self):
return {
'title': _(
'Edit options for user: %s'
) % self.get_user()
}
def get_object(self, queryset=None):
return self.get_user().user_options
def get_post_action_redirect(self):
return reverse('user_management:user_list')
def get_user(self):
return get_object_or_404(
get_user_model().objects.filter(
is_superuser=False, is_staff=False
), pk=self.kwargs['pk']
)
class UserSetPasswordView(MultipleObjectFormActionView):
form_class = SetPasswordForm
model = get_user_model()