In Django, restrict user view using class based views - django

I have this url pattern:
path("user/<int:pk>", MyAccountView.as_view(), name='my_account'),
And this view:
class MyAccountView(DetailView):
model = CustomUser
When the user is logged Django redirect to that URL.
The problem is that any user can access other users.
For example, if the logged user has pk 25, he can access the view of user with pk 26 by writing in the browser url box:
localhost:8000/user/26
I want that each user can access to his user page only, so if user with pk 25 try to access the url with pk 26, the access should be denied.
Can you point me in some direction of how this is done? The Django documentation is very confusing in this respect.
Thanks.

You need to override the get method of DetailView
from django.core.exceptions import PermissionDenied
from django.contrib.auth.mixins import LoginRequiredMixin
class MyAccountView(LoginRequiredMixin, DetailView):
model = CustomUser
def get(self, request, pk):
if request.user.pk != pk:
raise PermissionDenied()
self.object = self.get_object()
context = self.get_context_data(object=self.object)
return self.render_to_response(context)

Easy !
First change the view path from user/<int:pk>/ to user/
Link the view to the current user, DetailView won't work because it heavily relies on either pk or slug and we won't be using none of them, so you'll have to write a new view. (Example using FBV because i do not use CBV)
# views.py
from django.contrib.auth.decorators import login_required
# redirects to login page if the user is not authenticated
#login_required(login_url='/example url you want redirect/')
def get_user_profile(request):
context = dict(user=request.user)
return render(request, "template.html", context)
And that's it, any user visiting /user/ will only see their account/profile.

Related

how do I make sure a particular user is the one logged in?

so I have some code which creates a user but how do I make sure every url that the user goes to will be in his/her own user session such that request.user will be the user I created?
def liked(request):
try:
sp = Spotify(auth_manager=oauth)
liked = sp.current_user_saved_tracks(limit=30)['items']
spotify_user = sp.current_user()
user__ , created =
User.objects.get_or_create(username=spotify_user['uri'],
first_name=spotify_user["display_name"])
userprofile = UserProfile.objects.get_or_create(user=user__)[0]
i have other views i want only accessible to the user created or gotten from the get_or_create, i reckon I'd have to use the #login_required decorator but then again I do not know what constitutes this "login" with respect to the user I created. How do I do ensure that user is the logged in user?
Django's default MIDDLEWARE takes care of this for you.
In every request object or self.request there is a user object.
Django checks the session via the cookie that the browser sends with every request, and puts the user object in the current request.
An example in a Class based view:
class ExampleView(TemplateView):
template = 'example.html'
def get_context_data(self, **kwargs):
context = super(ExampleView, self).get_context_data(**kwargs)
user = self.request.user
users_data = ExampleData.objects.filter(user=user)
Once a user is logged in, every other page is put into the session. Just log that user in first.
If you want to check if the user is logged in
In class based views;
if self.request.user.is_authenticated
In function based view
if request.user.is_authenticated
Ok as i understand you need to limit some users to access some pages and not for others .. i suggest to use permissions way
add permission to any Model you Have Like this
then when you create your special user do this to assign this permission to him like that : myuser.user_permissions.add('can_view_all_clients', 'another_perm', ...)
For Function Based Views :
from django.contrib.auth.decorators import permission_required
#permission_required("YourModel.can_view_all_clients")
def your_special_view(request):
return 'your view'
For Class Based View :
from django.contrib.auth.mixins import PermissionRequiredMixin
from django.views.generic import DetailView
class ModelDetailView(PermissionRequiredMixin, DetailView):
template_name = "model_detail.html"
model = Model
permission_required = "can_view_all_clients"

How to restrict website users from seeing other user profiles?

