跳转到主要内容

SAML Python工具包。使用此库将SAML支持添加到您的Python软件

项目描述

SAML Python工具包(兼容Python3)

Python package PyPI Downloads Coverage Status PyPi Version Python versions

使用此库将SAML支持添加到您的Python软件。忘记那些复杂的库,使用SAML工具社区提供的开源库。

此版本支持Python3。Python 2支持已于2020年1月1日弃用: python-saml

警告

版本1.16.X是支持Python2的最新版本,考虑其使用已弃用。1.17将不支持Python2。

版本1.13.0将sha256和rsa-sha256设置为默认算法

版本1.8.0将严格模式默认激活

python3-saml 升级到 1.5.0,这个版本包括防止 XEE 和 Xpath 注入的安全改进。

python3-saml 升级到 1.4.0,这个版本修复了 CVE-2017-11427 漏洞。

此版本还改变了计算指纹方法的工作方式,并期望输入格式化的 X.509 证书。

python3-saml 升级到 1.2.6,该版本增加了使用 defusedxml 的功能,可以防止 XEE 以及基于 XML滥用的其他攻击。(CVE-2017-9672)

python3-saml 升级到 >= 1.2.11.2.0 版本在签名验证过程中存在一个错误(当使用 wantAssertionsSignedwantMessagesSigned 时)。CVE-2016-1000251

1.2.0 版本包含一个安全补丁,包含额外的验证,可以防止签名包装攻击。

python3-saml < v1.2.0 是有漏洞的,允许签名包装!

安全指南

如果您认为您已发现此工具包中的安全漏洞,请通过电子邮件向维护者报告: sixto.martin.garcia+security@gmail.com

为什么要在我的软件中添加 SAML 支持?

SAML 是一种基于 XML 的标准,用于网络浏览器的单点登录,由 OASIS 安全服务技术委员会定义。该标准自 2002 年以来一直存在,但最近由于其优势而越来越受欢迎。

  • 可用性 - 从门户或内部网络一键访问、深度链接、消除密码和自动续期会话,使用户的生活更加便捷。
  • 安全性 - 基于强大的数字签名进行身份验证和完整性,SAML 是一种安全单点登录协议,世界上最大和最注重安全的企业的首选。
  • 速度 - SAML 很快。只需一个浏览器重定向即可安全地将用户签入应用程序。
  • 钓鱼预防 - 如果您没有应用程序的密码,您就不会被欺骗在伪造的登录页面上输入它。
  • 对 IT 友好 - SAML 简化了 IT 人员的生活,因为它集中了身份验证,提供了更大的可见性,并使目录集成更加容易。
  • 机会 - B2B 云供应商应支持 SAML 以促进其产品集成。

一般描述

SAML Python 工具包允许您将您的 Python 应用程序转换为 SP(服务提供商),它可以连接到 IdP(身份提供者)。

支持

  • SSO 和 SLO(SP 启动和 IdP 启动)。
  • 断言和 nameId 加密。
  • 断言签名。
  • 消息签名:AuthNRequestLogoutRequestLogoutResponses
  • 启用断言消费者服务端点。
  • 启用单一登出服务端点。
  • 发布 SP 元数据(可以签名)。

主要功能

  • saml2int - 实现了 SAML 2.0 网络浏览器 SSO 配置文件。
  • 无会话 - 忘记 SP 和最终应用程序之间常见的冲突,该工具包将委托最终应用程序中的会话。
  • 易于使用 - 程序员将被允许编写高级和低级编程,提供了两个易于使用的 API。
  • 经过测试 - 经过彻底测试。

安装

依赖关系

  • python 3.6
  • xmlsec XML 安全库的 Python 绑定。
  • lxml libxml2 和 libxslt 库的 Python 绑定。
  • isodate ISO 8601 日期/时间/持续时间解析器和格式化程序

查看 setup.py 文件以了解 python3-saml 所使用的库版本

代码

选项 1. 从 GitHub 下载

该工具包托管在 GitHub 上。您可以从中下载它

复制库的核心代码(src/onelogin/saml2 文件夹)并将其中的 setup.py 合并到 Python 应用程序中。(每个应用程序都有自己的结构,所以请花时间找到最佳位置的 Python SAML 工具包)。

选项 2:从 PyPI 下载

工具包托管在 PyPI,您可以在 https://pypi.python.org/pypi/python3-saml 找到 python3-saml

您可以通过执行以下命令来安装它:

$ pip install python3-saml

如果您想了解项目如何处理 Python 包,请查看此 指南 并审查此 示例项目

注意

为了避免 libxml2 库在 xmlseclxml 之间的版本不兼容,建议不要从二进制文件安装 lxml

可以通过执行以下命令来确保这一点:

$ pip install --force-reinstall --no-binary lxml lxml

安全警告

在生产环境中,必须将 strict 参数设置为 "true"。否则,您的环境不安全,将面临攻击。

在生产环境中,我们还强烈建议在设置中注册 IdP 证书而不是使用指纹方法。指纹是一种散列,因此最终可能容易受到碰撞攻击,这可能导致签名验证绕过。其他 SAML 工具包已弃用该机制,我们保留它以保持兼容性,并且还可以用于测试环境。

避免开放重定向攻击

一些实现使用 RelayState 参数作为在 SSO 和 SLO 成功时控制流程的方法。所以基本上,用户会被重定向到 RelayState 的值。

如果您正在使用 HTTP-Redirect 绑定上的签名验证,您将获得 RelayState 价值的完整性保证,否则,在 HTTP-POST 绑定中,您不能信任 RelayState,因此在执行验证之前,您需要验证其值属于受信任的预期 URL。

有关开放重定向的更多信息,请参阅 CWE-601

避免重放攻击

重放攻击基本上是尝试重复使用拦截的有效 SAML 消息来模拟 SAML 动作(SSO 或 SLO)。

SAML 消息具有有限的有效期(NotBefore,NotOnOrAfter),这使得此类攻击更难,但仍然可能。

为了避免它们,SP 可以保留已验证和处理的 SAML 消息或断言 ID 的列表。这些值只需要存储与 SAML 消息寿命相同的时间,所以我们不需要存储所有处理过的消息/断言 ID,但只需要最近的那些。

OneLogin_Saml2_Auth 类包含 get_last_request_idget_last_message_idget_last_assertion_id 方法来检索 ID

检查当前消息/断言的 ID 是否不存在于已处理的 ID 列表中,这将防止重放攻击。

入门指南

了解工具包

新的 SAML 工具包包含不同的文件夹(certslibdemo-djangodemo-flasktests)和一些文件。

让我们先描述它们

src

此文件夹包含工具包的核心,onelogin/saml2 文件夹包含在后续部分中描述的新版本类和方法。

demo-django

此文件夹包含一个 Django 项目,该项目将用作示例,以展示如何将 SAML 支持添加到 Django 框架。demo 是 Django 项目的主体文件夹(包含其 settings.pyviews.pyurls.py),templates 是项目的 Django 模板,saml 是一个文件夹,其中包含可用于存储 X.509 公钥和私钥以及 SAML 工具包设置的 certs 文件夹(settings.jsonadvanced_settings.json)。

有关证书的注意事项

SAML需要X.509证书来签名和加密如NameIDMessageAssertionMetadata等元素。

如果我们的环境需要签名或加密支持,证书文件夹可能包含SP将使用的X.509证书和私钥

  • sp.crt SP的公开证书
  • sp.key SP的私钥

或者我们也可以在设置文件中将这些数据提供在sp元素的x509certprivateKey JSON参数中。

有时我们可能需要在SP发布的元数据上签名,在这种情况下,我们可以使用之前提到的X.509证书或使用新的X.509证书:metadata.crtmetadata.key

如果你处于密钥轮换过程并想在服务提供者元数据上发布该X.509证书,请使用sp_new.crt

如果你想创建自签名证书,你可以在https://www.samltool.com/self_signed_certs.php服务中做,或者使用以下命令

openssl req -new -x509 -days 3652 -nodes -out sp.crt -keyout sp.key

demo-flask

此文件夹包含一个Flask项目,该项目将被用作演示,以展示如何将SAML支持添加到Flask框架。 index.py是包含所有代码的主要Flask文件,此文件使用存储在templates文件夹中的模板。在saml文件夹中我们找到了存储X.509公开和私钥以及SAML工具包设置的certs文件夹(settings.jsonadvanced_settings.json)。

demo_pyramid

此文件夹包含一个Pyramid项目,该项目将被用作演示,以展示如何将SAML支持添加到Pyramid Web Framework\_\_init__.py是配置应用及其路由的主要文件,views.py是处理所有逻辑和SAML处理的文件,模板存储在templates文件夹中。与其它两个演示相同,saml文件夹是相同的。

demo-tornado

此文件夹包含一个Tornado项目,该项目将被用作演示,以展示如何将SAML支持添加到Tornado框架。 views.py(及其settings.py)是包含所有代码的主要Flask文件,此文件使用存储在templates文件夹中的模板。在saml文件夹中我们找到了存储X.509公开和私钥以及SAML工具包设置(settings.jsonadvanced_settings.json)的certs文件夹。

需要python3.5(使用的是tornado 6.0.3)

setup.py

设置脚本是构建、分发和安装模块活动中的中心。更多信息请参阅https://pythonhosted.org/an_example_pypi_project/setuptools.html

tests

包含工具包的单元测试。

为了执行测试,你只需加载已经正确安装了工具包的虚拟环境

pip install -e ".[test]"

然后执行

python setup.py test

上一行将运行整个工具包的测试。你还可以运行特定模块的测试。要对认证模块进行测试,你需要执行以下操作

python setup.py test --test-suite tests.src.OneLogin.saml2_tests.auth_test.OneLogin_Saml2_Auth_Test

使用--test-suite参数你可以指定要测试的模块。你可以在tests/src/OneLogin/saml2_tests/找到所有模块及其类名。

工作原理

设置

首先我们需要配置工具包。SP的信息、IdP的信息,以及在某些情况下,配置高级安全问题,如签名和加密。

有两种方式提供设置信息

  • 使用一个settings.json文件,我们应该将其定位在任何文件夹中,但需要通过custom_base_path参数指定其路径。

  • 使用包含设置数据的JSON对象,并将其直接提供给类的构造函数(如果您的工具集集成需要证书,请记住将custom_base_path作为设置的一部分或作为构造函数的参数提供)。

在demo-django和demo-flask文件夹中,您将找到一个saml文件夹,其中包含一个certs文件夹和文件settings.jsonadvanced_settings.json。这些文件包含SAML工具集的设置。将它们复制到您的项目中并设置正确的值。

这是settings.json文件

{
    // If strict is True, then the Python Toolkit will reject unsigned
    // or unencrypted messages if it expects them to be signed or encrypted.
    // Also it will reject the messages if the SAML standard is not strictly
    // followed. Destination, NameId, Conditions ... are validated too.
    "strict": true,

    // Enable debug mode (outputs errors).
    "debug": true,

    // Service Provider Data that we are deploying.
    "sp": {
        // Identifier of the SP entity  (must be a URI)
        "entityId": "https://<sp_domain>/metadata/",
        // Specifies info about where and how the <AuthnResponse> message MUST be
        // returned to the requester, in this case our SP.
        "assertionConsumerService": {
            // URL Location where the <Response> from the IdP will be returned
            "url": "https://<sp_domain>/?acs",
            // SAML protocol binding to be used when returning the <Response>
            // message. SAML Toolkit supports this endpoint for the
            // HTTP-POST binding only.
            "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
        },
        // Specifies info about where and how the <Logout Request/Response> message MUST be sent.
        "singleLogoutService": {
            // URL Location where the <LogoutRequest> from the IdP will be sent (IdP-initiated logout, request)
            "url": "https://<sp_domain>/?sls",
            // URL Location where the <LogoutResponse> from the IdP will sent (SP-initiated logout, reply)
            // OPTIONAL: only specify if different from url parameter
            //"responseUrl": "https://<sp_domain>/?sls",
            // SAML protocol binding to be used when returning the <Response>
            // message. SAML Toolkit supports the HTTP-Redirect binding
            // only for this endpoint.
            "binding": "urn:oasis:names:tc:SAML:2.0:bindings:SOAP",
            "responseUrl": "https://<sp_domain>/?sls",
            // SAML protocol binding to be used with SP-initiated logout
            // It defaults 'sp.singleLogoutService.binding' if not specified but `responseUrl` is specified 
            "responseBinding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
        },
        // If you need to specify requested attributes, set a
        // attributeConsumingService. nameFormat, attributeValue and
        // friendlyName can be ommited. If more than 1 service is added, the 
        // index parameter is required. 
        "attributeConsumingServices": [{
                // Index is an integer which identifies the attributeConsumingService used
                // to the SP. 
                "index": "1",
                "serviceName": "SP test",
                "serviceDescription": "Test Service",
                "requestedAttributes": [
                    {
                        "name": "",
                        "isRequired": false,
                        "nameFormat": "",
                        "friendlyName": "",
                        "attributeValue": []
                    }
                ]
        }],
        // Specifies the constraints on the name identifier to be used to
        // represent the requested subject.
        // Take a look on src/onelogin/saml2/constants.py to see the NameIdFormat that are supported.
        "NameIDFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified",
        // Usually X.509 cert and privateKey of the SP are provided by files placed at
        // the certs folder. But we can also provide them with the following parameters
        "x509cert": "",
        "privateKey": ""

        /*
         * Key rollover
         * If you plan to update the SP X.509cert and privateKey
         * you can define here the new X.509cert and it will be
         * published on the SP metadata so Identity Providers can
         * read them and get ready for rollover.
         */
        // 'x509certNew': '',
    },

    // Identity Provider Data that we want connected with our SP.
    "idp": {
        // Identifier of the IdP entity  (must be a URI)
        "entityId": "https://app.onelogin.com/saml/metadata/<onelogin_connector_id>",
        // SSO endpoint info of the IdP. (Authentication Request protocol)
        "singleSignOnService": {
            // URL Target of the IdP where the Authentication Request Message
            // will be sent.
            "url": "https://app.onelogin.com/trust/saml2/http-post/sso/<onelogin_connector_id>",
            // SAML protocol binding to be used when returning the <Response>
            // message. SAML Toolkit supports the HTTP-Redirect binding
            // only for this endpoint.
            "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
        },
        // SLO endpoint info of the IdP.
        "singleLogoutService": {
            // URL Location where the <LogoutRequest> from the IdP will be sent (IdP-initiated logout)
            "url": "https://app.onelogin.com/trust/saml2/http-redirect/slo/<onelogin_connector_id>",
            // URL Location where the <LogoutResponse> from the IdP will sent (SP-initiated logout, reply)
            // OPTIONAL: only specify if different from url parameter
            "responseUrl": "https://app.onelogin.com/trust/saml2/http-redirect/slo_return/<onelogin_connector_id>",
            // SAML protocol binding to be used when returning the <Response>
            // message. SAML Toolkit supports the HTTP-Redirect binding
            // only for this endpoint.
            "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
        },
        // Public X.509 certificate of the IdP
        "x509cert": "<onelogin_connector_cert>"
        /*
         *  Instead of using the whole X.509cert you can use a fingerprint in order to
         *  validate a SAMLResponse (but you still need the X.509cert to validate LogoutRequest and LogoutResponse using the HTTP-Redirect binding).
         *  But take in mind that the algorithm for the fingerprint should be as strong as the algorithm in a normal certificate signature
	 *  (e.g. SHA256 or strong)
         *
         *  (openssl x509 -noout -fingerprint -in "idp.crt" to generate it,
         *  or add for example the -sha256 , -sha384 or -sha512 parameter)
         *
         *  If a fingerprint is provided, then the certFingerprintAlgorithm is required in order to
         *  let the toolkit know which algorithm was used.
         Possible values: sha1, sha256, sha384 or sha512
         *  'sha1' is the default value.
         *
         *  Notice that if you want to validate any SAML Message sent by the HTTP-Redirect binding, you
         *  will need to provide the whole X.509cert.
         */
        // "certFingerprint": "",
        // "certFingerprintAlgorithm": "sha1",

        /* In some scenarios the IdP uses different certificates for
         * signing/encryption, or is under key rollover phase and
         * more than one certificate is published on IdP metadata.
         * In order to handle that the toolkit offers that parameter.
         * (when used, 'X.509cert' and 'certFingerprint' values are
         * ignored).
         */
        // 'x509certMulti': {
        //      'signing': [
        //          '<cert1-string>'
        //      ],
        //      'encryption': [
        //          '<cert2-string>'
        //      ]
        // }
    }
}

除了必需的设置数据(idp,sp)外,还可以在advanced_settings.json中定义额外的设置

{
    // Security settings
    "security": {

        /** signatures and encryptions offered **/

        // Indicates that the nameID of the <samlp:logoutRequest> sent by this SP
        // will be encrypted.
        "nameIdEncrypted": false,

        // Indicates whether the <samlp:AuthnRequest> messages sent by this SP
        // will be signed.  [Metadata of the SP will offer this info]
        "authnRequestsSigned": false,

        // Indicates whether the <samlp:logoutRequest> messages sent by this SP
        // will be signed.
        "logoutRequestSigned": false,

        // Indicates whether the <samlp:logoutResponse> messages sent by this SP
        // will be signed.
        "logoutResponseSigned": false,

        /* Sign the Metadata
         false || true (use sp certs) || {
                                            "keyFileName": "metadata.key",
                                            "certFileName": "metadata.crt"
                                         }
        */
        "signMetadata": false,

        /** signatures and encryptions required **/

        // Indicates a requirement for the <samlp:Response>, <samlp:LogoutRequest>
        // and <samlp:LogoutResponse> elements received by this SP to be signed.
        "wantMessagesSigned": false,

        // Indicates a requirement for the <saml:Assertion> elements received by
        // this SP to be signed. [Metadata of the SP will offer this info]
        "wantAssertionsSigned": false,

        // Indicates a requirement for the <saml:Assertion>
        // elements received by this SP to be encrypted.
        "wantAssertionsEncrypted": false,

        // Indicates a requirement for the NameID element on the SAMLResponse
        // received by this SP to be present.
        "wantNameId": true,

        // Indicates a requirement for the NameID received by
        // this SP to be encrypted.
        "wantNameIdEncrypted": false,

        // Indicates a requirement for the AttributeStatement element
        "wantAttributeStatement": true,

        // Authentication context.
        // Set to false and no AuthContext will be sent in the AuthNRequest,
        // Set true or don't present this parameter and you will get an AuthContext 'exact' 'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport'
        // Set an array with the possible auth context values: array ('urn:oasis:names:tc:SAML:2.0:ac:classes:Password', 'urn:oasis:names:tc:SAML:2.0:ac:classes:X509'),
        "requestedAuthnContext": true,
	// Allows the authn comparison parameter to be set, defaults to 'exact' if the setting is not present.
        "requestedAuthnContextComparison": "exact",
        // Set to true to check that the AuthnContext(s) received match(es) the requested.
        "failOnAuthnContextMismatch": false,

        // In some environment you will need to set how long the published metadata of the Service Provider gonna be valid.
        // is possible to not set the 2 following parameters (or set to null) and default values will be set (2 days, 1 week)
        // Provide the desire TimeStamp, for example 2015-06-26T20:00:00Z
        "metadataValidUntil": null,
        // Provide the desire Duration, for example PT518400S (6 days)
        "metadataCacheDuration": null,

        // If enabled, URLs with single-label-domains will
        // be allowed and not rejected by the settings validator (Enable it under Docker/Kubernetes/testing env, not recommended on production)
        "allowSingleLabelDomains": false,

        // Algorithm that the toolkit will use on signing process. Options:
        //    'http://www.w3.org/2000/09/xmldsig#rsa-sha1'
        //    'http://www.w3.org/2000/09/xmldsig#dsa-sha1'
        //    'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256'
        //    'http://www.w3.org/2001/04/xmldsig-more#rsa-sha384'
        //    'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512'
        "signatureAlgorithm": "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256",

        // Algorithm that the toolkit will use on digest process. Options:
        //    'http://www.w3.org/2000/09/xmldsig#sha1'
        //    'http://www.w3.org/2001/04/xmlenc#sha256'
        //    'http://www.w3.org/2001/04/xmldsig-more#sha384'
        //    'http://www.w3.org/2001/04/xmlenc#sha512'
        'digestAlgorithm': "http://www.w3.org/2001/04/xmlenc#sha256",

        // Specify if you want the SP to view assertions with duplicated Name or FriendlyName attributes to be valid
        // Defaults to false if not specified
        'allowRepeatAttributeName': false,

        // If the toolkit receive a message signed with a
        // deprecated algorithm (defined at the constant class)
        // will raise an error and reject the message
        "rejectDeprecatedAlgorithm": true
    },

    // Contact information template, it is recommended to suply a
    // technical and support contacts.
    "contactPerson": {
        "technical": {
            "givenName": "technical_name",
            "emailAddress": "technical@example.com"
        },
        "support": {
            "givenName": "support_name",
            "emailAddress": "support@example.com"
        }
    },

    // Organization information template, the info in en_US lang is
    // recommended, add more if required.
    "organization": {
        "en-US": {
            "name": "sp_test",
            "displayname": "SP test",
            "url": "http://sp.example.com"
        }
    }
}

security部分,您可以设置SP将如何处理消息和断言。联系IdP的管理员,并询问他们IdP期望什么,并决定SP将处理哪些验证以及SP将有什么要求,并将这些要求传达给IdP的管理员。

一旦我们知道可以配置哪些类型的数据,让我们谈谈在工具集中处理设置的方式。

描述的设置文件(settings.jsonadvanced_settings.json)在工具集构造函数中加载,如果没有在构造函数中提供其他包含设置信息的dict。让我们看看一些例子。

# Initializes toolkit with settings.json & advanced_settings.json files.
auth = OneLogin_Saml2_Auth(req)
# or
settings = OneLogin_Saml2_Settings()

# Initializes toolkit with settings.json & advanced_settings.json files from a custom base path.
custom_folder = '/var/www/django-project'
auth = OneLogin_Saml2_Auth(req, custom_base_path=custom_folder)
# or
settings = OneLogin_Saml2_Settings(custom_base_path=custom_folder)

# Initializes toolkit with the dict provided.
auth = OneLogin_Saml2_Auth(req, settings_data)
# or
settings = OneLogin_Saml2_Settings(settings_data)

您可以在包含构造函数执行的文件中声明settings_data,或者在任意文件中定位它们,然后加载该文件以获取如以下示例中所示的可用字典。

filename = "/var/www/django-project/custom_settings.json" # The custom_settings.json contains a
json_data_file = open(filename, 'r')                      # settings_data dict.
settings_data = json.load(json_data_file)
json_data_file.close()

auth = OneLogin_Saml2_Auth(req, settings_data)

基于元数据的配置

上述方法需要手动指定有关IdP的一些额外属性。(以及您的SP应用程序)

有一种更简单的方法——使用元数据交换。元数据只是一个定义了IdP和SP应用程序功能的XML文件。它还包含X.509公共密钥证书,这增加了可信关系。IdP管理员还可以根据元数据为SP配置自定义设置。

使用parse_remote可以轻松地获取IdP元数据并将其添加到设置中。

请注意,OneLogin_Saml2_IdPMetadataParser类不会以任何方式验证用于解析的URL。

通常,处理服务提供者的同一个管理员也会设置IdP的URL,这应该是一个可信的资源。

但还有其他场景,比如SAAS应用程序,管理员将此功能委托给其他用户。在这种情况下,应采取额外的预防措施以验证此类URL输入,并避免如SSRF之类的攻击。

idp_data = OneLogin_Saml2_IdPMetadataParser.parse_remote('https://example.com/auth/saml2/idp/metadata')

您可以为元数据检索指定以秒为单位的超时时间,如果没有指定,则无法保证请求将完成

idp_data = OneLogin_Saml2_IdPMetadataParser.parse_remote('https://example.com/auth/saml2/idp/metadata', timeout=5)

如果元数据包含多个实体,可以通过EntityDescriptorentityId值在从IdpMetadataParser检索设置时指定相关的EntityDescriptor

idp_data = OneLogin_Saml2_IdPMetadataParser.parse_remote(https://example.com/metadatas, entity_id='idp_entity_id')

如何加载库

为了使用工具集库,您需要导入包含您将需要的类的文件,并将其放在Python文件的顶部。

from onelogin.saml2.auth import OneLogin_Saml2_Auth
from onelogin.saml2.settings import OneLogin_Saml2_Settings
from onelogin.saml2.utils import OneLogin_Saml2_Utils

请求

构建OneLogin_Saml2_Auth对象需要一个request参数

auth = OneLogin_Saml2_Auth(req)

此参数具有以下方案

req = {
    "http_host": "",
    "script_name": "",
    "get_data": "",
    "post_data": "",

    # Advanced request options
    "https": "",
    "request_uri": "",
    "query_string": "",
    "validate_signature_from_qs": False,
    "lowercase_urlencoding": False
}

每个Python框架都构建自己的request对象,您可以将它们的数据映射到SAML工具集所期望的内容。让我们看看一些例子

def prepare_from_django_request(request):
    return {
        'http_host': request.META['HTTP_HOST'],
        'script_name': request.META['PATH_INFO'],
        'get_data': request.GET.copy(),
        'post_data': request.POST.copy()
    }

def prepare_from_flask_request(request):
    url_data = urlparse(request.url)
    return {
        'http_host': request.host,
        'script_name': request.path,
        'get_data': request.args.copy(),
        'post_data': request.form.copy()
    }

一些高级请求参数的解释

  • https - 默认为off。如果通过HTTPS接收响应,请将其设置为on

  • request_uri - SAML服务器接收请求的路径。如果请求未在服务器根目录接收,则设置此选项。

  • query_string - 设置此选项以传递给请求端点的附加查询参数。

  • validate_signature_from_qs - 如果设置为True,则使用query_string来验证请求和响应签名。否则,使用get_data。默认值为False。注意,当使用get_data时,查询参数需要经过URL编码以进行验证。默认情况下,我们使用大写URL编码。某些IdP(例如Microsoft AD)使用小写URL编码,这会导致签名验证失败。为了解决这个问题,可以传递query_string并将validate_signature_from_qs设置为True,这对于所有IdP都有效,或者将lowercase_urlencoding设置为True,这对于AD有效。

启动SSO

为了向IdP发送AuthNRequest

from onelogin.saml2.auth import OneLogin_Saml2_Auth

req = prepare_request_for_toolkit(request)
auth = OneLogin_Saml2_Auth(req)   # Constructor of the SP, loads settings.json
                                  # and advanced_settings.json

auth.login()      # This method will build and return a AuthNRequest URL that can be
                  # either redirected to, or printed out onto the screen as a hyperlink

AuthNRequest将根据advanced_settings.json文件的安全信息(即authnRequestsSigned)进行签名或未签名。

然后IdP将SAML响应返回给用户的客户端。客户端随后被转发到SP的Assertion Consumer Service (ACS),带有这些信息。

我们可以将return_to URL参数设置到登录函数中,并将其转换为RelayState参数

target_url = 'https://example.com'
auth.login(return_to=target_url)

登录方法可以接收3个额外的可选参数

  • force_authn 当设置为true时,AuthNReuqest将设置ForceAuthn='true'
  • is_passive 当为真时,AuthNReuqest将设置Ispassive='true'
  • set_nameid_policy 当为真时,AuthNReuqest将设置一个nameIdPolicy元素。

如果需要匹配未来的SAMLResponse ID和要发送的AuthNRequest ID,则必须提取并存储该AuthNRequest ID以供将来验证,我们可以通过以下方式获取该ID

auth.get_last_request_id()

SP端点

与SP相关,有3个重要的端点:元数据视图、ACS视图和SLS视图。工具包在演示中提供了这些视图的示例,但让我们看看一个示例。

SP元数据

此代码将提供我们SP的XML元数据文件,基于我们在设置文件中提供的信息。

req = prepare_request_for_toolkit(request)
auth = OneLogin_Saml2_Auth(req)
saml_settings = auth.get_settings()
metadata = saml_settings.get_sp_metadata()
errors = saml_settings.validate_metadata(metadata)
if len(errors) == 0:
    print(metadata)
else:
    print("Error found on Metadata: %s" % (', '.join(errors)))

get_sp_metadata将根据advanced_settings.json的安全信息(即signMetadata)返回已签名或未签名的元数据。

在暴露XML元数据之前,将进行检查以确保提供的信息有效。

您可以直接使用以下方法,而不是使用Auth对象,以获取设置对象,并通过sp_validation_only=True参数避免对IdP设置进行验证。

saml_settings = OneLogin_Saml2_Settings(settings=None, custom_base_path=None, sp_validation_only=True)

断言消费者服务(ACS)

此代码处理IdP通过用户的客户端转发给SP的SAML响应。

req = prepare_request_for_toolkit(request)
auth = OneLogin_Saml2_Auth(req)
auth.process_response()
errors = auth.get_errors()
if not errors:
    if auth.is_authenticated():
        request.session['samlUserdata'] = auth.get_attributes()
        if 'RelayState' in req['post_data'] and
          OneLogin_Saml2_Utils.get_self_url(req) != req['post_data']['RelayState']:
            # To avoid 'Open Redirect' attacks, before execute the redirection confirm
                # the value of the req['post_data']['RelayState'] is a trusted URL.
            auth.redirect_to(req['post_data']['RelayState'])
        else:
            for attr_name in request.session['samlUserdata'].keys():
                print('%s ==> %s' % (attr_name, '|| '.join(request.session['samlUserdata'][attr_name])))
    else:
      print('Not authenticated')
else:
    print("Error when processing SAML Response: %s %s" % (', '.join(errors), auth.get_last_error_reason()))

处理SAML响应并检查是否存在错误。它还验证用户是否已认证,并将userdata存储在会话中。

此时有两种可能的替代方案

  • 如果没有提供RelayState,则可以在该视图中显示用户数据或以任何其他方式显示。
  • 如果提供了RelayState,则进行重定向。

注意,我们在重定向之前将用户数据保存在会话中,以便在RelayState视图中可以使用用户数据。

为了检索属性,我们使用

attributes = auth.get_attributes()

使用此方法,我们得到一个字典,其中包含IdP在SAML响应的断言中提供的所有用户数据。

如果我们执行print attributes,我们可以得到

{
    "cn": ["Jhon"],
    "sn": ["Doe"],
    "mail": ["Doe"],
    "groups": ["users", "members"]
}

每个属性名称都可以用作键来获取其值。每个属性都是值的列表。单值属性是单个元素的列表。

以下代码是等效的

attributes = auth.get_attributes()
print(attributes['cn'])

print(auth.get_attribute('cn'))

在尝试获取属性之前,请检查用户是否已认证。如果用户未认证,将返回一个空字典。例如,如果我们在一个 auth.process_response 之前调用 get_attributes,则 get_attributes() 将返回一个空字典。

单一登出服务(SLS)

此代码处理登出请求和登出响应。

delete_session_callback = lambda: request.session.flush()
url = auth.process_slo(delete_session_cb=delete_session_callback)
errors = auth.get_errors()
if len(errors) == 0:
    if url is not None:
        # To avoid 'Open Redirect' attacks, before execute the redirection confirm
        # the value of the url is a trusted URL.
        return redirect(url)
    else:
        print("Successfully Logged out")
else:
    print("Error when processing SLO: %s %s" % (', '.join(errors), auth.get_last_error_reason()))

如果SLS端点收到一个登出响应,则将验证响应并使用回调关闭会话。

# Part of the process_slo method
logout_response = OneLogin_Saml2_Logout_Response(self.__settings, self.__request_data['get_data']['SAMLResponse'])
if not logout_response.is_valid(self.__request_data, request_id):
    self.__errors.append('invalid_logout_response')
elif logout_response.get_status() != OneLogin_Saml2_Constants.STATUS_SUCCESS:
    self.__errors.append('logout_not_success')
elif not keep_local_session:
    OneLogin_Saml2_Utils.delete_local_session(delete_session_cb)

如果SLS端点收到一个登出请求,则验证请求,关闭会话并向IdP的SLS端点发送登出响应。

# Part of the process_slo method
request = OneLogin_Saml2_Utils.decode_base64_and_inflate(self.__request_data['get_data']['SAMLRequest'])
if not OneLogin_Saml2_Logout_Request.is_valid(self.__settings, request, self.__request_data):
    self.__errors.append('invalid_logout_request')
else:
    if not keep_local_session:
        OneLogin_Saml2_Utils.delete_local_session(delete_session_cb)

    in_response_to = request.id
    response_builder = OneLogin_Saml2_Logout_Response(self.__settings)
    response_builder.build(in_response_to)
    logout_response = response_builder.get_response()

    parameters = {'SAMLResponse': logout_response}
    if 'RelayState' in self.__request_data['get_data']:
        parameters['RelayState'] = self.__request_data['get_data']['RelayState']

    security = self.__settings.get_security_data()
    if 'logoutResponseSigned' in security and security['logoutResponseSigned']:
        parameters['SigAlg'] = OneLogin_Saml2_Constants.RSA_SHA1
        parameters['Signature'] = self.build_response_signature(logout_response, parameters.get('RelayState', None))

    return self.redirect_to(self.get_slo_url(), parameters)

如果我们不希望process_slo销毁会话,请将一个true参数传递给process_slo方法

keepLocalSession = true
auth.process_slo(keep_local_session=keepLocalSession);

启动SLO

为了向IdP发送登出请求

登出请求将根据advanced_settings.jsonlogoutRequestSigned)的安全信息进行签名或未签名。

IdP将通过用户的客户端将登出响应返回给SP的SLS。

我们可以将return_to url参数设置到登出函数中,该参数将被转换为RelayState参数

target_url = 'https://example.com'
auth.logout(return_to=target_url)

此外,还可以设置其他5个可选参数

  • name_id:该参数将用于构建LogoutRequest。如果没有设置name_id参数,并且auth对象处理了一个包含NameId的SAML响应,则将使用此NameId
  • session_indexSessionIndex用于标识用户的会话。
  • nq:IDP名称限定符。
  • name_id_format:将设置在LogoutRequest中的NameID格式。
  • spnqNameID SP NameQualifier将在LogoutRequest中设置。

如果没有提供name_id,则LogoutRequest将包含一个实体格式的NameID。如果提供了name_id但没有提供name_id_format,则将使用设置的NameIDFormat

如果需要在要发送的LogoutResponse ID和LogoutRequest ID之间进行匹配,则必须提取并存储该ID以供将来验证,我们可以通过以下方式获取该ID

auth.get_last_request_id()

初始化SSO请求并处理响应的视图示例(即acs目标)

我们可以编写一个独特的文件来初始化SSO流程,处理响应,获取属性,启动SLO并处理登出响应。

注意:请参阅演示,在稍后的部分中,我们将更详细地解释演示用例。

req = prepare_request_for_toolkit(request)  # Process the request and build the request dict that
                                            # the toolkit expects

auth = OneLogin_Saml2_Auth(req)             # Initialize the SP SAML instance

if 'sso' in request.args:                   # SSO action (SP-SSO initited).  Will send an AuthNRequest to the IdP
    return redirect(auth.login())
elif 'sso2' in request.args:                       # Another SSO init action
    return_to = '%sattrs/' % request.host_url      # but set a custom RelayState URL
    return redirect(auth.login(return_to))
elif 'slo' in request.args:                     # SLO action. Will sent a Logout Request to IdP
    nameid = request.session['samlNameId']
    nameid_format = request.session['samlNameIdFormat']
    nameid_nq = request.session['samlNameIdNameQualifier']
    nameid_spnq = request.session['samlNameIdSPNameQualifier']
    session_index = request.session['samlSessionIndex']
    return redirect(auth.logout(None, nameid, session_index, nameid_nq, nameid_format, nameid_spnq))
elif 'acs' in request.args:                 # Assertion Consumer Service
    auth.process_response()                     # Process the Response of the IdP
    errors = auth.get_errors()              # This method receives an array with the errors
    if len(errors) == 0:                    # that could took place during the process
        if not auth.is_authenticated():         # This check if the response was ok and the user
            msg = "Not authenticated"           # data retrieved or not (user authenticated)
        else:
            request.session['samlUserdata'] = auth.get_attributes()     # Retrieves user data
            request.session['samlNameId'] = auth.get_nameid()
            request.session['samlNameIdFormat'] = auth.get_nameid_format()
            request.session['samlNameIdNameQualifier'] = auth.get_nameid_nq()
            request.session['samlNameIdSPNameQualifier'] = auth.get_nameid_spnq()
            request.session['samlSessionIndex'] = auth.get_session_index()
            self_url = OneLogin_Saml2_Utils.get_self_url(req)
            if 'RelayState' in request.form and self_url != request.form['RelayState']:
                # To avoid 'Open Redirect' attacks, before execute the redirection confirm
                # the value of the request.form['RelayState'] is a trusted URL.
                return redirect(auth.redirect_to(request.form['RelayState']))   # Redirect if there is a relayState
            else:                           # If there is user data we save that to print it later.
                msg = ''
                for attr_name in request.session['samlUserdata'].keys():
                    msg += '%s ==> %s' % (attr_name, '|| '.join(request.session['samlUserdata'][attr_name]))
elif 'sls' in request.args:                                             # Single Logout Service
    delete_session_callback = lambda: session.clear()           # Obtain session clear callback
    url = auth.process_slo(delete_session_cb=delete_session_callback)   # Process the Logout Request & Logout Response
    errors = auth.get_errors()              #  Retrieves possible validation errors
    if len(errors) == 0:
        if url is not None:
            # To avoid 'Open Redirect' attacks, before execute the redirection confirm
            # the value of the url is a trusted URL.
            return redirect(url)
        else:
            msg = "Successfully logged out"

if len(errors) == 0:
  print(msg)
else:
  print(', '.join(errors))

SP密钥轮换

如果您计划更新SP的x509certprivateKey,您可以将新的x509cert定义为settings['sp']['x509certNew'],它将发布在SP元数据上,以便身份提供者可以读取它们并准备轮换。

具有多个证书的IdP

在某些场景中,IdP使用不同的证书进行签名/加密,或者处于密钥轮换阶段,并且在IdP元数据上发布了多个证书。

为了处理这种情况,工具包提供了settings['idp']['x509certMulti']参数。

当使用该参数时,工具包将忽略x509certcertFingerprint值。

x509certMulti是一个具有2个键的数组

  • signing:用于验证IdP签名的证书数组
  • encryption:用于加密发送到IdP的数据的唯一证书数组

重放攻击

为了避免重放攻击,您可以存储已处理过的SAML消息的ID,以避免重复处理。由于消息会过期并且因此无效,您不需要存储这些ID的时间超过您当前接受的时间范围。

使用Auth对象的get_last_message_id/get_last_assertion_id方法获取最后处理的消息/断言的ID。

主要类和方法

以下描述了可以从SAML2库调用的主要类和方法。

OneLogin_Saml2_Auth - auth.py

SAML Python工具包的主要类

  • __init__ 初始化SP SAML实例。
  • login 启动SSO过程。
  • logout 启动SLO过程。
  • process_response 处理由IdP发送的SAML响应。
  • process_slo 处理由IdP发送的SAML注销响应/注销请求。
  • redirect_to 将用户重定向到参数中指定的URL或我们定义的SSO请求中的URL。
  • is_authenticated 检查用户是否已认证。
  • get_attributes 返回SAML属性的集合。
  • get_attribute 返回请求的SAML属性。
  • get_nameid 返回nameID
  • get_session_indexAuthnStatement获取SessionIndex
  • get_session_expirationAuthnStatement获取SessionNotOnOrAfter
  • get_errors 返回错误代码列表,如果发生错误。
  • get_last_error_reason 返回最后错误的理由。
  • get_sso_url 获取SSO URL。
  • get_slo_url 获取SLO URL。
  • get_last_request_id 最后生成的SAML请求消息(AuthNRequestLogoutRequest)的ID。
  • get_last_authn_contexts 返回最后SAML响应中发送的认证上下文列表。
  • build_request_signature 构建SAML请求的签名。
  • build_response_signature 构建SAML响应的签名。
  • get_settings 返回设置信息。
  • set_strict 设置严格模式激活/禁用。
  • get_last_request_xml 返回最近构造/处理的XML SAML请求(AuthNRequestLogoutRequest)。
  • get_last_response_xml 返回最近构造/处理的XML SAML响应(SAMLResponseLogoutResponse)。如果SAMLResponse包含加密的断言,则对其进行解密。
  • get_last_response_in_response_to 最后处理的SAML响应的InResponseTo ID。
  • get_last_message_id 最后处理的响应SAML消息的ID。
  • get_last_assertion_id 最后处理的断言的ID。
  • get_last_assertion_not_on_or_after 最后处理的断言中有效的SubjectConfirmationData节点的NotOnOrAfter值(如果有)(仅在严格=true时计算)。
  • get_last_assertion_issue_instant 最后处理的断言的IssueInstant值。

OneLogin_Saml2_Auth - authn_request.py

SAML 2认证请求类

  • __init__ 此类处理一个AuthNRequest。它构建一个AuthNRequest对象。
  • get_request 返回未签名的AuthnRequest
  • get_id 返回AuthNRequest ID。
  • get_xml 返回作为请求一部分发送的XML。

OneLogin_Saml2_Response - response.py

SAML 2认证响应类

  • __init__ 构造SAML响应对象。
  • is_valid 确定SAML响应是否有效。包括通过证书检查签名。
  • check_status 检查响应的状态是否成功。
  • get_audiences 获取受众。
  • get_issuers 获取发行者(来自消息和断言)。
  • get_nameid_data 从IdP提供的SAML响应中获取NameID数据(返回一个字典)
  • get_nameid 从IdP提供的SAML响应中获取NameID(返回一个字符串)
  • get_session_not_on_or_afterAuthnStatement获取SessionNotOnOrAfter
  • get_session_indexAuthnStatement获取SessionIndex
  • get_attributesAttributeStatement元素获取属性
  • validate_num_assertions 验证文档只包含单个断言(加密或不加密)
  • validate_timestamps 验证根据条件元素文档是否有效
  • get_error 执行验证过程后,如果失败,此方法返回原因
  • get_xml_document 返回SAML响应文档(如果包含加密断言,则解密它)
  • get_id 响应的ID
  • get_assertion_id 响应中断言的ID
  • get_assertion_not_on_or_after 如果有任何有效的SubjectConfirmationData,则返回其NotOnOrAfter

OneLogin_Saml2_LogoutRequest - logout_request.py

SAML 2注销请求类

  • __init__ 构建注销请求对象。
  • get_request 返回压缩、base64编码的注销请求
  • get_id 返回注销请求的ID。(如果您有该对象,您可以访问id属性)
  • get_nameid_data 获取注销请求的NameID数据(返回一个字典)。
  • get_nameid 获取注销请求消息的NameID(返回一个字符串)。
  • get_issuer 获取注销请求消息的发行者。
  • get_session_indexes 从注销请求中获取SessionIndexes
  • is_valid 检查接收到的注销请求是否有效。
  • get_error 执行验证过程后,如果失败,此方法返回原因。
  • get_xml 返回作为请求的一部分发送或接收到的XML

OneLogin_Saml2_LogoutResponse - logout_response.py

SAML 2注销响应类

  • __init__ 构建注销响应对象。
  • get_issuer 获取注销响应消息的发行者
  • get_status 获取注销响应的状态
  • is_valid 确定SAML LogoutResponse是否有效
  • build 创建注销响应对象。
  • get_response 返回注销响应对象。
  • get_error 执行验证过程后,如果失败,此方法返回原因。
  • get_xml 返回作为响应的一部分发送或接收到的XML

OneLogin_Saml2_Settings - settings.py

SAML Python工具包的配置

  • __init__ 初始化设置:设置不同文件夹的路径,并从设置文件或提供的数组/对象中加载设置信息。
  • check_settings 检查设置信息。
  • check_idp_settings 检查IdP设置信息。
  • check_sp_settings 检查SP设置信息。
  • get_errors 返回包含错误的数组,当设置正常时,数组为空。
  • get_sp_metadata 获取SP元数据。XML表示形式。
  • validate_metadata 验证XML SP元数据。
  • get_base_path 返回基本路径。
  • get_cert_path 返回证书路径。
  • get_lib_path 返回库路径。
  • get_ext_lib_path 返回外部库路径。
  • get_schemas_path 返回模式路径。
  • check_sp_certs 检查SP的X.509证书是否存在且有效。
  • get_sp_key 返回SP的X.509私钥。
  • get_sp_cert 返回SP的X.509公钥证书。
  • get_sp_cert_new 返回SP未来X.509公钥证书。
  • get_idp_cert 返回IdP的X.509公钥证书。
  • get_sp_data 获取SP数据。
  • get_idp_data 获取IdP数据。
  • get_security_data 获取安全数据。
  • get_contacts 获取联系人数据。
  • get_organization 获取组织数据。
  • format_idp_cert 格式化IdP证书。
  • format_idp_cert_multi 格式化所有已注册的IdP证书。
  • format_sp_cert 格式化SP证书。
  • format_sp_cert_new 格式化新的SP证书。
  • format_sp_key 格式化私钥。
  • set_strict 启用或禁用严格模式。
  • is_strict 返回是否启用了严格模式。
  • is_debug_active 返回是否启用了调试模式。

OneLogin_Saml2_Metadata - metadata.py

一个包含与SP元数据相关功能的类

  • builder 根据设置生成SP的元数据。
  • sign_metadata 使用提供的密钥/证书签名元数据。
  • add_x509_key_descriptors 将X.509描述符(签名/加密)添加到元数据中。

OneLogin_Saml2_Utils - utils.py

一个包含多个方法的辅助类

  • decode_base64_and_inflate 根据RFC1951进行Base64解码然后进行膨胀。
  • deflate_and_base64_encode 压缩并base64编码一个字符串。
  • format_cert 返回一个X.509证书(如果需要则添加头和尾)。
  • format_private_key 返回一个私钥(如果需要则添加头和尾)。
  • redirect 执行到提供的URL的重定向(或返回目标URL)。
  • get_self_url_host 返回协议 + 当前主机 + 端口(如果不同于常用端口)。
  • get_self_host 返回当前主机。
  • is_https 检查是否为https或http。
  • get_self_url_no_query 返回当前主机 + 当前视图的URL。
  • get_self_routed_url_no_query 返回当前主机 + 当前视图的路由URL。
  • get_self_url 返回当前主机 + 当前视图 + 查询的URL。
  • generate_unique_id 生成一个唯一的字符串(例如,用作断言的ID)。
  • parse_time_to_SAML 将UNIX时间戳转换为SAML2时间戳,格式为yyyy-mm-ddThh:mm:ss(.s+)?Z。
  • parse_SAML_to_time 将格式为yyyy-mm-ddThh:mm:ss(.s+)?Z的SAML2时间戳转换为UNIX时间戳。
  • now 返回实际时间的UNIX时间戳。
  • parse_duration 解释相对于给定时间戳的ISO8601持续时间值。
  • get_expire_time 比较两个日期并返回最早的一个。
  • delete_local_session 删除本地会话。
  • calculate_X.509_fingerprint 计算X.509证书的指纹。
  • format_finger_print 格式化指纹。
  • generate_name_id 生成nameID。
  • get_status 从响应中获取状态。
  • decrypt_element 解密加密元素。
  • write_temp_file 将一些内容写入临时文件并返回它。
  • add_sign 将签名密钥和发送者证书添加到元素(消息或断言)中。
  • validate_sign 验证签名(消息或断言)。
  • validate_binary_sign 验证签名二进制数据(用于验证GET签名)。

OneLogin_Saml2_XML- xml_utils.py

一个包含处理XML方法的类

  • to_string 将元素序列化为XML树的编码字符串表示。
  • to_etree 从字符串解析XML文档或片段。
  • validate_xml 对XML进行模式验证。
  • query 从Element中提取匹配查询的节点。
  • extract_tag_text

OneLogin_Saml2_IdPMetadataParser - idp_metadata_parser.py

一个包含从IdP获取和解析元数据的方法的类。

  • get_metadata 从提供的URL获取元数据XML。
  • parse_remote 从提供的URL获取元数据XML并解析它,返回包含提取数据的字典。
  • parse 解析身份提供者元数据,返回包含提取数据的字典。
  • merge_settings 将从IdP元数据中提取的新设置数据更新到设置中。

