How to manage roles and permission in Django Rest framework mongoengine - django

I am building a Restapi using Django and Rest framework and mongoengine, so far all requests require a user to be authenticated and check against a token.
But now I need to allow different actions to different users. I don't know where to begin. Any guidelines ?
For example I want only the admin to be able to write and read users objects:
class UsersViewSet(ModelViewSet):
queryset = Users.objects.all()
serializer_class = UsersSerializer
def me(self, request, *args, **kwargs):
serializer = self.serializer_class(request.user)
return Response(serializer.data)

Read the chapter on custom permisssion. You will want to extend permissions.BasePermission and provide the authentication logic inside has_permission.
from rest_framework import permissions
class CustomUserPermission(permissions.BasePermission):
def has_permission(self, request, view):
# return True if user has permission
pass
Then inside your view.
class UsersViewSet(ModelViewSet):
permission_classes = (CustomUserPermission,)

Related

DRF Protect user registration view

I have a following user view:
class UserViewSet(viewsets.ModelViewSet):
serializer_class = UserSerializer
permission_classes = [NotSafeMethodAndAllowAny | permissions.IsAuthenticated]
def get_queryset(self):
if self.request.user.is_superuser:
return User.objects.all()
else:
return User.objects.filter(id=self.request.user.id)
#action(detail=False, methods=["get"])
def current(self, request, *args, **kwargs):
return Response(self.get_serializer(request.user).data)
As this is a ModelViewSet, it allows API users to create, list, update, delete the users.
Don't mind the NotSafeMethodAndAllowAny permission.
Now I want to somehow protect my API from the users using it like in a script. For example, when I submit the user registration form on my frontend (a separate React app), it should be accepted, but if someone posts to my api/user/ with random registration data like from Postman/curl/etc., I want it to be discarded.
Is there a way for DRF to distinguish between those two types of requests? And possibly via some setting, so I can test it via Postman when in development, but when it runs in production, it would not accept automated requests. I want to prevent a situation where someone would create like a bazillion users in a matter of minutes.
BTW: I use JWT for authentication, but for obvious reasons the registration/login endpoints do not require authentication.
If i understand maybe you can create a CustomPermission and only allow requests from specific IP ADDRESS.
from rest_framework import permissions
class IpAddressPermission(permissions.BasePermission):
"""
Global permission check for IPs.
"""
def has_permission(self, request, view):
remote_address = request.META['REMOTE_ADDR']
if remote_addr == settings.ALLOWED_IP_ADDRESS:
return True
return False

Unable to implement django-rules authorization

I'm a new django user looking for some help with django-rules. I'm trying to set up an authorization system that is 'OR' based. I have a 'File' model. I would like only the creator to be able to delete it, but a specific set of users to edit it. I've been able to followed their tutorial and implementation throughout; it works in the shell but not on my site. At the moment no one can delete or update anything.
My view currently looks like:
class FileUpdateView(PermissionRequiredMixin, generics.RetrieveUpdateAPIView):
"""
View updating details of a bill
"""
queryset = File.objects.all()
serializer_class = FileSerializer
permission_required = 'fileupload.change_file'
raise_exception = True
class FileDeleteView(PermissionRequiredMixin, generics.RetrieveDestroyAPIView):
"""
View for deleting a bill
"""
queryset = File.objects.all()
serializer_class = FileSerializer
permission_required = 'fileupload.delete_file'
raise_exception = True
The rules themselves are:
import rules
#rules.predicate
def is_creator(user, file):
"""Checks if user is file's creator"""
return file.owner == user
is_editor = rules.is_group_member('ReadAndWrite')
rules.add_perm('fileupload.change_file', is_editor | is_creator)
rules.add_perm('fileupload.delete_file', is_creator)
I know I'm close I feel like I'm just missing one step.
Thanks in advance!
please check & add settings file authentication backend for Django-rules. Also, you are mixing Django rest permissions with Django-rules permission. you need to check Django-rules permission in Django-rest permissions on view.
in short.
define a custom permission in rest-framework like this.
from rest_framework import permissions
class RulesPermissions(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
return request.user.has_perm('books.edit_book', obj)
in viewset.
class BookView(viewsets.ModelViewSet):
permission_classes = (RulesPermissions,)
I've been working with the django REST framework and django-rules for a project and found an answer to your question.
The django REST framework uses an API view which is not compatible with rules' views.PermissionRequiredMixin, the authorization workflow and methods called during the API dispatch is different from django's class based view.
Try the following mixin for django REST framework API views and their subclasses:
import six
from django.core.exceptions import ImproperlyConfigured
class PermissionRequiredMixin:
permission_required = None
def get_permission_object(self):
object_getter = getattr(self, 'get_object', lambda: None)
return object_getter()
def get_permission_required(self):
if self.permission_required is None:
raise ImproperlyConfigured(
'{0} is missing the permission_required attribute. Define '
'{0}.permission_required, or override '
'{0}.get_permission_required().'
.format(self.__class__.__name__)
)
if isinstance(self.permission_required, six.string_types):
perms = (self.permission_required, )
else:
perms = self.permission_required
return perms
def check_permissions(self, request):
obj = self.get_permission_object()
user = request.user
missing_permissions = [perm for perm in self.get_permission_required()
if not user.has_perm(perm, obj)]
if any(missing_permissions):
self.permission_denied(
request,
message=('MISSING: {}'.format(', '.join(missing_permissions))))
With this mixin you're not forced to write a REST framework permission for each rules permission.

How to add django rest framework permissions on specific method only ?

I have following functions in rest API for User model. I want to set AllowAny permission on only POST request. Can someone help me out.
class UserList(APIView):
"""Get and post users data."""
def get(self, request, format=None):
"""Get users."""
users = User.objects.all()
serialized_users = UserSerializer(users, many=True)
return Response(serialized_users.data)
def post(self, request, format=None):
"""Post users."""
serializer = UserSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
You can write a custom Permission class IsPostOrIsAuthenticated which will allow unrestricted access to POST requests but will allow only authenticated GET requests.
To implement the custom permission IsPostOrIsAuthenticated, override the BasePermission class and implement .has_permission(self, request, view) method. The method should return True if the request should be granted access, and False otherwise.
from rest_framework import permissions
class IsPostOrIsAuthenticated(permissions.BasePermission):
def has_permission(self, request, view):
# allow all POST requests
if request.method == 'POST':
return True
# Otherwise, only allow authenticated requests
# Post Django 1.10, 'is_authenticated' is a read-only attribute
return request.user and request.user.is_authenticated
So, all POST requests will be granted unrestricted access. For other requests, authentication will be required.
Now, you need to include this custom permission class in your global settings.
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'my_app.permissions.IsPostOrIsAuthenticated',
)
}
http://www.django-rest-framework.org/api-guide/permissions/
as per above URL you have to write one custom permission class
class ExampleView(APIView):
permission_classes = (MyCustomAuthenticated,)
Write your own logic using AllowAny or IsAuthenticated inside MyCUstomAuthenticated based on POST and GET

