19 declare(strict_types=1);
    39         $source = $this->create->sourceById($auth->
getAuthId());
    40         $config = $source->getMetadata();
    45             'entityid' => $source->getEntityId(),
    46             'metadata-set' => 
'saml20-sp-remote',
    47             'SingleLogoutService' => $this->
singleLogoutService($config, ILIAS_HTTP_PATH, $source->getAuthId()),
    48             'AssertionConsumerService' => $acs[
'services'],
    60         $builder = $this->create->builder($source->getEntityId());
    61         $builder->addMetadataSP20($metadata_sp20, $acs[
'supported_protocols']);
    62         $builder->addOrganizationInfo($metadata_sp20);
    64         $xml = $builder->getEntityDescriptorText();
    65         $xml = $this->create->sign($xml, $config->toArray(), 
'SAML 2 SP');
    70     private function singleLogoutService(Configuration $config, 
string $logout_url, 
string $source_id): array
    72         $logout_url = $logout_url . 
'/module.php/saml/sp/saml2-logout.php/' . $source_id;
    73         $store = $this->create->store();
    75         $bindings = $config->getOptionalArray(
'SingleLogoutServiceBinding', [
    76             Constants::BINDING_HTTP_REDIRECT,
    77             Constants::BINDING_SOAP,
    80         $bindings = $store instanceof SQLStore ?
    82                   array_values(array_filter($bindings, fn(
string $b): 
bool => $b !== Constants::BINDING_SOAP));
    84         return array_map(fn(
string $b): array => [
    86             'Location' => $config->getOptionalString(
'SingleLogoutServiceLocation', $logout_url),
    92         $services = $config->getOptionalArray(
'acs.Bindings', array_keys($default));
    94         $services = array_intersect($services, array_keys($default));
    96         $services = array_map(fn(
string $service, 
int $index): array => array_merge($default[$service] ?? [], [
    98         ]), $services, range(0, count($services) - 1));
   101             'services' => array_map($this->
removeKey(
'Protocol'), $services),
   102             'supported_protocols' => array_unique(array_values(array_column($services, 
'Protocol'))),
   109             'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST' => [
   110                 'Binding' => Constants::BINDING_HTTP_POST,
   111                 'Location' => sprintf(
'%s/module.php/saml/sp/saml2-acs.php/%s', $base_url, $source_id),
   112                 'Protocol' => Constants::NS_SAMLP,
   114             'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact' => [
   115                 'Binding' => 
'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact',
   116                 'Location' => sprintf(
'%s/module.php/saml/sp/saml2-acs.php/%s', $base_url, $source_id),
   117                 'Protocol' => Constants::NS_SAMLP,
   121         if ($config->getOptionalString(
'ProtocolBinding', 
'') === 
'urn:oasis:names:tc:SAML:2.0:profiles:holder-of-key:SSO:browser') {
   122             $default[
'urn:oasis:names:tc:SAML:2.0:profiles:holder-of-key:SSO:browser'] = [
   123                 'Binding' => 
'urn:oasis:names:tc:SAML:2.0:profiles:holder-of-key:SSO:browser',
   124                 'hoksso:ProtocolBinding' => Constants::BINDING_HTTP_REDIRECT,
   125                 'Location' => sprintf(
"%s/module.php/saml2-acs/%s", $base_url, $source_id),
   126                 'Protocol' => Constants::NS_SAMLP,
   135         $format = $config->getOptionalValue(
'NameIDPolicy', null);
   136         return match (gettype($format)) {
   138                 'NameIDFormat' => $this->create->configFromArray($format)->getString(
'Format', Constants::NAMEID_TRANSIENT),
   140             'string' => [
'NameIDFormat' => $format],
   147         if (!$config->hasValue(
'name') || !$config->hasValue(
'attributes')) {
   152             'name' => $config->getLocalizedString(
'name'),
   153             'attributes' => $config->getArray(
'attributes'),
   157             [
'attributes.required', 
'getArray'],
   158             [
'description', 
'getString'],
   159             [
'attributes.NameFormat', 
'getString'],
   160             [
'attributes.index', 
'getInteger'],
   161             [
'attributes.isDefault', 
'getBoolean'],
   168         if ($config->hasValue(
'OrganizationName')) {
   169             $array[
'OrganizationName'] = $config->getLocalizedString(
'OrganizationName');
   171             $array = array_merge($array, $this->
addIfExists($config, 
'OrganizationDisplayName', 
'getLocalizedString'));
   173             if (!$config->hasValue(
'OrganizationURL')) {
   174                 throw new Exception(
'If OrganizationName is set, OrganizationURL must also be set.');
   176             $array[
'OrganizationURL'] = $config->getLocalizedString(
'OrganizationURL');
   179         foreach ($config->getOptionalArray(
'contacts', []) as $contact) {
   180             $array[
'contacts'][] = $this->create->contact($contact);
   184         if ($config->hasValue(
'technicalcontact_email') && $config->getString(
'technicalcontact_email') !== 
'na@example.org') {
   186                 'emailAddress' => $config->getString(
'technicalcontact_email'),
   187                 'name' => $config->getOptionalString(
'technicalcontact_name', null),
   188                 'contactType' => 
'technical',
   190             $array[
'contacts'][] = $this->create->contact($techcontact);
   198         $crypt = $this->create->crypt();
   200         $key = $this->
buildCertData($crypt->loadPublicKey($config, 
false, 
'new_'), 
true);
   201         $has_new_cert = 
$key !== null;
   203         $keys = array_values(array_filter([
   205             $this->
buildCertData($crypt->loadPublicKey($config), !$has_new_cert),
   208         if (count($keys) === 1) {
   209             return [
'certData' => $keys[0][
'X509Certificate']];
   210         } elseif (count($keys) > 1) {
   211             return [
'keys' => $keys];
   219         if ($cert_info[
'certData'] ?? 
false) {
   221                 'type' => 
'X509Certificate',
   223                 'encryption' => $encryption,
   224                 'X509Certificate' => $cert_info[
'certData'],
   234             [
'EntityAttributes', 
'getArray'],
   235             [
'UIInfo', 
'getArray'],
   236             [
'RegistrationInfo', 
'getArray'],
   237             [
'WantAssertionsSigned', 
'getBoolean', 
'saml20.sign.assertion'],
   238             [
'redirect.sign', 
'getBoolean', 
'redirect.validate'],
   239             [
'sign.authnrequest', 
'getBoolean', 
'validate.authnrequest'],
   249         return function (array 
$a) use (
$key) {
   260         return array_merge(...$list);
   263     private function addIfExists(Configuration $config, 
string $needle, 
string $selector = 
'getValue', ?
string $as_key = null): array
   265         return $config->hasValue($needle) ?
   266             [$as_key ?? $needle => $config->$selector($needle)] :
   276             fn($pair): array => $this->
addIfExists($config, ...(is_array($pair) ? $pair : [$pair])),
 
$a
thx to https://mlocati.github.io/php-cs-fixer-configurator for the examples