2015-02-12 22:42:54 +01:00
|
|
|
from django.contrib.auth import login as auth_login
|
|
|
|
from django.contrib.auth import logout as auth_logout
|
2015-06-16 10:37:23 +02:00
|
|
|
from django.contrib.auth.forms import AuthenticationForm
|
2016-08-29 17:05:06 +02:00
|
|
|
from django.utils.encoding import force_text
|
2015-06-18 22:39:58 +02:00
|
|
|
from django.utils.translation import ugettext as _
|
2014-10-11 14:34:49 +02:00
|
|
|
|
2015-09-16 00:55:27 +02:00
|
|
|
from ..core.config import config
|
2017-01-15 13:33:54 +01:00
|
|
|
from ..utils.auth import anonymous_is_enabled
|
2017-01-14 13:02:26 +01:00
|
|
|
from ..utils.collection import CollectionElement
|
2015-11-06 15:44:27 +01:00
|
|
|
from ..utils.rest_api import (
|
|
|
|
ModelViewSet,
|
|
|
|
Response,
|
2016-01-25 22:35:23 +01:00
|
|
|
SimpleMetadata,
|
2015-11-06 15:44:27 +01:00
|
|
|
ValidationError,
|
|
|
|
detail_route,
|
|
|
|
status,
|
|
|
|
)
|
2016-10-01 14:26:28 +02:00
|
|
|
from ..utils.views import APIView
|
2016-12-17 09:30:20 +01:00
|
|
|
from .access_permissions import GroupAccessPermissions, UserAccessPermissions
|
2014-10-11 14:34:49 +02:00
|
|
|
from .models import Group, User
|
2016-09-18 16:00:31 +02:00
|
|
|
from .serializers import GroupSerializer
|
2011-07-31 10:46:29 +02:00
|
|
|
|
2012-07-07 15:26:00 +02:00
|
|
|
|
2015-07-01 23:18:48 +02:00
|
|
|
# Viewsets for the REST API
|
2012-08-10 19:49:46 +02:00
|
|
|
|
2015-02-12 18:48:14 +01:00
|
|
|
class UserViewSet(ModelViewSet):
|
2015-01-06 00:11:22 +01:00
|
|
|
"""
|
2015-07-01 23:18:48 +02:00
|
|
|
API endpoint for users.
|
|
|
|
|
2015-08-31 14:07:24 +02:00
|
|
|
There are the following views: metadata, list, retrieve, create,
|
|
|
|
partial_update, update, destroy and reset_password.
|
2015-01-06 00:11:22 +01:00
|
|
|
"""
|
2016-02-11 22:58:32 +01:00
|
|
|
access_permissions = UserAccessPermissions()
|
2015-01-06 00:11:22 +01:00
|
|
|
queryset = User.objects.all()
|
|
|
|
|
2015-07-01 23:18:48 +02:00
|
|
|
def check_view_permissions(self):
|
2015-01-06 00:11:22 +01:00
|
|
|
"""
|
2015-07-01 23:18:48 +02:00
|
|
|
Returns True if the user has required permissions.
|
2015-01-06 00:11:22 +01:00
|
|
|
"""
|
2016-09-17 22:26:23 +02:00
|
|
|
if self.action in ('list', 'retrieve'):
|
|
|
|
result = self.get_access_permissions().check_permissions(self.request.user)
|
|
|
|
elif self.action in ('metadata', 'update', 'partial_update'):
|
2015-07-01 23:18:48 +02:00
|
|
|
result = self.request.user.has_perm('users.can_see_name')
|
2016-01-10 10:52:44 +01:00
|
|
|
elif self.action in ('create', 'destroy', 'reset_password'):
|
2015-07-01 23:18:48 +02:00
|
|
|
result = (self.request.user.has_perm('users.can_see_name') and
|
|
|
|
self.request.user.has_perm('users.can_see_extra_data') and
|
|
|
|
self.request.user.has_perm('users.can_manage'))
|
|
|
|
else:
|
|
|
|
result = False
|
|
|
|
return result
|
2015-01-06 00:11:22 +01:00
|
|
|
|
2015-09-06 10:29:23 +02:00
|
|
|
def update(self, request, *args, **kwargs):
|
|
|
|
"""
|
|
|
|
Customized view endpoint to update an user.
|
|
|
|
|
|
|
|
Checks also whether the requesting user can update the user. He
|
|
|
|
needs at least the permissions 'users.can_see_name' (see
|
|
|
|
self.check_view_permissions()). Also it is evaluated whether he
|
|
|
|
wants to update himself or is manager.
|
|
|
|
"""
|
|
|
|
# Check manager perms
|
|
|
|
if (request.user.has_perm('users.can_see_extra_data') and
|
|
|
|
request.user.has_perm('users.can_manage')):
|
2016-01-09 11:59:34 +01:00
|
|
|
if request.data.get('is_active') is False and self.get_object() == request.user:
|
|
|
|
# A user can not deactivate himself.
|
|
|
|
raise ValidationError({'detail': _('You can not deactivate yourself.')})
|
2015-09-06 10:29:23 +02:00
|
|
|
else:
|
|
|
|
# Check permissions only to update yourself.
|
2016-08-31 16:53:02 +02:00
|
|
|
if str(request.user.pk) != self.kwargs['pk']:
|
2015-09-06 10:29:23 +02:00
|
|
|
self.permission_denied(request)
|
2016-08-31 16:53:02 +02:00
|
|
|
response = super().update(request, *args, **kwargs)
|
2015-09-06 10:29:23 +02:00
|
|
|
return response
|
|
|
|
|
2016-01-09 11:59:34 +01:00
|
|
|
def destroy(self, request, *args, **kwargs):
|
|
|
|
"""
|
|
|
|
Customized view endpoint to delete an user.
|
|
|
|
|
|
|
|
Ensures that no one can delete himself.
|
|
|
|
"""
|
|
|
|
instance = self.get_object()
|
|
|
|
if instance == self.request.user:
|
|
|
|
raise ValidationError({'detail': _('You can not delete yourself.')})
|
|
|
|
self.perform_destroy(instance)
|
|
|
|
return Response(status=status.HTTP_204_NO_CONTENT)
|
|
|
|
|
2015-06-18 22:39:58 +02:00
|
|
|
@detail_route(methods=['post'])
|
|
|
|
def reset_password(self, request, pk=None):
|
|
|
|
"""
|
2016-01-10 13:47:59 +01:00
|
|
|
View to reset the password using the requested password.
|
2015-06-18 22:39:58 +02:00
|
|
|
"""
|
|
|
|
user = self.get_object()
|
2016-08-25 11:40:37 +02:00
|
|
|
if isinstance(request.data.get('password'), str):
|
|
|
|
user.set_password(request.data.get('password'))
|
|
|
|
user.save()
|
|
|
|
return Response({'detail': _('Password successfully reset.')})
|
|
|
|
else:
|
|
|
|
raise ValidationError({'detail': 'Password has to be a string.'})
|
2015-06-18 22:39:58 +02:00
|
|
|
|
2015-01-06 00:11:22 +01:00
|
|
|
|
2016-01-25 22:35:23 +01:00
|
|
|
class GroupViewSetMetadata(SimpleMetadata):
|
|
|
|
"""
|
|
|
|
Customized metadata class for OPTIONS requests.
|
|
|
|
"""
|
|
|
|
def get_field_info(self, field):
|
|
|
|
"""
|
|
|
|
Customized method to change the display name of permission choices.
|
|
|
|
"""
|
|
|
|
field_info = super().get_field_info(field)
|
|
|
|
if field.field_name == 'permissions':
|
2016-08-29 17:05:06 +02:00
|
|
|
field_info['choices'] = [
|
|
|
|
{
|
|
|
|
'value': choice_value,
|
|
|
|
'display_name': force_text(choice_name, strings_only=True).split(' | ')[2]
|
|
|
|
}
|
|
|
|
for choice_value, choice_name in field.choices.items()
|
|
|
|
]
|
2016-01-25 22:35:23 +01:00
|
|
|
return field_info
|
|
|
|
|
|
|
|
|
2015-02-12 18:48:14 +01:00
|
|
|
class GroupViewSet(ModelViewSet):
|
2015-02-04 00:08:38 +01:00
|
|
|
"""
|
2015-07-01 23:18:48 +02:00
|
|
|
API endpoint for groups.
|
|
|
|
|
2015-08-31 14:07:24 +02:00
|
|
|
There are the following views: metadata, list, retrieve, create,
|
|
|
|
partial_update, update and destroy.
|
2015-02-04 00:08:38 +01:00
|
|
|
"""
|
2016-01-25 22:35:23 +01:00
|
|
|
metadata_class = GroupViewSetMetadata
|
2016-12-17 09:30:20 +01:00
|
|
|
queryset = Group.objects.all()
|
2015-02-04 00:08:38 +01:00
|
|
|
serializer_class = GroupSerializer
|
2016-12-17 09:30:20 +01:00
|
|
|
access_permissions = GroupAccessPermissions()
|
2015-02-04 00:08:38 +01:00
|
|
|
|
2015-07-01 23:18:48 +02:00
|
|
|
def check_view_permissions(self):
|
2015-02-04 00:08:38 +01:00
|
|
|
"""
|
2015-07-01 23:18:48 +02:00
|
|
|
Returns True if the user has required permissions.
|
2015-02-04 00:08:38 +01:00
|
|
|
"""
|
2016-12-17 09:30:20 +01:00
|
|
|
if self.action in ('list', 'retrieve'):
|
|
|
|
result = self.get_access_permissions().check_permissions(self.request.user)
|
|
|
|
elif self.action == 'metadata':
|
|
|
|
# Every authenticated user can see the metadata.
|
|
|
|
# Anonymous users can do so if they are enabled.
|
2017-01-15 13:33:54 +01:00
|
|
|
result = self.request.user.is_authenticated() or anonymous_is_enabled()
|
2015-07-01 23:18:48 +02:00
|
|
|
elif self.action in ('create', 'partial_update', 'update', 'destroy'):
|
|
|
|
# Users with all app permissions can edit groups.
|
|
|
|
result = (self.request.user.has_perm('users.can_see_name') and
|
|
|
|
self.request.user.has_perm('users.can_see_extra_data') and
|
|
|
|
self.request.user.has_perm('users.can_manage'))
|
|
|
|
else:
|
|
|
|
# Deny request in any other case.
|
|
|
|
result = False
|
|
|
|
return result
|
2015-02-04 00:08:38 +01:00
|
|
|
|
2015-02-17 00:45:53 +01:00
|
|
|
def destroy(self, request, *args, **kwargs):
|
|
|
|
"""
|
2016-11-04 08:30:58 +01:00
|
|
|
Protects builtin groups 'Default' (pk=1) from being deleted.
|
2015-02-17 00:45:53 +01:00
|
|
|
"""
|
|
|
|
instance = self.get_object()
|
2016-11-04 08:30:58 +01:00
|
|
|
if instance.pk == 1:
|
2015-02-17 00:45:53 +01:00
|
|
|
self.permission_denied(request)
|
2015-07-01 23:18:48 +02:00
|
|
|
self.perform_destroy(instance)
|
|
|
|
return Response(status=status.HTTP_204_NO_CONTENT)
|
2015-02-17 00:45:53 +01:00
|
|
|
|
2015-02-04 00:08:38 +01:00
|
|
|
|
2015-07-01 23:18:48 +02:00
|
|
|
# Special API views
|
2015-02-12 22:42:54 +01:00
|
|
|
|
|
|
|
class UserLoginView(APIView):
|
|
|
|
"""
|
2015-09-16 00:55:27 +02:00
|
|
|
Login the user.
|
2015-02-12 22:42:54 +01:00
|
|
|
"""
|
2016-01-09 01:10:37 +01:00
|
|
|
http_method_names = ['get', 'post']
|
2015-02-12 22:42:54 +01:00
|
|
|
|
|
|
|
def post(self, *args, **kwargs):
|
2016-12-19 14:14:46 +01:00
|
|
|
# If the client tells that cookies are disabled, do not continue as guest (if enabled)
|
|
|
|
if not self.request.data.get('cookies', True):
|
|
|
|
raise ValidationError({'detail': _('Cookies have to be enabled to use OpenSlides.')})
|
2015-02-12 22:42:54 +01:00
|
|
|
form = AuthenticationForm(self.request, data=self.request.data)
|
2015-12-11 16:28:56 +01:00
|
|
|
if not form.is_valid():
|
|
|
|
raise ValidationError({'detail': _('Username or password is not correct.')})
|
|
|
|
self.user = form.get_user()
|
|
|
|
auth_login(self.request, self.user)
|
2015-02-12 22:42:54 +01:00
|
|
|
return super().post(*args, **kwargs)
|
|
|
|
|
|
|
|
def get_context_data(self, **context):
|
2016-01-09 01:10:37 +01:00
|
|
|
"""
|
|
|
|
Adds some context.
|
|
|
|
|
|
|
|
For GET requests adds login info text to context. This info text is
|
|
|
|
taken from the config. If this value is empty, a special text is used
|
|
|
|
if the admin user has the password 'admin'.
|
|
|
|
|
|
|
|
For POST requests adds the id of the current user to the context.
|
|
|
|
"""
|
|
|
|
if self.request.method == 'GET':
|
|
|
|
if config['general_login_info_text']:
|
|
|
|
context['info_text'] = config['general_login_info_text']
|
|
|
|
else:
|
|
|
|
try:
|
|
|
|
user = User.objects.get(username='admin')
|
|
|
|
except User.DoesNotExist:
|
|
|
|
context['info_text'] = ''
|
|
|
|
else:
|
|
|
|
if user.check_password('admin'):
|
|
|
|
context['info_text'] = _(
|
|
|
|
'Installation was successfully. Use {username} and '
|
|
|
|
'{password} for first login. Important: Please change '
|
2016-01-30 10:04:20 +01:00
|
|
|
'your password!').format(
|
2016-01-09 01:10:37 +01:00
|
|
|
username='<strong>admin</strong>',
|
2016-01-30 10:04:20 +01:00
|
|
|
password='<strong>admin</strong>')
|
2016-01-09 01:10:37 +01:00
|
|
|
else:
|
|
|
|
context['info_text'] = ''
|
|
|
|
else:
|
|
|
|
# self.request.method == 'POST'
|
|
|
|
context['user_id'] = self.user.pk
|
2017-01-14 13:02:26 +01:00
|
|
|
user_collection = CollectionElement.from_instance(self.user)
|
|
|
|
context['user'] = user_collection.as_dict_for_user(self.user)
|
2015-02-12 22:42:54 +01:00
|
|
|
return super().get_context_data(**context)
|
|
|
|
|
|
|
|
|
|
|
|
class UserLogoutView(APIView):
|
|
|
|
"""
|
2015-09-16 00:55:27 +02:00
|
|
|
Logout the user.
|
2015-02-12 22:42:54 +01:00
|
|
|
"""
|
|
|
|
http_method_names = ['post']
|
|
|
|
|
|
|
|
def post(self, *args, **kwargs):
|
2015-12-11 16:28:56 +01:00
|
|
|
if not self.request.user.is_authenticated():
|
|
|
|
raise ValidationError({'detail': _('You are not authenticated.')})
|
2015-02-12 22:42:54 +01:00
|
|
|
auth_logout(self.request)
|
|
|
|
return super().post(*args, **kwargs)
|
|
|
|
|
|
|
|
|
|
|
|
class WhoAmIView(APIView):
|
|
|
|
"""
|
2015-07-01 23:18:48 +02:00
|
|
|
Returns the id of the requesting user.
|
2015-02-12 22:42:54 +01:00
|
|
|
"""
|
|
|
|
http_method_names = ['get']
|
|
|
|
|
|
|
|
def get_context_data(self, **context):
|
|
|
|
"""
|
2015-12-10 00:02:16 +01:00
|
|
|
Appends the user id to the context. Uses None for the anonymous
|
|
|
|
user. Appends also a flag if guest users are enabled in the config.
|
2017-01-14 09:14:42 +01:00
|
|
|
Appends also the serialized user if available.
|
2015-02-12 22:42:54 +01:00
|
|
|
"""
|
2017-01-14 09:14:42 +01:00
|
|
|
user_id = self.request.user.pk
|
2017-01-14 13:02:26 +01:00
|
|
|
if user_id is not None:
|
2017-01-14 09:14:42 +01:00
|
|
|
user_collection = CollectionElement.from_instance(self.request.user)
|
|
|
|
user_data = user_collection.as_dict_for_user(self.request.user)
|
|
|
|
else:
|
|
|
|
user_data = None
|
2015-02-12 22:42:54 +01:00
|
|
|
return super().get_context_data(
|
2017-01-14 09:14:42 +01:00
|
|
|
user_id=user_id,
|
2017-01-15 13:33:54 +01:00
|
|
|
guest_enabled=anonymous_is_enabled(),
|
2017-01-14 09:14:42 +01:00
|
|
|
user=user_data,
|
2015-02-12 22:42:54 +01:00
|
|
|
**context)
|
2015-07-01 23:18:48 +02:00
|
|
|
|
|
|
|
|
2015-11-06 15:44:27 +01:00
|
|
|
class SetPasswordView(APIView):
|
|
|
|
"""
|
|
|
|
Users can set a new password for themselves.
|
|
|
|
"""
|
|
|
|
http_method_names = ['post']
|
|
|
|
|
|
|
|
def post(self, request, *args, **kwargs):
|
|
|
|
user = request.user
|
|
|
|
if user.check_password(request.data['old_password']):
|
|
|
|
user.set_password(request.data['new_password'])
|
|
|
|
user.save()
|
|
|
|
else:
|
2015-12-11 16:28:56 +01:00
|
|
|
raise ValidationError({'detail': _('Old password does not match.')})
|
2015-11-06 15:44:27 +01:00
|
|
|
return super().post(request, *args, **kwargs)
|