Trouble understanding decorator - python

I want to check if the user is authenticated before he/she can access the method, so I wrote a decorator named authorize but only the decorator code is executed and even if the user is authenticated, the method is not called after it.
here are the method and the decorator codes:
#authorize
def post(self, **kw):
# store data in database after authentication done using #authorize
def authorize(f):
def wrapper(*args, **kwargs):
secret_key = config.get('auth_secret_key')
auth_message = config.get('auth_message')
if 'HTTP_TOKEN' not in request.environ:
abort(401, detail='Authentication failed', passthrough='json')
gibberish = request.environ['HTTP_TOKEN']
if triple_des(secret_key).decrypt(gibberish, padmode=2).decode() != auth_message:
abort(401, detail='Authentication failed', passthrough='json')
return wrapper
If user has authentication problem, 401 is raised and request is aborted but if he is authenticated, the post method is not called. BTW, its my first time writing decorator so I might be completely wrong. Thanks for any answers

You need to actually call the function within your wrapper.
f(*args, **kwargs)

Related

user_passes_test: raise exception if user does not pass

I am trying to define a decorator to check if the user has admin certain privileges:
def admin_required(function=None, redirect_field_name=REDIRECT_FIELD_NAME, login_url=None):
actual_decorator = user_passes_test(
lambda u: u.is_staff and u.is_authenticated() and not Association.objects.filter(admins=u).exists(),
login_url=login_url,
redirect_field_name=redirect_field_name
)
if function:
return actual_decorator(function)
return actual_decorator
The aim is to use this throught the views. Particularly, I am using it in a class-based view:
class MyCBV(CreateView):
#method_decorator(admin_required)
def dispatch(self, request, *args, **kwargs):
return super(MyCBV, self).dispatch(request, *args, **kwargs)
The problem is that this view is loaded via AJAX, so the redirect doesn't happen. Also, the HTTP status the view returns is success even when the user authentication fails, so the client (JS) has no way of telling when the action really succeeded or not.
I usually have trouble understanding decorators and Django authentication, so my question is: how can I raise an exception (preferably the PermissionDenied exception) when the authentication decoration function fails?
In Django 1.9+, you can use the UserPassesTestMixin instead of a decorator, and set raise_exception to True.
Since you are using Django 1.4, which is insecure and obsolete, you won't be able to do this. There isn't an option to make user_passes_test raise PermissionDenied rather than redirect. You could either try to detect the redirect in your JavaScript, or look at the user_passes_test source code and implement something similar that returns the response you want.

Cron job fails accessing url with admin_required decorator

According to the documentation Cron jobs should be allowed to access admin protected views. However I get a 302 error if I have the #admin_required decorator on the GET method.
In app.yaml I have defined this:
- url: /generator
script: run.news.app
login: admin
the view:
class GeneratorView(MethodView):
#admin_required
def get(self):
return 'success', 200
urls.py
app.add_url_rule('/generator', 'generator', view_func=GeneratorView.as_view('generator'))
cron job:
cron:
- description: Scrape every 3 hours
url: /generator
schedule: every 3 hours synchronized
decorator:
def admin_required(func):
"""Requires App Engine admin credentials"""
#wraps(func)
def decorated_view(*args, **kwargs):
if users.get_current_user():
if not users.is_current_user_admin():
abort(401) # Unauthorized
return func(*args, **kwargs)
return redirect(users.create_login_url(request.url))
return decorated_view
the funny part is, when I remove the admin_required decorator, the url is still admin-only protected because of login: admin in app.yaml.
However my unit test fails the authorization check because of the missing decorator.
def test_generator_fails_as_normal_user(self):
self.setCurrentUser(u'john#example.com', u'123')
rv = self.client.get('/generator')
self.assertEqual(rv.status_code, 401)
AssertionError: 200 != 401
If I put the decorator back in, the unit test passes and cron job fails. Any suggestions?
The unit test's self.client.get no doubt doesn't go back all the way to app.yaml for routing -- so it's not surprising that, if you remove the app-level check you do in the decorator, it lets non-admin users through.
The real issue however is that the decorator is not finding anybody "logged in" when it's cron that's hitting that URL. This is hinted at (though it surely should be more clearly/explicitly documented!) at https://cloud.google.com/appengine/docs/python/config/cron#Python_app_yaml_Securing_URLs_for_cron :
Note: While cron jobs can use URL paths restricted with login: admin,
they cannot use URL paths restricted with login: required.
This indicates that the serving infrastructure does not validate cron requests by checking the currently logged-in user as it would find none. Rather, it relies on a header in the request:
Requests from the Cron Service will also contain a HTTP header:
X-AppEngine-Cron: true
The X-AppEngine-Cron header is set internally by Google App Engine. If
your request handler finds this header it can trust that the request
is a cron request. If the header is present in an external user
request to your app, it is stripped, except for requests from logged
in administrators of the application, who are allowed to set the
header for testing purposes.
So, your decorator must examine the headers at self.request -- if it finds X-AppEngine-Cron: true, it must let the request through, else it can go on to perform the checks you're doing now.
I'm not quite sure how you should best get at the request's header in your chosen web framework, which you don't mention, but if it was e.g webapp2 then something like:
#wraps(func)
def decorated_view(self, *args, **kwargs):
if self.request.headers.get('X-AppEngine-Cron') == 'true':
return func(self, *args, **kwargs)
# continue here with the other checks you do now
should do the trick.
The flask docs seem to say that you can't decorate your methods like that:
Decorating Views
Since the view class itself is not the view
function that is added to the routing system it does not make much
sense to decorate the class itself. Instead you either have to
decorate the return value of as_view() by hand:
def user_required(f):
"""Checks whether user is logged in or raises error 401."""
def decorator(*args, **kwargs):
if not g.user:
abort(401)
return f(*args, **kwargs)
return decorator
view = user_required(UserAPI.as_view('users'))
app.add_url_rule('/users/', view_func=view)
Starting with Flask 0.8 there is also an alternative way where you can
specify a list of decorators to apply in the class declaration:
class UserAPI(MethodView):
decorators = [user_required]
Due to the implicit self from the caller’s perspective you cannot use
regular view decorators on the individual methods of the view however,
keep this in mind.
I don't understand the reasoning, though.

User authentication best practices

I have an app on GAE that checks if an administrator is logged in before it calls any webpage. I have tried various methods to manage the login process.
Q1 - What am I doing wrong with my decorator in example two?
Q2 - Does one normally do this check on the post function too?
Before I used an if statement in each get function. The problem is that I would repeat this if statement over and over in each function.
class IncomePage(webapp2.RequestHandler):
def get(self):
if users.is_current_user_admin():
self.response.write('My Webpage')
else:
self.response.write('Please Login')
Then I tried to make a decorator do that for me. It didn't work so what am I doing wrong.
def check(func):
if users.is_current_user_admin():
return func
else:
response.write('Please Login') ### Doesn't work
class IncomePage(webapp2.RequestHandler):
#check
def get(self):
self.response.write('My Webpage')
That's not a decorator. A decorator needs to return a wrapper function that is called in place of the actual function, and it's the wrapper that needs to do the test and then call the original.
def check(func):
def wrapper(*args, **kwargs):
if users.is_current_user_admin():
return func(*args, **kwargs)
else:
response.write('Please Login')
return wrapper
If all users of the handler must be logged in and be admin, then you can specify the restriction in the app.yaml rather than in your code.
See https://developers.google.com/appengine/docs/python/config/appconfig#Python_app_yaml_Requiring_login_or_administrator_status
And would look something like
- url: /admin/.*
script: somefile.application
login: admin
Be sure to read the docs completely not just skim. It is clear that you have some additional options
auth_fail_action
Describes the action taken when login is present and the user is not
logged in. Has two possible values:
redirect (the default). The user is redirected to the Google sign-in
page, or /_ah/login_required if OpenID authentication is used. The
user is redirected back to the application URL after signing in or
creating an account. unauthorized. The request is rejected with an
HTTP status code of 401 and an error message.
Further down in the document you will see examples.
As an alternative to your own decorator or securing via app.yaml.
webapp2 (which you are using ) has decorators for the handler to do what you require
See https://webapp-improved.appspot.com/api/webapp2_extras/appengine/users.html

Check authentification for each request in Bottle

For every request in Bottle I would like to check if the request is eligible through HTTP authentication. My idea is to use a function, which is called at the start of every #route function.
def check_authentificaiton(requests):
auth = request.headers.get('Authorization')
credentials = parse_auth(auth)
if credentials[0] is not 'user' or credentials[1] is not 'password':
raise Exception('Request is not authorized')
This seems a bit redundant, since I want to protect every request, and it could fail if I forget to call it. Is there a better way?
I think you are looking for a decorator which mandates a route to be accessed only if the user is loggedin. Like in the example below, #require_uid is a decorator which you can use around any function where you need user to be logged in. Flask has a login_required decorator.
Using decorators to require sign in with bottle.py
def require_uid(fn):
def check_uid(**kwargs):
cookie_uid = request.get_cookie('cookieName', secret='cookieSignature')
if cookie_uid:
# do stuff with a user object
return fn(**kwargs)
else:
redirect("/loginagain")
return check_uid
#route('/userstuff', method='GET')
#require_uid
#view('app')
def app_userstuff():
# doing things is what i like to do
return dict(foo="bar")

access django session from a decorator

I have a decorator that I use for my views #valid_session
from django.http import Http404
def valid_session(the_func):
"""
function to check if the user has a valid session
"""
def _decorated(*args, **kwargs):
if ## check if username is in the request.session:
raise Http404('not logged in.')
else:
return the_func(*args, **kwargs)
return _decorated
I would like to access my session in my decoartor. When user is logged in, I put the username in my session.
Will something like the following solve your problem:
def valid_session(func):
def decorated(request, *args, **kwargs):
print request.session
return func(request, *args, **kwargs)
return decorated
The view function takes the request as the first parameter, so the decorator will receive it as its first parameter as well. You can pull the session out of it with just request.session.
You could pass the request (or just the session) in as a parameter to the decorator. I just don't know how to get at it to pass it in. I was trying to figure out something similar last night.

Categories

Resources