Flask problem: decorators are overwriting each other - flask

I am working on creating a flask application with users that must log in. So I made two decorators, login_required and admin_required to check if the user is logged in and if they are an admin. This is the code:
# decorator so that users must be logged in to access the page.
def login_required(view):
wraps(view)
def log_req(**kwargs):
if session['a_id'] is None:
return redirect(url_for('website.login'))
return view(**kwargs)
return log_req
# decorator so that users must be an admin to access the page.
def admin_required(view):
wraps(view)
def adm_req(**kwargs):
if session['role'] != 'admin':
return redirect(url_for('wesite.portal_home'))
return view(**kwargs)
return adm_req
This is the route:
#website.route('/api/register-user', methods=('POST',))
#login_required
#admin_required
def register_user():
...
However, it gives me this error:
AssertionError: View function mapping is overwriting an existing endpoint function: website.log_req
If I reverse the order of the decorators, then it says that website.adm_req is being overwritten.
Why is that happening, and how can I fix this problem?

I found the problem. wraps(view) should be the decorator #wraps(view). This is the fixed function:
# decorator so that users must be logged in to access the page.
def login_required(view):
#wraps(view)
def log_req(**kwargs):
if session['a_id'] is None:
return redirect(url_for('website.login'))
return view(**kwargs)
return log_req

Related

Django : How can we custom login_required decorator?

I want to write a decorator like the login_required decorator of Django to check the Azure AD authentication and the Django authentication at the same time. If one of the two is not true, it redirects to the login page.
For the authentication, I used the tutorial (https://learn.microsoft.com/en-us/graph/tutorials/python). I do not how to deal with groups and permissions since I use Azure AD authentication. So I take the username and surname from the token that comes from the Azure Authentication and with this two infos, I create an user in the User Django models. I know it is not the best idea, but I can start to play with groups and permissions.
The django authentication is automatic without that the user create it. It is done in the callback function.
def callback(request):
# Make the token request
result = get_token_from_code(request)
#Get the user's profile
user = get_user(result['access_token'])
# Store user
store_user(request, user)
# Get user info
# user attribute like displayName,surname,mail etc. are defined by the
# institute incase you are using single-tenant. You can get these
# attribute by exploring Microsoft graph-explorer.
username = user['displayName']
password = user['surname']
email = user['mail']
try:
# if use already exist
user = User.objects.get(username=username)
except User.DoesNotExist:
# if user does not exist then create a new user
user = User.objects.create_user(username,email,password)
user.save()
user = authenticate(username=username,password=password)
if user is not None:
login(request,user)
messages.success(request,"Success: You were successfully logged in.")
return redirect('home')
return redirect('home')
If I want to check if the user is authenticated by Azure AD. From the tutorial, I should do something like that :
if request.session.get('user').get('is_authenticated') :
But I do not know how to combine with the django authentication to check both. Anyone can help me
Thanks
simplest way would be to use the user_passes_test decorator to make your own function and apply that as a decorator to your views as per the docs
from django.contrib.auth.decorators import user_passes_test
def check_azure(user):
# so something here to check the azure login which should result in True/False
return #theResult of your check
#user_passes_test(check_azure)
def my_view(request):
...
Here is my solution :
from django.shortcuts import redirect
def authenticated_user(view_func) :
def wrapper_func(request, *args, **kwargs):
if request.user.is_authenticated and request.session.get('user').get('is_authenticated') :
return view_func(request, *args, **kwargs)
else :
return redirect('login')
return wrapper_func

How can I disallow the user to go to the register page when he is already logged in? Django 2.1.2

I made an admin panel in django. Which consists of login registration and dashboard for admin but i am facing a problem that:
If suppose a user is logged in to the system, and then the user goes to the url and go to register page using the url for ex. localhost/register when he is already logged in and no need of going to register page to register another user account. I don,t want this to happen.
How can I disallow the user to go to the register page when he is already logged in. How can we resolve this issue? Please help
You can try like this:
def register(request):
if request.user.is_authenticated:
return redirect('/') # or reverse('home_url_name')
else:
....
Update from comments:
You need to override LoginView for adding this functionality in login as well, for example:
from django.contrib.auth import views as auth_views
class CustomLoginView(auth_views.LoginView):
def get(self, request, *args, **kwargs):
if request.user.is_authenticated:
return redirect('/')
return super(CustomLogin, self).get(request, *args, **kwargs)
# URL
path('accounts/login/', CustomLoginView.as_view()),

Django -- Requiring a certain access level for pages

In my Django project, I want to restrict a page depending on if they have an access level of 100, 200, etc. I tried making a wrapper for this like login_required, however I'm not sure how to access the user model. Any advice?
Example:
def required_access(access):
if access <= user.access:
print 'The user can access this page, their access level is greater than or equal to whats required.'
else:
print 'This page cannot be viewed, their access level is too low.'
#login_required
#required_access(300)
def foo(request):
return render(request, 'bar.html', {})
Python decorators have access to the wrapped functions arguments.
With this knowledge we could alter our decorator like so, to have access to the request.user object. This is assuming the first argument to my_view() will always be the request object.
def required_access(access=None):
def wrap(func):
def inner(*args, **kwargs):
if access <= args[0].user.access:
print 'The user can access this page, their access level is greater than or equal to whats required.'
else:
print 'This page cannot be viewed, their access level is too low.'
return func(*args, **kwargs)
return inner
return wrap
#login_required
#required_access(access=300)
def foo(request):
return render(request, 'bar.html', {})
Try this..
https://docs.djangoproject.com/en/1.7/topics/auth/default/#limiting-access-to-logged-in-users-that-pass-a-test
from django.contrib.auth.decorators import user_passes_test
def email_check(user):
return user.email.endswith('#example.com')
#user_passes_test(email_check)
def my_view(request):
[...]

Custom authentication with django?

Because I didn't want to use Django's in-build authentication system (maybe I should do, please tell me if this is the case), I created a simple little auth class:
import random
import hashlib
from myapp import models
class CustomerAuth:
key = 'customer'
def __init__(self, session):
self.session = session
def attempt(self, email_address, password):
password_hash = hashlib.sha1(password).hexdigest()
try:
return models.Customer.objects.get(
email_address=email_address,
password_hash=password_hash)
except models.Customer.DoesNotExist:
return None
def login(self, customer):
self.session[self.key] = customer
def logout(self):
if self.session.has_key(self.key):
self.session[self.key] = None
def is_logged_in(self):
return self.session.has_key(self.key)
and self.session[self.key] != None
def get_active(self):
if self.is_logged_in():
return self.session[self.key]
else:
raise Exception('No user is logged in.')
def redirect_to_login(self):
return HttpResponseRedirect('/login/')
def redirect_from_login(self):
return HttpResponseRedirect('/account/')
The problem is, that when I want to use it to stop unauthorized access, I have to use this code snippet in every single view method:
def example(req):
auth = CustomerAuth(req.session)
if not auth.is_logged_in():
return auth.redirect_to_login()
As you can imagine, this yields fairly ugly and repetitive code. What is a better way of doing this? Should I be using Django's auth framework?
Firstly, yes you should use Django's authentication framework, and build your own custom auth backend.
Secondly, however you do it, you'll need to have something in the views that you want to restrict access to. The best way to do that is via a decorator on the view. Again, Django's built-in framework gives you access to the #login_required decorator, which does exactly what you want.

How can make Django permission_required decorator not to redirect already logged-in users to login page, but display some message

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.