* WIP: add third-party-auth scope and usage BOM-1264 * Fix tests now that we do permissions in a more standard way. Rather than manually setting the permission class we previously explicitly raised a PermissionDenied exception. The way DRF permissoning logic works, if we use the WWW-Authenticate header in the highest priority auth class, it will return a 401 instead of a 403. * Added test to make sure having permissions gives access to user mapping api * Test new filters logic. Ensure that the filters we add to the application access model make it into the JWT correctly. * quality fix * quality fix * disable pylint warning * quality fix * fix indent prob Co-authored-by: Feanil Patel <feanil@edx.org> Co-authored-by: Manjinder Singh <49171515+jinder1s@users.noreply.github.com>
92 lines
3.1 KiB
Python
92 lines
3.1 KiB
Python
"""
|
|
Third party auth API related permissions
|
|
"""
|
|
|
|
import logging
|
|
|
|
from edx_django_utils.monitoring import set_custom_metric
|
|
from edx_rest_framework_extensions.auth.jwt.decoder import decode_jwt_filters
|
|
from edx_rest_framework_extensions.permissions import (
|
|
IsSuperuser,
|
|
JwtHasScope,
|
|
JwtRestrictedApplication,
|
|
NotJwtRestrictedApplication
|
|
)
|
|
from rest_condition import C
|
|
from rest_framework.permissions import BasePermission
|
|
from third_party_auth.models import ProviderApiPermissions
|
|
|
|
from openedx.core.lib.api.permissions import ApiKeyHeaderPermission
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
|
class ThirdPartyAuthProviderApiPermission(BasePermission):
|
|
"""
|
|
Allow someone to access the view if they have valid OAuth client credential.
|
|
|
|
Deprecated: Only works for DOP oauth applications. To be removed as part of DOPrecation.
|
|
|
|
"""
|
|
def has_permission(self, request, view):
|
|
"""
|
|
Check if the OAuth client associated with auth token in current request has permission to access
|
|
the information for provider
|
|
"""
|
|
provider_id = view.kwargs.get('provider_id')
|
|
if not request.auth or not provider_id:
|
|
# doesn't have access token or no provider_id specified
|
|
return False
|
|
|
|
try:
|
|
ProviderApiPermissions.objects.get(client__pk=request.auth.client_id, provider_id=provider_id)
|
|
except ProviderApiPermissions.DoesNotExist:
|
|
return False
|
|
|
|
set_custom_metric('deprecated_ThirdPartyAuthProviderApiPermission', True)
|
|
return True
|
|
|
|
|
|
class JwtHasTpaProviderFilterForRequestedProvider(BasePermission):
|
|
"""
|
|
Ensures the JWT used to authenticate contains the appropriate tpa_provider
|
|
filter for the provider_id requested in the view.
|
|
"""
|
|
message = 'JWT missing required tpa_provider filter.'
|
|
|
|
def has_permission(self, request, view):
|
|
"""
|
|
Ensure that the provider_id kwarg provided to the view exists exists
|
|
in the tpa_provider filters in the JWT used to authenticate.
|
|
"""
|
|
provider_id = view.kwargs.get('provider_id')
|
|
if not provider_id:
|
|
log.warning("Permission JwtHasTpaProviderFilterForRequestedProvider requires a view with provider_id.")
|
|
return False
|
|
|
|
jwt_filters = decode_jwt_filters(request.auth)
|
|
for filter_type, filter_value in jwt_filters:
|
|
if filter_type == 'tpa_provider' and filter_value == provider_id:
|
|
return True
|
|
|
|
log.warning(
|
|
"Permission JwtHasTpaProviderFilterForRequestedProvider: required filter tpa_provider:%s was not found.",
|
|
provider_id,
|
|
)
|
|
return False
|
|
|
|
|
|
# TODO: Remove ApiKeyHeaderPermission. Check deprecated_api_key_header custom metric for active usage.
|
|
_NOT_JWT_RESTRICTED_TPA_PERMISSIONS = (
|
|
C(NotJwtRestrictedApplication) &
|
|
(C(IsSuperuser) | ApiKeyHeaderPermission | ThirdPartyAuthProviderApiPermission)
|
|
)
|
|
_JWT_RESTRICTED_TPA_PERMISSIONS = (
|
|
C(JwtRestrictedApplication) &
|
|
JwtHasScope &
|
|
JwtHasTpaProviderFilterForRequestedProvider
|
|
)
|
|
TPA_PERMISSIONS = (
|
|
(_NOT_JWT_RESTRICTED_TPA_PERMISSIONS | _JWT_RESTRICTED_TPA_PERMISSIONS)
|
|
)
|