[IMP] http: Sessions implicit deactivation
- Store a token inside sessions to allow implicit session deactivation when needed.
This commit is contained in:
@@ -96,3 +96,6 @@ class ResUsers(models.Model):
|
|||||||
internally
|
internally
|
||||||
"""
|
"""
|
||||||
return default_crypt_context
|
return default_crypt_context
|
||||||
|
|
||||||
|
def _get_session_token_fields(self):
|
||||||
|
return super(ResUsers, self)._get_session_token_fields() | {'password_crypt'}
|
||||||
|
|||||||
@@ -125,3 +125,6 @@ class ResUsers(models.Model):
|
|||||||
res = self.sudo().search([('id', '=', self.env.uid), ('oauth_access_token', '=', password)])
|
res = self.sudo().search([('id', '=', self.env.uid), ('oauth_access_token', '=', password)])
|
||||||
if not res:
|
if not res:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
def _get_session_token_fields(self):
|
||||||
|
return super(ResUsers, self)._get_session_token_fields() | {'oauth_access_token'}
|
||||||
|
|||||||
@@ -3,11 +3,13 @@
|
|||||||
import pytz
|
import pytz
|
||||||
import datetime
|
import datetime
|
||||||
import logging
|
import logging
|
||||||
|
import hmac
|
||||||
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from itertools import chain, repeat
|
from itertools import chain, repeat
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
from lxml.builder import E
|
from lxml.builder import E
|
||||||
|
from hashlib import sha256
|
||||||
|
|
||||||
from odoo import api, fields, models, tools, SUPERUSER_ID, _
|
from odoo import api, fields, models, tools, SUPERUSER_ID, _
|
||||||
from odoo.exceptions import AccessDenied, AccessError, UserError, ValidationError
|
from odoo.exceptions import AccessDenied, AccessError, UserError, ValidationError
|
||||||
@@ -375,6 +377,8 @@ class Users(models.Model):
|
|||||||
db = self._cr.dbname
|
db = self._cr.dbname
|
||||||
for id in self.ids:
|
for id in self.ids:
|
||||||
self.__uid_cache[db].pop(id, None)
|
self.__uid_cache[db].pop(id, None)
|
||||||
|
if any(key in values for key in self._get_session_token_fields()):
|
||||||
|
self._invalidate_session_cache()
|
||||||
|
|
||||||
return res
|
return res
|
||||||
|
|
||||||
@@ -385,6 +389,7 @@ class Users(models.Model):
|
|||||||
db = self._cr.dbname
|
db = self._cr.dbname
|
||||||
for id in self.ids:
|
for id in self.ids:
|
||||||
self.__uid_cache[db].pop(id, None)
|
self.__uid_cache[db].pop(id, None)
|
||||||
|
self._invalidate_session_cache()
|
||||||
return super(Users, self).unlink()
|
return super(Users, self).unlink()
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
@@ -510,6 +515,34 @@ class Users(models.Model):
|
|||||||
finally:
|
finally:
|
||||||
cr.close()
|
cr.close()
|
||||||
|
|
||||||
|
def _get_session_token_fields(self):
|
||||||
|
return {'id', 'login', 'password', 'active'}
|
||||||
|
|
||||||
|
@tools.ormcache('sid')
|
||||||
|
def _compute_session_token(self, sid):
|
||||||
|
""" Compute a session token given a session id and a user id """
|
||||||
|
# retrieve the fields used to generate the session token
|
||||||
|
session_fields = ', '.join(sorted(self._get_session_token_fields()))
|
||||||
|
self.env.cr.execute("""SELECT %s, (SELECT value FROM ir_config_parameter WHERE key='database.secret')
|
||||||
|
FROM res_users
|
||||||
|
WHERE id=%%s""" % (session_fields), (self.id,))
|
||||||
|
if self.env.cr.rowcount != 1:
|
||||||
|
self._invalidate_session_cache()
|
||||||
|
return False
|
||||||
|
data_fields = self.env.cr.fetchone()
|
||||||
|
# generate hmac key
|
||||||
|
key = (u'%s' % (data_fields,)).encode('utf-8')
|
||||||
|
# hmac the session id
|
||||||
|
data = sid.encode('utf-8')
|
||||||
|
h = hmac.new(key, data, sha256)
|
||||||
|
# keep in the cache the token
|
||||||
|
return h.hexdigest()
|
||||||
|
|
||||||
|
@api.multi
|
||||||
|
def _invalidate_session_cache(self):
|
||||||
|
""" Clear the sessions cache """
|
||||||
|
self._compute_session_token.clear_cache(self)
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def change_password(self, old_passwd, new_passwd):
|
def change_password(self, old_passwd, new_passwd):
|
||||||
"""Change current user password. Old password must be provided explicitly
|
"""Change current user password. Old password must be provided explicitly
|
||||||
|
|||||||
+12
-3
@@ -1046,7 +1046,7 @@ class OpenERPSession(werkzeug.contrib.sessions.Session):
|
|||||||
self.db = db
|
self.db = db
|
||||||
self.uid = uid
|
self.uid = uid
|
||||||
self.login = login
|
self.login = login
|
||||||
self.password = password
|
self.session_token = uid and security.compute_session_token(self)
|
||||||
request.uid = uid
|
request.uid = uid
|
||||||
request.disable_db = False
|
request.disable_db = False
|
||||||
|
|
||||||
@@ -1061,7 +1061,16 @@ class OpenERPSession(werkzeug.contrib.sessions.Session):
|
|||||||
"""
|
"""
|
||||||
if not self.db or not self.uid:
|
if not self.db or not self.uid:
|
||||||
raise SessionExpiredException("Session expired")
|
raise SessionExpiredException("Session expired")
|
||||||
security.check(self.db, self.uid, self.password)
|
|
||||||
|
# == BACKWARD COMPATIBILITY TO CONVERT OLD SESSION TYPE TO THE NEW ONES ! REMOVE ME AFTER 11.0 ==
|
||||||
|
if self.get('password'):
|
||||||
|
security.check(self.db, self.uid, self.password)
|
||||||
|
self.session_token = security.compute_session_token(self)
|
||||||
|
self.pop('password')
|
||||||
|
# =================================================================================================
|
||||||
|
# here we check if the session is still valid
|
||||||
|
if not security.check_session(self):
|
||||||
|
raise SessionExpiredException("Session expired")
|
||||||
|
|
||||||
def logout(self, keep_db=False):
|
def logout(self, keep_db=False):
|
||||||
for k in self.keys():
|
for k in self.keys():
|
||||||
@@ -1074,7 +1083,7 @@ class OpenERPSession(werkzeug.contrib.sessions.Session):
|
|||||||
self.setdefault("db", None)
|
self.setdefault("db", None)
|
||||||
self.setdefault("uid", None)
|
self.setdefault("uid", None)
|
||||||
self.setdefault("login", None)
|
self.setdefault("login", None)
|
||||||
self.setdefault("password", None)
|
self.setdefault("session_token", None)
|
||||||
self.setdefault("context", {})
|
self.setdefault("context", {})
|
||||||
|
|
||||||
def get_context(self):
|
def get_context(self):
|
||||||
|
|||||||
@@ -11,3 +11,16 @@ def login(db, login, password):
|
|||||||
def check(db, uid, passwd):
|
def check(db, uid, passwd):
|
||||||
res_users = odoo.registry(db)['res.users']
|
res_users = odoo.registry(db)['res.users']
|
||||||
return res_users.check(db, uid, passwd)
|
return res_users.check(db, uid, passwd)
|
||||||
|
|
||||||
|
def compute_session_token(session):
|
||||||
|
with odoo.registry(session.db).cursor() as cr:
|
||||||
|
self = odoo.api.Environment(cr, session.uid, {})['res.users'].browse(session.uid)
|
||||||
|
return self._compute_session_token(session.sid)
|
||||||
|
|
||||||
|
def check_session(session):
|
||||||
|
with odoo.registry(session.db).cursor() as cr:
|
||||||
|
self = odoo.api.Environment(cr, session.uid, {})['res.users'].browse(session.uid)
|
||||||
|
if odoo.tools.misc.consteq(self._compute_session_token(session.sid), session.session_token):
|
||||||
|
return True
|
||||||
|
self._invalidate_session_cache()
|
||||||
|
return False
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ import werkzeug
|
|||||||
|
|
||||||
import odoo
|
import odoo
|
||||||
from odoo import api
|
from odoo import api
|
||||||
|
from odoo.service import security
|
||||||
|
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -286,7 +288,7 @@ class HttpCase(TransactionCase):
|
|||||||
session.db = db
|
session.db = db
|
||||||
session.uid = uid
|
session.uid = uid
|
||||||
session.login = user
|
session.login = user
|
||||||
session.password = password
|
session.session_token = uid and security.compute_session_token(session)
|
||||||
session.context = env['res.users'].context_get() or {}
|
session.context = env['res.users'].context_get() or {}
|
||||||
session.context['uid'] = uid
|
session.context['uid'] = uid
|
||||||
session._fix_lang(session.context)
|
session._fix_lang(session.context)
|
||||||
|
|||||||
Reference in New Issue
Block a user