So far I have used oauth2 to manage authentication using Google Accounts for my app, which gives me some data to complete a basic profile. The problem is that I now want to manage permissions to view and edit a lot of the content in the app, with different groups of people being able to view/edit different parts of the app.
I want some parts of my application to be accessed by users with permission for A, some for B, C, etc. The way I started doing this was using a decorator in the get and post method of each handler, like this:
class SomeHandler(Handler):
#validate_access
def get(self):
pass
#validate_access
def post(self):
pass
Where #validate_access executes the code in the function only if the user has permission for it, and returning an authorization error if not. This seemed to be a good solution a while back, but since there are many handlers I have to use that decorator everywhere, which is annoying and dangerous, since I may forget to put it in some functions.
Is there a way to put this validations in the initialization of the base handler, so that I don't have to use that decorator everywhere? I imagine something like this:
class BaseHandler(webapp2.RequestHandler):
def initialize(self, request, response):
super(Handler, self).initialize(request, response)
self.user = users.get_current_user()
employee = Employee.query(user_id=self.user.user_id).get()
if employee.auth_level > 3:
#See the content: Do whatever the "get" method of the corresponding handler does.
pass
else:
#Raise authorization error
pass
Or is there a better way to do this? (Sorry if it's a basic question, I've never done this before)
Yes, you can overwrite the webapp2 dispatch handler for this purpose. I used this method to enforce role based access control (RBAC).
Example code:
class BaseHandler(webapp2.RequestHandler):
""" webapp2 base handler """
def dispatch(self):
# UserAccess aborts if the user does not have permission to use a handler
UserAccess(self.request)
super(BaseHandler, self).dispatch()
....
class ExampleHandler(BaseHandler):
def get(self):
.....
I use a config file with the allowed roles for a handler. This file is also used to create the webapp2 routes and the dynamic user menu.
Related
I have an endpoint method that requires a user object. Would I do the following? It seems a bit strange since I could get user using endpoints.get_current_user()
#endpoints.method(FriendListRequest, FriendListResponse,
path='scores', http_method='POST',
name='scores.list')
def friend_list(self, request):
# work would go here
Then the FriendListRequest would be
class FriendListRequest(messages.Message):
user_object = messages.Field(1, required=True)
The reason I need the User object is because I must use the User email to query and find the friends of said user.
To securely authenticate a user, Cloud Endpoints provides simple OAuth 2.0 support. Instead of passing a user object (insecure) in with the request, the request message can be a VoidMessage and you can rely on the Authorization header, which contains the OAuth 2.0 token for the user.
To actually get the current user, you will call endpoints.get_current_user(); this will require adding allowed_client_ids and/or audiences in either the #endpoints.method decorator or the the #endpoints.api method. See the docs for more info.
For example, on the API:
#endpoints.api(name='myapi', ...,
allowed_client_ids=[MY_CLIENT_ID])
class MyApi(...):
#endpoints.method(message_types.VoidMessage, FriendListResponse,
path='scores', http_method='POST',
name='scores.list')
def friend_list(self, request):
user = endpoints.get_current_user()
# work would go here
or, on the method:
#endpoints.method(message_types.VoidMessage, FriendListResponse,
path='scores', http_method='POST',
name='scores.list',
allowed_client_ids=[MY_CLIENT_ID])
def friend_list(self, request):
user = endpoints.get_current_user()
# work would go here
Create a user object using users and pass the same
A User instance can be also constructed from an email address:
from google.appengine.api import users
user = users.User("A***.j***#gmail.com")
Using Google App Engines webapp framework, is there any way to pass data to a base template?
To be specific, I just want a logout button to be visible if the user is logged on (using googles own authentication system).
I'm still learning so I'm not sure what parts are GAE specific and what parts are django specific; having to send the logged in user from every single request handler seems very un-DRY.
Arguments to base templates are passed the same way as any other template arguments, by being passed to template.render. I usually solve this by having a convenience method on my base handler that inserts common template arguments, like this:
class BaseHandler(webapp.RequestHandler):
def render_template(self, filename, template_args):
path = os.path.join(os.path.dirname(__file__), 'templates', filename)
template_args.update({
'user': users.get_current_user(),
# ...
})
class MyHandler(BaseHandler):
def get(self):
self.render_template('my.html', {'foo': 'bar'})
I think you are looking for something like login_required decorator in django. You can either try to use a complete django framework in GAE (I never tried) or it can be customized easily with decoration and add your own behavior. In your case, it will be a good idea to pass user's login status to template engine.
#the decorator
def login_checked(f):
def wrap(request, *args, **kwargs):
# get current user
user = get_current_user()
template_path, vars = f(request, *args, **kwargs)
vars['user']= user
template.render(template_path, vars)
return wrap
# usage
class MyPage(webapp.RequestHandler):
#login_checked # add a decoration
def get(self):
# your page
return "the_template_page_you_want", {"the value you want to pass to template": "xxx"}
Take a look at this example:
from google.appengine.api import users
class MyHandler(webapp.RequestHandler):
def get(self):
user = users.get_current_user()
if user:
greeting = ("Welcome, %s! (sign out)" %
(user.nickname(), users.create_logout_url("/")))
else:
greeting = ("Sign in or register." %
users.create_login_url("/"))
self.response.out.write("<html><body>%s</body></html>" % greeting)
Source: http://code.google.com/appengine/docs/python/users/loginurls.html
Is it possible to override methids for db.Model in Google App Engine?
I want to declare beforeSave, afterSave methods, etc.. to create automatic tagging system.
I know there are hooks, but it seems to me a wrong way to solve this issue :)
Thanks!
Yes, it's possible to override these methods. Have a look at this blog post by Nick Johnson.The hooked model class looks this:
class HookedModel(db.Model):
def before_put(self):
pass
def after_put(self):
pass
def put(self, **kwargs):
self.before_put()
super(HookedModel, self).put(**kwargs)
self.after_put()
Read the blog to see how to handle the db.put() method too.
You might also be interested on "derived properties".
I posted an extension to jbochi's HookedModel class so that the before_put and after_put methods are correctly invoked when called from db.put() and the _async family of functions.
See AppEngine PreCall API hooks
I've attempted to improve upon the answer by #jbochi:
According to https://developers.google.com/appengine/docs/python/datastore/modelclass#Model_put , put() should return a Key, so the return value from the library should be passed through.
db.Model.save(), while deprecated, is (a) sometimes still used, (b) meant to be a synonym for put() and (c) apparently not called directly by put() - so should be handled manually.
Revised code:
class HookedModel(db.Model):
def before_put(self):
pass
def after_put(self):
pass
def put(self, **kwargs):
self.before_put()
result = super(HookedModel, self).put(**kwargs)
self.after_put()
return result
def save(self, **kwargs):
self.before_put()
result = super(HookedModel, self).save(**kwargs)
self.after_put()
return result
You should still read http://blog.notdot.net/2010/04/Pre--and-post--put-hooks-for-Datastore-models if you wish to use the monkeypatching, or Chris Farmiloe's answer for use of the async methods.
today i tried to get django-piston and ExtJS working. I used the ExtJS restful example and the piston example to create a little restful webapp. Everything works fine except the authentication.
Whats the best way to get Basic/Digest/OAuth authentication working with ExtJS?
Atm I'm not sure where to set the Username/Password.
Thanks
If you want to use piston with ExtJS, I would suggest writing an anonymous handler that checks the user is logged in via standard auth.
Try this:
class AnonymousUserProfileHandler(BaseHandler):
fields = ('title', 'url', 'affiliation')
model = UserProfile
def read(self, request, nickname):
profile = UserProfile.objects.get(nickname=nickname)
if request.user == profile.user:
return profile
class UserProfileHandler(BaseHandler):
anonymous = AnonymousUserProfileHandler
allowed_methods = ('GET')
fields = ('title', 'url', 'affiliation')
model = UserProfile
def read(self, request, nickname):
profile = UserProfile.objects.get(nickname=nickname)
return profile
In this example, when UserProfileHandler is called, without any authorization, it delegates to the anonymous handler. The anonymous handler checks whether the user is logged in via the usual request.user mode. If there is a valid user, it returns their profile object. You would then, obviously, mark the view calling this as requiring login.
The point is: when extJS makes its JSON call, it will send authentication data via the usual cookie. If you use an "anonymous" handler in Piston, but manually check the user is logged in before returning the data, then you essentially use traditional auth for your own site.
I have a Django app that gets it's data completely from an external source (queried via HTTP). That is, I don't have the option for a local database. Session data is stored in the cache (on my development server I use a SQLite database, so that is no error source). I'm using bleeding edge Django 1.1svn.
Enter the problem: I want to use Django's own authentication system for the users.
It seems quite simple to write my own Authentication Backend, but always just under the condition that you have a local database where to save the users. Without database my main problem is persistence.
I tried it with the following (assume that datasource.get() is a function that returns some kind of dict):
class ModelBackend (object):
"""Login backend."""
def authenticate (self, username=None, password=None):
"""Check, if a given user/password combination is valid"""
data = datasource.get ('login', username, password)
if data and data['ok']:
return MyUser (username=username)
else:
raise TypeError
return None
def get_user (self, username):
"""get data about a specific user"""
try:
data = datasource.get ('userdata', username)
if data and data['ok']:
return data.user
except:
pass
return None
class MyUser (User):
"""Django user who isn't saved in DB"""
def save (self):
return None
But the intentionally missing save() method on MyUser seems to break the session storage of a login.
How should MyUser look like without a local database?
OK, it's much more complicated than I thought. First, start with http://docs.djangoproject.com/en/dev/howto/auth-remote-user/, but you'll need to extend it with your own backend and user.
from django.contrib.auth.backends import RemoteUserBackend
class MyRemoteUserBackend (RemoteUserBackend):
# Create a User object if not already in the database?
create_unknown_user = False
def get_user (self, user_id):
user = somehow_create_an_instance_of (MyUser, user_id)
return user
def authenticate (self, **credentials):
check_credentials ()
user = somehow_create_an_instance_of (MyUser, credentials)
return user
Then the user:
from django.contrib.auth.models import User
class MyUser (User):
def save (self):
"""saving to DB disabled"""
pass
objects = None # we cannot really use this w/o local DB
username = "" # and all the other properties likewise.
# They're defined as model.CharField or similar,
# and we can't allow that
def get_group_permissions (self):
"""If you don't make your own permissions module,
the default also will use the DB. Throw it away"""
return [] # likewise with the other permission defs
def get_and_delete_messages (self):
"""Messages are stored in the DB. Darn!"""
return []
Phew! Django really isn't designed for usage without a database...
Rather than overwriting the save method, you may also disconnect the signal that invokes it. This is what I do in some apps which have read-only access to the user database.
# models.py (for instance)
from django.contrib.auth.models import update_last_login, user_logged_in
user_logged_in.disconnect(update_last_login)
grepping the source showed, that the only place user.save() is actually called (except for user creation and password management code, which you don't need to use at all) is django.contrib.auth.login(), to update user.last_login value.
# TODO: It would be nice to support different login methods, like signed cookies.
user.last_login = datetime.datetime.now()
user.save()
If you don't want user data to rest in DB, try adding dummy save() method. If I'm right, it should work.
def save(self, *args, **kwargs):
pass
Of course, because you have no persistence at all, you should consider caching datasource.get results, otherwise in worst case you may end up querying data again and again on every single logged in user's hit.