项目作者: AdactiveSAS

项目描述 :
Symfony bundle that provide a SAML Identity Provider (idp).
高级语言: PHP
项目地址: git://github.com/AdactiveSAS/saml2-bridge-bundle.git
创建时间: 2017-03-15T12:40:35Z
项目社区:https://github.com/AdactiveSAS/saml2-bridge-bundle

开源协议:GNU General Public License v3.0

下载


SAML2 Bridge Bundle

Coverage Status
Build Status
SensioLabsInsight
A bundle that adds SAML capabilities to your application using simplesamlphp/saml2 highly inspired by
OpenConext/Stepup-saml-bundle

SAML Support

SAML Support is limited, this bundle can be used to provide a basic identity provider with the following support:

  • Basic metadata
  • Single Sign On:
    • Binding:
      • Http-POST & Http-Redirect signed request
      • Http-POST & Http-Post signed response
  • Single Logout:
    • Binding:
      • Http-POST & Http-Redirect signed request
      • Http-POST & Http-Redirect signed response
    • Both identity provider initiated and service provider initiated

Getting started

Installation

  • Add the package to your Composer file

    1. composer require adactive-sas/saml2-bridge-bundle
  • Add the bundle to your kernel in app/AppKernel.php

    1. public function registerBundles()
    2. {
    3. // ...
    4. $bundles[] = new AdactiveSas\Saml2BridgeBundle\AdactiveSasSaml2BridgeBundle();
    5. }

Configuration

  1. adactive_sas_saml2_bridge:
  2. hosted:
  3. metadata_route: name_of_the_route_of_metadata_url
  4. identity_provider:
  5. enabled: true
  6. service_provider_repository: service.name.of.entity_repository
  7. sso_route: name_of_the_route_of_the_single_sign_on_url
  8. sls_route: name_of_the_route_of_the_single_logout_url
  9. login_route: name_of_the_route_of_the_login_url
  10. logout_route: name_of_the_route_of_the_logout_url
  11. public_key: %idp_public_key_file_path%
  12. private_key: %idp_private_key_file_path%

Also add logout handler.

  1. logout:
  2. handlers: [adactive_sas_saml2_bridge.logout.handler]

The hosted configuration lists the configuration for the services (SP, IdP or both) that your application offers. SP and IdP
functionality can be turned off and on individually through the repective enabled flags.

The inlined certificate in the last line can be replaced with certificate_file containing a filesystem path to
a file which contains said certificate.

It is recommended to use parameters as listed above. The various publickey and privatekey variables are the
contents of the key in a single line, without the certificate etc. delimiters. The use of parameters as listed above
is highly recommended so that the actual key contents can be kept out of the configuration files (using for instance
a local parameters.yml file).

The service_provider_repository is a repository of service providers for which you offer IdP services. The service
configured must implement the AdactiveSas\Saml2BridgeBundle\Entity\ServiceProviderRepository interface.

Example Usage

