~fkooman/php-saml-sp

a2f7f02ce9e6ef34015c1eaae2617a05c309c74c — Fran├žois Kooman 2 months ago d4860fa
Introduce a template for eduPersonTargetedID for determining format of "User ID"

Fixes: https://codeberg.org/fkooman/php-saml-sp/issues/2
M benchmarks/ResponseBench.php => benchmarks/ResponseBench.php +2 -1
@@ 53,7 53,8 @@ class ResponseBench
                'http://localhost:8081/acs',
                'http://localhost:8081/slo',
                false,
                ['en-US' => 'My SP', 'nl-NL' => 'Mijn SP']
                ['en-US' => 'My SP', 'nl-NL' => 'Mijn SP'],
                '{{USER_ID}}'
            ),
            new IdpInfo('http://localhost:8080/metadata.php', 'Test', 'http://localhost:8080/sso.php', null, [PublicKey::fromFile(\dirname(__DIR__) . '/tests/data/certs/FrkoIdP.crt')], []),
            $samlResponse,

M src/Response.php => src/Response.php +18 -1
@@ 252,7 252,7 @@ class Response
                $attributeValue = $nameId->nameIdValue();
            }

            return sprintf('%s!%s!%s', $idpInfo->getEntityId(), $spInfo->getEntityId(), $attributeValue);
            return self::prepareTargetedId($spInfo->targetedIdTemplate(), $idpInfo->getEntityId(), $spInfo->getEntityId(), $attributeValue);
        }

        // filter "scoped" attributes


@@ 323,4 323,21 @@ class Response

        return $attributeValue;
    }

    private static function prepareTargetedId(string $targetedIdTemplate, string $idpEntityId, string $spEntityId, string $userId): string
    {
        return str_replace(
            [
                '{{IDP_ENTITY_ID}}',
                '{{SP_ENTITY_ID}}',
                '{{USER_ID}}',
            ],
            [
                $idpEntityId,
                $spEntityId,
                $userId,
            ],
            $targetedIdTemplate
        );
    }
}

