PySAML2

repository·master·Indexed 20 days ago

https://github.com/identitypython/pysaml2

A pure Python implementation of the SAML Version 2 Standard (version 7.5.4), enabling developers to build both Service Providers (SP) and Identity Providers (IdP). It provides components for authentication and attribute aggregation, includes extensions for various web frameworks, and requires the external xmlsec binary to function.

Tokens
42.2K
Snippets
104
Records
143
Agent score
68%

What's inside pysaml2

  1. Overview of PySAML2 capabilities

    master

    PySAML2 is a pure Python implementation of the SAML Version 2 Standard. It provides the necessary components to build both:

    • SAML2 Service Providers (SP)
    • SAML2 Identity Providers (IdP)

    While originally designed for WSGI environments, it includes extensions for use with other web frameworks. The distribution includes practical examples for both Service Provider and Identity Provider implementations.

  2. What is PySAML2 and how is it used?

    master

    PySAML2 is a pure Python implementation of the SAML 2.0 standard, designed for building both Service Providers (SP) and Identity Providers (IdP).

    Because SAML involves exchanging authentication and authorization data between security domains, PySAML2 is primarily designed as middleware. While it can be used in non-WSGI environments (for example, performing SOAP-based AttributeQuery calls to an AttributeAuthority), most common use cases require running PySAML2 behind a web server to handle the responses from an Identity Provider.

  3. Define IdP/AA policies for specific services

    master

    The policy configuration allows an IdP or AA to behave differently based on the requesting service (SP).

    Policies are looked up in this order:

    1. The SP's entityID.
    2. The Registration Authority name (if present in SP metadata).
    3. The default key.

    If no default is provided and only specific SP IDs are listed, the server will only accept connections from those specified SPs.

    Key policy options:

    • lifetime: Maximum time (in minutes/hours) before information is considered stale (maps to NotOnOrAfter).
    • attribute_restrictions: A dictionary mapping attribute names to allowed values (can be None for all values, or a regex string/list for filtering).
    • name_form: The name-form used when sending assertions.
    • nameid_format: The NameID format (defaults to urn:oasis:names:tc:SAML:2.0:nameid-format:transient).
    • entity_categories: List of entity categories to apply.
    • sign: Choices: "response", "assertion", "on_demand".
    "service": {
            "idp": {
                "policy": {
                    "urn:mace:example.com:saml:roland:sp": {
                        "lifetime": {"minutes": 5},
                        "attribute_restrictions": {
                            "givenName": None,
                            "surName": None,
                        },
                    },
                    "http://www.swamid.se/": {
                        "attribute_restrictions": {
                            "givenName": None,
                        },
                    },
                    "default": {
                        "lifetime": {"minutes":15},
                        "attribute_restrictions": None,
                        "name_form": "urn:oasis:names:tc:SAML:2.0:attrname-format:uri",
                        "entity_categories": [
                            "edugain",
                        ],
                    },
                }
            }
        }
  4. How sp_test components and objects work together

    master

    The sp_test tool is organized into several hierarchical abstractions that manage the lifecycle of a SAML2 test session:

    • Client (sp_test/__init__.py): The top-level orchestrator. It reads configuration files (using the json_config key) and command-line arguments, initializes the test driver IDP, creates a Conversation, and executes the test sequence via .do_sequence_and_tests().
    • Conversation (sp_test/base.py): Manages the state and execution of a specific test session.
    • Operation (oper): A named test scenario (e.g., 'Basic Login test'). It contains a sequence of flows and optional pre and post tests.
    • Flow: A tuple of classes representing a SAML request-response pair. A standard solicited authentication flow consists of 4 elements:
      • flow[0]: An Operation (e.g., handling discovery or WAYF).
      • flow[1]: A Request class (processes the authentication request).
      • flow[2]: A Response class (sends the authentication response).
      • flow[3]: An optional Check class (e.g., verifying error status).
    • Test: A class executed during an operation. These can be run pre (before the sequence), post (after the sequence), or mid (between a SAML request and response, such as VerifyIfRequestIsSigned).
    • Check: An optional class executed upon receiving the SP's HTTP response(s). It writes structured reports to conv.test_output. Checks can validate expected errors (like invalid signatures) without raising exceptions.
    • Interaction: An automation mechanism that searches responses for constants to simulate human user interface interactions.
  5. Understand the usage model of PySAML2

    master

    PySAML2 is primarily designed as middleware for WSGI applications. While it can be used in non-WSGI environments (for example, performing AttributeQuery operations over SOAP), its core functionality is intended to facilitate SAML interactions within a web server context.

    Regardless of whether you are using PySAML2 in a WSGI or non-WSGI environment, the configuration process remains the same.

  6. How a SAML2 Service Provider (SP) handles identity

    master

    A Service Provider (SP) in PySAML2 handles authentication and attribute aggregation. When integrated with repoze.who, the SP follows a specific pattern for identity storage:

    1. The SP gathers information from the Identity Provider (IdP) and any Attribute Authorities (AA).
    2. This information is placed in the WSGI environment dictionary under the key environ["repoze.who.identity"].
    3. The identity data is stored as a dictionary under the sub-key 'user'.

    Example structure of environ["repoze.who.identity"]:

    {
        'user': {
            'attribute_name': 'attribute_value',
            # If a friendly name exists for an attribute, it is used as the key.
        }
    }
  7. Configure Entity Categories for Metadata or Attribute Filtering

    master

    PySAML2 supports Entity Categories in two distinct ways depending on whether you want to include them in your metadata or use them to filter attributes released to Service Providers (SPs).

    1. Including Entity Categories in Metadata

    To generate EntityAttribute metadata elements, use the entity_category_support configuration option. This is useful for declaring which categories your entity belongs to (e.g., edugain.COCO).

    2. Using Entity Categories as Attribute Filters

    To use Entity Categories as a filter that controls which attributes are released to an SP, include the entity_categories option within your policy configuration. If an SP does not conform to the specified categories, the attributes will not be released.

    Note: Entity category definitions and attributes are located in src/saml2/entity_category/<registrar-of-entity-category>.py.

    # Example 1: Metadata configuration
    config = {
        'entity_category_support': [
            edugain.COCO, # "http://www.geant.net/uri/dataprotection-code-of-conduct/v1"
            refeds.RESEARCH_AND_SCHOLARSHIP,
        ],
        # ... other config
    }
    
    # Example 2: Policy configuration (Attribute Filtering)
    config = {
        "policy": {
          "default": {
            "lifetime": {"minutes": 15},
            "entity_categories": ["refeds"],
          }
        }
    }
  8. Set up the development environment

    master

    To set up a development environment for PySAML2, follow these steps:

    1. Manage Python versions: Use pyenv to install and enable supported Python versions (3.6, 3.7, 3.8, 3.9, 3.10).
    2. Manage dependencies: Use poetry to manage dependencies and virtual environments. It is recommended to install poetry using pipx.
    3. Install dependencies: Use poetry install with specific groups to include development, testing, coverage, and documentation tools.
    4. Activate environment: Use poetry shell to enter a shell with the virtual environment loaded, or simply use poetry run <command> to execute commands within the environment.
    # Install supported python versions using pyenv
    $ for v in 3.6 3.7 3.8 3.9 3.10; do pyenv install "${v}:latest"; done
    $ pyenv versions --bare | xargs pyenv local
    
    # Install development dependencies with poetry
    $ poetry install --with dev,test,coverage,docs --sync
    
    # Enter the virtual environment
    $ poetry shell
  9. Generate SAML2 Metadata

    master

    To allow an IdP to communicate with your SP, you must provide it with a metadata XML file. You can generate this using the make_metadata.py script found in the tools directory.

    Run the following command in the directory containing your sp_conf.py:

    make_metadata.py sp_conf.py > sp.xml
    make_metadata.py sp_conf.py > sp.xml
  10. Set up a simple SAML2 Identity Provider (IDP) example

    master

    The idp2 example provides a basic Identity Provider implementation with a static user definition. User attributes are managed in idp_user.py and passwords are defined in the PASSWD dictionary within idp.py.

    To set up this example:

    1. Prepare Configuration: Locate the idp_conf.py.example file in the [your path]/pysaml2/example/idp2 directory. Rename it to idp_conf.py.
    2. Generate Metadata: Create the SAML metadata XML file from your configuration using the make_metadata.py script.
    3. Run the IDP: Execute the idp.py script, passing the configuration filename (without the .py extension) as an argument.
    # 1. Rename the example config
    cp idp_conf.py.example idp_conf.py
    
    # 2. Generate metadata
    python make_metadata.py idp_conf.py > idp.xml
    
    # 3. Run the IDP (note: do not include .py extension in the argument)
    python idp.py idp_conf