Django permissions when using Django Rest Framework + VueJs as a front - django

My question address to the usage of Django permission architecture even when the front is on Vue.js and data is requested/responded through Django REST framework.
I am a little confused about being able to use default permission libraries of Django when the app is combined with Rest Framework and VueJs:
from django.contrib.auth.decorators import login_required, permission_required
#permission_required('pobapp.can_add_instance')
#login_required
def addEmployeeInstance(request):
return render(request, 'pobapp/search.html')
If not, how can I restrict some data and pages for specific users? For example, if I only wanted to let authenticated users to view some specific pages?

If you want to restrict a logged user in pages, first, you should define a Role and a Resource model for User model.
Then, you can define a permission class for controlling this restriction. for example my permission class for this purpose is like this,
class IsAuthRolePermission(permissions.BasePermission):
def has_permission(self, request, view):
if request.user.is_authenticated:
try:
obj = Resource.objects.get(name=view.__class__.__name__,
roles__in=request.user.roles.all())
return True
except Resource.DoesNotExist:
return False
else:
return False

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

Custom authorization in Django Rest

I am just a newbie with Django, python. I try to build 1 simple API Collection include CRUD basic, and authentication, authorization.
I have 1 simple Views like:
#api_view(['GET'])
#permission_classes([IsUser])
def get_test(request : Request):
return JsonResponse({"message": "success"}, status = status.HTTP_200_OK)
and IsUser is:
class IsUser(IsAuthenticated):
def has_permission(self, request : Request, view):
token = request.query_params.get('token')
if token:
role = jwt.decode(token.split(" ").__getitem__(1), key="secret_key",algorithms="HS256").get('role')
if role == 'User':
return True
else:
return False
return False
My purpose wants to parse the JWT token and authorization based on that. I wonder don't know if my way is really correct? Can anyone give me some comments as well as documentation to better understand this issue? I don't use any other lib because I want to code by myself at first to understand the flow.
Thanks for helping me.
If you are using the default JWT authentication of DRF then permission class IsAuthenticated will verify the the token for you and instead of specifying token in query parameter specify in headers.
However, if you want to allow a specific type(role) of user to access your endpoint. Then create a subclass of BasePermission class as show in the example.
Then simply add IsAuthenticated in #permission_classes([IsUser & IsAuthenticated]) to make it work.
Again, this would work only if you are using the default Group model.
Here is how you can import it from django.contrib.auth.models import Group
from rest_framework.permissions import BasePermission
USER = 'USER'
class IsUser(BasePermission):
def has_permission(self, request, view, user=None):
if not user:
user = request.user
return USER in [role.name for role in user.groups.filter()]

django rest framework simulate required_permissions

often when i define permissions on my django views I would use something like this
#permission_required('comment.add_thread', raise_exception=True)
def save_comment(request, id=None):
""" """
But, when rest framework, how can I tell the API method to check for comment.add_thread permission before performing the operation?
It all depends how you create your api views, if your view is not generic, then you need to create your own permission like this:
from rest_framework import permissions
class AddCommentPermission(permissions.BasePermission):
def has_permission(self, request, view):
return request.user.has_perm('comment.add_thread')
but if your view has a model property set, then you just simply use DjangoModelPermissions, as documented here:
http://django-rest-framework.org/api-guide/permissions.html#djangomodelpermissions

Controlling Django auth user access to specific object instances

In my Django project, I have various users created by Django's built-in authentication system. Each user can create their own instances of the App model. I would like to restrict user access to objects such that users can only view the instances they have created. To do that I have created this view:
#login_required
def appDetail(request, app_id):
try:
app = App.objects.get(pk=app_id)
# Testing if the currently logged in user is
# the same as the user that created the 'app':
if request.user.id == app.user.user.id:
if request.method == 'POST':
form = AppForm(request.POST, instance=app)
if form.is_valid():
edited_app = form.save()
return HttpResponseRedirect('/thanks/')
else:
form = AppForm(instance=app)
# If 'app' does not belong to logged in user, redirect to 'accessdenied' page:
else:
return HttpResponseRedirect('/accessdenied/')
except LeaveApp.DoesNotExist:
raise Http404
return render(request, 'AppDetail.html', {'form':form})
It works, but I'm wondering if there's a more commonly accepted and/or safe way to do this?
This is called row-level permissions and it's a very common problem. See here for all the apps that solve it.
If that particular test is all you need to do, go for a custom solution like yours (though, since it's boilerplate, it's preferable to move it to a decorator). Otherwise, just use an existing app.
I would put the form submission in a different view and write a custom decorator, which you could also use for similar issues.
I would also return a 404 instead of access denied. You might not want to show users that you are protecting something.
There is a decorator called user_passes_test that restricts access to a view based on if the user passes a certain check
from django.contrib.auth.decorators import login_required, user_passes_test
#login_required
#user_passes_test(lambda user: user.username == app.user.user.id)
MyView(request):
...
You can also add in an optional argument for a url to redirect to in the event they fail the check.
Trying to do this from the admin page is also pretty easy, but takes a few extra steps.
Docs Here

Creating user profile pages in Django

I'm a beginner in Django. I need to setup a website, where each user has a profile page. I've seen django admin. The profile page for users, should store some information which can be edited by the user only. Can anyone point me out how that is possible?. Any tutorial links would be really helpful. Also, are there any modules for django, which can be used for setting up user page.
You would just need to create a view that's available to an authenticated user and return a profile editing form if they're creating a GET request or update the user's profile data if they're creating a POST request.
Most of the work is already done for you because there are generic views for editing models, such as the UpdateView. What you need to expand that with is checking for authenticated users and providing it with the object that you want to provide editing for. That's the view component in the MTV triad that provides the behavior for editing a user's profile--the Profile model will define the user profile and the template will provide the presentation discretely.
So here's some behavior to throw at you as a simple solution:
from django.contrib.auth.decorators import login_required
from django.views.generic.detail import SingleObjectMixin
from django.views.generic import UpdateView
from django.utils.decorators import method_decorator
from myapp.models import Profile
class ProfileObjectMixin(SingleObjectMixin):
"""
Provides views with the current user's profile.
"""
model = Profile
def get_object(self):
"""Return's the current users profile."""
try:
return self.request.user.get_profile()
except Profile.DoesNotExist:
raise NotImplemented(
"What if the user doesn't have an associated profile?")
#method_decorator(login_required)
def dispatch(self, request, *args, **kwargs):
"""Ensures that only authenticated users can access the view."""
klass = ProfileObjectMixin
return super(klass, self).dispatch(request, *args, **kwargs)
class ProfileUpdateView(ProfileObjectMixin, UpdateView):
"""
A view that displays a form for editing a user's profile.
Uses a form dynamically created for the `Profile` model and
the default model's update template.
"""
pass # That's All Folks!
You can
create another Model for storing profile information about user
add AUTH_PROFILE_MODULE='yourprofileapp.ProfileModel' to settings.py
In profile editing view, allow only logged in users to edit their own profiles
example:
#login_required
def edit_profile(request):
'''
edit profile of logged in user i.e request.user
'''
You can also make sure that whenever new user is created the user's profile is also created using django's signals
Read about storing additional information about users from django documentation