I use Django group permissions, I can assign some permission for users.
Here is my test scenario:
I have a Company Model,
User1.hasPerm -> view_company, change_company, add_company, delete_company
Permissions.py:
class HasPermissions(permissions.BasePermission):
def has_permission(self, request, view):
if request.user.has_perm('MYAPP.view_company'):
return True
else:
return False
if request.user.has_perm('MYAPP.change_company'):
return True
else:
return False
if request.user.has_perm('MYAPP.add_company'):
return True
else:
return False
if request.user.has_perm('MYAPP.delete_company'):
return True
else:
return False
return True
CompanyView.py:
class CompanyViewSet(ModelViewSet):
queryset = Company.objects.all()
filter_class = CompanyFilter
serializer_class = CompanySerializer
permission_classes = [IsAuthenticated, HasPermissions]
def get_queryset(self):
if self.request.user.is_authenticated and self.request.user.is_active:
company = Company.objects.filter(companyUser__exact=self.request.user)
return company
else:
return Company.objects.all()
I wrote HasPermissions function to control user permissions, and this function works only CompanyView. I want to make global this function for I can control all view.
HasPermissions function is like hard coding, I want to change the more usable version to control all views.
How can ı do this?
You do not need to write this yourself. Django already has a DjangoModelPermissions [drf-doc] for that:
This permission class ties into Django's standard django.contrib.auth
model permissions. This permission must only be applied to views that
have a .queryset property set. Authorization will only be granted if
the user is authenticated and has the relevant model permissions
assigned.
POST requests require the user to have the add permission on the
model.
PUT and PATCH requests require the user to have the change permission on the model.
DELETE requests require the user to have the delete permission on the model.
So you can use:
class CompanyViewSet(ModelViewSet):
queryset = Company.objects.all()
filter_class = CompanyFilter
serializer_class = CompanySerializer
permission_classes = [IsAuthenticated, DjangoModelPermissions]
Related
I am trying to create a custom permission for my view that allow read and write permissions to the owner of the model in the QuerySet but do not allow any permission/request to other users or un-authenticated ones.
Source: https://www.django-rest-framework.org/tutorial/4-authentication-and-permissions/
View:
class My_classListCreateAPIView(generics.ListCreateAPIView):
queryset = Model.objects.all()
serializer_class = ModelSerializer
permission_classes = [IsModelOwner]
Permission:
class IsModelOwner(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
# Permissions are only allowed to the owner of the model and admins.
if request.user.is_staff == True:
return True
return obj.owner == request.user
unfortunately it seems that my view is not even calling my custom permission class. (I imported it etc.) If instead of my custom permission class, I use a default one like permissions.isAuthenticatedOrReadOnly that works instead. What am I missing here?
Thanks.
The has_object_permission method is only called on objects, not on querysets, what this means is that on a list request it won't be called.
Your view only has list and create endpoints, neither of those use the object_permissions, use has_permission instead.
However i believe what you want to do is actually use the isAuthenticated permission combined with a modified get_queryset in your view
class My_classListCreateAPIView(generics.ListCreateAPIView):
queryset = Model.objects.all()
serializer_class = ModelSerializer
permission_classes = [isAuthenticated]
def get_queryset(self):
return Model.objects.filter(owner=self.request.user)
I want to get the user's detail information:
class UserDetailAPIView(RetrieveAPIView):
"""
User detail information
"""
queryset = User.objects.filter(is_valid=True).exclude(status=4)
serializer_class = UserDetailSerializer
lookup_field = "username"
I want to limit other users to access this APIView, I want only admin user and the user it self to access that.
How to limit this?
you should define your own permission class.something like this:
from rest_framework import permissions
class OwnerProfilePermission(permissions.BasePermission):
"""object lvl permissions for owner """
def has_object_permission(self, request, view, obj):
return obj.user == request.user
and in your views include permission_classes .see DRF documention.
http://www.tomchristie.com/rest-framework-2-docs/api-guide/permissions
and the class base views you choose is important.
http://www.tomchristie.com/rest-framework-2-docs/api-guide/generic-views
I'm in the process of debugging my custom permissions class and returning a value of False for my has_object_permission() function, but my I'm still able to access my API (GET request), via Restframework's API browser without authenticating and I can't understand why. Any help would be greatly appreciated. Please see code below. for whatever reasons, it appears that my has_object_permission function is not executing. Please Help
urls.py
router = BulkRouter()
router.register(r'api1', SimpleViewSet1)
urlpatterns = [
url(r'^test/', include(router.urls, namespace='api1')),
]
views.py
class SimpleViewSet1(generics.BulkModelViewSet):
queryset = Barcode.objects.all()
permission_classes = (MyUserPermission,)
serializer_class = SimpleSerializer1
def get_queryset(self):
user = User.objects.get(pk=2)
return Barcode.objects.filter(owner = user)
def get_object(self):
obj = get_object_or_404(self.get_queryset())
self.check_object_permissions(self.request, obj)
return obj
permissions.py
class MyUserPermission(BasePermission):
def has_permission(self, request, view):
return True
def has_object_permission(self, request, view, obj):
return False
serializer.py
class SimpleSerializer1(BulkSerializerMixin, # only required in DRF3
ModelSerializer):
owner = serializers.ReadOnlyField(source='owner.username')
class Meta(object):
model = Barcode
# only required in DRF3
list_serializer_class = BulkListSerializer
fields = ('barcode_number', 'barcode_type', 'owner')
models.py
#python_2_unicode_compatible
class Barcode(models.Model):
owner = models.ForeignKey('auth.User', related_name = 'barcodes')
barcode_number = models.CharField(max_length=200)
barcode_type = models.CharField(max_length=200)
def __str__(self):
return self.barcode_number
Django Rest API Guide says:
Also note that the generic views will only check the object-level permissions for views that retrieve a single model instance. If you require object-level filtering of list views, you'll need to filter the queryset separately. See the filtering documentation for more details.
rest_framework.generics.BulkModelViewSet, as it's name suggests,does bulk operations. It means that you have to use object-level filtering as proposed in the docs.
You should be looking especially under this section. Pay close attention to the example and make use of the code. You should also read about the DjangoModelPermissions to understand how does the example in the link above works.
I am new to DRF, and I had came across the following problem when I trying to customize the Permission in DRF.
Suppose I had the following code in my permissions.py file:
class GetPermission(BasePermission):
obj_attr = 'POITS'
def has_permission(self, request, view):
user = request.user
employee = Employee.objects.get(user=user)
# Return a dict which indicates whether the request user has the corresponding permissions
permissions = get_permission(employee.id)
return permissions[GetPermission.obj_attr]
And in my view, I want to override the static variable in the GetPermission class:
class AssignmentList(generics.ListCreateAPIView):
GetPermission.obj_attr = 'ASSIGNMENT'
permission_classes = (IsAuthenticated, IsStaff, GetPermission)
queryset = Assignment.objects.all()
serializer_class = AssignmentSerializer
pagination_class = LargeResultsSetPagination
def perform_create(self, serializer):
employee = Employee.objects.get(user=self.request.user)
serializer.save(sender=employee, status=0, operatable=0)
However, as the documentation of DRF points out:
Permission checks are always run at the very start of the view, before any other code is allowed to proceed.
So how am I supposed to do, thanks in advance, any ideas will be welcomed, since I am a new fish to DRF.
You need to create subclasses of your permission for each attribute, and use self.obj_attr inside the has_permission method.
class DefaultPermission(GetPermission):
obj_attr = 'POITS'
class AssignmentPermission(GetPermission):
obj_attr = 'ASSIGNMENT'
I have an active Django project where the admin panel is used by the customer support team. Django lacks a view permission because of which I have to assign the change permission to the customer support team which is slightly dangerous. I have some models for which the customer support team needs just the view access and not the change access because of security issues. Why is the view permision missing in Django? Any workaround to this?
Here's a workaround.
Models
Simply create models with view permission by inheriting them from mixin:
class ViewPermissionsMixin(models.Model):
"""
Mixin adds view permission to model.
"""
class Meta:
abstract=True
default_permissions = ('add', 'change', 'delete', 'view')
Example model:
class ExampleModel(ViewPermissionsMixin):
name = models.CharField(max_length=255)
class Meta(ViewPermissionsMixin.Meta):
abstract = False
This will add view permission that can be assigned to certain user/group. But such permission is useless without proper admin modification.
Admins
Here is mixin for your admins:
class AdminViewMixin(admin.ModelAdmin):
def has_perm(self,user,permission):
"""
Usefull shortcut for `user.has_perm()`
"""
if user.has_perm("%s.%s_%s" % (self.model._meta.app_label,permission,self.model.__name__.lower(),)):
return True
return False
def has_module_permission(self, request): # Django 1.8
pass
def has_change_permission(self, request, obj=None):
"""
Necessary permission check to let Django show change_form for `view` permissions
"""
if request.user.is_superuser:
return True
elif self.has_perm(request.user,'change'):
return True
elif self.has_perm(request.user,'view'):
return True
return super(AdminMixin, self).has_change_permission(request, obj)
def get_readonly_fields(self, request, obj=None):
"""
Turn each model field into read-only for `viewers`
"""
all_model_fields = []
for field in self.model._meta.fields:
# TODO in Django 1.8 use ModelAdmin.get_fields()
if not field.auto_created \
and (not hasattr(field,'auto_now_add') or not field.auto_now_add) \
and (not hasattr(field,'auto_now') or not field.auto_now) \
:
all_model_fields.append(field.name)
if request.user.is_superuser:
return self.readonly_fields
elif self.has_perm(request.user,'change'):
return self.readonly_fields
elif self.has_perm(request.user,'view'):
return all_model_fields
return self.readonly_fields
def change_view(self, request, object_id, extra_context=None):
"""
Disable buttons for `viewers` in `change_view`
"""
if request.user.is_superuser:
pass
elif self.has_perm(request.user,'change'):
pass
elif self.has_perm(request.user,'view'):
extra_context = extra_context or {}
extra_context['hide_save_buttons'] = True
return super(AdminViewMixin, self).change_view(request, object_id, extra_context=extra_context)
Example admin:
#admin.register(models.ExampleModel)
class ExampleAdmin(AdminViewMixin):
list_display = ('name',)
pass
Finally just assign view permission for specific models to any user or group in your Django Admin.
I think this should work:
1.Add "view" permission, see https://stackoverflow.com/a/23411901/1266258
2.Customize "change" permission:
class FooAdmin(ModelAdmin):
def has_change_permission(self, request, obj=None):
# user can view the change list
if not obj and request.user.has_perm('myapp.view_foo'):
return True
# user can view the change form and change the obj
return request.user.has_perm('myapp.change_foo')