Skip to content
Closed
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions corehq/apps/domain/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,13 @@ def determine_authtype_from_header(request, default=DIGEST):
return DIGEST
elif auth_header.startswith('bearer '):
return OAUTH2
elif request.META.get('HTTP_X_MAC_DIGEST', None):
return FORMPLAYER
elif _is_api_key_authentication(request):
# do this after header checks since it may read the request body which interferes with
# other auth methods e.g. HMAC
Comment on lines +79 to +80
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh that's tricky

return API_KEY

if request.META.get('HTTP_X_MAC_DIGEST', None):
return FORMPLAYER

return default


Expand Down
2 changes: 1 addition & 1 deletion corehq/apps/hqwebapp/session_details_endpoint/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ def post(self, request, *args, **kwargs):
'username': user.username,
'djangoUserId': user.pk,
'superUser': user.is_superuser,
'authToken': None,
'authToken': session_id,
'domains': list(domains),
'anonymous': False,
'enabled_toggles': list(enabled_toggles),
Expand Down
112 changes: 46 additions & 66 deletions corehq/apps/receiverwrapper/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from corehq.apps.domain.auth import (
BASIC,
DIGEST,
FORMPLAYER,
NOAUTH,
API_KEY,
OAUTH2,
Expand All @@ -33,6 +34,7 @@
login_or_basic_ex,
login_or_digest_ex,
login_or_api_key_ex,
login_or_formplayer_ex,
login_or_oauth2_ex,
two_factor_exempt,
)
Expand Down Expand Up @@ -338,65 +340,6 @@ def case_block_ok(case_updates):
)


@login_or_digest_ex(allow_cc_users=True)
@two_factor_exempt
@set_request_duration_reporting_threshold(60)
def _secure_post_digest(request, domain, app_id=None):
"""only ever called from secure post"""
return _process_form(
request=request,
domain=domain,
app_id=app_id,
user_id=request.couch_user.get_id,
authenticated=True,
)


@handle_401_response
@login_or_basic_ex(allow_cc_users=True)
@two_factor_exempt
@set_request_duration_reporting_threshold(60)
def _secure_post_basic(request, domain, app_id=None):
"""only ever called from secure post"""
return _process_form(
request=request,
domain=domain,
app_id=app_id,
user_id=request.couch_user.get_id,
authenticated=True,
)


@handle_401_response
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this isn't necessary on this view since the decorator only support basic auth

@login_or_oauth2_ex(allow_cc_users=True, oauth_scopes=['sync'])
@two_factor_exempt
@set_request_duration_reporting_threshold(60)
def _secure_post_oauth2(request, domain, app_id=None):
"""only ever called from secure post"""
return _process_form(
request=request,
domain=domain,
app_id=app_id,
user_id=request.couch_user.get_id,
authenticated=True,
)


@login_or_api_key_ex()
@require_permission(HqPermissions.edit_data)
@require_permission(HqPermissions.access_api)
@set_request_duration_reporting_threshold(60)
def _secure_post_api_key(request, domain, app_id=None):
"""only ever called from secure post"""
return _process_form(
request=request,
domain=domain,
app_id=app_id,
user_id=request.couch_user.get_id,
authenticated=True,
)


@waf_allow('XSS_BODY')
@location_safe
@csrf_exempt
Expand All @@ -405,23 +348,60 @@ def _secure_post_api_key(request, domain, app_id=None):
@set_request_duration_reporting_threshold(60)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I notice this decorator is on both the parent and the child views. Is it perhaps that one of the decorators doesn't call functools.wraps and so the wrapped fn doesn't have the proper attribute set?

def secure_post(request, domain, app_id=None):
authtype_map = {
DIGEST: _secure_post_digest,
BASIC: _secure_post_basic,
NOAUTH: _noauth_post,
API_KEY: _secure_post_api_key,
OAUTH2: _secure_post_oauth2,
DIGEST: [
two_factor_exempt,
login_or_digest_ex(allow_cc_users=True),
],
BASIC: [
two_factor_exempt,
login_or_basic_ex(allow_cc_users=True),
handle_401_response,
],
API_KEY: [
require_permission(HqPermissions.edit_data),
require_permission(HqPermissions.access_api),
login_or_api_key_ex(),
],
OAUTH2: [
two_factor_exempt,
login_or_oauth2_ex(allow_cc_users=True, oauth_scopes=['sync']),
],
FORMPLAYER: [
two_factor_exempt,
login_or_formplayer_ex(allow_cc_users=True)
],
}

if request.GET.get('authtype'):
authtype = request.GET['authtype']
else:
authtype = determine_authtype_from_request(request, default=BASIC)

if authtype == NOAUTH:
return _noauth_post(request, domain, app_id=app_id)

try:
decorated_view = authtype_map[authtype]
decorators = authtype_map[authtype]
except KeyError:
return HttpResponseBadRequest(
'authtype must be one of: {0}'.format(','.join(authtype_map))
)

return decorated_view(request, domain, app_id=app_id)
@set_request_duration_reporting_threshold(60)
def decorated_view(request, domain, app_id):
return _process_form(
request=request,
domain=domain,
app_id=app_id,
user_id=request.couch_user.get_id,
authenticated=True,
)

for decorator in decorators:
decorated_view = decorator(decorated_view)

return decorated_view(
request=request,
domain=domain,
app_id=app_id,
)