Merge pull request #32928 from openedx/sr-pact-provider

feat: Pact Provider Verification for Profile Endpoint
This commit is contained in:
Swayam Rana
2023-08-14 10:13:01 -04:00
committed by GitHub
6 changed files with 205 additions and 1 deletions

View File

@@ -11,4 +11,7 @@ PROVIDER_STATES_URL = True
MOCK_USERNAME = 'Mock User'
######################### Add Authentication Middleware for Pact Verification Calls #########################
MIDDLEWARE = MIDDLEWARE + ['common.test.pacts.middleware.AuthenticationMiddleware', ]
MIDDLEWARE = MIDDLEWARE + [
'common.test.pacts.middleware.AuthenticationMiddleware',
'openedx.core.djangoapps.user_api.accounts.tests.pact.user-middleware',
]

View File

@@ -0,0 +1,78 @@
{
"consumer": {
"name": "frontend-app-profile"
},
"provider": {
"name": "edx-platform"
},
"interactions": [
{
"description": "A request for user's basic information",
"providerState": "I have a user's basic information",
"request": {
"method": "GET",
"path": "/api/user/v1/accounts/staff"
},
"response": {
"body": {
"bio": "This is my bio",
"country": "ME",
"name": "Lemon Seltzer",
"username": "staff",
"is_active": true,
"gender": "m",
"mailing_address": "Park Ave",
"goals": "Learn and Grow!",
"year_of_birth": 1901,
"phone_number": "+11234567890"
},
"headers": {"Content-Type": "application/json"},
"matchingRules": {
"$.body.bio": {
"match": "type"
},
"$.body.country": {
"match": "type"
},
"$.body.name": {
"match": "type"
},
"$.body.username": {
"match": "type"
},
"$.body.is_active": {
"match": "type"
},
"$.body.gender": {
"match": "type"
},
"$.body.mailing_address": {
"match": "type"
},
"$.body.goals": {
"match": "type"
},
"$.body.year_of_birth": {
"match": "type"
},
"$.body.phone_number": {
"match": "type"
},
"status": 200
}
}
}
],
"metadata": {
"pact-js": {
"version": "11.0.2"
},
"pactRust": {
"ffi": "0.4.0",
"models": "1.0.4"
},
"pactSpecification": {
"version": "2.0.0"
}
}
}

View File

@@ -0,0 +1,32 @@
"""
Contain the middleware logic needed during pact verification
"""
from django.contrib import auth
from django.utils.deprecation import MiddlewareMixin
User = auth.get_user_model()
class AuthenticationMiddleware(MiddlewareMixin):
"""
Middleware to add default authentication into the requests for pact verification.
This middleware is required to add a default authenticated user and bypass CSRF validation
into the requests during the pact verification workflow. Without the authentication, the pact verification
process will not work as the apis.
See https://docs.pact.io/faq#how-do-i-test-oauth-or-other-security-headers
"""
def __init__(self, get_response):
super().__init__(get_response)
self.auth_user = User.objects.get_or_create(username='staff', is_staff=True)[0]
self.get_response = get_response
def process_view(self, request, view_func, view_args, view_kwargs): # pylint: disable=unused-argument
"""
Add a default authenticated user and remove CSRF checks for a request
in a subset of views.
"""
if request.user.is_anonymous and 'Pact-Authentication' in request.headers:
request.user = self.auth_user
request._dont_enforce_csrf_checks = True # pylint: disable=protected-access

View File

@@ -0,0 +1,79 @@
"""
User Verification Server for Profile Information
"""
import os
import logging
from django.test import LiveServerTestCase
from django.urls import reverse
from pact import Verifier
from common.djangoapps.student.tests.factories import UserFactory
from common.djangoapps.student.models import User
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST
import json
log = logging.getLogger(__name__)
logging.basicConfig(level=logging.DEBUG)
PACT_DIR = os.path.dirname(os.path.realpath(__file__))
PACT_FILE = "frontend-app-profile-edx-platform.json"
class ProviderState():
""" Provider State for the testing profile """
def account_setup(self, request):
""" Sets up the Profile that we want to mock in accordance to our contract """
User.objects.filter(username="staff").delete()
user_acc = UserFactory.create(username="staff")
user_acc.profile.name = "Lemon Seltzer"
user_acc.profile.bio = "This is my bio"
user_acc.profile.country = "ME"
user_acc.profile.is_active = True
user_acc.profile.goals = "Learn and Grow!"
user_acc.profile.year_of_birth = 1901
user_acc.profile.phone_number = "+11234567890"
user_acc.profile.mailing_address = "Park Ave"
user_acc.profile.save()
return user_acc
@csrf_exempt
@require_POST
def provider_state(request):
""" Provider State view for our verifier"""
state_setup = {"I have a user's basic information": ProviderState().account_setup}
request_body = json.loads(request.body)
state = request_body.get('state')
User.objects.filter(username="staff").delete()
print('Setting up provider state for state value: {}'.format(state))
state_setup["I have a user's basic information"](request)
return JsonResponse({'result': state})
class ProviderVerificationServer(LiveServerTestCase):
""" Live Server for Pact Account Verification """
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.PACT_URL = cls.live_server_url
cls.verifier = Verifier(
provider='edx-platform',
provider_base_url=cls.PACT_URL,
)
@classmethod
def tearDownClass(cls):
super().tearDownClass()
def test_pact(self):
output, _ = self.verifier.verify_pacts(
os.path.join(PACT_DIR, PACT_FILE),
headers=['Pact-Authentication: Allow', ],
provider_states_setup_url=f"{self.PACT_URL}{reverse('acc-provider-state-view')}",
)
assert output == 0

View File

@@ -5,6 +5,7 @@ Defines the URL routes for this app.
from django.conf import settings
from django.urls import include, path, re_path
from django.conf.urls import url
from rest_framework import routers
from ..profile_images.views import ProfileImageView
@@ -226,3 +227,14 @@ urlpatterns = [
path('v1/preferences/time_zones/', user_api_views.CountryTimeZoneListView.as_view(),
),
]
# Provider States url for Account
if getattr(settings, 'PROVIDER_STATES_URL', None):
from openedx.core.djangoapps.user_api.accounts.tests.pact.verify_user import provider_state as acc_provider_state
urlpatterns += [
url(
r'^pact/provider_states/$',
acc_provider_state,
name='acc-provider-state-view',
)
]