fixes for front end saml work and to align with data requirements.

This commit is contained in:
Talia
2020-07-24 14:45:22 -04:00
parent de8ef23d24
commit 6d365ca1da
8 changed files with 122 additions and 11 deletions

View File

@@ -0,0 +1,33 @@
# Generated by Django 2.2.14 on 2020-07-21 16:50
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('third_party_auth', '0002_samlproviderconfig_country'),
]
operations = [
migrations.AlterField(
model_name='samlproviderconfig',
name='send_to_registration_first',
field=models.BooleanField(default=True, help_text='If this option is selected, users will be directed to the registration page immediately after authenticating with the third party instead of the login page.'),
),
migrations.AlterField(
model_name='samlproviderconfig',
name='skip_email_verification',
field=models.BooleanField(default=True, help_text='If this option is selected, users will not be required to confirm their email, and their account will be activated immediately upon registration.'),
),
migrations.AlterField(
model_name='samlproviderconfig',
name='skip_hinted_login_dialog',
field=models.BooleanField(default=True, help_text='If this option is enabled, users that visit a "TPA hinted" URL for this provider (e.g. a URL ending with `?tpa_hint=[provider_name]`) will be forwarded directly to the login URL of the provider instead of being first prompted with a login dialog.'),
),
migrations.AlterField(
model_name='samlproviderconfig',
name='skip_registration_form',
field=models.BooleanField(default=True, help_text='If this option is enabled, users will not be asked to confirm their details (name, email, etc.) during the registration process. Only select this option for trusted providers that are known to provide accurate user information.'),
),
]

View File

