Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
cd941ead
Commit
cd941ead
authored
Jun 12, 2015
by
Braden MacDonald
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
New SAML/Shibboleth tests - PR 8518
parent
b4904adc
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
175 additions
and
9 deletions
+175
-9
common/djangoapps/student/views.py
+1
-1
common/djangoapps/third_party_auth/management/commands/saml.py
+2
-2
common/djangoapps/third_party_auth/models.py
+16
-5
common/djangoapps/third_party_auth/tests/data/saml_key.key
+15
-0
common/djangoapps/third_party_auth/tests/data/saml_key.pub
+17
-0
common/djangoapps/third_party_auth/tests/data/saml_key_alt.key
+16
-0
common/djangoapps/third_party_auth/tests/data/saml_key_alt.pub
+15
-0
common/djangoapps/third_party_auth/tests/data/testshib_metadata.xml
+0
-0
common/djangoapps/third_party_auth/tests/data/testshib_response.txt
+0
-0
common/djangoapps/third_party_auth/tests/specs/test_testshib.py
+0
-0
common/djangoapps/third_party_auth/tests/test_views.py
+64
-0
common/djangoapps/third_party_auth/tests/testutil.py
+28
-0
lms/djangoapps/student_account/views.py
+1
-1
No files found.
common/djangoapps/student/views.py
View file @
cd941ead
...
...
@@ -368,7 +368,7 @@ def signin_user(request):
for
msg
in
messages
.
get_messages
(
request
):
if
msg
.
extra_tags
.
split
()[
0
]
==
"social-auth"
:
# msg may or may not be translated. Try translating [again] in case we are able to:
third_party_auth_error
=
_
(
msg
)
# pylint: disable=translation-of-non-string
third_party_auth_error
=
_
(
unicode
(
msg
)
)
# pylint: disable=translation-of-non-string
break
context
=
{
...
...
common/djangoapps/third_party_auth/management/commands/saml.py
View file @
cd941ead
...
...
@@ -60,7 +60,7 @@ class Command(BaseCommand):
self
.
stdout
.
write
(
"
\n
→ Fetching {}
\n
"
.
format
(
url
))
if
not
url
.
lower
()
.
startswith
(
'https'
):
self
.
stdout
.
write
(
"→ WARNING: This URL is not secure! It should use HTTPS.
\n
"
)
response
=
requests
.
get
(
url
,
verify
=
True
)
# May raise HTTPError or SSLError
response
=
requests
.
get
(
url
,
verify
=
True
)
# May raise HTTPError or SSLError
or ConnectionError
response
.
raise_for_status
()
# May raise an HTTPError
try
:
...
...
@@ -75,7 +75,7 @@ class Command(BaseCommand):
public_key
,
sso_url
,
expires_at
=
self
.
_parse_metadata_xml
(
xml
,
entity_id
)
self
.
_update_data
(
entity_id
,
public_key
,
sso_url
,
expires_at
)
except
Exception
as
err
:
# pylint: disable=broad-except
self
.
stderr
.
write
(
"→ ERROR: {}
\n\n
"
.
format
(
err
.
message
))
self
.
stderr
.
write
(
u
"→ ERROR: {}
\n\n
"
.
format
(
err
.
message
))
@classmethod
def
_parse_metadata_xml
(
cls
,
xml
,
entity_id
):
...
...
common/djangoapps/third_party_auth/models.py
View file @
cd941ead
...
...
@@ -8,6 +8,7 @@ from django.conf import settings
from
django.core.exceptions
import
ValidationError
from
django.db
import
models
from
django.utils
import
timezone
from
django.utils.translation
import
ugettext
as
_
import
json
import
logging
from
social.backends.base
import
BaseAuth
...
...
@@ -53,7 +54,7 @@ class AuthNotConfigured(SocialAuthBaseException):
self
.
provider_name
=
provider_name
def
__str__
(
self
):
return
'Authentication with {} is currently unavailable.'
.
format
(
return
_
(
'Authentication with {} is currently unavailable.'
)
.
format
(
self
.
provider_name
)
...
...
@@ -313,10 +314,20 @@ class SAMLConfiguration(ConfigurationModel):
self
.
org_info_str
=
clean_json
(
self
.
org_info_str
,
dict
)
self
.
other_config_str
=
clean_json
(
self
.
other_config_str
,
dict
)
self
.
private_key
=
self
.
private_key
.
replace
(
"-----BEGIN PRIVATE KEY-----"
,
""
)
.
strip
()
self
.
private_key
=
self
.
private_key
.
replace
(
"-----END PRIVATE KEY-----"
,
""
)
.
strip
()
self
.
public_key
=
self
.
public_key
.
replace
(
"-----BEGIN CERTIFICATE-----"
,
""
)
.
strip
()
self
.
public_key
=
self
.
public_key
.
replace
(
"-----END CERTIFICATE-----"
,
""
)
.
strip
()
self
.
private_key
=
(
self
.
private_key
.
replace
(
"-----BEGIN RSA PRIVATE KEY-----"
,
""
)
.
replace
(
"-----BEGIN PRIVATE KEY-----"
,
""
)
.
replace
(
"-----END RSA PRIVATE KEY-----"
,
""
)
.
replace
(
"-----END PRIVATE KEY-----"
,
""
)
.
strip
()
)
self
.
public_key
=
(
self
.
public_key
.
replace
(
"-----BEGIN CERTIFICATE-----"
,
""
)
.
replace
(
"-----END CERTIFICATE-----"
,
""
)
.
strip
()
)
def
get_setting
(
self
,
name
):
""" Get the value of a setting, or raise KeyError """
...
...
common/djangoapps/third_party_auth/tests/data/saml_key.key
0 → 100644
View file @
cd941ead
-----BEGIN RSA PRIVATE KEY-----
MIICWwIBAAKBgQDM+Nf7IeRdIIgYUke6sR3n7osHVYXwH6pb+Ovq8j3hUoy8kzT9
kJF0RB3h3Q2VJ3ZWiQtT94fZX2YYorVdoGVK2NWzjLwgpHUsgfeJq5pCjP0d2OQu
9Qvjg6YOtYP6PN3j7eK7pUcxQvIcaY9APDF57ua/zPsm3UzbjhRlJZQUewIDAQAB
AoGADWBsD/qdQaqe1x9/iOKINhuuPRNKw2n9nzT2iIW4nhzaDHB689VceL79SEE5
4rMJmQomkBtGZVxBeHgd5/dQxNy3bC9lPN1uoMuzjQs7UMk+lvy0MoHfiJcuIxPX
RdyZTV9LKN8vq+ZpVykVu6pBdDlne4psPZeQ76ynxke/24ECQQD3NX7JeluZ64la
tC6b3VHzA4Hd1qTXDWtEekh2WaR2xuKzcLyOWhqPIWprylBqVc1m+FA/LRRWQ9y6
vJMiXMk7AkEA1ELWj9DtZzk9BV1JxsDUUP0/IMAiYliVac3YrvQfys8APCY1xr9q
BAGurH4VWXuEnbx1yNXK89HqFI7kDrMtwQJAVTXtVAmHFZEosUk2X6d0He3xj8Py
4eXQObRk0daoaAC6F9weQnsweHGuOyVrfpvAx2OEVaJ2Rh3yMbPai5esDQJAS9Yh
gLqdx26M3bjJ3igQ82q3vkTHRCnwICA6la+FGFnC9LqWJg9HmmzbcqeNiy31YMHv
tzSjUV+jaXrwAkyEQQJAK/SCIVsWRhFe/ssr8hS//V+hZC4kvCv4b3NqzZK1x+Xm
7GaGMV0xEWN7shqVSRBU4O2vn/RWD6/6x3sHkU57qg==
-----END RSA PRIVATE KEY-----
common/djangoapps/third_party_auth/tests/data/saml_key.pub
0 → 100644
View file @
cd941ead
-----BEGIN CERTIFICATE-----
MIICsDCCAhmgAwIBAgIJAJrENr8EPgpcMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
aWRnaXRzIFB0eSBMdGQwHhcNMTUwNjEzMDEwNTE0WhcNMjUwNjEyMDEwNTE0WjBF
MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50
ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB
gQDM+Nf7IeRdIIgYUke6sR3n7osHVYXwH6pb+Ovq8j3hUoy8kzT9kJF0RB3h3Q2V
J3ZWiQtT94fZX2YYorVdoGVK2NWzjLwgpHUsgfeJq5pCjP0d2OQu9Qvjg6YOtYP6
PN3j7eK7pUcxQvIcaY9APDF57ua/zPsm3UzbjhRlJZQUewIDAQABo4GnMIGkMB0G
A1UdDgQWBBTjOyPvAuej5q4C80jlFrQmOlszmzB1BgNVHSMEbjBsgBTjOyPvAuej
5q4C80jlFrQmOlszm6FJpEcwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUt
U3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZIIJAJrENr8E
PgpcMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAV5w0SxjUTFWfL3ZG
6sgA0gKf8aV8w3AlihLt9tKCRgrK4sBK9xmfwp/fnbdxkHU58iozI894HqmrRzCi
aRLWmy3W8640E/XCa6P+i8ET7RksgNJ5cD9WtISHkGc2dnW76+2nv8d24JKeIx2w
oJAtspMywzr0SoxDIJr42N6Kvjk=
-----END CERTIFICATE-----
common/djangoapps/third_party_auth/tests/data/saml_key_alt.key
0 → 100644
View file @
cd941ead
-----BEGIN PRIVATE KEY-----
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAMoR8CP+HlvsPRwi
VCCuWxZOdNjYa4Qre3JEWPkqlUwpci1XGTBqH7DK9b2hmBXMjYoDKOnF5pL7Y453
3JSJ2+AG7D4AJGSotA3boKF18EDgeMzAWjAhDVhTprGz+/1G+W0R4SSyY5QGyBhL
Z36xF2w5HyeiqN/Iiq3QKGl2CFORAgMBAAECgYEAwH2CAudqSCqstAZHmbI99uva
B09ybD93owxUrVbRTfIVX/eeeS4+7g0JNxGebPWkxxnneXoaAV4UIn0v1RfWKMs3
QGiBsOSup1DWWwkBfvQ1hNlJfVCqgZH1QVbhPpw9M9gxhLZQaSZoI/qY/8n/54L0
zU4S6VYBH6hnkgZZmiECQQDpYUS8HgnkMUX/qcDNBJT23qHewHsZOe6uqC+7+YxQ
xKT8iCxybDbZU7hmZ1Av8Ns4iF7EvZ0faFM8Ls76wFX1AkEA3afLUMLHfTx40XwO
oU7GWrYFyLNCc3/7JeWi6ZKzwzQqiGvFderRf/QGQsCtpLQ8VoLz/knF9TkQdSh6
yuIprQJATfcmxUmruEYVwnFtbZBoS4jYvtfCyAyohkS9naiijaEEFTFQ1/D66eOk
KOG+0iU+t0YnksZdpU5u8B4bG34BuQJAXv6FhTQk+MhM40KupnUzTzcJXY1t4kAs
K36yBjZoMjWOMO83LiUX6iVz9XHMOXVBEraGySlm3IS7R+q0TXUF9QJAQ69wautf
8q1OQiLcg5WTFmSFBEXqAvVwX6FcDSxor9UnI0iHwyKBss3a2IXY9LoTPTjR5SHh
GDq2lXmP+kmbnQ==
-----END PRIVATE KEY-----
common/djangoapps/third_party_auth/tests/data/saml_key_alt.pub
0 → 100644
View file @
cd941ead
-----BEGIN CERTIFICATE-----
MIICWDCCAcGgAwIBAgIJAMlM2wrOvplkMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
aWRnaXRzIFB0eSBMdGQwHhcNMTUwNjEzMDEyMTAwWhcNMjUwNjEyMDEyMTAwWjBF
MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB
gQDKEfAj/h5b7D0cIlQgrlsWTnTY2GuEK3tyRFj5KpVMKXItVxkwah+wyvW9oZgV
zI2KAyjpxeaS+2OOd9yUidvgBuw+ACRkqLQN26ChdfBA4HjMwFowIQ1YU6axs/v9
RvltEeEksmOUBsgYS2d+sRdsOR8noqjfyIqt0ChpdghTkQIDAQABo1AwTjAdBgNV
HQ4EFgQUU0TNPc1yGas/W4HJl/Hgtrmdu6MwHwYDVR0jBBgwFoAUU0TNPc1yGas/
W4HJl/Hgtrmdu6MwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQCE4BqJ
v2s99DS16NbZtR7tpqXDxiDaCg59VtgcHQwxN4qXcixZi5N4yRvzjYschAQN5tQ6
bofXdIK3tJY9Ynm0KPO+5l0RCv7CkhNgftTww0bWC91xaHJ/y66AqONuLpaP6s43
SZYG2D6ric57ZY4kQ6ZlUv854TPzmvapnGG7Hw==
-----END CERTIFICATE-----
common/djangoapps/third_party_auth/tests/data/testshib_metadata.xml
0 → 100644
View file @
cd941ead
This diff is collapsed.
Click to expand it.
common/djangoapps/third_party_auth/tests/data/testshib_response.txt
0 → 100644
View file @
cd941ead
This diff is collapsed.
Click to expand it.
common/djangoapps/third_party_auth/tests/specs/test_testshib.py
0 → 100644
View file @
cd941ead
This diff is collapsed.
Click to expand it.
common/djangoapps/third_party_auth/tests/test_views.py
0 → 100644
View file @
cd941ead
"""
Test the views served by third_party_auth.
"""
# pylint: disable=no-member
import
ddt
from
lxml
import
etree
import
unittest
from
.testutil
import
AUTH_FEATURE_ENABLED
,
SAMLTestCase
# Define some XML namespaces:
SAML_XML_NS
=
'urn:oasis:names:tc:SAML:2.0:metadata'
XMLDSIG_XML_NS
=
'http://www.w3.org/2000/09/xmldsig#'
@unittest.skipUnless
(
AUTH_FEATURE_ENABLED
,
'third_party_auth not enabled'
)
@ddt.ddt
class
SAMLMetadataTest
(
SAMLTestCase
):
"""
Test the SAML metadata view
"""
METADATA_URL
=
'/auth/saml/metadata.xml'
def
test_saml_disabled
(
self
):
""" When SAML is not enabled, the metadata view should return 404 """
self
.
enable_saml
(
enabled
=
False
)
response
=
self
.
client
.
get
(
self
.
METADATA_URL
)
self
.
assertEqual
(
response
.
status_code
,
404
)
@ddt.data
(
'saml_key'
,
'saml_key_alt'
)
# Test two slightly different key pair export formats
def
test_metadata
(
self
,
key_name
):
self
.
enable_saml
(
private_key
=
self
.
_get_private_key
(
key_name
),
public_key
=
self
.
_get_public_key
(
key_name
),
entity_id
=
"https://saml.example.none"
,
)
doc
=
self
.
_fetch_metadata
()
# Check the ACS URL:
acs_node
=
doc
.
find
(
".//{}"
.
format
(
etree
.
QName
(
SAML_XML_NS
,
'AssertionConsumerService'
)))
self
.
assertIsNotNone
(
acs_node
)
self
.
assertEqual
(
acs_node
.
attrib
[
'Location'
],
'http://example.none/auth/complete/tpa-saml/'
)
def
test_signed_metadata
(
self
):
self
.
enable_saml
(
private_key
=
self
.
_get_private_key
(),
public_key
=
self
.
_get_public_key
(),
entity_id
=
"https://saml.example.none"
,
other_config_str
=
'{"SECURITY_CONFIG": {"signMetadata": true} }'
,
)
doc
=
self
.
_fetch_metadata
()
sig_node
=
doc
.
find
(
".//{}"
.
format
(
etree
.
QName
(
XMLDSIG_XML_NS
,
'SignatureValue'
)))
self
.
assertIsNotNone
(
sig_node
)
def
_fetch_metadata
(
self
):
""" Fetch and parse the metadata XML at self.METADATA_URL """
response
=
self
.
client
.
get
(
self
.
METADATA_URL
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
response
[
'Content-Type'
],
'text/xml'
)
# The result should be valid XML:
try
:
metadata_doc
=
etree
.
fromstring
(
response
.
content
)
except
etree
.
LxmlError
:
self
.
fail
(
'SAML metadata must be valid XML'
)
self
.
assertEqual
(
metadata_doc
.
tag
,
etree
.
QName
(
SAML_XML_NS
,
'EntityDescriptor'
))
return
metadata_doc
common/djangoapps/third_party_auth/tests/testutil.py
View file @
cd941ead
...
...
@@ -8,6 +8,7 @@ from contextlib import contextmanager
from
django.conf
import
settings
import
django.test
import
mock
import
os.path
from
third_party_auth.models
import
OAuth2ProviderConfig
,
SAMLProviderConfig
,
SAMLConfiguration
,
cache
as
config_cache
...
...
@@ -87,6 +88,33 @@ class TestCase(ThirdPartyAuthTestMixin, django.test.TestCase):
pass
class
SAMLTestCase
(
TestCase
):
"""
Base class for SAML-related third_party_auth tests
"""
def
setUp
(
self
):
super
(
SAMLTestCase
,
self
)
.
setUp
()
self
.
client
.
defaults
[
'SERVER_NAME'
]
=
'example.none'
# The SAML lib we use doesn't like testserver' as a domain
self
.
url_prefix
=
'http://example.none'
@classmethod
def
_get_public_key
(
cls
,
key_name
=
'saml_key'
):
""" Get a public key for use in the test. """
return
cls
.
_read_data_file
(
'{}.pub'
.
format
(
key_name
))
@classmethod
def
_get_private_key
(
cls
,
key_name
=
'saml_key'
):
""" Get a private key for use in the test. """
return
cls
.
_read_data_file
(
'{}.key'
.
format
(
key_name
))
@staticmethod
def
_read_data_file
(
filename
):
""" Read the contents of a file in the data folder """
with
open
(
os
.
path
.
join
(
os
.
path
.
dirname
(
__file__
),
'data'
,
filename
))
as
f
:
return
f
.
read
()
@contextmanager
def
simulate_running_pipeline
(
pipeline_target
,
backend
,
email
=
None
,
fullname
=
None
,
username
=
None
):
"""Simulate that a pipeline is currently running.
...
...
lms/djangoapps/student_account/views.py
View file @
cd941ead
...
...
@@ -198,7 +198,7 @@ def _third_party_auth_context(request, redirect_to):
for
msg
in
messages
.
get_messages
(
request
):
if
msg
.
extra_tags
.
split
()[
0
]
==
"social-auth"
:
# msg may or may not be translated. Try translating [again] in case we are able to:
context
[
'errorMessage'
]
=
_
(
msg
)
# pylint: disable=translation-of-non-string
context
[
'errorMessage'
]
=
_
(
unicode
(
msg
)
)
# pylint: disable=translation-of-non-string
break
return
context
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment