Validate the session without using the pool on bus requests

This commit is contained in:
Nicolas Évrard
2025-10-04 15:53:18 +02:00
parent a72adf0fdc
commit f0199c149b
3 changed files with 60 additions and 7 deletions
+1 -1
View File
@@ -226,7 +226,7 @@ else:
@app.route('/<string:database_name>/bus', methods=['POST'])
@app.auth_required
@app.session_valid
def subscribe(request, database_name):
if not _allow_subscribe:
raise exceptions.NotImplemented
+31
View File
@@ -1,15 +1,19 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
import datetime as dt
import ipaddress
import logging
import random
import time
from secrets import compare_digest
try:
from http import HTTPStatus
except ImportError:
from http import client as HTTPStatus
from sql import Table
from sql.conditionals import Coalesce
from werkzeug.exceptions import abort
from trytond import backend
@@ -168,6 +172,33 @@ def check_timeout(dbname, user, session, context=None):
return valid
def check_session(dbname, user, session, remote_addr=None):
"Check the session without using the pool"
database = backend.Database(dbname)
now = dt.datetime.now()
timeout = dt.timedelta(config.getint('session', 'max_age'))
conn = database.get_connection(readonly=True)
if remote_addr:
ip_addr = str(ipaddress.ip_address(remote_addr))
else:
ip_addr = None
try:
ir_session = Table('ir_session')
cursor = conn.cursor()
cursor.execute(*ir_session.select(
Coalesce(ir_session.write_date, ir_session.create_date),
ir_session.key,
where=((ir_session.create_uid == user)
& (ir_session.ip_address == ip_addr))))
for session_date, session_key in cursor:
if abs(session_date - now) < timeout:
if compare_digest(session_key, session):
return True
return False
finally:
database.put_connection(conn)
def reset(dbname, session, context):
try:
with Transaction().start(dbname, 0, context=context, autocommit=True):
+28 -6
View File
@@ -27,7 +27,7 @@ try:
except ImportError:
from werkzeug.wsgi import SharedDataMiddleware
from trytond import backend
from trytond import backend, security
from trytond.config import config
from trytond.protocols.jsonrpc import JSONProtocol
from trytond.protocols.wrappers import (
@@ -41,6 +41,14 @@ __all__ = ['TrytondWSGI', 'app']
logger = logging.getLogger(__name__)
def _do_basic_auth(request):
headers = {}
if request.headers.get('X-Requested-With') != 'XMLHttpRequest':
headers['WWW-Authenticate'] = 'Basic realm="Tryton"'
response = Response(None, http.client.UNAUTHORIZED, headers)
abort(http.client.UNAUTHORIZED, response=response)
class Base64Converter(BaseConverter):
def to_python(self, value):
@@ -81,11 +89,25 @@ class TrytondWSGI(object):
if request.user_id:
return func(request, *args, **kwargs)
else:
headers = {}
if request.headers.get('X-Requested-With') != 'XMLHttpRequest':
headers['WWW-Authenticate'] = 'Basic realm="Tryton"'
response = Response(None, http.client.UNAUTHORIZED, headers)
abort(http.client.UNAUTHORIZED, response=response)
_do_basic_auth(request)
return wrapper
def session_valid(self, func):
@wraps(func)
def wrapper(request, *args, **kwargs):
if (not request.authorization
or request.authorization.type != 'session'):
_do_basic_auth(request)
userid = request.authorization.get('userid')
session = request.authorization.get('session')
dbname = request.view_args.get('database_name')
if not security.check_session(
dbname, userid, session, request.remote_addr):
_do_basic_auth(request)
return func(request, *args, **kwargs)
return wrapper
def dispatch_request(self, request):