webhooks: Fix passing client string to authenticated webhook API views.

This fixes a regression in 93678e89cd
and a4979410f9, where the webhooks using
authenticated_rest_api_view were migrated to a new model that didn't
include setting a custom Client string for the webhook.

When restoring these webhooks' client strings, we also fix places
where the client string was not capitalized the same was as the
product's name.
This commit is contained in:
Tim Abbott 2018-03-16 15:37:32 -07:00
parent a4979410f9
commit 34e165c100
7 changed files with 21 additions and 10 deletions

View File

@ -294,8 +294,13 @@ body:
message = message.strip(' ')
webhook_logger.exception(message)
def full_webhook_client_name(raw_client_name: Optional[str]=None) -> Optional[str]:
if raw_client_name is None:
return None
return "Zulip{}Webhook".format(raw_client_name)
# Use this for webhook views that don't get an email passed in.
def api_key_only_webhook_view(client_name: Text) -> Callable[[ViewFuncT], ViewFuncT]:
def api_key_only_webhook_view(webhook_client_name: Text) -> Callable[[ViewFuncT], ViewFuncT]:
# TODO The typing here could be improved by using the Extended Callable types:
# https://mypy.readthedocs.io/en/latest/kinds_of_types.html#extended-callable-types
def _wrapped_view_func(view_func: ViewFuncT) -> ViewFuncT:
@ -305,7 +310,7 @@ def api_key_only_webhook_view(client_name: Text) -> Callable[[ViewFuncT], ViewFu
def _wrapped_func_arguments(request: HttpRequest, api_key: Text=REQ(),
*args: Any, **kwargs: Any) -> HttpResponse:
user_profile = validate_api_key(request, None, api_key, is_webhook=True,
client_name="Zulip{}Webhook".format(client_name))
client_name=full_webhook_client_name(webhook_client_name))
if settings.RATE_LIMITING:
rate_limit_user(request, user_profile, domain='all')
@ -468,7 +473,11 @@ def authenticated_api_view(is_webhook: bool=False) -> Callable[[ViewFuncT], View
# A more REST-y authentication decorator, using, in particular, HTTP Basic
# authentication.
def authenticated_rest_api_view(is_webhook: bool=False) -> Callable[[ViewFuncT], ViewFuncT]:
#
# If webhook_client_name is specific, the request is a webhook view
# with that string as the basis for the client string.
def authenticated_rest_api_view(*, webhook_client_name: str=None,
is_webhook: bool=False) -> Callable[[ViewFuncT], ViewFuncT]:
def _wrapped_view_func(view_func: ViewFuncT) -> ViewFuncT:
@csrf_exempt
@wraps(view_func)
@ -490,7 +499,9 @@ def authenticated_rest_api_view(is_webhook: bool=False) -> Callable[[ViewFuncT],
# Now we try to do authentication or die
try:
# profile is a Union[UserProfile, RemoteZulipServer]
profile = validate_api_key(request, role, api_key, is_webhook)
profile = validate_api_key(request, role, api_key,
is_webhook=is_webhook or webhook_client_name is not None,
client_name=full_webhook_client_name(webhook_client_name))
except JsonableError as e:
return json_unauthorized(e.msg)
# Apply rate limiting

View File

@ -99,7 +99,7 @@ def rest_dispatch(request: HttpRequest, **kwargs: Any) -> HttpResponse:
view_kwargs = {}
if 'allow_incoming_webhooks' in view_flags:
view_kwargs['is_webhook'] = True
target_function = authenticated_rest_api_view(**view_kwargs)(target_function)
target_function = authenticated_rest_api_view(**view_kwargs)(target_function) # type: ignore # likely mypy bug
# Pick a way to tell user they're not authed based on how the request was made
else:
# If this looks like a request from a top-level page in a

View File

@ -37,7 +37,7 @@ def beanstalk_decoder(view_func: ViewFuncT) -> ViewFuncT:
return _wrapped_view_func # type: ignore # https://github.com/python/mypy/issues/1927
@beanstalk_decoder
@authenticated_rest_api_view(is_webhook=True)
@authenticated_rest_api_view(webhook_client_name="Beanstalk")
@has_request_variables
def api_beanstalk_webhook(request: HttpRequest, user_profile: UserProfile,
payload: Dict[str, Any]=REQ(validator=check_dict([])),

View File

@ -11,7 +11,7 @@ from zerver.lib.webhooks.git import SUBJECT_WITH_BRANCH_TEMPLATE, \
get_push_commits_event_message
from zerver.models import UserProfile, get_client
@authenticated_rest_api_view(is_webhook=True)
@authenticated_rest_api_view(webhook_client_name="Bitbucket")
@has_request_variables
def api_bitbucket_webhook(request: HttpRequest, user_profile: UserProfile,
payload: Mapping[Text, Any]=REQ(validator=check_dict([])),

View File

@ -14,7 +14,7 @@ from zerver.models import UserProfile, get_client
# There's no raw JSON for us to work from. Thus, it makes sense to just write
# a template Zulip message within Desk.com and have the webhook extract that
# from the "data" param and post it, which this does.
@authenticated_rest_api_view(is_webhook=True)
@authenticated_rest_api_view(webhook_client_name="Desk")
@has_request_variables
def api_deskdotcom_webhook(request: HttpRequest, user_profile: UserProfile,
data: Text=REQ()) -> HttpResponse:

View File

@ -103,7 +103,7 @@ def format_freshdesk_ticket_creation_message(ticket: TicketDict) -> str:
return content
@authenticated_rest_api_view(is_webhook=True)
@authenticated_rest_api_view(webhook_client_name="Freshdesk")
@has_request_variables
def api_freshdesk_webhook(request: HttpRequest, user_profile: UserProfile,
payload: Dict[str, Any]=REQ(argument_type='body')) -> HttpResponse:

View File

@ -14,7 +14,7 @@ def truncate(string: Text, length: int) -> Text:
string = string[:length-3] + '...'
return string
@authenticated_rest_api_view(is_webhook=True)
@authenticated_rest_api_view(webhook_client_name="Zendesk")
@has_request_variables
def api_zendesk_webhook(request: HttpRequest, user_profile: UserProfile,
ticket_title: str=REQ(), ticket_id: str=REQ(),