From ac2185a2e855daed41cab84c3bb6fa9fb51c77cd Mon Sep 17 00:00:00 2001 From: Zixuan James Li Date: Mon, 1 Aug 2022 16:50:38 -0400 Subject: [PATCH] decorator: Extract get_basic_credentials. Signed-off-by: Zixuan James Li --- zerver/decorator.py | 60 +++++++++++++++++++++++++++++++-------------- 1 file changed, 42 insertions(+), 18 deletions(-) diff --git a/zerver/decorator.py b/zerver/decorator.py index 8b826c61de..830290711e 100644 --- a/zerver/decorator.py +++ b/zerver/decorator.py @@ -4,7 +4,18 @@ import logging import urllib from functools import wraps from io import BytesIO -from typing import TYPE_CHECKING, Callable, Dict, Optional, Sequence, TypeVar, Union, cast, overload +from typing import ( + TYPE_CHECKING, + Callable, + Dict, + Optional, + Sequence, + Tuple, + TypeVar, + Union, + cast, + overload, +) import django_otp from django.conf import settings @@ -711,6 +722,33 @@ def authenticated_uploads_api_view( return _wrapped_view_func +def get_basic_credentials( + request: HttpRequest, beanstalk_email_decode: bool = False +) -> Tuple[str, str]: + """ + Extracts the role and API key as a tuple from the Authorization header + for HTTP basic authentication. + """ + try: + # Grab the base64-encoded authentication string, decode it, and split it into + # the email and API key + auth_type, credentials = request.headers["Authorization"].split() + # case insensitive per RFC 1945 + if auth_type.lower() != "basic": + raise JsonableError(_("This endpoint requires HTTP basic authentication.")) + role, api_key = base64.b64decode(credentials).decode().split(":") + if beanstalk_email_decode: + # Beanstalk's web hook UI rejects URL with a @ in the username section + # So we ask the user to replace them with %40 + role = role.replace("%40", "@") + except ValueError: + raise UnauthorizedError(_("Invalid authorization header for basic auth")) + except KeyError: + raise UnauthorizedError(_("Missing authorization header for basic auth")) + + return role, api_key + + # A more REST-y authentication decorator, using, in particular, HTTP basic # authentication. # @@ -737,23 +775,9 @@ def authenticated_rest_api_view( def _wrapped_func_arguments( request: HttpRequest, /, *args: ParamT.args, **kwargs: ParamT.kwargs ) -> HttpResponse: - # First try block attempts to get the credentials we need to do authentication - try: - # Grab the base64-encoded authentication string, decode it, and split it into - # the email and API key - auth_type, credentials = request.headers["Authorization"].split() - # case insensitive per RFC 1945 - if auth_type.lower() != "basic": - raise JsonableError(_("This endpoint requires HTTP basic authentication.")) - role, api_key = base64.b64decode(credentials).decode().split(":") - if beanstalk_email_decode: - # Beanstalk's web hook UI rejects URL with a @ in the username section - # So we ask the user to replace them with %40 - role = role.replace("%40", "@") - except ValueError: - raise UnauthorizedError(_("Invalid authorization header for basic auth")) - except KeyError: - raise UnauthorizedError(_("Missing authorization header for basic auth")) + role, api_key = get_basic_credentials( + request, beanstalk_email_decode=beanstalk_email_decode + ) # Now we try to do authentication or die try: