If you check the appadmin.py, you’ll find an interesting piece of code:
if not gluon.fileutils.check_credentials(request): redirect(URL(a='admin', c='default', f='index'))
def check_credentials(request, other_application='admin'): """ checks that user is authorized to access other_application""" ........................................................................... return get_session(request, other_application).authorized
For those who don't know a Storage object is a kind of dictionary
that allows you to access keys using something like : my_storage.my_key or session.my_var and if the variable is not there, then no problem, it will return None instead of raising an error
The appadmin controller gets our session file out there in the other application and checks for that field and if it is True, it will let us in.
and we’ll be able to control things
These modifications were just to understand this interesting feature
and I discourage you to do it.
Let’s g0 … .
I want something like:
I went to tools.py and edited the function : requires_login() to look like:
def requires_login(self, other_application=None): """ decorator that prevents access to action if not logged in """ def decorator(action): import fileutils request = self.environment.request def f(*a, **b): if not other_application or \ other_application==self.environment.request.application: if not self.basic() and not self.is_logged_in(): next = URL(r=request,args=request.args, vars=request.get_vars) redirect(self.settings.login_url + '?_next='+urllib.quote(next)) else: session = fileutils.get_session(request,\ other_application=other_application) if not session or not (session.auth and session.auth.user.id): raise HTTP(404) else: self.environment.session.auth = session.auth self.user = session.auth.user return action(*a, **b) f.__doc__ = action.__doc__ return f
@auth.requires_login(other_application='the_other_application') def secure(): response.flash = T('wow') return dict(messag=T('Hello'))
And things works !!!
And also the decorator should not do the login stuff because it’s not his duty.
def secure(): import fileutils osession = fileutils.get_session(request,other_application='other') if not osession or not osession.auth: raise HTTP(403) # Not authorized else: session.auth = osession.auth auth.user = osession.auth.user response.flash = T('wowo') return dict(messag=T('Hello'))
- and if you’ve logged in the ‘other’ application, you’ll be automatically logged in this application
Why would you ever use this technique ?
get_session() is passed a request object that is used to get the cookies and from it session_id can be retrieved, in fact request object includes many session ids in it and session id consists of the string ‘session_id_%s’%request.application , in this way your request object contains ids of as many web applications as you access on the same web server .
Try printing your request object and you’ll get something similar to :
'session_id_test=127-0-0-1-4b858b00-f761-449c-824c-36d7055aa394; session_id_admin=127-0-0-1-cb53593a-2fd9-4086-9222-8049284c9e7a; session_id_welcome=127-0-0-1-f438e3bf-33d2-4349-bc31-95292b3979ff; session_id_other=127-0-0-1-5d7e62c1-d38a-4b4e-8439-08afd159ff4c; session_id_my_app=127-0-0-1-0cd6c4ec-5953-4074-a3f9-9a9a783b3719', ......
If some body has an idea to improve the auth.requires_login(), let me know
Now For Guys who want to make a separate application for authentication but want to use database to store sessions not the file system what they’re going to do ?
It’s very simple, in your model, in the authentication application you’ll need something like:
db = DAL('mysql://root:hamdy@localhost/hamdy') session.connect(request, response, db) # so that sessions are stored in database ......................... auth.define_tables() # so that auth tables are created ......................... db.define_table('table_a', Field('a'))
Now you’re free to define your tables here or in the other application
it’s up to you.
In the other application,you can make in the model db.py something like:
db = DAL('mysql://root:hamdy@localhost/hamdy') # This must be the same database as the auth app session.connect(request, response, db , masterapp='authentication') auth.define_tables(migrate=False) ................................................. db.define_table('table_a', Field('a'), migrate=False)
- database connection string should be the same as the previous application so that you connect from this application to the same database which has sessions stored somewhere in a database table
- session.connect() is important here too.You’ve to specify that you need your session from database and you’ve to determine the masterapp which is the application that you’ll get the session from
- getting another application session means that you’ve to be logged in in the other application, so that you can get the session and become the same user as there.
it doesn’t mean you’ll be logged in on the fly or something, just login in the auth application then go to the other application and Woooow you’re already logged in.
so this won’t work:
in the second application why? because they’re responsible for creating auth tables like : auth_user, auth_group, …But wait !!!! they’ve been created by the auth application and they can’t be recreated here …. Right you go it .
But why not migration is not working in this case?
because you’re in another application 😀 and migration is based on creating special files in the database folder in your application which are not there in the new application 😀
so you’ll get an error.
So you need to make sure that auth tables having their migration flag set to False
The same goes for other tables created by the auth application, in order to use them in the other application , you’ve to re-define them in your model in the other application, but set the migrate flag to False for every table[in the other application of course]
in order for the application to use the existing tables without problems.
Is there any other solution for my application to be able to have the table definitions ?
In fact you can share the same database folder of the auth application in your other application and things will work like sharm without problems untill …..
you change the table definition in one of them, then you’ll run into an interesting situation I called ‘Chaotic migration’, that’s when a user runs an application of them, then the table changes in the database, then another one runs the other application then table changes again ……
Man you’ll end up with a corrupted database tables .
And the solution ?
just stick with the role ….. when 2 or more applications share database tables, make table definitions on all of them with migrate=False except the main one [that you’ll change database from whenever you want and the one that is going to be the first to run], after all if you ran on of the other applications, you’ll end up with an error since migrate is set to False and thus, no database tables are going to be created in the 1st time and nothing will work.
what about Google appengine, can I have a separate application for authentication?
you can just in your authentication application have your model look like:
if request.env.web2py_runtime_gae: db = DAL('gae') from gluon.contrib.memdb import MEMDB from google.appengine.api.memcache import Client session.connect(request, response, db = MEMDB(Client())) ........db.define_table() auth.define_tables()
in the other application :
if request.env.web2py_runtime_gae: db = DAL('gae') from gluon.contrib.memdb import MEMDB from google.appengine.api.memcache import Client session.connect(request, response, db = MEMDB(Client()), masterapp='authentication') ........db.define_table() auth.define_tables(migrate=False)
And congratulations !!!!