I have a new app:
newApp
with 3 users
[admin, user1, user2]
and a login page (not from /admin/). Is it possible to restrict/redirect access to django's Admin login? i want to just get into the admin/ path if the user logged IS and admin, That means it should not work if im not logged nor logged as normal user.
You could write a custom view decorator to check if user is admin, and if not raise a Http404, for example:
def user_is_admin(function):
def wrap(request, *args, **kwargs):
if request.user.is_authenticated and request.user.is_admin:
return function(request, *args, **kwargs)
else:
raise Http404
return wrap
user.is_admin would be a custom implementation, just an example. Now you just need to wrap your views
#user_is_admin
def my_view(request):
...
Im adding this answer because the one provided by Pedro couldnt answer the part that i was trying to figure out, not letting anyone access to the /admin/ path.
I used Django-Decorator-Include to mix it with Pedro's custom decorator
and the result on my urls.py is:
from decorator_include import decorator_include
from django.core.exceptions import PermissionDenied
def user_is_admin(function):
def wrap(request, *args, **kwargs):
if request.user.is_authenticated and request.user.is_staff:
return function(request, *args, **kwargs)
else:
raise PermissionDenied
return wrap
urlpatterns = [
.....
path('admin/', decorator_include([user_is_admin], admin.site.urls)),
.....
]
Related
I am creating an app in django where all the authentication like login signup is done by using an exernal rest api so there is no database involved in it. This is why I am saving logged in user data in request.session now the issue is I don't want to show some of the pages if the user is not inside the sessions, what we usually do back in django apps is use #login_required decorator but now as there is no database so what will be the procedure of not showing those pages to not logged in users. thanks
You can define a decorator that will look for a key in the request.sessions:
# app_name/decorators.py
from django.shortcuts import redirect
from functools import wraps
def session_login_required(function=None, session_key='user'):
def decorator(view_func):
#wraps(view_func)
def f(request, *args, **kwargs):
if session_key in request.session:
return view_func(request, *args, **kwargs)
return redirect('name-of-login-view')
return f
if function is not None:
return decorator(function)
return decorator
here it will thus check if 'user' is a key in the request.session. You can of course change this to a different key.
Then we use the decorator for these views:
# app_name/views.py
from app_name.decorators import session_login_required
#session_login_required
def my_view(request, some, parameters):
# …
I am many views and more than one user type. I want some views that can be seen by specific user types and other users cant see this.
For example, only company see this views and for this i did that like this below:
#login_required
def only_company_can_view(request):
if not Company.objects.filter(owner_id=request.user.id).exists():
return HttpResponse('Permission Denied. Only Company can see this')
# > rest of the logic
return render(request, 'template.html')
and above this working very well and solves my problem but i don't like this. coz, i don't want to write every time for the rest of the views for the company related views.
So i am finding a solution so that i can use decorator or something else that are best practice
Can anyone help me in this case?
You can wrap the logic in a decorator:
from django.core.exceptions import PermissionDenied
from functools import wraps
def requires_company(view):
#wraps(view)
def _view(request, *args, **kwargs):
if not Company.objects.filter(owner_id=request.user.id).exists():
raise PermissionDenied
return view(request, *args, **kwargs)
return _view
Then you use the decorator with:
#login_required
#requires_company
def only_company_can_view(request):
# … rest of the logic …
return render(request, 'template.html')
Currently I use these patterns to log in and out
urlpatterns += patterns("",
(r'^login/$', 'django.contrib.auth.views.login', {'template_name': 'login.html'}),
(r'^logout/$', 'django.contrib.auth.views.logout', {'template_name': 'logout.html'})
)
Inspite of having LOGIN_REDIRECT_URL = '/profile/' in my settings.py, Django does not send me to /profile/ if I want to access /login/ when I'm already logged in...
Can I somehow redirect in the URL patterns of the auth system? I'm reluctant to write a custom view for that.
I use something like this in my urls.py:
from django.contrib.auth.views import login
from django.contrib.auth.decorators import user_passes_test
login_forbidden = user_passes_test(lambda u: u.is_anonymous(), '/')
urlpatterns = patterns('',
url(r'^accounts/login/$', login_forbidden(login), name="login"),
How about riding the Django login view?
Then add this little piece of code in that view:
if request.user.is_authenticated():
# Redirect to profile
If you want to do something else in template itself for the register user:
{% if user.is_authenticated %}
I ended up writing a decorator for such task.
Please take note that I've made it quick & dirty.
from django.conf import settings
from django.shortcuts import redirect
def redirect_if_logged(f=None, redirect_to_url=None):
u"""
Decorator for views that checks that the user is already logged in, redirecting
to certain URL if so.
"""
def _decorator(view_func):
def _wrapped_view(request, *args, **kwargs):
if request.user.is_authenticated():
redirect_url = redirect_to_url
if redirect_to_url is None:
# URL has 'next' param?
redirect_url_1 = request.GET.get('next')
# If not, redirect to referer
redirect_url_2 = request.META.get('HTTP_REFERER')
# If none, redirect to default redirect URL
redirect_url_3 = settings.LOGIN_REDIRECT_URL
redirect_url = redirect_url_1 or redirect_url_2 or redirect_url_3
return redirect(redirect_url, *args, **kwargs)
else:
return view_func(request, *args, **kwargs)
return _wrapped_view
if f is None:
return _decorator
else:
return _decorator(f)
Looking at the source code on Github, the default login view provided by django.contrib.auth will only use LOGIN_REDIRECT_URL if the login form is submitted via a POST request, and it has no next parameter. (The docs mention this too).
The login view doesn't actually do any checking of whether you're authenticated already or not - so an already-authenticated user hitting the page with a standard GET request will see the login form again - just the same as an unauthenticated user.
If you want different behaviour, I'd recommend writing your own login view.
On your projects urls.py file (url_patterns list), add redirect_authenticated_user=True as a parameter in login path like this:
path('admin/', admin.site.urls),
path('login/', auth_views.LoginView.as_view(
template_name='blog_app/login.html',
redirect_authenticated_user=True
), name='login'),
You could use this decorator
def login_excluded(redirect_to):
""" This decorator kicks authenticated users out of a view """
def _method_wrapper(view_method):
def _arguments_wrapper(self, request, *args, **kwargs):
if self.request.user.is_authenticated:
return redirect(redirect_to)
return view_method(request, *args, **kwargs)
return _arguments_wrapper
return _method_wrapper
is it possible, to deny access to django.contrib.auth.views.password_reset for users who are authenticated?
I want to use it, and make it only for not logged in.
I can't figure out how to do that. I have done all things with reset, but all users can enter it:/
UPDATE:
I made it that way:
views.py
def password_remind(request):
from django.contrib.auth.views import password_reset
if request.user.is_authenticated():
return HttpResponseRedirect(reverse('index'))
return password_reset(request, template_name='remind_password.html', post_reset_redirect=reverse('password_remind_sent'))
def password_remind_sent(request):
from django.contrib.auth.views import password_reset_done
if request.user.is_authenticated():
return HttpResponseRedirect(reverse('index'))
return password_reset_done(request, template_name='remind_password.html')
def password_remind_set_new(request, uidb36, token):
from django.contrib.auth.views import password_reset_confirm
if request.user.is_authenticated():
return HttpResponseRedirect(reverse('index'))
return password_reset_confirm(request, template_name='set_new_password.html', post_reset_redirect=reverse('password_remind_changed'), token=token, uidb36=uidb36)
def password_remind_changed(request):
from django.contrib.auth.views import password_reset_complete
if request.user.is_authenticated():
return HttpResponseRedirect(reverse('index'))
return password_reset_complete(request, template_name='set_new_password.html')
urls.py:
url(r'^remind_password/$', 'password_remind', name='password_remind'),
url(r'^remind_password/sent/$', 'password_remind_sent', name='password_remind_sent'),
url(r'^remind_password/set_new/(?P<uidb36>[0-9A-Za-z]+)-(?P<token>.+)/$', 'password_remind_set_new', name='password_remind_set_new'),
url(r'^remind_password/changed/$', 'password_remind_changed', name='password_remind_changed'),
Sure, you can always wrap the function in your own function with some conditions and point your url to your wrapper function instead. (note to pass in whatever arguments you were passing in before...)
from django.contrib.auth.views import password_reset as real_password_reset
def password_reset(request, *args, **kwargs):
if request.user.is_authenticated():
# separate logic, redirect, whatever you wish.
return direct_to_template(request, "my_password_change.html", {})
return real_password_reset(request, *args, **kwargs)
(r'^password_reset/$', password_reset),
I'd make a decorator function that ensures that request.user is not authenticated and if they are, redirects to an 'access denied' page with a 403 code.
Then, in my urls.py, I'd include url patterns for the view(s) I want to lock down (there are about four, I recall) and wrap the method call in that decorator function.
That way, you don't have to hack contrib.auth's views at all - you just wrap your code around it.
But do you really need to stop users accessing the reset password view?
UPDATE:
Example decorator function (untested code!)
from django.http import HttpResponseForbidden
def unauthenticated_users_only(func):
"""
Decorator that checks whether a user
is logged in. 403s if not.
"""
def dec(request, *args, **kwargs):
if not request.user.is_authenticated():
return HttpResponseForbidden()
return func(request, *args, **kwargs)
dec.__doc__ = func.__doc__
return dec
and then in your main urls.py, import all of the auth urls related to the reset flow and import and apply that function for each of them:
url(r'^password/reset/$', unauthenticated_users_only(auth_views.password_reset), name='auth_password_reset'),
How can make Django permission_required decorator not to redirect already logged-in users to login page, but display some message like Insufficient permissions?
Thank you.
A quick and dirty solution would be to write your own decorator to do this. Something like this:
decorator_with_arguments = lambda decorator: lambda *args, **kwargs: lambda func: decorator(func, *args, **kwargs)
#decorator_with_arguments
def custom_permission_required(function, perm):
def _function(request, *args, **kwargs):
if request.user.has_perm(perm):
return function(request, *args, **kwargs)
else:
request.user.message_set.create(message = "What are you doing here?!")
# Return a response or redirect to referrer or some page of your choice
return _function
You can then decorate your view thus:
#custom_permission_required('my_perm')
def my_view(request, *args, **kwargs):
#Do stuff
Since django 1.4 permission_required has a raise_exception parameter that you can set to True to have an unauthorized PermissionDenied exception raised
Eg. to give an exemple on a Class Based View:
from django.contrib.auth.decorators import permission_required
...
class MyView(TemplateView):
#method_decorator(permission_required('can_do_something', raise_exception=True))
def dispatch(self, *args, **kwargs):
return super(MyView, self).dispatch(*args, **kwargs)
Ref:permission_required decorator doc
I'm assuming this question requires two pieces
Users that are already logged in do not get redirected to the login page
Users that are not logged in do not get redirected
#Manoj Govindan's answer nor #Stefano's answer will not work. #Lidor's answer will work, but he has fully re-implemented the permission_required function.
Here is a simpler way:
#login_required
#permission_required('hi there', raise_exception=True)
def blah(request):
pass
With this, if the user is not logged in, they will be redirected. If they are but they don't have permissions, they will be down an error.
I had the same problem but learned about raise_exception parameter at the #permission_required decorator!
this parameter is False by default, but once you pass it the True value, it will automatically redirect the without permission user to the 403.html page! (if there was any 403.html page in the root of your project, otherwise shows the server 403 forbidden page!
read more in Django docs here
#permission_required('app_name.view_model', raise_exception=True)
def some_model_view(request):
...
You can write your own decorator to replace django's permission_required decorator:
from django.utils import six
from django.core.exceptions import PermissionDenied
from django.contrib.auth.decorators import user_passes_test
def permission_required(perm, login_url=None, raise_exception=True):
def check_perms(user):
if isinstance(perm, six.string_types):
perms = (perm, )
else:
perms = perm
if user.has_perms(perms):
return True
if raise_exception and user.pk:
raise PermissionDenied
return False
return user_passes_test(check_perms, login_url=login_url)
And use it the same way:
#permission_required('app.permission')
def view_page(request):
# your view function
Logged in users with no permission will get a 403 forbidden error. Those who are not logged in will be redirected to the login page.