def test_create_metadata(rf, private_settings, caplog): ns = { 'sm': 'urn:oasis:names:tc:SAML:2.0:metadata', 'ds': 'http://www.w3.org/2000/09/xmldsig#', 'idpdisc': 'urn:oasis:names:tc:SAML:profiles:SSO:idp-discovery-protocol', } private_settings.MELLON_PUBLIC_KEYS = ['xxx', '/yyy'] private_settings.MELLON_NAME_ID_FORMATS = [ lasso.SAML2_NAME_IDENTIFIER_FORMAT_UNSPECIFIED ] private_settings.MELLON_DEFAULT_ASSERTION_CONSUMER_BINDING = 'artifact' request = rf.get('/') with mock.patch('mellon.utils.file', mock.mock_open(read_data='BEGIN\nyyy\nEND'), create=True): metadata = create_metadata(request) assert_xml_constraints(metadata.encode('utf-8'), ( '/sm:EntityDescriptor[@entityID="http://testserver/metadata/"]', 1, ('/*', 1), ('/sm:SPSSODescriptor', 1, ('/*', 7), ('/sm:Extensions', 1, ('/idpdisc:DiscoveryResponse', 1)), ('/sm:NameIDFormat', 1), ('/sm:SingleLogoutService', 1), ('/sm:AssertionConsumerService[@isDefault=\'true\'][@Binding=\'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact\']', 1), ('/sm:AssertionConsumerService[@isDefault=\'true\'][@Binding=\'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\']', 0), ('/sm:AssertionConsumerService[@Binding=\'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\']', 1), ('/sm:KeyDescriptor/ds:KeyInfo/ds:X509Data', 2, ('/ds:X509Certificate', 2), ('/ds:X509Certificate[text()=\'xxx\']', 1), ('/ds:X509Certificate[text()=\'yyy\']', 1)))), namespaces=ns) assert metadata is create_metadata(request)
def test_sso_artifact_no_loop(db, app, caplog, sp_settings, idp_metadata, idp_private_key, rf): sp_settings.MELLON_DEFAULT_ASSERTION_CONSUMER_BINDING = 'artifact' request = rf.get('/') sp_metadata = create_metadata(request) idp = MockIdp(idp_metadata, idp_private_key, sp_metadata) response = app.get(reverse('mellon_login')) url, body, relay_state = idp.process_authn_request_redirect( response['Location']) assert body is None assert reverse('mellon_login') in url assert 'SAMLart' in url acs_artifact_url = url.split('testserver', 1)[1] # forget the artifact idp.artifact = '' with HTTMock(idp.mock_artifact_resolver()): response = app.get(acs_artifact_url) assert 'MELLON_RETRY_LOGIN=1;' in response['Set-Cookie'] # first error, we retry assert urlparse.urlparse( response['Location']).path == reverse('mellon_login') # check we are not logged assert not app.session # redo response = app.get(reverse('mellon_login')) url, body, relay_state = idp.process_authn_request_redirect( response['Location']) assert body is None assert reverse('mellon_login') in url assert 'SAMLart' in url acs_artifact_url = url.split('testserver', 1)[1] # forget the artifact idp.artifact = '' with HTTMock(idp.mock_artifact_resolver()): response = app.get(acs_artifact_url) # check cookie is deleted after failed retry # Py3-Dj111 variation assert re.match(r'.*MELLON_RETRY_LOGIN=("")?;', response['Set-Cookie']) assert 'Location' not in response # check we are still not logged assert not app.session # check return url is in page assert '"%s"' % sp_settings.LOGIN_REDIRECT_URL in response.text
def test_sso_artifact(db, app, caplog, sp_settings, idp_metadata, idp_private_key, rf): sp_settings.MELLON_DEFAULT_ASSERTION_CONSUMER_BINDING = 'artifact' request = rf.get('/') sp_metadata = create_metadata(request) idp = MockIdp(idp_metadata, idp_private_key, sp_metadata) response = app.get(reverse('mellon_login') + '?next=/whatever/') url, body, relay_state = idp.process_authn_request_redirect( response['Location']) assert relay_state assert body is None assert reverse('mellon_login') in url assert 'SAMLart' in url acs_artifact_url = url.split('testserver', 1)[1] with HTTMock(idp.mock_artifact_resolver()): response = app.get(acs_artifact_url, params={'RelayState': relay_state}) assert 'created new user' in caplog.text assert 'logged in using SAML' in caplog.text assert urlparse.urlparse(response['Location']).path == '/whatever/' # force delog, but keep session information for relaystate handling assert app.session del app.session['_auth_user_id'] assert 'dead artifact' not in caplog.text with HTTMock(idp.mock_artifact_resolver()): response = app.get(acs_artifact_url, params={'RelayState': relay_state}) # verify retry login was asked assert 'dead artifact' in caplog.text assert urlparse.urlparse( response['Location']).path == reverse('mellon_login') response = response.follow() url, body, relay_state = idp.process_authn_request_redirect( response['Location']) assert relay_state reset_caplog(caplog) # verify caplog has been cleaned assert 'created new user' not in caplog.text assert body is None assert reverse('mellon_login') in url assert 'SAMLart' in url acs_artifact_url = url.split('testserver', 1)[1] with HTTMock(idp.mock_artifact_resolver()): response = app.get(acs_artifact_url, params={'RelayState': relay_state}) assert 'created new user' in caplog.text assert 'logged in using SAML' in caplog.text assert urlparse.urlparse(response['Location']).path == '/whatever/'
def test_sso_artifact(db, app, caplog, sp_settings, idp_metadata, idp_private_key, rf): sp_settings.MELLON_DEFAULT_ASSERTION_CONSUMER_BINDING = 'artifact' request = rf.get('/') sp_metadata = create_metadata(request) idp = MockIdp(idp_metadata, idp_private_key, sp_metadata) response = app.get(reverse('mellon_login')) url, body = idp.process_authn_request_redirect(response['Location']) assert body is None assert reverse('mellon_login') in url assert 'SAMLart' in url acs_artifact_url = url.split('testserver', 1)[1] with HTTMock(idp.mock_artifact_resolver()): response = app.get(acs_artifact_url) assert 'created new user' in caplog.text assert 'logged in using SAML' in caplog.text assert response['Location'].endswith(sp_settings.LOGIN_REDIRECT_URL) # force delog app.session.flush() assert 'dead artifact' not in caplog.text with HTTMock(idp.mock_artifact_resolver()): response = app.get(acs_artifact_url) # verify retry login was asked assert 'dead artifact' in caplog.text assert response.status_code == 302 assert reverse('mellon_login') in url response = response.follow() url, body = idp.process_authn_request_redirect(response['Location']) reset_caplog(caplog) # verify caplog has been cleaned assert 'created new user' not in caplog.text assert body is None assert reverse('mellon_login') in url assert 'SAMLart' in url acs_artifact_url = url.split('testserver', 1)[1] with HTTMock(idp.mock_artifact_resolver()): response = app.get(acs_artifact_url) assert 'created new user' in caplog.text assert 'logged in using SAML' in caplog.text assert response['Location'].endswith(sp_settings.LOGIN_REDIRECT_URL)
def test_sso_request_denied_artifact(db, app, caplog, sp_settings, idp_metadata, idp_private_key, rf): sp_settings.MELLON_DEFAULT_ASSERTION_CONSUMER_BINDING = 'artifact' request = rf.get('/') sp_metadata = create_metadata(request) idp = MockIdp(idp_metadata, idp_private_key, sp_metadata) response = app.get(reverse('mellon_login')) url, body, relay_state = idp.process_authn_request_redirect( response['Location'], auth_result=False, msg='User is not allowed to login') assert not relay_state assert body is None assert reverse('mellon_login') in url assert 'SAMLart' in url acs_artifact_url = url.split('testserver', 1)[1] with HTTMock(idp.mock_artifact_resolver()): response = app.get(acs_artifact_url, params={'RelayState': relay_state}) assert "status is not success codes: ['urn:oasis:names:tc:SAML:2.0:status:Responder',\ 'urn:oasis:names:tc:SAML:2.0:status:RequestDenied']" in caplog.text assert 'User is not allowed to login' in response
def sp_metadata(sp_settings, rf): request = rf.get('/') return create_metadata(request)