I need to check different types of permissions for different types of actions from request user. For example get permission only need [IsAuthenticated] but when user request perform_create method. I want to implement another permission that is CanCreateProject
permissions.py
class CanCreateProject(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
if request.user.is_superuser:
return True
else:
return request.user.profile_limitation.can_create_project
views.py
class ProjectView(ModelViewSet):
serializer_class = ProjectSerializer
permission_classes = [IsAuthenticated]
def get_queryset(self):
queryset = Project.objects.all()
organization = self.request.user.organization
query_set = queryset.filter(organization=organization)
return query_set
def perform_create(self, serializer):
self.permission_classes = [CanCreateProject] ## here
project = self.request.data["project_name"]
path = self.request.data["project_name"]
organization = self.request.data["organization"]
serializer.save(project_name=project, project_path=path, organization=organization)
How can I run the CanCreateProject method only for perform_create method is requested.
Override the get_permissions(...) method
class ProjectView(ModelViewSet):
serializer_class = ProjectSerializer
permission_classes = [IsAuthenticated]
def get_queryset(self):
queryset = Project.objects.all()
organization = self.request.user.organization
query_set = queryset.filter(organization=organization)
return query_set
def get_permissions(self):
if self.action == 'create':
composed_perm = IsAuthenticated & CanCreateProject
return [composed_perm()]
return super().get_permissions()
# def perform_create(self, serializer):
# self.permission_classes = [CanCreateProject] ## here
#
# project = self.request.data["project_name"]
# path = self.request.data["project_name"]
# organization = self.request.data["organization"]
# serializer.save(project_name=project, project_path=path,
# organization=organization)
Notes:
You really don't need to use perform_create(...) method here
a possible dup: DRF Viewset remove permission for detail route
Update-1
You should implement the has_permission(..) method of the Permission class, not has_object_permission(...) method
from rest_framework import permissions
class CanCreateProject(permissions.BasePermission):
def has_permission(self, request, view):
if request.user.is_superuser:
return True
else:
return request.user.profile_limitation.can_create_project
Related
My problem is very simple : I'm trying to create some custom permissions for my django rest API. This is my code (permission.py) :
class UserPermissions(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
return obj == request.user
I just want that the users can only get, delete and update their own account.
The problem is that I think my code is not read by Django. I have try to always return false (without any condition) and it does nothing. I have also try to print some debug message at the beginning of the file and it's does nothing.
(My file permissions.py is at the root of my application)$
This is my user view (UserView.py) :
class UserViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows users to be viewed or edited.
"""
queryset = User.objects.all().order_by("-date_joined")
serializer_class = UserSerializer
permission_classes = [permissions.IsAuthenticated]
swagger_tag = ["User"]
class LoginView(KnoxLoginView):
"""
API endpoint allowing the user to login and receive a token
"""
permission_classes = [
permissions.AllowAny,
]
#swagger_auto_schema(request_body=AuthTokenSerializer)
def post(self, request, format=None):
serializer = AuthTokenSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.validated_data["user"]
login(request, user)
return super(LoginView, self).post(request, format=None)
As #UtkucanBıyıklı says in their comment, you should specify the permission in the ViewSet:
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.order_by('-date_joined')
serializer_class = UserSerializer
permission_classes = [permissions.IsAuthenticated, UserPermissions]
swagger_tag = ['User']
I'm using django restframework to do some permission requiring work. What I want to do is make a whole permission require in my view and a different permission require in my specified method. And below is my trial with some main codes.
1 basic view
class VSAccount(viewsets.ModelViewSet):
queryset = MAccount.objects.all().filter(active=True)
serializer_class = DEFAULT_ACCOUNT_SERIALIZER_CLASS
filter_backends = (SearchFilter, DjangoFilterBackend)
permission_classes = [IsAuthenticated, BaseDataPermission, ]
filter_class = FAccount
search_fields = []
module_perm = 'account.get_account'
# 1) add module_perm account.get_account required for whole view.
#action(methods=['get'], url_path='list-with-daily-spends', detail=False)
def list_daily_spend(self, request, *args, **kwargs):
self.module_perm = 'account.get_account-list-with-daily-spend'
# 2) add module_perm for this method only but doesn't work here
self.permission_classes = [BaseDataPermission, ]
self.serializer_class = SAccountListItemDaily
ret_data = super().list(request, *args, **kwargs)
self.serializer_class = DEFAULT_ACCOUNT_SERIALIZER_CLASS
return ret_data
2 customer permission
class BaseDataPermission(BasePermission):
authenticated_users_only = True
def has_perms(self, user, perm):
user_perms = user.get_all_permissions()
print(perm) # it's always what I write in viewset?
if perm in user_perms:
return True
return False
def has_permission(self, request, view):
if request.user.is_superuser:
return True
assert hasattr(view, "module_perm"), "need module_perm"
assert isinstance(view.module_perm, str), "module_perm should be a string"
if getattr(view, '_ignore_model_permissions', False):
return True
if hasattr(view, 'get_queryset'):
queryset = view.get_queryset()
else:
queryset = getattr(view, 'queryset', None)
assert queryset is not None, (
'Cannot apply DjangoModelPermissions on a view that '
'does not set `.queryset` or have a `.get_queryset()` method.'
)
return (
request.user and
(request.user.is_authenticated or not self.authenticated_users_only) and
self.has_perms(request.user, view.module_perm)
)
My question is why I rewrite the moudle_perm in method list_daily_spend, the permission required is still account.get_account which I write in VSAccount and how can I get the expected result?
Thanks
Changing the value of self.permission_classes will not get you there, you need to override the get_permissions(...) method of ModelViewSet as,
class VSAccount(viewsets.ModelViewSet):
# rest of your code
def get_permissions(self):
if self.action == 'list_daily_spend':
self.module_perm = 'account.get_account-list-with-daily-spends'
permission_classes = [BaseDataPermission, ]
return [permission() for permission in permission_classes]
return super().get_permissions()
Alternatively, you can set the permission classes in your action decorator as,
class VSAccount(viewsets.ModelViewSet):
#action(methods=['get'],
url_path='list-with-daily-spends',
detail=False,
permission_classes=[BaseDataPermission, ], module_perm = 'account.get_account-list-with-daily-spends')
def list_daily_spend(self, request, *args, **kwargs):
# your code
How to add custom permission in viewset in django rest framework other than the default permission while creating a module?
I have a permission "fix_an_appointment". In the below viewset, how to include this permission? Those who have this permission has only able to create.
My views.py file:
class settingsViewSet(viewsets.ModelViewSet):
serializer_class = SettingsSerializer
queryset = Setting.objects.all()
Can anyone help?
I can't use a decorator like: #permission_classes(IsAuthenticated, ) in extra actions within ViewSet
To use different permissions in actions, instead, put it into the #action() as a parameter.
#action(detail=True, methods=['post'], permission_classes=[IsAdminOrIsSelf])
def set_password(self, request, pk=None):
...
drf doc
simply create a custom permission class
class FixAnAppointmentPermssion(permissions.BasePermission):
def has_permission(self, request, view):
return True or False
then the in your view set class use your custom permission
class settingsViewSet(viewsets.ModelViewSet):
serializer_class = SettingsSerializer
queryset = Setting.objects.all()
permission_classes = (FixAnAppointmentPermssion,)
by docs custom-permissions, list of view actions actions
my_permissions.py
from rest_framework import permissions
class FixPermission(permissions.BasePermission):
"""
fix_an_appointment
"""
def has_permission(self, request, view):
if request.user.is_authenticated :
if view.action == 'retrieve':
return request.user.has_perms('fix_list_perm')
if view.action == 'retrieve':
return request.user.has_perms('fix_an_appointment')
return False
in views.py
from my_permissions import FixPermission
class settingsViewSet(viewsets.ModelViewSet):
serializer_class = SettingsSerializer
queryset = Setting.objects.all()
permission_classes = (FixPermission,)
We can set permission for each functions like create, retrive, update, delete(add,edit,delete and update)
from my_permissions import FixPermission
class FixAnAppointmentPermssion(permissions.BasePermission):
def has_permission(self, request, view):
return True or False
class YourViewSet(viewsets.ModelViewSet):
serializer_class = SettingsSerializer
queryset = Your.objects.all()
#permission_classes(FixAnAppointmentPermssion,)
def create(request, format=None):
content = {
'status': 'request was permitted'
}
return Response(content)
#permission_classes(FixAnAppointmentPermssion,)
def retrive(request, format=None):
content = {
'status': 'request was permitted'
}
return Response(content)
I have a django model and and i want that model to be accessed only by its owner(user who created the model). So i created a permission class as follows
class IsOwnerOnly(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
# Write permissions are only allowed to the owner of the snippet.
return obj.owner == request.user
and applied this permission on modelviewset
class ItemViewSet(viewsets.ModelViewSet):
queryset = Item.objects.all()
serializer_class = ItemSerializer
permission_classes = (IsOwnerOnly,)
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
While accessing a single item it works, But even then every authenticated user can access the list of items. So how could i limit the item access to only its owner?
I have included Tokenauthentication in settings page as shown
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
),
}
and the item looks like
class Item(models.Model):
name=models.CharField(max_length=30)
address=models.TextField()
owner = models.ForeignKey('auth.User', related_name='items', on_delete=models.CASCADE)
def __str__(self):
return self.name
You can't control who can access item list by owner,if you what,you need to override has_permission to class IsOwnerOnly, like:
class IsAuthenticatedOwner(permissions.BasePermission):
def has_permission(self, request, view):
# work when your access /item/
if request.user and is_authenticated(request.user):
if request.user.id in [1, 2, 3]:
return True
else:
return False
else:
return False
def has_object_permission(self, request, view, obj):
# work when your access /item/item_id/
# Instance must have an attribute named `owner`.
return obj.owner == request.user
Notice:has_permission work when your access /item/(list),has_object_permission work when your access /item/item_id/(retrieve and update).
If you want to let user see the items only he created,simple as:
class ItemsViewSet(ModelViewSet):
queryset = Items.objects.all()
serializer_class = ItemsSerializer
permission_classes = (IsAuthenticated)
def get_queryset(self):
queryset = self.get_queryset().filter(owner=self.request.user)
return queryset
You can override the method get_queryset on your View
class ItemViewSet(viewsets.ModelViewSet):
queryset = Item.objects.all()
serializer_class = ItemSerializer
permission_classes = (IsOwnerOnly,)
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
def get_queryset(self):
return self.queryset.filter(owner=self.request.user)
This way the method list (from ModelViewSet) will call your "get_queryset" to build the pagination with the data.
This is my ViewSet:
class PostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.order_by('-createdAt')
serializer_class = PostSerializer
permission_classes = (IsAuthenticated, IsLikeOrOwnerDeleteOrReadOnly,)
def perform_create(self, serializer):
serializer.save(owner=self.request.user, location=self.request.user.userextended.location)
def get_serializer_context(self):
"""
Extra context provided to the serializer class.
"""
return {
'request': self.request,
'format': self.format_kwarg,
'view': self,
'location': self.request.user.userextended.location
}
#detail_route(methods=['post'], permission_classes=[IsFromLocationOrReadOnly])
def like(self, request, pk=None):
post = self.get_object()
post.usersVoted.add(request.user)
return Response(status=status.HTTP_204_NO_CONTENT)
This is my router / urls.py:
router = routers.DefaultRouter()
router.register(r'posts', views.PostViewSet)
So when I go to /posts I get a list of all posts. What I want is to be able to allow the end-user to go to a specific URL like so: /posts/username and when he does, I want to give him all the posts of that specific username (the filtering will be simple. Something along these lines:
queryset = Post.objects.filter(username=usernameProvidedByTheURL)
How do I go about doing this? Is it possible using DRF?
In your url:
url(r'^/post/(?P<username>\w+)/?$', PostViewSet.as_view({'get': 'list'})),
Then in your PostViewSet, overwrite the get_queryset() method to filter the data by username
class PostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.order_by('-createdAt')
serializer_class = PostSerializer
permission_classes = (IsAuthenticated, IsLikeOrOwnerDeleteOrReadOnly,)
def get_queryset(self):
username = self.kwargs['username']
return Post.objects.filter(username=username)
UPDATE
If you want to keep /post/ endpoint to retrieve all post. Then you need to create an extra view to handle /post/username
class PostsListByUsername(generics.ListAPIView):
serializer_class = PostSerializer
def get_queryset(self):
username = self.kwargs['username']
return Post.objects.filter(username=username)
Then in your urls.py
url(r'^/post/(?P<username>\w+)/?$', PostsListByUsername.as_view()),
Note:
In your get_serializer_context method, you don't need to return request, format and view. DRF will append it for you.
def get_serializer_context(self):
"""
Extra context provided to the serializer class.
"""
return {
'location': self.request.user.userextended.location
}