Django Rest Framework authentication - how to implement custom decorator

I am trying to implement TokenAuthentication using the Rest Framework, but it seems that I can't add my own custom decorators to my ViewSets because they are evaluated BEFORE the authentication. Consider this:
from django.utils.decorators import method_decorator
from django.http.response import HttpResponseForbidden
def require_staff(View):
def staffOnly(function):
def wrap(request, *args, **kwargs):
if request.user.is_active and request.user.is_staff:
return function(request, *args, **kwargs)
else:
return HttpResponseForbidden()
return wrap
View.dispatch = method_decorator(staffOnly)(View.dispatch)
return View
When I try to implement this, it seems the decorator code fires first, so the authentication is never run.
#require_staff
class CustomerViewSet(ModelViewSet):
model = Customer
filter_class = CustomerFilter
filter_backends = (DjangoFilterBackend,)
Since request.user is never set, introducing the decorator breaks authentication.
I think the issue is that Authentication is occuring the rest_frameworks dispatch() function and it is not clear to me how I could add additional (say) custom security if authentication is done that late in the game.
Am I missing something here, or what is the proper way to implement this customization?
Someone suggested using Permissions for this instead. I assume they mean custom DRF Permissions, right?
Everything you need to know is DRF permissions is here: http://www.django-rest-framework.org/api-guide/permissions
DRF provides a built in permission that is similar to yours, called IsAdminUser
The IsAdminUser permission class will deny permission to any user,
unless user.is_staff is True in which case permission will be allowed.
This permission is suitable is you want your API to only be accessible
to a subset of trusted administrators.
To use this permission in a Class Based View:
class ExampleView(APIView):
permission_classes = (IsAdminUser,)
Now you have two options to do an extra check for user.is_active.
The first is override the IsAdminUser permission, like so:
from rest_framework import permissions
class IsActiveAndAdminUser(permissions.IsAdminUser):
"""Only allow a user who is Admin and Active to view this endpoint. """
def has_permission(self, request, view):
is_admin = super(IsAdminAndActiveUser, self).has_permission(request, view)
return request.user.is_active and is_admin
The second is to create an IsActiveUser permission, and chain them in your view.
IsActiveUser Permission:
from rest_framework import permissions
class IsActiveUser(permissions.BasePermission):
""" Only Active Users have permission """
def has_permission(self, request, view):
return request.user.is_active
Your new permission list in your class based view:
class ExampleView(APIView):
permission_classes = (IsActiveUser, IsAdminUser,)

Using different authentication for different operations in ModelViewSet in Django REST framework

I have the following ModelViewSet
class UserViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows users to be viewed or edited.
"""
queryset = User.objects.all()
serializer_class = UserSerializer
authentication_classes = (TokenAuthentication,)
permission_classes = (permissions.IsAuthenticated, MyUserPermissions)
I want the create method (POST on /users/) to not ask for any authentication. How can I override the authentication_classes in this case? I'm talking about ModelViewSet not generic API views.
I want the create method (POST on /users/) to not ask for any authentication.
Actually that's not quite what you want. You want POST on users to not require any permissions, which will have the effect that either authenticated or unauthenticated requests will succeed.
I'd suggest overriding your permission classes so that they always allow POST requests. Follow the custom permissions documentation for more info on that.
Essentially you'll have something like:
class IsAuthenticatedOrCreate(permissions.IsAuthenticated):
def has_permission(self, request, view):
if request.method == 'POST':
return True
return super(IsAuthenticatedOrCreate, self).has_permission(request, view)
And probably something similar for your other permission class too.