Add support for basic authentication for user application

Closes #14528
This commit is contained in:
Cédric Krier
2026-01-21 14:55:49 +01:00
parent aafdc31cea
commit d7b80ff2ee
4 changed files with 97 additions and 7 deletions
+1
View File
@@ -1,3 +1,4 @@
* Add support for basic authentication for user application
* Support the conversion of MJML reports to HTML
* Allow filtering users to be notified by cron tasks
+3 -2
View File
@@ -52,8 +52,9 @@ creation of such route.
``user_application(name[, json])``
Set the :attr:`~trytond.transaction.Transaction.user` from the
``Authorization`` header using the type ``bearer`` and a valid key for the
named user application.
``Authorization`` header using the ``bearer`` type with the user application
key, or the ``basic`` type without a username and with the user application
key as the password.
User Application Key
====================
+8 -4
View File
@@ -292,13 +292,17 @@ def user_application(name, json=True):
authorization = parse_authorization_header(header)
if authorization is None:
abort(HTTPStatus.UNAUTHORIZED)
if authorization.type != 'bearer':
abort(HTTPStatus.FORBIDDEN)
token = getattr(authorization, 'token', '')
if authorization.type == 'bearer':
token = getattr(authorization, 'token', '')
elif authorization.type == 'basic':
token = authorization.get('password')
else:
abort(HTTPStatus.UNAUTHORIZED)
application = UserApplication.check(token, name)
if not application:
abort(HTTPStatus.FORBIDDEN)
abort(HTTPStatus.UNAUTHORIZED)
transaction = Transaction()
# TODO language
with transaction.set_user(application.user.id), \
+85 -1
View File
@@ -3,12 +3,17 @@
import datetime
import json
from base64 import b64encode
from decimal import Decimal
from trytond.pool import Pool
from trytond.protocols.jsonrpc import JSONDecoder, JSONEncoder, JSONRequest
from trytond.protocols.wrappers import (
HTTPStatus, Response, user_application, with_pool, with_transaction)
from trytond.protocols.xmlrpc import XMLRequest, client
from trytond.tests.test_tryton import TestCase
from trytond.tests.test_tryton import Client, RouteTestCase, TestCase
from trytond.tools.immutabledict import ImmutableDict
from trytond.wsgi import TrytondWSGI
def _identity(x):
@@ -130,3 +135,82 @@ class XMLTestCase(DumpsLoadsMixin, TestCase):
result, _ = client.loads(s)
result, = result
self.assertEqual(result, Decimal('3.141592653589793'))
class UserApplication(RouteTestCase):
module = 'res'
@classmethod
def setUpDatabase(cls):
pool = Pool()
User = pool.get('res.user')
UserApplication = pool.get('res.user.application')
UserApplication.application.selection.append(('test', "Test"))
User.create([{
'login': 'user',
'applications': [('create', [{
'key': 'secret_key',
'application': 'test',
'state': 'validated',
}])],
}])
def test_authorization_bearer(self):
app = TrytondWSGI()
test_application = user_application('test')
@app.route('/<database_name>/test/user_application')
@with_pool
@with_transaction()
@test_application
def _route(request, pool):
return ''
client = Client(app, Response)
response = client.get(
f'/{self.db_name}/test/user_application',
headers=[
('Authorization', 'bearer secret_key'),
])
self.assertEqual(response.status_code, HTTPStatus.OK)
def test_authorization_basic(self):
app = TrytondWSGI()
test_application = user_application('test')
@app.route('/<database_name>/test/user_application')
@with_pool
@with_transaction()
@test_application
def _route(request, pool):
return ''
client = Client(app, Response)
auth = b64encode(b':secret_key').decode('ascii')
response = client.get(
f'/{self.db_name}/test/user_application',
headers=[
('Authorization', f'basic {auth}'),
])
self.assertEqual(response.status_code, HTTPStatus.OK)
def test_authorization_bad_auth(self):
app = TrytondWSGI()
test_application = user_application('test')
@app.route('/<database_name>/test/user_application')
@with_pool
@with_transaction()
@test_application
def _route(request, pool):
return ''
client = Client(app, Response)
response = client.get(
f'/{self.db_name}/test/user_application',
headers=[
('Authorization', 'bearer foo'),
])
self.assertEqual(response.status_code, HTTPStatus.UNAUTHORIZED)