I created some users for my website in Django and i want each user to access only his own profile page .
The template for the user profile page is fetched through a CBV Detail View called UserDetailView attached to a URL containing the user's and the page is loading only after authentication .( user logged in). So far so good.
urls.py:
from django.conf.urls import url
from django.urls import path
from basicapp import views
from django.contrib.auth.decorators import login_required
app_name='basicapp'
urlpatterns = [
url(r'^$',views.index,name='index'),
url(r'^user_list/',views.UserView.as_view(),name='user_list'),
url(r'^course_list/',views.CourseView.as_view(),name='course_list'),
url(r'^user_detail/(?P<pk>[-\w]+)/$',views.UserDetailView.as_view(),name='user_detail'),
]
The problem is after I login and get the user detail page : If I manually change the <pk> in the URL I get other user profile pages loaded. I don't want that to happen .
For ex, the URL for the logged in user profile is :
http://127.0.0.1:8000/basicapp/user_detail/1/
With the user already logged in i manually change the URL to :
http://127.0.0.1:8000/basicapp/user_detail/2/
and it works. It should retrict me or show me an error message
I tried using LoginRequiredMixin
views.py:
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
from django.utils.decorators import method_decorator
class UserDetailView(LoginRequiredMixin,DetailView):
context_object_name='user_detail'
model=models.User
template_name='basicapp/user_detail.html'
raise_exception = True # Raise exception when no access instead of redirect
permission_denied_message = "This page dows not exist."
and I also tried usingmethod_decorator :
#method_decorator(login_required)
class UserDetailView(LoginRequiredMixin,DetailView):
context_object_name='user_detail'
model=models.User
template_name='basicapp/user_detail.html'
raise_exception = True # Raise exception when no access instead of redirect
permission_denied_message = "This page dows not exist."
but it doesn't seem to work . I restarted the server.
Any ideas what i am doing wrong?
The LoginRequiredMixin will ensure that you can only see the page if you are logged in, but that does not mean you have to be that user.
However if you can only see your own profile, it does not make much sense to add a primary key in the url anyway, you can just define the url as:
url(r'^user_detail/$', views.UserDetailView.as_view(), name='user_detail'),
In the view you then return the logged in user for the .get_object() method [Django-doc]:
class UserDetailView(LoginRequiredMixin,DetailView):
context_object_name='user_detail'
model=models.User
template_name='basicapp/user_detail.html'
def get_object(self, *args, **kwargs):
return self.request.user
Or you can restrict users by filtering the queryset:
path('^user_detail/<int:pk>/', views.UserDetailView.as_view(), name='user_detail'),
class UserDetailView(LoginRequiredMixin,DetailView):
context_object_name='user_detail'
model=models.User
template_name='basicapp/user_detail.html'
def get_queryset(self, *args, **kwargs):
qs = super().get_queryset(*args, **kwargs)
if not self.request.user.is_superuser:
qs = qs.filter(pk=self.request.user.pk)
return qs

One login form for two types of accounts and LoginRequiredMixin in django

I have one LoginForm for two types of users, until here all works fine. Issue comes when I do LoginRequiredMixin for any view, I dont know why but is redirecting to the success_url of the login form. Here is my LoginForm:
form_valid method redirect the users depends of the perms he has.
class LoginView(FormView):
form_class = AuthenticationForm
template_name = 'login.html'
def dispatch(self,request,*args,**kwargs):
if request.user.is_authenticated() and request.user.has_perm('users.is_contribuyente'):
return HttpResponseRedirect('/solicitudes-enviadas')
if request.user.is_authenticated() and request.user.has_perm('users.is_professional'):
return HttpResponseRedirect('/panel/inicio')
return super(LoginView,self).dispatch(request,*args,**kwargs)
def form_valid(self,form):
login(self.request,form.get_user())
if self.request.user.has_perm('users.is_contribuyente'):
return redirect('/solicitudes-enviadas')
if self.request.user.has_perm('users.is_professional'):
return redirect('/panel/inicio')
Here my view with a LoginRequiredMixin:
class SendQuoteView(LoginRequiredMixin,VerifiedUserMixin,FormMixin,DetailView):
login_url = '/entrar/'
model = Request
form_class = SendQuoteForm
template_name = 'send_quote.html'
Let's say this is the URL when an not authenticated user is trying to access: http://127.0.0.1:8000/entrar/?next=/panel/detalle-solicitud/declaracion-de-renta. And lets says the user has perm of "is_professional" It supose that have to redirect to /panel/detalle-solicitud/declaracion-de-renta but instead of that is redirecting to /panel/inicio/, the URL of the instrucion of form_valid
New form_valid method only for test to getting next variable:
def form_valid(self, form):
#login(self.request, form.get_user())
next = self.request.GET('next')
print next
return super(LoginView, self).form_valid(form)
How can I achieve that respect the URL of the LoginRequiredMixin?
There is nothing in Django that magically handles ?next=... in the url. You have to fetch the value from request.GET. Note that you should not trust the value from the user - Django has an is_safe_url method that you can use.
from django.http.utils import is_safe_url
next_url = self.request.GET.get('next')
if next_url and is_safe_url(next_url, self.request.get_host()):
# redirect to next
else:
# redirect to fallback
You might find it easier to use the built in login view to handle logins. You can then create a custom view for the LOGIN_REDIRECT_URL setting, and handle the permissions-based redirects there.