M src/SpInfo.php => src/SpInfo.php +9 -1
@@ 49,6 49,8 @@ class SpInfo
    /** @var array<string,string> */
    private $displayNameList;

    private string $targetedIdTemplate;

    /**
     * @param string               $entityId          SP entityID
     * @param string               $acsUrl            AssertionConsumerService URL


@@ 56,7 58,7 @@ class SpInfo
     * @param bool                 $requireEncryption
     * @param array<string,string> $displayNameList
     */
    public function __construct($entityId, CryptoKeys $cryptoKeys, $acsUrl, $sloUrl, $requireEncryption, array $displayNameList)
    public function __construct($entityId, CryptoKeys $cryptoKeys, $acsUrl, $sloUrl, $requireEncryption, array $displayNameList, string $targetedIdTemplate)
    {
        $this->entityId = $entityId;
        $this->cryptoKeys = $cryptoKeys;


@@ 64,6 66,7 @@ class SpInfo
        $this->sloUrl = $sloUrl;
        $this->requireEncryption = $requireEncryption;
        $this->displayNameList = $displayNameList;
        $this->targetedIdTemplate = $targetedIdTemplate;
    }

    /**


@@ 117,4 120,9 @@ class SpInfo
    {
        return $this->displayNameList;
    }

    public function targetedIdTemplate(): string
    {
        return $this->targetedIdTemplate;
    }
}

M src/Web/Config.php => src/Web/Config.php +12 -0
@@ 233,6 233,18 @@ class Config
        return $metadataList;
    }

    public function targetedIdTemplate(): string
    {
        if (!array_key_exists('targetedIdTemplate', $this->configData)) {
            return '{{IDP_ENTITY_ID}}!{{SP_ENTITY_ID}}!{{USER_ID}}';
        }
        if (!is_string($this->configData['targetedIdTemplate'])) {
            throw new ConfigException('"targetedIdTemplate" MUST be of type "string"');
        }

        return $this->configData['targetedIdTemplate'];
    }

    /**
     * @param string $k
     *

M tests/ResponseTest.php => tests/ResponseTest.php +32 -16
@@ 54,7 54,8 @@ class ResponseTest extends TestCase
                'https://portal.uia.eduvpn.no/php-saml-sp/acs',
                'https://portal.uia.eduvpn.no/php-saml-sp/slo',
                false,
                ['en-US' => 'My SP', 'nl-NL' => 'Mijn SP']
                ['en-US' => 'My SP', 'nl-NL' => 'Mijn SP'],
                '{{USER_ID}}'
            ),
            new IdpInfo('https://sts.windows.net/8482881e-3699-4b3f-b135-cf4800bc1efb/', 'Test', 'http://localhost:8080/sso.php', null, [PublicKey::fromFile(__DIR__ . '/data/certs/azure_idp_assertion.crt')], []),
            $samlResponse,


@@ 76,7 77,8 @@ class ResponseTest extends TestCase
                'http://localhost:8081/acs',
                'http://localhost:8081/slo',
                false,
                ['en-US' => 'My SP', 'nl-NL' => 'Mijn SP']
                ['en-US' => 'My SP', 'nl-NL' => 'Mijn SP'],
                '{{USER_ID}}'
            ),
            new IdpInfo('http://localhost:8080/metadata.php', 'Test', 'http://localhost:8080/sso.php', null, [PublicKey::fromFile(__DIR__ . '/data/certs/FrkoIdP.crt')], []),
            $samlResponse,


@@ 125,7 127,8 @@ class ResponseTest extends TestCase
                'http://localhost:8082/acs',
                'http://localhost:8082/slo',
                false,
                ['en-US' => 'My SP', 'nl-NL' => 'Mijn SP']
                ['en-US' => 'My SP', 'nl-NL' => 'Mijn SP'],
                '{{USER_ID}}'
            ),
            new IdpInfo('https://idp.tuxed.net/saml/metadata', 'Test', 'https://idp.tuxed.net/saml/sso', null, [PublicKey::fromFile(__DIR__ . '/data/certs/idp.tuxed.net.crt')], []),
            $samlResponse,


@@ 147,7 150,8 @@ class ResponseTest extends TestCase
                'https://eduvpn3.pionier.net.pl/php-saml-sp/acs',
                'https://eduvpn3.pionier.net.pl/php-saml-sp/slo',
                false,
                ['en-US' => 'My SP', 'nl-NL' => 'Mijn SP']
                ['en-US' => 'My SP', 'nl-NL' => 'Mijn SP'],
                '{{USER_ID}}'
            ),
            new IdpInfo('https://sso.man.poznan.pl/idp-test/shibboleth', 'Test', 'http://localhost:8080/sso.php', null, [PublicKey::fromFile(__DIR__ . '/data/certs/inclusive-namespaces.crt')], []),
            $samlResponse,


@@ 169,7 173,8 @@ class ResponseTest extends TestCase
                'https://labrat.eduvpn.nl/saml/postResponse',
                'https://labrat.eduvpn.nl/saml/postResponse',
                false,
                ['en-US' => 'My SP', 'nl-NL' => 'Mijn SP']
                ['en-US' => 'My SP', 'nl-NL' => 'Mijn SP'],
                '{{IDP_ENTITY_ID}}!{{SP_ENTITY_ID}}!{{USER_ID}}'
            ),
            new IdpInfo('https://idp.surfnet.nl', 'Test', 'http://localhost:8080/sso.php', null, [PublicKey::fromFile(__DIR__ . '/data/certs/SURFconext.crt')], []),
            $samlResponse,


@@ 213,7 218,8 @@ class ResponseTest extends TestCase
                    'https://labrat.eduvpn.nl/saml/postResponse',
                    'https://labrat.eduvpn.nl/saml/postResponse',
                    false,
                    ['en-US' => 'My SP', 'nl-NL' => 'Mijn SP']
                    ['en-US' => 'My SP', 'nl-NL' => 'Mijn SP'],
                    '{{USER_ID}}'
                ),
                new IdpInfo('https://idp.surfnet.nl', 'Test', 'http://localhost:8080/sso.php', null, [PublicKey::fromFile(__DIR__ . '/data/certs/SURFconext.crt')], []),
                $samlResponse,


@@ 270,7 276,8 @@ class ResponseTest extends TestCase
                    'http://localhost:8081/acs',
                    'http://localhost:8081/slo',
                    false,
                    ['en-US' => 'My SP', 'nl-NL' => 'Mijn SP']
                    ['en-US' => 'My SP', 'nl-NL' => 'Mijn SP'],
                    '{{USER_ID}}'
                ),
                new IdpInfo('http://localhost:8080/metadata.php', 'Test', 'http://localhost:8080/sso.php', null, [PublicKey::fromFile(__DIR__ . '/data/certs/FrkoIdP.crt')], []),
                $samlResponse,


@@ 296,7 303,8 @@ class ResponseTest extends TestCase
                    'http://localhost:8081/acs',
                    'http://localhost:8081/slo',
                    false,
                    ['en-US' => 'My SP', 'nl-NL' => 'Mijn SP']
                    ['en-US' => 'My SP', 'nl-NL' => 'Mijn SP'],
                    '{{USER_ID}}'
                ),
                new IdpInfo('http://localhost:8080/metadata.php', 'Test', 'http://localhost:8080/sso.php', null, [PublicKey::fromFile(__DIR__ . '/data/certs/simpleSAMLphp.crt')], []),
                $samlResponse,


@@ 322,7 330,8 @@ class ResponseTest extends TestCase
                    'http://localhost:8081/acs',
                    'http://localhost:8081/slo',
                    false,
                    ['en-US' => 'My SP', 'nl-NL' => 'Mijn SP']
                    ['en-US' => 'My SP', 'nl-NL' => 'Mijn SP'],
                    '{{USER_ID}}'
                ),
                new IdpInfo('http://localhost:8080/metadata.php', 'Test', 'http://localhost:8080/sso.php', null, [PublicKey::fromFile(__DIR__ . '/data/certs/FrkoIdP.crt')], []),
                $samlResponse,


@@ 348,7 357,8 @@ class ResponseTest extends TestCase
                    'http://localhost:8081/acs',
                    'http://localhost:8081/slo',
                    false,
                    ['en-US' => 'My SP', 'nl-NL' => 'Mijn SP']
                    ['en-US' => 'My SP', 'nl-NL' => 'Mijn SP'],
                    '{{USER_ID}}'
                ),
                new IdpInfo('http://localhost:8080/metadata.php', 'Test', 'http://localhost:8080/sso.php', null, [PublicKey::fromFile(__DIR__ . '/data/certs/FrkoIdP.crt')], []),
                $samlResponse,


@@ 374,7 384,8 @@ class ResponseTest extends TestCase
                    'http://localhost:8081/acs',
                    'http://localhost:8081/slo',
                    false,
                    ['en-US' => 'My SP', 'nl-NL' => 'Mijn SP']
                    ['en-US' => 'My SP', 'nl-NL' => 'Mijn SP'],
                    '{{USER_ID}}'
                ),
                new IdpInfo('https://x509idp.moonshot.utr.surfcloud.nl/metadata', 'Test', 'https://x509idp.moonshot.utr.surfcloud.nl/sso', null, [PublicKey::fromFile(__DIR__ . '/data/certs/x509idp.moonshot.utr.surfcloud.nl.crt')], []),
                $samlResponse,


@@ 428,7 439,8 @@ class ResponseTest extends TestCase
                    'https://kluitje.eduvpn.nl/portal/_saml/acs',
                    'https://kluitje.eduvpn.nl/portal/_saml/slo',
                    false,
                    ['en-US' => 'My SP', 'nl-NL' => 'Mijn SP']
                    ['en-US' => 'My SP', 'nl-NL' => 'Mijn SP'],
                    '{{USER_ID}}'
                ),
                new IdpInfo('https://sa-gw.test.surfconext.nl/authentication/metadata', 'Test', 'SSO', null, [PublicKey::fromFile(__DIR__ . '/data/certs/SURFsecureID.crt')], []),
                $samlResponse,


@@ 453,7 465,8 @@ class ResponseTest extends TestCase
                'https://demo.eduvpn.nl/portal/_saml/acs',
                'https://demo.eduvpn.nl/portal/_saml/slo',
                false,
                ['en-US' => 'My SP', 'nl-NL' => 'Mijn SP']
                ['en-US' => 'My SP', 'nl-NL' => 'Mijn SP'],
                '{{USER_ID}}'
            ),
            new IdpInfo('https://sa-gw.surfconext.nl/authentication/metadata', 'Test', 'http://localhost:8080/sso.php', null, [PublicKey::fromFile(__DIR__ . '/data/certs/SURFsecureID_production.crt')], []),
            $samlResponse,


@@ 479,7 492,8 @@ class ResponseTest extends TestCase
                'http://localhost:8081/acs',
                'http://localhost:8081/slo',
                false,
                ['en-US' => 'My SP', 'nl-NL' => 'Mijn SP']
                ['en-US' => 'My SP', 'nl-NL' => 'Mijn SP'],
                '{{USER_ID}}'
            ),
            new IdpInfo('http://localhost:8080/metadata.php', 'Test', 'http://localhost:8080/sso.php', null, [PublicKey::fromFile(__DIR__ . '/data/certs/FrkoIdP.crt')], ['example.com']),
            $samlResponse,


@@ 532,7 546,8 @@ class ResponseTest extends TestCase
                'http://localhost:8081/acs',
                'http://localhost:8081/slo',
                false,
                ['en-US' => 'My SP', 'nl-NL' => 'Mijn SP']
                ['en-US' => 'My SP', 'nl-NL' => 'Mijn SP'],
                '{{USER_ID}}'
            ),
            new IdpInfo('http://localhost:8080/metadata.php', 'Test', 'http://localhost:8080/sso.php', null, [PublicKey::fromFile(__DIR__ . '/data/certs/FrkoIdP.crt')], ['example.org']),
            $samlResponse,


@@ 589,7 604,8 @@ class ResponseTest extends TestCase
                'https://vpn.tuxed.net/vpn-user-portal/_saml/acs',
                'https://vpn.tuxed.net/vpn-user-portal/_saml/slo',
                true,
                ['en-US' => 'My SP', 'nl-NL' => 'Mijn SP']
                ['en-US' => 'My SP', 'nl-NL' => 'Mijn SP'],
                '{{IDP_ENTITY_ID}}!{{SP_ENTITY_ID}}!{{USER_ID}}'
            ),
            new IdpInfo('https://testidp3-dev.aai.dfn.de/idp/shibboleth', 'Test', 'SSO', null, [PublicKey::fromFile(__DIR__ . '/data/certs/shib_idp.crt')], []),
            $samlResponse,

M tests/SPTest.php => tests/SPTest.php +2 -1
@@ 56,7 56,8 @@ class SPTest extends TestCase
            'http://localhost:8081/acs',
            'http://localhost:8081/slo',
            false,
            ['en-US' => 'My SP', 'nl-NL' => 'Mijn SP']
            ['en-US' => 'My SP', 'nl-NL' => 'Mijn SP'],
            '{{USER_ID}'
        );
        $this->sp = new TestSP(
            $spInfo,

M web/index.php => web/index.php +2 -1
@@ 91,7 91,8 @@ try {
        $request->getRootUri() . 'acs',
        $request->getRootUri() . 'slo',
        $config->getRequireEncryption(),
        $config->getServiceNames()
        $config->getServiceNames(),
        $config->targetedIdTemplate()
    );
    $sp = new SP($spInfo, $idpSource, $seSession, $logger);
    $service = new Service($config, $tpl, $sp, $seCookie);