Implement the Service Provider Repository

  1. <?php
  2. namespace Acme\SamlBundle\Entity;
  3. use AdactiveSas\Saml2BridgeBundle\Entity\ServiceProvider;
  4. use AdactiveSas\Saml2BridgeBundle\Entity\ServiceProviderRepository;
  5. class SamlServiceProviderRepository implements ServiceProviderRepository
  6. {
  7. protected $spMap = [];
  8. public function __construct() {
  9. $this->spMap["https://test.fake/metadata"] = new ServiceProvider(
  10. [
  11. /**
  12. * Returns the contents of an X509 pem certificate, without the '-----BEGIN CERTIFICATE-----' and
  13. * '-----END CERTIFICATE-----'.
  14. *
  15. * @return null|string
  16. */
  17. 'certificateData' => 'MIIEJTCCAw2gAwIBAgIJANug+o++1X5IMA0GCSqGSIb3DQEBCwUAMIGoMQswCQYDVQQGEwJOTDEQMA4GA1UECAwHVXRyZWNodDEQMA4GA1UEBwwHVXRyZWNodDEVMBMGA1UECgwMU1VSRm5ldCBCLlYuMRMwEQYDVQQLDApTVVJGY29uZXh0MRwwGgYDVQQDDBNTVVJGbmV0IERldmVsb3BtZW50MSswKQYJKoZIhvcNAQkBFhxzdXJmY29uZXh0LWJlaGVlckBzdXJmbmV0Lm5sMB4XDTE0MTAyMDEyMzkxMVoXDTE0MTExOTEyMzkxMVowgagxCzAJBgNVBAYTAk5MMRAwDgYDVQQIDAdVdHJlY2h0MRAwDgYDVQQHDAdVdHJlY2h0MRUwEwYDVQQKDAxTVVJGbmV0IEIuVi4xEzARBgNVBAsMClNVUkZjb25leHQxHDAaBgNVBAMME1NVUkZuZXQgRGV2ZWxvcG1lbnQxKzApBgkqhkiG9w0BCQEWHHN1cmZjb25leHQtYmVoZWVyQHN1cmZuZXQubmwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDXuSSBeNJY3d4p060oNRSuAER5nLWT6AIVbv3XrXhcgSwc9m2b8u3ksp14pi8FbaNHAYW3MjlKgnLlopYIylzKD/6Ut/clEx67aO9Hpqsc0HmIP0It6q2bf5yUZ71E4CN2HtQceO5DsEYpe5M7D5i64kS2A7e2NYWVdA5Z01DqUpQGRBc+uMzOwyif6StBiMiLrZH3n2r5q5aVaXU4Vy5EE4VShv3Mp91sgXJj/v155fv0wShgl681v8yf2u2ZMb7NKnQRA4zM2Ng2EUAyy6PQ+Jbn+rALSm1YgiJdVuSlTLhvgwbiHGO2XgBi7bTHhlqSrJFK3Gs4zwIsop/XqQRBAgMBAAGjUDBOMB0GA1UdDgQWBBQCJmcoa/F7aM3jIFN7Bd4uzWRgzjAfBgNVHSMEGDAWgBQCJmcoa/F7aM3jIFN7Bd4uzWRgzjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBd80GpWKjp1J+Dgp0blVAox1s/WPWQlex9xrx1GEYbc5elp3svS+S82s7dFm2llHrrNOBt1HZVC+TdW4f+MR1xq8O5lOYjDRsosxZc/u9jVsYWYc3M9bQAx8VyJ8VGpcAK+fLqRNabYlqTnj/t9bzX8fS90sp8JsALV4g84Aj0G8RpYJokw+pJUmOpuxsZN5U84MmLPnVfmrnuCVh/HkiLNV2c8Pk8LSomg6q1M1dQUTsz/HVxcOhHLj/owwh3IzXf/KXV/E8vSYW8o4WWCAnruYOWdJMI4Z8NG1Mfv7zvb7U3FL1C/KLV04DqzALXGj+LVmxtDvuxqC042apoIDQV',
  18. /**
  19. * Returns the full path to the (local) file that contains the X509 pem certificate.
  20. *
  21. * @return null|string
  22. */
  23. "certificateFile" => "",
  24. /**
  25. * @return null|string
  26. */
  27. "entityId" => "https://test.fake/saml/metadata",
  28. /**
  29. * @return null|bool
  30. */
  31. "assertionEncryptionEnabled" => true,
  32. "assertionConsumerUrl" => "https://test.fake/saml/acs",
  33. "assertionConsumerBinding" => \SAML2_Const::BINDING_HTTP_POST,
  34. "singleLogoutUrl" => "https://test.fake/saml/sls",
  35. "singleLogoutBinding" => \SAML2_Const::BINDING_HTTP_REDIRECT,
  36. "nameIdFormat" => \SAML2_Const::NAMEID_PERSISTENT,
  37. "nameIdValue" => function (UserInterface $user) {
  38. /** @var User $user */
  39. return $user->getEmailCanonical();
  40. },
  41. "NameQualifier" => 'test.fake',
  42. "wantSignedAuthnRequest" => true,
  43. "wantSignedAuthnResponse" => true,
  44. "wantSignedAssertions" => false,
  45. "wantSignedLogoutRequest" => false,
  46. "wantSignedLogoutResponse" => false,
  47. "attributes" => [
  48. 'User.Email' => function (UserInterface $user) {
  49. /** @var User $user */
  50. return $user->getEmailCanonical();
  51. },
  52. 'User.Username' => function (UserInterface $user) {
  53. /** @var User $user */
  54. return $user->getName();
  55. },
  56. 'first_name' => function (UserInterface $user) {
  57. /** @var User $user */
  58. return $user->getFirstName();
  59. },
  60. 'last_name' => function (UserInterface $user) {
  61. /** @var User $user */
  62. return $user->getLastName();
  63. },
  64. ],
  65. "validAudiences" => [
  66. "https://test.fake/saml/acs",
  67. ],
  68. "assertionNotBeforeInterval" => new \DateInterval('PT0S'),
  69. "assertionNotOnOrAfterInterval" => new \DateInterval('PT5M'),
  70. "assertionSessionNotOnOrAfterInterval" => new \DateInterval('P1D'),
  71. ]
  72. );
  73. }
  74. /**
  75. * @param string $entityId
  76. * @return ServiceProvider
  77. */
  78. public function getServiceProvider($entityId)
  79. {
  80. return $this->hasServiceProvider($entityId) ? $this->spMap[$entityId] : null;
  81. }
  82. /**
  83. * @param string $entityId
  84. * @return bool
  85. */
  86. public function hasServiceProvider($entityId)
  87. {
  88. return array_key_exists($entityId, $this->spMap);
  89. }
  90. }
