passing arguments between multiple decorators - python

I am using two decorators to decorate a function "add_user", the first one #auth_user authenticates the logged in user via json web token request headers and returns the user's information as an argument "user_info" back to the decorated function add_user. The second decorator #has_permission is supposed to check whether or not the user has permissions on the requested resource. I wanted to pass the user's information returned as user_info by #auth_user to #has_permission but don't know how to do that, here is the code so far:
#has_permission
#auth_user
def add_user(user_info):
"""
do something
"""
return None
The decorators:
def auth_user(f):
#wraps(f)
def wrapper(*args, **kwargs):
data = jwt.decode(request.headers['some-access-token'], some_secret_key)
try:
user_info = User.query.filter(User.id==data['user_id']).first()
except:
return jsonify({"message" : "error"}), 401
return f(user_info, *args, **kwargs)
return wrapper
and the second decorator where I want to access the user_info:
def has_permission(f):
wraps(f)
def wrapper(*args, **kwargs):
# This is where I want to access the user information instead of processing the key again #
# return some new_args ... #
return f(*args, **kwargs)
return wrapper
What is the best way to achieve this?

With some help, I managed to get this working and the main problem was the order/sequence of the decorators. In the question; #has_permission took #auth_user as an argument and #auth_user took the original function add_user as an argument like so:
has_permission(auth_user(add_user(user_info)))
This would mean that when arguments are returned, the outer most decorator #has_permission would return arguments to #auth_user and then to add_user. This meant that #has_permission could not access user_info returned by #auth_user so I modified the original function:
#auth_user
#has_permission
def add_user(user_info):
"""
do something
"""
return None
Now the decorators would be called in the correct order like this:
auth_user(has_permission(add_user(user_info)))
I also changed the way user_info was injected by #auth_user and changed it to kwargs instead of args to access it in #has_permission:
def auth_user(f):
#wraps(f)
def wrapper(*args, **kwargs):
data = jwt.decode(request.headers['some-access-token'], some_secret_key)
try:
user_info = User.query.filter(User.id==data['user_id']).first()
except:
return jsonify({"message" : "error"}), 401
kwargs['user_info'] = user_info
return f(*args, **kwargs)
return wrapper
And finally passing on the user_info to original function:
def has_permission(f):
wraps(f)
def wrapper(*args, **kwargs):
#access user info
user_info = kwargs['user_info']
return f(user_info, *args, **kwargs)
return wrapper
Hope this helps someone!

Related

Multi-Level decorator in Flask

I'm developing Flask application and I have a decorator called login_required which checks if the user is logged in, and sends the current user to the next function.
def login_required(function):
#wraps(function)
def decorator(*args, **kwargs):
token = None
if 'x-access-tokens' in request.headers:
token = request.headers['x-access-tokens']
if not token:
return jsonify({'message': 'a valid token is missing'}), 401
try:
data = jwt.decode(token, app.secret_key)
current_user = User.query.filter_by(username=data['username']).first()
except:
return jsonify({'message': 'token is invalid'}), 401
return function(current_user, *args, **kwargs)
return decorator
So, the callback function is declared like this.
#blueprint.route('/settings', methods=['GET'])
#login_required
def settings(current_user):
# here I check if the current_user is admin or not
...
# function body
Now I want to implement an admin_required decorator which depends on login_required decorator, to be easy using it within functions like this.
#blueprint.route('/settings', methods=['GET'])
#admin_required
def settings(current_user):
...
# function body
How can I achieve this?
So you can create your functionality like this
def decorator1(function):
def fun(*args, **kwargs):
user = function(*args, **kwargs)
print("here")
return user
return fun
def decorator2(function):
def fun(*args, **kwargs):
# firslty i will decorate function with decorator1
decor1 = decorator1(function)
decor1_result = decor1(*args, **kwargs)
# do operation with above result in your case now check if user is admin
print("here in decor2")
return decor1_result + 5
return fun
#decorator2
def test_function(a,b):
return a+b
# Now you want to access a+b and then add c to it
I have included comments for better understanding.
In your case decorator1 will be login_required decorator
and decorator2 will be admin_check decorator. So while creating admin check decorator you can access login_required decorator inside it.

Python decorator class with members

I want to write a class that will have member variables and member functions that will be used as decorators.
class decorator_class:
def __init__(self, baseurl, username,password):
self._baseurl = baseurl
self._username = _username
self._password = _password
def decorator_get(self,arguments):
def inner_function(function):
#wraps(function)
def wrapper(*args, **kwargs):
url = self._url + argument
if len(kwargs) > 0:
url+="?"
argseperator=""
for k,v in kwargs.items():
url+="{}{}={}".format(argseperator,k,v)
argseperator="&"
r = requests.get(url, auth=(self._username, self._password))
if r.status_code != 200:
raise Exception('Failed to GET URL: {}'.format(url))
return function(args[0],json = r.json())
return wrapper
return inner_function
class usedecorator:
def __init__(baseurl, self,user,password):
self.dec = decorator_class(baseurl, self,user,password)
#dec.decorator_get('/path/to/resource1')
def process_output_resource1(self, json):
do_something_with_json
The problem is that __init__ is being called after the class is loaded and at that time dec is undefined.
if I define the decorator_class globally it works, but then there is no way to pass the url, user and password to it at runtime.
Any suggestions?
Your decorator_get > innder_function > wrapper has the userdecorator's self.
Weird sentence but eh.
You have some weird namings, IDK why did you use self as a second argument for instance but, I tried to follow your naming.
def decorator_get(arguments):
def inner_function(function):
#wraps(function)
def wrapper(self, *args, **kwargs):
url = self._base_url + arguments
if len(kwargs) > 0:
url+="?"
argseperator=""
for k,v in kwargs.items():
url+="{}{}={}".format(argseperator,k,v)
argseperator="&"
r = requests.get(url, auth=(self._username, self._password))
if r.status_code != 200:
raise Exception('Failed to GET URL: {}'.format(url))
return function(self, json = r.json())
return wrapper
return inner_function
class usedecorator:
def __init__(self, baseurl,user,password):
self._base_url = baseurl
self._username = user
self._password= password
#decorator_get('/path/to/resource1')
def process_output_resource1(self, json):
do_something_with_json
Indeed -to have a decorator for methods in a class, it must already be defined (i.e. ready to be used) when the method to be decorated is declared: which means it have to be declared either at top-level or inside the class body.
Code inside methods, including __init__, however will only run when an instance is created - and that is the point where the class will get your connection parameters.
If this decorator is being used always in this model, you can turn it into a descriptor: an object which is a class attribute, but which has code (in a method named __get__) that is executed after the instance is created.
This descriptor could then fetch the connection parameters in the instance itself, after it has been created, and prepare way for calling the underlying method.
That will require some reorganization on your code: the object returned by __get__ has to be a callable which will ultimately run your function, but it would not be nice if simply retrieving the method name would trigger the network request - one will expect it to be triggered when the process_output... method is actually called. The __get__ method then should return your inner "wrapper" function, which will have all the needed data for the request from the "instance" attribute Python passes automatically, but for the payload which it gets via kwargs.
class decorator_class:
def __init__(self, path=None):
self.path = None
self.func = None
def __get__(self, instance, owner):
if instance is None:
return self
def bound_to_request(**kwargs):
# retrieves the baseurl, user and password from the host instance:
# build query part of the target URL - instead of your convoluted
# code to build the query string (which will break ont he first special character,
# just pass kwargs as the "params" argument)
response = requests.get(instance._base_url, auth=(
instance.user, instance.password), params=kwargs)
# error treatment code
#...
return self.func(response.json())
return bound_to_request
def __call__(self, arg):
# create a new instance of this class on each stage:
# first anotate the API path, on the second call annotate the actual method
new_inst = type(self)()
if not self.path:
if not isinstance(arg, str):
raise TypeError("Expecting an API path for this method")
new_inst.path = arg
else:
if not callable(arg):
raise TypeError("Expecting a target method to be decorated")
new_inst.func = wraps(arg)
return new_inst
def __repr__(self):
return f"{self.func.__name__!r} method bound to retrieve data from {self.path!r}"
class use_decorator:
dec = decorator_class()
def __init__(self=, baseurl, user, password):
# the decorator assumes these to be set as instance attributes
self.baseurl = baseurl
self.user = user
self.password = password
# <- the call passing the path returns an instance of
# the decorator with the path set. it is use as an
# decorator is called again, and on this second call, the decorated method is set.
#dec.decorator_get('/path/to/resource1')
def process_output_resource1(self, json):
# do_something_with_json
...
In time, re-reading your opening paragraph, I see you intended to have more than one decorator inside your original class, probably others intended for "POST" and other HTTP requests: most important thing, the __get__ name here has nothing to do with HTTP: it is a fixed method name in the Python spec which is called automatically by the language when one will retrieve your method from an instance of use_decorator. That is, when there is code: my_instance.process_output_resource1(...), the __get__ method of the descriptor is called. Whatever it returned is then called.
For enabling the same decorator to use POST and other HTTP methods, I suggest you to have as a first parameter when annotating the path for each method, and then simply call the appropriate requests method by checking self.method inside the bound_to_request function.
I think you're going too far with the decorator approach. Let's break this down into a single question: What is the actual shared state here that you need a class for? To me, it looks like just the baseurl, user, and password. So let's just use those directly without a decorator:
from requests import Session
from requests.auth import HTTPBasicAuth
class UseDecorator: # this isn't a good name, but we will keep it temporarily
def __init__(self, baseurl, user, password):
self.baseurl = baseurl
self.session = Session()
# we've now bound the authentication to the session
self.session.auth = HTTPBasicAuth(user, password)
# now let's just bind a uri argument to a function to simply
# send a request
def send_request(self, uri, *args, **kwargs):
url = self.baseurl + uri
# you don't need to manually inject parameters, just use
# the params kwarg
r = self.session.get(url, params=kwargs)
# this will check the response code for you and even handle
# a redirect, which your 200 check will fail on
r.raise_for_status()
return r.json()
# then just handle each individual path
def path_1(self, *args, **kwargs):
data = self.send_request('/path/1', *args, **kwargs)
# process data
def path_2(self, *args, **kwargs):
data = self.send_request('/path/2', *args, **kwargs)
# process data
Because we're leveraging the machinery offered to us by requests, most of your decorator is simplified, and we can boil it down to a simple function call for each path

Use decorators check user login in flask?

I defined a check user method:
from functools import wraps
def check_user(func):
#wraps(func)
def wrapper(*args, **kwargs):
if session['logged_in']:
return func(*args, **kwargs)
else:
return 'Log in'
return wrapper
#app.route('/test')
#check_user
def test():
return "Hello"
It does not work. how can I correct it?
It seems you don't know how to create decorators in python. There are many helpful answers on this question: How can I make a chain of function decorators in Python?
Below is how you can create a decorator that checks if a user is logged in.
from functools import wraps
def checkuser(func):
"""Checks whether user is logged in or raises error 401."""
#wraps(func)
def wrapper(*args, **kwargs):
if not g.user:
abort(401)
return func(*args, **kwargs)
return wrapper
The decorator above will raise a 401 error if a user is not logged in. It will return the view function otherwise.

Class Based View and decorators

I have a FormView for displaying form, and code goes on like this:
class AddProject(FormView):
template_name = "project/add_project.html"
#method_decorator(check_user_type)
def dispatch(self,request, *args, **kwargs):
return super(AddProject,self).dispatch(request, *args, **kwargs)
def get_form_class(self):
return AddProjectForm
def form_valid(self,form):
#do validation, return response
the decorator check_user_type is like this:
def check_user_type(func):
def wrapped_func(request, *args, **kwargs):
kwargs['invalid_user'] = True
return func(request,*args, **kwargs)
return wrapped_func
In my decorator I want To make sure that only certain type of user get to see the form, i.e if request.user.Iam == 'Architect' or request.user.Iam == 'Interior Designer' only see the form and others see a message "Only architects/Interior Designer get to upload photos".For this i want to insert a variable 'invalid_user' to be passed along, depending on which i display the form or the message.
Problem is I am unable to pass the variable :( alongwith it .. and a doubt.. if i have correctly devised the idea to do so.. ?
If I understand you correctly, you want to pass this argument to check_user_type decorator, right? Then you need to nest another function in your decorator, using closure to set variables inside it. Something like
def check_user_type(parameter):
def check_user_type_inner(func):
def wrapped_func(request, *args, **kwargs):
... # parameter here depends on argument passed to most outer function
return func(request,*args, **kwargs)
return wrapped_func
return check_user_type_inner
then parameter is available inside scopes of both inner functions.

Having trouble making a custom django view decorator (with args)

So I've read all the similar questions and copied what they wrote but I still keep having issues. So I want something like this
# Yes, I know django has one but I want to make my own
#rate_limit(seconds=10)
myview(request, somearg, *args, **kwargs):
# Return a response
...
def rate_limit(seconds=10):
def decorator(view):
def wrapper(request, *args, **kwargs):
# Do some stuff
return view(request, *args, **kwargs)
return wrapper
return decorator
When I run it I get the error
decorator() got an unexpected keyword argument 'somearg'
So I append decorator to take in args and kwargs and get this error
# New decorator signature
def decorator(view, *args, **kwargs)
and error
'function' object has no attribute 'status_code'
edit:
So the solution was to use. Thanks Martijn Pieters!
#rate_limit()
instead of
#rate_limit
Your first attempt works just fine, but you probably forgot to call the rate_limit() decorator factory.
In other words, your first error occurs if you do this:
#rate_limit
def myview(request, somearg, *args, **kwargs):
instead of:
#rate_limit(seconds=10)
def myview(request, somearg, *args, **kwargs):
You also really want to use functools.wraps() on decorators used in Django, especially if you want to mix this with other Django decorators such as csrf_exempt:
from functools import wraps
def rate_limit(seconds=10):
def decorator(view):
#wraps(view)
def wrapper(request, *args, **kwargs):
# Do some stuff
return view(request, *args, **kwargs)
return wrapper
return decorator
This ensures that any attributes set on the to-be-wrapped function are copied over correctly to the wrapper.

Categories

Resources