有关更多信息,请查看源代码。每个方法都有文档说明,并提供关于如何使用它的详细信息。请务必检查doc文件夹,其中包含有关类和方法的HTML文档。

工具包中的示例

工具包包括3个示例,用于教授如何使用工具包(Django、Flask和Tornado项目),请查看。示例要求在测试之前,确保SP和IdP配置良好,因此请编辑设置文件。

请注意,每个Python框架都有自己处理路由/URL和请求的方式,因此请关注其部署方式。欢迎以贡献的形式提供使用其他Python框架的新示例。

入门指南

我们提到此工具包包括Django应用程序示例和Flask应用程序示例,让我们看看它们部署得有多快。

Virtualenv

强烈建议使用virtualenv

virtualenv有助于隔离用于运行工具包的Python环境。您可以在官方文档中找到更多详细信息。

一旦您准备好了virtualenv并已加载,您可以通过执行以下操作在开发模式下安装工具包:

 python setup.py develop

使用此部署方法,工具包文件将被链接而不是复制,因此如果对其进行更改,您不需要重新安装工具包。

如果您想以常规模式安装,请执行:

 python setup.py install

示例Flask

您需要一个安装了工具包的virtualenv。

要运行示例,您首先需要安装要求。加载您的virtualenv并执行:

 pip install -r demo-flask/requirements.txt

这将安装Flask及其依赖项。一旦完成,您必须完成工具包的配置。您可以在demo-flask/settings.json中找到它。

现在,在加载了virtualenv之后,您可以像这样运行示例:

 cd demo-flask
 python index.py

您将在https://:8000运行示例。

内容

Flask项目包含

  • index.py 是主要的Flask文件,其中处理SAML处理。

  • templates。是Flask存储项目模板的文件夹。实现了一个base.html模板,它被index.html和attrs.html扩展,这是我们的简单示例模板,显示消息、可用时显示用户属性以及登录和注销链接。

  • saml 是一个包含'certs'文件夹的文件夹,可以用来存储X.509公共和私有密钥,以及saml工具包设置(settings.json和advanced_settings.json)。

SP设置

SAML Python Toolkit允许您以两种方式提供设置信息:设置文件或定义设置字典。在demo-flask中,它使用第一种方法。

index.py文件中,我们定义了app.config['SAML_PATH'],它将指向saml文件夹。我们需要它来加载设置文件。

首先我们需要编辑saml/settings.json文件,配置SP部分,并审查IdP的元数据以及完善IdP信息。之后编辑saml/advanced_settings.json文件,并配置工具包的工作方式。如果有任何疑问,请查看本文件的设置部分。

IdP设置

一旦SP配置完成,SP的元数据将在/metadata URL上发布。基于这些信息,配置IdP。

工作原理

  1. 第一次访问主视图(https://:8000)时,您可以选择登录并返回同一视图,或者登录并重定向到/?attrs(属性视图)。

  2. 当您点击

    第一个链接中的2.1,我们访问到/?sso(索引视图)。向IdP发送一个AuthNRequest,我们在IdP处进行身份验证,然后通过用户的客户端将响应发送到SP,具体是Assertion Consumer Service视图:/?acs。请注意,将RelayState参数设置为启动此过程时的URL,即索引视图。

    第二个链接中的2.2,我们访问到/?attrs(属性视图),我们将经历与2.1中描述的相同的过程,不同的是RelayState设置为attrs URL。

  3. SAML响应在ACS /?acs中处理,如果响应无效,则在此处停止处理并显示消息。否则,我们将重定向到RelayState视图。a) / 或 b) /?attrs

  4. 我们在应用程序中登录,并显示用户属性。在此阶段,我们可以测试单个注销功能。

单个注销功能可以通过两种方式测试。

5.1 SLO Initiated by SP. Click on the ``logout`` link at the SP, after that a Logout Request is sent to the IdP, the session at the IdP is closed and replies through the client to the SP with a Logout Response (sent to the Single Logout Service endpoint). The SLS endpoint ``/?sls`` of the SP process the Logout Response and if is valid, close the user session of the local app. Notice that the SLO Workflow starts and ends at the SP.

5.2 SLO Initiated by IdP. In this case, the action takes place on the IdP side, the logout process is initiated at the IdP, sends a Logout Request to the SP (SLS endpoint, ``/?sls``). The SLS endpoint of the SP process the Logout Request and if is valid, close the session of the user at the local app and send a Logout Response to the IdP (to the SLS endpoint of the IdP). The IdP receives the Logout Response, process it and close the session at of the IdP. Notice that the SLO Workflow starts and ends at the IdP.

请注意,所有SAML请求和响应都由一个唯一的视图(索引)处理,以及如何使用GET参数来了解必须执行的操作。

演示Tornado

您需要一个安装了工具包的virtualenv。

首先您需要一些软件包,执行

apt-get install libxml2-dev libxmlsec1-dev libxmlsec1-openssl

要运行示例,您首先需要安装要求。加载您的virtualenv并执行:

 pip install -r demo-tornado/requirements.txt

这将安装tornado及其依赖项。安装完成后,您必须完成工具包的配置。您可以在demo-tornado/saml/settings.json中找到它。

现在,在加载了virtualenv之后,您可以像这样运行示例:

 cd demo-tornado
 python views.py

您将在https://:8000运行示例。

内容

tornado项目包含

  • views.py 是主flask文件,其中处理SAML。

  • settings.py 包含基本路径以及位于saml文件夹和template文件夹的路径

  • templates。是tornado存储项目模板的文件夹。实现了一个base.html模板,它被index.html和attrs.html扩展,这些是我们简单的演示模板,显示消息、用户属性(如有)以及登录和注销链接。

  • saml 是一个包含'certs'文件夹的文件夹,可以用来存储X.509公共和私有密钥,以及saml工具包设置(settings.json和advanced_settings.json)。

SP设置

SAML Python Toolkit允许您以两种方式提供设置信息:设置文件或定义设置字典。在demo-tornado中,它使用第一种方法。

settings.py文件中,我们定义了SAML_PATH,它将指向saml文件夹。我们需要它来加载设置文件。

首先我们需要编辑saml/settings.json文件,配置SP部分,并审查IdP的元数据以及完善IdP信息。之后编辑saml/advanced_settings.json文件,并配置工具包的工作方式。如果有任何疑问,请查看本文件的设置部分。

IdP设置

一旦SP配置完成,SP的元数据将在/metadata URL上发布。基于这些信息,配置IdP。

工作原理

  1. 第一次访问主视图(https://:8000)时,您可以选择登录并返回同一视图,或者登录并重定向到/?attrs(属性视图)。

  2. 当您点击

    第一个链接中的2.1,我们访问到/?sso(索引视图)。向IdP发送一个AuthNRequest,我们在IdP处进行身份验证,然后通过用户的客户端将响应发送到SP,具体是Assertion Consumer Service视图:/?acs。请注意,将RelayState参数设置为启动此过程时的URL,即索引视图。

    第二个链接中的2.2,我们访问到/?attrs(属性视图),我们将经历与2.1中描述的相同的过程,不同的是RelayState设置为attrs URL。

  3. SAML响应在ACS /?acs中处理,如果响应无效,则在此处停止处理并显示消息。否则,我们将重定向到RelayState视图。a) / 或 b) /?attrs

  4. 我们在应用程序中登录,并显示用户属性。在此阶段,我们可以测试单个注销功能。

单个注销功能可以通过两种方式测试。

5.1 SLO Initiated by SP. Click on the ``logout`` link at the SP, after that a Logout Request is sent to the IdP, the session at the IdP is closed and replies through the client to the SP with a Logout Response (sent to the Single Logout Service endpoint). The SLS endpoint ``/?sls`` of the SP process the Logout Response and if is valid, close the user session of the local app. Notice that the SLO Workflow starts and ends at the SP.

5.2 SLO Initiated by IdP. In this case, the action takes place on the IdP side, the logout process is initiated at the IdP, sends a Logout Request to the SP (SLS endpoint, ``/?sls``). The SLS endpoint of the SP process the Logout Request and if is valid, close the session of the user at the local app and send a Logout Response to the IdP (to the SLS endpoint of the IdP). The IdP receives the Logout Response, process it and close the session at of the IdP. Notice that the SLO Workflow starts and ends at the IdP.

