Merge pull request #19088 from edx/saleem-latif/ENT-1088
ENT-1088: Third Party Authentication (SSO): Default values for SAML attributes
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.15 on 2018-10-12 07:07
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('third_party_auth', '0021_sso_id_verification'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='samlproviderconfig',
|
||||
name='default_email',
|
||||
field=models.CharField(blank=True, help_text=b'Default value for email to be used if not present in SAML response.', max_length=255, verbose_name=b'Default Value for Email'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='samlproviderconfig',
|
||||
name='default_first_name',
|
||||
field=models.CharField(blank=True, help_text=b'Default value for first name to be used if not present in SAML response.', max_length=255, verbose_name=b'Default Value for First Name'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='samlproviderconfig',
|
||||
name='default_full_name',
|
||||
field=models.CharField(blank=True, help_text=b'Default value for full name to be used if not present in SAML response.', max_length=255, verbose_name=b'Default Value for Full Name'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='samlproviderconfig',
|
||||
name='default_last_name',
|
||||
field=models.CharField(blank=True, help_text=b'Default value for last name to be used if not present in SAML response.', max_length=255, verbose_name=b'Default Value for Last Name'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='samlproviderconfig',
|
||||
name='default_username',
|
||||
field=models.CharField(blank=True, help_text=b'Default value for username to be used if not present in SAML response.', max_length=255, verbose_name=b'Default Value for Username'),
|
||||
),
|
||||
]
|
||||
@@ -543,18 +543,33 @@ class SAMLProviderConfig(ProviderConfig):
|
||||
attr_full_name = models.CharField(
|
||||
max_length=128, blank=True, verbose_name="Full Name Attribute",
|
||||
help_text="URN of SAML attribute containing the user's full name. Leave blank for default.")
|
||||
default_full_name = models.CharField(
|
||||
max_length=255, blank=True, verbose_name="Default Value for Full Name",
|
||||
help_text="Default value for full name to be used if not present in SAML response.")
|
||||
attr_first_name = models.CharField(
|
||||
max_length=128, blank=True, verbose_name="First Name Attribute",
|
||||
help_text="URN of SAML attribute containing the user's first name. Leave blank for default.")
|
||||
default_first_name = models.CharField(
|
||||
max_length=255, blank=True, verbose_name="Default Value for First Name",
|
||||
help_text="Default value for first name to be used if not present in SAML response.")
|
||||
attr_last_name = models.CharField(
|
||||
max_length=128, blank=True, verbose_name="Last Name Attribute",
|
||||
help_text="URN of SAML attribute containing the user's last name. Leave blank for default.")
|
||||
default_last_name = models.CharField(
|
||||
max_length=255, blank=True, verbose_name="Default Value for Last Name",
|
||||
help_text="Default value for last name to be used if not present in SAML response.")
|
||||
attr_username = models.CharField(
|
||||
max_length=128, blank=True, verbose_name="Username Hint Attribute",
|
||||
help_text="URN of SAML attribute to use as a suggested username for this user. Leave blank for default.")
|
||||
default_username = models.CharField(
|
||||
max_length=255, blank=True, verbose_name="Default Value for Username",
|
||||
help_text="Default value for username to be used if not present in SAML response.")
|
||||
attr_email = models.CharField(
|
||||
max_length=128, blank=True, verbose_name="Email Attribute",
|
||||
help_text="URN of SAML attribute containing the user's email address[es]. Leave blank for default.")
|
||||
default_email = models.CharField(
|
||||
max_length=255, blank=True, verbose_name="Default Value for Email",
|
||||
help_text="Default value for email to be used if not present in SAML response.")
|
||||
automatic_refresh_enabled = models.BooleanField(
|
||||
default=True, verbose_name="Enable automatic metadata refresh",
|
||||
help_text="When checked, the SAML provider's metadata will be included "
|
||||
@@ -643,10 +658,27 @@ class SAMLProviderConfig(ProviderConfig):
|
||||
attrs = (
|
||||
'attr_user_permanent_id', 'attr_full_name', 'attr_first_name',
|
||||
'attr_last_name', 'attr_username', 'attr_email', 'entity_id')
|
||||
attr_defaults = {
|
||||
'attr_full_name': 'default_full_name',
|
||||
'attr_first_name': 'default_first_name',
|
||||
'attr_last_name': 'default_last_name',
|
||||
'attr_username': 'default_username',
|
||||
'attr_email': 'default_email',
|
||||
}
|
||||
|
||||
# Defaults for missing attributes in SAML Response
|
||||
conf['attr_defaults'] = {}
|
||||
|
||||
for field in attrs:
|
||||
field_name = attr_defaults.get(field)
|
||||
val = getattr(self, field)
|
||||
if val:
|
||||
conf[field] = val
|
||||
|
||||
# Default values for SAML attributes
|
||||
default = getattr(self, field_name) if field_name else None
|
||||
conf['attr_defaults'][field] = default
|
||||
|
||||
# Now get the data fetched automatically from the metadata.xml:
|
||||
data = SAMLProviderData.current(self.entity_id)
|
||||
if not data or not data.is_valid():
|
||||
|
||||
@@ -211,6 +211,17 @@ class EdXSAMLIdentityProvider(SAMLIdentityProvider):
|
||||
})
|
||||
return details
|
||||
|
||||
def get_attr(self, attributes, conf_key, default_attribute):
|
||||
"""
|
||||
Internal helper method.
|
||||
Get the attribute 'default_attribute' out of the attributes,
|
||||
unless self.conf[conf_key] overrides the default by specifying
|
||||
another attribute to use.
|
||||
"""
|
||||
key = self.conf.get(conf_key, default_attribute)
|
||||
default = self.conf['attr_defaults'].get(conf_key) or None
|
||||
return attributes[key][0] if key in attributes else default
|
||||
|
||||
@property
|
||||
def saml_sp_configuration(self):
|
||||
"""Get the SAMLConfiguration for this IdP"""
|
||||
@@ -242,6 +253,14 @@ class SapSuccessFactorsIdentityProvider(EdXSAMLIdentityProvider):
|
||||
'country': 'country',
|
||||
}
|
||||
|
||||
defaults_value_mapping = {
|
||||
'defaultFullName': 'attr_full_name',
|
||||
'firstName': 'attr_first_name',
|
||||
'lastName': 'attr_last_name',
|
||||
'username': 'attr_username',
|
||||
'email': 'attr_email',
|
||||
}
|
||||
|
||||
# Define a simple mapping to relate SAPSF values to Open edX-compatible values for
|
||||
# any given field. By default, this only contains the Country field, as SAPSF supplies
|
||||
# a country name, which has to be translated to a country code.
|
||||
@@ -260,7 +279,12 @@ class SapSuccessFactorsIdentityProvider(EdXSAMLIdentityProvider):
|
||||
Get a dictionary mapping registration field names to default values.
|
||||
"""
|
||||
field_mapping = self.field_mappings
|
||||
registration_fields = {edx_name: response['d'].get(odata_name, '') for odata_name, edx_name in field_mapping.items()}
|
||||
value_defaults = self.conf.get('attr_defaults', {})
|
||||
value_defaults = {key: value_defaults.get(value, '') for key, value in self.defaults_value_mapping.items()}
|
||||
registration_fields = {
|
||||
edx_name: response['d'].get(odata_name, value_defaults.get(odata_name, ''))
|
||||
for odata_name, edx_name in field_mapping.items()
|
||||
}
|
||||
value_mapping = self.value_mappings
|
||||
for field, value in registration_fields.items():
|
||||
if field in value_mapping and value in value_mapping[field]:
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,19 @@
|
||||
<saml2p:Response xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol" Destination="http://example.none/auth/complete/tpa-saml/" ID="_a07fd9a0848373e55320dc342494ef5d" InResponseTo="TESTID" IssueInstant="2015-06-15T00:07:15.188Z" Version="2.0"><saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">https://idp.testshib.org/idp/shibboleth</saml2:Issuer><saml2p:Status><saml2p:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/></saml2p:Status><saml2:Assertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:xs="http://www.w3.org/2001/XMLSchema" ID="_302d198c26a82fda79326d40c9237bc0" IssueInstant="2015-06-15T00:07:15.188Z" Version="2.0"><saml2:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">https://idp.testshib.org/idp/shibboleth</saml2:Issuer><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/><ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/><ds:Reference URI="#_302d198c26a82fda79326d40c9237bc0"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"><ec:InclusiveNamespaces xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="xs"/></ds:Transform></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><ds:DigestValue>DCu+i7m7i+AmlzMmsqSLMvjjMiRi6r9ocGsGymCTRj0=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>UJahwzSLB/I8S3zqwqnn+NxR5gYcZ+QqAwn7CDxuTcsGt2XzNH7OXuzWRjInSRaQy/Wl2gOFGRKITiVBuv4FLetA9u1S7MAB10o8Y5NkHtKQqB0nqCt/FkJnfYVcx1j3LS11vtvggQm9Dahfmxglv4B3lM3JHdy0/EKgE1H9QPQ0040Ii28IHWOj6KjjY/hSBdxvq8t/EM6WN0B10e7YFVZ1xoVXuKn6GHIRGMYVXTlkTSVm6e+BadB1YNAIkw4bEulKO0af52qxS8JcL9kbsLW6kRIBQj43ZXjuOK7xgCBlfzYM4wYA+jVQBiKAJDHAiUFd1NAqWgMkrbwzWSJjJw==</ds:SignatureValue><ds:KeyInfo><ds:X509Data><ds:X509Certificate>MIIEDjCCAvagAwIBAgIBADANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJVUzEVMBMGA1UECBMM
|
||||
UGVubnN5bHZhbmlhMRMwEQYDVQQHEwpQaXR0c2J1cmdoMREwDwYDVQQKEwhUZXN0U2hpYjEZMBcG
|
||||
A1UEAxMQaWRwLnRlc3RzaGliLm9yZzAeFw0wNjA4MzAyMTEyMjVaFw0xNjA4MjcyMTEyMjVaMGcx
|
||||
CzAJBgNVBAYTAlVTMRUwEwYDVQQIEwxQZW5uc3lsdmFuaWExEzARBgNVBAcTClBpdHRzYnVyZ2gx
|
||||
ETAPBgNVBAoTCFRlc3RTaGliMRkwFwYDVQQDExBpZHAudGVzdHNoaWIub3JnMIIBIjANBgkqhkiG
|
||||
9w0BAQEFAAOCAQ8AMIIBCgKCAQEArYkCGuTmJp9eAOSGHwRJo1SNatB5ZOKqDM9ysg7CyVTDClcp
|
||||
u93gSP10nH4gkCZOlnESNgttg0r+MqL8tfJC6ybddEFB3YBo8PZajKSe3OQ01Ow3yT4I+Wdg1tsT
|
||||
pSge9gEz7SrC07EkYmHuPtd71CHiUaCWDv+xVfUQX0aTNPFmDixzUjoYzbGDrtAyCqA8f9CN2txI
|
||||
fJnpHE6q6CmKcoLADS4UrNPlhHSzd614kR/JYiks0K4kbRqCQF0Dv0P5Di+rEfefC6glV8ysC8dB
|
||||
5/9nb0yh/ojRuJGmgMWHgWk6h0ihjihqiu4jACovUZ7vVOCgSE5Ipn7OIwqd93zp2wIDAQABo4HE
|
||||
MIHBMB0GA1UdDgQWBBSsBQ869nh83KqZr5jArr4/7b+QazCBkQYDVR0jBIGJMIGGgBSsBQ869nh8
|
||||
3KqZr5jArr4/7b+Qa6FrpGkwZzELMAkGA1UEBhMCVVMxFTATBgNVBAgTDFBlbm5zeWx2YW5pYTET
|
||||
MBEGA1UEBxMKUGl0dHNidXJnaDERMA8GA1UEChMIVGVzdFNoaWIxGTAXBgNVBAMTEGlkcC50ZXN0
|
||||
c2hpYi5vcmeCAQAwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAjR29PhrCbk8qLN5M
|
||||
FfSVk98t3CT9jHZoYxd8QMRLI4j7iYQxXiGJTT1FXs1nd4Rha9un+LqTfeMMYqISdDDI6tv8iNpk
|
||||
OAvZZUosVkUo93pv1T0RPz35hcHHYq2yee59HJOco2bFlcsH8JBXRSRrJ3Q7Eut+z9uo80JdGNJ4
|
||||
/SJy5UorZ8KazGj16lfJhOBXldgrhppQBb0Nq6HKHguqmwRfJ+WkxemZXzhediAjGeka8nz8Jjwx
|
||||
pUjAiSWYKLtJhGEaTqCYxCCX2Dw+dOTqUzHOZ7WKv4JXPK5G/Uhr8K/qhmFT2nIQi538n6rVYLeW
|
||||
j8Bbnl+ev0peYzxFyF5sQA==</ds:X509Certificate></ds:X509Data></ds:KeyInfo></ds:Signature><saml2:Subject><saml2:NameID Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient" NameQualifier="https://idp.testshib.org/idp/shibboleth">_37f8251113491e2cd666495938ccf270</saml2:NameID><saml2:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><saml2:SubjectConfirmationData Address="70.36.54.223" InResponseTo="TESTID" NotOnOrAfter="2015-06-15T00:12:15.188Z" Recipient="http://example.none/auth/complete/tpa-saml/"/></saml2:SubjectConfirmation></saml2:Subject><saml2:Conditions NotBefore="2015-06-15T00:07:15.188Z" NotOnOrAfter="2015-06-15T00:12:15.188Z"><saml2:AudienceRestriction><saml2:Audience>https://saml.example.none</saml2:Audience></saml2:AudienceRestriction></saml2:Conditions><saml2:AuthnStatement AuthnInstant="2015-06-15T00:07:14.939Z" SessionIndex="_6302f91a5c4c51987d814d16002480d2"><saml2:SubjectLocality Address="70.36.54.223"/><saml2:AuthnContext><saml2:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml2:AuthnContextClassRef></saml2:AuthnContext></saml2:AuthnStatement><saml2:AttributeStatement><saml2:Attribute FriendlyName="uid" Name="urn:oid:0.9.2342.19200300.100.1.1" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"><saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">myself</saml2:AttributeValue></saml2:Attribute><saml2:Attribute FriendlyName="eduPersonAffiliation" Name="urn:oid:1.3.6.1.4.1.5923.1.1.1.1" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"><saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">Member</saml2:AttributeValue><saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">Staff</saml2:AttributeValue></saml2:Attribute><saml2:Attribute FriendlyName="eduPersonPrincipalName" Name="urn:oid:1.3.6.1.4.1.5923.1.1.1.6" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"><saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">myself@testshib.org</saml2:AttributeValue></saml2:Attribute><saml2:Attribute FriendlyName="sn" Name="urn:oid:2.5.4.4" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"><saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">And I</saml2:AttributeValue></saml2:Attribute><saml2:Attribute FriendlyName="eduPersonScopedAffiliation" Name="urn:oid:1.3.6.1.4.1.5923.1.1.1.9" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"><saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">Member@testshib.org</saml2:AttributeValue><saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">Staff@testshib.org</saml2:AttributeValue></saml2:Attribute><saml2:Attribute FriendlyName="givenName" Name="urn:oid:2.5.4.42" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"><saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">Me Myself</saml2:AttributeValue></saml2:Attribute><saml2:Attribute FriendlyName="eduPersonEntitlement" Name="urn:oid:1.3.6.1.4.1.5923.1.1.1.7" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"><saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">urn:mace:dir:entitlement:common-lib-terms</saml2:AttributeValue></saml2:Attribute><saml2:Attribute FriendlyName="cn" Name="urn:oid:2.5.4.3" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"><saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">Me Myself And I</saml2:AttributeValue></saml2:Attribute><saml2:Attribute FriendlyName="eduPersonTargetedID" Name="urn:oid:1.3.6.1.4.1.5923.1.1.1.10" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"><saml2:AttributeValue><saml2:NameID Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent" NameQualifier="https://idp.testshib.org/idp/shibboleth" SPNameQualifier="https://saml.example.none">uJ4gM4NoVMUAwfe16VxPYEVyfj0=</saml2:NameID></saml2:AttributeValue></saml2:Attribute><saml2:Attribute FriendlyName="telephoneNumber" Name="urn:oid:2.5.4.20" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"><saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">555-5555</saml2:AttributeValue></saml2:Attribute></saml2:AttributeStatement></saml2:Assertion></saml2p:Response>
|
||||
@@ -4,6 +4,7 @@ Third_party_auth integration tests using a mock version of the TestShib provider
|
||||
import datetime
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import unittest
|
||||
from unittest import skip
|
||||
|
||||
@@ -25,7 +26,7 @@ from openedx.features.enterprise_support.tests.factories import EnterpriseCustom
|
||||
from third_party_auth import pipeline
|
||||
from third_party_auth.saml import SapSuccessFactorsIdentityProvider, log as saml_log
|
||||
from third_party_auth.tasks import fetch_saml_metadata
|
||||
from third_party_auth.tests import testutil
|
||||
from third_party_auth.tests import testutil, utils
|
||||
|
||||
from .base import IntegrationTestMixin
|
||||
|
||||
@@ -124,10 +125,15 @@ class SamlIntegrationTestUtilities(object):
|
||||
""" Mocked: the user logs in to TestShib and then gets redirected back """
|
||||
# The SAML provider (TestShib) will authenticate the user, then get the browser to POST a response:
|
||||
self.assertTrue(provider_redirect_url.startswith(TESTSHIB_SSO_URL))
|
||||
|
||||
saml_response_xml = utils.read_and_pre_process_xml(
|
||||
os.path.join(os.path.dirname(os.path.dirname(__file__)), 'data', 'testshib_saml_response.xml')
|
||||
)
|
||||
|
||||
return self.client.post(
|
||||
self.complete_url,
|
||||
content_type='application/x-www-form-urlencoded',
|
||||
data=self.read_data_file('testshib_response.txt'),
|
||||
data=utils.prepare_saml_response_from_xml(saml_response_xml),
|
||||
)
|
||||
|
||||
|
||||
@@ -488,6 +494,57 @@ class SuccessFactorsIntegrationTest(SamlIntegrationTestUtilities, IntegrationTes
|
||||
)
|
||||
self._test_register(country=expected_country)
|
||||
|
||||
def test_register_sapsf_with_value_default(self):
|
||||
"""
|
||||
Configure the provider such that it can talk to a mocked-out version of the SAP SuccessFactors
|
||||
API, and ensure that the data it gets that way gets passed to the registration form.
|
||||
|
||||
Check that value mappings overrides work in cases where we override a value other than
|
||||
what we're looking for, and when an empty override is provided it should use the default value
|
||||
provided by the configuration.
|
||||
"""
|
||||
# Mock the call to the SAP SuccessFactors OData user endpoint
|
||||
ODATA_USER_URL = (
|
||||
'http://api.successfactors.com/odata/v2/User(userId=\'myself\')'
|
||||
'?$select=username,firstName,country,lastName,defaultFullName,email'
|
||||
)
|
||||
|
||||
def user_callback(request, _uri, headers):
|
||||
auth_header = request.headers.get('Authorization')
|
||||
self.assertEqual(auth_header, 'Bearer faketoken')
|
||||
return (
|
||||
200,
|
||||
headers,
|
||||
json.dumps({
|
||||
'd': {
|
||||
'username': 'jsmith',
|
||||
'firstName': 'John',
|
||||
'lastName': 'Smith',
|
||||
'defaultFullName': 'John Smith',
|
||||
'country': 'Australia'
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
httpretty.register_uri(httpretty.GET, ODATA_USER_URL, content_type='application/json', body=user_callback)
|
||||
|
||||
provider_settings = {
|
||||
'sapsf_oauth_root_url': 'http://successfactors.com/oauth/',
|
||||
'sapsf_private_key': 'fake_private_key_here',
|
||||
'odata_api_root_url': 'http://api.successfactors.com/odata/v2/',
|
||||
'odata_company_id': 'NCC1701D',
|
||||
'odata_client_id': 'TatVotSEiCMteSNWtSOnLanCtBGwNhGB',
|
||||
}
|
||||
|
||||
self._configure_testshib_provider(
|
||||
identity_provider_type='sap_success_factors',
|
||||
metadata_source=TESTSHIB_METADATA_URL,
|
||||
other_settings=json.dumps(provider_settings),
|
||||
default_email='default@testshib.org'
|
||||
)
|
||||
self.USER_EMAIL = 'default@testshib.org'
|
||||
self._test_register()
|
||||
|
||||
@patch.dict('django.conf.settings.REGISTRATION_EXTRA_FIELDS', country='optional')
|
||||
def test_register_sapsf_metadata_present_override_relevant_value(self):
|
||||
"""
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
"""Common utility for testing third party oauth2 features."""
|
||||
import json
|
||||
from base64 import b64encode
|
||||
|
||||
import httpretty
|
||||
from onelogin.saml2.utils import OneLogin_Saml2_Utils
|
||||
|
||||
from provider.constants import PUBLIC
|
||||
from provider.oauth2.models import Client
|
||||
from social_core.backends.facebook import FacebookOAuth2, API_VERSION as FACEBOOK_API_VERSION
|
||||
@@ -96,3 +99,42 @@ class ThirdPartyOAuthTestMixinGoogle(object):
|
||||
USER_URL = "https://www.googleapis.com/plus/v1/people/me"
|
||||
# In google-oauth2 responses, the "email" field is used as the user's identifier
|
||||
UID_FIELD = "email"
|
||||
|
||||
|
||||
def read_and_pre_process_xml(file_name):
|
||||
"""
|
||||
Read XML file with the name specified in the argument and pre process the xml so that it can be parsed.
|
||||
|
||||
Pre Processing removes line retune characters (i.e. "\n").
|
||||
|
||||
Arguments:
|
||||
file_name (str): Name of the XML file.
|
||||
|
||||
Returns:
|
||||
(str): Pre Processed contents of the file.
|
||||
"""
|
||||
with open(file_name, 'r') as xml_file:
|
||||
return xml_file.read().replace('\n', '')
|
||||
|
||||
|
||||
def prepare_saml_response_from_xml(xml, relay_state='testshib'):
|
||||
"""
|
||||
Pre Process XML so that it can be used as a SAML Response coming from SAML IdP.
|
||||
|
||||
This method will perform the following operations on the XML in given order
|
||||
|
||||
1. base64 encode XML.
|
||||
2. URL encode the base64 encoded data.
|
||||
|
||||
Arguments:
|
||||
xml (string): XML data
|
||||
relay_state (string): Relay State of the SAML Response
|
||||
|
||||
Returns:
|
||||
(str): Base64 and URL encoded XML.
|
||||
"""
|
||||
b64encoded_xml = b64encode(xml)
|
||||
return 'RelayState={relay_state}&SAMLResponse={saml_response}'.format(
|
||||
relay_state=OneLogin_Saml2_Utils.case_sensitive_urlencode(relay_state),
|
||||
saml_response=OneLogin_Saml2_Utils.case_sensitive_urlencode(b64encoded_xml)
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user