@@ -625,6 +625,36 @@ class SAMLProviderConfig(ProviderConfig):
),
blank=True,
)
skip_hinted_login_dialog = models.BooleanField(
default=True,
help_text=_(
"If this option is enabled, users that visit a \"TPA hinted\" URL for this provider "
"(e.g. a URL ending with `?tpa_hint=[provider_name]`) will be forwarded directly to "
"the login URL of the provider instead of being first prompted with a login dialog."
),
)
skip_registration_form = models.BooleanField(
default=True,
help_text=_(
"If this option is enabled, users will not be asked to confirm their details "
"(name, email, etc.) during the registration process. Only select this option "
"for trusted providers that are known to provide accurate user information."
),
)
skip_email_verification = models.BooleanField(
default=True,
help_text=_(
"If this option is selected, users will not be required to confirm their "
"email, and their account will be activated immediately upon registration."
),
)
send_to_registration_first = models.BooleanField(
default=True,
help_text=_(
"If this option is selected, users will be directed to the registration page "
"immediately after authenticating with the third party instead of the login page."
),
)
other_settings = models.TextField(
verbose_name=u"Advanced settings", blank=True,
help_text=(

View File

@@ -13,7 +13,7 @@ from rest_framework import status
from rest_framework.test import APITestCase
from enterprise.models import EnterpriseCustomerIdentityProvider, EnterpriseCustomer
from enterprise.constants import ENTERPRISE_ADMIN_ROLE
from enterprise.constants import ENTERPRISE_ADMIN_ROLE, ENTERPRISE_LEARNER_ROLE
from third_party_auth.tests.samlutils import set_jwt_cookie
from third_party_auth.models import SAMLProviderConfig
from third_party_auth.tests import testutil
@@ -218,3 +218,17 @@ class SAMLProviderConfigTests(APITestCase):
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
provider_config = SAMLProviderConfig.objects.get(slug='test-slug-empty')
self.assertEqual(provider_config.country, '')
def test_unauthenticated_request_is_forbidden(self):
self.client.logout()
urlbase = reverse('saml_provider_config-list')
query_kwargs = {'enterprise_customer_uuid': ENTERPRISE_ID}
url = '{}?{}'.format(urlbase, urlencode(query_kwargs))
set_jwt_cookie(self.client, self.user, [(ENTERPRISE_LEARNER_ROLE, ENTERPRISE_ID)])
response = self.client.get(url, format='json')
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
self.client.logout()
set_jwt_cookie(self.client, self.user, [(ENTERPRISE_ADMIN_ROLE, ENTERPRISE_ID_NON_EXISTENT)])
response = self.client.get(url, format='json')
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

View File

@@ -57,14 +57,14 @@ class SAMLProviderConfigViewSet(PermissionRequiredMixin, SAMLProviderMixin, view
EnterpriseCustomerIdentityProvider,
enterprise_customer__uuid=self.requested_enterprise_uuid
)
return SAMLProviderConfig.objects.filter(slug=enterprise_customer_idp.provider_id)
return SAMLProviderConfig.objects.current_set().filter(slug=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 == "POST":
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')

View File

@@ -11,7 +11,7 @@ from rest_framework import status
from rest_framework.test import APITestCase
from enterprise.models import EnterpriseCustomer, EnterpriseCustomerIdentityProvider
from enterprise.constants import ENTERPRISE_ADMIN_ROLE
from enterprise.constants import ENTERPRISE_ADMIN_ROLE, ENTERPRISE_LEARNER_ROLE
from third_party_auth.tests import testutil
from third_party_auth.models import SAMLProviderData, SAMLProviderConfig
@@ -39,6 +39,7 @@ SINGLE_PROVIDER_DATA_2['entity_id'] = 'http://entity-id-2'
SINGLE_PROVIDER_DATA_2['sso_url'] = 'http://test2.url'
ENTERPRISE_ID = str(uuid4())
BAD_ENTERPRISE_ID = str(uuid4())
@unittest.skipUnless(testutil.AUTH_FEATURE_ENABLED, testutil.AUTH_FEATURES_KEY + ' not enabled')
@@ -67,7 +68,7 @@ class SAMLProviderDataTests(APITestCase):
fetched_at=SINGLE_PROVIDER_DATA['fetched_at']
)
cls.enterprise_customer_idp, _ = EnterpriseCustomerIdentityProvider.objects.get_or_create(
provider_id=cls.saml_provider_config.id,
provider_id=cls.saml_provider_config.slug,
enterprise_customer_id=ENTERPRISE_ID
)
@@ -141,12 +142,12 @@ class SAMLProviderDataTests(APITestCase):
def test_delete_one_provider_data(self):
# DELETE auth/saml/v0/providerdata/ -d data
url = reverse('saml_provider_data-detail', kwargs={'pk': self.saml_provider_data.id})
data = {}
data['enterprise_customer_uuid'] = ENTERPRISE_ID
url_base = reverse('saml_provider_data-detail', kwargs={'pk': self.saml_provider_data.id})
query_kwargs = {'enterprise_customer_uuid': ENTERPRISE_ID}
url = '{}?{}'.format(url_base, urlencode(query_kwargs))
orig_count = SAMLProviderData.objects.count()
response = self.client.delete(url, data)
response = self.client.delete(url)
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
self.assertEqual(SAMLProviderData.objects.count(), orig_count - 1)
@@ -154,3 +155,29 @@ class SAMLProviderDataTests(APITestCase):
# ensure only the sso_url was updated
query_set_count = SAMLProviderData.objects.filter(pk=self.saml_provider_data.id).count()
self.assertEqual(query_set_count, 0)
def test_get_one_provider_data_failure(self):
set_jwt_cookie(self.client, self.user, [(ENTERPRISE_ADMIN_ROLE, BAD_ENTERPRISE_ID)])
self.client.force_authenticate(user=self.user)
url_base = reverse('saml_provider_data-list')
query_kwargs = {'enterprise_customer_uuid': BAD_ENTERPRISE_ID}
url = '{}?{}'.format(url_base, urlencode(query_kwargs))
response = self.client.get(url, format='json')
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
def test_unauthenticated_request_is_forbidden(self):
self.client.logout()
urlbase = reverse('saml_provider_data-list')
query_kwargs = {'enterprise_customer_uuid': ENTERPRISE_ID}
url = '{}?{}'.format(urlbase, urlencode(query_kwargs))
set_jwt_cookie(self.client, self.user, [(ENTERPRISE_LEARNER_ROLE, ENTERPRISE_ID)])
response = self.client.get(url, format='json')
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
# manually running second case as DDT is having issues.
self.client.logout()
set_jwt_cookie(self.client, self.user, [(ENTERPRISE_ADMIN_ROLE, BAD_ENTERPRISE_ID)])
response = self.client.get(url, format='json')
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

View File

@@ -3,6 +3,7 @@
"""
from django.shortcuts import get_object_or_404
from django.http import Http404
from edx_rbac.mixins import PermissionRequiredMixin
from edx_rest_framework_extensions.auth.jwt.authentication import JwtAuthentication
from rest_framework import permissions, viewsets
@@ -52,7 +53,10 @@ class SAMLProviderDataViewSet(PermissionRequiredMixin, SAMLProviderDataMixin, vi
EnterpriseCustomerIdentityProvider,
enterprise_customer__uuid=self.requested_enterprise_uuid
)
saml_provider = SAMLProviderConfig.objects.get(pk=enterprise_customer_idp.provider_id)
try:
saml_provider = SAMLProviderConfig.objects.current_set().get(slug=enterprise_customer_idp.provider_id)
except SAMLProviderConfig.DoesNotExist:
raise Http404('No matching SAML provider found.')
return SAMLProviderData.objects.filter(entity_id=saml_provider.entity_id)
@property
@@ -60,7 +64,7 @@ class SAMLProviderDataViewSet(PermissionRequiredMixin, SAMLProviderDataMixin, vi
"""
The enterprise customer uuid from request params or post body
"""
if self.request.method in ('POST', 'PATCH', 'DELETE'):
if self.request.method in ('POST', 'PATCH'):
uuid_str = self.request.POST.get('enterprise_customer_uuid')
if uuid_str is None:
raise ParseError('Required enterprise_customer_uuid is missing')

View File

@@ -109,6 +109,8 @@ class SamlIntegrationTestUtilities(object):
kwargs.setdefault('icon_class', 'fa-university')
kwargs.setdefault('attr_email', 'urn:oid:1.3.6.1.4.1.5923.1.1.1.6') # eduPersonPrincipalName
kwargs.setdefault('max_session_length', None)
kwargs.setdefault('send_to_registration_first', False)
kwargs.setdefault('skip_email_verification', False)
saml_provider = self.configure_saml_provider(**kwargs) # pylint: disable=no-member
if fetch_metadata:

View File

@@ -274,6 +274,7 @@ class LoginAndRegistrationTest(ThirdPartyAuthTestMixin, UrlResetMixin, ModuleSto
kwargs.setdefault('icon_class', 'fa-university')
kwargs.setdefault('attr_email', 'dummy-email-attr')
kwargs.setdefault('max_session_length', None)
kwargs.setdefault('skip_registration_form', False)
self.configure_saml_provider(**kwargs)
@mock.patch('django.conf.settings.MESSAGE_STORAGE', 'django.contrib.messages.storage.cookie.CookieStorage')