# HG changeset patch # User Florent Aide <florent.aide@gmail.com> # Date 1415620067 -3600 # Mon Nov 10 12:47:47 2014 +0100 # Node ID 06ccd421baba44872336dab75fb8bc4a9e4f769f # Parent 29ff14e288ef835341c8b458cf7aa88046507266 Tokens are now in their own table to avoid locks on res.users diff --git a/res_users.py b/res_users.py --- a/res_users.py +++ b/res_users.py @@ -1,3 +1,4 @@ +# -*- encoding: utf-8 -*- import logging import lasso @@ -20,10 +21,6 @@ 'SAML User ID', help="SAML Provider user_id", ), - 'saml_access_token': fields.char( - 'Current SAML token for this user', - help="The current SAML token in use", - ), } _sql_constraints = [ @@ -53,10 +50,8 @@ raise Exception('Invalid assertion') # TODO use a real token validation from LASSO - validation = {} - # TODO push into the validation result a real UPN - validation['user_id'] = login.assertion.subject.nameId.content + validation = {'user_id': login.assertion.subject.nameId.content} """ if p.data_endpoint: @@ -80,6 +75,7 @@ This method can be overridden to add alternative signin methods. """ + token_osv = self.pool.get('auth_saml.token') saml_uid = validation['user_id'] user_ids = self.search( @@ -97,12 +93,35 @@ # production code... assert len(user_ids) == 1 + # browse the user because we'll need this in the response user = self.browse(cr, uid, user_ids[0], context=context) - # WARNING: writing this means you can only log-in once with a single - # user... - # TODO add a new table with access tokens linked to users - # in order to be able to log multiple times with the same user - user.write({'saml_access_token': saml_response}) + + user_id = user.id + + # now find if a token for this user/provider already exists + token_ids = token_osv.search( + cr, uid, + [ + ('saml_provider_id', '=', provider), + ('user_id', '=', user_id), + ] + ) + if token_ids: + token_osv.write( + cr, uid, token_ids, + {'saml_access_token': saml_response}, + context=context + ) + else: + token_osv.create( + cr, uid, + { + 'saml_access_token': saml_response, + 'saml_provider_id': provider, + 'user_id': user_id, + }, + context=context + ) return user.login @@ -125,26 +144,30 @@ raise openerp.exceptions.AccessDenied() # return user credentials - return (cr.dbname, login, saml_response) + return cr.dbname, login, saml_response def check_credentials(self, cr, uid, token): """token can be a password if the user has used the normal form... but we are more interested in the case when they are tokens and the interesting code is inside the except clause """ + token_osv = self.pool.get('auth_saml.token') try: - return super(res_users, self).check_credentials(cr, uid, token) + super(res_users, self).check_credentials(cr, uid, token) except openerp.exceptions.AccessDenied: - res = self.search( + # since normal auth did not succeed we now try to find if the user + # has an active token attached to his uid + res = token_osv.search( cr, SUPERUSER_ID, [ - ('id', '=', uid), + ('user_id', '=', uid), ('saml_access_token', '=', token), ] ) + # if the user is not found we re-raise the AccessDenied if not res: # TODO: maybe raise a defined exception instead of the last - # exception that occured in our execution frame + # exception that occurred in our execution frame raise diff --git a/token.py b/token.py new file mode 100644 --- /dev/null +++ b/token.py @@ -0,0 +1,31 @@ +# -*- encoding: utf-8 -*- +__author__ = 'faide' + + +import logging +from openerp.osv import osv, fields + +_logger = logging.getLogger(__name__) + + +class saml_token(osv.Model): + _name = "auth_saml.token" + _rec_name = "user_id" + + _columns = { + 'saml_provider_id': fields.many2one( + 'auth.saml.provider', + string='SAML Provider that issued the token', + ), + 'user_id': fields.many2one( + 'res.users', + string="User", + # we want the token to be destroyed if the corresponding res.users + # is deleted + ondelete="cascade" + ), + 'saml_access_token': fields.char( + 'Current SAML token for this user', + help="The current SAML token in use", + ), + }