Slack example
  1. <?php
  2. $this->spMap["https://slack.com"] = new ServiceProvider(
  3. [
  4. /**
  5. * Returns the contents of an X509 pem certificate, without the '-----BEGIN CERTIFICATE-----' and
  6. * '-----END CERTIFICATE-----'.
  7. *
  8. * @return null|string
  9. */
  10. 'certificateData' => 'MIIDrzCCApagAwIBAgIBADANBgkqhkiG9w0BAQ0FADBxMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEhMB8GA1UECgwYU2xhY2sgVGVjaG5vbG9naWVzLCBJbmMuMRIwEAYDVQQDDAlzbGFjay5jb20xFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28wHhcNMTUwMzE3MDEyMzMyWhcNMjUwMzE0MDEyMzMyWjBxMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEhMB8GA1UECgwYU2xhY2sgVGVjaG5vbG9naWVzLCBJbmMuMRIwEAYDVQQDDAlzbGFjay5jb20xFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28wggEjMA0GCSqGSIb3DQEBAQUAA4IBEAAwggELAoIBAgDB0y4ruySosz1GX/3KI1jp4oivxtnXLeMwKELrBgG+rZ8pl+UMhLG2iCp0nbnwSxXVU0ONJVI3SSzJ5VQtBHHCA4UAzse0HRaSZfBs+6urKoMLf8iusBYk62f2g/RAPjsMVcjC8B3FHyhaD9OnWSdJ7uGopmwwEhDiwf/gdS9Uw8FojYDuVprODfmj7+fgWPkGTf8TRGaHjudjuP1LMDRAz2cI0ym09jbnW8BVynSjjUrE+K9ri1uWzT2tp49OHqSgjaXkWWY6prFa9MT8jsibe02Id2i5+h0c4F892O7MybNWgF139dMGapmW4rf3GT7brLZEO4sZPwovhlj3b6U+8wIDAQABo1AwTjAdBgNVHQ4EFgQUa2YVk5yi+WMxLT/q7rokAfzyvU0wHwYDVR0jBBgwFoAUa2YVk5yi+WMxLT/q7rokAfzyvU0wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQ0FAAOCAQIAUwv53vh2LkgbJbBGyRlSkjAZyybwM7pO6TtQ4SHyn366SG1lZXkc9S9u8m4kMETDquOujC/fZLiAe4f8rZ8+ZXV0f17FL/RhMDzVBv6DgDabfpXAkt+Yn+ZIThFi2D7L4jyJzZPbaf7soCu1e/Dx0CBhm/Lz2nsny6Il7rkEbDB7gBpjZODMMi/PEJ5I462JUrj+9aSZBtx2/NXIoFkLZ1B4j3UG+WJhcYMlMBim/GTimKS7yzkvfqdADmIAaO0RPYduNPds6Dyjyjbqj3XR3WwdsmTorO95UKitRGu10ImwByXo2xzQCwGNP8WuRAmWVIlisLLNEDKTnZDb38085gY=',
  11. /**
  12. * Returns the full path to the (local) file that contains the X509 pem certificate.
  13. *
  14. * @return null|string
  15. */
  16. "certificateFile" => "",
  17. /**
  18. * @return null|string
  19. */
  20. "entityId" => "https://slack.com",
  21. /**
  22. * @return null|bool
  23. */
  24. "assertionEncryptionEnabled" => true,
  25. "assertionConsumerUrl" => "https://$slackTeamName.slack.com/sso/saml",
  26. "assertionConsumerBinding" => \SAML2_Const::BINDING_HTTP_POST,
  27. "singleLogoutUrl" => "https://$slackTeamName.slack.com/sso/saml/logout",
  28. "singleLogoutBinding" => \SAML2_Const::BINDING_HTTP_REDIRECT,
  29. "nameIdFormat" => \SAML2_Const::NAMEID_PERSISTENT,
  30. "nameIdValue" => function (UserInterface $user) {
  31. /** @var User $user */
  32. return $user->getEmailCanonical();
  33. },
  34. "NameQualifier" => "$slackTeamName.slack.com",
  35. "wantSignedAuthnRequest" => true,
  36. "wantSignedAuthnResponse" => true,
  37. "wantSignedAssertions" => false,
  38. "attributes" => [
  39. 'User.Email' => function (UserInterface $user) {
  40. /** @var User $user */
  41. return $user->getEmailCanonical();
  42. },
  43. 'User.Username' => function (UserInterface $user) {
  44. /** @var User $user */
  45. return $user->getName();
  46. },
  47. 'first_name' => function (UserInterface $user) {
  48. /** @var User $user */
  49. return $user->getFirstName();
  50. },
  51. 'last_name' => function (UserInterface $user) {
  52. /** @var User $user */
  53. return $user->getLastName();
  54. },
  55. ],
  56. ]
  57. );
Freshdesk example
  1. <?php
  2. $this->spMap["https://$freshdeskAccountName.freshdesk.com"] = new ServiceProvider(
  3. [
  4. /**
  5. * Returns the contents of an X509 pem certificate, without the '-----BEGIN CERTIFICATE-----' and
  6. * '-----END CERTIFICATE-----'.
  7. *
  8. * @return null|string
  9. */
  10. 'certificateData' => '',
  11. /**
  12. * Returns the full path to the (local) file that contains the X509 pem certificate.
  13. *
  14. * @return null|string
  15. */
  16. "certificateFile" => "",
  17. /**
  18. * @return null|string
  19. */
  20. "entityId" => "https://$freshdeskAccountName.freshdesk.com",
  21. /**
  22. * @return null|bool
  23. */
  24. "assertionEncryptionEnabled" => false,
  25. "assertionConsumerUrl" => "https://$freshdeskAccountName.freshdesk.com/login/saml",
  26. "assertionConsumerBinding" => \SAML2_Const::BINDING_HTTP_POST,
  27. "singleLogoutUrl" => "https://$freshdeskAccountName.freshdesk.com/logout/saml",
  28. "singleLogoutBinding" => \SAML2_Const::BINDING_HTTP_REDIRECT,
  29. "nameIdFormat" => 'urn:oasis:names:tc:SAML:2.0:nameid-format:email',
  30. "nameIdValue" => function (UserInterface $user) {
  31. /** @var User $user */
  32. return $user->getEmailCanonical();
  33. },
  34. "NameQualifier" => "$freshdeskAccountName.freshdesk.com",
  35. "wantSignedAuthnRequest" => false,
  36. "wantSignedAuthnResponse" => false,
  37. "wantSignedAssertions" => true,
  38. "attributes" => [
  39. 'email' => function (UserInterface $user) {
  40. /** @var User $user */
  41. return $user->getEmailCanonical();
  42. },
  43. 'name' => function (UserInterface $user) {
  44. /** @var User $user */
  45. return $user->getName();
  46. },
  47. 'given_name' => function (UserInterface $user) {
  48. /** @var User $user */
  49. return $user->getFirstName();
  50. },
  51. 'family_name' => function (UserInterface $user) {
  52. /** @var User $user */
  53. return $user->getLastName();
  54. },
  55. ],
  56. ]
  57. );
