Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions time_monkey/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,3 +133,9 @@
FIXTURE_DIRS = [
'users/fixtures/',
]

REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': (
'rest_framework.renderers.AdminRenderer',
)
}
7 changes: 7 additions & 0 deletions users/common/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,10 @@ class ChoiceEnum(Enum):
@classmethod
def choices(cls):
return tuple((i.name, i.value) for i in cls)


class Action(ChoiceEnum):
LIST = 'list'
RETRIEVE = 'retrieve'
CREATE = 'create'
UPDATE = 'update'
3 changes: 3 additions & 0 deletions users/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ def _create_user(
password,
is_staff,
is_superuser,
user_type,
):
"""
Creates and saves a user with the given email and password.
Expand All @@ -46,6 +47,7 @@ def _create_user(
is_staff=is_staff,
is_active=True,
is_superuser=is_superuser,
user_type=user_type,
)
user.set_password(password)
user.save()
Expand All @@ -62,6 +64,7 @@ def create_superuser(
password,
is_staff=True,
is_superuser=True,
user_type=CustomUser.UserType.ADMIN.name,
)


Expand Down
17 changes: 17 additions & 0 deletions users/permissions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from rest_framework import permissions
Comment thread
TheCM marked this conversation as resolved.
from users.models import CustomUser


class IsAdminUser(permissions.BasePermission):
message = 'You are not allowed to enter - for admin only.'
def has_permission(self, request, view):
return request.user and request.user.is_authenticated and request.user.user_type == CustomUser.UserType.ADMIN.name


class IsOwnerOrAdmin(permissions.BasePermission):
message = "It's none of your business."
def has_permission(self, request, view):
return (view.get_object() == request.user) or (request.user and request.user.is_authenticated and request.user.user_type == CustomUser.UserType.ADMIN.name)
def has_object_permission(self, request, view, obj):
if request.user.is_authenticated:
return request.user.user_type == CustomUser.UserType.ADMIN.name or obj == request.user
54 changes: 52 additions & 2 deletions users/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,65 @@
from users.models import CustomUser



class UserSerializer(CountryFieldMixin, serializers.ModelSerializer):
country = CountryField(country_dict=True)
class Meta:
model = CustomUser
fields = '__all__'


class UserListSerializer(UserSerializer):
url = serializers.HyperlinkedIdentityField(
view_name="users-detail",
)
class Meta:
model = CustomUser
fields = (
'url',
'id',
'email',
'first_name',
'last_name',
)


class UserDetailSerializer(UserSerializer):
class Meta:
model = CustomUser
fields = (
'email',
'first_name',
'last_name',
'date_of_birth',
'phone_number',
'country',
'user_type',
)


class UserUpdateSerializer(UserSerializer):
class Meta:
model = CustomUser
fields = (
'first_name',
'last_name',
'date_of_birth',
'phone_number',
'country',
)


class UserCreateSerializer(UserSerializer):
class Meta:
model = CustomUser
fields = (
'email',
'first_name',
'last_name',
'date_of_birth',
'phone_number',
'country',
'user_type',
'is_staff',
'is_superuser',
'is_active',
)
29 changes: 22 additions & 7 deletions users/tests.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
from django.core.exceptions import ValidationError
from django.core.validators import EmailValidator
from django.core.validators import MaxLengthValidator
from django.test import TestCase
from users.models import CustomUser
from django.urls import reverse

from rest_framework.test import APITestCase
from rest_framework.test import APIClient
from django.core.exceptions import ValidationError
from django.core.validators import EmailValidator
from django.core.validators import MaxLengthValidator

from users.common.exceptions import CustomValidationError
from users.common.strings import CustomValidationErrorText
from users.common.strings import CustomUserModelText
from users.common.strings import CustomValidationErrorText
from users.common import constants
from users.models import CustomUser


class TestCustomUserLogin(TestCase):
Expand All @@ -19,6 +21,7 @@ def setUp(self):
"testuserpasswd",
False,
False,
CustomUser.UserType.EMPLOYEE.name,
)

def test_user_should_login_correctly(self):
Expand Down Expand Up @@ -63,14 +66,26 @@ def test_user_should_hold_email_address(self):
CustomValidationError,
CustomValidationErrorText.VALIDATION_ERROR_EMAIL_MESSAGE,
):
CustomUser.objects._create_user(None,"testuserpasswd",False,False)
CustomUser.objects._create_user(
None,
"testuserpasswd",
False,
False,
CustomUser.UserType.EMPLOYEE.name,
)

def test_user_should_hold_password(self):
with self.assertRaisesRegexp(
CustomValidationError,
CustomValidationErrorText.VALIDATION_ERROR_PASSWORD_MESSAGE,
):
CustomUser.objects._create_user("testuser@example.com",None,False,False)
CustomUser.objects._create_user(
"testuser@example.com",
None,
False,
False,
CustomUser.UserType.EMPLOYEE.name,
)

def test_user_should_not_hold_same_email_as_another_user(self):
old_user = CustomUser.objects.create(
Expand Down
38 changes: 30 additions & 8 deletions users/urls.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,32 @@
from django.conf.urls import url, include
from rest_framework.routers import DefaultRouter
from users import views
from django.conf.urls import url
from rest_framework import renderers
from rest_framework.urlpatterns import format_suffix_patterns
from users.views import UserViewSet
from users.views import UsersViewSet
from users.views import api_root
Comment thread
TheCM marked this conversation as resolved.

router = DefaultRouter()
router.register(r'users', views.UserViewSet)
Comment thread
TheCM marked this conversation as resolved.
users_list = UsersViewSet.as_view({
'get': 'list',
'post': 'create'
})

urlpatterns = [
url(r'^', include(router.urls))
]
users_detail = UsersViewSet.as_view({
'get': 'retrieve',
'put': 'update',
'patch': 'partial_update',
'delete': 'destroy'
})

user_account_detail = UserViewSet.as_view({
'get': 'retrieve',
'put': 'update',
'patch': 'partial_update',
'delete': 'destroy'
})

urlpatterns = format_suffix_patterns([
url(r'^$', api_root),
url(r'^users/$', users_list, name='users-list'),
url(r'^users/(?P<pk>[0-9]+)/$', users_detail, name='users-detail'),
url(r'^account/(?P<pk>[0-9]+)/$', user_account_detail, name='user-account-detail'),
])
69 changes: 67 additions & 2 deletions users/views.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,73 @@
from rest_framework import viewsets
from rest_framework import permissions
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework.reverse import reverse

from users.common.fields import Action
from users.models import CustomUser
from users.permissions import IsAdminUser
from users.permissions import IsOwnerOrAdmin
from users.serializers import UserCreateSerializer
from users.serializers import UserDetailSerializer
from users.serializers import UserListSerializer
from users.serializers import UserSerializer
from rest_framework import viewsets
from users.serializers import UserUpdateSerializer
Comment thread
TheCM marked this conversation as resolved.


@api_view()
def api_root(request, format=None):
Comment thread
TheCM marked this conversation as resolved.
if request.user.is_authenticated and request.user.user_type == CustomUser.UserType.ADMIN.name:
return Response({
Comment thread
TheCM marked this conversation as resolved.
'users': reverse(
'users-list',
request=request,
format=format,
),
'account': reverse(
'user-account-detail',
args=(request.user.pk,),
request=request,
format=format,
),
})
elif request.user.is_authenticated:
return Response({
'account': reverse(
'user-account-detail',
args=(request.user.pk,),
request=request,
format=format,
),
})
Comment thread
TheCM marked this conversation as resolved.
else:
return Response({
})


class UsersViewSet(viewsets.ModelViewSet):
Comment thread
TheCM marked this conversation as resolved.
queryset = CustomUser.objects.all()
permission_classes = (IsAdminUser,)

def get_serializer_class(self):
if self.action == Action.LIST.value:
return UserListSerializer
if self.action == Action.RETRIEVE.value:
return UserDetailSerializer
if self.action == Action.CREATE.value:
return UserCreateSerializer
if self.action == Action.UPDATE.value:
return UserCreateSerializer
return UserSerializer


class UserViewSet(viewsets.ModelViewSet):
queryset = CustomUser.objects.all()
serializer_class = UserSerializer
permission_classes = (IsOwnerOrAdmin,)

def get_serializer_class(self):
if self.action == Action.RETRIEVE.value:
return UserDetailSerializer
if self.action == Action.UPDATE.value:
return UserUpdateSerializer
return UserSerializer