i am creating a Todo list app using Django rest framework.
in this app only manager can post task in the list.
User Profile has a field named as a role.
I have a User Profile Model with extended User Model.
model.py
class UserProfile(models.Model):
user = models.OneToOneField(User,unique=False)
department = models.CharField(max_length=50, choices=DEPARTMENT_CHOICES,default='technology')
role = models.CharField(max_length=50, choices=ROLE_CHOICES,default='manager')
User Profile have a role field.
I want the only manager can post Task in my app.
How can I write custom user permission in order to achieve this?
Restricted to POST request only. All other requests can be permitted.
You would create a permission which subclasses rest_framework.permissions.IsAuthenticated and add your logic in has_permission(self, request, view) (see more).
If you only want it to be applied to POST, simply check the request's method and return True if it's a different method. Something like:
from rest_framework import permissions
class CustomPermission(permissions.IsAuthenticated):
def has_permission(self, request, view):
if request.user.user_profile.role == 'ADMIN' or request.method != 'POST':
return True
return False
Don't forget to include this permission in your view's permission_classes.
PS: I should warn you, though, it's a bit odd that you'd allow only admins to POST tasks while allowing everyone to PUT and DELETE on them. Maybe you mean you want to allow everyone in safe_methods only (GET, OPTIONS and HEAD)? If that's the case, replace request.method != 'POST' with request.method in permissions.SAFE_METHODS.
Related
My main concern is to show a list of Models that user can interact with based on given permissions.
I am thinking of doing this by way of user's group permissions. I wanna mimic how the Django admin is able to list the models when you give any permissions to the user related to that model like this:
For example I have model called Posts with permissions can_add_post and can_delete_post. I am assigned to a Group called Writer that has been assigned with both permissions. In my view, I want to be able to retrieve the name of 'Posts' model as a text so I can do something with it like set an onClick function to call a respective React component like this:
I have no clear business logic in mind to do this though. However, I am using DRF and I have a Login API and I'm thinking I might put the logic inside this API so that I can fetch all permissions of this logged in user and show those modules that he has access on.
UPDATE:
Here is my Login API View:
class LoginAPI(generics.GenericAPIView):
serializer_class = LoginSerializer
permission_classes = ()
def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.validated_data
return Response(
{
"user": UserSerializer(user, context=self.get_serializer_context()).data,
"token": AuthToken.objects.create(user)[1],
"authenticated": True
}
)
I guess you need this?
from django.contrib.auth.models import Permission
#for user permisions
set(Permission.objects.filter(user=your_user).values_list('content_type__model', flat = True))
#for user groups permisions
set(Permission.objects.filter(group__user=your_user).values_list('content_type__model', flat = True))
This is concept, so you should adapt it to your needs
p.s.: using set() because i don't know how to distinct() on content_type__model field, perhaps a more elegant solution exists.
I have one model which has user as its ForeignKey attribute which is auto fill ie. logged in user is filled there. I have made token authentication. Only Authenticated // i mean authorized users can visit that view. But i am planning to make such that only the user which had created that model object can only update the content of that object.
For example:
class Something(models.Model):
sth_name = models.CharField(max_length=18)
sth_qty = models.IntegerField()
user = models.ForeignKey(User)
on my View:
I override perform_create() to associate to above model automaticall.
def perform_create(self, serializer):
return serializer.save(user=self.request.user)
What do i exactly need to do? I have to write some permissions method, But I am really stuck.
Yes, you need to create an object level permission. The DRF tutorial covers this nicely here: http://www.django-rest-framework.org/tutorial/4-authentication-and-permissions/#object-level-permissions
Specifically, create a file permissions.py in your app, and add this permission there:
class IsOwnerOrReadOnly(permissions.BasePermission):
"""
Custom permission to only allow owners of an object to edit it.
"""
def has_object_permission(self, request, view, obj):
if request.method in permissions.SAFE_METHODS:
return True
return obj.user == request.user
Then, in your view class which has the update resource for the Something model (probably SomethingDetail), add the permission_classes field:
class SomethingDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Something.objects.all()
serializer_class = SomethingSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,
IsOwnerOrReadOnly,)
Just add the user when retrieving the object
obj = get_object_or_404(Something, pk=pk, user=request.user)
Note that this will throw 404. If you want 403 error, use custom condition to check the user and raise PermissionDenied. If you want to do this for multiple views, put the condition logic in a decorator.
I have an app 'app_name' which has a profile object which is linked to User object via OneToOne field way. Now on login I redirect the user to a edit profile page on admin site using url using his profile object id. I've granted as expected is_staff access and permission to edit profiles.
Now I wan't to prevent him from accessing other profile objects. I've got two ways, as of now, which I can think so-
1. Make a custom permission which grants access to edit for his profile only who logins.
2. Blocking urls which doesn't involves profile object id.
Superuser should be able to access normally as it is available as default.
I've no idea how can I execute one of the ways. Any other possible ways are too welcomed. Please help!
Override ModelAdmin.get_queryset() can exclude other objects:
class ProfileAdmin(ModelAdmin):
# current user can only see his profile
def get_queryset(self, request):
qs = super(ProfileAdmin, self).get_queryset(request)
if not request.user.is_superuser:
qs = qs.filter(id=request.user.id)
return qs
If you only want to control change permission, override ModelAdmin.has_change_permission():
class ProfileAdmin(ModelAdmin):
# current user can only edit his profile
def has_change_permission(self, request, obj=None):
# user has permission to edit a obj
if not obj or request.user.is_superuser:
return True
# user ONLY has permission to edit his obj
return request.user.id == obj.user.id
I already know how to create ModelResource in tastypie. For example, I have UserResource in resources.py and User in models.py. However in views.py, I have a view called match_user where it takes the list of all user and match to request.user. It return a render_to_response html called mymatch.html. Everything works on the browser but I want to create an API for this particular match_user. How can I do that?
Thank you
I think the following answers your question:
class UserResource(ModelResource):
class Meta:
queryset = User.objects.all()
resource_name = "user"
authentication = SessionAuthentication()
# User is only authorized to view his own details
def apply_authorization_limits(self, request, object_list):
return object_list.filter(pk=request.user.pk)
Session authentication would work if the user has an active session and is already logged in. For more authentication options, see https://django-tastypie.readthedocs.org/en/latest/authentication_authorization.html?highlight=authentication#authentication-options
What approach is the best way to make content-types restricted to a user in Django?
Let us say I want all users with the user-role "blogger" to have its own blog.
I have created a weblog app. How do I restrict it so that the user logged in can only post in his "own" blog, and how do I make views that shows only a user's blog?
First your blog entries has to be attached to user, so you know on whos blog display, it, right? models.py:
class BlogEntry(models.Model):
user = models.ForeignKey(User, related_name='blog_entries')
other_field_1 = ...
other_field_2 = ...
Next, skip it in ModelForm, forms.py:
class BlogEntryModelForm(forms.ModelForm):
class Meta:
exclude = ('user',)
Then, when user want to post entry you require he's logged, views.py:
#login_required
def post_blog_entry(request):
....
if request.method == 'POST':
form = BlogEntryModelForm(request.POST)
if form.is_valid():
new_entry = form.save(commit=False)
new_entry.user = request.user
new_entry.save()
When you want display some user blog, views.py:
def view_blog(request, blogger_name):
user = get_object_or_404(User, username=blogger_name)
entries = user.blog_entries.all()
User is django.contrib.auth.models.User
You can add custom role checking to views above to display 404 page or error page if user has no rights to create blog.
Optionally you can replace User from django.contrib.auth with your own User implementation but you'll have to write model, authentication and middleware for it as well...
I didnt try to implement this, but I found another soultion that worked very good. It was easy to implement and did everything i wanted.
Check it out...