Merge pull request #30055 from openedx/kiram15/ENT-5486
feat: add display name and make SAML config draft compatible
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
# Generated by Django 3.2.12 on 2022-03-14 15:51
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('third_party_auth', '0005_auto_20210723_1527'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='samlproviderconfig',
|
||||
name='display_name',
|
||||
field=models.CharField(blank=True, help_text='A configuration nickname.', max_length=35),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='ltiproviderconfig',
|
||||
name='name',
|
||||
field=models.CharField(blank=True, help_text='Name of this provider (shown to users)', max_length=50),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='oauth2providerconfig',
|
||||
name='name',
|
||||
field=models.CharField(blank=True, help_text='Name of this provider (shown to users)', max_length=50),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='samlconfiguration',
|
||||
name='slug',
|
||||
field=models.SlugField(blank=True, default='default', help_text='A short string uniquely identifying this configuration. Cannot contain spaces. Examples: "ubc", "mit-staging"', max_length=30),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='samlproviderconfig',
|
||||
name='backend_name',
|
||||
field=models.CharField(blank=True, default='tpa-saml', help_text="Which python-social-auth provider backend to use. 'tpa-saml' is the standard edX SAML backend.", max_length=50),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='samlproviderconfig',
|
||||
name='entity_id',
|
||||
field=models.CharField(blank=True, help_text='Example: https://idp.testshib.org/idp/shibboleth', max_length=255, verbose_name='Entity ID'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='samlproviderconfig',
|
||||
name='identity_provider_type',
|
||||
field=models.CharField(blank=True, choices=[('standard_saml_provider', 'Standard SAML provider'), ('sap_success_factors', 'SAP SuccessFactors provider')], default='standard_saml_provider', help_text='Some SAML providers require special behavior. For example, SAP SuccessFactors SAML providers require an additional API call to retrieve user metadata not provided in the SAML response. Select the provider type which best matches your use case. If in doubt, choose the Standard SAML Provider type.', max_length=128, verbose_name='Identity Provider Type'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='samlproviderconfig',
|
||||
name='metadata_source',
|
||||
field=models.CharField(blank=True, help_text="URL to this provider's XML metadata. Should be an HTTPS URL. Example: https://www.testshib.org/metadata/testshib-providers.xml", max_length=255),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='samlproviderconfig',
|
||||
name='name',
|
||||
field=models.CharField(blank=True, help_text='Name of this provider (shown to users)', max_length=50),
|
||||
),
|
||||
]
|
||||
@@ -111,7 +111,8 @@ class ProviderConfig(ConfigurationModel):
|
||||
'SVG images are recommended as they can scale to any size.'
|
||||
),
|
||||
)
|
||||
name = models.CharField(max_length=50, blank=False, help_text="Name of this provider (shown to users)")
|
||||
name = models.CharField(
|
||||
max_length=50, blank=True, help_text="Name of this provider (shown to users)")
|
||||
slug = models.SlugField(
|
||||
max_length=30, db_index=True, default='default',
|
||||
help_text=(
|
||||
@@ -429,6 +430,7 @@ class SAMLConfiguration(ConfigurationModel):
|
||||
slug = models.SlugField(
|
||||
max_length=30,
|
||||
default='default',
|
||||
blank=True,
|
||||
help_text=(
|
||||
'A short string uniquely identifying this configuration. '
|
||||
'Cannot contain spaces. Examples: "ubc", "mit-staging"'
|
||||
@@ -569,13 +571,17 @@ class SAMLProviderConfig(ProviderConfig):
|
||||
.. no_pii:
|
||||
"""
|
||||
prefix = 'saml'
|
||||
display_name = models.CharField(
|
||||
max_length=35, blank=True,
|
||||
help_text=_("A configuration nickname."))
|
||||
backend_name = models.CharField(
|
||||
max_length=50, default='tpa-saml', blank=False,
|
||||
max_length=50, default='tpa-saml', blank=True,
|
||||
help_text="Which python-social-auth provider backend to use. 'tpa-saml' is the standard edX SAML backend.")
|
||||
entity_id = models.CharField(
|
||||
max_length=255, verbose_name="Entity ID", help_text="Example: https://idp.testshib.org/idp/shibboleth")
|
||||
max_length=255, verbose_name="Entity ID", blank=True,
|
||||
help_text="Example: https://idp.testshib.org/idp/shibboleth")
|
||||
metadata_source = models.CharField(
|
||||
max_length=255,
|
||||
max_length=255, blank=True,
|
||||
help_text=(
|
||||
"URL to this provider's XML metadata. Should be an HTTPS URL. "
|
||||
"Example: https://www.testshib.org/metadata/testshib-providers.xml"
|
||||
@@ -622,7 +628,7 @@ class SAMLProviderConfig(ProviderConfig):
|
||||
"in the automatic refresh job, if configured."
|
||||
)
|
||||
identity_provider_type = models.CharField(
|
||||
max_length=128, blank=False, verbose_name="Identity Provider Type", default=STANDARD_SAML_PROVIDER_KEY,
|
||||
max_length=128, blank=True, verbose_name="Identity Provider Type", default=STANDARD_SAML_PROVIDER_KEY,
|
||||
choices=get_saml_idp_choices(), help_text=(
|
||||
"Some SAML providers require special behavior. For example, SAP SuccessFactors SAML providers require an "
|
||||
"additional API call to retrieve user metadata not provided in the SAML response. Select the provider type "
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
Tests for SAMLProviderConfig endpoints
|
||||
"""
|
||||
import copy
|
||||
import re
|
||||
from uuid import uuid4
|
||||
from django.urls import reverse
|
||||
from django.contrib.sites.models import Site
|
||||
@@ -32,6 +33,7 @@ SINGLE_PROVIDER_CONFIG = {
|
||||
SINGLE_PROVIDER_CONFIG_2 = copy.copy(SINGLE_PROVIDER_CONFIG)
|
||||
SINGLE_PROVIDER_CONFIG_2['name'] = 'name-of-config-2'
|
||||
SINGLE_PROVIDER_CONFIG_2['slug'] = 'test-slug-2'
|
||||
SINGLE_PROVIDER_CONFIG_2['display_name'] = 'display-name'
|
||||
|
||||
SINGLE_PROVIDER_CONFIG_3 = copy.copy(SINGLE_PROVIDER_CONFIG)
|
||||
SINGLE_PROVIDER_CONFIG_3['name'] = 'name-of-config-3'
|
||||
@@ -96,6 +98,7 @@ class SAMLProviderConfigTests(APITestCase):
|
||||
assert results[0]['entity_id'] == SINGLE_PROVIDER_CONFIG['entity_id']
|
||||
assert results[0]['metadata_source'] == SINGLE_PROVIDER_CONFIG['metadata_source']
|
||||
assert response.data['results'][0]['country'] == SINGLE_PROVIDER_CONFIG['country']
|
||||
assert re.match(r"test-slug-\d{4}", results[0]['display_name'])
|
||||
assert SAMLProviderConfig.objects.count() == 1
|
||||
|
||||
def test_get_one_config_by_enterprise_uuid_invalid_uuid(self):
|
||||
@@ -147,6 +150,7 @@ class SAMLProviderConfigTests(APITestCase):
|
||||
assert provider_config.name == 'name-of-config-2'
|
||||
assert provider_config.country == SINGLE_PROVIDER_CONFIG_2['country']
|
||||
assert provider_config.attr_username == SINGLE_PROVIDER_CONFIG['attr_first_name']
|
||||
assert provider_config.display_name == SINGLE_PROVIDER_CONFIG_2['display_name']
|
||||
|
||||
# check association has also been created
|
||||
assert EnterpriseCustomerIdentityProvider.objects.filter(
|
||||
|
||||
@@ -4,8 +4,9 @@ Signal handlers for program enrollments
|
||||
|
||||
|
||||
import logging
|
||||
from datetime import datetime
|
||||
|
||||
from django.db.models.signals import post_save
|
||||
from django.db.models.signals import pre_save, post_save
|
||||
from django.dispatch import receiver
|
||||
from social_django.models import UserSocialAuth
|
||||
|
||||
@@ -44,6 +45,25 @@ def listen_for_social_auth_creation(sender, instance, created, **kwargs): # lin
|
||||
raise
|
||||
|
||||
|
||||
def generate_default_display_name(self):
|
||||
"""
|
||||
Returns a default display name for SamlProviderConfig.
|
||||
"""
|
||||
time = datetime.now().strftime('%M%S')
|
||||
return f'{self.slug}-{time}'
|
||||
|
||||
|
||||
@receiver(pre_save, sender=SAMLProviderConfig)
|
||||
def save_default_display_name(sender, instance, **kwargs): # lint-amnesty, pylint: disable=unused-argument
|
||||
"""
|
||||
Post-save signal that sets default display name if one is not provided
|
||||
"""
|
||||
this_display_name = instance.display_name
|
||||
# check if display_name is None, empty, or just spaces
|
||||
if not (this_display_name and this_display_name.strip()):
|
||||
instance.display_name = generate_default_display_name(instance)
|
||||
|
||||
|
||||
def matriculate_learner(user, uid):
|
||||
"""
|
||||
Update any waiting program enrollments with a user,
|
||||
|
||||
Reference in New Issue
Block a user