ILIAS  release_8 Revision v8.23
metadata.php
Go to the documentation of this file.
1 <?php
2 
19 // ilias-patch: begin
20 use SAML2\Constants;
35 
36 chdir(__DIR__);
37 
39 $cookie_path = dirname($_SERVER['PHP_SELF']);
40 
41 $i = 0;
42 while (!is_file($ilias_main_directory . 'ilias.ini.php') && $i < 20) {
43  $ilias_main_directory .= '../';
44  ++$i;
45 
46  $cookie_path = dirname($cookie_path);
47 }
49 
50 if (!is_file(getcwd() . '/ilias.ini.php')) {
51  die('Please ensure ILIAS is installed!');
52 }
53 
54 $cookie_path .= (!preg_match("/[\/|\\\\]$/", $cookie_path)) ? "/" : "";
55 
56 if (isset($_GET["client_id"])) {
57  if ($cookie_path === "\\") {
58  $cookie_path = '/';
59  }
60 
61  setcookie('ilClientId', $_GET['client_id'], 0, $cookie_path, '');
62  $_COOKIE['ilClientId'] = $_GET['client_id'];
63 }
64 define('IL_COOKIE_PATH', $cookie_path);
65 
66 require_once 'Services/Context/classes/class.ilContext.php';
68 
69 require_once 'Services/Init/classes/class.ilInitialisation.php';
71 
72 $iliasHttpPath = ILIAS_HTTP_PATH;
73 
74 require_once 'Services/Saml/classes/class.ilSamlAuthFactory.php';
76 $auth = $factory->auth();
77 
78 // The source code below is copied from the SimpleSAMLphp library and modified regarding the HTTP path
79 // ilias-patch: end
80 if (!array_key_exists('PATH_INFO', $_SERVER)) {
81  global $DIC;
82  $DIC->logger()->root()->warning('Missing "PATH_INFO" variable. This could be a false positive log entry, but you have to ensure a valid "PATH_INFO" setting for your HTTP server.');
83 }
84 
85 $config = Configuration::getInstance();
86 if ($config->getBoolean('admin.protectmetadata', false)) {
87  Auth::requireAdmin();
88 }
89 // ilias-patch: begin
90 //$sourceId = substr($_SERVER['PATH_INFO'], 1);
91 $sourceId = $auth->getAuthId();
92 // ilias-patch: end
93 $source = Source::getById($sourceId);
94 if ($source === null) {
95  throw new AuthSource($sourceId, 'Could not find authentication source.');
96 }
97 
98 if (!($source instanceof SP)) {
99  throw new AuthSource(
100  $sourceId,
101  'The authentication source is not a SAML Service Provider.'
102  );
103 }
104 
105 $entityId = $source->getEntityId();
106 $spconfig = $source->getMetadata();
107 $store = Store::getInstance();
108 
110 
112  Constants::BINDING_HTTP_REDIRECT,
113  Constants::BINDING_SOAP,
114 ];
115 
116 $slob = $spconfig->getArray('SingleLogoutServiceBinding', $slosvcdefault);
117 // ilias-patch: begin
118 $slol = $iliasHttpPath . '/saml2-logout.php/' . $sourceId . '/' . CLIENT_ID;
119 // ilias-patch: end
120 
121 foreach ($slob as $binding) {
122  if ($binding == Constants::BINDING_SOAP && !($store instanceof SQL)) {
123  // we cannot properly support SOAP logout
124  continue;
125  }
126  $metaArray20['SingleLogoutService'][] = [
127  'Binding' => $binding,
128  'Location' => $spconfig->getString('SingleLogoutServiceLocation', $slol),
129  ];
130 }
131 
133  'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST',
134  'urn:oasis:names:tc:SAML:1.0:profiles:browser-post',
135  'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact',
136  'urn:oasis:names:tc:SAML:1.0:profiles:artifact-01',
137 ];
138 
139 if ($spconfig->getString('ProtocolBinding', '') == 'urn:oasis:names:tc:SAML:2.0:profiles:holder-of-key:SSO:browser') {
140  $assertionsconsumerservicesdefault[] = 'urn:oasis:names:tc:SAML:2.0:profiles:holder-of-key:SSO:browser';
141 }
142 
144 
145 $index = 0;
146 $eps = [];
148 foreach ($assertionsconsumerservices as $services) {
149  $acsArray = ['index' => $index];
150  switch ($services) {
151  case 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST':
152  $acsArray['Binding'] = Constants::BINDING_HTTP_POST;
153  // ilias-patch: begin
154  $acsArray['Location'] = $iliasHttpPath . "/saml2-acs.php/{$sourceId}/" . CLIENT_ID;
155  // ilias-patch: end
156  if (!in_array(Constants::NS_SAMLP, $supported_protocols, true)) {
157  $supported_protocols[] = Constants::NS_SAMLP;
158  }
159  break;
160  case 'urn:oasis:names:tc:SAML:1.0:profiles:browser-post':
161  $acsArray['Binding'] = 'urn:oasis:names:tc:SAML:1.0:profiles:browser-post';
162  // ilias-patch: begin
163  $acsArray['Location'] = $iliasHttpPath . "/saml1-acs.php/{$sourceId}/" . CLIENT_ID;
164  // ilias-patch: end
165  if (!in_array('urn:oasis:names:tc:SAML:1.1:protocol', $supported_protocols, true)) {
166  $supported_protocols[] = 'urn:oasis:names:tc:SAML:1.1:protocol';
167  }
168  break;
169  case 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact':
170  $acsArray['Binding'] = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact';
171  // ilias-patch: begin
172  $acsArray['Location'] = $iliasHttpPath . "/saml2-acs.php/{$sourceId}/" . CLIENT_ID;
173  // ilias-patch: end
174  if (!in_array(Constants::NS_SAMLP, $supported_protocols, true)) {
175  $supported_protocols[] = Constants::NS_SAMLP;
176  }
177  break;
178  case 'urn:oasis:names:tc:SAML:1.0:profiles:artifact-01':
179  $acsArray['Binding'] = 'urn:oasis:names:tc:SAML:1.0:profiles:artifact-01';
180  // ilias-patch: begin
181  $acsArray['Location'] = $iliasHttpPath . "/saml1-acs.php/{$sourceId}/artifact/" . CLIENT_ID;
182  // ilias-patch: end
183  if (!in_array('urn:oasis:names:tc:SAML:1.1:protocol', $supported_protocols, true)) {
184  $supported_protocols[] = 'urn:oasis:names:tc:SAML:1.1:protocol';
185  }
186  break;
187  case 'urn:oasis:names:tc:SAML:2.0:profiles:holder-of-key:SSO:browser':
188  $acsArray['Binding'] = 'urn:oasis:names:tc:SAML:2.0:profiles:holder-of-key:SSO:browser';
189  // ilias-patch: begin
190  $acsArray['Location'] = $iliasHttpPath . "/saml2-acs.php/{$sourceId}/" . CLIENT_ID;
191  // ilias-patch: end
192  $acsArray['hoksso:ProtocolBinding'] = Constants::BINDING_HTTP_REDIRECT;
193  if (!in_array(Constants::NS_SAMLP, $supported_protocols, true)) {
194  $supported_protocols[] = Constants::NS_SAMLP;
195  }
196  break;
197  }
198  $eps[] = $acsArray;
199  $index++;
200 }
201 
202 $metaArray20['AssertionConsumerService'] = $eps;
203 
204 $keys = [];
205 $certInfo = Crypto::loadPublicKey($spconfig, false, 'new_');
206 if ($certInfo !== null && array_key_exists('certData', $certInfo)) {
207  $hasNewCert = true;
208 
209  $certData = $certInfo['certData'];
210 
211  $keys[] = [
212  'type' => 'X509Certificate',
213  'signing' => true,
214  'encryption' => true,
215  'X509Certificate' => $certInfo['certData'],
216  ];
217 } else {
218  $hasNewCert = false;
219 }
220 
221 $certInfo = Crypto::loadPublicKey($spconfig);
222 if ($certInfo !== null && array_key_exists('certData', $certInfo)) {
223  $certData = $certInfo['certData'];
224 
225  $keys[] = [
226  'type' => 'X509Certificate',
227  'signing' => true,
228  'encryption' => ($hasNewCert ? false : true),
229  'X509Certificate' => $certInfo['certData'],
230  ];
231 } else {
232  $certData = null;
233 }
234 
235 $format = $spconfig->getValue('NameIDPolicy', null);
236 if ($format !== null) {
237  if (is_array($format)) {
238  $metaArray20['NameIDFormat'] = Configuration::loadFromArray($format)->getString(
239  'Format',
240  Constants::NAMEID_TRANSIENT
241  );
242  } elseif (is_string($format)) {
243  $metaArray20['NameIDFormat'] = $format;
244  }
245 }
246 
247 $name = $spconfig->getLocalizedString('name', null);
248 $attributes = $spconfig->getArray('attributes', []);
249 
250 if ($name !== null && !empty($attributes)) {
251  $metaArray20['name'] = $name;
252  $metaArray20['attributes'] = $attributes;
253  $metaArray20['attributes.required'] = $spconfig->getArray('attributes.required', []);
254 
255  if (empty($metaArray20['attributes.required'])) {
256  unset($metaArray20['attributes.required']);
257  }
258 
259  $description = $spconfig->getArray('description', null);
260  if ($description !== null) {
261  $metaArray20['description'] = $description;
262  }
263 
264  $nameFormat = $spconfig->getString('attributes.NameFormat', null);
265  if ($nameFormat !== null) {
266  $metaArray20['attributes.NameFormat'] = $nameFormat;
267  }
268 
269  if ($spconfig->hasValue('attributes.index')) {
270  $metaArray20['attributes.index'] = $spconfig->getInteger('attributes.index', 0);
271  }
272 
273  if ($spconfig->hasValue('attributes.isDefault')) {
274  $metaArray20['attributes.isDefault'] = $spconfig->getBoolean('attributes.isDefault', false);
275  }
276 }
277 
278 // add organization info
279 $orgName = $spconfig->getLocalizedString('OrganizationName', null);
280 if ($orgName !== null) {
281  $metaArray20['OrganizationName'] = $orgName;
282 
283  $metaArray20['OrganizationDisplayName'] = $spconfig->getLocalizedString('OrganizationDisplayName', null);
284  if ($metaArray20['OrganizationDisplayName'] === null) {
285  $metaArray20['OrganizationDisplayName'] = $orgName;
286  }
287 
288  $metaArray20['OrganizationURL'] = $spconfig->getLocalizedString('OrganizationURL', null);
289  if ($metaArray20['OrganizationURL'] === null) {
290  throw new \SimpleSAML\Error\Exception('If OrganizationName is set, OrganizationURL must also be set.');
291  }
292 }
293 
294 if ($spconfig->hasValue('contacts')) {
295  $contacts = $spconfig->getArray('contacts');
296  foreach ($contacts as $contact) {
297  $metaArray20['contacts'][] = Metadata::getContact($contact);
298  }
299 }
300 
301 // add technical contact
302 $email = $config->getString('technicalcontact_email', 'na@example.org');
303 if ($email && $email !== 'na@example.org') {
304  $techcontact = [
305  'emailAddress' => $email,
306  'name' => $config->getString('technicalcontact_name', null),
307  'contactType' => 'technical'
308  ];
309  $metaArray20['contacts'][] = \SimpleSAML\Utils\Config\Metadata::getContact($techcontact);
310 }
311 
312 // add certificate
313 if (count($keys) === 1) {
314  $metaArray20['certData'] = $keys[0]['X509Certificate'];
315 } elseif (count($keys) > 1) {
316  $metaArray20['keys'] = $keys;
317 }
318 
319 // add EntityAttributes extension
320 if ($spconfig->hasValue('EntityAttributes')) {
321  $metaArray20['EntityAttributes'] = $spconfig->getArray('EntityAttributes');
322 }
323 
324 // add UIInfo extension
325 if ($spconfig->hasValue('UIInfo')) {
326  $metaArray20['UIInfo'] = $spconfig->getArray('UIInfo');
327 }
328 
329 // add RegistrationInfo extension
330 if ($spconfig->hasValue('RegistrationInfo')) {
331  $metaArray20['RegistrationInfo'] = $spconfig->getArray('RegistrationInfo');
332 }
333 
334 // add signature options
335 if ($spconfig->hasValue('WantAssertionsSigned')) {
336  $metaArray20['saml20.sign.assertion'] = $spconfig->getBoolean('WantAssertionsSigned');
337 }
338 if ($spconfig->hasValue('redirect.sign')) {
339  $metaArray20['redirect.validate'] = $spconfig->getBoolean('redirect.sign');
340 } elseif ($spconfig->hasValue('sign.authnrequest')) {
341  $metaArray20['validate.authnrequest'] = $spconfig->getBoolean('sign.authnrequest');
342 }
343 
344 $metaArray20['metadata-set'] = 'saml20-sp-remote';
345 $metaArray20['entityid'] = $entityId;
346 
347 $metaBuilder = new SAMLBuilder($entityId);
349 $metaBuilder->addOrganizationInfo($metaArray20);
350 
351 $xml = $metaBuilder->getEntityDescriptorText();
352 
353 unset($metaArray20['UIInfo'], $metaArray20['metadata-set'], $metaArray20['entityid']);
354 
355 // sanitize the attributes array to remove friendly names
356 if (isset($metaArray20['attributes']) && is_array($metaArray20['attributes'])) {
357  $metaArray20['attributes'] = array_values($metaArray20['attributes']);
358 }
359 
360 // sign the metadata if enabled
361 $xml = Signer::sign($xml, $spconfig->toArray(), 'SAML 2 SP');
362 
363 if (array_key_exists('output', $_REQUEST) && $_REQUEST['output'] == 'xhtml') {
364  $t = new Template($config, 'metadata.php', 'admin');
365 
366  $t->data['clipboard.js'] = true;
367  $t->data['header'] = 'saml20-sp'; // TODO: Replace with headerString in 2.0
368  $t->data['headerString'] = Translate::noop('metadata_saml20-sp');
369  $t->data['metadata'] = htmlspecialchars($xml);
370  $t->data['metadataflat'] = '$metadata[' . var_export($entityId, true) . '] = ' . VarExporter::export($metaArray20) . ';';
371  // ilias-patch: begin
372  $t->data['metaurl'] = $iliasHttpPath . "/metadata.php{$sourceId}/" . CLIENT_ID;
373  // ilias-patch: end
374  $t->show();
375 } else {
376  header('Content-Type: application/samlmetadata+xml');
377  // ilias-patch: begin
379  header("Content-Disposition:attachment; filename=\"" . $ascii_filename . "\"");
380  // ilias-patch: end
381  echo($xml);
382 }
$ilias_main_directory
Definition: metadata.php:38
$store
Definition: metadata.php:107
$attributes
Definition: metadata.php:248
$cookie_path
Definition: metadata.php:39
$slob
Definition: metadata.php:116
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
$metaArray20
Definition: metadata.php:109
if(!array_key_exists('PATH_INFO', $_SERVER)) $config
Definition: metadata.php:85
if($name !==null &&!empty($attributes)) $orgName
Definition: metadata.php:279
$slol
Definition: metadata.php:118
$index
Definition: metadata.php:145
static getASCIIFilename(string $a_filename)
$ascii_filename
Definition: metadata.php:378
global $DIC
Definition: feed.php:28
foreach($slob as $binding) $assertionsconsumerservicesdefault
Definition: metadata.php:132
$auth
Definition: metadata.php:76
if($format !==null) $name
Definition: metadata.php:247
static initILIAS()
ilias initialisation
const CONTEXT_SAML
$keys
Definition: metadata.php:204
$_GET['client_id']
Definition: saml1-acs.php:21
$iliasHttpPath
Definition: metadata.php:72
$_SERVER['HTTP_HOST']
Definition: raiseError.php:10
$spconfig
Definition: metadata.php:106
const CLIENT_ID
Definition: constants.php:41
$slosvcdefault
Definition: metadata.php:111
$format
Definition: metadata.php:235
$metaBuilder
Definition: metadata.php:347
$xml
Definition: metadata.php:351
if($spconfig->getString('ProtocolBinding', '')=='urn:oasis:names:tc:SAML:2.0:profiles:holder-of-key:SSO:browser') $assertionsconsumerservices
Definition: metadata.php:143
if($orgName !==null) if($spconfig->hasValue('contacts')) $email
Definition: metadata.php:302
$supported_protocols
Definition: metadata.php:147
$certInfo
Definition: metadata.php:205
static init(string $a_type)
Init context by type.
if($source===null) if(!($source instanceof SP)) $entityId
Definition: metadata.php:105
if($config->getBoolean('admin.protectmetadata', false)) $sourceId
Definition: metadata.php:91
Class ilSamlAuthFactory.
$eps
Definition: metadata.php:146
$_COOKIE[session_name()]
Definition: xapitoken.php:54
$source
Definition: metadata.php:93
$factory
Definition: metadata.php:75
$i
Definition: metadata.php:41