Commit 0446f449 authored by ibuler's avatar ibuler

Add auth and permission backends

parent a62a2178
...@@ -68,7 +68,6 @@ INSTALLED_APPS = [ ...@@ -68,7 +68,6 @@ INSTALLED_APPS = [
'django.contrib.sessions', 'django.contrib.sessions',
'django.contrib.messages', 'django.contrib.messages',
'django.contrib.staticfiles', 'django.contrib.staticfiles',
'ws4redis',
] ]
...@@ -264,12 +263,14 @@ REST_FRAMEWORK = { ...@@ -264,12 +263,14 @@ REST_FRAMEWORK = {
# Use Django's standard `django.contrib.auth` permissions, # Use Django's standard `django.contrib.auth` permissions,
# or allow read-only access for unauthenticated users. # or allow read-only access for unauthenticated users.
'DEFAULT_PERMISSION_CLASSES': ( 'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAdminUser', # 'rest_framework.permissions.IsAuthenticated',
'users.backends.IsValidUser',
), ),
'DEFAULT_AUTHENTICATION_CLASSES': ( 'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.BasicAuthentication', 'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication', 'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.TokenAuthentication', 'rest_framework.authentication.TokenAuthentication',
'users.backends.AppSignAuthentication',
), ),
} }
# This setting is required to override the Django's main loop, when running in # This setting is required to override the Django's main loop, when running in
......
...@@ -7,11 +7,12 @@ from rest_framework import generics, status ...@@ -7,11 +7,12 @@ from rest_framework import generics, status
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework_bulk import ListBulkCreateUpdateDestroyAPIView from rest_framework_bulk import ListBulkCreateUpdateDestroyAPIView
from common.mixins import BulkDeleteApiMixin
from common.utils import get_logger
from .models import User, UserGroup from .models import User, UserGroup
from .serializers import UserDetailSerializer, UserAndGroupSerializer, \ from .serializers import UserDetailSerializer, UserAndGroupSerializer, \
GroupDetailSerializer, UserPKUpdateSerializer, UserBulkUpdateSerializer, GroupBulkUpdateSerializer GroupDetailSerializer, UserPKUpdateSerializer, UserBulkUpdateSerializer, GroupBulkUpdateSerializer
from common.mixins import BulkDeleteApiMixin from .backends import IsSuperUser, IsAppUser, IsValidUser, IsSuperUserOrAppUser
from common.utils import get_logger
logger = get_logger(__name__) logger = get_logger(__name__)
...@@ -84,6 +85,10 @@ class GroupDetailApi(generics.RetrieveUpdateDestroyAPIView): ...@@ -84,6 +85,10 @@ class GroupDetailApi(generics.RetrieveUpdateDestroyAPIView):
class UserListUpdateApi(BulkDeleteApiMixin, ListBulkCreateUpdateDestroyAPIView): class UserListUpdateApi(BulkDeleteApiMixin, ListBulkCreateUpdateDestroyAPIView):
queryset = User.objects.all() queryset = User.objects.all()
serializer_class = UserBulkUpdateSerializer serializer_class = UserBulkUpdateSerializer
permission_classes = (IsSuperUserOrAppUser,)
def get(self, request, *args, **kwargs):
return super(UserListUpdateApi, self).get(request, *args, **kwargs)
class GroupListUpdateApi(BulkDeleteApiMixin, ListBulkCreateUpdateDestroyAPIView): class GroupListUpdateApi(BulkDeleteApiMixin, ListBulkCreateUpdateDestroyAPIView):
......
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
from rest_framework import authentication, exceptions from rest_framework import authentication, exceptions, permissions
from rest_framework.compat import is_authenticated
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from common.utils import unsign from common.utils import unsign, get_object_or_none
from .models import User from .models import User
class APPSignAuthentication(authentication.BaseAuthentication): class AppSignAuthentication(authentication.BaseAuthentication):
keyword = 'Sign' keyword = 'Sign'
model = User model = User
...@@ -30,17 +31,50 @@ class APPSignAuthentication(authentication.BaseAuthentication): ...@@ -30,17 +31,50 @@ class APPSignAuthentication(authentication.BaseAuthentication):
except UnicodeError: except UnicodeError:
msg = _('Invalid token header. Sign string should not contain invalid characters.') msg = _('Invalid token header. Sign string should not contain invalid characters.')
raise exceptions.AuthenticationFailed(msg) raise exceptions.AuthenticationFailed(msg)
return self.authenticate_credentials(sign) return self.authenticate_credentials(sign)
def authenticate_credentials(self, key): def authenticate_credentials(self, sign):
try: app = unsign(sign, max_age=300)
token = self.model.objects.select_related('user').get(key=key) if app:
except self.model.DoesNotExist: user = get_object_or_none(self.model, username=app, role='App')
raise exceptions.AuthenticationFailed(_('Invalid token.')) else:
raise exceptions.AuthenticationFailed(_('Invalid sign.'))
if not token.user.is_active: if not user.is_active:
raise exceptions.AuthenticationFailed(_('User inactive or deleted.')) raise exceptions.AuthenticationFailed(_('User inactive or deleted.'))
return user, None
class IsValidUser(permissions.IsAuthenticated, permissions.BasePermission):
"""Allows access to valid user, is active and not expired"""
def has_permission(self, request, view):
return super(IsValidUser, self).has_permission(request, view) \
and request.user.is_valid
class IsAppUser(IsValidUser, permissions.BasePermission):
"""Allows access only to app user """
def has_permission(self, request, view):
return super(IsAppUser, self).has_permission(request, view) \
and request.user.is_app_user
class IsSuperUser(IsValidUser, permissions.BasePermission):
"""Allows access only to superuser"""
def has_permission(self, request, view):
return super(IsSuperUser, self).has_permission(request, view) \
and request.user.is_superuser
class IsSuperUserOrAppUser(IsValidUser, permissions.BasePermission):
"""Allows access between superuser and app user"""
def has_permission(self, request, view):
return super(IsSuperUserOrAppUser, self).has_permission(request, view) \
and (request.user.is_superuser or request.user.is_app_user)
if __name__ == '__main__': if __name__ == '__main__':
......
...@@ -69,6 +69,7 @@ class User(AbstractUser): ...@@ -69,6 +69,7 @@ class User(AbstractUser):
ROLE_CHOICES = ( ROLE_CHOICES = (
('Admin', _('Administrator')), ('Admin', _('Administrator')),
('User', _('User')), ('User', _('User')),
('App', _('Application')),
) )
username = models.CharField(max_length=20, unique=True, verbose_name=_('Username')) username = models.CharField(max_length=20, unique=True, verbose_name=_('Username'))
...@@ -148,9 +149,18 @@ class User(AbstractUser): ...@@ -148,9 +149,18 @@ class User(AbstractUser):
else: else:
self.role = 'User' self.role = 'User'
is_admin = is_superuser
@property
def is_app_user(self):
if self.role == 'App':
return True
else:
return False
@property @property
def is_staff(self): def is_staff(self):
if self.is_authenticated and self.is_active and not self.is_expired and self.is_superuser: if self.is_authenticated and self.is_valid:
return True return True
else: else:
return False return False
...@@ -185,14 +195,14 @@ class User(AbstractUser): ...@@ -185,14 +195,14 @@ class User(AbstractUser):
Token.objects.filter(user=self).delete() Token.objects.filter(user=self).delete()
return Token.objects.create(user=self) return Token.objects.create(user=self)
def generate_reset_token(self):
return signing.dumps({'reset': self.id, 'email': self.email})
def is_member_of(self, user_group): def is_member_of(self, user_group):
if user_group in self.groups.all(): if user_group in self.groups.all():
return True return True
return False return False
def generate_reset_token(self):
return signing.dumps({'reset': self.id, 'email': self.email})
@classmethod @classmethod
def validate_reset_token(cls, token, max_age=3600): def validate_reset_token(cls, token, max_age=3600):
try: try:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment