19 declare(strict_types=1);
38 $source = $this->create->sourceById($auth->
getAuthId());
39 $config = $source->getMetadata();
40 $base_url = rtrim(ILIAS_HTTP_PATH,
'/');
45 'entityid' => $source->getEntityId(),
46 'metadata-set' =>
'saml20-sp-remote',
47 'SingleLogoutService' => $this->
singleLogoutService($config, $base_url, $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,
static fn(
string $b):
bool => $b !== Constants::BINDING_SOAP));
84 return array_map(
static 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(
static fn(
string $service,
int $index): array => array_merge($default[$service] ?? [], [
98 ]), $services, range(0, count($services) - 1));
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'),
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 static 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])),
while($session_entry=$r->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) return null
$a
thx to https://mlocati.github.io/php-cs-fixer-configurator for the examples