mirror of https://github.com/jumpserver/jumpserver
Add auth and permission backends
parent
a62a2178d0
commit
0446f449e9
|
@ -68,7 +68,6 @@ INSTALLED_APPS = [
|
|||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'ws4redis',
|
||||
|
||||
]
|
||||
|
||||
|
@ -264,12 +263,14 @@ REST_FRAMEWORK = {
|
|||
# Use Django's standard `django.contrib.auth` permissions,
|
||||
# or allow read-only access for unauthenticated users.
|
||||
'DEFAULT_PERMISSION_CLASSES': (
|
||||
'rest_framework.permissions.IsAdminUser',
|
||||
# 'rest_framework.permissions.IsAuthenticated',
|
||||
'users.backends.IsValidUser',
|
||||
),
|
||||
'DEFAULT_AUTHENTICATION_CLASSES': (
|
||||
'rest_framework.authentication.BasicAuthentication',
|
||||
'rest_framework.authentication.SessionAuthentication',
|
||||
'rest_framework.authentication.TokenAuthentication',
|
||||
'users.backends.AppSignAuthentication',
|
||||
),
|
||||
}
|
||||
# This setting is required to override the Django's main loop, when running in
|
||||
|
|
|
@ -7,11 +7,12 @@ from rest_framework import generics, status
|
|||
from rest_framework.response import Response
|
||||
from rest_framework_bulk import ListBulkCreateUpdateDestroyAPIView
|
||||
|
||||
from common.mixins import BulkDeleteApiMixin
|
||||
from common.utils import get_logger
|
||||
from .models import User, UserGroup
|
||||
from .serializers import UserDetailSerializer, UserAndGroupSerializer, \
|
||||
GroupDetailSerializer, UserPKUpdateSerializer, UserBulkUpdateSerializer, GroupBulkUpdateSerializer
|
||||
from common.mixins import BulkDeleteApiMixin
|
||||
from common.utils import get_logger
|
||||
from .backends import IsSuperUser, IsAppUser, IsValidUser, IsSuperUserOrAppUser
|
||||
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
@ -84,6 +85,10 @@ class GroupDetailApi(generics.RetrieveUpdateDestroyAPIView):
|
|||
class UserListUpdateApi(BulkDeleteApiMixin, ListBulkCreateUpdateDestroyAPIView):
|
||||
queryset = User.objects.all()
|
||||
serializer_class = UserBulkUpdateSerializer
|
||||
permission_classes = (IsSuperUserOrAppUser,)
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
return super(UserListUpdateApi, self).get(request, *args, **kwargs)
|
||||
|
||||
|
||||
class GroupListUpdateApi(BulkDeleteApiMixin, ListBulkCreateUpdateDestroyAPIView):
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
# -*- 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 common.utils import unsign
|
||||
from common.utils import unsign, get_object_or_none
|
||||
from .models import User
|
||||
|
||||
|
||||
class APPSignAuthentication(authentication.BaseAuthentication):
|
||||
class AppSignAuthentication(authentication.BaseAuthentication):
|
||||
keyword = 'Sign'
|
||||
model = User
|
||||
|
||||
|
@ -30,17 +31,50 @@ class APPSignAuthentication(authentication.BaseAuthentication):
|
|||
except UnicodeError:
|
||||
msg = _('Invalid token header. Sign string should not contain invalid characters.')
|
||||
raise exceptions.AuthenticationFailed(msg)
|
||||
|
||||
return self.authenticate_credentials(sign)
|
||||
|
||||
def authenticate_credentials(self, key):
|
||||
try:
|
||||
token = self.model.objects.select_related('user').get(key=key)
|
||||
except self.model.DoesNotExist:
|
||||
raise exceptions.AuthenticationFailed(_('Invalid token.'))
|
||||
def authenticate_credentials(self, sign):
|
||||
app = unsign(sign, max_age=300)
|
||||
if app:
|
||||
user = get_object_or_none(self.model, username=app, role='App')
|
||||
else:
|
||||
raise exceptions.AuthenticationFailed(_('Invalid sign.'))
|
||||
|
||||
if not token.user.is_active:
|
||||
if not user.is_active:
|
||||
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__':
|
||||
|
|
|
@ -69,6 +69,7 @@ class User(AbstractUser):
|
|||
ROLE_CHOICES = (
|
||||
('Admin', _('Administrator')),
|
||||
('User', _('User')),
|
||||
('App', _('Application')),
|
||||
)
|
||||
|
||||
username = models.CharField(max_length=20, unique=True, verbose_name=_('Username'))
|
||||
|
@ -148,9 +149,18 @@ class User(AbstractUser):
|
|||
else:
|
||||
self.role = 'User'
|
||||
|
||||
is_admin = is_superuser
|
||||
|
||||
@property
|
||||
def is_app_user(self):
|
||||
if self.role == 'App':
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
@property
|
||||
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
|
||||
else:
|
||||
return False
|
||||
|
@ -185,14 +195,14 @@ class User(AbstractUser):
|
|||
Token.objects.filter(user=self).delete()
|
||||
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):
|
||||
if user_group in self.groups.all():
|
||||
return True
|
||||
return False
|
||||
|
||||
def generate_reset_token(self):
|
||||
return signing.dumps({'reset': self.id, 'email': self.email})
|
||||
|
||||
@classmethod
|
||||
def validate_reset_token(cls, token, max_age=3600):
|
||||
try:
|
||||
|
|
Loading…
Reference in New Issue