-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: adding skeleton for provisioning create api
ENT-9970. Adds a functional (in terms of auth N/Z, routing, tests) provisioning API skeleton that does no actual provisioning. * Now has drf-spec stuff configured.
- Loading branch information
1 parent
8b16678
commit b26a256
Showing
19 changed files
with
1,358 additions
and
1,069 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
""" | ||
Serializers for the provisioning app. | ||
""" | ||
import logging | ||
|
||
from django_countries.serializer_fields import CountryField | ||
from rest_framework import serializers | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
class BaseSerializer(serializers.Serializer): | ||
""" | ||
Base implementation for request and response serializers. | ||
""" | ||
def create(self, *args, **kwargs): | ||
return None | ||
|
||
def update(self, *args, **kwargs): | ||
return None | ||
|
||
|
||
## All the REQUEST serializers go under here ## | ||
|
||
|
||
class EnterpriseCustomerRequestSerializer(BaseSerializer): | ||
""" | ||
Customer object serializer for provisioning requests. | ||
""" | ||
name = serializers.CharField( | ||
help_text='The unique name of the Enterprise Customer.', | ||
) | ||
country = CountryField( | ||
help_text='The two letter ISO 3166-2 ISO code representing the customer country.', | ||
) | ||
slug = serializers.SlugField( | ||
help_text='An optional customer slug. One will be generated if not provided.', | ||
required=False, | ||
allow_blank=True, | ||
) | ||
|
||
|
||
class PendingCustomerAdminRequestSerializer(BaseSerializer): | ||
""" | ||
Pending admin serializer for provisioning requests. | ||
""" | ||
user_email = serializers.EmailField( | ||
help_text='The email address of the requested admin.', | ||
) | ||
|
||
|
||
class ProvisioningRequestSerializer(BaseSerializer): | ||
""" | ||
Request serializer for provisioning create view. | ||
""" | ||
enterprise_customer = EnterpriseCustomerRequestSerializer( | ||
help_text='Object describing the requested Enterprise Customer.' | ||
) | ||
pending_admins = PendingCustomerAdminRequestSerializer( | ||
help_text='List of objects containing requested customer admin email addresses.', | ||
many=True, | ||
) | ||
|
||
|
||
## All the RESPONSE serializers go under here ## | ||
|
||
|
||
class EnterpriseCustomerResponseSerializer(BaseSerializer): | ||
""" | ||
Customer object serializer for provisioning responses. | ||
""" | ||
uuid = serializers.UUIDField() | ||
name = serializers.CharField() | ||
country = CountryField() | ||
slug = serializers.SlugField(required=False, allow_blank=True) | ||
|
||
|
||
class PendingCustomerAdminResponseSerializer(BaseSerializer): | ||
""" | ||
Pending admin serializer for provisioning responses. | ||
""" | ||
user_email = serializers.EmailField() | ||
|
||
|
||
class ProvisioningResponseSerializer(BaseSerializer): | ||
""" | ||
Response serializer for provisioning create view. | ||
""" | ||
enterprise_customer = EnterpriseCustomerResponseSerializer() | ||
pending_admins = PendingCustomerAdminResponseSerializer(many=True) |
92 changes: 92 additions & 0 deletions
92
enterprise_access/apps/api/v1/tests/test_provisioning_views.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
""" | ||
Tests for the provisioning views. | ||
""" | ||
import uuid | ||
|
||
import ddt | ||
from edx_rbac.constants import ALL_ACCESS_CONTEXT | ||
from rest_framework import status | ||
from rest_framework.reverse import reverse | ||
|
||
from enterprise_access.apps.core.constants import ( | ||
SYSTEM_ENTERPRISE_ADMIN_ROLE, | ||
SYSTEM_ENTERPRISE_LEARNER_ROLE, | ||
SYSTEM_ENTERPRISE_OPERATOR_ROLE, | ||
SYSTEM_ENTERPRISE_PROVISIONING_ADMIN_ROLE | ||
) | ||
from test_utils import APITest | ||
|
||
PROVISIONING_CREATE_ENDPOINT = reverse('api:v1:provisioning-create') | ||
|
||
TEST_ENTERPRISE_UUID = uuid.uuid4() | ||
|
||
|
||
@ddt.ddt | ||
class TestProvisioningAuth(APITest): | ||
""" | ||
Tests Authentication and Permission checking for provisioning. | ||
""" | ||
@ddt.data( | ||
# A role that's not mapped to any feature perms will get you a 403. | ||
( | ||
{'system_wide_role': 'some-other-role', 'context': str(TEST_ENTERPRISE_UUID)}, | ||
status.HTTP_403_FORBIDDEN, | ||
), | ||
# A good learner role, AND in the correct context/customer STILL gets you a 403. | ||
# Provisioning APIs are inaccessible to all learners. | ||
( | ||
{'system_wide_role': SYSTEM_ENTERPRISE_LEARNER_ROLE, 'context': ALL_ACCESS_CONTEXT}, | ||
status.HTTP_403_FORBIDDEN, | ||
), | ||
# An admin role is not authorized to provision. | ||
( | ||
{'system_wide_role': SYSTEM_ENTERPRISE_ADMIN_ROLE, 'context': ALL_ACCESS_CONTEXT}, | ||
status.HTTP_403_FORBIDDEN, | ||
), | ||
# Even operators can't provision | ||
( | ||
{'system_wide_role': SYSTEM_ENTERPRISE_OPERATOR_ROLE, 'context': ALL_ACCESS_CONTEXT}, | ||
status.HTTP_403_FORBIDDEN, | ||
), | ||
# No JWT based auth, no soup for you. | ||
( | ||
None, | ||
status.HTTP_401_UNAUTHORIZED, | ||
), | ||
) | ||
@ddt.unpack | ||
def test_provisioning_create_view_forbidden(self, role_context_dict, expected_response_code): | ||
""" | ||
Tests that we get expected 40x responses for the provisioning create view.. | ||
""" | ||
# Set the JWT-based auth that we'll use for every request | ||
if role_context_dict: | ||
self.set_jwt_cookie([role_context_dict]) | ||
|
||
response = self.client.post(PROVISIONING_CREATE_ENDPOINT) | ||
assert response.status_code == expected_response_code | ||
|
||
def test_provisioning_create_allowed_for_provisioning_admins(self): | ||
""" | ||
Tests that we get expected 200 response for the provisioning create view when | ||
the requesting user has the correct system role and provides a valid request payload. | ||
""" | ||
self.set_jwt_cookie([{ | ||
'system_wide_role': SYSTEM_ENTERPRISE_PROVISIONING_ADMIN_ROLE, | ||
'context': ALL_ACCESS_CONTEXT, | ||
}]) | ||
|
||
request_payload = { | ||
"enterprise_customer": { | ||
"name": "Test customer", | ||
"country": "US", | ||
"slug": "test-customer", | ||
}, | ||
"pending_admins": [ | ||
{ | ||
"user_email": "[email protected]", | ||
}, | ||
], | ||
} | ||
response = self.client.post(PROVISIONING_CREATE_ENDPOINT, data=request_payload) | ||
assert response.status_code == status.HTTP_201_CREATED |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
""" | ||
Rest API views for the browse and request app. | ||
""" | ||
import logging | ||
|
||
from drf_spectacular.utils import extend_schema | ||
from edx_rbac.mixins import PermissionRequiredMixin | ||
from edx_rest_framework_extensions.auth.jwt.authentication import JwtAuthentication | ||
from rest_framework import generics, permissions, status | ||
from rest_framework.response import Response | ||
|
||
from enterprise_access.apps.api import serializers | ||
from enterprise_access.apps.core import constants | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
PROVISIONING_API_TAG = 'Provisioning' | ||
|
||
|
||
@extend_schema( | ||
tags=[PROVISIONING_API_TAG], | ||
summary='Create a new provisioning request.', | ||
request=serializers.ProvisioningRequestSerializer, | ||
responses={ | ||
status.HTTP_200_OK: serializers.ProvisioningResponseSerializer, | ||
status.HTTP_201_CREATED: serializers.ProvisioningResponseSerializer, | ||
}, | ||
) | ||
class ProvisioningCreateView(PermissionRequiredMixin, generics.CreateAPIView): | ||
""" | ||
Create view for provisioning. | ||
""" | ||
authentication_classes = (JwtAuthentication,) | ||
permission_classes = (permissions.IsAuthenticated,) | ||
permission_required = constants.PROVISIONING_CREATE_PERMISSION | ||
|
||
def create(self, request, *args, **kwargs): | ||
request_serializer = serializers.ProvisioningRequestSerializer(data=request.data) | ||
request_serializer.is_valid(raise_exception=True) | ||
return Response('ack', status=status.HTTP_201_CREATED) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.