Files
edx-platform/common/djangoapps/third_party_auth/samlproviderconfig/views.py
2021-03-19 15:39:48 +05:00

114 lines
4.9 KiB
Python

"""
Viewset for auth/saml/v0/samlproviderconfig
"""
from django.shortcuts import get_object_or_404
from edx_rbac.mixins import PermissionRequiredMixin
from edx_rest_framework_extensions.auth.jwt.authentication import JwtAuthentication
from rest_framework import permissions, viewsets, status
from rest_framework.response import Response
from rest_framework.authentication import SessionAuthentication
from rest_framework.exceptions import ParseError, ValidationError
from enterprise.models import EnterpriseCustomerIdentityProvider, EnterpriseCustomer
from common.djangoapps.third_party_auth.utils import validate_uuid4_string
from ..models import SAMLProviderConfig
from .serializers import SAMLProviderConfigSerializer
from ..utils import convert_saml_slug_provider_id
class SAMLProviderMixin:
authentication_classes = [JwtAuthentication, SessionAuthentication]
permission_classes = [permissions.IsAuthenticated]
serializer_class = SAMLProviderConfigSerializer
class SAMLProviderConfigViewSet(PermissionRequiredMixin, SAMLProviderMixin, viewsets.ModelViewSet):
"""
A View to handle SAMLProviderConfig CRUD
Usage:
NOTE: Only the GET request requires a request parameter, otherwise pass the uuid as part
of the post body
GET /auth/saml/v0/provider_config/?enterprise-id=uuid
POST /auth/saml/v0/provider_config/ -d postData (must contain 'enterprise_customer_uuid')
DELETE /auth/saml/v0/provider_config/:pk -d postData (must contain 'enterprise_customer_uuid')
PATCH /auth/saml/v0/provider_config/:pk -d postData (must contain 'enterprise_customer_uuid')
permission_required refers to the Django permission name defined
in enterprise.rules.
The associated rule will allow edx-rbac to check if the EnterpriseCustomer
returned by the get_permission_object method here, can be
accessed by the user making this request (request.user)
Access is only allowed if the user has the system role
of 'ENTERPRISE_ADMIN' which is defined in enterprise.constants
"""
permission_required = 'enterprise.can_access_admin_dashboard'
def get_queryset(self):
"""
Find and return the matching providerconfig for the given enterprise uuid
if an association exists in EnterpriseCustomerIdentityProvider model
"""
if self.requested_enterprise_uuid is None:
raise ParseError('Required enterprise_customer_uuid is missing')
enterprise_customer_idp = get_object_or_404(
EnterpriseCustomerIdentityProvider,
enterprise_customer__uuid=self.requested_enterprise_uuid
)
return SAMLProviderConfig.objects.current_set().filter(
slug=convert_saml_slug_provider_id(enterprise_customer_idp.provider_id))
@property
def requested_enterprise_uuid(self):
"""
The enterprise customer uuid from request params or post body
"""
if self.request.method in ('POST', 'PUT'):
uuid_str = self.request.POST.get('enterprise_customer_uuid')
if uuid_str is None:
raise ParseError('Required enterprise_customer_uuid is missing')
return uuid_str
else:
uuid_str = self.request.query_params.get('enterprise_customer_uuid')
if validate_uuid4_string(uuid_str) is False:
raise ParseError('Invalid UUID enterprise_customer_id')
return uuid_str
def get_permission_object(self):
"""
Retrieve an EnterpriseCustomer uuid to do auth against
Right now this is the same as from the request object
meaning that only users belonging to the same enterprise
can access these endpoints, we have to sort out the operator role use case
"""
return self.requested_enterprise_uuid
def create(self, request, *args, **kwargs):
"""
Process POST /auth/saml/v0/provider_config/ {postData}
"""
customer_uuid = self.requested_enterprise_uuid
try:
enterprise_customer = EnterpriseCustomer.objects.get(pk=customer_uuid)
except EnterpriseCustomer.DoesNotExist:
raise ValidationError(f'Enterprise customer not found at uuid: {customer_uuid}') # lint-amnesty, pylint: disable=raise-missing-from
# Create the samlproviderconfig model first
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
# Associate the enterprise customer with the provider
association_obj = EnterpriseCustomerIdentityProvider(
enterprise_customer=enterprise_customer,
provider_id=convert_saml_slug_provider_id(serializer.data['slug'])
)
association_obj.save()
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)