From 6f43d36ad8f4b2192e17f141c5ced3a98663c428 Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Tue, 12 May 2020 18:58:48 +0200 Subject: [PATCH] misc: do not trace on invalid HTTP auth (#42784) --- passerelle/utils/__init__.py | 15 ++++++-- tests/test_utils.py | 71 ++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 4 deletions(-) create mode 100644 tests/test_utils.py diff --git a/passerelle/utils/__init__.py b/passerelle/utils/__init__.py index d0494aad..ad6add67 100644 --- a/passerelle/utils/__init__.py +++ b/passerelle/utils/__init__.py @@ -78,10 +78,17 @@ def get_request_users(request): users.extend(ApiUser.objects.filter(keytype='API', key=request.GET['apikey'])) elif 'HTTP_AUTHORIZATION' in request.META: - (scheme, param) = request.META['HTTP_AUTHORIZATION'].split(' ', 1) - if scheme.lower() == 'basic': - username, password = force_text(base64.b64decode(force_bytes(param.strip()))).split(':', 1) - users.extend(ApiUser.objects.filter(keytype='SIGN', username=username, key=password)) + http_authorization = request.META['HTTP_AUTHORIZATION'].split(' ', 1) + scheme = http_authorization[0].lower() + if scheme == 'basic' and len(http_authorization) > 1: + param = http_authorization[1] + try: + decoded = force_text(base64.b64decode(force_bytes(param.strip()))) + username, password = decoded.split(':', 1) + except (TypeError, ValueError): + pass + else: + users.extend(ApiUser.objects.filter(keytype='SIGN', username=username, key=password)) def ip_match(ip, match): if not ip: diff --git a/tests/test_utils.py b/tests/test_utils.py new file mode 100644 index 00000000..5bfd9675 --- /dev/null +++ b/tests/test_utils.py @@ -0,0 +1,71 @@ +# Copyright (C) 2019 Entr'ouvert +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU Affero General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +import base64 + +from django.utils.encoding import force_str + +from passerelle.base.models import ApiUser +from passerelle.base.signature import sign_query +from passerelle.utils import get_request_users + + +def test_get_request_users_publik_signature(db, rf): + request = rf.get('/?' + sign_query('orig=orig', key='publikkey')) + assert get_request_users(request) == [] + + api_user = ApiUser.objects.create(keytype='SIGN', username='orig', key='publikkey') + assert get_request_users(request) == [api_user] + + request = rf.get('/?orig=orig&signature=xxx') + assert get_request_users(request) == [] + + +def test_get_request_users_apikey(db, rf): + request = rf.get('/', data={'apikey': 'apikey'}) + assert get_request_users(request) == [] + + api_user = ApiUser.objects.create(keytype='API', key='apikey') + assert get_request_users(request) == [api_user] + + +def test_get_request_users_http_auth_basic(db, rf): + api_user = ApiUser.objects.create(keytype='SIGN', username='john', key='password') + encoded = force_str(base64.b64encode(b'john:password')) + request = rf.get('/', HTTP_AUTHORIZATION='Basic ' + encoded) + assert get_request_users(request) == [api_user] + + +def test_get_request_users_http_auth_invalid(db, rf): + request = rf.get('/', HTTP_AUTHORIZATION='') + assert get_request_users(request) == [] + + # no param + request = rf.get('/', HTTP_AUTHORIZATION='Basic') + assert get_request_users(request) == [] + + # invalid base64 + request = rf.get('/', HTTP_AUTHORIZATION='Basic xx') + assert get_request_users(request) == [] + + # missing : in decoded base64 + request = rf.get('/', HTTP_AUTHORIZATION='Basic ' + force_str(base64.b64encode(b'x'))) + assert get_request_users(request) == [] + + # other schemes are ignored + request = rf.get('/', HTTP_AUTHORIZATION='Bearer') + assert get_request_users(request) == [] + request = rf.get('/', HTTP_AUTHORIZATION='Digest kjkjlkjlkjljlkj') + assert get_request_users(request) == []