├── .editorconfig
├── .github
├── issue_template.md
└── pull_request_template.md
├── .gitignore
├── .travis.yml
├── CHANGELOG.md
├── CONTRIBUTING.md
├── DEVELOPERS.md
├── LICENSE
├── README.md
├── RELEASE.md
├── SECURITY.md
├── docs
├── FIXUP_COMMITS.md
├── Makefile
├── _static
│ └── ViewmeonGitHub.png
├── conf.py
├── examples
│ ├── idp.rst
│ ├── index.rst
│ └── sp.rst
├── howto
│ ├── config.rst
│ └── index.rst
├── index.rst
├── install.rst
├── make.bat
├── make.sh
└── sp_test
│ └── internal.rst
├── example
├── README
├── all.sh
├── attributemaps
│ ├── adfs_v1x.py
│ ├── adfs_v20.py
│ ├── basic.py
│ ├── saml_uri.py
│ └── shibboleth_uri.py
├── create_key.sh
├── idp2
│ ├── htdocs
│ │ └── login.mako
│ ├── idp.py
│ ├── idp.xml
│ ├── idp_conf.py.example
│ ├── idp_user.py
│ ├── idp_uwsgi.py
│ ├── pki
│ │ ├── mycert.pem
│ │ └── mykey.pem
│ ├── static
│ │ └── css
│ │ │ └── main.css
│ └── templates
│ │ └── root.mako
├── idp2_repoze
│ ├── htdocs
│ │ └── login.mako
│ ├── idp.py
│ ├── idp.subject
│ ├── idp_conf.py.example
│ ├── idp_user.py
│ ├── modules
│ │ ├── login.mako.py
│ │ └── root.mako.py
│ ├── pki
│ │ ├── mycert.pem
│ │ └── mykey.pem
│ ├── static
│ │ └── css
│ │ │ └── main.css
│ └── templates
│ │ └── root.mako
├── requirements.txt
├── sp-repoze
│ ├── attributemaps
│ │ ├── basic.py
│ │ ├── saml_uri.py
│ │ └── shibboleth_uri.py
│ ├── pki
│ │ ├── certgeneration.py
│ │ ├── mycert.pem
│ │ └── mykey.pem
│ ├── sp.py
│ ├── sp_conf.example
│ ├── sp_conf.py.example
│ └── who.ini
└── sp-wsgi
│ ├── pki
│ ├── mycert.pem
│ └── mykey.pem
│ ├── service_conf.py.example
│ ├── sp.py
│ └── sp_conf.py.example
├── poetry.lock
├── pyproject.toml
├── script
├── __init__.py
├── filter_testcase_ids.py
├── idp_testdrv.py
├── sp_testdrv.py
└── utility
│ ├── filter_testcase_ids.py
│ ├── run_available_sp_tests.sh
│ ├── run_list_of_tests.py
│ └── run_oper.sh
├── src
├── saml2
│ ├── __init__.py
│ ├── algsupport.py
│ ├── argtree.py
│ ├── assertion.py
│ ├── attribute_converter.py
│ ├── attribute_resolver.py
│ ├── attributemaps
│ │ ├── __init__.py
│ │ ├── adfs_v1x.py
│ │ ├── adfs_v20.py
│ │ ├── basic.py
│ │ ├── saml_uri.py
│ │ └── shibboleth_uri.py
│ ├── authn.py
│ ├── authn_context
│ │ ├── __init__.py
│ │ ├── ippword.py
│ │ ├── mobiletwofactor.py
│ │ ├── ppt.py
│ │ ├── pword.py
│ │ ├── sslcert.py
│ │ └── timesync.py
│ ├── cache.py
│ ├── cert.py
│ ├── client.py
│ ├── client_base.py
│ ├── config.py
│ ├── country_codes.py
│ ├── cryptography
│ │ ├── __init__.py
│ │ ├── asymmetric.py
│ │ ├── errors.py
│ │ ├── pki.py
│ │ └── symmetric.py
│ ├── data
│ │ ├── __init__.py
│ │ ├── schemas
│ │ │ ├── __init__.py
│ │ │ ├── eidas-schema-attribute-legalperson.xsd
│ │ │ ├── eidas-schema-attribute-naturalperson.xsd
│ │ │ ├── eidas-schema-metadata-servicelist.xsd
│ │ │ ├── eidas-schema-saml-extensions.xsd
│ │ │ ├── envelope.xsd
│ │ │ ├── saml-schema-assertion-2.0.xsd
│ │ │ ├── saml-schema-authn-context-2.0.xsd
│ │ │ ├── saml-schema-authn-context-auth-telephony-2.0.xsd
│ │ │ ├── saml-schema-authn-context-ip-2.0.xsd
│ │ │ ├── saml-schema-authn-context-ippword-2.0.xsd
│ │ │ ├── saml-schema-authn-context-kerberos-2.0.xsd
│ │ │ ├── saml-schema-authn-context-mobileonefactor-reg-2.0.xsd
│ │ │ ├── saml-schema-authn-context-mobileonefactor-unreg-2.0.xsd
│ │ │ ├── saml-schema-authn-context-mobiletwofactor-reg-2.0.xsd
│ │ │ ├── saml-schema-authn-context-mobiletwofactor-unreg-2.0.xsd
│ │ │ ├── saml-schema-authn-context-nomad-telephony-2.0.xsd
│ │ │ ├── saml-schema-authn-context-personal-telephony-2.0.xsd
│ │ │ ├── saml-schema-authn-context-pgp-2.0.xsd
│ │ │ ├── saml-schema-authn-context-ppt-2.0.xsd
│ │ │ ├── saml-schema-authn-context-pword-2.0.xsd
│ │ │ ├── saml-schema-authn-context-session-2.0.xsd
│ │ │ ├── saml-schema-authn-context-smartcard-2.0.xsd
│ │ │ ├── saml-schema-authn-context-smartcardpki-2.0.xsd
│ │ │ ├── saml-schema-authn-context-softwarepki-2.0.xsd
│ │ │ ├── saml-schema-authn-context-spki-2.0.xsd
│ │ │ ├── saml-schema-authn-context-srp-2.0.xsd
│ │ │ ├── saml-schema-authn-context-sslcert-2.0.xsd
│ │ │ ├── saml-schema-authn-context-telephony-2.0.xsd
│ │ │ ├── saml-schema-authn-context-timesync-2.0.xsd
│ │ │ ├── saml-schema-authn-context-types-2.0.xsd
│ │ │ ├── saml-schema-authn-context-x509-2.0.xsd
│ │ │ ├── saml-schema-authn-context-xmldsig-2.0.xsd
│ │ │ ├── saml-schema-dce-2.0.xsd
│ │ │ ├── saml-schema-ecp-2.0.xsd
│ │ │ ├── saml-schema-metadata-2.0.xsd
│ │ │ ├── saml-schema-protocol-2.0.xsd
│ │ │ ├── saml-schema-x500-2.0.xsd
│ │ │ ├── saml-schema-xacml-2.0.xsd
│ │ │ ├── saml-subject-id-attr-v1.0.xsd
│ │ │ ├── sstc-metadata-attr.xsd
│ │ │ ├── sstc-req-attr-ext.xsd
│ │ │ ├── sstc-saml-attribute-ext.xsd
│ │ │ ├── sstc-saml-metadata-algsupport-v1.0.xsd
│ │ │ ├── sstc-saml-metadata-ui-v1.0.xsd
│ │ │ ├── xenc-schema-11.xsd
│ │ │ ├── xenc-schema.xsd
│ │ │ ├── xml.xsd
│ │ │ └── xmldsig-core-schema.xsd
│ │ └── templates
│ │ │ ├── __init__.py
│ │ │ └── template_enc.xml
│ ├── discovery.py
│ ├── ecp.py
│ ├── ecp_client.py
│ ├── entity.py
│ ├── entity_category
│ │ ├── __init__.py
│ │ ├── at_egov_pvp2.py
│ │ ├── edugain.py
│ │ ├── incommon.py
│ │ ├── refeds.py
│ │ └── swamid.py
│ ├── eptid.py
│ ├── extension
│ │ ├── __init__.py
│ │ ├── algsupport.py
│ │ ├── dri.py
│ │ ├── idpdisc.py
│ │ ├── mdattr.py
│ │ ├── mdrpi.py
│ │ ├── mdui.py
│ │ ├── pefim.py
│ │ ├── reqinit.py
│ │ ├── requested_attributes.py
│ │ ├── shibmd.py
│ │ └── sp_type.py
│ ├── filter.py
│ ├── httpbase.py
│ ├── httputil.py
│ ├── ident.py
│ ├── mcache.py
│ ├── md.py
│ ├── mdbcache.py
│ ├── mdie.py
│ ├── mdstore.py
│ ├── metadata.py
│ ├── mongo_store.py
│ ├── pack.py
│ ├── population.py
│ ├── profile
│ │ ├── __init__.py
│ │ ├── ecp.py
│ │ ├── paos.py
│ │ └── samlec.py
│ ├── request.py
│ ├── response.py
│ ├── s2repoze
│ │ ├── __init__.py
│ │ └── plugins
│ │ │ ├── __init__.py
│ │ │ ├── challenge_decider.py
│ │ │ ├── entitlement.py
│ │ │ ├── formswithhidden.py
│ │ │ ├── ini.py
│ │ │ └── sp.py
│ ├── s_utils.py
│ ├── saml.py
│ ├── samlp.py
│ ├── schema
│ │ ├── __init__.py
│ │ ├── soap.py
│ │ ├── soapenv.py
│ │ └── wsdl.py
│ ├── sdb.py
│ ├── server.py
│ ├── sigver.py
│ ├── soap.py
│ ├── time_util.py
│ ├── tools
│ │ ├── make_metadata.py
│ │ ├── mdexport.py
│ │ ├── mdexport_test.py
│ │ ├── mdimport.py
│ │ ├── merge_metadata.py
│ │ ├── parse_xsd2.py
│ │ ├── sync_attrmaps.py
│ │ ├── update_metadata.sh
│ │ └── verify_metadata.py
│ ├── userinfo
│ │ ├── __init__.py
│ │ └── ldapinfo.py
│ ├── validate.py
│ ├── version.py
│ ├── virtual_org.py
│ ├── ws
│ │ ├── __init__.py
│ │ ├── wsaddr.py
│ │ ├── wspol.py
│ │ ├── wssec.py
│ │ ├── wstrust.py
│ │ └── wsutil.py
│ ├── xml
│ │ ├── __init__.py
│ │ └── schema
│ │ │ └── __init__.py
│ ├── xmldsig
│ │ └── __init__.py
│ └── xmlenc
│ │ └── __init__.py
├── saml2test
│ ├── __init__.py
│ ├── check.py
│ ├── interaction.py
│ ├── opfunc.py
│ ├── status.py
│ └── tool.py
└── utility
│ ├── __init__.py
│ └── metadata.py
└── tests
├── InCommon-metadata.xml
├── SWITCHaaiRootCA.crt.pem
├── _test_80_p11_backend.py
├── aa_conf.py
├── attribute.map
├── attribute_response.xml
├── attribute_statement_data.py
├── attributemaps
├── basic.py
├── saml_uri.py
└── shibboleth_uri.py
├── conftest.py
├── create_certs.sh
├── disco_conf.py
├── ds_data.py
├── ecp_soap.xml
├── edugain.pem
├── eidas_response.xml
├── empty_metadata_file.xml
├── enc_tmpl.xml
├── encrypted_attribute_statement.xml
├── entity_anonymous_sp.xml
├── entity_cat_re.xml
├── entity_cat_re_nren.xml
├── entity_cat_rs.xml
├── entity_cat_sfs_hei.xml
├── entity_esi_and_coco_sp.xml
├── entity_no_friendly_name_sp.xml
├── entity_personalized_sp.xml
├── entity_pseudonymous_sp.xml
├── extended.xml
├── extra_lines.crt
├── fakeIDP.py
├── get_metadata.sh
├── idp.xml
├── idp_2.xml
├── idp_aa.xml
├── idp_all.xml
├── idp_all_conf.py
├── idp_conf.py
├── idp_conf_ec.py
├── idp_conf_mdb.py
├── idp_conf_mdb2.py
├── idp_conf_sp_no_encrypt.py
├── idp_conf_verify_cert.py
├── idp_example.xml
├── idp_slo_redirect.xml
├── idp_slo_redirect_conf.py
├── idp_soap.xml
├── idp_soap_conf.py
├── idp_sp_conf.py
├── idp_test
├── config.py.example
└── target_idp.py
├── idp_uiinfo.xml
├── inc-md-cert.pem
├── invalid_metadata_file.xml
├── kalmar2.pem
├── keys
├── mycert.pem
└── mykey.pem
├── localhost.py
├── malformed.crt
├── md_data.py
├── metadata.aaitest.xml
├── metadata.xml
├── metadata
├── idp.xml
├── idp_2.xml
└── idp_uiinfo.xml
├── metadata_cert.xml
├── metadata_example.xml
├── metadata_sp_1.xml
├── metadata_sp_1_no_encryption.xml
├── metadata_sp_2.xml
├── metasp.xml
├── myentitycategory.py
├── okta_assertion
├── okta_response.xml
├── openssl.cnf
├── otest_61_makemeta.py
├── pathutils.py
├── pdp_meta.xml
├── pki
├── cert.crt
├── test_3.crt
└── test_3.key
├── pre_enc.xml
├── private_key.pem
├── pubkey.pem
├── remote_data
├── InCommon-metadata-export.xml
└── metadata.aaitest.xml
├── restrictive_idp_conf.py
├── root_cert
├── localhost.ca.crt
└── localhost.ca.key
├── saml2_data.py
├── saml2_response.xml
├── saml_false_signed.xml
├── saml_hok.xml
├── saml_hok_invalid.xml
├── saml_signed.xml
├── saml_unsigned.xml
├── samlp_data.py
├── server2_conf.py
├── server3_conf.py
├── server_conf.py
├── server_conf_syslog.py
├── servera.xml
├── servera_conf.py
├── simplesamlphp_authnresponse.xml
├── sp.xml
├── sp_0.metadata
├── sp_1_conf.py
├── sp_2_conf.py
├── sp_conf_nameidpolicy.py
├── sp_mdext_conf.py
├── sp_slo_redirect.xml
├── sp_slo_redirect_conf.py
├── sp_test
├── config.py
└── targetsp.py
├── swamid-1.0.xml
├── swamid-2.0.xml
├── swamid.md
├── test.key
├── test.key.p8
├── test.pem
├── test_00_xmldsig.py
├── test_01_xmlenc.py
├── test_02_saml.py
├── test_03_saml2.py
├── test_04_samlp.py
├── test_05_md.py
├── test_06_setarg.py
├── test_1.crt
├── test_1.der
├── test_1.key
├── test_10_time_util.py
├── test_12_s_utils.py
├── test_13_validate.py
├── test_19_attribute_converter.py
├── test_2.crt
├── test_2.key
├── test_20_assertion.py
├── test_22_mdie.py
├── test_30_mdstore.py
├── test_30_mdstore_old.py
├── test_31_config.py
├── test_32_cache.py
├── test_33_identifier.py
├── test_34_population.py
├── test_36_mdbcache.py
├── test_37_entity_categories.py
├── test_38_metadata_filter.py
├── test_39_metadata.py
├── test_40_sigver.py
├── test_41_response.py
├── test_42_enc.py
├── test_43_soap.py
├── test_44_authnresp.py
├── test_50_server.py
├── test_51_client.py
├── test_52_default_sign_alg.py
├── test_60_sp.py
├── test_62_vo.py
├── test_63_ecp.py
├── test_64_artifact.py
├── test_65_authn_query.py
├── test_66_name_id_mapping.py
├── test_67_manage_name_id.py
├── test_68_assertion_id.py
├── test_69_discovery.py
├── test_70_redirect_signing.py
├── test_71_authn_request.py
├── test_72_eptid.py
├── test_75_mongodb.py
├── test_76_metadata_in_mdb.py
├── test_77_authn_context.py
├── test_81_certificates.py
├── test_82_pefim.py
├── test_83_md_extensions.py
├── test_88_nsprefix.py
├── test_89_http_post_relay_state.py
├── test_92_aes.py
├── test_93_hok.py
├── test_94_read_cert.py
├── test_chain.pem
├── test_chain_with_linebreaks.pem
├── test_schema_validator.py
├── test_xmlsec1_key_data.py
├── test_xsw.py
├── urn-mace-swami.se-swamid-test-1.0-metadata.xml
├── uu.xml
├── vo_metadata.xml
├── xmlsec1-keydata
├── signed-assertion-random-embedded-cert.xml
├── signed-assertion-with-hmac.xml
└── signed-response-with-hmac.xml
└── xsw
├── signed-xsw-assertion-assertion.xml
├── signed-xsw-assertion-extensions.xml
├── signed-xsw-assertion-in-assertion-first-sig.xml
├── signed-xsw-assertion-wrapper.xml
└── signed-xsw-response-in-response-first-sig.xml
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 4
7 | tab_width = 4
8 | end_of_line = lf
9 | insert_final_newline = true
10 | trim_trailing_whitespace = true
11 | max_line_length = 120
12 |
13 | [{*.y{a,}ml,*.html,*.xhtml,*.xml,*.xsd}]
14 | indent_size = 2
15 | tab_width = 2
16 |
--------------------------------------------------------------------------------
/.github/issue_template.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ### Code Version
4 |
5 |
6 | ### Expected Behavior
7 |
8 |
9 | ### Current Behavior
10 |
11 |
12 | ### Possible Solution
13 |
14 |
15 | ### Steps to Reproduce
16 |
17 |
18 |
19 |
20 | 1.
21 | 2.
22 | 3.
23 | 4.
24 |
--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 | ### Description
2 |
3 | ##### The feature or problem addressed by this PR
4 |
5 |
6 |
7 |
8 |
9 |
10 | ##### What your changes do and why you chose this solution
11 |
12 |
13 |
14 |
15 | ### Checklist
16 |
17 | * [ ] Checked that no other issues or pull requests exist for the same issue/change
18 | * [ ] Added tests covering the new functionality
19 | * [ ] Updated documentation OR the change is too minor to be documented
20 | * [ ] Updated CHANGELOG.md OR changes are insignificant
21 |
--------------------------------------------------------------------------------
/RELEASE.md:
--------------------------------------------------------------------------------
1 | ## Release instructions
2 |
3 | When releasing a new version, the following steps should be taken:
4 |
5 | 1. Make sure the package metadata in `pyproject.toml` is up-to-date.
6 |
7 | ```
8 | poetry check
9 | ```
10 |
11 | 2. Make sure all automated tests pass:
12 |
13 | ```
14 | poetry run pytest
15 | ```
16 |
17 | 3. Bump the version of the package
18 |
19 | ```
20 | poetry version -- X.Y.Z
21 | ```
22 |
23 | 4. Update the [CHANGELOG.md]
24 |
25 | 5. Commit and sign the changes:
26 |
27 | ```
28 | git add -u # CHANGELOG.md pyproject.toml
29 | git commit -v -s -m "Release version X.Y.Z"
30 | ```
31 |
32 | 6. Create a signed release [tag]:
33 |
34 | ```
35 | git tag -a -s vX.Y.Z -m "Version X.Y.Z"
36 | ```
37 |
38 | 7. Push the changes and the release to Github:
39 |
40 | ```
41 | git push --follow-tags
42 | ```
43 |
44 | 8. Publish the release on PyPI:
45 |
46 | ```
47 | poetry publish --build
48 | ```
49 |
50 | 9. Send an email to the pysaml2 list announcing this release
51 |
52 |
53 | [VERSION]: https://github.com/IdentityPython/pysaml2/blob/master/VERSION
54 | [CHANGELOG.md]: https://github.com/IdentityPython/pysaml2/blob/master/CHANGELOG.md
55 | [docutils]: http://docutils.sourceforge.net/
56 | [branch]: https://git-scm.com/book/en/v2/Git-Branching-Branches-in-a-Nutshell
57 | [tag]: https://git-scm.com/book/en/v2/Git-Basics-Tagging#_annotated_tags
58 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | You can find more information on security incidents
4 | on the [IdPy security webpage](https://idpy.org/security/).
5 |
6 | You read on the [incident response policy](https://github.com/IdentityPython/Governance/blob/master/idpy-incidentresponse.md)
7 | under the [governance documentation](https://github.com/IdentityPython/Governance).
8 |
9 |
10 | ## Incident report / Reporting a Vulnerability
11 |
12 | Anyone can submit a potential security vulnerability to `incident-response@idpy.org`.
13 | The incident-response team will verify the issue and contact you on how this will be
14 | handled.
15 |
16 |
17 | ## Public Discussions
18 |
19 | When a new vulnerability is reported and verified, a new security advisory is created on
20 | GitHub and the issue is assigned a CVE identifier. Progress on the mitigation is tracked
21 | on a private fork, where the incident-response team and developers communicate to fix
22 | the issue.
23 |
24 | When the fix is ready, a release plan is prepared and all communication channels are
25 | used to notify the community of the presence of a new issue and the expected release
26 | plan. This allows the community time to prepare for a security upgrade. (Notice that
27 | security fixes are not backported at the moment.)
28 |
29 | When the advisory is published, GitHub automatically notifies all associated projects of
30 | the published advisory. Projects that use IdPy projects as dependencies should
31 | automatically get Pull Requests by dependabot. Additionally, all communication channels
32 | are used again, to notify the community of the release of a new version of the affected
33 | software that contains the relevant fixes that mitigate the reported issue.
34 |
35 |
36 | ## Supported versions
37 |
38 | Notice, that security fixes are not backported at the moment to older releases than the
39 | latest. The team does not have the capacity to guarantee that these backports will exist.
40 | You are advised to be prepared to upgrade to the latest version once the fix is out.
41 |
--------------------------------------------------------------------------------
/docs/_static/ViewmeonGitHub.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IdentityPython/pysaml2/0252ec96058c87c43f89e05d978e72b6ba2e6978/docs/_static/ViewmeonGitHub.png
--------------------------------------------------------------------------------
/docs/examples/idp.rst:
--------------------------------------------------------------------------------
1 | .. _example_idp:
2 |
3 | An extremely simple example of a SAML2 identity provider.
4 | ========================================================
5 |
6 | There are 2 example IDPs in the project's example directory:
7 |
8 | * idp2 has a static definition of users:
9 |
10 | * user attributes are defined in idp_user.py
11 | * the password is defined in the PASSWD dict in idp.py
12 |
13 | * idp2_repoze is using repoze.who middleware to perform authentication and attribute retrieval
14 |
15 | Configuration
16 | -------------
17 | Entity configuration is described in "Configuration of pysaml2 entities"
18 | Server parameters like host and port and various command line parameters are
19 | defined in the main part of idp.py
20 |
21 | Setup:
22 | ******
23 |
24 | The folder [your path]/pysaml2/example/idp2 contains a file named idp_conf.py.example
25 |
26 | Take the file named idp_conf.py.example and rename it idp_conf.py
27 |
28 | Generate a metadata file based in the configuration file (idp_conf.py) by using the command::
29 |
30 | make_metadata.py idp_conf.py > idp.xml
31 |
32 |
33 | Run IDP:
34 | ********
35 |
36 | Open a Terminal::
37 |
38 | cd [your path]/pysaml2/example/idp2
39 | python idp.py idp_conf
40 |
41 | Note that you should not have the .py extension on the idp_conf.py while running the program
42 |
--------------------------------------------------------------------------------
/docs/examples/index.rst:
--------------------------------------------------------------------------------
1 | .. _example_index:
2 |
3 | Quick pysaml2 example
4 | =====================
5 |
6 | :Release: |version|
7 | :Date: |today|
8 |
9 | In order to confirm that pysaml2 has been installed correctly and are ready to use you could run this basic example
10 |
11 | Contents:
12 |
13 | .. toctree::
14 | :maxdepth: 1
15 |
16 | sp
17 | idp
18 |
19 |
--------------------------------------------------------------------------------
/docs/howto/index.rst:
--------------------------------------------------------------------------------
1 | .. _howto:
2 |
3 | How to use PySAML2
4 | ===================
5 |
6 | :Release: |release|
7 | :Date: |today|
8 |
9 | Before you can use Pysaml2, you'll need to get it installed.
10 | If you have not done it yet, read the :ref:`install`
11 |
12 | Well, now you have it installed and you want to do something.
13 |
14 | And I'm sorry to tell you this; but there isn't really a lot you can do with
15 | this code on its own.
16 |
17 | Sure you can send a AuthenticationRequest to an IdentityProvider or a
18 | AttributeQuery to an AttributeAuthority, but in order to get what they
19 | return you have to sit behind a Web server. Well that is not really true since
20 | the AttributeQuery would be over SOAP and you would get the result over the
21 | connection you have to the AttributeAuthority.
22 |
23 | But anyway, you may get my point. This is middleware stuff!
24 |
25 | PySAML2 is built to fit into a
26 | `WSGI `_ application
27 |
28 | But it can be used in a non-WSGI environment too.
29 |
30 | So you will find descriptions of both cases here.
31 |
32 | The configuration is the same regardless of whether you are using PySAML2 in a
33 | WSGI or non-WSGI environment.
34 |
35 | .. toctree::
36 | :maxdepth: 1
37 |
38 | config
39 |
40 |
41 |
--------------------------------------------------------------------------------
/docs/index.rst:
--------------------------------------------------------------------------------
1 | :Release: |release|
2 | :Date: |today|
3 |
4 | About SAML 2.0
5 | ==============
6 |
7 | SAML 2.0 or Security Assertion Markup Language 2.0 is a version of the SAML standard for exchanging authentication and authorization data between security domains.
8 |
9 | About PySAML2
10 | =============
11 |
12 | PySAML2 is a pure python implementation of SAML2. It contains all
13 | necessary pieces for building a SAML2 service provider or an identity provider.
14 | The distribution contains examples of both.
15 | Originally written to work in a WSGI environment, there are extensions that
16 | allow you to use it with other frameworks.
17 |
18 |
19 | How to use PySAML2
20 | ===================
21 |
22 | Before you can use Pysaml2, you'll need to get it installed.
23 | If you have not done it yet, read the :ref:`install`
24 |
25 | Well, now you have it installed, and you want to do something.
26 |
27 | And I'm sorry to tell you this, but there isn't really a lot you can do with
28 | this code on it's own.
29 |
30 | Sure you can send a AuthenticationRequest to an IdentityProvider or an
31 | AttributeQuery to an AttributeAuthority but in order to get what they
32 | return you have to sit behind a Web server. Well, that is not really true since
33 | the AttributeQuery would be over SOAP and you would get the result over the
34 | connection you have to the AttributeAuthority.
35 |
36 | But anyway, you may get my point. This is middleware stuff!
37 |
38 | PySAML2 is built to fit into a
39 | `WSGI `_ application
40 |
41 | But it can be used in a non-WSGI environment too.
42 |
43 | So you will find descriptions of both cases here.
44 |
45 | The configuration is the same disregarding whether you are using PySAML2 in a
46 | WSGI or non-WSGI environment.
47 |
48 |
49 | Python compatibility
50 | ^^^^^^^^^^^^^^^^^^^^
51 |
52 | PySAML2 has transitioned to Python3. Master Apps is maintaining a fork with Python2
53 | compatibility on [GitHub](https://github.com/masterapps-au/pysaml2).
54 |
55 |
56 | Table of contents
57 | ==================
58 |
59 | .. toctree::
60 | :maxdepth: 2
61 |
62 | install
63 | examples/index
64 | howto/index
65 | sp_test/internal
66 |
67 |
68 |
69 | * :ref:`genindex`
70 | * :ref:`modindex`
71 | * :ref:`search`
72 |
73 |
--------------------------------------------------------------------------------
/docs/install.rst:
--------------------------------------------------------------------------------
1 | .. _install:
2 |
3 | Quick install guide
4 | ===================
5 |
6 | Before you can use PySAML2, you'll need to get it installed. This guide
7 | will guide you to a simple, minimal installation.
8 |
9 | Install PySAML2
10 | ---------------
11 |
12 | For all this to work, you need to have Python installed.
13 | The development has been done using 2.7.
14 | There is now a 3.X version.
15 |
16 | Prerequisites
17 | ^^^^^^^^^^^^^
18 |
19 | You have to have ElementTree, which is either part of your Python distribution
20 | if it's recent enough, or if the Python is too old you have to install it,
21 | for instance by getting it from the Python Package Instance by using
22 | easy_install.
23 |
24 | You also need xmlsec1 which you can download from http://www.aleksey.com/xmlsec/
25 |
26 | If you're on macOS, you can get xmlsec1 installed from MacPorts or Fink.
27 |
28 | If you're on rhel/centos 7 you will need to install xmlsec1 and xmlsec1-openssl::
29 |
30 | yum install xmlsec1 xmlsec1-openssl
31 |
32 | Depending on how you are going to use PySAML2 you might also need
33 |
34 | * Mako
35 | * pyASN1
36 | * repoze.who
37 | * python-memcache
38 | * memcached
39 |
40 | Quick build instructions
41 | ^^^^^^^^^^^^^^^^^^^^^^^^
42 |
43 | Once you have installed all the necessary prerequisites a simple::
44 |
45 | python setup.py install
46 |
47 | will install the basic code.
48 |
49 | Note for rhel/centos 6: cffi depends on libffi-devel, and cryptography on openssl-devel to compile
50 | So you might want first to do:
51 | yum install libffi-devel openssl-devel
52 |
53 | After this, you ought to be able to run the tests without a hitch.
54 | The tests are based on the pypy test environment, so::
55 |
56 | cd tests
57 | pip install -r test-requirements.txt
58 | pytest
59 |
60 | is what you should use. If you don't have py.test, get it it's part of pypy!
61 | It's really good!
62 |
63 |
--------------------------------------------------------------------------------
/docs/make.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | rm -f saml2*
3 | sphinx-apidoc -F -o ../docs/ ../src/saml2
4 | make clean
5 | make html
6 |
--------------------------------------------------------------------------------
/example/README:
--------------------------------------------------------------------------------
1 | This is a very simple setup just to check that all your gear are in order.
2 |
3 | The setup consists of one IdP and one SP, in idp2/ and sp-wsgi/ respectively.
4 |
5 | To run the setup do:
6 |
7 | ./all.sh start
8 |
9 | and then use your favourite webbrowser to look at "http://localhost:8087/"
10 |
11 | To shut it down do:
12 |
13 | ./all.sh stop
14 |
15 | The IdP authenticates users using a dictionary built in to idp2/idp.py;
16 | look for the dictionary called PASSWD inside that file.
17 |
18 | Other metadata about the accounts (names, email addresses, etc) are
19 | stored in idp2/idp_user.py. (Note, not all accounts have all such data
20 | defined.)
21 |
22 | The username:password pairs in PASSWD:
23 |
24 | daev0001:qwerty
25 | testuser:qwerty
26 | roland:dianakra
27 | babs:howes
28 | upper:crust
29 |
30 | The SP doesn't do anything but show you the information that the IdP sent.
31 |
32 | Note, the listeners are all configured to bind to localhost (127.0.0.1) only.
33 | If you want to be able to connect to them externally, grep "HOST = '127.0.0.1'"
34 | example/*/*.py and replace 127.0.0.1 with 0.0.0.0 or a specific IP.
35 |
36 | To make it easy, for me :-), both the IdP and the SP uses the same keys.
37 | To generate new keys, run create_key.sh and follow its instructions.
38 |
39 | There are alternate IdP and SP configs in idp2_repoze/ and sp-repoze/ that
40 | are still in flux; do not use them unless you know what you are doing.
41 |
42 |
--------------------------------------------------------------------------------
/example/all.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | startme() {
4 | cd sp-wsgi
5 | if [ ! -f sp_conf.py ] ; then
6 | cp sp_conf.py.example sp_conf.py
7 | fi
8 | if [ ! -f service_conf.py ] ; then
9 | cp service_conf.py.example service_conf.py
10 | fi
11 | ../../src/saml2/tools/make_metadata.py sp_conf > sp.xml
12 |
13 | cd ../idp2
14 | if [ ! -f idp_conf.py ] ; then
15 | cp idp_conf.py.example idp_conf.py
16 | fi
17 | ../../src/saml2/tools/make_metadata.py idp_conf > idp.xml
18 |
19 | cd ../sp-wsgi
20 | ./sp.py sp_conf &
21 |
22 | cd ../idp2
23 | ./idp.py idp_conf &
24 |
25 | cd ..
26 | }
27 |
28 | stopme() {
29 | pkill -f "sp.py"
30 | pkill -f "idp.py"
31 | }
32 |
33 | case "$1" in
34 | start) startme ;;
35 | stop) stopme ;;
36 | restart) stopme; startme ;;
37 | *) echo "usage: $0 start|stop|restart" >&2
38 | exit 1
39 | ;;
40 | esac
41 |
--------------------------------------------------------------------------------
/example/attributemaps/adfs_v1x.py:
--------------------------------------------------------------------------------
1 | # See http://technet.microsoft.com/en-us/library/cc733065(v=ws.10).aspx
2 | # and http://technet.microsoft.com/en-us/library/ee913589(v=ws.10).aspx
3 | # for information regarding the default claim types supported by
4 | # Microsoft ADFS v1.x.
5 |
6 | MAP = {
7 | "identifier": "urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified",
8 | "fro": {
9 | "http://schemas.xmlsoap.org/claims/commonname": "commonName",
10 | "http://schemas.xmlsoap.org/claims/emailaddress": "emailAddress",
11 | "http://schemas.xmlsoap.org/claims/group": "group",
12 | "http://schemas.xmlsoap.org/claims/upn": "upn",
13 | },
14 | "to": {
15 | "commonName": "http://schemas.xmlsoap.org/claims/commonname",
16 | "emailAddress": "http://schemas.xmlsoap.org/claims/emailaddress",
17 | "group": "http://schemas.xmlsoap.org/claims/group",
18 | "upn": "http://schemas.xmlsoap.org/claims/upn",
19 | },
20 | }
21 |
--------------------------------------------------------------------------------
/example/create_key.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | cat <
2 |
3 |
Please log in
4 |
5 | To register it's quite simple: enter a login and a password
6 |
7 |
8 |
30 |
--------------------------------------------------------------------------------
/example/idp2/pki/mycert.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIIFjTCCA3WgAwIBAgIUPqN85BV5eJsrEW8IN2rVIcHGliUwDQYJKoZIhvcNAQEL
3 | BQAwVTELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAldBMRAwDgYDVQQHDAdTZWF0dGxl
4 | MRowGAYDVQQKDBFweXNhbWwyIERlbW8gQ2VydDELMAkGA1UECwwCSVQwIBcNMjEx
5 | MDA3MDgwNzE1WhgPMjEyMTEwMzEwODA3MTVaMFUxCzAJBgNVBAYTAlVTMQswCQYD
6 | VQQIDAJXQTEQMA4GA1UEBwwHU2VhdHRsZTEaMBgGA1UECgwRcHlzYW1sMiBEZW1v
7 | IENlcnQxCzAJBgNVBAsMAklUMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC
8 | AgEAuWw/2OYDIO3pAZyQFQadOWnOh/ilqWXd66M3gD3tft+zydi2IRTUphhlrVmL
9 | /7UrWjHYfrBbF6UrliwvXfjgdECRAA6ziMlzbc+RYHqw1XhR/Y1DxH1V3OZmidaH
10 | B3oisfXe68v28kTHnq0iGQjivkRaMWZXrMSQfakm5ZQz6HC92DgxsOXcSTMlCETN
11 | lziEik2C/4mhA96p+upQPoAC3e9Dn7lNUS2oJT/cZBggJOtLScV6Cyzv+k+u2MCu
12 | ddzenrFONHBe7MNUYEg7Ho9Utas3y6prH36ZwsArtUPpNwBZrNlVenFeF6rUN80x
13 | hKNwvptlXQqAC+2NIhunYgNRoUHmy5opGBJpjqy0k16k+aac2t9Y/Lc+Q52PSh5Z
14 | e3C9Ip5CxoxA6hMk7IZT1kpsUl6uc8FBX9SLNRovIOC4r1anaxIareQjRab2iw+a
15 | RhTmKlGSXzlaSMS2FZMpT0q73d/rFo1/RKeE2dzG9fXe27jXJTv50J4Y46RF/6/C
16 | 0BWxTAzdBPQj1I5KWVVX9XuZklSbhKv/sGNXxPe5p5hR9rwt2oCrAhHzFZzddrzI
17 | BVRi6UtgnPMg8klnUcHsDt7elCuAF2ouB4Z8YplxhhYJga3NfLmHXYw1R7PBF2Ud
18 | 4FSgkjCO/ME/+FgyUKAaG6qKBMe3FJzuXch314qR1+1xT+ECAwEAAaNTMFEwHQYD
19 | VR0OBBYEFIwaXBfOL6adUTxpgv/NJGZ3gLrIMB8GA1UdIwQYMBaAFIwaXBfOL6ad
20 | UTxpgv/NJGZ3gLrIMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIB
21 | AI4SQxTjjNvV7E903U/l1pd7PoyvM9LxcYkmnjTGMHBoK1/HbpU27s9ZHjodfT7k
22 | k1uMCh6tx7kuaSboWX9BX8Sdgtfj8Y9hs7Bb4MhTH3qJ3LFGK2d0LgK+lTFNRtmy
23 | JfExfmDcXi/YO+0IVd3mK49BfGdpN8lT7KjtcbUk4gObGLuN/XGCTjFPmyIRJfgZ
24 | K6kLWskthddoa4beqzpVQnbn5TBtShzlzmrjmU5YieP54IoQXDzE5mWn4PFuMsZ9
25 | wCLcSwRYiP3fUMGCChs64QADuIjZYB8xl3CHKxZMu9Ki+dHQ2W1X1UyozzF5i19S
26 | rhVdYXlumVOS6jnAjZ/UNIS1mYCZk5AT8jtsCBkgmNmhZab3BNbLklLz2AYi9fpI
27 | CYkHei2fYlAHKXPFZFKIHmG3mqpgu9nClyelrjRsxvrvkJ4Di/aPZ3qA+I006uYw
28 | Ti+NsL6eXBhIPUuA/NHax53kzJSW/AcjRhSKriKdSqD+lLOoQ+z9ZnZXeBKCWzGU
29 | 9bcMUk1x2pJJJBI7AJC/ObAbJDCtQreZZE+u+CABhkwbLPkWx8ZgS9a0/gBVmIJ3
30 | 4qsVmrv0sHnk1JQoFkMNT2oQmCuoA17HtfqxGyEPCxEJu/kqgK3fviKgd1MEP4Y7
31 | 3F6yVtiEWnspy4cQqr6pHBBggDbjve6DPNsIM1jHGUsR
32 | -----END CERTIFICATE-----
33 |
--------------------------------------------------------------------------------
/example/idp2/static/css/main.css:
--------------------------------------------------------------------------------
1 | /* Sample css file */
2 |
3 |
--------------------------------------------------------------------------------
/example/idp2/templates/root.mako:
--------------------------------------------------------------------------------
1 | <% self.seen_css = set() %>
2 | <%def name="css_link(path, media='')" filter="trim">
3 | % if path not in self.seen_css:
4 |
5 | % endif
6 | <% self.seen_css.add(path) %>
7 | %def>
8 | <%def name="css()" filter="trim">
9 | ${css_link('/static/css/main.css', 'screen')}
10 | %def>
11 | <%def name="pre()" filter="trim">
12 |
15 | %def>
16 | <%def name="post()" filter="trim">
17 |
18 |
21 |
22 | %def>
23 | ##
25 |
26 | IDP test login
27 | ${self.css()}
28 |
29 |
30 |
31 | ${pre()}
32 | ## ${comps.dict_to_table(pageargs)}
33 | ##
34 | ${next.body()}
35 | ${post()}
36 |
37 |
38 |
--------------------------------------------------------------------------------
/example/idp2_repoze/htdocs/login.mako:
--------------------------------------------------------------------------------
1 | <%inherit file="root.mako"/>
2 |
3 | Please log in
4 |
5 | To register it's quite simple: enter a login and a password
6 |
7 |
8 |
30 |
--------------------------------------------------------------------------------
/example/idp2_repoze/idp.subject:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IdentityPython/pysaml2/0252ec96058c87c43f89e05d978e72b6ba2e6978/example/idp2_repoze/idp.subject
--------------------------------------------------------------------------------
/example/idp2_repoze/idp_user.py:
--------------------------------------------------------------------------------
1 | USERS = {
2 | "haho0032": {
3 | "sn": "Hoerberg",
4 | "givenName": "Hans",
5 | "eduPersonScopedAffiliation": "staff@example.com",
6 | "eduPersonPrincipalName": "haho@example.com",
7 | "uid": "haho",
8 | "eduPersonTargetedID": "one!for!all",
9 | "c": "SE",
10 | "o": "Example Co.",
11 | "ou": "IT",
12 | "initials": "P",
13 | "schacHomeOrganization": "example.com",
14 | "email": "hans@example.com",
15 | "displayName": "Hans Hoerberg",
16 | "labeledURL": "http://www.example.com/haho My homepage",
17 | "norEduPersonNIN": "SE199012315555",
18 | },
19 | "roland": {
20 | "sn": "Hedberg",
21 | "givenName": "Roland",
22 | "eduPersonScopedAffiliation": "staff@example.com",
23 | "eduPersonPrincipalName": "rohe@example.com",
24 | "uid": "rohe",
25 | "eduPersonTargetedID": "one!for!all",
26 | "c": "SE",
27 | "o": "Example Co.",
28 | "ou": "IT",
29 | "initials": "P",
30 | # "schacHomeOrganization": "example.com",
31 | "email": "roland@example.com",
32 | "displayName": "P. Roland Hedberg",
33 | "labeledURL": "http://www.example.com/rohe My homepage",
34 | "norEduPersonNIN": "SE197001012222",
35 | },
36 | "babs": {"surname": "Babs", "givenName": "Ozzie", "eduPersonAffiliation": "affiliate"},
37 | "upper": {"surname": "Jeter", "givenName": "Derek", "eduPersonAffiliation": "affiliate"},
38 | }
39 |
40 | EXTRA = {
41 | "roland": {
42 | "eduPersonEntitlement": "urn:mace:swamid.se:foo:bar",
43 | "schacGender": "male",
44 | "schacUserPresenceID": "skype:pepe.perez",
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/example/idp2_repoze/pki/mycert.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIIFjTCCA3WgAwIBAgIUPqN85BV5eJsrEW8IN2rVIcHGliUwDQYJKoZIhvcNAQEL
3 | BQAwVTELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAldBMRAwDgYDVQQHDAdTZWF0dGxl
4 | MRowGAYDVQQKDBFweXNhbWwyIERlbW8gQ2VydDELMAkGA1UECwwCSVQwIBcNMjEx
5 | MDA3MDgwNzE1WhgPMjEyMTEwMzEwODA3MTVaMFUxCzAJBgNVBAYTAlVTMQswCQYD
6 | VQQIDAJXQTEQMA4GA1UEBwwHU2VhdHRsZTEaMBgGA1UECgwRcHlzYW1sMiBEZW1v
7 | IENlcnQxCzAJBgNVBAsMAklUMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC
8 | AgEAuWw/2OYDIO3pAZyQFQadOWnOh/ilqWXd66M3gD3tft+zydi2IRTUphhlrVmL
9 | /7UrWjHYfrBbF6UrliwvXfjgdECRAA6ziMlzbc+RYHqw1XhR/Y1DxH1V3OZmidaH
10 | B3oisfXe68v28kTHnq0iGQjivkRaMWZXrMSQfakm5ZQz6HC92DgxsOXcSTMlCETN
11 | lziEik2C/4mhA96p+upQPoAC3e9Dn7lNUS2oJT/cZBggJOtLScV6Cyzv+k+u2MCu
12 | ddzenrFONHBe7MNUYEg7Ho9Utas3y6prH36ZwsArtUPpNwBZrNlVenFeF6rUN80x
13 | hKNwvptlXQqAC+2NIhunYgNRoUHmy5opGBJpjqy0k16k+aac2t9Y/Lc+Q52PSh5Z
14 | e3C9Ip5CxoxA6hMk7IZT1kpsUl6uc8FBX9SLNRovIOC4r1anaxIareQjRab2iw+a
15 | RhTmKlGSXzlaSMS2FZMpT0q73d/rFo1/RKeE2dzG9fXe27jXJTv50J4Y46RF/6/C
16 | 0BWxTAzdBPQj1I5KWVVX9XuZklSbhKv/sGNXxPe5p5hR9rwt2oCrAhHzFZzddrzI
17 | BVRi6UtgnPMg8klnUcHsDt7elCuAF2ouB4Z8YplxhhYJga3NfLmHXYw1R7PBF2Ud
18 | 4FSgkjCO/ME/+FgyUKAaG6qKBMe3FJzuXch314qR1+1xT+ECAwEAAaNTMFEwHQYD
19 | VR0OBBYEFIwaXBfOL6adUTxpgv/NJGZ3gLrIMB8GA1UdIwQYMBaAFIwaXBfOL6ad
20 | UTxpgv/NJGZ3gLrIMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIB
21 | AI4SQxTjjNvV7E903U/l1pd7PoyvM9LxcYkmnjTGMHBoK1/HbpU27s9ZHjodfT7k
22 | k1uMCh6tx7kuaSboWX9BX8Sdgtfj8Y9hs7Bb4MhTH3qJ3LFGK2d0LgK+lTFNRtmy
23 | JfExfmDcXi/YO+0IVd3mK49BfGdpN8lT7KjtcbUk4gObGLuN/XGCTjFPmyIRJfgZ
24 | K6kLWskthddoa4beqzpVQnbn5TBtShzlzmrjmU5YieP54IoQXDzE5mWn4PFuMsZ9
25 | wCLcSwRYiP3fUMGCChs64QADuIjZYB8xl3CHKxZMu9Ki+dHQ2W1X1UyozzF5i19S
26 | rhVdYXlumVOS6jnAjZ/UNIS1mYCZk5AT8jtsCBkgmNmhZab3BNbLklLz2AYi9fpI
27 | CYkHei2fYlAHKXPFZFKIHmG3mqpgu9nClyelrjRsxvrvkJ4Di/aPZ3qA+I006uYw
28 | Ti+NsL6eXBhIPUuA/NHax53kzJSW/AcjRhSKriKdSqD+lLOoQ+z9ZnZXeBKCWzGU
29 | 9bcMUk1x2pJJJBI7AJC/ObAbJDCtQreZZE+u+CABhkwbLPkWx8ZgS9a0/gBVmIJ3
30 | 4qsVmrv0sHnk1JQoFkMNT2oQmCuoA17HtfqxGyEPCxEJu/kqgK3fviKgd1MEP4Y7
31 | 3F6yVtiEWnspy4cQqr6pHBBggDbjve6DPNsIM1jHGUsR
32 | -----END CERTIFICATE-----
33 |
--------------------------------------------------------------------------------
/example/idp2_repoze/static/css/main.css:
--------------------------------------------------------------------------------
1 | /* Sample css file */
2 |
3 |
--------------------------------------------------------------------------------
/example/idp2_repoze/templates/root.mako:
--------------------------------------------------------------------------------
1 | <% self.seen_css = set() %>
2 | <%def name="css_link(path, media='')" filter="trim">
3 | % if path not in self.seen_css:
4 |
5 | % endif
6 | <% self.seen_css.add(path) %>
7 | %def>
8 | <%def name="css()" filter="trim">
9 | ${css_link('/static/css/main.css', 'screen')}
10 | %def>
11 | <%def name="pre()" filter="trim">
12 |
15 | %def>
16 | <%def name="post()" filter="trim">
17 |
18 |
21 |
22 | %def>
23 | ##
25 |
26 | IDP test login
27 | ${self.css()}
28 |
29 |
30 |
31 | ${pre()}
32 | ## ${comps.dict_to_table(pageargs)}
33 | ##
34 | ${next.body()}
35 | ${post()}
36 |
37 |
38 |
--------------------------------------------------------------------------------
/example/requirements.txt:
--------------------------------------------------------------------------------
1 | mako
2 | cherrypy>14.0.2
3 |
--------------------------------------------------------------------------------
/example/sp-repoze/pki/certgeneration.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | from saml2.cert import OpenSSLWrapper
3 |
4 |
5 | __author__ = "haho0032"
6 |
7 |
8 | cert_info_ca = {
9 | "cn": "localhost.ca",
10 | "country_code": "se",
11 | "state": "ac",
12 | "city": "umea",
13 | "organization": "ITS Umea University",
14 | "organization_unit": "DIRG",
15 | }
16 |
17 | osw = OpenSSLWrapper()
18 |
19 | ca_cert, ca_key = osw.create_certificate(cert_info_ca, request=False, write_to_file=True, cert_dir="./")
20 |
--------------------------------------------------------------------------------
/example/sp-repoze/pki/mycert.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIIFjTCCA3WgAwIBAgIUPqN85BV5eJsrEW8IN2rVIcHGliUwDQYJKoZIhvcNAQEL
3 | BQAwVTELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAldBMRAwDgYDVQQHDAdTZWF0dGxl
4 | MRowGAYDVQQKDBFweXNhbWwyIERlbW8gQ2VydDELMAkGA1UECwwCSVQwIBcNMjEx
5 | MDA3MDgwNzE1WhgPMjEyMTEwMzEwODA3MTVaMFUxCzAJBgNVBAYTAlVTMQswCQYD
6 | VQQIDAJXQTEQMA4GA1UEBwwHU2VhdHRsZTEaMBgGA1UECgwRcHlzYW1sMiBEZW1v
7 | IENlcnQxCzAJBgNVBAsMAklUMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC
8 | AgEAuWw/2OYDIO3pAZyQFQadOWnOh/ilqWXd66M3gD3tft+zydi2IRTUphhlrVmL
9 | /7UrWjHYfrBbF6UrliwvXfjgdECRAA6ziMlzbc+RYHqw1XhR/Y1DxH1V3OZmidaH
10 | B3oisfXe68v28kTHnq0iGQjivkRaMWZXrMSQfakm5ZQz6HC92DgxsOXcSTMlCETN
11 | lziEik2C/4mhA96p+upQPoAC3e9Dn7lNUS2oJT/cZBggJOtLScV6Cyzv+k+u2MCu
12 | ddzenrFONHBe7MNUYEg7Ho9Utas3y6prH36ZwsArtUPpNwBZrNlVenFeF6rUN80x
13 | hKNwvptlXQqAC+2NIhunYgNRoUHmy5opGBJpjqy0k16k+aac2t9Y/Lc+Q52PSh5Z
14 | e3C9Ip5CxoxA6hMk7IZT1kpsUl6uc8FBX9SLNRovIOC4r1anaxIareQjRab2iw+a
15 | RhTmKlGSXzlaSMS2FZMpT0q73d/rFo1/RKeE2dzG9fXe27jXJTv50J4Y46RF/6/C
16 | 0BWxTAzdBPQj1I5KWVVX9XuZklSbhKv/sGNXxPe5p5hR9rwt2oCrAhHzFZzddrzI
17 | BVRi6UtgnPMg8klnUcHsDt7elCuAF2ouB4Z8YplxhhYJga3NfLmHXYw1R7PBF2Ud
18 | 4FSgkjCO/ME/+FgyUKAaG6qKBMe3FJzuXch314qR1+1xT+ECAwEAAaNTMFEwHQYD
19 | VR0OBBYEFIwaXBfOL6adUTxpgv/NJGZ3gLrIMB8GA1UdIwQYMBaAFIwaXBfOL6ad
20 | UTxpgv/NJGZ3gLrIMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIB
21 | AI4SQxTjjNvV7E903U/l1pd7PoyvM9LxcYkmnjTGMHBoK1/HbpU27s9ZHjodfT7k
22 | k1uMCh6tx7kuaSboWX9BX8Sdgtfj8Y9hs7Bb4MhTH3qJ3LFGK2d0LgK+lTFNRtmy
23 | JfExfmDcXi/YO+0IVd3mK49BfGdpN8lT7KjtcbUk4gObGLuN/XGCTjFPmyIRJfgZ
24 | K6kLWskthddoa4beqzpVQnbn5TBtShzlzmrjmU5YieP54IoQXDzE5mWn4PFuMsZ9
25 | wCLcSwRYiP3fUMGCChs64QADuIjZYB8xl3CHKxZMu9Ki+dHQ2W1X1UyozzF5i19S
26 | rhVdYXlumVOS6jnAjZ/UNIS1mYCZk5AT8jtsCBkgmNmhZab3BNbLklLz2AYi9fpI
27 | CYkHei2fYlAHKXPFZFKIHmG3mqpgu9nClyelrjRsxvrvkJ4Di/aPZ3qA+I006uYw
28 | Ti+NsL6eXBhIPUuA/NHax53kzJSW/AcjRhSKriKdSqD+lLOoQ+z9ZnZXeBKCWzGU
29 | 9bcMUk1x2pJJJBI7AJC/ObAbJDCtQreZZE+u+CABhkwbLPkWx8ZgS9a0/gBVmIJ3
30 | 4qsVmrv0sHnk1JQoFkMNT2oQmCuoA17HtfqxGyEPCxEJu/kqgK3fviKgd1MEP4Y7
31 | 3F6yVtiEWnspy4cQqr6pHBBggDbjve6DPNsIM1jHGUsR
32 | -----END CERTIFICATE-----
33 |
--------------------------------------------------------------------------------
/example/sp-repoze/sp_conf.example:
--------------------------------------------------------------------------------
1 | from saml2 import BINDING_HTTP_REDIRECT
2 | from saml2.saml import NAME_FORMAT_URI
3 |
4 | HOST = 'localhost'
5 | PORT = 8087
6 |
7 | BASE = "http://%s:%s" % (HOST, PORT)
8 |
9 | CONFIG = {
10 | "entityid": "%s/sp.xml" % BASE,
11 | "description": "My SP",
12 | "service": {
13 | "sp": {
14 | "name": "Rolands SP",
15 | "endpoints": {
16 | "assertion_consumer_service": [BASE],
17 | "single_logout_service": [(BASE + "/slo",
18 | BINDING_HTTP_REDIRECT)],
19 | },
20 | "required_attributes": ["surname", "givenname",
21 | "edupersonaffiliation"],
22 | "optional_attributes": ["title"],
23 | }
24 | },
25 | "debug": 1,
26 | "key_file": "pki/mykey.pem",
27 | "cert_file": "pki/mycert.pem",
28 | "attribute_map_dir": "./attributemaps",
29 | "metadata": {"local": ["../idp2/idp.xml"]},
30 | # -- below used by make_metadata --
31 | "organization": {
32 | "name": "Exempel AB",
33 | "display_name": [("Exempel AB", "se"), ("Example Co.", "en")],
34 | "url": "http://www.example.com/roland",
35 | },
36 | "contact_person": [{
37 | "given_name":"John",
38 | "sur_name": "Smith",
39 | "email_address": ["john.smith@example.com"],
40 | "contact_type": "technical",
41 | },
42 | ],
43 | #"xmlsec_binary":"/opt/local/bin/xmlsec1",
44 | "name_form": NAME_FORMAT_URI,
45 | "logger": {
46 | "rotating": {
47 | "filename": "sp.log",
48 | "maxBytes": 100000,
49 | "backupCount": 5,
50 | },
51 | "loglevel": "debug",
52 | }
53 | }
--------------------------------------------------------------------------------
/example/sp-repoze/sp_conf.py.example:
--------------------------------------------------------------------------------
1 | from saml2 import BINDING_HTTP_REDIRECT
2 | from saml2.saml import NAME_FORMAT_URI
3 |
4 | BASE= "http://localhost:8087"
5 | #BASE= "http://lingon.catalogix.se:8087"
6 |
7 | CONFIG = {
8 | "entityid": "%s/sp.xml" % BASE,
9 | "description": "My SP",
10 | "service": {
11 | "sp": {
12 | "name": "Rolands SP",
13 | "endpoints": {
14 | "assertion_consumer_service": [BASE],
15 | "single_logout_service": [(BASE + "/slo",
16 | BINDING_HTTP_REDIRECT)],
17 | },
18 | "required_attributes": ["surname", "givenname",
19 | "edupersonaffiliation"],
20 | "optional_attributes": ["title"],
21 | }
22 | },
23 | "debug": 1,
24 | "key_file": "pki/mykey.pem",
25 | "cert_file": "pki/mycert.pem",
26 | "attribute_map_dir": "./attributemaps",
27 | "metadata": {"local": ["../idp2/idp.xml"]},
28 | # -- below used by make_metadata --
29 | "organization": {
30 | "name": "Exempel AB",
31 | "display_name": [("Exempel AB", "se"), ("Example Co.", "en")],
32 | "url": "http://www.example.com/roland",
33 | },
34 | "contact_person": [{
35 | "given_name":"John",
36 | "sur_name": "Smith",
37 | "email_address": ["john.smith@example.com"],
38 | "contact_type": "technical",
39 | },
40 | ],
41 | #"xmlsec_binary":"/opt/local/bin/xmlsec1",
42 | "name_form": NAME_FORMAT_URI,
43 | "logger": {
44 | "rotating": {
45 | "filename": "sp.log",
46 | "maxBytes": 100000,
47 | "backupCount": 5,
48 | },
49 | "loglevel": "debug",
50 | }
51 | }
--------------------------------------------------------------------------------
/example/sp-repoze/who.ini:
--------------------------------------------------------------------------------
1 | [plugin:auth_tkt]
2 | # identification
3 | use = repoze.who.plugins.auth_tkt:make_plugin
4 | secret = kasamark
5 | cookie_name = pysaml2
6 | secure = False
7 | include_ip = True
8 | timeout = 3600
9 | reissue_time = 3000
10 |
11 | # IDENTIFIER
12 | # @param :
13 | # - rememberer_name : name of the plugin for remembering (delegate)
14 | [plugin:saml2auth]
15 | use = s2repoze.plugins.sp:make_plugin
16 | saml_conf = sp_conf
17 | remember_name = auth_tkt
18 | sid_store = outstanding
19 | idp_query_param = IdPEntityId
20 | #discovery = http://130.239.201.5/role/idp.ds
21 |
22 | [general]
23 | request_classifier = s2repoze.plugins.challenge_decider:my_request_classifier
24 | challenge_decider = repoze.who.classifiers:default_challenge_decider
25 | remote_user_key = REMOTE_USER
26 |
27 | [identifiers]
28 | # plugin_name;classifier_name:.. or just plugin_name (good for any)
29 | plugins =
30 | saml2auth
31 | auth_tkt
32 |
33 | [authenticators]
34 | # plugin_name;classifier_name.. or just plugin_name (good for any)
35 | plugins = saml2auth
36 |
37 | [challengers]
38 | # plugin_name;classifier_name:.. or just plugin_name (good for any)
39 | plugins = saml2auth
40 |
41 | [mdproviders]
42 | plugins = saml2auth
43 |
--------------------------------------------------------------------------------
/example/sp-wsgi/pki/mycert.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIIFjTCCA3WgAwIBAgIUPqN85BV5eJsrEW8IN2rVIcHGliUwDQYJKoZIhvcNAQEL
3 | BQAwVTELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAldBMRAwDgYDVQQHDAdTZWF0dGxl
4 | MRowGAYDVQQKDBFweXNhbWwyIERlbW8gQ2VydDELMAkGA1UECwwCSVQwIBcNMjEx
5 | MDA3MDgwNzE1WhgPMjEyMTEwMzEwODA3MTVaMFUxCzAJBgNVBAYTAlVTMQswCQYD
6 | VQQIDAJXQTEQMA4GA1UEBwwHU2VhdHRsZTEaMBgGA1UECgwRcHlzYW1sMiBEZW1v
7 | IENlcnQxCzAJBgNVBAsMAklUMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC
8 | AgEAuWw/2OYDIO3pAZyQFQadOWnOh/ilqWXd66M3gD3tft+zydi2IRTUphhlrVmL
9 | /7UrWjHYfrBbF6UrliwvXfjgdECRAA6ziMlzbc+RYHqw1XhR/Y1DxH1V3OZmidaH
10 | B3oisfXe68v28kTHnq0iGQjivkRaMWZXrMSQfakm5ZQz6HC92DgxsOXcSTMlCETN
11 | lziEik2C/4mhA96p+upQPoAC3e9Dn7lNUS2oJT/cZBggJOtLScV6Cyzv+k+u2MCu
12 | ddzenrFONHBe7MNUYEg7Ho9Utas3y6prH36ZwsArtUPpNwBZrNlVenFeF6rUN80x
13 | hKNwvptlXQqAC+2NIhunYgNRoUHmy5opGBJpjqy0k16k+aac2t9Y/Lc+Q52PSh5Z
14 | e3C9Ip5CxoxA6hMk7IZT1kpsUl6uc8FBX9SLNRovIOC4r1anaxIareQjRab2iw+a
15 | RhTmKlGSXzlaSMS2FZMpT0q73d/rFo1/RKeE2dzG9fXe27jXJTv50J4Y46RF/6/C
16 | 0BWxTAzdBPQj1I5KWVVX9XuZklSbhKv/sGNXxPe5p5hR9rwt2oCrAhHzFZzddrzI
17 | BVRi6UtgnPMg8klnUcHsDt7elCuAF2ouB4Z8YplxhhYJga3NfLmHXYw1R7PBF2Ud
18 | 4FSgkjCO/ME/+FgyUKAaG6qKBMe3FJzuXch314qR1+1xT+ECAwEAAaNTMFEwHQYD
19 | VR0OBBYEFIwaXBfOL6adUTxpgv/NJGZ3gLrIMB8GA1UdIwQYMBaAFIwaXBfOL6ad
20 | UTxpgv/NJGZ3gLrIMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIB
21 | AI4SQxTjjNvV7E903U/l1pd7PoyvM9LxcYkmnjTGMHBoK1/HbpU27s9ZHjodfT7k
22 | k1uMCh6tx7kuaSboWX9BX8Sdgtfj8Y9hs7Bb4MhTH3qJ3LFGK2d0LgK+lTFNRtmy
23 | JfExfmDcXi/YO+0IVd3mK49BfGdpN8lT7KjtcbUk4gObGLuN/XGCTjFPmyIRJfgZ
24 | K6kLWskthddoa4beqzpVQnbn5TBtShzlzmrjmU5YieP54IoQXDzE5mWn4PFuMsZ9
25 | wCLcSwRYiP3fUMGCChs64QADuIjZYB8xl3CHKxZMu9Ki+dHQ2W1X1UyozzF5i19S
26 | rhVdYXlumVOS6jnAjZ/UNIS1mYCZk5AT8jtsCBkgmNmhZab3BNbLklLz2AYi9fpI
27 | CYkHei2fYlAHKXPFZFKIHmG3mqpgu9nClyelrjRsxvrvkJ4Di/aPZ3qA+I006uYw
28 | Ti+NsL6eXBhIPUuA/NHax53kzJSW/AcjRhSKriKdSqD+lLOoQ+z9ZnZXeBKCWzGU
29 | 9bcMUk1x2pJJJBI7AJC/ObAbJDCtQreZZE+u+CABhkwbLPkWx8ZgS9a0/gBVmIJ3
30 | 4qsVmrv0sHnk1JQoFkMNT2oQmCuoA17HtfqxGyEPCxEJu/kqgK3fviKgd1MEP4Y7
31 | 3F6yVtiEWnspy4cQqr6pHBBggDbjve6DPNsIM1jHGUsR
32 | -----END CERTIFICATE-----
33 |
--------------------------------------------------------------------------------
/example/sp-wsgi/service_conf.py.example:
--------------------------------------------------------------------------------
1 | from saml2.assertion import Policy
2 | import saml2.xmldsig as ds
3 |
4 | HOST = 'localhost'
5 | PORT = 8087
6 | HTTPS = False
7 | SIGN_ALG = None
8 | DIGEST_ALG = None
9 | #SIGN_ALG = ds.SIG_RSA_SHA512
10 | #DIGEST_ALG = ds.DIGEST_SHA512
11 |
12 | # Which groups of entity categories to use
13 | POLICY = Policy(
14 | {
15 | "default": {"entity_categories": ["swamid", "edugain"]}
16 | }
17 | )
18 |
19 | # HTTPS cert information
20 | SERVER_CERT = "pki/mycert.pem"
21 | SERVER_KEY = "pki/mykey.pem"
22 | CERT_CHAIN = ""
23 |
--------------------------------------------------------------------------------
/example/sp-wsgi/sp_conf.py.example:
--------------------------------------------------------------------------------
1 | from saml2.entity_category.edugain import COC
2 | from saml2 import BINDING_HTTP_REDIRECT
3 | from saml2 import BINDING_HTTP_POST
4 | from saml2.saml import NAME_FORMAT_URI
5 |
6 | try:
7 | from saml2.sigver import get_xmlsec_binary
8 | except ImportError:
9 | get_xmlsec_binary = None
10 |
11 |
12 | if get_xmlsec_binary:
13 | xmlsec_path = get_xmlsec_binary(["/opt/local/bin","/usr/local/bin"])
14 | else:
15 | xmlsec_path = '/usr/local/bin/xmlsec1'
16 |
17 | # Make sure the same port number appear in service_conf.py
18 | BASE = "http://localhost:8087"
19 |
20 | CONFIG = {
21 | "entityid": "%s/%ssp.xml" % (BASE, ""),
22 | 'entity_category': [COC],
23 | "description": "Example SP",
24 | "service": {
25 | "sp": {
26 | "want_response_signed": False,
27 | "authn_requests_signed": True,
28 | "logout_requests_signed": True,
29 | "endpoints": {
30 | "assertion_consumer_service": [
31 | ("%s/acs/post" % BASE, BINDING_HTTP_POST)
32 | ],
33 | "single_logout_service": [
34 | ("%s/slo/redirect" % BASE, BINDING_HTTP_REDIRECT),
35 | ("%s/slo/post" % BASE, BINDING_HTTP_POST),
36 | ],
37 | }
38 | },
39 | },
40 | "key_file": "pki/mykey.pem",
41 | "cert_file": "pki/mycert.pem",
42 | "xmlsec_binary": xmlsec_path,
43 | "metadata": {"local": ["../idp2/idp.xml"]},
44 | "name_form": NAME_FORMAT_URI,
45 | }
46 |
--------------------------------------------------------------------------------
/script/__init__.py:
--------------------------------------------------------------------------------
1 | __author__ = "rolandh"
2 |
--------------------------------------------------------------------------------
/script/filter_testcase_ids.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # extract test case IDs from json-formatted list (`sp_testdrv.py -l` or `idp_testdrv.py -l`)
3 | # usage:
4 | # sp_testdrv.py -l | filter_testcase_ids.py
5 | __author__ = "rhoerbe"
6 |
7 | import json
8 | import sys
9 |
10 |
11 | jdata = json.load(sys.stdin)
12 | for k in jdata:
13 | print(k["id"])
14 |
--------------------------------------------------------------------------------
/script/idp_testdrv.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | __author__ = "rohe0002"
3 |
4 | # from idp_test import saml2base
5 | from idp_test import SAML2client
6 | from idp_test.check import factory
7 |
8 |
9 | cli = SAML2client(factory)
10 | cli.run()
11 |
--------------------------------------------------------------------------------
/script/sp_testdrv.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | __author__ = "rohe0002"
3 |
4 | from sp_test import Client
5 | from sp_test import tests
6 | from sp_test.check import factory
7 |
8 |
9 | cli = Client(tests, factory)
10 | cli.run()
11 |
--------------------------------------------------------------------------------
/script/utility/filter_testcase_ids.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # extract test case IDs from json-formatted list (`sp_testdrv.py -l` or `idp_testdrv.py -l`)
3 | # usage:
4 | # sp_testdrv.py -l | filter_testcase_ids.py
5 | __author__ = "rhoerbe"
6 |
7 | import json
8 | import sys
9 |
10 |
11 | jdata = json.load(sys.stdin)
12 | for k in jdata:
13 | print(k["id"])
14 |
--------------------------------------------------------------------------------
/script/utility/run_available_sp_tests.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # run all tests that are availabe in sp_test
3 | /usr/bin/env python ./tt_config.py > tt_config.json
4 | mkdir -p log
5 | sp_testdrv.py -l | ./filter_testcase_ids.py | sort | ./run_list_of_tests.py
6 |
--------------------------------------------------------------------------------
/script/utility/run_list_of_tests.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | import fileinput
4 | from subprocess import call
5 |
6 |
7 | for line in fileinput.input():
8 | cmd = f"./run_oper.sh {line.rstrip()}"
9 | print(f"executing {cmd}")
10 | call(cmd, shell=True)
11 |
--------------------------------------------------------------------------------
/script/utility/run_oper.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 |
4 | sp_testdrv.py -H -d -Y -J tt_config.json -c td_config $1 2> log/$1.log
5 |
--------------------------------------------------------------------------------
/src/saml2/attribute_resolver.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | #
3 |
4 | """
5 | Contains classes and functions that a SAML2.0 Service Provider (SP) may use
6 | to do attribute aggregation.
7 | """
8 | import logging
9 |
10 | # from saml2 import client
11 | from saml2 import BINDING_SOAP
12 |
13 |
14 | logger = logging.getLogger(__name__)
15 |
16 | DEFAULT_BINDING = BINDING_SOAP
17 |
18 |
19 | class AttributeResolver:
20 | def __init__(self, saml2client, metadata=None, config=None):
21 | self.metadata = metadata
22 | self.saml2client = saml2client
23 | self.metadata = saml2client.config.metadata
24 |
25 | def extend(self, name_id, issuer, vo_members):
26 | """
27 | :param name_id: The identifier by which the subject is know
28 | among all the participents of the VO
29 | :param issuer: Who am I the poses the query
30 | :param vo_members: The entity IDs of the IdP who I'm going to ask
31 | for extra attributes
32 | :return: A dictionary with all the collected information about the
33 | subject
34 | """
35 | result = []
36 | for member in vo_members:
37 | for ass in self.metadata.attribute_consuming_service(member):
38 | for attr_serv in ass.attribute_service:
39 | logger.info("Send attribute request to %s", attr_serv.location)
40 | if attr_serv.binding != BINDING_SOAP:
41 | continue
42 | # attribute query assumes SOAP binding
43 | session_info = self.saml2client.attribute_query(name_id, attr_serv.location, issuer_id=issuer)
44 | if session_info:
45 | result.append(session_info)
46 | return result
47 |
--------------------------------------------------------------------------------
/src/saml2/attributemaps/__init__.py:
--------------------------------------------------------------------------------
1 | __author__ = "rohe0002"
2 | __all__ = ["adfs_v1x", "adfs_v20", "basic", "saml_uri", "shibboleth_uri"]
3 |
--------------------------------------------------------------------------------
/src/saml2/attributemaps/adfs_v1x.py:
--------------------------------------------------------------------------------
1 | CLAIMS = "http://schemas.xmlsoap.org/claims/"
2 |
3 |
4 | MAP = {
5 | "identifier": "urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified",
6 | "fro": {
7 | f"{CLAIMS}commonname": "commonName",
8 | f"{CLAIMS}emailaddress": "emailAddress",
9 | f"{CLAIMS}group": "group",
10 | f"{CLAIMS}upn": "upn",
11 | },
12 | "to": {
13 | "commonName": f"{CLAIMS}commonname",
14 | "emailAddress": f"{CLAIMS}emailaddress",
15 | "group": f"{CLAIMS}group",
16 | "upn": f"{CLAIMS}upn",
17 | },
18 | }
19 |
--------------------------------------------------------------------------------
/src/saml2/attributemaps/adfs_v20.py:
--------------------------------------------------------------------------------
1 | CLAIMS = "http://schemas.xmlsoap.org/claims/"
2 | COM_WS_CLAIMS = "http://schemas.xmlsoap.com/ws/2005/05/identity/claims/"
3 | MS_CLAIMS = "http://schemas.microsoft.com/ws/2008/06/identity/claims/"
4 | ORG_WS_CLAIMS = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/"
5 |
6 |
7 | MAP = {
8 | "identifier": "urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified",
9 | "fro": {
10 | f"{CLAIMS}commonname": "commonName",
11 | f"{CLAIMS}group": "group",
12 | f"{COM_WS_CLAIMS}denyonlysid": "denyOnlySid",
13 | f"{MS_CLAIMS}authenticationmethod": "authenticationMethod",
14 | f"{MS_CLAIMS}denyonlyprimarygroupsid": "denyOnlyPrimaryGroupSid",
15 | f"{MS_CLAIMS}denyonlyprimarysid": "denyOnlyPrimarySid",
16 | f"{MS_CLAIMS}groupsid": "groupSid",
17 | f"{MS_CLAIMS}primarygroupsid": "primaryGroupSid",
18 | f"{MS_CLAIMS}primarysid": "primarySid",
19 | f"{MS_CLAIMS}role": "role",
20 | f"{MS_CLAIMS}windowsaccountname": "windowsAccountName",
21 | f"{ORG_WS_CLAIMS}emailaddress": "emailAddress",
22 | f"{ORG_WS_CLAIMS}givenname": "givenName",
23 | f"{ORG_WS_CLAIMS}name": "name",
24 | f"{ORG_WS_CLAIMS}nameidentifier": "nameId",
25 | f"{ORG_WS_CLAIMS}privatepersonalidentifier": "privatePersonalId",
26 | f"{ORG_WS_CLAIMS}surname": "surname",
27 | f"{ORG_WS_CLAIMS}upn": "upn",
28 | },
29 | "to": {
30 | "authenticationMethod": f"{MS_CLAIMS}authenticationmethod",
31 | "commonName": f"{CLAIMS}commonname",
32 | "denyOnlyPrimaryGroupSid": f"{MS_CLAIMS}denyonlyprimarygroupsid",
33 | "denyOnlyPrimarySid": f"{MS_CLAIMS}denyonlyprimarysid",
34 | "denyOnlySid": f"{COM_WS_CLAIMS}denyonlysid",
35 | "emailAddress": f"{ORG_WS_CLAIMS}emailaddress",
36 | "givenName": f"{ORG_WS_CLAIMS}givenname",
37 | "group": f"{CLAIMS}group",
38 | "groupSid": f"{MS_CLAIMS}groupsid",
39 | "name": f"{ORG_WS_CLAIMS}name",
40 | "nameId": f"{ORG_WS_CLAIMS}nameidentifier",
41 | "primaryGroupSid": f"{MS_CLAIMS}primarygroupsid",
42 | "primarySid": f"{MS_CLAIMS}primarysid",
43 | "privatePersonalId": f"{ORG_WS_CLAIMS}privatepersonalidentifier",
44 | "role": f"{MS_CLAIMS}role",
45 | "surname": f"{ORG_WS_CLAIMS}surname",
46 | "upn": f"{ORG_WS_CLAIMS}upn",
47 | "windowsAccountName": f"{MS_CLAIMS}windowsaccountname",
48 | },
49 | }
50 |
--------------------------------------------------------------------------------
/src/saml2/cryptography/__init__.py:
--------------------------------------------------------------------------------
1 | """This module provides cryptographic elements needed by saml2."""
2 |
--------------------------------------------------------------------------------
/src/saml2/cryptography/asymmetric.py:
--------------------------------------------------------------------------------
1 | """This module provides methods for asymmetric cryptography."""
2 |
3 | import cryptography.hazmat.primitives.asymmetric as _asymmetric
4 | import cryptography.hazmat.primitives.hashes as _hashes
5 | import cryptography.hazmat.primitives.serialization as _serialization
6 |
7 |
8 | def load_pem_private_key(data, password=None):
9 | """Load RSA PEM certificate."""
10 | key = _serialization.load_pem_private_key(data, password)
11 | return key
12 |
13 |
14 | def key_sign(rsakey, message, digest):
15 | """Sign the given message with the RSA key."""
16 | padding = _asymmetric.padding.PKCS1v15()
17 | signature = rsakey.sign(message, padding, digest)
18 | return signature
19 |
20 |
21 | def key_verify(rsakey, signature, message, digest):
22 | """Verify the given signature with the RSA key."""
23 | padding = _asymmetric.padding.PKCS1v15()
24 | if isinstance(rsakey, _asymmetric.rsa.RSAPrivateKey):
25 | rsakey = rsakey.public_key()
26 |
27 | try:
28 | rsakey.verify(signature, message, padding, digest)
29 | except Exception:
30 | return False
31 | else:
32 | return True
33 |
34 |
35 | hashes = _hashes
36 |
--------------------------------------------------------------------------------
/src/saml2/cryptography/errors.py:
--------------------------------------------------------------------------------
1 | from saml2 import Error
2 |
3 |
4 | class CryptographyError(Error):
5 | """Generic error from saml2.cryptography modules"""
6 |
7 |
8 | class SymmetricCryptographyError(CryptographyError):
9 | """Generic error from saml2.cryptography.symmetric modules"""
10 |
11 |
12 | class AsymmetricCryptographyError(CryptographyError):
13 | """Generic error from saml2.cryptography.asymmetric modules"""
14 |
15 |
16 | class PKICryptographyError(CryptographyError):
17 | """Generic error from saml2.cryptography.pki modules"""
18 |
--------------------------------------------------------------------------------
/src/saml2/cryptography/pki.py:
--------------------------------------------------------------------------------
1 | """This module provides methods for PKI operations."""
2 |
3 | from logging import getLogger as get_logger
4 |
5 | from cryptography.hazmat.primitives.serialization import Encoding as _cryptography_encoding
6 | import cryptography.x509 as _x509
7 |
8 |
9 | logger = get_logger(__name__)
10 |
11 | DEFAULT_CERT_TYPE = "pem"
12 |
13 |
14 | def load_pem_x509_certificate(data):
15 | """Load X.509 PEM certificate."""
16 | return _x509.load_pem_x509_certificate(data)
17 |
18 |
19 | def load_der_x509_certificate(data):
20 | """Load X.509 DER certificate."""
21 | return _x509.load_der_x509_certificate(data)
22 |
23 |
24 | def load_x509_certificate(data, cert_type="pem"):
25 | cert_reader = _x509_loaders.get(cert_type)
26 |
27 | if not cert_reader:
28 | cert_reader = _x509_loaders.get("pem")
29 | context = {
30 | "message": "Unknown cert_type, falling back to default",
31 | "cert_type": cert_type,
32 | "default": DEFAULT_CERT_TYPE,
33 | }
34 | logger.warning(context)
35 |
36 | cert = cert_reader(data)
37 | return cert
38 |
39 |
40 | def get_public_bytes_from_cert(cert):
41 | data = cert.public_bytes(_cryptography_encoding.PEM).decode()
42 | return data
43 |
44 |
45 | _x509_loaders = {
46 | "pem": load_pem_x509_certificate,
47 | "der": load_der_x509_certificate,
48 | }
49 |
--------------------------------------------------------------------------------
/src/saml2/data/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IdentityPython/pysaml2/0252ec96058c87c43f89e05d978e72b6ba2e6978/src/saml2/data/__init__.py
--------------------------------------------------------------------------------
/src/saml2/data/schemas/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IdentityPython/pysaml2/0252ec96058c87c43f89e05d978e72b6ba2e6978/src/saml2/data/schemas/__init__.py
--------------------------------------------------------------------------------
/src/saml2/data/schemas/eidas-schema-saml-extensions.xsd:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/src/saml2/data/schemas/saml-schema-authn-context-2.0.xsd:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 | Document identifier: saml-schema-authn-context-2.0
12 | Location: http://docs.oasis-open.org/security/saml/v2.0/
13 | Revision history:
14 | V2.0 (March, 2005):
15 | New core authentication context schema for SAML V2.0.
16 | This is just an include of all types from the schema
17 | referred to in the include statement below.
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/src/saml2/data/schemas/saml-schema-dce-2.0.xsd:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 | Document identifier: saml-schema-dce-2.0
12 | Location: http://docs.oasis-open.org/security/saml/v2.0/
13 | Revision history:
14 | V2.0 (March, 2005):
15 | Custom schema for DCE attribute profile, first published in SAML 2.0.
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/src/saml2/data/schemas/saml-schema-ecp-2.0.xsd:
--------------------------------------------------------------------------------
1 |
2 |
13 |
15 |
17 |
19 |
20 |
21 | Document identifier: saml-schema-ecp-2.0
22 | Location: http://docs.oasis-open.org/security/saml/v2.0/
23 | Revision history:
24 | V2.0 (March, 2005):
25 | Custom schema for ECP profile, first published in SAML 2.0.
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/src/saml2/data/schemas/saml-schema-x500-2.0.xsd:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 | Document identifier: saml-schema-x500-2.0
12 | Location: http://docs.oasis-open.org/security/saml/v2.0/
13 | Revision history:
14 | V2.0 (March, 2005):
15 | Custom schema for X.500 attribute profile, first published in SAML 2.0.
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/saml2/data/schemas/saml-schema-xacml-2.0.xsd:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 | Document identifier: saml-schema-xacml-2.0
12 | Location: http://docs.oasis-open.org/security/saml/v2.0/
13 | Revision history:
14 | V2.0 (March, 2005):
15 | Custom schema for XACML attribute profile, first published in SAML 2.0.
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/saml2/data/schemas/saml-subject-id-attr-v1.0.xsd:
--------------------------------------------------------------------------------
1 |
2 |
12 |
20 |
21 |
22 |
23 | Document title: Metadata Extension Schema for
24 | SAML V2.0 Subject Identifier Attributes Profile Version 1.0
25 | Document identifier: saml-subject-id-attr-v1.0.xsd
26 | Location: http://docs.oasis-open.org/security/saml-subject-id-attr/v1.0/
27 | Revision history:
28 | September 2018:
29 | Initial version contributed to OASIS, (c) The Ohio State University
30 |
31 |
32 |
33 |
34 |
35 |
36 | SAML metadata extension used to regulate allowable attribute scopes.
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/src/saml2/data/schemas/sstc-metadata-attr.xsd:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Document title: SAML V2.0 Metadata Extention for Entity Attributes Schema
7 | Document identifier: sstc-metadata-attr.xsd
8 | Location: http://www.oasis-open.org/committees/documents.php?wg_abbrev=security
9 | Revision history:
10 | V1.0 (November 2008):
11 | Initial version.
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/src/saml2/data/schemas/sstc-req-attr-ext.xsd:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
20 |
21 |
23 |
24 |
25 |
26 | Document title: SAML V2.0 Protocol Extension For Requesting Attributes Per Request
27 | Document identifier: sstc-req-attr-ext
28 | Location: http://docs.oasis-open.org/security/saml-protoc-req-attr-req/v1.0/csprd01/schema/
29 | Revision history: WD-03
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/src/saml2/data/schemas/sstc-saml-attribute-ext.xsd:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
12 | Document title: SAML V2.0 Attribute Extension Schema
13 | Document identifier: sstc-saml-attribute-ext.xsd
14 | Location: http://www.oasis-open.org/committees/documents.php?wg_abbrev=security
15 | Revision history:
16 | V1.0 (October 2008):
17 | Initial version.
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/src/saml2/data/schemas/sstc-saml-metadata-algsupport-v1.0.xsd:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
13 |
21 |
22 |
23 |
24 | Document title: Metadata Extension Schema for SAML V2.0 Metadata Profile for Algorithm Support Version 1.0
25 | Document identifier: sstc-saml-metadata-algsupport.xsd
26 | Location: http://docs.oasis-open.org/security/saml/Post2.0/
27 | Revision history:
28 | V1.0 (June 2010):
29 | Initial version.
30 | (October 2010):
31 | Add processContents="lax" to wildcards.
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/src/saml2/data/templates/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IdentityPython/pysaml2/0252ec96058c87c43f89e05d978e72b6ba2e6978/src/saml2/data/templates/__init__.py
--------------------------------------------------------------------------------
/src/saml2/data/templates/template_enc.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/saml2/entity_category/__init__.py:
--------------------------------------------------------------------------------
1 | __author__ = "rolandh"
2 |
3 | ENTITYATTRIBUTES = "urn:oasis:names:tc:SAML:metadata:attribute&EntityAttributes"
4 |
5 |
6 | def entity_categories(md):
7 | res = []
8 | if "extensions" in md:
9 | for elem in md["extensions"]["extension_elements"]:
10 | if elem["__class__"] == ENTITYATTRIBUTES:
11 | for attr in elem["attribute"]:
12 | res.append(attr["text"])
13 |
14 | return res
15 |
--------------------------------------------------------------------------------
/src/saml2/entity_category/at_egov_pvp2.py:
--------------------------------------------------------------------------------
1 | __author__ = "rhoerbe" # 2013-09-05
2 | # Entity Categories specifying the PVP eGov Token as of "PVP2-Allgemein V2.1.0", http://www.ref.gv.at/
3 |
4 |
5 | EGOVTOKEN = [
6 | "PVP-VERSION",
7 | "PVP-PRINCIPAL-NAME",
8 | "PVP-GIVENNAME",
9 | "PVP-BIRTHDATE",
10 | "PVP-USERID",
11 | "PVP-GID",
12 | "PVP-BPK",
13 | "PVP-MAIL",
14 | "PVP-TEL",
15 | "PVP-PARTICIPANT-ID",
16 | "PVP-PARTICIPANT-OKZ",
17 | "PVP-OU-OKZ",
18 | "PVP-OU",
19 | "PVP-OU-GV-OU-ID",
20 | "PVP-FUNCTION",
21 | "PVP-ROLES",
22 | ]
23 |
24 |
25 | CHARGEATTR = [
26 | "PVP-INVOICE-RECPT-ID",
27 | "PVP-COST-CENTER-ID",
28 | "PVP-CHARGE-CODE",
29 | ]
30 |
31 | # all eGov Token attributes except (1) transaction charging and (2) chaining
32 | PVP2 = "http://www.ref.gv.at/ns/names/agiz/pvp/egovtoken"
33 | # transaction charging extension
34 | PVP2CHARGE = "http://www.ref.gv.at/ns/names/agiz/pvp/egovtoken-charge"
35 |
36 | RELEASE = {
37 | PVP2: EGOVTOKEN,
38 | PVP2CHARGE: CHARGEATTR,
39 | }
40 |
--------------------------------------------------------------------------------
/src/saml2/entity_category/edugain.py:
--------------------------------------------------------------------------------
1 | __author__ = "rolandh"
2 |
3 | COC = "http://www.geant.net/uri/dataprotection-code-of-conduct/v1"
4 | COCO = COC
5 |
6 | RELEASE = {
7 | "": ["eduPersonTargetedID"],
8 | # COC: ["eduPersonPrincipalName", "eduPersonScopedAffiliation", "mail",
9 | # "displayName", "schacHomeOrganization"],
10 | COCO: [
11 | "eduPersonPrincipalName",
12 | "eduPersonScopedAffiliation",
13 | "eduPersonAffiliation",
14 | "mail",
15 | "displayName",
16 | "cn",
17 | "schacHomeOrganization",
18 | ],
19 | }
20 |
21 | ONLY_REQUIRED = {COCO: True}
22 |
--------------------------------------------------------------------------------
/src/saml2/entity_category/incommon.py:
--------------------------------------------------------------------------------
1 | __author__ = "rolandh"
2 |
3 | RESEARCH_AND_SCHOLARSHIP = "http://id.incommon.org/category/research-and-scholarship"
4 |
5 | RELEASE = {
6 | "": ["eduPersonTargetedID"],
7 | RESEARCH_AND_SCHOLARSHIP: [
8 | "eduPersonPrincipalName",
9 | "eduPersonScopedAffiliation",
10 | "mail",
11 | "givenName",
12 | "sn",
13 | "displayName",
14 | ],
15 | }
16 |
--------------------------------------------------------------------------------
/src/saml2/entity_category/refeds.py:
--------------------------------------------------------------------------------
1 | __author__ = "rolandh"
2 |
3 | RESEARCH_AND_SCHOLARSHIP = "http://refeds.org/category/research-and-scholarship"
4 |
5 | RELEASE = {
6 | "": ["eduPersonTargetedID"],
7 | RESEARCH_AND_SCHOLARSHIP: [
8 | "eduPersonPrincipalName",
9 | "eduPersonScopedAffiliation",
10 | "mail",
11 | "givenName",
12 | "sn",
13 | "displayName",
14 | ],
15 | }
16 |
--------------------------------------------------------------------------------
/src/saml2/eptid.py:
--------------------------------------------------------------------------------
1 | # An eduPersonTargetedID comprises
2 | # the entity name of the identity provider, the entity name of the service
3 | # provider, and a opaque string value.
4 | # These strings are separated by "!" symbols. This form is advocated by
5 | # Internet2 and may overtake the other form in due course.
6 |
7 | import hashlib
8 | import logging
9 | import shelve
10 |
11 |
12 | logger = logging.getLogger(__name__)
13 |
14 |
15 | class Eptid:
16 | def __init__(self, secret):
17 | self._db = {}
18 | self.secret = secret
19 |
20 | def make(self, idp, sp, args):
21 | md5 = hashlib.md5()
22 | for arg in args:
23 | md5.update(arg.encode("utf-8"))
24 | if isinstance(sp, bytes):
25 | md5.update(sp)
26 | else:
27 | md5.update(sp.encode("utf-8"))
28 | if isinstance(self.secret, bytes):
29 | md5.update(self.secret)
30 | else:
31 | md5.update(self.secret.encode("utf-8"))
32 | md5.digest()
33 | hashval = md5.hexdigest()
34 | if isinstance(hashval, bytes):
35 | hashval = hashval.decode("ascii")
36 | return "!".join([idp, sp, hashval])
37 |
38 | def __getitem__(self, key):
39 | if isinstance(key, bytes):
40 | key = key.decode("utf-8")
41 | return self._db[key]
42 |
43 | def __setitem__(self, key, value):
44 | if isinstance(key, bytes):
45 | key = key.decode("utf-8")
46 | self._db[key] = value
47 |
48 | def get(self, idp, sp, *args):
49 | # key is a combination of sp_entity_id and object id
50 | key = ("__".join([sp, args[0]])).encode("utf-8")
51 | try:
52 | return self[key]
53 | except KeyError:
54 | val = self.make(idp, sp, args)
55 | self[key] = val
56 | return val
57 |
58 | def close(self):
59 | pass
60 |
61 |
62 | class EptidShelve(Eptid):
63 | def __init__(self, secret, filename):
64 | Eptid.__init__(self, secret)
65 | if filename.endswith(".db"):
66 | filename = filename.rsplit(".db", 1)[0]
67 | self._db = shelve.open(filename, writeback=True, protocol=2)
68 |
69 | def close(self):
70 | self._db.close()
71 |
--------------------------------------------------------------------------------
/src/saml2/extension/__init__.py:
--------------------------------------------------------------------------------
1 | # metadata extensions mainly
2 | __all__ = ["dri", "mdrpi", "mdui", "shibmd", "idpdisc", "algsupport", "mdattr"]
3 |
--------------------------------------------------------------------------------
/src/saml2/extension/idpdisc.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | #
4 | # Generated Thu Jun 23 09:01:47 2011 by parse_xsd.py version 0.4.
5 | #
6 |
7 | import saml2
8 | from saml2 import md
9 |
10 |
11 | NAMESPACE = "urn:oasis:names:tc:SAML:profiles:SSO:idp-discovery-protocol"
12 | BINDING_DISCO = "urn:oasis:names:tc:SAML:profiles:SSO:idp-discovery-protocol"
13 |
14 |
15 | class DiscoveryResponse(md.IndexedEndpointType_):
16 | """The urn:oasis:names:tc:SAML:profiles:SSO:idp-discovery-protocol:
17 | DiscoveryResponse element"""
18 |
19 | c_tag = "DiscoveryResponse"
20 | c_namespace = NAMESPACE
21 | c_children = md.IndexedEndpointType_.c_children.copy()
22 | c_attributes = md.IndexedEndpointType_.c_attributes.copy()
23 | c_child_order = md.IndexedEndpointType_.c_child_order[:]
24 | c_cardinality = md.IndexedEndpointType_.c_cardinality.copy()
25 |
26 |
27 | def discovery_response_from_string(xml_string):
28 | return saml2.create_class_from_xml_string(DiscoveryResponse, xml_string)
29 |
30 |
31 | ELEMENT_FROM_STRING = {
32 | DiscoveryResponse.c_tag: discovery_response_from_string,
33 | }
34 |
35 | ELEMENT_BY_TAG = {
36 | "DiscoveryResponse": DiscoveryResponse,
37 | }
38 |
39 |
40 | def factory(tag, **kwargs):
41 | return ELEMENT_BY_TAG[tag](**kwargs)
42 |
--------------------------------------------------------------------------------
/src/saml2/extension/pefim.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | import saml2
4 | from saml2 import SamlBase
5 | from saml2.xmldsig import KeyInfo
6 |
7 |
8 | NAMESPACE = "urn:net:eustix:names:tc:PEFIM:0.0:assertion"
9 |
10 |
11 | class SPCertEncType_(SamlBase):
12 | """The urn:net:eustix:names:tc:PEFIM:0.0:assertion:SPCertEncType element"""
13 |
14 | c_tag = "SPCertEncType"
15 | c_namespace = NAMESPACE
16 | c_children = SamlBase.c_children.copy()
17 | c_attributes = SamlBase.c_attributes.copy()
18 | c_child_order = SamlBase.c_child_order[:]
19 | c_cardinality = SamlBase.c_cardinality.copy()
20 | c_children["{http://www.w3.org/2000/09/xmldsig#}KeyInfo"] = ("key_info", [KeyInfo])
21 | c_cardinality["key_info"] = {"min": 1}
22 | c_attributes["VerifyDepth"] = ("verify_depth", "unsignedByte", False)
23 | c_child_order.extend(["key_info"])
24 |
25 | def __init__(
26 | self,
27 | key_info=None,
28 | x509_data=None,
29 | verify_depth="1",
30 | text=None,
31 | extension_elements=None,
32 | extension_attributes=None,
33 | ):
34 | SamlBase.__init__(
35 | self, text=text, extension_elements=extension_elements, extension_attributes=extension_attributes
36 | )
37 | if key_info:
38 | self.key_info = key_info
39 | elif x509_data:
40 | self.key_info = KeyInfo(x509_data=x509_data)
41 | else:
42 | self.key_info = []
43 | self.verify_depth = verify_depth
44 | # self.x509_data = x509_data
45 |
46 |
47 | def spcertenc_type__from_string(xml_string):
48 | return saml2.create_class_from_xml_string(SPCertEncType_, xml_string)
49 |
50 |
51 | class SPCertEnc(SPCertEncType_):
52 | """The urn:net:eustix:names:tc:PEFIM:0.0:assertion:SPCertEnc element"""
53 |
54 | c_tag = "SPCertEnc"
55 | c_namespace = NAMESPACE
56 | c_children = SPCertEncType_.c_children.copy()
57 | c_attributes = SPCertEncType_.c_attributes.copy()
58 | c_child_order = SPCertEncType_.c_child_order[:]
59 | c_cardinality = SPCertEncType_.c_cardinality.copy()
60 |
61 |
62 | def spcertenc_from_string(xml_string):
63 | return saml2.create_class_from_xml_string(SPCertEnc, xml_string)
64 |
65 |
66 | ELEMENT_FROM_STRING = {
67 | SPCertEnc.c_tag: spcertenc_from_string,
68 | SPCertEncType_.c_tag: spcertenc_type__from_string,
69 | }
70 |
71 | ELEMENT_BY_TAG = {
72 | "SPCertEnc": SPCertEnc,
73 | "SPCertEncType": SPCertEncType_,
74 | }
75 |
76 |
77 | def factory(tag, **kwargs):
78 | return ELEMENT_BY_TAG[tag](**kwargs)
79 |
--------------------------------------------------------------------------------
/src/saml2/extension/reqinit.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | #
4 | # Generated Thu May 15 13:58:36 2014 by parse_xsd.py version 0.5.
5 | #
6 |
7 | import saml2
8 | from saml2 import md
9 |
10 |
11 | NAMESPACE = "urn:oasis:names:tc:SAML:profiles:SSO:request-init"
12 |
13 |
14 | class RequestInitiator(md.EndpointType_):
15 | """The urn:oasis:names:tc:SAML:profiles:SSO:request-init:RequestInitiator
16 | element"""
17 |
18 | c_tag = "RequestInitiator"
19 | c_namespace = NAMESPACE
20 | c_children = md.EndpointType_.c_children.copy()
21 | c_attributes = md.EndpointType_.c_attributes.copy()
22 | c_child_order = md.EndpointType_.c_child_order[:]
23 | c_cardinality = md.EndpointType_.c_cardinality.copy()
24 |
25 |
26 | def request_initiator_from_string(xml_string):
27 | return saml2.create_class_from_xml_string(RequestInitiator, xml_string)
28 |
29 |
30 | ELEMENT_FROM_STRING = {
31 | RequestInitiator.c_tag: request_initiator_from_string,
32 | }
33 |
34 | ELEMENT_BY_TAG = {
35 | "RequestInitiator": RequestInitiator,
36 | }
37 |
38 |
39 | def factory(tag, **kwargs):
40 | return ELEMENT_BY_TAG[tag](**kwargs)
41 |
--------------------------------------------------------------------------------
/src/saml2/extension/shibmd.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | #
4 | # Generated Sun Mar 20 18:06:44 2011 by parse_xsd.py version 0.4.
5 | #
6 |
7 | import saml2
8 | from saml2 import SamlBase
9 | from saml2 import xmldsig as ds
10 |
11 |
12 | NAMESPACE = "urn:mace:shibboleth:metadata:1.0"
13 |
14 |
15 | class Scope(SamlBase):
16 | """The urn:mace:shibboleth:metadata:1.0:Scope element"""
17 |
18 | c_tag = "Scope"
19 | c_namespace = NAMESPACE
20 | c_value_type = {"base": "string"}
21 | c_children = SamlBase.c_children.copy()
22 | c_attributes = SamlBase.c_attributes.copy()
23 | c_child_order = SamlBase.c_child_order[:]
24 | c_cardinality = SamlBase.c_cardinality.copy()
25 | c_attributes["regexp"] = ("regexp", "boolean", False)
26 |
27 | def __init__(self, regexp="false", text=None, extension_elements=None, extension_attributes=None):
28 | SamlBase.__init__(
29 | self, text=text, extension_elements=extension_elements, extension_attributes=extension_attributes
30 | )
31 | self.regexp = regexp
32 |
33 |
34 | def scope_from_string(xml_string):
35 | return saml2.create_class_from_xml_string(Scope, xml_string)
36 |
37 |
38 | class KeyAuthority(SamlBase):
39 | """The urn:mace:shibboleth:metadata:1.0:KeyAuthority element"""
40 |
41 | c_tag = "KeyAuthority"
42 | c_namespace = NAMESPACE
43 | c_children = SamlBase.c_children.copy()
44 | c_attributes = SamlBase.c_attributes.copy()
45 | c_child_order = SamlBase.c_child_order[:]
46 | c_cardinality = SamlBase.c_cardinality.copy()
47 | c_children["{http://www.w3.org/2000/09/xmldsig#}KeyInfo"] = ("key_info", [ds.KeyInfo])
48 | c_cardinality["key_info"] = {"min": 1}
49 | c_attributes["VerifyDepth"] = ("verify_depth", "unsignedByte", False)
50 | c_child_order.extend(["key_info"])
51 |
52 | def __init__(self, key_info=None, verify_depth="1", text=None, extension_elements=None, extension_attributes=None):
53 | SamlBase.__init__(
54 | self, text=text, extension_elements=extension_elements, extension_attributes=extension_attributes
55 | )
56 | self.key_info = key_info or []
57 | self.verify_depth = verify_depth
58 |
59 |
60 | def key_authority_from_string(xml_string):
61 | return saml2.create_class_from_xml_string(KeyAuthority, xml_string)
62 |
63 |
64 | ELEMENT_FROM_STRING = {
65 | Scope.c_tag: scope_from_string,
66 | KeyAuthority.c_tag: key_authority_from_string,
67 | }
68 |
69 | ELEMENT_BY_TAG = {
70 | "Scope": Scope,
71 | "KeyAuthority": KeyAuthority,
72 | }
73 |
74 |
75 | def factory(tag, **kwargs):
76 | return ELEMENT_BY_TAG[tag](**kwargs)
77 |
--------------------------------------------------------------------------------
/src/saml2/extension/sp_type.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | #
4 | # Generated Tue Jul 18 15:03:44 2017 by parse_xsd.py version 0.5.
5 | #
6 |
7 | import saml2
8 | from saml2 import SamlBase
9 |
10 |
11 | NAMESPACE = "http://eidas.europa.eu/saml-extensions"
12 |
13 |
14 | class SPTypeType_(SamlBase):
15 | """The http://eidas.europa.eu/saml-extensions:SPTypeType element"""
16 |
17 | c_tag = "SPTypeType"
18 | c_namespace = NAMESPACE
19 | c_value_type = {"base": "xsd:string", "enumeration": ["public", "private"]}
20 | c_children = SamlBase.c_children.copy()
21 | c_attributes = SamlBase.c_attributes.copy()
22 | c_child_order = SamlBase.c_child_order[:]
23 | c_cardinality = SamlBase.c_cardinality.copy()
24 |
25 |
26 | def sp_type_type__from_string(xml_string):
27 | return saml2.create_class_from_xml_string(SPTypeType_, xml_string)
28 |
29 |
30 | class SPType(SPTypeType_):
31 | """The http://eidas.europa.eu/saml-extensions:SPType element"""
32 |
33 | c_tag = "SPType"
34 | c_namespace = NAMESPACE
35 | c_children = SPTypeType_.c_children.copy()
36 | c_attributes = SPTypeType_.c_attributes.copy()
37 | c_child_order = SPTypeType_.c_child_order[:]
38 | c_cardinality = SPTypeType_.c_cardinality.copy()
39 |
40 |
41 | def sp_type_from_string(xml_string):
42 | return saml2.create_class_from_xml_string(SPType, xml_string)
43 |
44 |
45 | ELEMENT_FROM_STRING = {
46 | SPType.c_tag: sp_type_from_string,
47 | SPTypeType_.c_tag: sp_type_type__from_string,
48 | }
49 |
50 | ELEMENT_BY_TAG = {
51 | "SPType": SPType,
52 | "SPTypeType": SPTypeType_,
53 | }
54 |
55 |
56 | def factory(tag, **kwargs):
57 | return ELEMENT_BY_TAG[tag](**kwargs)
58 |
--------------------------------------------------------------------------------
/src/saml2/filter.py:
--------------------------------------------------------------------------------
1 | __author__ = "roland"
2 |
3 |
4 | class Filter:
5 | def __init__(self):
6 | pass
7 |
8 | def __call__(self, *args, **kwargs):
9 | pass
10 |
11 |
12 | class AllowDescriptor(Filter):
13 | def __init__(self, allow):
14 | """
15 |
16 | :param allow: List of allowed descriptors
17 | :return:
18 | """
19 | super().__init__()
20 | self.allow = allow
21 |
22 | def __call__(self, entity_descriptor):
23 | # get descriptors
24 | _all = []
25 | for desc in list(entity_descriptor.keys()):
26 | if desc.endswith("_descriptor"):
27 | typ, _ = desc.rsplit("_", 1)
28 | if typ in self.allow:
29 | _all.append(typ)
30 | else:
31 | del entity_descriptor[desc]
32 |
33 | if not _all:
34 | return None
35 | else:
36 | return entity_descriptor
37 |
--------------------------------------------------------------------------------
/src/saml2/population.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | from saml2.cache import Cache
4 |
5 |
6 | logger = logging.getLogger(__name__)
7 |
8 |
9 | class Population:
10 | def __init__(self, cache=None):
11 | if cache:
12 | if isinstance(cache, str):
13 | self.cache = Cache(cache)
14 | else:
15 | self.cache = cache
16 | else:
17 | self.cache = Cache()
18 |
19 | def add_information_about_person(self, session_info):
20 | """If there already are information from this source in the cache
21 | this function will overwrite that information"""
22 |
23 | session_info = dict(session_info)
24 | name_id = session_info["name_id"]
25 | issuer = session_info.pop("issuer")
26 | self.cache.set(name_id, issuer, session_info, session_info["not_on_or_after"])
27 | return name_id
28 |
29 | def stale_sources_for_person(self, name_id, sources=None):
30 | """
31 |
32 | :param name_id: Identifier of the subject, a NameID instance
33 | :param sources: Sources for information about the subject
34 | :return:
35 | """
36 | if not sources:
37 | # assume that all the members has be asked
38 | # once before, hence they are represented in the cache
39 | sources = self.cache.entities(name_id)
40 | sources = [m for m in sources if not self.cache.active(name_id, m)]
41 | return sources
42 |
43 | def issuers_of_info(self, name_id):
44 | return self.cache.entities(name_id)
45 |
46 | def get_identity(self, name_id, entities=None, check_not_on_or_after=True):
47 | return self.cache.get_identity(name_id, entities, check_not_on_or_after)
48 |
49 | def get_info_from(self, name_id, entity_id, check_not_on_or_after=True):
50 | return self.cache.get(name_id, entity_id, check_not_on_or_after)
51 |
52 | def subjects(self):
53 | """Returns the name id's for all the persons in the cache"""
54 | return self.cache.subjects()
55 |
56 | def remove_person(self, name_id):
57 | self.cache.delete(name_id)
58 |
59 | def get_entityid(self, name_id, source_id, check_not_on_or_after=True):
60 | try:
61 | return self.cache.get(name_id, source_id, check_not_on_or_after)["name_id"]
62 | except (KeyError, ValueError):
63 | return ""
64 |
65 | def sources(self, name_id):
66 | return self.cache.entities(name_id)
67 |
--------------------------------------------------------------------------------
/src/saml2/profile/__init__.py:
--------------------------------------------------------------------------------
1 | # profile schema descriptions
2 | __author__ = "rolandh"
3 |
--------------------------------------------------------------------------------
/src/saml2/profile/samlec.py:
--------------------------------------------------------------------------------
1 | from saml2 import SamlBase
2 |
3 |
4 | NAMESPACE = "urn:ietf:params:xml:ns:samlec"
5 |
6 |
7 | class GeneratedKey(SamlBase):
8 | c_tag = "GeneratedKey"
9 | c_namespace = NAMESPACE
10 |
11 |
12 | ELEMENT_BY_TAG = {
13 | "GeneratedKey": GeneratedKey,
14 | }
15 |
--------------------------------------------------------------------------------
/src/saml2/s2repoze/__init__.py:
--------------------------------------------------------------------------------
1 | # Created by Roland Hedberg
2 |
--------------------------------------------------------------------------------
/src/saml2/s2repoze/plugins/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IdentityPython/pysaml2/0252ec96058c87c43f89e05d978e72b6ba2e6978/src/saml2/s2repoze/plugins/__init__.py
--------------------------------------------------------------------------------
/src/saml2/s2repoze/plugins/ini.py:
--------------------------------------------------------------------------------
1 | import ConfigParser
2 |
3 | # from repoze.who.interfaces import IChallenger, IIdentifier, IAuthenticator
4 | from repoze.who.interfaces import IMetadataProvider
5 | from zope.interface import implements
6 |
7 |
8 | class INIMetadataProvider:
9 |
10 | implements(IMetadataProvider)
11 |
12 | def __init__(self, ini_file, key_attribute):
13 |
14 | self.users = ConfigParser.ConfigParser()
15 | self.users.readfp(open(ini_file))
16 | self.key_attribute = key_attribute
17 |
18 | def add_metadata(self, _environ, identity):
19 | # logger = environ.get('repoze.who.logger','')
20 |
21 | key = identity.get("repoze.who.userid")
22 | try:
23 | if self.key_attribute:
24 | for sec in self.users.sections():
25 | if self.users.has_option(sec, self.key_attribute):
26 | if key in self.users.get(sec, self.key_attribute):
27 | identity["user"] = dict(self.users.items(sec))
28 | break
29 | else:
30 | identity["user"] = dict(self.users.items(key))
31 | except ValueError:
32 | pass
33 |
34 |
35 | def make_plugin(ini_file, key_attribute=""):
36 | return INIMetadataProvider(ini_file, key_attribute)
37 |
--------------------------------------------------------------------------------
/src/saml2/schema/__init__.py:
--------------------------------------------------------------------------------
1 | __author__ = "rolandh"
2 |
--------------------------------------------------------------------------------
/src/saml2/sdb.py:
--------------------------------------------------------------------------------
1 | from hashlib import sha1
2 | import logging
3 |
4 | from saml2.ident import code_binary
5 |
6 |
7 | logger = logging.getLogger(__name__)
8 |
9 |
10 | def context_match(cfilter, cntx):
11 | # TODO
12 | return True
13 |
14 |
15 | # The key to the stored authn statement is placed encrypted in the cookie
16 |
17 |
18 | class SessionStorage:
19 | """In memory storage of session information"""
20 |
21 | def __init__(self):
22 | self.db = {"assertion": {}, "authn": {}}
23 | self.assertion = self.db["assertion"]
24 | self.authn = self.db["authn"]
25 |
26 | def store_assertion(self, assertion, to_sign):
27 | self.assertion[assertion.id] = (assertion, to_sign)
28 | key = sha1(code_binary(assertion.subject.name_id)).hexdigest()
29 | try:
30 | self.authn[key].append(assertion.authn_statement)
31 | except KeyError:
32 | self.authn[key] = [assertion.authn_statement]
33 |
34 | def get_assertion(self, cid):
35 | return self.assertion[cid]
36 |
37 | def get_authn_statements(self, name_id, session_index=None, requested_context=None):
38 | """
39 |
40 | :param name_id:
41 | :param session_index:
42 | :param requested_context:
43 | :return:
44 | """
45 | result = []
46 | key = sha1(code_binary(name_id)).hexdigest()
47 | try:
48 | statements = self.authn[key]
49 | except KeyError:
50 | logger.info("Unknown subject %s", name_id)
51 | return []
52 |
53 | for statement in statements:
54 | if session_index:
55 | if statement.session_index != session_index:
56 | continue
57 | if requested_context:
58 | if not context_match(requested_context, statement[0].authn_context):
59 | continue
60 | result.append(statement)
61 |
62 | return result
63 |
64 | def remove_authn_statements(self, name_id):
65 | logger.debug("remove authn about: %s", name_id)
66 | nkey = sha1(code_binary(name_id)).hexdigest()
67 |
68 | del self.authn[nkey]
69 |
--------------------------------------------------------------------------------
/src/saml2/tools/mdexport.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import argparse
3 |
4 | from saml2.attribute_converter import ac_factory
5 | from saml2.httpbase import HTTPBase
6 | from saml2.mdstore import MetaDataExtern
7 | from saml2.mdstore import MetaDataFile
8 | from saml2.sigver import SecurityContext
9 | from saml2.sigver import _get_xmlsec_cryptobackend
10 |
11 |
12 | __author__ = "rolandh"
13 |
14 | """
15 | A script that imports and verifies metadata and then dumps it in a basic
16 | dictionary format.
17 | """
18 |
19 |
20 | def main():
21 | parser = argparse.ArgumentParser()
22 | parser.add_argument("-t", dest="type")
23 | parser.add_argument("-u", dest="url")
24 | parser.add_argument("-c", dest="cert")
25 | parser.add_argument("-a", dest="attrsmap")
26 | parser.add_argument("-o", dest="output")
27 | parser.add_argument("-x", dest="xmlsec")
28 | parser.add_argument(dest="item")
29 | args = parser.parse_args()
30 |
31 | metad = None
32 |
33 | if args.type == "local":
34 | metad = MetaDataFile(args.item, args.item)
35 | elif args.type == "external":
36 | ATTRCONV = ac_factory(args.attrsmap)
37 | httpc = HTTPBase()
38 | crypto = _get_xmlsec_cryptobackend(args.xmlsec)
39 | sc = SecurityContext(crypto)
40 | metad = MetaDataExtern(ATTRCONV, args.url, sc, cert=args.cert, http=httpc)
41 |
42 | if metad is not None:
43 | metad.load()
44 | txt = metad.dumps()
45 | if args.output:
46 | f = open(args.output, "w")
47 | f.write(txt)
48 | f.close()
49 | else:
50 | print(txt)
51 |
52 |
53 | if __name__ == "__main__":
54 | main()
55 |
--------------------------------------------------------------------------------
/src/saml2/tools/mdexport_test.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | """
4 | A script that imports and verifies metadata and then dumps it in a basic
5 | dictionary format.
6 | """
7 |
8 | import sys
9 |
10 | from saml2.mdstore import MetaDataExtern
11 | from saml2.mdstore import MetaDataFile
12 |
13 |
14 | MDIMPORT = {
15 | "swamid": {
16 | "url": "https://kalmar2.org/simplesaml/module.php/aggregator/?id=kalmarcentral2&set=saml2",
17 | "cert": "kalmar2.pem",
18 | "type": "external",
19 | },
20 | "incommon": {"file": "InCommon-metadata.xml", "type": "local"},
21 | "test": {"file": "mdtest.xml", "type": "local"},
22 | }
23 |
24 |
25 | def main():
26 | item = MDIMPORT[sys.argv[1]]
27 |
28 | metad = None
29 |
30 | if item["type"] == "local":
31 | metad = MetaDataFile(sys.argv[1], item["file"])
32 | elif item["type"] == "external":
33 | metad = MetaDataExtern(sys.argv[1], item["url"], "/opt/local/bin/xmlsec1", item["cert"])
34 |
35 | if metad:
36 | metad.load()
37 | print(metad.dumps())
38 |
39 |
40 | if __name__ == "__main__":
41 | main()
42 |
--------------------------------------------------------------------------------
/src/saml2/tools/mdimport.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import time
3 |
4 | from saml2.attribute_converter import ac_factory
5 | from saml2.mdstore import MetaDataFile
6 | from saml2.mdstore import MetaDataMD
7 |
8 |
9 | __author__ = "rolandh"
10 |
11 |
12 | def main():
13 | start = time.time()
14 | for i in range(1, 10):
15 | mdmd = MetaDataMD(ac_factory("../tests/attributemaps"), "swamid2.md")
16 | mdmd.load()
17 |
18 | _ = mdmd.keys()
19 |
20 | print(time.time() - start)
21 |
22 | start = time.time()
23 | for i in range(1, 10):
24 | mdf = MetaDataFile(ac_factory("../tests/attributemaps"), "../tests/swamid-2.0.xml")
25 | mdf.load()
26 | _ = mdf.keys()
27 |
28 | print(time.time() - start)
29 |
30 |
31 | if __name__ == "__main__":
32 | main()
33 |
--------------------------------------------------------------------------------
/src/saml2/tools/merge_metadata.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import argparse
3 |
4 | from saml2.attribute_converter import ac_factory
5 | from saml2.httpbase import HTTPBase
6 | from saml2.mdstore import MetaDataExtern
7 | from saml2.mdstore import MetaDataFile
8 | from saml2.mdstore import MetadataStore
9 | from saml2.sigver import SecurityContext
10 | from saml2.sigver import _get_xmlsec_cryptobackend
11 |
12 |
13 | __author__ = "rolandh"
14 |
15 | """
16 | A script that imports and verifies metadata.
17 | """
18 |
19 |
20 | def main():
21 | parser = argparse.ArgumentParser()
22 | parser.add_argument("-a", dest="attrsmap")
23 | parser.add_argument("-o", dest="output", default="local")
24 | parser.add_argument("-x", dest="xmlsec")
25 | parser.add_argument("-i", dest="ignore_valid", action="store_true")
26 | parser.add_argument(dest="conf")
27 | args = parser.parse_args()
28 |
29 | metad = None
30 |
31 | # config file format
32 | #
33 | # local
34 | # remote
35 | #
36 | # for instance
37 | #
38 | # local metadata_sp_1.xml
39 | # local InCommon-metadata.xml
40 | # remote https://kalmar2.org/simplesaml/module.php/aggregator/?id=kalmarcentral2&set=saml2 kalmar2.pem
41 | #
42 |
43 | ATTRCONV = ac_factory(args.attrsmap)
44 |
45 | mds = MetadataStore(None, None)
46 |
47 | for line in open(args.conf).readlines():
48 | line = line.strip()
49 | if len(line) == 0:
50 | continue
51 | elif line[0] == "#":
52 | continue
53 | spec = line.split(" ")
54 |
55 | if args.ignore_valid:
56 | kwargs = {"check_validity": False}
57 | else:
58 | kwargs = {}
59 |
60 | if spec[0] == "local":
61 | metad = MetaDataFile(spec[1], spec[1], **kwargs)
62 | elif spec[0] == "remote":
63 | ATTRCONV = ac_factory(args.attrsmap)
64 | httpc = HTTPBase()
65 | crypto = _get_xmlsec_cryptobackend(args.xmlsec)
66 | sc = SecurityContext(crypto, key_type="", cert_type="")
67 | metad = MetaDataExtern(ATTRCONV, spec[1], sc, cert=spec[2], http=httpc, **kwargs)
68 |
69 | if metad is not None:
70 | metad.load()
71 |
72 | mds.metadata[spec[1]] = metad
73 |
74 | print(mds.dumps(args.output))
75 |
76 |
77 | if __name__ == "__main__":
78 | main()
79 |
--------------------------------------------------------------------------------
/src/saml2/tools/update_metadata.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | curl -O -G http://md.swamid.se/md/swamid-2.0.xml
3 | python3 mdexport.py -t local -o swamid2.md swamid-2.0.xml
4 |
--------------------------------------------------------------------------------
/src/saml2/tools/verify_metadata.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | import argparse
4 |
5 | from saml2.attribute_converter import ac_factory
6 | from saml2.httpbase import HTTPBase
7 | from saml2.mdstore import MetaDataExtern
8 | from saml2.mdstore import MetaDataFile
9 | from saml2.sigver import SecurityContext
10 | from saml2.sigver import _get_xmlsec_cryptobackend
11 |
12 |
13 | __author__ = "rolandh"
14 |
15 | """
16 | A script that imports and verifies metadata.
17 | """
18 |
19 |
20 | def main():
21 | parser = argparse.ArgumentParser()
22 | parser.add_argument("-t", dest="type")
23 | parser.add_argument("-u", dest="url")
24 | parser.add_argument("-c", dest="cert")
25 | parser.add_argument("-a", dest="attrsmap")
26 | parser.add_argument("-o", dest="output")
27 | parser.add_argument("-x", dest="xmlsec")
28 | parser.add_argument("-i", dest="ignore_valid", action="store_true")
29 | parser.add_argument(dest="item")
30 | args = parser.parse_args()
31 |
32 | metad = None
33 |
34 | if args.ignore_valid:
35 | kwargs = {"check_validity": False}
36 | else:
37 | kwargs = {}
38 |
39 | if args.type == "local":
40 | if args.cert and args.xmlsec:
41 | crypto = _get_xmlsec_cryptobackend(args.xmlsec)
42 | sc = SecurityContext(crypto)
43 | metad = MetaDataFile(args.item, args.item, cert=args.cert, security=sc, **kwargs)
44 | else:
45 | metad = MetaDataFile(args.item, args.item, **kwargs)
46 | elif args.type == "external":
47 | ATTRCONV = ac_factory(args.attrsmap)
48 | httpc = HTTPBase()
49 | crypto = _get_xmlsec_cryptobackend(args.xmlsec)
50 | sc = SecurityContext(crypto)
51 | metad = MetaDataExtern(ATTRCONV, args.url, sc, cert=args.cert, http=httpc, **kwargs)
52 |
53 | if metad:
54 | metad.load()
55 | print("OK")
56 |
57 |
58 | if __name__ == "__main__":
59 | main()
60 |
--------------------------------------------------------------------------------
/src/saml2/userinfo/__init__.py:
--------------------------------------------------------------------------------
1 | # Interface to external user info resources
2 |
3 | import copy
4 |
5 |
6 | class UserInfo:
7 | """Read only interface to a user info store"""
8 |
9 | def __init__(self):
10 | pass
11 |
12 | def __call__(self, **kwargs):
13 | pass
14 |
15 |
16 | class UserInfoDB(UserInfo):
17 | """Read only interface to a user info store"""
18 |
19 | def __init__(self, db=None):
20 | self.db = db
21 |
22 | @staticmethod
23 | def filter(userinfo, user_info_claims=None):
24 | """
25 | Return only those claims that are asked for.
26 | It's a best effort task; if essential claims are not present
27 | no error is flagged.
28 |
29 | :param userinfo: A dictionary containing the available user info.
30 | :param user_info_claims: A dictionary specifying the asked for claims
31 | :return: A dictionary of filtered claims.
32 | """
33 |
34 | if user_info_claims is None:
35 | return copy.copy(userinfo)
36 | else:
37 | result = {}
38 | missing = []
39 | optional = []
40 | for key, restr in user_info_claims.items():
41 | try:
42 | result[key] = userinfo[key]
43 | except KeyError:
44 | if restr == {"essential": True}:
45 | missing.append(key)
46 | else:
47 | optional.append(key)
48 | return result
49 |
50 | def __call__(self, userid, user_info_claims=None, **kwargs):
51 | try:
52 | return self.filter(self.db[userid], user_info_claims)
53 | except KeyError:
54 | return {}
55 |
--------------------------------------------------------------------------------
/src/saml2/userinfo/ldapinfo.py:
--------------------------------------------------------------------------------
1 | import ldap
2 | from ldap import SCOPE_SUBTREE
3 |
4 | from saml2.userinfo import UserInfo
5 |
6 |
7 | class UserInfoLDAP(UserInfo):
8 | def __init__(
9 | self, uri, base, filter_pattern, scope=SCOPE_SUBTREE, tls=False, user="", passwd="", attr=None, attrsonly=False
10 | ):
11 | UserInfo.__init__(self)
12 | self.ldapuri = uri
13 | self.base = base
14 | self.filter_pattern = filter_pattern
15 | self.scope = scope
16 | self.tls = tls
17 | self.attr = attr
18 | self.attrsonly = attrsonly
19 | self.ld = ldap.initialize(uri)
20 | self.ld.protocol_version = ldap.VERSION3
21 | self.ld.simple_bind_s(user, passwd)
22 |
23 | def __call__(
24 | self, userid, base="", filter_pattern="", scope=SCOPE_SUBTREE, tls=False, attr=None, attrsonly=False, **kwargs
25 | ):
26 |
27 | if filter_pattern:
28 | _filter = filter_pattern % userid
29 | else:
30 | _filter = self.filter_pattern % userid
31 |
32 | _base = base or self.base
33 | _scope = scope or self.scope
34 | _attr = attr or self.attr
35 | _attrsonly = attrsonly or self.attrsonly
36 | arg = [_base, _scope, _filter, _attr, _attrsonly]
37 | res = self.ld.search_s(*arg)
38 | # should only be one entry and the information per entry is
39 | # the tuple (dn, ava)
40 | return res[0][1]
41 |
--------------------------------------------------------------------------------
/src/saml2/version.py:
--------------------------------------------------------------------------------
1 | try:
2 | from importlib.metadata import version as _resolve_package_version
3 | except ImportError:
4 | from importlib_metadata import version as _resolve_package_version # type: ignore[no-redef]
5 |
6 |
7 | def _parse_version():
8 | value = _resolve_package_version("pysaml2")
9 | return value
10 |
11 |
12 | version = _parse_version()
13 |
--------------------------------------------------------------------------------
/src/saml2/ws/__init__.py:
--------------------------------------------------------------------------------
1 | __author__ = "roland"
2 |
--------------------------------------------------------------------------------
/src/saml2/xml/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IdentityPython/pysaml2/0252ec96058c87c43f89e05d978e72b6ba2e6978/src/saml2/xml/__init__.py
--------------------------------------------------------------------------------
/src/saml2test/__init__.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import socket
3 | from subprocess import PIPE
4 | from subprocess import Popen
5 | import sys
6 | import time
7 | import traceback
8 |
9 | import requests
10 |
11 | from saml2test.check import CRITICAL
12 |
13 |
14 | logger = logging.getLogger(__name__)
15 |
16 | __author__ = "rolandh"
17 |
18 |
19 | class FatalError(Exception):
20 | pass
21 |
22 |
23 | class CheckError(Exception):
24 | pass
25 |
26 |
27 | class HTTP_ERROR(Exception):
28 | pass
29 |
30 |
31 | class Unknown(Exception):
32 | pass
33 |
34 |
35 | class OperationError(Exception):
36 | pass
37 |
38 |
39 | class ContextFilter(logging.Filter):
40 | """
41 | This is a filter which injects time laps information into the log.
42 | """
43 |
44 | def start(self):
45 | self.start = time.time()
46 |
47 | def filter(self, record):
48 | record.delta = time.time() - self.start
49 | return True
50 |
51 |
52 | def start_script(path, *args):
53 | popen_args = [path]
54 | popen_args.extend(args)
55 | return Popen(popen_args, stdout=PIPE, stderr=PIPE)
56 |
57 |
58 | def stop_script_by_name(name):
59 | import os
60 | import signal
61 | import subprocess
62 |
63 | p = subprocess.Popen(["ps", "-A"], stdout=subprocess.PIPE)
64 | out, err = p.communicate()
65 |
66 | for line in out.splitlines():
67 | if name in line:
68 | pid = int(line.split(None, 1)[0])
69 | os.kill(pid, signal.SIGKILL)
70 |
71 |
72 | def stop_script_by_pid(pid):
73 | import os
74 | import signal
75 |
76 | os.kill(pid, signal.SIGKILL)
77 |
78 |
79 | def get_page(url):
80 | resp = requests.get(url)
81 | if resp.status_code == 200:
82 | return resp.text
83 | else:
84 | raise HTTP_ERROR(resp.status)
85 |
86 |
87 | def exception_trace(tag, exc, log=None):
88 | message = traceback.format_exception(*sys.exc_info())
89 |
90 | try:
91 | _exc = f"Exception: {exc}"
92 | except UnicodeEncodeError:
93 | _exc = f"Exception: {exc.message.encode('utf-8', 'replace')}"
94 |
95 | return {"status": CRITICAL, "message": _exc, "content": "".join(message)}
96 |
97 |
98 | def ip_addresses():
99 | return [ip for ip in socket.gethostbyname_ex(socket.gethostname())[2] if not ip.startswith("127.")]
100 |
--------------------------------------------------------------------------------
/src/saml2test/status.py:
--------------------------------------------------------------------------------
1 | __author__ = "rolandh"
2 |
3 | INFORMATION = 0
4 | OK = 1
5 | WARNING = 2
6 | ERROR = 3
7 | CRITICAL = 4
8 | INTERACTION = 5
9 |
10 | STATUSCODE = ["INFORMATION", "OK", "WARNING", "ERROR", "CRITICAL", "INTERACTION"]
11 |
--------------------------------------------------------------------------------
/src/utility/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IdentityPython/pysaml2/0252ec96058c87c43f89e05d978e72b6ba2e6978/src/utility/__init__.py
--------------------------------------------------------------------------------
/src/utility/metadata.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import os.path
3 | import time
4 | from time import strftime
5 | import urllib
6 |
7 |
8 | __author__ = "rhoerbe"
9 |
10 | logger = logging.getLogger(__name__)
11 |
12 |
13 | def fetch_metadata(url, path, maxage=600):
14 | """
15 | :param url: metadata remote location
16 | :param path: metdata file name
17 | :param maxage: if max age of existing metadata file (s) is exceeded,
18 | the file will be fetched from the remote location
19 | """
20 | fetch = False
21 | if not os.path.isfile(path):
22 | fetch = True
23 | logger.debug("metadata file %s not found", path)
24 | elif (os.path.getmtime(path) + maxage) < time.time():
25 | fetch = True
26 | logger.debug(
27 | "metadata file %s from %s is more than %s s old",
28 | path,
29 | strftime("%Y-%m-%d %H:%M:%S", time.localtime(os.path.getmtime(path))),
30 | maxage,
31 | )
32 | else:
33 | logger.debug("metadata file %s is less than %s s old", path, maxage)
34 | if fetch:
35 | f = urllib.URLopener()
36 | try:
37 | f.retrieve(url, path)
38 | logger.debug("downloaded metadata from %s into %s", url, path)
39 | except Exception as e:
40 | logger.debug("downloaded metadata from %s failed: %s", url, str(e))
41 |
--------------------------------------------------------------------------------
/tests/SWITCHaaiRootCA.crt.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIIDnzCCAoegAwIBAgINSWITCHaai+Root+CAzANBgkqhkiG9w0BAQUFADBrMQsw
3 | CQYDVQQGEwJDSDFAMD4GA1UEChM3U3dpdGNoIC0gVGVsZWluZm9ybWF0aWtkaWVu
4 | c3RlIGZ1ZXIgTGVocmUgdW5kIEZvcnNjaHVuZzEaMBgGA1UEAxMRU1dJVENIYWFp
5 | IFJvb3QgQ0EwHhcNMDgwNTE1MDYzMDAwWhcNMjgwNTE1MDYyOTU5WjBrMQswCQYD
6 | VQQGEwJDSDFAMD4GA1UEChM3U3dpdGNoIC0gVGVsZWluZm9ybWF0aWtkaWVuc3Rl
7 | IGZ1ZXIgTGVocmUgdW5kIEZvcnNjaHVuZzEaMBgGA1UEAxMRU1dJVENIYWFpIFJv
8 | b3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDUSWbn/rhWew/s
9 | LJRyciyRKDGyFXSgiDO/EohYuZLw6EAKLLlhZorNtEHQbbn0Oo13S33MclHMvGWT
10 | KJM0u1hG+6gLy78EPmJbqAE1Uv23wVEH4SX0VJfl3JVqIebiAH/CjuLubgMUspDI
11 | jOdQHNLS7pthTbm7Tgh7zMsiLPyMTZJep5CGbqv8NoK6bMaF0Z+Bt7e1JRlhHFCV
12 | iJJaR/+hfpzLsJ8NWVivvrpRGaGJ1XR+9FGsTkjNdMCirNJJZ6XvUOe5w7pHSd9M
13 | cppFP0eyLs02AMzMXI4iz6PK/w3EdzXGXpK+gSgvLxWYct4xHpv1e2NXhNgdJOSN
14 | 9ra/wJLVAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEG
15 | MB0GA1UdDgQWBBTpmuIGWOsP14EDXVyXubG1k307hDANBgkqhkiG9w0BAQUFAAOC
16 | AQEAMV/eIW6pFB+mbk7rD7hUPTWDRaoca3kHqmFGFnHfuY8+c0/Mqjh8Y/jyX1yb
17 | f58crTSWrbyGbUZ3oxDGQ34tuZSkmeR32NqryiX3sP5qlNSozVguQKt8o4vhS1Qe
18 | WPsXALs3em2pdKuIGSOpbuDnopPcmU2g5Zi2R5P7qpKDKAKtNUEwV+LW7GBMEksO
19 | Nj7BFXk4AFBFBijaYJGgHmoKSImVgeNIvsV+BSv5HJ4q6vcxfnwuvvGHM0AGphYO
20 | 6f5qtHMUgvAblI8M/2QsBgethaGrirtKJ3aCRLdaR2R1QfaGRpck/Ron5/MpMxiJ
21 | wLT8YlW/zjx2yNABhPSAjfzeMw==
22 | -----END CERTIFICATE-----
23 |
--------------------------------------------------------------------------------
/tests/aa_conf.py:
--------------------------------------------------------------------------------
1 | from saml2 import BINDING_HTTP_REDIRECT
2 | from saml2 import BINDING_SOAP
3 | from saml2 import NAME_FORMAT_URI
4 |
5 |
6 | BASE = "http://localhost:8089/"
7 |
8 | from pathutils import full_path
9 |
10 |
11 | CONFIG = {
12 | "service": {
13 | "aa": {
14 | "endpoints": {
15 | "attribute_service": [(f"{BASE}as", BINDING_HTTP_REDIRECT)],
16 | "single_logout_service": [(f"{BASE}slo", BINDING_SOAP)],
17 | },
18 | "release_policy": {
19 | "default": {
20 | "lifetime": {"minutes": 15},
21 | "attribute_restrictions": None, # means all I have
22 | "name_form": NAME_FORMAT_URI,
23 | },
24 | },
25 | "subject_data": full_path("aa.db"),
26 | }
27 | },
28 | "entityid": f"{BASE}aa",
29 | "name": "Rolands AA",
30 | "debug": 1,
31 | "key_file": full_path("test.key"),
32 | "cert_file": full_path("test.pem"),
33 | # "xmlsec_binary" : None,
34 | "metadata": {
35 | "local": [full_path("metadata.xml"), full_path("vo_metadata.xml")],
36 | },
37 | "attribute_map_dir": full_path("attributemaps"),
38 | "organization": {
39 | "name": "Exempel AB",
40 | "display_name": [("Exempel AB", "se"), ("Example Co.", "en")],
41 | "url": "http://www.example.com/roland",
42 | },
43 | "contact_person": [
44 | {
45 | "given_name": "John",
46 | "sur_name": "Smith",
47 | "email_address": ["john.smith@example.com"],
48 | "contact_type": "technical",
49 | },
50 | ],
51 | }
52 |
--------------------------------------------------------------------------------
/tests/attribute.map:
--------------------------------------------------------------------------------
1 | urn:oid:2.5.4.4 surName urn:oasis:names:tc:SAML:2.0:attrname-format:uri
2 | urn:oid:2.5.4.42 givenName urn:oasis:names:tc:SAML:2.0:attrname-format:uri
3 | urn:oid:2.5.4.12 title urn:oasis:names:tc:SAML:2.0:attrname-format:uri
4 | urn:oid:0.9.2342.19200300.100.1.1 uid urn:oasis:names:tc:SAML:2.0:attrname-format:uri
5 | urn:oid:0.9.2342.19200300.100.1.3 mail urn:oasis:names:tc:SAML:2.0:attrname-format:uri
6 | urn:oid:1.3.6.1.4.1.5923.1.1.1.1 eduPersonAffiliation urn:oasis:names:tc:SAML:2.0:attrname-format:uri
7 | urn:oid:1.3.6.1.4.1.5923.1.1.1.7 eduPersonEntitlement urn:oasis:names:tc:SAML:2.0:attrname-format:uri
8 |
--------------------------------------------------------------------------------
/tests/conftest.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | import pytest
4 |
5 |
6 | # TODO: On my system this function seems to be returning an incorrect location
7 | @pytest.fixture
8 | def xmlsec(request):
9 | for path in os.environ["PATH"].split(":"):
10 | fil = os.path.join(path, "xmlsec1")
11 | if os.access(fil, os.X_OK):
12 | return fil
13 |
14 | raise Exception("Can't find xmlsec1")
15 |
16 |
17 | @pytest.fixture
18 | def AVA(request):
19 | return [
20 | {
21 | "surName": ["Jeter"],
22 | "givenName": ["Derek"],
23 | },
24 | {
25 | "surName": ["Howard"],
26 | "givenName": ["Ryan"],
27 | },
28 | {
29 | "surName": ["Suzuki"],
30 | "givenName": ["Ischiro"],
31 | },
32 | {
33 | "surName": ["Hedberg"],
34 | "givenName": ["Roland"],
35 | },
36 | ]
37 |
--------------------------------------------------------------------------------
/tests/create_certs.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | newcert="openssl req -x509 -new -days 365000 -sha256 -config openssl.cnf -set_serial 1"
3 | $newcert -key test_1.key -out test_1.crt -subj '/C=zz/ST=zz/L=zzzz/O=Zzzzz/OU=Zzzzz/CN=test'
4 | $newcert -key test_2.key -out test_2.crt -subj '/C=zz/ST=zz/L=zzzz/O=Zzzzz/OU=Zzzzz/CN=test'
5 | $newcert -key pki/test_3.key -out pki/test_3.crt -subj '/C=zz/ST=zz/L=zzzz/O=Zzzzz/OU=Zzzzz/CN=test'
6 | $newcert -key root_cert/localhost.ca.key -out root_cert/localhost.ca.crt -subj '/C=se/ST=ac/L=umea/O=ITS Umea University/OU=DIRG/CN=localhost.ca'
7 |
--------------------------------------------------------------------------------
/tests/disco_conf.py:
--------------------------------------------------------------------------------
1 | from pathutils import full_path
2 | from pathutils import xmlsec_path
3 |
4 | from saml2.extension.idpdisc import BINDING_DISCO
5 |
6 |
7 | BASE = "http://localhost:8088"
8 |
9 | CONFIG = {
10 | "entityid": f"{BASE}/disco.xml",
11 | "name": "Rolands Discoserver",
12 | "service": {
13 | "ds": {
14 | "endpoints": {
15 | "disco_service": [
16 | (f"{BASE}/disco", BINDING_DISCO),
17 | ]
18 | },
19 | },
20 | },
21 | "debug": 1,
22 | "xmlsec_binary": xmlsec_path,
23 | "metadata": [
24 | {
25 | "class": "saml2.mdstore.MetaDataFile",
26 | "metadata": [(full_path("servera.xml"),)],
27 | }
28 | ],
29 | }
30 |
--------------------------------------------------------------------------------
/tests/ecp_soap.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
11 |
14 |
15 |
18 |
19 | https://sp.example.org/entity
20 |
21 | ABCDEFGHI01234567
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/tests/edugain.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIICVzCCAcACCQDnwXdaJ4G3vTANBgkqhkiG9w0BAQUFADBwMQswCQYDVQQGEwJO
3 | TzESMBAGA1UECBMJVHJvbmRoZWltMRIwEAYDVQQHEwlUcm9uZGhlaW0xDjAMBgNV
4 | BAoTBUZlaWRlMQ4wDAYDVQQLEwVGZWlkZTEZMBcGA1UEAxMQZWR1Z2Fpbi5mZWlk
5 | ZS5ubzAeFw0wOTA4MzEwNjU2NDJaFw0zNzAxMTUwNjU2NDJaMHAxCzAJBgNVBAYT
6 | Ak5PMRIwEAYDVQQIEwlUcm9uZGhlaW0xEjAQBgNVBAcTCVRyb25kaGVpbTEOMAwG
7 | A1UEChMFRmVpZGUxDjAMBgNVBAsTBUZlaWRlMRkwFwYDVQQDExBlZHVnYWluLmZl
8 | aWRlLm5vMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC0/w1x1eIAbLYSVqCc
9 | OWiPC3lhWRNEBgRXFdCo/CFQt1DSAh6AYIw9nGWatqiKD0dtXhn+g4or36fF+l4t
10 | FlKwMjIRdB9EM3dp8ErhecauLTAXzJGI16YrfI5932UJr4NDJB/Wm1GKefyu5QIR
11 | w9NTEImw8CmUzzzmMd7TBM2epwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAJpIWdXn
12 | FL/j9Cm/Pdn6Yoxkf1mWy8L8WSwF8j9xfkvp53/GMd9IFkgkBbZo+F9CDH2la6H3
13 | vseA3ZJrXrxSn5RBhI5XJ85DGfdcMYJy3K42Y6mAUghVv1n+rf39w/cyuSRIW0IY
14 | XE3ANufnryezpDUffXpzdUltuTCpu2qfKEj2
15 | -----END CERTIFICATE-----
16 |
--------------------------------------------------------------------------------
/tests/empty_metadata_file.xml:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IdentityPython/pysaml2/0252ec96058c87c43f89e05d978e72b6ba2e6978/tests/empty_metadata_file.xml
--------------------------------------------------------------------------------
/tests/enc_tmpl.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
7 |
8 |
9 |
11 |
12 | my-rsa-key
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/tests/extra_lines.crt:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIICITCCAYoCAQEwDQYJKoZIhvcNAQELBQAwWDELMAkGA1UEBhMCenoxCzAJBgNV
3 | BAgMAnp6MQ0wCwYDVQQHDAR6enp6MQ4wDAYDVQQKDAVaenp6ejEOMAwGA1UECwwF
4 | Wnp6enoxDTALBgNVBAMMBHRlc3QwIBcNMTkwNDEyMTk1MDM0WhgPMzAxODA4MTMx
5 | OTUwMzRaMFgxCzAJBgNVBAYTAnp6MQswCQYDVQQIDAJ6ejENMAsGA1UEBwwEenp6
6 | ejEOMAwGA1UECgwFWnp6enoxDjAMBgNVBAsMBVp6enp6MQ0wCwYDVQQDDAR0ZXN0
7 | MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDjW0kJM+4baWKtvO24ZsGXNvNK
8 | KkwTMz7OW5Z6BRqhSOq2WA0c5NCpMk6rD8Z2OTFEolPojEjf8dVyd/Ds/hrjFKQv
9 | 8wQgbdXLN51YTIsgd6h+hBJO+vzhl0PT4aT7M0JKo5ALtS6qk4tsworW2BnwyvsG
10 | SAinwfeWt4t/b1J3kwIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAFtj7WArQQBugmh/
11 | KQjjlfTQ5A052QeXfgTyO9vv1S6MRIi7qgiaEv49cGXnJv/TWbySkMKObPMUApjg
12 | 6z8PqcxuShew5FCTkNvwhABFPiyu0fUj3e2FEPHfsBu76jz4ugtmhUqjqhzwFY9c
13 | tnWRkkl6J0AjM3LnHOSgjNIclDZG
14 | -----END CERTIFICATE-----
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/tests/get_metadata.sh:
--------------------------------------------------------------------------------
1 | curl -G -O http://md.incommon.org/InCommon/InCommon-metadata.xml
2 | curl -G -O http://metadata.aai.switch.ch/metadata.aaitest.xml
3 |
--------------------------------------------------------------------------------
/tests/idp_conf_ec.py:
--------------------------------------------------------------------------------
1 | from pathutils import full_path
2 |
3 | from saml2 import BINDING_HTTP_POST
4 | from saml2 import BINDING_HTTP_REDIRECT
5 | from saml2 import BINDING_SOAP
6 | from saml2.saml import NAME_FORMAT_URI
7 | from saml2.sigver import get_xmlsec_binary
8 |
9 |
10 | xmlsec_path = get_xmlsec_binary(["/opt/local/bin"])
11 |
12 | BASE = "http://localhost:8088"
13 |
14 | CONFIG = {
15 | "entityid": "urn:mace:example.com:saml:roland:idp",
16 | "name": "Rolands IdP",
17 | "service": {
18 | "idp": {
19 | "endpoints": {
20 | "single_sign_on_service": [(f"{BASE}/sso", BINDING_HTTP_REDIRECT)],
21 | "single_logout_service": [(f"{BASE}/slo", BINDING_SOAP), (f"{BASE}/slop", BINDING_HTTP_POST)],
22 | },
23 | "policy": {
24 | "default": {
25 | "lifetime": {"minutes": 15},
26 | "entity_categories": ["swamid", "edugain"],
27 | "name_form": NAME_FORMAT_URI,
28 | }
29 | },
30 | "subject_data": full_path("subject_data.db"),
31 | # "domain": "umu.se",
32 | # "name_qualifier": ""
33 | },
34 | },
35 | "debug": 1,
36 | "key_file": full_path("test.key"),
37 | "cert_file": full_path("test.pem"),
38 | "xmlsec_binary": xmlsec_path,
39 | "metadata": [
40 | {
41 | "class": "saml2.mdstore.MetaDataFile",
42 | "metadata": [(full_path("metadata_sp_1.xml"),), (full_path("vo_metadata.xml"),)],
43 | }
44 | ],
45 | "attribute_map_dir": full_path("attributemaps"),
46 | "organization": {
47 | "name": "Exempel AB",
48 | "display_name": [("Exempel AB", "se"), ("Example Co.", "en")],
49 | "url": "http://www.example.com/roland",
50 | },
51 | "contact_person": [
52 | {
53 | "given_name": "John",
54 | "sur_name": "Smith",
55 | "email_address": ["john.smith@example.com"],
56 | "contact_type": "technical",
57 | },
58 | ],
59 | }
60 |
--------------------------------------------------------------------------------
/tests/idp_conf_sp_no_encrypt.py:
--------------------------------------------------------------------------------
1 | from pathutils import full_path
2 | from pathutils import xmlsec_path
3 |
4 | from saml2 import BINDING_HTTP_POST
5 | from saml2 import BINDING_HTTP_REDIRECT
6 | from saml2 import BINDING_SOAP
7 | from saml2.saml import NAME_FORMAT_URI
8 | from saml2.saml import NAMEID_FORMAT_PERSISTENT
9 |
10 |
11 | BASE = "http://localhost:8088"
12 |
13 | CONFIG = {
14 | "entityid": "urn:mace:example.com:saml:roland:idp",
15 | "name": "Rolands IdP",
16 | "service": {
17 | "idp": {
18 | "endpoints": {
19 | "single_sign_on_service": [(f"{BASE}/sso", BINDING_HTTP_REDIRECT)],
20 | "single_logout_service": [(f"{BASE}/slo", BINDING_SOAP), (f"{BASE}/slop", BINDING_HTTP_POST)],
21 | },
22 | "policy": {
23 | "default": {
24 | "lifetime": {"minutes": 15},
25 | "attribute_restrictions": None, # means all I have
26 | "name_form": NAME_FORMAT_URI,
27 | },
28 | "urn:mace:example.com:saml:roland:sp": {
29 | "lifetime": {"minutes": 5},
30 | "nameid_format": NAMEID_FORMAT_PERSISTENT,
31 | # "attribute_restrictions":{
32 | # "givenName": None,
33 | # "surName": None,
34 | # }
35 | },
36 | },
37 | # "domain": "umu.se",
38 | # "name_qualifier": ""
39 | },
40 | },
41 | "debug": 1,
42 | "key_file": full_path("test.key"),
43 | "cert_file": full_path("test.pem"),
44 | "xmlsec_binary": xmlsec_path,
45 | "metadata": [
46 | {
47 | "class": "saml2.mdstore.MetaDataFile",
48 | "metadata": [(full_path("metadata_sp_1_no_encryption.xml"),), (full_path("vo_metadata.xml"),)],
49 | }
50 | ],
51 | "attribute_map_dir": full_path("attributemaps"),
52 | "organization": {
53 | "name": "Exempel AB",
54 | "display_name": [("Exempel AB", "se"), ("Example Co.", "en")],
55 | "url": "http://www.example.com/roland",
56 | },
57 | "contact_person": [
58 | {
59 | "given_name": "John",
60 | "sur_name": "Smith",
61 | "email_address": ["john.smith@example.com"],
62 | "contact_type": "technical",
63 | },
64 | ],
65 | }
66 |
--------------------------------------------------------------------------------
/tests/idp_slo_redirect.xml:
--------------------------------------------------------------------------------
1 |
2 | MIICsDCCAhmgAwIBAgIJAJrzqSSwmDY9MA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
3 | BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
4 | aWRnaXRzIFB0eSBMdGQwHhcNMDkxMDA2MTk0OTQxWhcNMDkxMTA1MTk0OTQxWjBF
5 | MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50
6 | ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB
7 | gQDJg2cms7MqjniT8Fi/XkNHZNPbNVQyMUMXE9tXOdqwYCA1cc8vQdzkihscQMXy
8 | 3iPw2cMggBu6gjMTOSOxECkuvX5ZCclKr8pXAJM5cY6gVOaVO2PdTZcvDBKGbiaN
9 | efiEw5hnoZomqZGp8wHNLAUkwtH9vjqqvxyS/vclc6k2ewIDAQABo4GnMIGkMB0G
10 | A1UdDgQWBBRePsKHKYJsiojE78ZWXccK9K4aJTB1BgNVHSMEbjBsgBRePsKHKYJs
11 | iojE78ZWXccK9K4aJaFJpEcwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUt
12 | U3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZIIJAJrzqSSw
13 | mDY9MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAJSrKOEzHO7TL5cy6
14 | h3qh+3+JAk8HbGBW+cbX6KBCAw/mzU8flK25vnWwXS3dv2FF3Aod0/S7AWNfKib5
15 | U/SA9nJaz/mWeF9S0farz9AQFc8/NSzAzaVq7YbM4F6f6N2FRl7GikdXRCed45j6
16 | mrPzGzk3ECbupFnqyREH3+ZPSdk=
17 | Exempel AB Exempel AB Example Co. http://www.example.com/roland John Smith john.smith@example.com
18 |
--------------------------------------------------------------------------------
/tests/idp_slo_redirect_conf.py:
--------------------------------------------------------------------------------
1 | from pathutils import full_path
2 |
3 | from saml2 import BINDING_HTTP_REDIRECT
4 | from saml2.saml import NAME_FORMAT_URI
5 | from saml2.saml import NAMEID_FORMAT_PERSISTENT
6 |
7 |
8 | CONFIG = {
9 | "entityid": "urn:mace:example.com:saml:roland:idp",
10 | "name": "Rolands IdP",
11 | "service": {
12 | "idp": {
13 | "endpoints": {
14 | "single_sign_on_service": [("http://localhost:8088/sso", BINDING_HTTP_REDIRECT)],
15 | "single_logout_service": [("http://localhost:8088/slo", BINDING_HTTP_REDIRECT)],
16 | },
17 | "policy": {
18 | "default": {
19 | "lifetime": {"minutes": 15},
20 | "attribute_restrictions": None, # means all I have
21 | "name_form": NAME_FORMAT_URI,
22 | },
23 | "urn:mace:example.com:saml:roland:sp": {
24 | "lifetime": {"minutes": 5},
25 | "nameid_format": NAMEID_FORMAT_PERSISTENT,
26 | },
27 | },
28 | "subject_data": full_path("subject_data.db"),
29 | }
30 | },
31 | "debug": 1,
32 | "key_file": full_path("test.key"),
33 | "cert_file": full_path("test.pem"),
34 | "xmlsec_binary": None,
35 | "metadata": [
36 | {
37 | "class": "saml2.mdstore.MetaDataFile",
38 | "metadata": [(full_path("sp_slo_redirect.xml"),)],
39 | }
40 | ],
41 | "attribute_map_dir": full_path("attributemaps"),
42 | "organization": {
43 | "name": "Exempel AB",
44 | "display_name": [("Exempel AB", "se"), ("Example Co.", "en")],
45 | "url": "http://www.example.com/roland",
46 | },
47 | "contact_person": [
48 | {
49 | "given_name": "John",
50 | "sur_name": "Smith",
51 | "email_address": ["john.smith@example.com"],
52 | "contact_type": "technical",
53 | },
54 | ],
55 | }
56 |
--------------------------------------------------------------------------------
/tests/idp_soap.xml:
--------------------------------------------------------------------------------
1 |
2 | MIICsDCCAhmgAwIBAgIJAJrzqSSwmDY9MA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
3 | BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
4 | aWRnaXRzIFB0eSBMdGQwHhcNMDkxMDA2MTk0OTQxWhcNMDkxMTA1MTk0OTQxWjBF
5 | MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50
6 | ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB
7 | gQDJg2cms7MqjniT8Fi/XkNHZNPbNVQyMUMXE9tXOdqwYCA1cc8vQdzkihscQMXy
8 | 3iPw2cMggBu6gjMTOSOxECkuvX5ZCclKr8pXAJM5cY6gVOaVO2PdTZcvDBKGbiaN
9 | efiEw5hnoZomqZGp8wHNLAUkwtH9vjqqvxyS/vclc6k2ewIDAQABo4GnMIGkMB0G
10 | A1UdDgQWBBRePsKHKYJsiojE78ZWXccK9K4aJTB1BgNVHSMEbjBsgBRePsKHKYJs
11 | iojE78ZWXccK9K4aJaFJpEcwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUt
12 | U3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZIIJAJrzqSSw
13 | mDY9MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAJSrKOEzHO7TL5cy6
14 | h3qh+3+JAk8HbGBW+cbX6KBCAw/mzU8flK25vnWwXS3dv2FF3Aod0/S7AWNfKib5
15 | U/SA9nJaz/mWeF9S0farz9AQFc8/NSzAzaVq7YbM4F6f6N2FRl7GikdXRCed45j6
16 | mrPzGzk3ECbupFnqyREH3+ZPSdk=
17 | Exempel AB Exempel AB Example Co. http://www.example.com/roland John Smith john.smith@example.com
18 |
--------------------------------------------------------------------------------
/tests/idp_soap_conf.py:
--------------------------------------------------------------------------------
1 | from pathutils import full_path
2 |
3 | from saml2 import BINDING_HTTP_REDIRECT
4 | from saml2 import BINDING_SOAP
5 | from saml2.saml import NAME_FORMAT_URI
6 | from saml2.saml import NAMEID_FORMAT_PERSISTENT
7 |
8 |
9 | CONFIG = {
10 | "entityid": "urn:mace:example.com:saml:roland:idp",
11 | "name": "Rolands IdP",
12 | "service": {
13 | "idp": {
14 | "endpoints": {
15 | "single_sign_on_service": [("http://localhost:8088/sso", BINDING_HTTP_REDIRECT)],
16 | "single_logout_service": [("http://localhost:8088/slo", BINDING_SOAP)],
17 | },
18 | "policy": {
19 | "default": {
20 | "lifetime": {"minutes": 15},
21 | "attribute_restrictions": None, # means all I have
22 | "name_form": NAME_FORMAT_URI,
23 | },
24 | "urn:mace:example.com:saml:roland:sp": {
25 | "lifetime": {"minutes": 5},
26 | "nameid_format": NAMEID_FORMAT_PERSISTENT,
27 | # "attribute_restrictions":{
28 | # "givenName": None,
29 | # "surName": None,
30 | # }
31 | },
32 | },
33 | "subject_data": full_path("subject_data.db"),
34 | }
35 | },
36 | "debug": 1,
37 | "key_file": full_path("test.key"),
38 | "cert_file": full_path("test.pem"),
39 | # "xmlsec_binary" : None,
40 | "metadata": [
41 | {
42 | "class": "saml2.mdstore.MetaDataFile",
43 | "metadata": [(full_path("metadata.xml"),), (full_path("vo_metadata.xml"),)],
44 | }
45 | ],
46 | "attribute_map_dir": full_path("attributemaps"),
47 | "organization": {
48 | "name": "Exempel AB",
49 | "display_name": [("Exempel AB", "se"), ("Example Co.", "en")],
50 | "url": "http://www.example.com/roland",
51 | },
52 | "contact_person": [
53 | {
54 | "given_name": "John",
55 | "sur_name": "Smith",
56 | "email_address": ["john.smith@example.com"],
57 | "contact_type": "technical",
58 | },
59 | ],
60 | }
61 |
--------------------------------------------------------------------------------
/tests/idp_sp_conf.py:
--------------------------------------------------------------------------------
1 | __author__ = "rolandh"
2 |
3 | from saml2 import BINDING_HTTP_POST
4 | from saml2 import BINDING_HTTP_REDIRECT
5 | from saml2 import BINDING_SOAP
6 | from saml2.saml import NAME_FORMAT_URI
7 | from saml2.saml import NAMEID_FORMAT_PERSISTENT
8 |
9 |
10 | BASE = "http://localhost:8088/"
11 |
12 | from pathutils import full_path
13 |
14 |
15 | CONFIG = {
16 | "entityid": "urn:mace:example.com:saml:roland:idp",
17 | "name": "Rolands IdP",
18 | "service": {
19 | "idp": {
20 | "endpoints": {
21 | "single_sign_on_service": [(f"{BASE}sso", BINDING_HTTP_REDIRECT)],
22 | "single_logout_service": [(f"{BASE}slo", BINDING_SOAP), (f"{BASE}slop", BINDING_HTTP_POST)],
23 | },
24 | "policy": {
25 | "default": {
26 | "lifetime": {"minutes": 15},
27 | "attribute_restrictions": None, # means all I have
28 | "name_form": NAME_FORMAT_URI,
29 | },
30 | "urn:mace:example.com:saml:roland:sp": {
31 | "lifetime": {"minutes": 5},
32 | "nameid_format": NAMEID_FORMAT_PERSISTENT,
33 | # "attribute_restrictions":{
34 | # "givenName": None,
35 | # "surName": None,
36 | # }
37 | },
38 | },
39 | "subject_data": full_path("subject_data.db"),
40 | },
41 | "sp": {
42 | "endpoints": {
43 | "assertion_consumer_service": [(BASE, BINDING_HTTP_REDIRECT)],
44 | },
45 | "required_attributes": ["surName", "givenName", "mail"],
46 | "optional_attributes": ["title"],
47 | },
48 | },
49 | "debug": 1,
50 | "key_file": full_path("test.key"),
51 | "cert_file": full_path("test.pem"),
52 | "xmlsec_binary": None,
53 | "metadata": [
54 | {
55 | "class": "saml2.mdstore.MetaDataFile",
56 | "metadata": [(full_path("metadata.xml"),), (full_path("vo_metadata.xml"),)],
57 | }
58 | ],
59 | "attribute_map_dir": full_path("attributemaps"),
60 | "organization": {
61 | "name": "Exempel AB",
62 | "display_name": [("Exempel AB", "se"), ("Example Co.", "en")],
63 | "url": "http://www.example.com/roland",
64 | },
65 | "contact_person": [
66 | {
67 | "given_name": "John",
68 | "sur_name": "Smith",
69 | "email_address": ["john.smith@example.com"],
70 | "contact_type": "technical",
71 | },
72 | ],
73 | }
74 |
--------------------------------------------------------------------------------
/tests/idp_uiinfo.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | descriptor-example.org
6 | descriptor-example[^0-9]*\.org
7 |
8 |
9 |
10 | idpssodescriptor-example.org
11 | foo bar http://example.com/logo.jpg http://example.com/saml2/info.html Example Co. Exempel bolag http://example.com/saml2/privacyStatement.html
12 |
13 |
14 | MIICsDCCAhmgAwIBAgIJAJrzqSSwmDY9MA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMDkxMDA2MTk0OTQxWhcNMDkxMTA1MTk0OTQxWjBFMQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDJg2cms7MqjniT8Fi/XkNHZNPbNVQyMUMXE9tXOdqwYCA1cc8vQdzkihscQMXy3iPw2cMggBu6gjMTOSOxECkuvX5ZCclKr8pXAJM5cY6gVOaVO2PdTZcvDBKGbiaNefiEw5hnoZomqZGp8wHNLAUkwtH9vjqqvxyS/vclc6k2ewIDAQABo4GnMIGkMB0GA1UdDgQWBBRePsKHKYJsiojE78ZWXccK9K4aJTB1BgNVHSMEbjBsgBRePsKHKYJsiojE78ZWXccK9K4aJaFJpEcwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZIIJAJrzqSSwmDY9MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAJSrKOEzHO7TL5cy6h3qh+3+JAk8HbGBW+cbX6KBCAw/mzU8flK25vnWwXS3dv2FF3Aod0/S7AWNfKib5U/SA9nJaz/mWeF9S0farz9AQFc8/NSzAzaVq7YbM4F6f6N2FRl7GikdXRCed45j6mrPzGzk3ECbupFnqyREH3+ZPSdk=
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/tests/inc-md-cert.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIIDgTCCAmmgAwIBAgIJAJRJzvdpkmNaMA0GCSqGSIb3DQEBCwUAMFcxCzAJBgNV
3 | BAYTAlVTMRUwEwYDVQQKDAxJbkNvbW1vbiBMTEMxMTAvBgNVBAMMKEluQ29tbW9u
4 | IEZlZGVyYXRpb24gTWV0YWRhdGEgU2lnbmluZyBLZXkwHhcNMTMxMjE2MTkzNDU1
5 | WhcNMzcxMjE4MTkzNDU1WjBXMQswCQYDVQQGEwJVUzEVMBMGA1UECgwMSW5Db21t
6 | b24gTExDMTEwLwYDVQQDDChJbkNvbW1vbiBGZWRlcmF0aW9uIE1ldGFkYXRhIFNp
7 | Z25pbmcgS2V5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0Chdkrn+
8 | dG5Zj5L3UIw+xeWgNzm8ajw7/FyqRQ1SjD4Lfg2WCdlfjOrYGNnVZMCTfItoXTSp
9 | g4rXxHQsykeNiYRu2+02uMS+1pnBqWjzdPJE0od+q8EbdvE6ShimjyNn0yQfGyQK
10 | CNdYuc+75MIHsaIOAEtDZUST9Sd4oeU1zRjV2sGvUd+JFHveUAhRc0b+JEZfIEuq
11 | /LIU9qxm/+gFaawlmojZPyOWZ1JlswbrrJYYyn10qgnJvjh9gZWXKjmPxqvHKJcA
12 | TPhAh2gWGabWTXBJCckMe1hrHCl/vbDLCmz0/oYuoaSDzP6zE9YSA/xCplaHA0mo
13 | C1Vs2H5MOQGlewIDAQABo1AwTjAdBgNVHQ4EFgQU5ij9YLU5zQ6K75kPgVpyQ2N/
14 | lPswHwYDVR0jBBgwFoAU5ij9YLU5zQ6K75kPgVpyQ2N/lPswDAYDVR0TBAUwAwEB
15 | /zANBgkqhkiG9w0BAQsFAAOCAQEAaQkEx9xvaLUt0PNLvHMtxXQPedCPw5xQBd2V
16 | WOsWPYspRAOSNbU1VloY+xUkUKorYTogKUY1q+uh2gDIEazW0uZZaQvWPp8xdxWq
17 | Dh96n5US06lszEc+Lj3dqdxWkXRRqEbjhBFh/utXaeyeSOtaX65GwD5svDHnJBcl
18 | AGkzeRIXqxmYG+I2zMm/JYGzEnbwToyC7yF6Q8cQxOr37hEpqz+WN/x3qM2qyBLE
19 | CQFjmlJrvRLkSL15PCZiu+xFNFd/zx6btDun5DBlfDS9DG+SHCNH6Nq+NfP+ZQ8C
20 | GzP/3TaZPzMlKPDCjp0XOQfyQqFIXdwjPFTWjEusDBlm4qJAlQ==
21 | -----END CERTIFICATE-----
22 |
--------------------------------------------------------------------------------
/tests/invalid_metadata_file.xml:
--------------------------------------------------------------------------------
1 | this content is invalid for a metadata file
2 |
--------------------------------------------------------------------------------
/tests/kalmar2.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIIC+zCCAmSgAwIBAgIJALIv7VqXanQYMA0GCSqGSIb3DQEBBQUAMF0xCzAJBgNV
3 | BAYTAk5PMRIwEAYDVQQIEwlUcm9uZGhlaW0xEjAQBgNVBAcTCVRyb25kaGVpbTEQ
4 | MA4GA1UEChMHVU5JTkVUVDEUMBIGA1UEAxMLa2FsbWFyMi5vcmcwHhcNMDkxMDI2
5 | MDY1OTQyWhcNMTkxMDI2MDY1OTQyWjBdMQswCQYDVQQGEwJOTzESMBAGA1UECBMJ
6 | VHJvbmRoZWltMRIwEAYDVQQHEwlUcm9uZGhlaW0xEDAOBgNVBAoTB1VOSU5FVFQx
7 | FDASBgNVBAMTC2thbG1hcjIub3JnMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB
8 | gQCi7bEwud2nKm9FSojyIFGXN1p2ZGpUOKSFxhmeicCujHQr1VYUhyXBDHONwPDt
9 | K9PdHFZ4lLgLKcAIqUcOoAWr65m/MJJVkX0P7TUTZ6OS4mDAo1NsZexZxTBof9hC
10 | wnFz9dbqEThqZw2UtyEDnhW6kCb6SBd+2Yjvd+YDZg8lfwIDAQABo4HCMIG/MB0G
11 | A1UdDgQWBBS2Sw/w4drjYrTiAHeWXyN2W1j1iDCBjwYDVR0jBIGHMIGEgBS2Sw/w
12 | 4drjYrTiAHeWXyN2W1j1iKFhpF8wXTELMAkGA1UEBhMCTk8xEjAQBgNVBAgTCVRy
13 | b25kaGVpbTESMBAGA1UEBxMJVHJvbmRoZWltMRAwDgYDVQQKEwdVTklORVRUMRQw
14 | EgYDVQQDEwtrYWxtYXIyLm9yZ4IJALIv7VqXanQYMAwGA1UdEwQFMAMBAf8wDQYJ
15 | KoZIhvcNAQEFBQADgYEALx5V6xKtPr7urC/QOWiHxUChQO+SJsbnlwIquwaEGgUf
16 | 0WrGidPu04zdv+VpKtR+/KZbIDuSWx0/AkbexiE9ZUzJ2GvdVSxr/uON9CtQIQTp
17 | 5WjZD0KaieaoIMy/w5shc+trjkV550g/MWFFqAjproXwHRrEQoAxWL0smtR1R/I=
18 | -----END CERTIFICATE-----
19 |
--------------------------------------------------------------------------------
/tests/keys/mycert.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIIC8jCCAlugAwIBAgIJAJHg2V5J31I8MA0GCSqGSIb3DQEBBQUAMFoxCzAJBgNV
3 | BAYTAlNFMQ0wCwYDVQQHEwRVbWVhMRgwFgYDVQQKEw9VbWVhIFVuaXZlcnNpdHkx
4 | EDAOBgNVBAsTB0lUIFVuaXQxEDAOBgNVBAMTB1Rlc3QgU1AwHhcNMDkxMDI2MTMz
5 | MTE1WhcNMTAxMDI2MTMzMTE1WjBaMQswCQYDVQQGEwJTRTENMAsGA1UEBxMEVW1l
6 | YTEYMBYGA1UEChMPVW1lYSBVbml2ZXJzaXR5MRAwDgYDVQQLEwdJVCBVbml0MRAw
7 | DgYDVQQDEwdUZXN0IFNQMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDkJWP7
8 | bwOxtH+E15VTaulNzVQ/0cSbM5G7abqeqSNSs0l0veHr6/ROgW96ZeQ57fzVy2MC
9 | FiQRw2fzBs0n7leEmDJyVVtBTavYlhAVXDNa3stgvh43qCfLx+clUlOvtnsoMiiR
10 | mo7qf0BoPKTj7c0uLKpDpEbAHQT4OF1HRYVxMwIDAQABo4G/MIG8MB0GA1UdDgQW
11 | BBQ7RgbMJFDGRBu9o3tDQDuSoBy7JjCBjAYDVR0jBIGEMIGBgBQ7RgbMJFDGRBu9
12 | o3tDQDuSoBy7JqFepFwwWjELMAkGA1UEBhMCU0UxDTALBgNVBAcTBFVtZWExGDAW
13 | BgNVBAoTD1VtZWEgVW5pdmVyc2l0eTEQMA4GA1UECxMHSVQgVW5pdDEQMA4GA1UE
14 | AxMHVGVzdCBTUIIJAJHg2V5J31I8MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEF
15 | BQADgYEAMuRwwXRnsiyWzmRikpwinnhTmbooKm5TINPE7A7gSQ710RxioQePPhZO
16 | zkM27NnHTrCe2rBVg0EGz7QTd1JIwLPvgoj4VTi/fSha/tXrYUaqc9AqU1kWI4WN
17 | +vffBGQ09mo+6CffuFTZYeOhzP/2stAPwCTU4kxEoiy0KpZMANI=
18 | -----END CERTIFICATE-----
19 |
--------------------------------------------------------------------------------
/tests/keys/mykey.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN RSA PRIVATE KEY-----
2 | MIICXAIBAAKBgQDkJWP7bwOxtH+E15VTaulNzVQ/0cSbM5G7abqeqSNSs0l0veHr
3 | 6/ROgW96ZeQ57fzVy2MCFiQRw2fzBs0n7leEmDJyVVtBTavYlhAVXDNa3stgvh43
4 | qCfLx+clUlOvtnsoMiiRmo7qf0BoPKTj7c0uLKpDpEbAHQT4OF1HRYVxMwIDAQAB
5 | AoGAbx9rKH91DCw/ZEPhHsVXJ6cYHxGcMoAWvnMMC9WUN+bNo4gNL205DLfsxXA1
6 | jqXFXZj3+38vSFumGPA6IvXrN+Wyp3+Lz3QGc4K5OdHeBtYlxa6EsrxPgvuxYDUB
7 | vx3xdWPMjy06G/ML+pR9XHnRaPNubXQX3UxGBuLjwNXVmyECQQD2/D84tYoCGWoq
8 | 5FhUBxFUy2nnOLKYC/GGxBTX62iLfMQ3fbQcdg2pJsB5rrniyZf7UL+9FOsAO9k1
9 | 8DO7G12DAkEA7Hkdg1KEw4ZfjnnjEa+KqpyLTLRQ91uTVW6kzR+4zY719iUJ/PXE
10 | PxJqm1ot7mJd1LW+bWtjLpxs7jYH19V+kQJBAIEpn2JnxdmdMuFlcy/WVmDy09pg
11 | 0z0imdexeXkFmjHAONkQOv3bWv+HzYaVMo8AgCOksfEPHGqN4eUMTfFeuUMCQF+5
12 | E1JSd/2yCkJhYqKJHae8oMLXByNqRXTCyiFioutK4JPYIHfugJdLfC4QziD+Xp85
13 | RrGCU+7NUWcIJhqfiJECQAIgUAzfzhdj5AyICaFPaOQ+N8FVMLcTyqeTXP0sIlFk
14 | JStVibemTRCbxdXXM7OVipz1oW3PBVEO3t/VyjiaGGg=
15 | -----END RSA PRIVATE KEY-----
16 |
--------------------------------------------------------------------------------
/tests/localhost.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | from saml2.saml import NAME_FORMAT_URI
3 |
4 |
5 | __author__ = "rolandh"
6 |
7 | import json
8 |
9 |
10 | BASE = "http://localhost:8088"
11 |
12 | metadata = open("idp_test/idp.xml").read()
13 |
14 | info = {
15 | "entity_id": f"{BASE}/idp.xml",
16 | "interaction": [
17 | {
18 | "matches": {"url": f"{BASE}/login", "title": "IDP test login"},
19 | "page-type": "login",
20 | "control": {"type": "form", "set": {"login": "roland", "password": "dianakra"}},
21 | },
22 | {
23 | "matches": {"url": f"{BASE}/sso/redirect", "title": "SAML 2.0 POST"},
24 | "page-type": "other",
25 | "control": {"index": 0, "type": "form", "set": {}},
26 | },
27 | {
28 | "matches": {"url": f"{BASE}/sso/post", "title": "SAML 2.0 POST"},
29 | "page-type": "other",
30 | "control": {"index": 0, "type": "form", "set": {}},
31 | },
32 | {
33 | "matches": {"url": f"{BASE}/slo/post", "title": "SAML 2.0 POST"},
34 | "page-type": "other",
35 | "control": {"index": 0, "type": "form", "set": {}},
36 | },
37 | ],
38 | "metadata": metadata,
39 | "name_format": NAME_FORMAT_URI,
40 | }
41 |
42 | print(json.dumps(info))
43 |
--------------------------------------------------------------------------------
/tests/malformed.crt:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIICITCCAYoCAQEwDQYJKoZIhvcNAQELBQAwWDELMAkGA1UEBhMCenoxCzAJBgNV
3 | BAgMAnp6MQ0wCwYDVQQHDAR6enp6MQ4wDAYDVQQKDAVaenp6ejEOMAwGA1UECwwF
4 | Wnp6enoxDTALBgNVBAMMBHRlc3QwIBcNMTkwNDEyMTk1MDM0WhgPMzAxODA4MTMx
5 | OTUwMzRaMFgxCzAJBgNVBAYTAnp6MQswCQYDVQQIDAJ6ejENMAsGA1UEBwwEenp6
6 | MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDjW0kJM+4baWKtvO24ZsGXNvNK
7 | KkwTMz7OW5Z6BRqhSOq2WA0c5NCpMk6rD8Z2OTFEolPojEjf8dVyd/Ds/hrjFKQv
8 | 8wQgbdXLN51YTIsgd6h+hBJO+vzhl0PT4aT7M0JKo5ALtS6qk4tsworW2BnwyvsG
9 | SAinwfeWt4t/b1J3kwIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAFtj7WArQQBugmh/
10 | KQjjlfTQ5A052QeXfgTyO9vv1S6MRIi7qgiaEv49cGXnJv/TWbySkMKObPMUApjg
11 | 6z8PqcxuShew5FCTkNvwhABFPiyu0fUj3e2FEPHfsBu76jz4ugtmhUqjqhzwFY9c
12 | tnWRkkl6J0AjM3LnHOSgjNIclDZG
13 |
--------------------------------------------------------------------------------
/tests/metadata/idp_uiinfo.xml:
--------------------------------------------------------------------------------
1 |
2 | example.org foo bar http://example.com/logo.jpg http://example.com/saml2/info.html Example Co. Exempel bolag http://example.com/saml2/privacyStatement.html MIICsDCCAhmgAwIBAgIJAJrzqSSwmDY9MA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
3 | BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
4 | aWRnaXRzIFB0eSBMdGQwHhcNMDkxMDA2MTk0OTQxWhcNMDkxMTA1MTk0OTQxWjBF
5 | MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50
6 | ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB
7 | gQDJg2cms7MqjniT8Fi/XkNHZNPbNVQyMUMXE9tXOdqwYCA1cc8vQdzkihscQMXy
8 | 3iPw2cMggBu6gjMTOSOxECkuvX5ZCclKr8pXAJM5cY6gVOaVO2PdTZcvDBKGbiaN
9 | efiEw5hnoZomqZGp8wHNLAUkwtH9vjqqvxyS/vclc6k2ewIDAQABo4GnMIGkMB0G
10 | A1UdDgQWBBRePsKHKYJsiojE78ZWXccK9K4aJTB1BgNVHSMEbjBsgBRePsKHKYJs
11 | iojE78ZWXccK9K4aJaFJpEcwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUt
12 | U3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZIIJAJrzqSSw
13 | mDY9MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAJSrKOEzHO7TL5cy6
14 | h3qh+3+JAk8HbGBW+cbX6KBCAw/mzU8flK25vnWwXS3dv2FF3Aod0/S7AWNfKib5
15 | U/SA9nJaz/mWeF9S0farz9AQFc8/NSzAzaVq7YbM4F6f6N2FRl7GikdXRCed45j6
16 | mrPzGzk3ECbupFnqyREH3+ZPSdk=
17 |
18 |
--------------------------------------------------------------------------------
/tests/myentitycategory.py:
--------------------------------------------------------------------------------
1 | CUSTOM_R_AND_S = [
2 | "eduPersonTargetedID",
3 | "eduPersonPrincipalName",
4 | "mail",
5 | "displayName",
6 | "givenName",
7 | "sn",
8 | "eduPersonScopedAffiliation",
9 | "eduPersonUniqueId",
10 | ]
11 |
12 | RESEARCH_AND_SCHOLARSHIP = "http://refeds.org/category/research-and-scholarship"
13 |
14 | RELEASE = {
15 | "": ["eduPersonTargetedID"],
16 | RESEARCH_AND_SCHOLARSHIP: CUSTOM_R_AND_S,
17 | }
18 |
--------------------------------------------------------------------------------
/tests/okta_assertion:
--------------------------------------------------------------------------------
1 |
2 | http://www.okta.com/Issuer userName audience urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport John Doe
--------------------------------------------------------------------------------
/tests/openssl.cnf:
--------------------------------------------------------------------------------
1 | [req]
2 | req_extensions = v3_req
3 | distinguished_name = req_distinguished_name
4 |
5 | [req_distinguished_name]
6 | [v3_req]
7 |
--------------------------------------------------------------------------------
/tests/pathutils.py:
--------------------------------------------------------------------------------
1 | import os.path
2 |
3 |
4 | BASEDIR = os.path.abspath(os.path.dirname(__file__))
5 |
6 |
7 | def full_path(local_file):
8 | return os.path.join(BASEDIR, local_file)
9 |
10 |
11 | def dotname(module):
12 | if not BASEDIR.endswith("tests"):
13 | return f"tests.{module}"
14 | else:
15 | return module
16 |
17 |
18 | try:
19 | from saml2.sigver import get_xmlsec_binary
20 | except ImportError:
21 | get_xmlsec_binary = None
22 |
23 | if get_xmlsec_binary:
24 | xmlsec_path = get_xmlsec_binary(["/opt/local/bin"])
25 | else:
26 | xmlsec_path = "/usr/bin/xmlsec1"
27 |
--------------------------------------------------------------------------------
/tests/pki/cert.crt:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIICMjCCAZsCAQEwDQYJKoZIhvcNAQELBQAwYjELMAkGA1UEBhMCcXcxDzANBgNV
3 | BAgTBnF3ZXJ0eTEPMA0GA1UEBxMGcXdlcnR5MQ8wDQYDVQQKEwZxd2VydHkxDzAN
4 | BgNVBAsTBnF3ZXJ0eTEPMA0GA1UEAxMGcXdlcnR5MB4XDTE0MDIwNDA4NTY0N1oX
5 | DTE0MDIwNTA4NTY0N1owYTELMAkGA1UEBhMCYXMxDzANBgNVBAgTBmFzZGZnaDEP
6 | MA0GA1UEBxMGYXNkZmdoMQ8wDQYDVQQKEwZhc2RmZ2gxDjAMBgNVBAsTBWFzZGZn
7 | MQ8wDQYDVQQDEwZhc2RmZ2gwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKUZ
8 | NSY8xkonIyXOMPwK2aRpkAxnM9/0l15aJ0DCrufCzMWZF+oqne1RZXNZbH4VOqwh
9 | CSq9iXk5ONe0RqIlxvvVvFO+jx6/4vulhEnhOXyrYTJ8AJhBoFRRkJ8VMgN1Hf0C
10 | wIhVqVQpe6UIOkg17PCXoYjicLTeV+CQrrKEajghAgMBAAEwDQYJKoZIhvcNAQEL
11 | BQADgYEAJTspJ8fDcTXlAM0Rgr73EVyhDpIN1MC5hUFay0YrLenOuXaNH9rFzg8j
12 | AdsB5+N6KJg7JB4+oqbucgz9/poqrKUG9amg/uv87vjMR7O7xtlKXt1iSLOdu/uU
13 | cYhtRVSlwRaVfhd6fiYylJag8ujraUmPbqmvM8y23QL5l+O3Nng=
14 | -----END CERTIFICATE-----
15 |
--------------------------------------------------------------------------------
/tests/pki/test_3.crt:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIICITCCAYoCAQEwDQYJKoZIhvcNAQELBQAwWDELMAkGA1UEBhMCenoxCzAJBgNV
3 | BAgMAnp6MQ0wCwYDVQQHDAR6enp6MQ4wDAYDVQQKDAVaenp6ejEOMAwGA1UECwwF
4 | Wnp6enoxDTALBgNVBAMMBHRlc3QwIBcNMTkwNDEyMTk1MDM0WhgPMzAxODA4MTMx
5 | OTUwMzRaMFgxCzAJBgNVBAYTAnp6MQswCQYDVQQIDAJ6ejENMAsGA1UEBwwEenp6
6 | ejEOMAwGA1UECgwFWnp6enoxDjAMBgNVBAsMBVp6enp6MQ0wCwYDVQQDDAR0ZXN0
7 | MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCvGSW8evdwkkQciY8kJ+x0h3/J
8 | +GCRmxsrOGgtNhfXSAL1DPVJsbFQngYpCf4iVoxZjhZOV7klQ+JsMg3mxi0XcYLk
9 | k9akj+wp5trueLslYE4J4CByBAwZi+5fhWRHYdi4yQo6z87d3W0B9B0SHx7QjWtf
10 | PJLZMSJXYYQJR5zUlQIDAQABMA0GCSqGSIb3DQEBCwUAA4GBABIjpaJbBYftDKCV
11 | k53bv6ArWJgBkAxq0GUdwf+X7MELfctFRXpksUyqtGff7Whw4EDkTGSCHmdmLXXG
12 | yGmc5sdEsAWGs68B7csJzznOPBlkPe5aIJFtlGRC0y1PHcXFM9H3Aof4rpAuJuby
13 | 7B8dlOXPEWoz+gLObD5bxWP8Qf+p
14 | -----END CERTIFICATE-----
15 |
--------------------------------------------------------------------------------
/tests/pki/test_3.key:
--------------------------------------------------------------------------------
1 | -----BEGIN RSA PRIVATE KEY-----
2 | MIICWwIBAAKBgQCvGSW8evdwkkQciY8kJ+x0h3/J+GCRmxsrOGgtNhfXSAL1DPVJ
3 | sbFQngYpCf4iVoxZjhZOV7klQ+JsMg3mxi0XcYLkk9akj+wp5trueLslYE4J4CBy
4 | BAwZi+5fhWRHYdi4yQo6z87d3W0B9B0SHx7QjWtfPJLZMSJXYYQJR5zUlQIDAQAB
5 | AoGACRxT3FTBnzfdF2cI7aauJPoP6iBkVe8uILeUpBWWc/spPDrqYGVAhqNSSrxc
6 | XskGEHrWKkliNtArbdnE42cYXXPbwxHOwzoXxaHlLYIORDH0b90ukv1Pp6DhzQaW
7 | guFb3yrxRO7tILhfA9V+nvYU4EcPLwRxRzLUipT7eNfIhYECQQDcTMWr3bvgLpD2
8 | KD8Pn6iihskBiAsvvz1uVOjfSS1bKGzsyYjtKdq/bO0AM0e8uUdSWHXH2yeEkG/f
9 | IMz9CXQlAkEAy3kqpl7uU+0Qe19mC+Icx/DbjJUV6Kv7dLozze3yjBzQFBMPQwi8
10 | b4zA+4lcuX5u9b8f1kpqC8hNMcE9jGM7sQJAClwp550z2qUV+B2IaamueoYwKbxG
11 | Gma58thXYzjDw0exZ6lKoSyYtuvecWX3964W5o52a4Go6BkKycl3Gmc5aQJAAsT3
12 | a6RHIiVL4CIARZEiSyZgFp3A2pXcqk4OfnMKphWOT3ei8Yqg5fPIfKP1+yBZakbb
13 | rBL/NoHXayHyMIL5QQJATnVslSzO10RzxBI+k1F+Mr1VRj1KnP4rjYP1gHQ9KqVP
14 | CveP1Cqnu7+tZ9HezUMZSlFZ60A+D6uB8AGB/Z8EHg==
15 | -----END RSA PRIVATE KEY-----
16 |
--------------------------------------------------------------------------------
/tests/private_key.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN RSA PRIVATE KEY-----
2 | MIICXgIBAAKBgQCoOodEKMwSYOhf+HnERrrfORO2p4EY3dnk/tPafbY1S6JOOlIp
3 | 3Yp3GyHq7xr1gbAUuwoS0QXcPURtHMu97RjiLFTGDTnXaQLzDbRyXhMwbbEQWmX1
4 | KYvUxMZ54k3tWr5abtWLkqK4PPEWVdUh7Q1yYOnVRd958Da/aVTeI97YKQIDAQAB
5 | AoGAKburRstgJMcJnpVDj9+ECFBehZd0PTj8DwTwhWleREZpE0MAkwY0jWubtG5w
6 | n6r2hY4I57YW4BYK3Y6hNJKW803Hc3Qv75SxlTkGEg4s2H9j9xS27jpTIoEWPEGB
7 | SCFUHDdcM2BGFg8C+CjLLPXoRZB3ozhPTF59EJG5kslH5QECQQDXRw8NOf7ewPf+
8 | 4LDWhFC/VWA1iv5qSuWiUEQG0WRHls0jvP7aUAEVVjcUJIqSnDt5mbe8Y7wdOaBx
9 | jC2Lp22xAkEAyA0Y2heoWNwSwxFuo2IMviDifUDzxF2MJrOZWGRKzh6tpOexlwNJ
10 | czunHBk+Xu0c8T7c0kj/NI964ZRnnR5X+QJBAI94pxpchXS1TFIMMy60PhBV+OmW
11 | OZpjUglLzxpwIJHpecwQoBSk7yPmMoz1EXlGMAkJnj8qhOtBRaGHF6+UJPECQQCY
12 | VnDgUzedQyj+Zp3ryNSPTBds7jMzC1GYfxd82hKk13O/qbfwmU4rc/eTTB3Ux5dM
13 | lmQtteyxJ7mZYhts6BhxAkEArx1IwLUA6hW6tPVmrK/A7TFl+bPacg3BH8HsLitB
14 | H16b9Fnyk0OSJc9+vxBRgIqTSq81X44aQHr0ZINCA6oPNQ==
15 | -----END RSA PRIVATE KEY-----
16 |
--------------------------------------------------------------------------------
/tests/pubkey.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIIDTDCCAjSgAwIBAgIJAKPpMz4LyS0VMA0GCSqGSIb3DQEBBQUAMCIxIDAeBgNV
3 | BAMTF2tleWJ1Y2tldC5hcHAubm9yZHUubmV0MB4XDTEyMDUzMTEyMjIyNFoXDTIy
4 | MDUyOTEyMjIyNFowIjEgMB4GA1UEAxMXa2V5YnVja2V0LmFwcC5ub3JkdS5uZXQw
5 | ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCofu5ht94gNZygBb3ZkwDG
6 | gjt5FTeZaCIE7+QshE72s5r79qlmLyhQ0KduecPdGt0vDs/aNIGIqj5yKXsWY8C3
7 | 7NpVvh7Ht1rBMhciF/KP2U2I/DeQSo5/Mk45uhhM8TdT5yNR0tZdjHMa1XIIilto
8 | yqPYYEbiUD299oCMbNd+qunK7+x4ltujVVtKq3DbOsCkheNkviiLjdzt26J1AD4P
9 | IGyl3m7nIITWqpibLScMgwgGjrX9RP8k/gctbWO2oKZlPwSun6xd4tbgOs8aU8zb
10 | qlKsOkRmoi2HVEyyBh7o+3h8DVXTAzR5VAjJaFst0yFKS91NhK60fmTQAB1+wmHR
11 | AgMBAAGjgYQwgYEwHQYDVR0OBBYEFGuSzft+tjDJiRwtEFBxvGc7f/NcMFIGA1Ud
12 | IwRLMEmAFGuSzft+tjDJiRwtEFBxvGc7f/NcoSakJDAiMSAwHgYDVQQDExdrZXli
13 | dWNrZXQuYXBwLm5vcmR1Lm5ldIIJAKPpMz4LyS0VMAwGA1UdEwQFMAMBAf8wDQYJ
14 | KoZIhvcNAQEFBQADggEBAGxfh0yZg/FNJlkalujlAyxAKl4enNYapyg8DtadRqnq
15 | WKAyVp3+OhDFKr1WrKghDAS8qARIoPEvPws24S/A1hrT0T2tux7j3puTFkt/JLbO
16 | vvjpFAcbu/ILfzsXk19zKInCXmFWW5UNl2iq9GRCKltMyWZ/8m74cZpm1X3x4u33
17 | u+VTbtj/ZeTzqsriFfXo2q8EsdEK+27tvluNbdUgEayz8YIFrLI9LJRlWH6X6BWh
18 | PbIrR0sd263vE/1fgWbeB66CRXys0oQ92k9arc+gkVB2hZ8o4BK82LkwO30Ueo+l
19 | oHJ24vcX55P2LXmP+x+hIjMXICkqly0QE7kUVUVBisQ=
20 | -----END CERTIFICATE-----
21 |
--------------------------------------------------------------------------------
/tests/restrictive_idp_conf.py:
--------------------------------------------------------------------------------
1 | from saml2 import BINDING_HTTP_REDIRECT
2 | from saml2 import BINDING_SOAP
3 | from saml2.saml import NAME_FORMAT_URI
4 |
5 |
6 | BASE = "http://localhost:8089/"
7 |
8 | from pathutils import full_path
9 |
10 |
11 | CONFIG = {
12 | "entityid": "urn:mace:example.com:saml:roland:idpr",
13 | "name": "Rolands restrictied IdP",
14 | "service": {
15 | "idp": {
16 | "endpoints": {
17 | "single_sign_on_service": [(f"{BASE}sso", BINDING_HTTP_REDIRECT)],
18 | "attribute_service": [(f"{BASE}aa", BINDING_SOAP)],
19 | },
20 | "policy": {
21 | "default": {"lifetime": {"minutes": 15}, "name_form": NAME_FORMAT_URI},
22 | "urn:mace:example.com:saml:roland:sp": {
23 | "lifetime": {"minutes": 5},
24 | "attribute_restrictions": {
25 | "givenName": None,
26 | "surName": None,
27 | "mail": [".*@example.com"],
28 | "eduPersonAffiliation": ["(employee|staff|faculty)"],
29 | },
30 | },
31 | },
32 | "subject_data": full_path("subject_data.db"),
33 | }
34 | },
35 | "key_file": full_path("test.key"),
36 | "cert_file": full_path("test.pem"),
37 | "xmlsec_binary": None,
38 | "metadata": [
39 | {
40 | "class": "saml2.mdstore.MetaDataFile",
41 | "metadata": [(full_path("sp_0.metadata"),)],
42 | }
43 | ],
44 | "attribute_map_dir": full_path("attributemaps"),
45 | }
46 |
--------------------------------------------------------------------------------
/tests/root_cert/localhost.ca.crt:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIICSzCCAbQCAQEwDQYJKoZIhvcNAQELBQAwbTELMAkGA1UEBhMCc2UxCzAJBgNV
3 | BAgMAmFjMQ0wCwYDVQQHDAR1bWVhMRwwGgYDVQQKDBNJVFMgVW1lYSBVbml2ZXJz
4 | aXR5MQ0wCwYDVQQLDARESVJHMRUwEwYDVQQDDAxsb2NhbGhvc3QuY2EwIBcNMTkw
5 | NDEyMTk1MDM0WhgPMzAxODA4MTMxOTUwMzRaMG0xCzAJBgNVBAYTAnNlMQswCQYD
6 | VQQIDAJhYzENMAsGA1UEBwwEdW1lYTEcMBoGA1UECgwTSVRTIFVtZWEgVW5pdmVy
7 | c2l0eTENMAsGA1UECwwERElSRzEVMBMGA1UEAwwMbG9jYWxob3N0LmNhMIGfMA0G
8 | CSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMmx45iD6EoaycZaKW/0Wt8fZyxD1RkbCD
9 | AfPNHYiq5CD/6c43F3gwjCM6dCss4m6Gm7nllNKl5P3TUY0MuPjE9CcWtZbIRPw3
10 | FmDDsiWtrnmJA1qhRal2LQlBJcj/0Jjy35jXvT7sN8EnrOjjLr4cpo4s3FnLghsH
11 | RIRpkEaytQIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAHeVAl4z5gxyBCHrmlyfjR04
12 | Ndpt57FjBGARgULvCI3VFQpsXZvezVBfnbd/Ynith2mKRVsTx+h6aEBucWsSgLiG
13 | gCR/ZPaX9bIbXNNadpe96J402CHpGlnA2nBtkZHN9TGEW8wCKe2D+RDjNMyi/1PJ
14 | aiscKKlLdKqmRpTQZ7sy
15 | -----END CERTIFICATE-----
16 |
--------------------------------------------------------------------------------
/tests/root_cert/localhost.ca.key:
--------------------------------------------------------------------------------
1 | -----BEGIN RSA PRIVATE KEY-----
2 | MIICXQIBAAKBgQDMmx45iD6EoaycZaKW/0Wt8fZyxD1RkbCDAfPNHYiq5CD/6c43
3 | F3gwjCM6dCss4m6Gm7nllNKl5P3TUY0MuPjE9CcWtZbIRPw3FmDDsiWtrnmJA1qh
4 | Ral2LQlBJcj/0Jjy35jXvT7sN8EnrOjjLr4cpo4s3FnLghsHRIRpkEaytQIDAQAB
5 | AoGAMw6aUjz/bNVzX2u1UPzOhIOWvjjeHFbAt1BraEnwasSWv4W2oeTHZ0XxHIsU
6 | oxS2A/0kPHgQwLkN5ge5rO0TlpAI5X9ZqlJ0SXF5zjJOBtyK6TWoUbwnyzS7lbFC
7 | q9AVrHwMX9uNCboccqzjrzHyNE+4/QT7z2G5AMzjfq+5EwECQQDvOJuUl2pbUUIK
8 | nMCmwkARFEZzZYV2oIBDsagTG8gX7glj5stoYXuez8EnYtNHRDBConyKqruuzqJk
9 | qSKlha7hAkEA2vT4CpAzHSCknwQKXmFwBD5hVBrv+JZSur6XpqEdwXkX2osScAaW
10 | xj3vQEQorJC2CryvUVOTeuFoog0f+6HiVQJBAMs0dMQ2ErxbPBQzr1p4K1/Wrzmb
11 | BVINaKcYJEOHF+Nr6kIYbLTQCeiPZe4E/p/NBomz6MMJ4L/O+xcyrSGZe0ECQQCZ
12 | ejELpnxNpH4AAKML+Ry9vMQYYjFnfGdNAx/l6vWikjEIPYeVAulY2D0GPUCNhXo1
13 | GIGDbiPodGwVe0G57oVpAkBjckA1LEE1Kzkq5sR9U9t9m+3WSBvMZxLUwJYdVmOY
14 | Y6xKVtJfe9XuQt9tzoWQW8iieWyKOw7yLYCBcjns2iZn
15 | -----END RSA PRIVATE KEY-----
16 |
--------------------------------------------------------------------------------
/tests/saml_hok_invalid.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 | https://idp:8443
8 |
9 |
10 |
11 |
12 | https://idp:8443
13 |
14 | 57a0a35eefdb29ca8b4ab78d5a118117
15 |
16 |
17 |
18 |
19 |
20 |
21 | urn:oasis:names:tc:SAML:2.0:ac:classes:Password
22 |
23 |
24 |
25 |
26 | testuser
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/tests/server2_conf.py:
--------------------------------------------------------------------------------
1 | from pathutils import full_path
2 |
3 |
4 | CONFIG = {
5 | "entityid": "urn:mace:example.com:saml:roland:sp",
6 | "name": "urn:mace:example.com:saml:roland:sp",
7 | "description": "My own SP",
8 | "service": {
9 | "sp": {
10 | "endpoints": {
11 | "assertion_consumer_service": ["http://lingon.catalogix.se:8087/"],
12 | },
13 | "required_attributes": ["surName", "givenName", "mail"],
14 | "optional_attributes": ["title"],
15 | "idp": ["urn:mace:example.com:saml:roland:idp"],
16 | "subject_data": "subject_data.db",
17 | }
18 | },
19 | "debug": 1,
20 | "key_file": full_path("test.key"),
21 | "cert_file": full_path("test.pem"),
22 | "xmlsec_binary": None,
23 | "metadata": {
24 | "local": [full_path("idp_soap.xml"), full_path("vo_metadata.xml")],
25 | },
26 | "virtual_organization": {
27 | "urn:mace:example.com:it:tek": {
28 | "nameid_format": "urn:oid:1.3.6.1.4.1.1466.115.121.1.15-NameID",
29 | "common_identifier": "umuselin",
30 | }
31 | },
32 | "accepted_time_diff": 60,
33 | "attribute_map_dir": full_path("attributemaps"),
34 | "organization": {
35 | "name": ("AB Exempel", "se"),
36 | "display_name": ("AB Exempel", "se"),
37 | "url": "http://www.example.org",
38 | },
39 | "contact_person": [
40 | {
41 | "given_name": "Roland",
42 | "sur_name": "Hedberg",
43 | "telephone_number": "+46 70 100 0000",
44 | "email_address": ["tech@example.com", "tech@example.org"],
45 | "contact_type": "technical",
46 | },
47 | ],
48 | }
49 |
--------------------------------------------------------------------------------
/tests/server3_conf.py:
--------------------------------------------------------------------------------
1 | from pathutils import full_path
2 |
3 |
4 | CONFIG = {
5 | "entityid": "urn:mace:example.com:saml:roland:sp",
6 | "name": "urn:mace:example.com:saml:roland:sp",
7 | "description": "My own SP",
8 | "service": {
9 | "sp": {
10 | "endpoints": {
11 | "assertion_consumer_service": ["http://lingon.catalogix.se:8087/"],
12 | },
13 | "required_attributes": ["surName", "givenName", "mail"],
14 | "optional_attributes": ["title"],
15 | "idp": ["urn:mace:example.com:saml:roland:idp"],
16 | "subject_data": full_path("subject_data.db"),
17 | }
18 | },
19 | "debug": 1,
20 | "key_file": full_path("test.key"),
21 | "cert_file": full_path("test.pem"),
22 | "xmlsec_binary": None,
23 | "metadata": {
24 | "local": [full_path("idp_aa.xml"), full_path("vo_metadata.xml")],
25 | },
26 | "virtual_organization": {
27 | "urn:mace:example.com:it:tek": {
28 | "nameid_format": "urn:oid:1.3.6.1.4.1.1466.115.121.1.15-NameID",
29 | "common_identifier": "umuselin",
30 | }
31 | },
32 | "accepted_time_diff": 60,
33 | "attribute_map_dir": full_path("attributemaps"),
34 | "organization": {
35 | "name": ("AB Exempel", "se"),
36 | "display_name": ("AB Exempel", "se"),
37 | "url": "http://www.example.org",
38 | },
39 | "contact_person": [
40 | {
41 | "given_name": "Roland",
42 | "sur_name": "Hedberg",
43 | "telephone_number": "+46 70 100 0000",
44 | "email_address": ["tech@example.com", "tech@example.org"],
45 | "contact_type": "technical",
46 | },
47 | ],
48 | }
49 |
--------------------------------------------------------------------------------
/tests/server_conf_syslog.py:
--------------------------------------------------------------------------------
1 | __author__ = "rolandh"
2 |
3 | from pathutils import full_path
4 |
5 |
6 | CONFIG = {
7 | "entityid": "urn:mace:example.com:saml:roland:sp",
8 | "name": "urn:mace:example.com:saml:roland:sp",
9 | "description": "My own SP",
10 | "service": {
11 | "sp": {
12 | "endpoints": {
13 | "assertion_consumer_service": ["http://lingon.catalogix.se:8087/"],
14 | },
15 | "required_attributes": ["surName", "givenName", "mail"],
16 | "optional_attributes": ["title"],
17 | "idp": ["urn:mace:example.com:saml:roland:idp"],
18 | }
19 | },
20 | "debug": 1,
21 | "key_file": full_path("test.key"),
22 | "cert_file": full_path("test.pem"),
23 | # "xmlsec_binary" : None,
24 | "metadata": [
25 | {
26 | "class": "saml2.mdstore.MetaDataFile",
27 | "metadata": [(full_path("idp.xml"),), (full_path("vo_metadata.xml"),)],
28 | }
29 | ],
30 | "virtual_organization": {
31 | "urn:mace:example.com:it:tek": {
32 | "nameid_format": "urn:oid:1.3.6.1.4.1.1466.115.121.1.15-NameID",
33 | "common_identifier": "umuselin",
34 | }
35 | },
36 | "subject_data": full_path("subject_data.db"),
37 | "accepted_time_diff": 60,
38 | "attribute_map_dir": full_path("attributemaps"),
39 | "organization": {
40 | "name": ("AB Exempel", "se"),
41 | "display_name": ("AB Exempel", "se"),
42 | "url": "http://www.example.org",
43 | },
44 | "contact_person": [
45 | {
46 | "given_name": "Roland",
47 | "sur_name": "Hedberg",
48 | "telephone_number": "+46 70 100 0000",
49 | "email_address": ["tech@eample.com", "tech@example.org"],
50 | "contact_type": "technical",
51 | },
52 | ],
53 | "logger": {
54 | "syslog": {
55 | "address": ("localhost", 514),
56 | "facility": "local3",
57 | "socktype": "dgram",
58 | },
59 | "loglevel": "info",
60 | },
61 | }
62 |
--------------------------------------------------------------------------------
/tests/sp_0.metadata:
--------------------------------------------------------------------------------
1 |
2 | MIICsDCCAhmgAwIBAgIJAJrzqSSwmDY9MA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
3 | BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
4 | aWRnaXRzIFB0eSBMdGQwHhcNMDkxMDA2MTk0OTQxWhcNMDkxMTA1MTk0OTQxWjBF
5 | MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50
6 | ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB
7 | gQDJg2cms7MqjniT8Fi/XkNHZNPbNVQyMUMXE9tXOdqwYCA1cc8vQdzkihscQMXy
8 | 3iPw2cMggBu6gjMTOSOxECkuvX5ZCclKr8pXAJM5cY6gVOaVO2PdTZcvDBKGbiaN
9 | efiEw5hnoZomqZGp8wHNLAUkwtH9vjqqvxyS/vclc6k2ewIDAQABo4GnMIGkMB0G
10 | A1UdDgQWBBRePsKHKYJsiojE78ZWXccK9K4aJTB1BgNVHSMEbjBsgBRePsKHKYJs
11 | iojE78ZWXccK9K4aJaFJpEcwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUt
12 | U3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZIIJAJrzqSSw
13 | mDY9MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAJSrKOEzHO7TL5cy6
14 | h3qh+3+JAk8HbGBW+cbX6KBCAw/mzU8flK25vnWwXS3dv2FF3Aod0/S7AWNfKib5
15 | U/SA9nJaz/mWeF9S0farz9AQFc8/NSzAzaVq7YbM4F6f6N2FRl7GikdXRCed45j6
16 | mrPzGzk3ECbupFnqyREH3+ZPSdk=
17 | Rolands SP Roland own test SP
18 |
--------------------------------------------------------------------------------
/tests/sp_1_conf.py:
--------------------------------------------------------------------------------
1 | from pathutils import full_path
2 |
3 |
4 | CONFIG = {
5 | "entityid": "urn:mace:example.com:saml:roland:sp",
6 | "name": "urn:mace:example.com:saml:roland:sp",
7 | "description": "My own SP",
8 | "service": {
9 | "sp": {
10 | "endpoints": {
11 | "assertion_consumer_service": ["http://lingon.catalogix.se:8087/"],
12 | },
13 | "required_attributes": ["surName", "givenName", "mail"],
14 | "optional_attributes": ["title"],
15 | "idp": ["urn:mace:example.com:saml:roland:idp"],
16 | }
17 | },
18 | "debug": 1,
19 | "key_file": full_path("test.key"),
20 | "cert_file": full_path("test.pem"),
21 | "xmlsec_binary": None,
22 | "metadata": [
23 | {
24 | "class": "saml2.mdstore.MetaDataFile",
25 | "metadata": [(full_path("idp.xml"),), (full_path("vo_metadata.xml"),)],
26 | }
27 | ],
28 | "virtual_organization": {
29 | "urn:mace:example.com:it:tek": {
30 | "nameid_format": "urn:oid:1.3.6.1.4.1.1466.115.121.1.15-NameID",
31 | "common_identifier": "umuselin",
32 | }
33 | },
34 | "subject_data": full_path("subject_data.db"),
35 | "accepted_time_diff": 60,
36 | "attribute_map_dir": full_path("attributemaps"),
37 | "organization": {
38 | "name": ("AB Exempel", "se"),
39 | "display_name": ("AB Exempel", "se"),
40 | "url": "http://www.example.org",
41 | },
42 | "contact_person": [
43 | {
44 | "given_name": "Roland",
45 | "sur_name": "Hedberg",
46 | "telephone_number": "+46 70 100 0000",
47 | "email_address": ["tech@eample.com", "tech@example.org"],
48 | "contact_type": "technical",
49 | },
50 | ],
51 | "secret": "0123456789",
52 | "http_client_timeout": 10,
53 | }
54 |
--------------------------------------------------------------------------------
/tests/sp_2_conf.py:
--------------------------------------------------------------------------------
1 | from pathutils import full_path
2 |
3 |
4 | CONFIG = {
5 | "entityid": "urn:mace:example.com:saml:roland:sp",
6 | "name": "urn:mace:example.com:saml:roland:sp",
7 | "description": "My own SP",
8 | "service": {
9 | "sp": {
10 | "endpoints": {
11 | "assertion_consumer_service": ["http://lingon.catalogix.se:8087/"],
12 | },
13 | "required_attributes": ["surName", "givenName", "mail"],
14 | "optional_attributes": ["title"],
15 | "idp": ["urn:mace:example.com:saml:roland:idp"],
16 | }
17 | },
18 | "debug": 1,
19 | "key_file": full_path("test.key"),
20 | "cert_file": full_path("test.pem"),
21 | "xmlsec_binary": None,
22 | "metadata": {
23 | "local": [full_path("idp_2.xml")],
24 | },
25 | "virtual_organization": {
26 | "urn:mace:example.com:it:tek": {
27 | "nameid_format": "urn:oid:1.3.6.1.4.1.1466.115.121.1.15-NameID",
28 | "common_identifier": "umuselin",
29 | }
30 | },
31 | "subject_data": full_path("subject_data.db"),
32 | "accepted_time_diff": 60,
33 | "attribute_map_dir": full_path("attributemaps"),
34 | "organization": {
35 | "name": ("AB Exempel", "se"),
36 | "display_name": ("AB Exempel", "se"),
37 | "url": "http://www.example.org",
38 | },
39 | "contact_person": [
40 | {
41 | "given_name": "Roland",
42 | "sur_name": "Hedberg",
43 | "telephone_number": "+46 70 100 0000",
44 | "email_address": ["tech@eample.com", "tech@example.org"],
45 | "contact_type": "technical",
46 | },
47 | ],
48 | "secret": "0123456789",
49 | "only_use_keys_in_metadata": True,
50 | }
51 |
--------------------------------------------------------------------------------
/tests/sp_conf_nameidpolicy.py:
--------------------------------------------------------------------------------
1 | from pathutils import full_path
2 | from pathutils import xmlsec_path
3 |
4 |
5 | CONFIG = {
6 | "entityid": "urn:mace:example.com:saml:roland:sp",
7 | "name": "urn:mace:example.com:saml:roland:sp",
8 | "description": "My own SP",
9 | "service": {
10 | "sp": {
11 | "endpoints": {
12 | "assertion_consumer_service": ["http://lingon.catalogix.se:8087/"],
13 | },
14 | "required_attributes": ["surName", "givenName", "mail"],
15 | "optional_attributes": ["title"],
16 | "idp": ["urn:mace:example.com:saml:roland:idp"],
17 | "name_id_policy_format": "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent",
18 | "name_id_format_allow_create": "true",
19 | }
20 | },
21 | "debug": 1,
22 | "key_file": full_path("test.key"),
23 | "cert_file": full_path("test.pem"),
24 | "encryption_keypairs": [
25 | {"key_file": full_path("test_1.key"), "cert_file": full_path("test_1.crt")},
26 | {"key_file": full_path("test_2.key"), "cert_file": full_path("test_2.crt")},
27 | ],
28 | "ca_certs": full_path("cacerts.txt"),
29 | "xmlsec_binary": xmlsec_path,
30 | "metadata": [
31 | {
32 | "class": "saml2.mdstore.MetaDataFile",
33 | "metadata": [(full_path("idp.xml"),), (full_path("vo_metadata.xml"),)],
34 | }
35 | ],
36 | "virtual_organization": {
37 | "urn:mace:example.com:it:tek": {
38 | "nameid_format": "urn:oid:1.3.6.1.4.1.1466.115.121.1.15-NameID",
39 | "common_identifier": "umuselin",
40 | }
41 | },
42 | "subject_data": "subject_data.db",
43 | "accepted_time_diff": 60,
44 | "attribute_map_dir": full_path("attributemaps"),
45 | "valid_for": 6,
46 | "organization": {
47 | "name": ("AB Exempel", "se"),
48 | "display_name": ("AB Exempel", "se"),
49 | "url": "http://www.example.org",
50 | },
51 | "contact_person": [
52 | {
53 | "given_name": "Roland",
54 | "sur_name": "Hedberg",
55 | "telephone_number": "+46 70 100 0000",
56 | "email_address": ["tech@eample.com", "tech@example.org"],
57 | "contact_type": "technical",
58 | },
59 | ],
60 | "logger": {
61 | "rotating": {
62 | "filename": full_path("sp.log"),
63 | "maxBytes": 100000,
64 | "backupCount": 5,
65 | },
66 | "loglevel": "info",
67 | },
68 | }
69 |
--------------------------------------------------------------------------------
/tests/sp_slo_redirect_conf.py:
--------------------------------------------------------------------------------
1 | from pathutils import full_path
2 |
3 | from saml2 import BINDING_HTTP_POST
4 | from saml2 import BINDING_HTTP_REDIRECT
5 |
6 |
7 | HOME = "http://lingon.catalogix.se:8087/"
8 | CONFIG = {
9 | "entityid": "urn:mace:example.com:saml:roland:sp",
10 | "name": "urn:mace:example.com:saml:roland:sp",
11 | "description": "My own SP",
12 | "service": {
13 | "sp": {
14 | "endpoints": {
15 | "assertion_consumer_service": [(HOME, BINDING_HTTP_POST)],
16 | "single_logout_service": [(f"{HOME}slo", BINDING_HTTP_REDIRECT)],
17 | },
18 | "required_attributes": ["surName", "givenName", "mail"],
19 | "optional_attributes": ["title"],
20 | "idp": ["urn:mace:example.com:saml:roland:idp"],
21 | "subject_data": full_path("subject_data.db"),
22 | }
23 | },
24 | "debug": 1,
25 | "key_file": full_path("test.key"),
26 | "cert_file": full_path("test.pem"),
27 | "xmlsec_binary": None,
28 | "metadata": [
29 | {
30 | "class": "saml2.mdstore.MetaDataFile",
31 | "metadata": [(full_path("idp_slo_redirect.xml"),)],
32 | }
33 | ],
34 | "virtual_organization": {
35 | "urn:mace:example.com:it:tek": {
36 | "nameid_format": "urn:oid:1.3.6.1.4.1.1466.115.121.1.15-NameID",
37 | "common_identifier": "umuselin",
38 | }
39 | },
40 | "accepted_time_diff": 60,
41 | "attribute_map_dir": full_path("attributemaps"),
42 | "organization": {
43 | "name": ("AB Exempel", "se"),
44 | "display_name": ("AB Exempel", "se"),
45 | "url": "http://www.example.org",
46 | },
47 | "contact_person": [
48 | {
49 | "given_name": "Roland",
50 | "sur_name": "Hedberg",
51 | "telephone_number": "+46 70 100 0000",
52 | "email_address": ["tech@eample.com", "tech@example.org"],
53 | "contact_type": "technical",
54 | },
55 | ],
56 | }
57 |
--------------------------------------------------------------------------------
/tests/sp_test/targetsp.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | from saml2.saml import AUTHN_PASSWORD
3 |
4 |
5 | __author__ = "rolandh"
6 |
7 | import json
8 |
9 |
10 | BASE = "http://localhost:8087"
11 | # BASE= "http://lingon.catalogix.se:8087"
12 |
13 | metadata = open("./sp/sp.xml").read()
14 |
15 | AUTHN = {"class_ref": AUTHN_PASSWORD, "authn_auth": "http://lingon.catalogix.se/login"}
16 |
17 | info = {
18 | "start_page": BASE,
19 | "entity_id": f"{BASE}/sp.xml",
20 | "result": {
21 | "matches": {"content": "Your identity are"},
22 | },
23 | "metadata": metadata,
24 | "args": {
25 | "AuthnResponse": {
26 | "sign_assertion": "always", # always, never
27 | "sign_response": "never", # always, never
28 | "sign_digest_alg": ds.DIGEST_SHA256,
29 | "sign_signature_alg": ds.SIG_RSA_SHA256,
30 | "authn": AUTHN,
31 | }
32 | },
33 | # This is the set of attributes and values that are returned in the
34 | # SAML Assertion
35 | "identity": {"given_name": "Roland", "sn": "Hedberg"},
36 | # This is the value of the NameID that is return in the Subject in the
37 | # Assertion
38 | "userid": "roland",
39 | # regex pattern that must be contained in the resulting echo page to validate
40 | # that the SP returned the right page after Login.
41 | "echopageIdPattern": r"SAML Echo Service ",
42 | # list of regex patterns that must be contained in the resulting echo page to validate
43 | # that the SP's echo page returns expected SAMLe response values (e.g. attribute values)
44 | "echopageContentPattern": [
45 | r"Given Name\s*\s*Roland ",
46 | r"Userid\s*\s*roalnd ",
47 | r"Surname\s*\s*Hedberg ",
48 | ],
49 | "constraints": {
50 | "authnRequest_signature_required": True,
51 | # allowed for assertion & response signature:
52 | "signature_algorithm": [
53 | # ds.SIG_RSA_SHA1, # you may need this for legacy deployments
54 | ds.SIG_RSA_SHA224,
55 | ds.SIG_RSA_SHA256,
56 | ds.SIG_RSA_SHA384,
57 | ds.SIG_RSA_SHA512,
58 | ],
59 | "digest_algorithm": [
60 | # ds.DIGEST_SHA1, # you may need this for legacy deployments
61 | ds.DIGEST_SHA1,
62 | ds.DIGEST_SHA224,
63 | ds.DIGEST_SHA256,
64 | ds.DIGEST_SHA384,
65 | ds.DIGEST_SHA512,
66 | ds.DIGEST_RIPEMD160,
67 | ],
68 | },
69 | }
70 |
71 | print(json.dumps(info))
72 |
--------------------------------------------------------------------------------
/tests/test.key:
--------------------------------------------------------------------------------
1 | -----BEGIN RSA PRIVATE KEY-----
2 | MIICXgIBAAKBgQDJg2cms7MqjniT8Fi/XkNHZNPbNVQyMUMXE9tXOdqwYCA1cc8v
3 | QdzkihscQMXy3iPw2cMggBu6gjMTOSOxECkuvX5ZCclKr8pXAJM5cY6gVOaVO2Pd
4 | TZcvDBKGbiaNefiEw5hnoZomqZGp8wHNLAUkwtH9vjqqvxyS/vclc6k2ewIDAQAB
5 | AoGBAKD2emW6ssmyhfQ9ztYFuJ4FlwiJf5icKuf7L4BsMRgjoHawUvt/k69l9aPK
6 | xZNrB7BycV+7lOqU57FaOf1MWGeWzsU5bYUVpFzOVwsY4umtsO78QGKLZe+91Z+k
7 | tOlmL3scAymAgE88Jmr0g8FC46Vv4Sam7zMCtmOvA9fYog1ZAkEA8lAe+XihSuZI
8 | 6IZcdRdB6QJ5cgAJoZdWKKtUovb5Ah2w4D/ebkfpsQJK44aSR5GbnrnqSaMeLJMR
9 | z++Td0edHwJBANTlUBzoo3ihcBOZ0VzGYgDIG8foCTEf3jDBYNYaY9RH/c4P50Gk
10 | Da4PBqtf1f+VORwAsC2NTeY6HUEWMpvfXyUCQQChQ3FZ1k6B6oDbP5CI3NGgoWTx
11 | 2dSPFojgyCWrz3IpVllA5UDDZFjC1SPCCO2Rc/Z9zH2ARG7we3B/UpJx79dBAkEA
12 | iPc6sk6NFQevpjyYcDqFRIF5NgQ3Ha6l8PIITdZOkXz7cX3Txuw3jNrH7KtMbxDe
13 | 3AApWDUHf+21cnFIf/WWLQJAeG0KKBfZw1iRu9vlcYakGWRUSga78QDy08uHDtxQ
14 | LXxOfSvm/y8N1KrEsXf/cJzHUGQJrqk8nLzR5mTRqnAZWA==
15 | -----END RSA PRIVATE KEY-----
16 |
--------------------------------------------------------------------------------
/tests/test.key.p8:
--------------------------------------------------------------------------------
1 | -----BEGIN PRIVATE KEY-----
2 | MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAMmDZyazsyqOeJPw
3 | WL9eQ0dk09s1VDIxQxcT21c52rBgIDVxzy9B3OSKGxxAxfLeI/DZwyCAG7qCMxM5
4 | I7EQKS69flkJyUqvylcAkzlxjqBU5pU7Y91Nly8MEoZuJo15+ITDmGehmiapkanz
5 | Ac0sBSTC0f2+Oqq/HJL+9yVzqTZ7AgMBAAECgYEAoPZ6ZbqyybKF9D3O1gW4ngWX
6 | CIl/mJwq5/svgGwxGCOgdrBS+3+Tr2X1o8rFk2sHsHJxX7uU6pTnsVo5/UxYZ5bO
7 | xTlthRWkXM5XCxji6a2w7vxAYotl773Vn6S06WYvexwDKYCATzwmavSDwULjpW/h
8 | JqbvMwK2Y68D19iiDVkCQQDyUB75eKFK5kjohlx1F0HpAnlyAAmhl1Yoq1Si9vkC
9 | HbDgP95uR+mxAkrjhpJHkZueuepJox4skxHP75N3R50fAkEA1OVQHOijeKFwE5nR
10 | XMZiAMgbx+gJMR/eMMFg1hpj1Ef9zg/nQaQNrg8Gq1/V/5U5HACwLY1N5jodQRYy
11 | m99fJQJBAKFDcVnWToHqgNs/kIjc0aChZPHZ1I8WiODIJavPcilWWUDlQMNkWMLV
12 | I8II7ZFz9n3MfYBEbvB7cH9SknHv10ECQQCI9zqyTo0VB6+mPJhwOoVEgXk2BDcd
13 | rqXw8ghN1k6RfPtxfdPG7DeM2sfsq0xvEN7cAClYNQd/7bVycUh/9ZYtAkB4bQoo
14 | F9nDWJG72+VxhqQZZFRKBrvxAPLTy4cO3FAtfE59K+b/Lw3UqsSxd/9wnMdQZAmu
15 | qTycvNHmZNGqcBlY
16 | -----END PRIVATE KEY-----
17 |
--------------------------------------------------------------------------------
/tests/test.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIICsDCCAhmgAwIBAgIJAJrzqSSwmDY9MA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
3 | BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
4 | aWRnaXRzIFB0eSBMdGQwHhcNMDkxMDA2MTk0OTQxWhcNMDkxMTA1MTk0OTQxWjBF
5 | MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50
6 | ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB
7 | gQDJg2cms7MqjniT8Fi/XkNHZNPbNVQyMUMXE9tXOdqwYCA1cc8vQdzkihscQMXy
8 | 3iPw2cMggBu6gjMTOSOxECkuvX5ZCclKr8pXAJM5cY6gVOaVO2PdTZcvDBKGbiaN
9 | efiEw5hnoZomqZGp8wHNLAUkwtH9vjqqvxyS/vclc6k2ewIDAQABo4GnMIGkMB0G
10 | A1UdDgQWBBRePsKHKYJsiojE78ZWXccK9K4aJTB1BgNVHSMEbjBsgBRePsKHKYJs
11 | iojE78ZWXccK9K4aJaFJpEcwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUt
12 | U3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZIIJAJrzqSSw
13 | mDY9MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAJSrKOEzHO7TL5cy6
14 | h3qh+3+JAk8HbGBW+cbX6KBCAw/mzU8flK25vnWwXS3dv2FF3Aod0/S7AWNfKib5
15 | U/SA9nJaz/mWeF9S0farz9AQFc8/NSzAzaVq7YbM4F6f6N2FRl7GikdXRCed45j6
16 | mrPzGzk3ECbupFnqyREH3+ZPSdk=
17 | -----END CERTIFICATE-----
18 |
--------------------------------------------------------------------------------
/tests/test_06_setarg.py:
--------------------------------------------------------------------------------
1 | from saml2 import saml
2 | from saml2.argtree import add_path
3 | from saml2.argtree import find_paths
4 | from saml2.argtree import is_set
5 | from saml2.argtree import set_arg
6 | from saml2.saml import Subject
7 | from saml2.samlp import Response
8 |
9 |
10 | __author__ = "roland"
11 |
12 |
13 | def test_path():
14 | result = find_paths(Subject, "in_response_to")
15 |
16 | assert result == [["subject_confirmation", "subject_confirmation_data", "in_response_to"]]
17 |
18 | result = find_paths(Response, "in_response_to")
19 |
20 | assert result == [
21 | ["assertion", "subject", "subject_confirmation", "subject_confirmation_data", "in_response_to"],
22 | ["in_response_to"],
23 | ]
24 |
25 |
26 | def test_set_arg():
27 | r = set_arg(Subject, "in_response_to", "123456")
28 |
29 | assert r == [{"subject_confirmation": {"subject_confirmation_data": {"in_response_to": "123456"}}}]
30 |
31 |
32 | def test_multi():
33 | t = {}
34 | t = add_path(t, ["subject_confirmation", "method", saml.SCM_BEARER])
35 | add_path(t["subject_confirmation"], ["subject_confirmation_data", "in_response_to", "1234"])
36 |
37 | assert t == {
38 | "subject_confirmation": {
39 | "subject_confirmation_data": {"in_response_to": "1234"},
40 | "method": "urn:oasis:names:tc:SAML:2.0:cm:bearer",
41 | }
42 | }
43 |
44 |
45 | def test_is_set():
46 | t = {}
47 | t = add_path(t, ["subject_confirmation", "method", saml.SCM_BEARER])
48 | add_path(t["subject_confirmation"], ["subject_confirmation_data", "in_response_to", "1234"])
49 |
50 | assert is_set(t, ["subject_confirmation", "method"])
51 | assert is_set(t, ["subject_confirmation", "subject_confirmation_data", "receiver"]) is False
52 |
--------------------------------------------------------------------------------
/tests/test_1.crt:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIICITCCAYoCAQEwDQYJKoZIhvcNAQELBQAwWDELMAkGA1UEBhMCenoxCzAJBgNV
3 | BAgMAnp6MQ0wCwYDVQQHDAR6enp6MQ4wDAYDVQQKDAVaenp6ejEOMAwGA1UECwwF
4 | Wnp6enoxDTALBgNVBAMMBHRlc3QwIBcNMTkwNDEyMTk1MDM0WhgPMzAxODA4MTMx
5 | OTUwMzRaMFgxCzAJBgNVBAYTAnp6MQswCQYDVQQIDAJ6ejENMAsGA1UEBwwEenp6
6 | ejEOMAwGA1UECgwFWnp6enoxDjAMBgNVBAsMBVp6enp6MQ0wCwYDVQQDDAR0ZXN0
7 | MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDHcj80WU/XBsd9FlyQmfjPUdfm
8 | edhCFDd6TEQmZNNqP/UG+VkGa+BXjRIHMfic/WxPTbGhCjv68ci0UDNomUXagFex
9 | LGNpkwa7+CRVtoc/1xgq+ySE6M4nhcCutScoxNvWNn5eSQ66i3U0sTv91MgsXxqE
10 | dTaiZg0BIufEc3dueQIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAGUV5B+USHvaRa8k
11 | gCNJSuNpo6ARlv0ekrk8bbdNRBiEUdCMyoGJFfuM9K0zybX6Vr25wai3nvaog294
12 | Vx/jWjX2g5SDbjItH6VGy6C9GCGf1A07VxFRCfJn5tA9HuJjPKiE+g/BmrV5N4Ce
13 | alzFxPHWYkNOzoRU8qI7OqUai1kL
14 | -----END CERTIFICATE-----
15 |
--------------------------------------------------------------------------------
/tests/test_1.der:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IdentityPython/pysaml2/0252ec96058c87c43f89e05d978e72b6ba2e6978/tests/test_1.der
--------------------------------------------------------------------------------
/tests/test_1.key:
--------------------------------------------------------------------------------
1 | -----BEGIN RSA PRIVATE KEY-----
2 | MIICXgIBAAKBgQDHcj80WU/XBsd9FlyQmfjPUdfmedhCFDd6TEQmZNNqP/UG+VkG
3 | a+BXjRIHMfic/WxPTbGhCjv68ci0UDNomUXagFexLGNpkwa7+CRVtoc/1xgq+ySE
4 | 6M4nhcCutScoxNvWNn5eSQ66i3U0sTv91MgsXxqEdTaiZg0BIufEc3dueQIDAQAB
5 | AoGBAIhWkN44L1vORpA7uQsgNfWC/ROQN0T0jPgNKokUY3E+R0F9Ml4xYCp5RNmm
6 | T00B8AhGFCcB1/6zSX/5Uystm5GNO/V8pNnM7qTrTtnwX/umyceIVxq03z4RCVYb
7 | Gdv67opQAy/Mjy+cHBdYBcIg4ihDbr9CX5qedf9buG4140wBAkEA4nKWZX7DEnEh
8 | sbLnU9KhxwEjadzeZb0JKxoWUjfTTrBlIkHbm6toabV6TVqNENWMSCe0fcjJ7tr7
9 | xPBdOGIlTwJBAOF5ka0khatTH5zSUMRiz8LkqXmy+F0RMXYJeuvNvTSlj3EM9/tK
10 | eWGunHgxv4n1IhGybmfkiXt9MA3sAmUKTbcCQG7nUfM5Zw6EK81c4mCyOxs82nxB
11 | eQZ406GxcBcqUioqyA1EFesiwstq3xA9dfM1szOvhn1INmXuB/qHAhDYOI8CQQCT
12 | zc56ErPxMCdL9O5vHlsVZjHWjkSTNZ8XwnUquI6sQU97i0XQG+zf5Me7XtkxhVjV
13 | AwOu5ThelBz5M1oKhCuXAkEAjDqWQQAiMR2JOFiG/PIHSnPzpmALmf4+A+bjIzbg
14 | VIDhB0LMS5PZikLUWvXPFzdY5e3UKyNTKCxqxqLEYpLWGQ==
15 | -----END RSA PRIVATE KEY-----
16 |
--------------------------------------------------------------------------------
/tests/test_2.crt:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIICITCCAYoCAQEwDQYJKoZIhvcNAQELBQAwWDELMAkGA1UEBhMCenoxCzAJBgNV
3 | BAgMAnp6MQ0wCwYDVQQHDAR6enp6MQ4wDAYDVQQKDAVaenp6ejEOMAwGA1UECwwF
4 | Wnp6enoxDTALBgNVBAMMBHRlc3QwIBcNMTkwNDEyMTk1MDM0WhgPMzAxODA4MTMx
5 | OTUwMzRaMFgxCzAJBgNVBAYTAnp6MQswCQYDVQQIDAJ6ejENMAsGA1UEBwwEenp6
6 | ejEOMAwGA1UECgwFWnp6enoxDjAMBgNVBAsMBVp6enp6MQ0wCwYDVQQDDAR0ZXN0
7 | MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDjW0kJM+4baWKtvO24ZsGXNvNK
8 | KkwTMz7OW5Z6BRqhSOq2WA0c5NCpMk6rD8Z2OTFEolPojEjf8dVyd/Ds/hrjFKQv
9 | 8wQgbdXLN51YTIsgd6h+hBJO+vzhl0PT4aT7M0JKo5ALtS6qk4tsworW2BnwyvsG
10 | SAinwfeWt4t/b1J3kwIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAFtj7WArQQBugmh/
11 | KQjjlfTQ5A052QeXfgTyO9vv1S6MRIi7qgiaEv49cGXnJv/TWbySkMKObPMUApjg
12 | 6z8PqcxuShew5FCTkNvwhABFPiyu0fUj3e2FEPHfsBu76jz4ugtmhUqjqhzwFY9c
13 | tnWRkkl6J0AjM3LnHOSgjNIclDZG
14 | -----END CERTIFICATE-----
15 |
--------------------------------------------------------------------------------
/tests/test_2.key:
--------------------------------------------------------------------------------
1 | -----BEGIN RSA PRIVATE KEY-----
2 | MIICXAIBAAKBgQDjW0kJM+4baWKtvO24ZsGXNvNKKkwTMz7OW5Z6BRqhSOq2WA0c
3 | 5NCpMk6rD8Z2OTFEolPojEjf8dVyd/Ds/hrjFKQv8wQgbdXLN51YTIsgd6h+hBJO
4 | +vzhl0PT4aT7M0JKo5ALtS6qk4tsworW2BnwyvsGSAinwfeWt4t/b1J3kwIDAQAB
5 | AoGAGsVz+y5vOjEVmomloFIRN6IT0cbbQGOHYLsuI94X/afdY21I1f8nhtTSqJfp
6 | 8Z/YalGG+doS9rO+Q4VWDEH7nkrD32kX7GVHDArixjYNYtO4iOk+XW0lzGr1+ghJ
7 | p9+zguQRwAiUFJE6/pqKvFlwhoKppJGtfUsniQpy7ANfyNkCQQD6arntqjlJsYml
8 | cKgpgbA9rhZl7ebVKf1edd15kp411NH/FDWhIlxvg9c3HcAH3jQgnc4Gf4bdvZ6Z
9 | UVDSa63HAkEA6Gzww5cXgGHqQrJoQW/qVaqBAVakeS5uf0CCyb6xvIcHRPS3lLlu
10 | 1upuHeA87IjVeRis8yULgaPzvVyiT/IX1QJBAJvhq/PCLv8swR53TnboACmlINQ6
11 | j6LKDKqsfD2dg1bHMCG1Ft1DYn8YdvQcVNmQ/KoBEasB35ZQ31VZRRJ3bSkCQEXg
12 | UrYK27buORaaOnvJ4MKmgyha2xHPosrBI1Dx8s+CLO5PQE4HPcqBKl/zBX37WWqR
13 | v5VOAtqT5vh8PBQa7Y0CQGXFzSKBBp+Zmxab2t85qaZ2VNiQLvqsNA9v8YgNTT2j
14 | jYHRzwnAZymfO4JCfKnsT3KiVy1xbcYsNEO9+DYpqBA=
15 | -----END RSA PRIVATE KEY-----
16 |
--------------------------------------------------------------------------------
/tests/test_22_mdie.py:
--------------------------------------------------------------------------------
1 | from saml2 import md
2 | from saml2.mdie import from_dict
3 | from saml2.mdstore import load_metadata_modules
4 |
5 |
6 | __author__ = "rolandh"
7 |
8 | ONTS = load_metadata_modules()
9 |
10 |
11 | def _eq(l1, l2):
12 | return set(l1) == set(l2)
13 |
14 |
15 | def _class(cls):
16 | return f"{cls.c_namespace}&{cls.c_tag}"
17 |
18 |
19 | def test_construct_contact():
20 | c = from_dict(
21 | {
22 | "__class__": _class(md.ContactPerson),
23 | "given_name": {"text": "Roland", "__class__": _class(md.GivenName)},
24 | "sur_name": {"text": "Hedberg", "__class__": _class(md.SurName)},
25 | "email_address": [{"text": "roland@catalogix.se", "__class__": _class(md.EmailAddress)}],
26 | },
27 | ONTS,
28 | )
29 |
30 | print(c)
31 | assert c.given_name.text == "Roland"
32 | assert c.sur_name.text == "Hedberg"
33 | assert c.email_address[0].text == "roland@catalogix.se"
34 | assert _eq(c.keyswv(), ["given_name", "sur_name", "email_address"])
35 |
--------------------------------------------------------------------------------
/tests/test_38_metadata_filter.py:
--------------------------------------------------------------------------------
1 | from pathutils import full_path
2 |
3 | from saml2 import config
4 | from saml2.attribute_converter import ac_factory
5 | from saml2.filter import AllowDescriptor
6 | from saml2.mdstore import MetadataStore
7 |
8 |
9 | __author__ = "roland"
10 |
11 | sec_config = config.Config()
12 |
13 |
14 | ATTRCONV = ac_factory(full_path("attributemaps"))
15 |
16 | METADATACONF = {
17 | "1": [
18 | {
19 | "class": "saml2.mdstore.MetaDataFile",
20 | "metadata": [(full_path("swamid-2.0.xml"),)],
21 | }
22 | ],
23 | }
24 |
25 |
26 | def test_swamid_sp():
27 | mds = MetadataStore(
28 | ATTRCONV, sec_config, disable_ssl_certificate_validation=True, filter=AllowDescriptor(["spsso"])
29 | )
30 |
31 | mds.imp(METADATACONF["1"])
32 | sps = mds.with_descriptor("spsso")
33 | assert len(sps) == 417
34 | idps = mds.with_descriptor("idpsso")
35 | assert idps == {}
36 |
37 |
38 | def test_swamid_idp():
39 | mds = MetadataStore(
40 | ATTRCONV, sec_config, disable_ssl_certificate_validation=True, filter=AllowDescriptor(["idpsso"])
41 | )
42 |
43 | mds.imp(METADATACONF["1"])
44 | sps = mds.with_descriptor("spsso")
45 | assert len(sps) == 0
46 | idps = mds.with_descriptor("idpsso")
47 | assert len(idps) == 275
48 |
49 |
50 | if __name__ == "__main__":
51 | test_swamid_idp()
52 |
--------------------------------------------------------------------------------
/tests/test_66_name_id_mapping.py:
--------------------------------------------------------------------------------
1 | __author__ = "rolandh"
2 |
3 | from contextlib import closing
4 |
5 | from saml2.client import Saml2Client
6 | from saml2.saml import NAMEID_FORMAT_PERSISTENT
7 | from saml2.saml import NAMEID_FORMAT_TRANSIENT
8 | from saml2.saml import NameID
9 | from saml2.samlp import NameIDMappingRequest
10 | from saml2.samlp import NameIDPolicy
11 | from saml2.server import Server
12 |
13 |
14 | def test_base_request():
15 | sp = Saml2Client(config_file="servera_conf")
16 |
17 | with closing(Server(config_file="idp_all_conf")) as idp:
18 | binding, destination = sp.pick_binding("name_id_mapping_service", entity_id=idp.config.entityid)
19 |
20 | policy = NameIDPolicy(
21 | format=NAMEID_FORMAT_TRANSIENT, sp_name_qualifier="urn:mace:swamid:junk", allow_create="true"
22 | )
23 |
24 | nameid = NameID(format=NAMEID_FORMAT_TRANSIENT, text="foobar")
25 |
26 | mid, nmr = sp.create_name_id_mapping_request(policy, nameid, destination)
27 |
28 | print(nmr)
29 |
30 | assert isinstance(nmr, NameIDMappingRequest)
31 |
32 |
33 | def test_request_response():
34 | sp = Saml2Client(config_file="servera_conf")
35 |
36 | with closing(Server(config_file="idp_all_conf")) as idp:
37 | binding, destination = sp.pick_binding("name_id_mapping_service", entity_id=idp.config.entityid)
38 |
39 | policy = NameIDPolicy(
40 | format=NAMEID_FORMAT_TRANSIENT, sp_name_qualifier="urn:mace:swamid:junk", allow_create="true"
41 | )
42 |
43 | nameid = NameID(format=NAMEID_FORMAT_TRANSIENT, text="foobar")
44 |
45 | mid, nmr = sp.create_name_id_mapping_request(policy, nameid, destination)
46 |
47 | print(nmr)
48 |
49 | args = sp.use_soap(nmr, destination)
50 |
51 | # ------- IDP ------------
52 |
53 | req = idp.parse_name_id_mapping_request(args["data"], binding)
54 |
55 | in_response_to = req.message.id
56 | name_id = NameID(format=NAMEID_FORMAT_PERSISTENT, text="foobar")
57 |
58 | idp_response = idp.create_name_id_mapping_response(name_id, in_response_to=in_response_to)
59 |
60 | print(idp_response)
61 |
62 | ht_args = sp.use_soap(idp_response)
63 |
64 | # ------- SP ------------
65 |
66 | _resp = sp.parse_name_id_mapping_request_response(ht_args["data"], binding)
67 |
68 | print(_resp.response)
69 |
70 | r_name_id = _resp.response.name_id
71 |
72 | assert r_name_id.format == NAMEID_FORMAT_PERSISTENT
73 | assert r_name_id.text == "foobar"
74 |
--------------------------------------------------------------------------------
/tests/test_67_manage_name_id.py:
--------------------------------------------------------------------------------
1 | from contextlib import closing
2 |
3 | from saml2 import BINDING_SOAP
4 | from saml2.client import Saml2Client
5 | from saml2.saml import NAMEID_FORMAT_TRANSIENT
6 | from saml2.saml import NameID
7 | from saml2.samlp import NewID
8 | from saml2.server import Server
9 |
10 |
11 | __author__ = "rolandh"
12 |
13 |
14 | def test_basic():
15 | sp = Saml2Client(config_file="servera_conf")
16 | with closing(Server(config_file="idp_all_conf")) as idp:
17 | # -------- @SP ------------
18 | binding, destination = sp.pick_binding("manage_name_id_service", entity_id=idp.config.entityid)
19 |
20 | nameid = NameID(format=NAMEID_FORMAT_TRANSIENT, text="foobar")
21 | newid = NewID(text="Barfoo")
22 |
23 | mid, mreq = sp.create_manage_name_id_request(destination, name_id=nameid, new_id=newid)
24 |
25 | print(mreq)
26 | rargs = sp.apply_binding(binding, f"{mreq}", destination, "")
27 |
28 | # --------- @IDP --------------
29 |
30 | _req = idp.parse_manage_name_id_request(rargs["data"], binding)
31 |
32 | print(_req.message)
33 |
34 | assert mid == _req.message.id
35 |
36 |
37 | def test_flow():
38 | sp = Saml2Client(config_file="servera_conf")
39 | with closing(Server(config_file="idp_all_conf")) as idp:
40 | binding, destination = sp.pick_binding("manage_name_id_service", entity_id=idp.config.entityid)
41 |
42 | nameid = NameID(format=NAMEID_FORMAT_TRANSIENT, text="foobar")
43 | newid = NewID(text="Barfoo")
44 |
45 | mid, midq = sp.create_manage_name_id_request(destination, name_id=nameid, new_id=newid)
46 |
47 | print(midq)
48 | rargs = sp.apply_binding(binding, f"{midq}", destination, "")
49 |
50 | # --------- @IDP --------------
51 |
52 | _req = idp.parse_manage_name_id_request(rargs["data"], binding)
53 |
54 | print(_req.message)
55 |
56 | mnir = idp.create_manage_name_id_response(_req.message, [binding])
57 |
58 | if binding != BINDING_SOAP:
59 | binding, destination = idp.pick_binding("manage_name_id_service", entity_id=sp.config.entityid)
60 | else:
61 | destination = ""
62 |
63 | respargs = idp.apply_binding(binding, f"{mnir}", destination, "")
64 |
65 | print(respargs)
66 |
67 | # ---------- @SP ---------------
68 |
69 | _response = sp.parse_manage_name_id_request_response(respargs["data"], binding)
70 |
71 | print(_response.response)
72 |
73 | assert _response.response.id == mnir.id
74 |
75 |
76 | if __name__ == "__main__":
77 | test_flow()
78 |
--------------------------------------------------------------------------------
/tests/test_70_redirect_signing.py:
--------------------------------------------------------------------------------
1 | from contextlib import closing
2 | from urllib.parse import parse_qs
3 |
4 | from pathutils import dotname
5 |
6 | from saml2 import BINDING_HTTP_REDIRECT
7 | from saml2.client import Saml2Client
8 | from saml2.config import SPConfig
9 | from saml2.pack import http_redirect_message
10 | from saml2.server import Server
11 | from saml2.sigver import SIG_RSA_SHA1
12 | from saml2.sigver import verify_redirect_signature
13 |
14 |
15 | __author__ = "rolandh"
16 |
17 |
18 | def list_values2simpletons(_dict):
19 | return {k: v[0] for k, v in _dict.items()}
20 |
21 |
22 | def test():
23 | with closing(Server(config_file=dotname("idp_all_conf"))) as idp:
24 | conf = SPConfig()
25 | conf.load_file(dotname("servera_conf"))
26 | sp = Saml2Client(conf)
27 |
28 | srvs = sp.metadata.single_sign_on_service(idp.config.entityid, BINDING_HTTP_REDIRECT)
29 |
30 | destination = srvs[0]["location"]
31 | req_id, req = sp.create_authn_request(destination, id="id1")
32 |
33 | info = http_redirect_message(
34 | req,
35 | destination,
36 | relay_state="RS",
37 | typ="SAMLRequest",
38 | sigalg=SIG_RSA_SHA1,
39 | sign=True,
40 | backend=sp.sec.sec_backend,
41 | )
42 |
43 | verified_ok = False
44 |
45 | for param, val in info["headers"]:
46 | if param == "Location":
47 | _dict = parse_qs(val.split("?")[1])
48 | _certs = idp.metadata.certs(sp.config.entityid, "any", "signing")
49 | for cert in _certs:
50 | if verify_redirect_signature(list_values2simpletons(_dict), sp.sec.sec_backend, cert[1]):
51 | verified_ok = True
52 |
53 | assert verified_ok
54 |
55 |
56 | if __name__ == "__main__":
57 | test()
58 |
--------------------------------------------------------------------------------
/tests/test_71_authn_request.py:
--------------------------------------------------------------------------------
1 | from contextlib import closing
2 |
3 | from saml2.client import Saml2Client
4 | from saml2.saml import AuthnContextClassRef
5 | from saml2.server import Server
6 |
7 |
8 | def test_authn_request_with_acs_by_index():
9 | # ACS index and location from SP metadata in servera.xml.
10 | ACS_INDEX = "4"
11 | ACS_LOCATION = "http://lingon.catalogix.se:8087/another/path"
12 |
13 | # Create SP using the configuration found in servera_conf.py.
14 | sp = Saml2Client(config_file="servera_conf")
15 |
16 | # Generate an authn request object that uses AssertionConsumerServiceIndex
17 | # instead of AssertionConsumerServiceURL. The index with label ACS_INDEX
18 | # exists in the SP metadata in servera.xml.
19 | request_id, authn_request = sp.create_authn_request(sp.config.entityid, assertion_consumer_service_index=ACS_INDEX)
20 |
21 | assert authn_request.requested_authn_context.authn_context_class_ref == [
22 | AuthnContextClassRef(accr)
23 | for accr in sp.config.getattr("requested_authn_context").get("authn_context_class_ref")
24 | ]
25 | assert authn_request.requested_authn_context.comparison == (
26 | sp.config.getattr("requested_authn_context").get("comparison")
27 | )
28 |
29 | # Make sure the authn_request contains AssertionConsumerServiceIndex.
30 | acs_index = getattr(authn_request, "assertion_consumer_service_index", None)
31 | assert acs_index == ACS_INDEX
32 |
33 | # Create IdP.
34 | with closing(Server(config_file="idp_all_conf")) as idp:
35 | # Ask the IdP to pick out the binding and destination from the
36 | # authn_request.
37 | binding, destination = idp.pick_binding("assertion_consumer_service", request=authn_request)
38 |
39 | # Make sure the IdP pick_binding method picks the correct location
40 | # or destination based on the ACS index in the authn request.
41 | assert destination == ACS_LOCATION
42 |
--------------------------------------------------------------------------------
/tests/test_72_eptid.py:
--------------------------------------------------------------------------------
1 | from saml2.eptid import Eptid
2 | from saml2.eptid import EptidShelve
3 |
4 |
5 | __author__ = "rolandh"
6 |
7 |
8 | def test_eptid():
9 | edb = Eptid("secret")
10 | e1 = edb.get("idp_entity_id", "sp_entity_id", "user_id", "some other data")
11 | print(e1)
12 | assert e1.startswith("idp_entity_id!sp_entity_id!")
13 | e2 = edb.get("idp_entity_id", "sp_entity_id", "user_id", "some other data")
14 | assert e1 == e2
15 |
16 | e3 = edb.get("idp_entity_id", "sp_entity_id", "user_2", "some other data")
17 | print(e3)
18 | assert e1 != e3
19 |
20 | e4 = edb.get("idp_entity_id", "sp_entity_id2", "user_id", "some other data")
21 | assert e4 != e1
22 | assert e4 != e3
23 |
24 |
25 | def test_eptid_shelve():
26 | edb = EptidShelve("secret", "eptid.db")
27 | e1 = edb.get("idp_entity_id", "sp_entity_id", "user_id", "some other data")
28 | print(e1)
29 | assert e1.startswith("idp_entity_id!sp_entity_id!")
30 | e2 = edb.get("idp_entity_id", "sp_entity_id", "user_id", "some other data")
31 | assert e1 == e2
32 |
33 | e3 = edb.get("idp_entity_id", "sp_entity_id", "user_2", "some other data")
34 | print(e3)
35 | assert e1 != e3
36 |
37 | e4 = edb.get("idp_entity_id", "sp_entity_id2", "user_id", "some other data")
38 | assert e4 != e1
39 | assert e4 != e3
40 |
41 |
42 | if __name__ == "__main__":
43 | test_eptid_shelve()
44 |
--------------------------------------------------------------------------------
/tests/test_82_pefim.py:
--------------------------------------------------------------------------------
1 | from pathutils import full_path
2 |
3 | from saml2 import config
4 | from saml2 import element_to_extension_element
5 | from saml2 import extension_elements_to_elements
6 | from saml2 import saml
7 | from saml2 import xmldsig as ds
8 | from saml2.client import Saml2Client
9 | from saml2.extension import pefim
10 | from saml2.extension.pefim import SPCertEnc
11 | from saml2.samlp import Extensions
12 | from saml2.samlp import authn_request_from_string
13 | from saml2.sigver import read_cert_from_file
14 |
15 |
16 | __author__ = "roland"
17 |
18 | conf = config.SPConfig()
19 | conf.load_file("server_conf")
20 | client = Saml2Client(conf)
21 |
22 | # place a certificate in an authn request
23 | cert = read_cert_from_file(full_path("test.pem"))
24 |
25 | spcertenc = SPCertEnc(x509_data=ds.X509Data(x509_certificate=ds.X509Certificate(text=cert)))
26 |
27 | extensions = Extensions(extension_elements=[element_to_extension_element(spcertenc)])
28 |
29 | req_id, req = client.create_authn_request(
30 | "http://www.example.com/sso",
31 | "urn:mace:example.com:it:tek",
32 | nameid_format=saml.NAMEID_FORMAT_PERSISTENT,
33 | message_id="666",
34 | extensions=extensions,
35 | )
36 |
37 |
38 | print(req)
39 |
40 | # Get a certificate from an authn request
41 |
42 | xml = f"{req}"
43 |
44 | parsed = authn_request_from_string(xml)
45 |
46 | _elem = extension_elements_to_elements(parsed.extensions.extension_elements, [pefim, ds])
47 |
48 | assert len(_elem) == 1
49 | _spcertenc = _elem[0]
50 | _cert = _spcertenc.key_info[0].x509_data[0].x509_certificate.text
51 | assert cert == _cert
52 |
--------------------------------------------------------------------------------
/tests/test_83_md_extensions.py:
--------------------------------------------------------------------------------
1 | from saml2 import create_class_from_xml_string as parse_str_as
2 | from saml2.config import Config
3 | from saml2.extension.sp_type import SPType
4 | from saml2.metadata import Attribute
5 | from saml2.metadata import entity_descriptor
6 |
7 |
8 | class TestMDExt:
9 | def test_sp_type_true(self):
10 | fil = "sp_mdext_conf.py"
11 | cnf = Config().load_file(fil)
12 | ed = entity_descriptor(cnf)
13 |
14 | assert ed.spsso_descriptor.extensions
15 | assert len(ed.spsso_descriptor.extensions.extension_elements) == 3
16 | assert ed.extensions
17 | assert len(ed.extensions.extension_elements) > 1
18 | assert any(e.tag is SPType.c_tag for e in ed.extensions.extension_elements)
19 |
20 | def test_sp_type_false(self):
21 | fil = "sp_mdext_conf.py"
22 | cnf = Config().load_file(fil)
23 | cnf.setattr("sp", "sp_type_in_metadata", False)
24 | ed = entity_descriptor(cnf)
25 |
26 | assert all(e.tag is not SPType.c_tag for e in ed.extensions.extension_elements)
27 |
28 | def test_entity_attributes(self):
29 | fil = "sp_mdext_conf.py"
30 | cnf = Config().load_file(fil)
31 | ed = entity_descriptor(cnf)
32 |
33 | entity_attributes = next(e for e in ed.extensions.extension_elements if e.tag == "EntityAttributes")
34 | attributes = [parse_str_as(Attribute, e.to_string()) for e in entity_attributes.children]
35 | assert all(
36 | a.name
37 | in [
38 | "urn:oasis:names:tc:SAML:profiles:subject-id:req",
39 | "somename",
40 | ]
41 | for a in attributes
42 | )
43 |
44 | import saml2.attribute_converter
45 |
46 | attrc = saml2.attribute_converter.ac_factory()
47 |
48 | import saml2.mdstore
49 |
50 | mds = saml2.mdstore.MetadataStore(attrc, cnf)
51 |
52 | mds.load("inline", ed.to_string())
53 | entityid = ed.entity_id
54 | entity_attributes = mds.entity_attributes(entityid)
55 | assert entity_attributes == {
56 | "urn:oasis:names:tc:SAML:profiles:subject-id:req": ["any"],
57 | "somename": ["x", "y", "z"],
58 | }
59 |
--------------------------------------------------------------------------------
/tests/test_88_nsprefix.py:
--------------------------------------------------------------------------------
1 | from saml2 import BINDING_HTTP_POST
2 | from saml2 import config
3 | from saml2 import saml
4 | from saml2 import samlp
5 | from saml2.client import Saml2Client
6 | from saml2.saml import NAMEID_FORMAT_TRANSIENT
7 |
8 |
9 | __author__ = "roland"
10 |
11 |
12 | def test_nsprefix():
13 | status_message = samlp.StatusMessage()
14 | status_message.text = "OK"
15 |
16 | txt = f"{status_message}"
17 |
18 | assert "ns0:StatusMessage" in txt
19 |
20 | status_message.register_prefix({"saml2": saml.NAMESPACE, "saml2p": samlp.NAMESPACE})
21 |
22 | txt = f"{status_message}"
23 |
24 | assert "saml2p:StatusMessage" in txt
25 |
26 |
27 | def test_nsprefix2():
28 | conf = config.SPConfig()
29 | conf.load_file("servera_conf")
30 | client = Saml2Client(conf)
31 |
32 | selected_idp = "urn:mace:example.com:saml:roland:idp"
33 |
34 | destination = client._sso_location(selected_idp, BINDING_HTTP_POST)
35 |
36 | reqid, req = client.create_authn_request(
37 | destination,
38 | nameid_format=NAMEID_FORMAT_TRANSIENT,
39 | nsprefix={"saml2": saml.NAMESPACE, "saml2p": samlp.NAMESPACE},
40 | )
41 |
42 | txt = f"{req}"
43 |
44 | assert "saml2p:AuthnRequest" in txt
45 | assert "saml2:Issuer" in txt
46 |
47 |
48 | if __name__ == "__main__":
49 | test_nsprefix2()
50 |
--------------------------------------------------------------------------------
/tests/test_92_aes.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | from saml2.cryptography.symmetric import AESCipher
4 |
5 |
6 | class TestAES:
7 | def test_aes_defaults(self):
8 | original_msg = b"ToBeOrNotTobe W.S."
9 | key = os.urandom(16)
10 | aes = AESCipher(key)
11 |
12 | encrypted_msg = aes.encrypt(original_msg)
13 | decrypted_msg = aes.decrypt(encrypted_msg)
14 | assert decrypted_msg == original_msg
15 |
16 | def test_aes_128_cbc(self):
17 | original_msg = b"ToBeOrNotTobe W.S."
18 | key = os.urandom(16)
19 | aes = AESCipher(key)
20 | alg = "aes_128_cbc"
21 |
22 | encrypted_msg = aes.encrypt(original_msg, alg=alg)
23 | decrypted_msg = aes.decrypt(encrypted_msg, alg=alg)
24 | assert decrypted_msg == original_msg
25 |
26 | def test_aes_128_cfb(self):
27 | original_msg = b"ToBeOrNotTobe W.S."
28 | key = os.urandom(16)
29 | aes = AESCipher(key)
30 | alg = "aes_128_cfb"
31 |
32 | encrypted_msg = aes.encrypt(original_msg, alg=alg)
33 | decrypted_msg = aes.decrypt(encrypted_msg, alg=alg)
34 | assert decrypted_msg == original_msg
35 |
36 | def test_aes_192_cbc(self):
37 | original_msg = b"ToBeOrNotTobe W.S."
38 | key = os.urandom(24)
39 | aes = AESCipher(key)
40 | alg = "aes_192_cbc"
41 |
42 | encrypted_msg = aes.encrypt(original_msg, alg=alg)
43 | decrypted_msg = aes.decrypt(encrypted_msg, alg=alg)
44 | assert decrypted_msg == original_msg
45 |
46 | def test_aes_192_cfb(self):
47 | original_msg = b"ToBeOrNotTobe W.S."
48 | key = os.urandom(24)
49 | aes = AESCipher(key)
50 | alg = "aes_192_cfb"
51 |
52 | encrypted_msg = aes.encrypt(original_msg, alg=alg)
53 | decrypted_msg = aes.decrypt(encrypted_msg, alg=alg)
54 | assert decrypted_msg == original_msg
55 |
56 | def test_aes_256_cbc(self):
57 | original_msg = b"ToBeOrNotTobe W.S."
58 | key = os.urandom(32)
59 | aes = AESCipher(key)
60 | alg = "aes_256_cbc"
61 |
62 | encrypted_msg = aes.encrypt(original_msg, alg=alg)
63 | decrypted_msg = aes.decrypt(encrypted_msg, alg=alg)
64 | assert decrypted_msg == original_msg
65 |
66 | def test_aes_256_cfb(self):
67 | original_msg = b"ToBeOrNotTobe W.S."
68 | key = os.urandom(32)
69 | aes = AESCipher(key)
70 | alg = "aes_256_cfb"
71 |
72 | encrypted_msg = aes.encrypt(original_msg, alg=alg)
73 | decrypted_msg = aes.decrypt(encrypted_msg, alg=alg)
74 | assert decrypted_msg == original_msg
75 |
--------------------------------------------------------------------------------
/tests/test_chain.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIICsDCCAhmgAwIBAgIJAJrzqSSwmDY9MA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
3 | BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
4 | aWRnaXRzIFB0eSBMdGQwHhcNMDkxMDA2MTk0OTQxWhcNMDkxMTA1MTk0OTQxWjBF
5 | MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50
6 | ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB
7 | gQDJg2cms7MqjniT8Fi/XkNHZNPbNVQyMUMXE9tXOdqwYCA1cc8vQdzkihscQMXy
8 | 3iPw2cMggBu6gjMTOSOxECkuvX5ZCclKr8pXAJM5cY6gVOaVO2PdTZcvDBKGbiaN
9 | efiEw5hnoZomqZGp8wHNLAUkwtH9vjqqvxyS/vclc6k2ewIDAQABo4GnMIGkMB0G
10 | A1UdDgQWBBRePsKHKYJsiojE78ZWXccK9K4aJTB1BgNVHSMEbjBsgBRePsKHKYJs
11 | iojE78ZWXccK9K4aJaFJpEcwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUt
12 | U3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZIIJAJrzqSSw
13 | mDY9MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAJSrKOEzHO7TL5cy6
14 | h3qh+3+JAk8HbGBW+cbX6KBCAw/mzU8flK25vnWwXS3dv2FF3Aod0/S7AWNfKib5
15 | U/SA9nJaz/mWeF9S0farz9AQFc8/NSzAzaVq7YbM4F6f6N2FRl7GikdXRCed45j6
16 | mrPzGzk3ECbupFnqyREH3+ZPSdk=
17 | -----END CERTIFICATE-----
18 | -----BEGIN CERTIFICATE-----
19 | MIIDnzCCAoegAwIBAgINSWITCHaai+Root+CAzANBgkqhkiG9w0BAQUFADBrMQsw
20 | CQYDVQQGEwJDSDFAMD4GA1UEChM3U3dpdGNoIC0gVGVsZWluZm9ybWF0aWtkaWVu
21 | c3RlIGZ1ZXIgTGVocmUgdW5kIEZvcnNjaHVuZzEaMBgGA1UEAxMRU1dJVENIYWFp
22 | IFJvb3QgQ0EwHhcNMDgwNTE1MDYzMDAwWhcNMjgwNTE1MDYyOTU5WjBrMQswCQYD
23 | VQQGEwJDSDFAMD4GA1UEChM3U3dpdGNoIC0gVGVsZWluZm9ybWF0aWtkaWVuc3Rl
24 | IGZ1ZXIgTGVocmUgdW5kIEZvcnNjaHVuZzEaMBgGA1UEAxMRU1dJVENIYWFpIFJv
25 | b3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDUSWbn/rhWew/s
26 | LJRyciyRKDGyFXSgiDO/EohYuZLw6EAKLLlhZorNtEHQbbn0Oo13S33MclHMvGWT
27 | KJM0u1hG+6gLy78EPmJbqAE1Uv23wVEH4SX0VJfl3JVqIebiAH/CjuLubgMUspDI
28 | jOdQHNLS7pthTbm7Tgh7zMsiLPyMTZJep5CGbqv8NoK6bMaF0Z+Bt7e1JRlhHFCV
29 | iJJaR/+hfpzLsJ8NWVivvrpRGaGJ1XR+9FGsTkjNdMCirNJJZ6XvUOe5w7pHSd9M
30 | cppFP0eyLs02AMzMXI4iz6PK/w3EdzXGXpK+gSgvLxWYct4xHpv1e2NXhNgdJOSN
31 | 9ra/wJLVAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEG
32 | MB0GA1UdDgQWBBTpmuIGWOsP14EDXVyXubG1k307hDANBgkqhkiG9w0BAQUFAAOC
33 | AQEAMV/eIW6pFB+mbk7rD7hUPTWDRaoca3kHqmFGFnHfuY8+c0/Mqjh8Y/jyX1yb
34 | f58crTSWrbyGbUZ3oxDGQ34tuZSkmeR32NqryiX3sP5qlNSozVguQKt8o4vhS1Qe
35 | WPsXALs3em2pdKuIGSOpbuDnopPcmU2g5Zi2R5P7qpKDKAKtNUEwV+LW7GBMEksO
36 | Nj7BFXk4AFBFBijaYJGgHmoKSImVgeNIvsV+BSv5HJ4q6vcxfnwuvvGHM0AGphYO
37 | 6f5qtHMUgvAblI8M/2QsBgethaGrirtKJ3aCRLdaR2R1QfaGRpck/Ron5/MpMxiJ
38 | wLT8YlW/zjx2yNABhPSAjfzeMw==
39 | -----END CERTIFICATE-----
40 |
--------------------------------------------------------------------------------
/tests/test_chain_with_linebreaks.pem:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | -----BEGIN CERTIFICATE-----
8 | MIICsDCCAhmgAwIBAgIJAJrzqSSwmDY9MA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
9 | BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
10 | aWRnaXRzIFB0eSBMdGQwHhcNMDkxMDA2MTk0OTQxWhcNMDkxMTA1MTk0OTQxWjBF
11 | MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50
12 | ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB
13 | gQDJg2cms7MqjniT8Fi/XkNHZNPbNVQyMUMXE9tXOdqwYCA1cc8vQdzkihscQMXy
14 | 3iPw2cMggBu6gjMTOSOxECkuvX5ZCclKr8pXAJM5cY6gVOaVO2PdTZcvDBKGbiaN
15 | efiEw5hnoZomqZGp8wHNLAUkwtH9vjqqvxyS/vclc6k2ewIDAQABo4GnMIGkMB0G
16 | A1UdDgQWBBRePsKHKYJsiojE78ZWXccK9K4aJTB1BgNVHSMEbjBsgBRePsKHKYJs
17 | iojE78ZWXccK9K4aJaFJpEcwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUt
18 | U3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZIIJAJrzqSSw
19 | mDY9MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAJSrKOEzHO7TL5cy6
20 | h3qh+3+JAk8HbGBW+cbX6KBCAw/mzU8flK25vnWwXS3dv2FF3Aod0/S7AWNfKib5
21 | U/SA9nJaz/mWeF9S0farz9AQFc8/NSzAzaVq7YbM4F6f6N2FRl7GikdXRCed45j6
22 | mrPzGzk3ECbupFnqyREH3+ZPSdk=
23 | -----END CERTIFICATE-----
24 |
25 |
26 |
27 | -----BEGIN CERTIFICATE-----
28 | MIIDnzCCAoegAwIBAgINSWITCHaai+Root+CAzANBgkqhkiG9w0BAQUFADBrMQsw
29 | CQYDVQQGEwJDSDFAMD4GA1UEChM3U3dpdGNoIC0gVGVsZWluZm9ybWF0aWtkaWVu
30 | c3RlIGZ1ZXIgTGVocmUgdW5kIEZvcnNjaHVuZzEaMBgGA1UEAxMRU1dJVENIYWFp
31 | IFJvb3QgQ0EwHhcNMDgwNTE1MDYzMDAwWhcNMjgwNTE1MDYyOTU5WjBrMQswCQYD
32 | VQQGEwJDSDFAMD4GA1UEChM3U3dpdGNoIC0gVGVsZWluZm9ybWF0aWtkaWVuc3Rl
33 | IGZ1ZXIgTGVocmUgdW5kIEZvcnNjaHVuZzEaMBgGA1UEAxMRU1dJVENIYWFpIFJv
34 | b3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDUSWbn/rhWew/s
35 | LJRyciyRKDGyFXSgiDO/EohYuZLw6EAKLLlhZorNtEHQbbn0Oo13S33MclHMvGWT
36 | KJM0u1hG+6gLy78EPmJbqAE1Uv23wVEH4SX0VJfl3JVqIebiAH/CjuLubgMUspDI
37 | jOdQHNLS7pthTbm7Tgh7zMsiLPyMTZJep5CGbqv8NoK6bMaF0Z+Bt7e1JRlhHFCV
38 | iJJaR/+hfpzLsJ8NWVivvrpRGaGJ1XR+9FGsTkjNdMCirNJJZ6XvUOe5w7pHSd9M
39 | cppFP0eyLs02AMzMXI4iz6PK/w3EdzXGXpK+gSgvLxWYct4xHpv1e2NXhNgdJOSN
40 | 9ra/wJLVAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEG
41 | MB0GA1UdDgQWBBTpmuIGWOsP14EDXVyXubG1k307hDANBgkqhkiG9w0BAQUFAAOC
42 | AQEAMV/eIW6pFB+mbk7rD7hUPTWDRaoca3kHqmFGFnHfuY8+c0/Mqjh8Y/jyX1yb
43 | f58crTSWrbyGbUZ3oxDGQ34tuZSkmeR32NqryiX3sP5qlNSozVguQKt8o4vhS1Qe
44 | WPsXALs3em2pdKuIGSOpbuDnopPcmU2g5Zi2R5P7qpKDKAKtNUEwV+LW7GBMEksO
45 | Nj7BFXk4AFBFBijaYJGgHmoKSImVgeNIvsV+BSv5HJ4q6vcxfnwuvvGHM0AGphYO
46 | 6f5qtHMUgvAblI8M/2QsBgethaGrirtKJ3aCRLdaR2R1QfaGRpck/Ron5/MpMxiJ
47 | wLT8YlW/zjx2yNABhPSAjfzeMw==
48 | -----END CERTIFICATE-----
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/tests/vo_metadata.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
8 |
10 |
11 | urn:mace:example.com:saml:aa
12 |
13 |
14 | urn:mace:example.com:saml:idp
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------