请注意,所有SAML请求和响应都由一个唯一的视图(索引)处理,以及如何使用GET参数来了解必须执行的操作。

演示Django

您需要一个安装了工具包的virtualenv。

要运行示例,您首先需要安装要求。加载您的virtualenv并执行:

 pip install -r demo-django/requirements.txt

这将安装django及其依赖项。安装完成后,您必须完成工具包的配置。

随后,在虚拟环境加载完成后,您可以通过以下方式运行演示

 cd demo-django
 python manage.py runserver 0.0.0.0:8000

您将有一个演示在https://:8000上运行。

请注意,许多配置文件都期望HTTPS。演示中不需要HTTPS,因为将这些SP URL替换为HTTP也可以正常工作。然而,HTTPS被强烈推荐,并且留作读者的特定需求作为练习。

如果您想集成一个Django生产应用程序,请查看这个使用我们的工具包添加SAML支持的SAMLServiceProviderBackend: https://github.com/KristianOellegaard/django-saml-service-provider

内容

Django项目包含

  • manage.py。这是一个在每个Django项目中自动创建的文件。它是围绕django-admin.py的一个薄包装器,负责将项目的包放置在sys.path上,并设置DJANGO_SETTINGS_MODULE环境变量。

  • saml是一个文件夹,其中包含可用于存储X.509公钥和私钥以及SAML工具包设置的“certs”文件夹(settings.jsonadvanced_settings.json)。

  • demo是Django项目的主文件夹,其中包含典型的文件

    • settings.py包含Django项目的默认参数,除了SAML_FOLDER参数,它可能包含saml文件夹的路径。
    • urls.py是一个定义URL路由的文件。在demo中,我们定义了'/',它与索引视图相关,'/attrs'与attrs视图相关,以及'/metadata',与元数据视图相关。
    • views.py该文件包含Django项目的视图和一些辅助方法。
    • wsgi.py一个文件,使我们能够使用WSGI部署Django,这是Python的Web服务器和应用程序的标准。
  • templates。是Django存储项目模板的文件夹。实现了一个base.html模板,该模板被index.htmlattrs.html扩展,这是我们的简单演示的模板,显示消息、用户属性(如果有的话)以及登录和注销链接。

SP设置

SAML Python Toolkit允许您以两种方式提供设置信息:设置文件或定义设置字典。在demo-django中,它使用了第一种方法。

demo/settings.py中设置SAML_FOLDER后,Python工具包的设置将在Django Web上加载。

首先,我们需要编辑saml/settings.json,配置SP部分,并审查IdP的元数据,完成IdP信息。稍后编辑saml/advanced_settings.json文件,并配置工具包的工作方式。如果有任何疑问,请参阅本文件的设置部分。

IdP设置

一旦SP配置完成,SP的元数据将在/metadata URL上发布。基于这些信息,配置IdP。

工作原理

这个演示与flask-demo非常相似(我们故意这样设计)。

在Heroku上启动

在Heroku上启动python3-saml需要一些额外的工作:python3-saml依赖于python-xmlsec,它依赖于从Linux包xmlsec1-dev安装的正确头文件。

首先,您需要将apt构建包添加到您的构建服务器

heroku buildpacks:add --index=1 -a your-app heroku-community/apt
heroku buildpacks:add --index=2 -a your-app heroku/python

您可以使用heroku buildpacks -a your-app确认已正确添加构建包,您应该首先看到apt构建包,然后是Python构建包。

然后,在存储库的根目录中添加一个Aptfile,其中包含libxmlsec1-dev包,该文件应如下所示

libxmlsec1-dev

最后,将python3-saml添加到您的requirements.txt中,并执行git push以触发构建。

演示Pyramid

与其他两个项目不同,您不需要现有的虚拟环境来启动,因为Pyramid来自buildout思想。

要运行演示,您需要安装Pyramid、需求等。

 cd demo_pyramid
 python3 -m venv env
 env/bin/pip install --upgrade pip setuptools
 env/bin/pip install -e ".[testing]"

如果您想确保测试通过,请运行

 env/bin/pytest

接下来,编辑demo_pyramid/saml/settings.json中的设置。(Pyramid默认在6543端口运行。)

现在您可以像这样运行演示

 env/bin/pserve development.ini

如果成功了,演示现在正在https://:6543运行。

内容

Pyramid项目包含

  • __init__.py是配置应用程序及其路由的主要Pyramid文件。

  • views.py 是所有 SAML 处理操作发生的地点。

  • templates 是 Pyramid 存储项目模板的文件夹。其中实现了一个 layout.jinja2 模板,它由 index.jinja2attrs.jinja2 扩展,这些是我们简单演示的模板,用于显示消息、可用时显示用户属性,以及登录和登出链接。

  • saml 是一个包含 'certs' 文件夹的文件夹,该文件夹可用于存储 X.509 公钥和私钥,以及 saml 工具包设置(settings.jsonadvanced_settings.json)。

SP设置

SAML Python 工具包允许您以两种方式提供设置信息:设置文件或定义设置字典。在 demo_pyramid 中,使用第一种方法。

views.py 文件中,我们定义了 SAML_PATH,它将指向 saml 文件夹。我们要求这样做是为了加载设置文件。

首先,我们需要编辑saml/settings.json,配置SP部分,并审查IdP的元数据,完成IdP信息。稍后编辑saml/advanced_settings.json文件,并配置工具包的工作方式。如果有任何疑问,请参阅本文件的设置部分。

IdP设置

一旦SP配置完成,SP的元数据将在/metadata URL上发布。基于这些信息,配置IdP。

工作原理

  1. 第一次访问主视图(https://:6543),您可以选择登录并返回同一视图或登录并重定向到 /?attrs(属性视图)。

  2. 当您点击

    第一个链接中的2.1,我们访问到/?sso(索引视图)。向IdP发送一个AuthNRequest,我们在IdP处进行身份验证,然后通过用户的客户端将响应发送到SP,具体是Assertion Consumer Service视图:/?acs。请注意,将RelayState参数设置为启动此过程时的URL,即索引视图。

    2.2 在第二个链接中,我们访问到 /?attrs(属性视图),我们将经历与 2.1 中描述的相同的过程,唯一的区别是,由于设置了 RelayState,所以 attrs url。

  3. SAML 响应在 ACS /?acs 中处理,如果响应无效,则在此处停止处理并显示一条消息。否则,我们将重定向到 RelayState 视图。a) / 或 b) /?attrs

  4. 我们在应用程序中登录,并显示用户属性。在此阶段,我们可以测试单个注销功能。

单个注销功能可以通过两种方式测试。

5.1 SLO Initiated by SP. Click on the "logout" link at the SP, after that a Logout Request is sent to the IdP, the session at the IdP is closed and replies through the client to the SP with a Logout Response (sent to the Single Logout Service endpoint). The SLS endpoint /?sls of the SP process the Logout Response and if is valid, close the user session of the local app. Notice that the SLO Workflow starts and ends at the SP.

5.2 SLO Initiated by IdP. In this case, the action takes place on the IdP side, the logout process is initiated at the IdP, sends a Logout Request to the SP (SLS endpoint, /?sls). The SLS endpoint of the SP process the Logout Request and if is valid, close the session of the user at the local app and send a Logout Response to the IdP (to the SLS endpoint of the IdP). The IdP receives the Logout Response, process it and close the session at of the IdP. Notice that the SLO Workflow starts and ends at the IdP.

请注意,所有SAML请求和响应都由一个唯一的视图(索引)处理,以及如何使用GET参数来了解必须执行的操作。

项目详情


下载文件

下载您平台上的文件。如果您不确定选择哪个,请了解更多关于 安装包 的信息。

源分发

maykin_python3_saml-1.16.1.tar.gz (353.4 kB 查看哈希)

上传时间

构建分发

maykin_python3_saml-1.16.1-py2.py3-none-any.whl (116.1 kB 查看哈希)

上传时间 Python 2 Python 3

由以下机构支持

AWS AWS 云计算和安全赞助商 Datadog Datadog 监控 Fastly Fastly CDN Google Google 下载分析 Microsoft Microsoft PSF 赞助商 Pingdom Pingdom 监控 Sentry Sentry 错误日志 StatusPage StatusPage 状态页面