summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristophe Simonis <chs@odoo.com>2018-03-19 19:51:33 +0100
committerChristophe Simonis <chs@odoo.com>2018-03-19 19:51:33 +0100
commit3d9a2340d99f9c8a202f9a5181ee32094cd801f9 (patch)
treef905076679a5dbbdf56ce75db5fbe9ca2306c898
parent0d552411852173efb3a05425d9dbb5241c875658 (diff)
parente098276644105d729500033f1df56107f0c0090f (diff)
[MERGE] forward port branch saas-14 up to e0982766441
-rw-r--r--addons/auth_crypt/models/res_users.py3
-rw-r--r--addons/auth_oauth/models/res_users.py3
-rw-r--r--odoo/addons/base/res/res_users.py33
-rw-r--r--odoo/http.py15
-rw-r--r--odoo/service/security.py13
-rw-r--r--odoo/tests/common.py4
6 files changed, 67 insertions, 4 deletions
diff --git a/addons/auth_crypt/models/res_users.py b/addons/auth_crypt/models/res_users.py
index 708998acbde..711cfc9d88d 100644
--- a/addons/auth_crypt/models/res_users.py
+++ b/addons/auth_crypt/models/res_users.py
@@ -96,3 +96,6 @@ class ResUsers(models.Model):
internally
"""
return default_crypt_context
+
+ def _get_session_token_fields(self):
+ return super(ResUsers, self)._get_session_token_fields() | {'password_crypt'}
diff --git a/addons/auth_oauth/models/res_users.py b/addons/auth_oauth/models/res_users.py
index b10f19a28be..be756149ba3 100644
--- a/addons/auth_oauth/models/res_users.py
+++ b/addons/auth_oauth/models/res_users.py
@@ -125,3 +125,6 @@ class ResUsers(models.Model):
res = self.sudo().search([('id', '=', self.env.uid), ('oauth_access_token', '=', password)])
if not res:
raise
+
+ def _get_session_token_fields(self):
+ return super(ResUsers, self)._get_session_token_fields() | {'oauth_access_token'}
diff --git a/odoo/addons/base/res/res_users.py b/odoo/addons/base/res/res_users.py
index 596620a9e52..fd8863701aa 100644
--- a/odoo/addons/base/res/res_users.py
+++ b/odoo/addons/base/res/res_users.py
@@ -3,11 +3,13 @@
import pytz
import datetime
import logging
+import hmac
from collections import defaultdict
from itertools import chain, repeat
from lxml import etree
from lxml.builder import E
+from hashlib import sha256
from odoo import api, fields, models, tools, SUPERUSER_ID, _
from odoo.exceptions import AccessDenied, AccessError, UserError, ValidationError
@@ -375,6 +377,8 @@ class Users(models.Model):
db = self._cr.dbname
for id in self.ids:
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
@@ -385,6 +389,7 @@ class Users(models.Model):
db = self._cr.dbname
for id in self.ids:
self.__uid_cache[db].pop(id, None)
+ self._invalidate_session_cache()
return super(Users, self).unlink()
@api.model
@@ -510,6 +515,34 @@ class Users(models.Model):
finally:
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
def change_password(self, old_passwd, new_passwd):
"""Change current user password. Old password must be provided explicitly
diff --git a/odoo/http.py b/odoo/http.py
index 780521db857..a9e9e8cf9ed 100644
--- a/odoo/http.py
+++ b/odoo/http.py
@@ -1046,7 +1046,7 @@ class OpenERPSession(werkzeug.contrib.sessions.Session):
self.db = db
self.uid = uid
self.login = login
- self.password = password
+ self.session_token = uid and security.compute_session_token(self)
request.uid = uid
request.disable_db = False
@@ -1061,7 +1061,16 @@ class OpenERPSession(werkzeug.contrib.sessions.Session):
"""
if not self.db or not self.uid:
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):
for k in self.keys():
@@ -1074,7 +1083,7 @@ class OpenERPSession(werkzeug.contrib.sessions.Session):
self.setdefault("db", None)
self.setdefault("uid", None)
self.setdefault("login", None)
- self.setdefault("password", None)
+ self.setdefault("session_token", None)
self.setdefault("context", {})
def get_context(self):
diff --git a/odoo/service/security.py b/odoo/service/security.py
index 264e88d42bd..229e4b84077 100644
--- a/odoo/service/security.py
+++ b/odoo/service/security.py
@@ -11,3 +11,16 @@ def login(db, login, password):
def check(db, uid, passwd):
res_users = odoo.registry(db)['res.users']
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
diff --git a/odoo/tests/common.py b/odoo/tests/common.py
index 8e78791721c..1f361362a86 100644
--- a/odoo/tests/common.py
+++ b/odoo/tests/common.py
@@ -26,6 +26,8 @@ import werkzeug
import odoo
from odoo import api
+from odoo.service import security
+
_logger = logging.getLogger(__name__)
@@ -285,7 +287,7 @@ class HttpCase(TransactionCase):
session.db = db
session.uid = uid
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['uid'] = uid
session._fix_lang(session.context)