OpenSlides/openslides/users/views.py
Norman Jäckel 3a19218bd5 Refactored parts of users app.
Refactored user creation and update via REST API. Used new serializer.
Cleaned up management commands, signals and imports. Moved code from 'api.py' to 'models.py'.
Changed usage of group 'Registered'. Now the users don't have to be members to gain its permissions. Used customized auth backend for this.
Added and changed some tests.
2015-02-14 02:29:53 +01:00

481 lines
16 KiB
Python

from django.contrib import messages
from django.contrib.auth.forms import PasswordChangeForm
from django.contrib.auth.hashers import make_password
from django.contrib.auth.views import login as django_login
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext as _, ugettext_lazy, activate
from openslides.config.api import config
from openslides.utils.rest_api import ModelViewSet
from openslides.utils.utils import delete_default_permissions, html_strong
from openslides.utils.views import (
CreateView, CSVImportView, DeleteView, DetailView, FormView, ListView,
PDFView, PermissionMixin, QuestionView, RedirectView, SingleObjectMixin,
UpdateView, LoginMixin)
from openslides.utils.exceptions import OpenSlidesError
from .api import get_protected_perm
from .csv_import import import_users
from .forms import (GroupForm, UserCreateForm, UserMultipleCreateForm,
UsersettingsForm, UserUpdateForm)
from .models import Group, User
from .pdf import users_to_pdf, users_passwords_to_pdf
from .serializers import GroupSerializer, UserCreateUpdateSerializer, UserFullSerializer, UserShortSerializer
class UserListView(ListView):
"""
Show all users.
"""
required_permission = 'users.can_see_extra_data'
context_object_name = 'users'
def get_queryset(self):
query = User.objects
if config['users_sort_users_by_first_name']:
query = query.order_by('first_name')
else:
query = query.order_by('last_name')
return query.all()
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
all_users = User.objects.count()
# context vars
context.update({
'allusers': all_users,
'request_user': self.request.user})
return context
class UserDetailView(DetailView, PermissionMixin):
"""
Classed based view to show a specific user in the interface.
"""
required_permission = 'users.can_see_extra_data'
model = User
context_object_name = 'shown_user'
class UserCreateView(CreateView):
"""
Create a new user.
"""
required_permission = 'users.can_manage'
model = User
context_object_name = 'edit_user'
form_class = UserCreateForm
success_url_name = 'user_list'
url_name_args = []
def manipulate_object(self, form):
self.object.username = User.objects.generate_username(
form.cleaned_data['first_name'], form.cleaned_data['last_name'])
if not self.object.default_password:
self.object.default_password = User.objects.generate_password()
self.object.set_password(self.object.default_password)
class UserMultipleCreateView(FormView):
"""
View to create multiple users at once using a big text field.
Sets the password with md5. It is the same password as in the
default_password field in cleartext. A stronger password hasher is used,
when the password is changed by the user.
"""
required_permission = 'users.can_manage'
template_name = 'users/user_form_multiple.html'
form_class = UserMultipleCreateForm
success_url_name = 'user_list'
def form_valid(self, form):
# TODO: Use bulk_create
for number, line in enumerate(form.cleaned_data['users_block'].splitlines()):
names_list = line.split()
first_name = ' '.join(names_list[:-1])
last_name = names_list[-1]
username = User.objects.generate_username(first_name, last_name)
default_password = User.objects.generate_password()
User.objects.create(
username=username,
first_name=first_name,
last_name=last_name,
default_password=default_password,
password=make_password(default_password, '', 'md5'))
messages.success(self.request, _('%(number)d users successfully created.') % {'number': number + 1})
return super(UserMultipleCreateView, self).form_valid(form)
class UserUpdateView(UpdateView):
"""
Update an existing users.
"""
required_permission = 'users.can_manage'
model = User
context_object_name = 'edit_user'
form_class = UserUpdateForm
success_url_name = 'user_list'
url_name_args = []
def get_form_kwargs(self, *args, **kwargs):
form_kwargs = super(UserUpdateView, self).get_form_kwargs(*args, **kwargs)
form_kwargs.update({'request': self.request})
return form_kwargs
class UserDeleteView(DeleteView):
"""
Delete a user.
"""
required_permission = 'users.can_manage'
model = User
success_url_name = 'user_list'
url_name_args = []
def pre_redirect(self, request, *args, **kwargs):
if self.get_object() == self.request.user:
messages.error(request, _("You can not delete yourself."))
else:
super().pre_redirect(request, *args, **kwargs)
def pre_post_redirect(self, request, *args, **kwargs):
if self.get_object() == self.request.user:
messages.error(self.request, _("You can not delete yourself."))
else:
super().pre_post_redirect(request, *args, **kwargs)
class SetUserStatusView(SingleObjectMixin, RedirectView):
"""
Activate or deactivate an user.
"""
required_permission = 'users.can_manage'
allow_ajax = True
url_name = 'user_list'
url_name_args = []
model = User
def pre_redirect(self, request, *args, **kwargs):
action = kwargs['action']
if action == 'activate':
self.get_object().is_active = True
elif action == 'deactivate':
if self.get_object().user == self.request.user:
messages.error(request, _("You can not deactivate yourself."))
else:
self.get_object().is_active = False
self.get_object().save()
return super(SetUserStatusView, self).pre_redirect(request, *args, **kwargs)
def get_ajax_context(self, **kwargs):
context = super(SetUserStatusView, self).get_ajax_context(**kwargs)
context['active'] = self.get_object().is_active
return context
class UsersListPDF(PDFView):
"""
Generate the userliste as PDF.
"""
required_permission = 'users.can_see_extra_data'
filename = ugettext_lazy("user-list")
document_title = ugettext_lazy('List of Users')
def append_to_pdf(self, pdf):
"""
Append PDF objects.
"""
users_to_pdf(pdf)
class UsersPasswordsPDF(PDFView):
"""
Generate the access data welcome paper for all users as PDF.
"""
required_permission = 'users.can_manage'
filename = ugettext_lazy("User-access-data")
top_space = 0
def build_document(self, pdf_document, story):
pdf_document.build(story)
def append_to_pdf(self, pdf):
"""
Append PDF objects.
"""
users_passwords_to_pdf(pdf)
class UserCSVImportView(CSVImportView):
"""
Import users via CSV.
"""
import_function = staticmethod(import_users)
required_permission = 'users.can_manage'
success_url_name = 'user_list'
template_name = 'users/user_form_csv_import.html'
class ResetPasswordView(SingleObjectMixin, QuestionView):
"""
Set the Passwort for a user to his default password.
"""
required_permission = 'users.can_manage'
model = User
allow_ajax = True
question_message = ugettext_lazy('Do you really want to reset the password?')
def get_redirect_url(self, **kwargs):
return self.get_object().get_absolute_url('update')
def on_clicked_yes(self):
self.get_object().reset_password()
self.get_object().save()
def get_final_message(self):
return _('The Password for %s was successfully reset.') % html_strong(self.get_object())
class UserViewSet(ModelViewSet):
"""
API endpoint to list, retrieve, create, update and delete users.
"""
queryset = User.objects.all()
def check_permissions(self, request):
"""
Calls self.permission_denied() if the requesting user has not the
permission to see users and in case of create, update or destroy
requests the permission to see extra user data and to manage users.
"""
if (not request.user.has_perm('users.can_see_name') or
(self.action in ('create', 'update', 'destroy') and not
(request.user.has_perm('users.can_manage') and
request.user.has_perm('users.can_see_extra_data')))):
self.permission_denied(request)
def get_serializer_class(self):
"""
Returns different serializer classes with respect to action and user's
permissions.
"""
if self.action in ('create', 'update'):
serializer_class = UserCreateUpdateSerializer
elif self.request.user.has_perm('users.can_see_extra_data'):
serializer_class = UserFullSerializer
else:
serializer_class = UserShortSerializer
return serializer_class
class GroupViewSet(ModelViewSet):
"""
API endpoint to list, retrieve, create, update and delete groups.
"""
queryset = Group.objects.all()
serializer_class = GroupSerializer
def check_permissions(self, request):
"""
Calls self.permission_denied() if the requesting user has not the
permission to see users and in case of create, update or destroy
requests the permission to see extra user data and to manage users.
"""
if (not request.user.has_perm('users.can_see_name') or
(self.action in ('create', 'update', 'destroy') and not
(request.user.has_perm('users.can_manage') and
request.user.has_perm('users.can_see_extra_data')))):
self.permission_denied(request)
class GroupListView(ListView):
"""
Overview over all groups.
"""
required_permission = 'users.can_manage'
template_name = 'users/group_list.html'
context_object_name = 'groups'
model = Group
class GroupDetailView(DetailView, PermissionMixin):
"""
Classed based view to show a specific group in the interface.
"""
required_permission = 'users.can_manage'
model = Group
template_name = 'users/group_detail.html'
context_object_name = 'group'
def get_context_data(self, *args, **kwargs):
context = super(GroupDetailView, self).get_context_data(*args, **kwargs)
query = User.objects
if config['users_sort_users_by_first_name']:
query = query.order_by('first_name')
else:
query = query.order_by('last_name')
context['group_members'] = query.filter(groups__in=[context['group']])
return context
class GroupCreateView(CreateView):
"""
Create a new group.
"""
required_permission = 'users.can_manage'
template_name = 'users/group_form.html'
context_object_name = 'group'
model = Group
form_class = GroupForm
success_url_name = 'group_list'
url_name_args = []
def get(self, request, *args, **kwargs):
delete_default_permissions()
return super(GroupCreateView, self).get(request, *args, **kwargs)
def get_apply_url(self):
"""
Returns the url when the user clicks on 'apply'.
"""
return self.get_url('group_update', args=[self.object.pk])
class GroupUpdateView(UpdateView):
"""
Update an existing group.
"""
required_permission = 'users.can_manage'
template_name = 'users/group_form.html'
model = Group
context_object_name = 'group'
form_class = GroupForm
url_name_args = []
success_url_name = 'group_list'
def get(self, request, *args, **kwargs):
delete_default_permissions()
return super().get(request, *args, **kwargs)
def get_form_kwargs(self, *args, **kwargs):
form_kwargs = super().get_form_kwargs(*args, **kwargs)
form_kwargs.update({'request': self.request})
return form_kwargs
def get_apply_url(self):
"""
Returns the url when the user clicks on 'apply'.
"""
return self.get_url('group_update', args=[self.object.pk])
class GroupDeleteView(DeleteView):
"""
Delete a group.
"""
required_permission = 'users.can_manage'
model = Group
success_url_name = 'group_list'
question_url_name = 'group_detail'
url_name_args = []
def pre_redirect(self, request, *args, **kwargs):
if not self.is_protected_from_deleting():
super().pre_redirect(request, *args, **kwargs)
def pre_post_redirect(self, request, *args, **kwargs):
if not self.is_protected_from_deleting():
super().pre_post_redirect(request, *args, **kwargs)
def is_protected_from_deleting(self):
"""
Checks whether the group is protected.
"""
if self.get_object().pk in [1, 2]:
messages.error(self.request, _('You can not delete this group.'))
return True
if (not self.request.user.is_superuser and
get_protected_perm() in self.get_object().permissions.all() and
not Group.objects.exclude(pk=self.get_object().pk).filter(
permissions__in=[get_protected_perm()],
user__pk=self.request.user.pk).exists()):
messages.error(
self.request,
_('You can not delete the last group containing the permission '
'to manage users you are in.'))
return True
return False
def get_url_name_args(self):
try:
answer = self.get_answer()
except OpenSlidesError:
answer = 'no'
if self.request.method == 'POST' and answer != 'no':
return []
else:
return [self.object.pk]
def login(request):
extra_content = {}
try:
admin = User.objects.get(pk=1)
if admin.check_password(admin.default_password):
user_data = {
'user': html_strong(admin.username),
'password': html_strong(admin.default_password)}
extra_content['first_time_message'] = _(
"Installation was successfully! Use %(user)s "
"(password: %(password)s) for first login.<br>"
"<strong>Important:</strong> Please change the password after "
"first login! Otherwise this message still appears for "
"everyone and could be a security risk.") % user_data
extra_content['next'] = reverse('password_change')
except User.DoesNotExist:
pass
return django_login(request, template_name='users/login.html', extra_context=extra_content)
class UserSettingsView(LoginMixin, UpdateView):
model = User
form_class = UsersettingsForm
success_url_name = 'user_settings'
url_name_args = []
template_name = 'users/settings.html'
def get_initial(self):
initial = super().get_initial()
initial['language'] = self.request.session.get('django_language', self.request.LANGUAGE_CODE)
return initial
def form_valid(self, form):
self.request.LANGUAGE_CODE = self.request.session['django_language'] = form.cleaned_data['language']
activate(self.request.LANGUAGE_CODE)
return super().form_valid(form)
def get_object(self):
return self.request.user
class UserPasswordSettingsView(LoginMixin, FormView):
form_class = PasswordChangeForm
success_url_name = 'core_dashboard'
template_name = 'users/password_change.html'
def form_valid(self, form):
form.save()
messages.success(self.request, _('Password successfully changed.'))
return super().form_valid(form)
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs