I have a mutation that has a decorator that checks the permissions (scopes) from the user token before s/he goes inside it. The decorator gets the input parameter from the mutate method and extracts the token and verify if the user has one of the allowed scopes.
The unit tests don't succeed if I don't remove or mock the requires_scope part from the code. The problem is that I don't know exactly what I have to mock to succeed in the unit tests. Should I mock the input itself? The token? The return of the requires_scopes decorator?
mutations.py
class MyMutation(graphene.Mutation):
success = graphene.Boolean()
class Arguments:
input = graphene.Argument(IdInput, required=True)
#classmethod
#requires_scopes(['app:first_test_permission', 'app:second_test_permission'])
def mutate(cls, root, info, input):
pass
decorators.py
def get_access_token_from_header(request):
"""
Obtains the Access Token from the Authorization Header
"""
header = request.context.META.get('HTTP_AUTHORIZATION', None)
header = header.split()
token = header[1]
return token
def requires_scopes(scopes: list):
"""
Determines if the required scope is present in the Access Token
Args:
scopes (list): The scopes required to access the resource
"""
def require_scopes(f):
#wraps(f)
def decorated(*args, **kwargs):
token = get_access_token_from_header(args[2])
decoded = jwt.decode(token, verify=False)
if decoded.get("scope"):
token_scopes = set(decoded["scope"].split())
required_scopes = set(scopes)
if required_scopes.intersection(token_scopes):
return f(*args, **kwargs)
raise Exception({'message': 'You don\'t have access to this resource'})
return decorated
return require_scopes
I mocked the get_access_token_from_header function:
MY_TOKEN = 'mytoken'
#patch('app.decorators.get_access_token_from_header', return_value=MY_TOKEN)
def test_my_mutation_successfully(self, get_access_token_from_header_mocker):
pass
Related
Is there a way to get the returned value of my original function when using more that one decorator?
Here are my decorators:
def format_print(text):
def wrapper():
text_wrap = """
**********************************************************
**********************************************************
"""
print(text_wrap)
text()
print(text_wrap)
return wrapper
def role_required(role):
def access_control(view):
credentials = ['user', 'supervisor']
def wrapper(*args, **kwargs):
requested_view = view()
if role in credentials:
print('access granted')
return requested_view
else:
print('access denied')
return '/home'
return wrapper
return access_control
When I run the function below with the #role_required I get what I was expecting:
#role_required('user')
def admin_page():
print('attempting to access admin page')
return '/admin_page'
x = admin_page()
print(x)
Returns:
attempting to access admin page
access granted
/admin_page
but when I uncomment the second decorator, admin_page() returns None
**********************************************************
**********************************************************
attempting to access admin page
access granted
**********************************************************
**********************************************************
None
Could someone maybe tell me where I am going wrong and how I would get the returned value from a function with multiple decorators.
Many Thanks
This is because you actually need do this:
def format_print(text):
def wrapper():
text_wrap = """
**********************************************************
**********************************************************
"""
print(text_wrap)
result = text()
print(text_wrap)
return result
return wrapper
Otherwise your wrapper will always return None.
I'm working on flask RESTful application that uses auth0 for authentication and authorization. Then, I wrote a decorator that validates the token and extract user's id from it. My goal is use that id, extracted from token, to used inside the decorated function, and throw an exception if user's id from token and from URL parameter doesn't match. This is aimed to avoid users to change data of another user, with his own token. I'm not sure this is the best practice for a RESTful app, but seems to be needed in my case.
That said, I trying to figure out the best approach pass the user's id from token, to the decorated funcion:
Something like this:
def authorization():
def inner(func):
def wrapper(*args, **kwargs):
try:
"""
token validation stuff
...
"""
wrapper.user_id = token_payload['user_id']
except Exception:
return {"success": False}, 500
return func(*args, **kwargs)
return wrapper
return inner
#authorization()
#app.route('/test', methods=['GET'])
def some_function():
return jsonify({
'success': True,
'user_id': some_function.user_id
})
As you can see, I setting the user_id field to the wrapper function, which seems to not be the best way, to do it. Is there any different approach to this situation? maybe using Flask resources?
You can skip one level of wrapping in your decorator since you're not giving it any parameters.
Also, I'd just pass the extracted id into the wrapped function directly instead of setting an attribute.
And finally, you should add the auth decorater innermost, since the decorated function is what you want to register with Flask.
Extra: use functools.wraps to update the signature of your wrapped function, to make introspection and debugging easier.
Thus:
from functools import wraps
def authorized(func):
#wraps(func)
def wrapper(*args, **kw):
try:
# token stuff
user_id = token_payload['user_id']
except:
return jsonify({"success": False}), 500
return func(user_id, *args, **kw)
return wrapper
#app.route('/test', methods=['GET'])
#authorized
def some_function(user_id):
return jsonify({
'success': True,
'user_id': user_id
})
Now every function you decorate with #authorized will need to have user_id as their first parameter, and everything should work as you expect.
I'm using Falcon, I need pass variable from middleware to resource, how can I do that?
main.py
app = falcon.API(middleware=[
AuthMiddleware()
])
app.add_route('/', Resource())
and auth
class AuthMiddleware(object):
def process_request(self, req, resp):
self.vvv = True
and resource
class Resource(object):
def __init__(self):
self.vvv = False
def on_get(self, req, resp):
logging.info(self.vvv) #vvv is always False
why self.vvv is always false? I have changed it in middleware to true.
First of all you are confussing what self means. Self affects only to the instance of the class, is a way of adding attributes to your class, therefore your self.vvv in AuthMiddleware is a complete different attribute from your self.vvv in your class Resource.
Second, you do not need to know anything from AuthMiddleware in your resource, thats why you want to use middleware. Middleware is a way to execute logic after or before each request. You need to implement your middleware so it raises Falcon exceptions or either modifies your request or response.
For example, if you don't authorize a request, you must raise an exception like this:
class AuthMiddleware(object):
def process_request(self, req, resp):
token = req.get_header('Authorization')
challenges = ['Token type="Fernet"']
if token is None:
description = ('Please provide an auth token '
'as part of the request.')
raise falcon.HTTPUnauthorized('Auth token required',
description,
challenges,
href='http://docs.example.com/auth')
if not self._token_is_valid(token):
description = ('The provided auth token is not valid. '
'Please request a new token and try again.')
raise falcon.HTTPUnauthorized('Authentication required',
description,
challenges,
href='http://docs.example.com/auth')
def _token_is_valid(self, token):
return True # Suuuuuure it's valid...
Check Falcon page examples.
From https://falcon.readthedocs.io/en/stable/api/middleware.html:
In order to pass data from a middleware function to a resource function use the req.context and resp.context objects. These context objects are intended to hold request and response data specific to your app as it passes through the framework.
class AuthMiddleware(object):
def process_request(self, req, resp):
# self.vvv = True # -
req.context.vvv = True # +
class Resource(object):
# def __init__(self): # -
# self.vvv = False # -
def on_get(self, req, resp):
# logging.info(self.vvv) # -
logging.info(req.context.vvv) # +
You should not use the attributes on middleware and resource instances for your request data. Since you only instantiate them once, modifying their attributes is generally not thread-safe.
I want my authorization decorator to be able to pass a custom user object to the view that it decorates.
Currently, I am having the decorator set an attribute on flask.g to do this. Is this acceptable use of flask.g or is there a better way?
My code looks something like this:
def auth(f):
#wraps(f):
def decorated(*args, **kwargs):
user = getUserObj(request.headers.get('user'), request.headers.get('pass'))
flask.g.user = user
return f(*args, **kwargs)
return decorated
And then the view is:
#api.route('/info')
#auth
def info():
flask.g.user # this contains my user object now
I'm trying to implement a decorator that authenticates the user's token before granting access to a function. My current implementation is kind of wonky in that I need to do two queries since I can't get locals in the decorator. Is there a better way to do this?
def require_auth(func):
print 'require_auth'
#wraps(func)
def inner():
if 'token' in request.json:
token = request.json['token']
session = Session()
for instance in session.query(SQLTypes.User).filter(SQLTypes.User.token==token):
auth_user = instance.username
try:
auth_user
print 'authenticated!'
except NameError:
abort(401)
else:
abort(401)
return func()
return inner
#app.route('/st/projects', methods=['POST'])
#require_auth
def post_project():
session = Session()
for instance in session.query(SQLTypes.User).filter(SQLTypes.User.token==request.json['token']):
auth_user = instance.username
# do something with auth_user
session.close()
You can store your authenticated user in flask.g:
from flask import g
# ...
def require_auth(func):
# ...
for instance in session.query(SQLTypes.User).filter(SQLTypes.User.token==token):
g.auth_user = instance.username
try:
g.auth_user
print 'authenticated!'
except AttributeError:
abort(401)
# ...
Then in your view function you can access the user as g.auth_user.