Restrict access to non-api view in Django Rest Framework

I'm building a website with a REST API backend using Django REST Framework and token based authentication from djoser.
The site has user pages that are editable through an admin page (e.g. <page-url>/admin)
and they should not be accessible unless you are the owner. What is the best way to restric acces to non-api views when request.user is not available?
There are two simple ways to restrict access for annonymous users:
Just add a rule for non request.user access for a view:
def admin_view(token):
if not Token.objects.get(token=token):
return redirect('%s' % (settings.LOGIN_URL))
Add a decorator for your admin views:
from django.contrib.auth.decorators import login_required
#login_required(login_url='/accounts/login/')
def admin_view(view):
...
In this case, you have to update the login_required mixin in the base class or edit the default auth/permissions middleware, but it depends of the behaviour of your actual auth system.
I am using decorators to restrict access for anonymous users.
from django.contrib.auth.decorators import login_required
urlpatterns = patterns(
url(r'^page_url/$', login_required(MyView.as_view())),
)
Also for translation I modified the TemplateView. You can create a TemplateView class to restrict access for static pages.
class TemplateView(TemplateResponseMixin, ContextMixin, View):
"""
A view that renders a template. This view will also pass into the context
any keyword arguments passed by the url conf.
"""
def get(self, request, **kwargs):
try:
current_user = request.user
if current_user.is_authenticated() and (bool(request.path_info == '/') or bool(request.path_info == '/login/')):
return HttpResponseRedirect('/dashboard')
current_language = current_user.language_code
translation.activate(current_language)
context = self.get_context_data(**kwargs)
return self.render_to_response(context)
except:
context = self.get_context_data(**kwargs)
return self.render_to_response(context)

Django - allauth - Prevent an unauthenticated user to view pages meant for authenticated users

I am trying to use Django with allauth trying to create a login page.
I am able to login and redirect the user to LOGIN_REDIRECT_URL (= "/users/{id}/mypage") page successfully. I have a separate view for /users/{id}/mypage called the UserHomePageView as defined in views.py:
from django.views.generic import TemplateView
class UserHomePageView(TemplateView):
template_name = "user_homepage.html"
I want a security measure to prevent anyone (basically non-loggedin-users) to navigating to "/users/{id}/mypage" (e.g /users/5/mypage). e.i If unauthenticated user tries to navigate to /users/5/mypage, he/she should get redirected to signin page/homepage.
Basically, how do i present a unauthenticated user to view pages that are meant for authenticated users (I don't want to use template tags users.authenitcated, but wish to override the TemplateView)
Another solution which is generally used is to decorate the dispatch method with the login_required decorator:
from django.utils.decorators import method_decorator
from django.contrib.auth.decorators import login_required
class UserHomePageView(TemplateView):
template_name = "user_homepage.html"
#method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super(UserHomePageView, self).dispatch(*args, **kwargs)
This has the advantage that it works with all methods (get, post, put, etc.), and that using the login_required decorator is more DRY than explicitly checking request.user.is_authenticated and redirecting.
I found the answer...
By overriding get() and using self.request.user.is_authenticated()
class UserHomePageView(TemplateView):
template_name = "user_homepage.html"
redirect_field_name = "next"
def get(self, request, *args, **kwargs):
'''
Overriding TemplateView.get() in order to
prevent unauthorized user to view UserHomePageView
'''
# Redirect to Homepage if user is not signed in
if not self.request.user.is_authenticated():
return redirect(self.get_redirect_url())
# Process normally if User is signed in
context = self.get_context_data(**kwargs)
return self.render_to_response(context)