NewRelic example
  1. <?php
  2. $this->spMap["rpm.newrelic.com"] = new ServiceProvider(
  3. [
  4. /**
  5. * Returns the contents of an X509 pem certificate, without the '-----BEGIN CERTIFICATE-----' and
  6. * '-----END CERTIFICATE-----'.
  7. *
  8. * @return null|string
  9. */
  10. 'certificateData' => '',
  11. /**
  12. * Returns the full path to the (local) file that contains the X509 pem certificate.
  13. *
  14. * @return null|string
  15. */
  16. "certificateFile" => "",
  17. /**
  18. * @return null|string
  19. */
  20. "entityId" => "rpm.newrelic.com",
  21. /**
  22. * @return null|bool
  23. */
  24. "assertionEncryptionEnabled" => false,
  25. "assertionConsumerUrl" => "https://rpm.newrelic.com/accounts/$accountId/sso/saml/finalize",
  26. "assertionConsumerBinding" => \SAML2_Const::BINDING_HTTP_POST,
  27. "singleLogoutUrl" => "",
  28. "singleLogoutBinding" => \SAML2_Const::BINDING_HTTP_REDIRECT,
  29. "nameIdFormat" => 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress',
  30. "nameIdValue" => function (UserInterface $user) {
  31. /** @var User $user */
  32. return $user->getEmailCanonical();
  33. },
  34. "NameQualifier" => "rpm.newrelic.com",
  35. "wantSignedAuthnRequest" => false,
  36. "wantSignedAuthnResponse" => false,
  37. "wantSignedAssertions" => true,
  38. "attributes" => [],
  39. ]
  40. );

Note: Keep in mind that this is a example, you may retrieve ServiceProviders from database

Create the Controller

  1. <?php
  2. namespace Acme\SamlBundle\Controller;
  3. use Symfony\Bundle\FrameworkBundle\Controller\Controller;
  4. use Symfony\Component\HttpFoundation\Request;
  5. use Symfony\Component\Routing\Annotation\Route;
  6. /**
  7. * @Route("/saml")
  8. */
  9. class SamlController extends Controller
  10. {
  11. /**
  12. * @Route("/sso", name="acme_saml_sso")
  13. * @return \Symfony\Component\HttpFoundation\Response
  14. */
  15. public function singleSignOnAction(Request $httpRequest)
  16. {
  17. $idpProcessor = $this->get("adactive_sas_saml2_bridge.processor.hosted_idp");
  18. return $idpProcessor->processSingleSignOn($httpRequest);
  19. }
  20. /**
  21. * @Route("/sls", name="acme_saml_sls")
  22. * @return \Symfony\Component\HttpFoundation\Response
  23. */
  24. public function singleLogoutAction(Request $httpRequest)
  25. {
  26. $idpProcessor = $this->get("adactive_sas_saml2_bridge.processor.hosted_idp");
  27. return $idpProcessor->processSingleLogoutService($httpRequest);
  28. }
  29. /**
  30. * @Route("/metadata", name="acme_saml_metadata", defaults={"_format"="xml"})
  31. *
  32. * @return \Symfony\Component\HttpFoundation\Response
  33. */
  34. public function metadataAction()
  35. {
  36. $idpProcessor = $this->get("adactive_sas_saml2_bridge.processor.hosted_idp");
  37. return $idpProcessor->getMetadataXmlResponse();
  38. }
  39. }

Define services

  1. <?xml version="1.0" ?>
  2. <container xmlns="http://symfony.com/schema/dic/services"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
  5. <services>
  6. <service id="acme.saml.service_provider_repository" class="Acme\SamlBundle\Entity\SamlServiceProviderRepository">
  7. </service>
  8. </services>
  9. </container>

Configuration

  1. adactive_sas_saml2_bridge:
  2. hosted:
  3. metadata_route: acme_saml_metadata
  4. identity_provider:
  5. enabled: true
  6. service_provider_repository: acme.saml.service_provider_repository
  7. sso_route: acme_saml_sso
  8. sls_route: acme_saml_sls
  9. login_route: fos_user_security_login
  10. logout_route: fos_user_security_logout
  11. public_key: '%kernel.root_dir%/../vendor/adactive-sas/saml2-bridge-bundle/src/Resources/keys/development_publickey.cer'
  12. private_key: '%kernel.root_dir%/../vendor/adactive-sas/saml2-bridge-bundle/src/Resources/keys/development_privatekey.pem'

Note: this is development keys, never use them in production !

Tests

We are aware that this bundle really miss tests, this would come in next releases.

Contributing

For the time being, this bundle is very limited but is designed to be support all SAML2 process.

So feel free to create issue and pull-request in order to help us making this bundle a bit more complete.