Skip to content

Commit

Permalink
[IMP] http: Sessions implicit deactivation
Browse files Browse the repository at this point in the history
- Store a token inside sessions to allow implicit session deactivation when needed.
  • Loading branch information
tbe-odoo committed Mar 19, 2018
1 parent caf6b4a commit da1f153
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 4 deletions.
3 changes: 3 additions & 0 deletions addons/auth_crypt/models/res_users.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,6 @@ def _crypt_context(self):
internally
"""
return default_crypt_context

def _get_session_token_fields(self):
return super(ResUsers, self)._get_session_token_fields() | {'password_crypt'}
3 changes: 3 additions & 0 deletions addons/auth_oauth/models/res_users.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,3 +125,6 @@ def check_credentials(self, password):
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'}
33 changes: 33 additions & 0 deletions odoo/addons/base/res/res_users.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -375,6 +377,8 @@ def write(self, values):
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

Expand All @@ -385,6 +389,7 @@ def unlink(self):
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
Expand Down Expand Up @@ -510,6 +515,34 @@ def check(cls, db, uid, passwd):
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
Expand Down
15 changes: 12 additions & 3 deletions odoo/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -1046,7 +1046,7 @@ def authenticate(self, db, login=None, password=None, uid=None):
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

Expand All @@ -1061,7 +1061,16 @@ def check_security(self):
"""
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():
Expand All @@ -1074,7 +1083,7 @@ def _default_values(self):
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):
Expand Down
13 changes: 13 additions & 0 deletions odoo/service/security.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
4 changes: 3 additions & 1 deletion odoo/tests/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@

import odoo
from odoo import api
from odoo.service import security


_logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -286,7 +288,7 @@ def authenticate(self, user, password):
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)
Expand Down

0 comments on commit da1f153

Please sign in to comment.