ILIAS  release_5-3 Revision v5.3.23-19-g915713cf615
Message.php
Go to the documentation of this file.
1 <?php
2 
3 namespace SAML2;
4 
8 
18 abstract class Message implements SignedElement
19 {
25  protected $extensions;
26 
34  private $tagName;
35 
41  private $id;
42 
48  private $issueInstant;
49 
55  private $destination;
56 
62  private $consent = Constants::CONSENT_UNSPECIFIED;
63 
69  private $issuer;
70 
76  private $relayState;
77 
86  protected $document;
87 
95  private $signatureKey;
96 
100  protected $messageContainedSignatureUponConstruction = false;
101 
107  private $certificates;
108 
114  private $validators;
115 
120 
136  protected function __construct($tagName, \DOMElement $xml = null)
137  {
138  assert(is_string($tagName));
139  $this->tagName = $tagName;
140 
141  $this->id = Utils::getContainer()->generateId();
142  $this->issueInstant = Temporal::getTime();
143  $this->certificates = array();
144  $this->validators = array();
145 
146  if ($xml === null) {
147  return;
148  }
149 
150  if (!$xml->hasAttribute('ID')) {
151  throw new \Exception('Missing ID attribute on SAML message.');
152  }
153  $this->id = $xml->getAttribute('ID');
154 
155  if ($xml->getAttribute('Version') !== '2.0') {
156  /* Currently a very strict check. */
157  throw new \Exception('Unsupported version: '.$xml->getAttribute('Version'));
158  }
159 
160  $this->issueInstant = Utils::xsDateTimeToTimestamp($xml->getAttribute('IssueInstant'));
161 
162  if ($xml->hasAttribute('Destination')) {
163  $this->destination = $xml->getAttribute('Destination');
164  }
165 
166  if ($xml->hasAttribute('Consent')) {
167  $this->consent = $xml->getAttribute('Consent');
168  }
169 
170  $issuer = Utils::xpQuery($xml, './saml_assertion:Issuer');
171  if (!empty($issuer)) {
172  $this->issuer = new XML\saml\Issuer($issuer[0]);
173  if ($this->issuer->Format === Constants::NAMEID_ENTITY) {
174  $this->issuer = $this->issuer->value;
175  }
176  }
177 
178  $this->validateSignature($xml);
179 
180  $this->extensions = Extensions::getList($xml);
181  }
182 
183 
193  private function validateSignature(\DOMElement $xml)
194  {
195  try {
197  $signatureMethod = Utils::xpQuery($xml, './ds:Signature/ds:SignedInfo/ds:SignatureMethod/@Algorithm');
198 
199  $sig = Utils::validateElement($xml);
200 
201  if ($sig !== false) {
202  $this->messageContainedSignatureUponConstruction = true;
203  $this->certificates = $sig['Certificates'];
204  $this->validators[] = array(
205  'Function' => array('\SAML2\Utils', 'validateSignature'),
206  'Data' => $sig,
207  );
208  $this->signatureMethod = $signatureMethod[0]->value;
209  }
210  } catch (\Exception $e) {
211  // ignore signature validation errors
212  }
213  }
214 
215 
225  public function addValidator($function, $data)
226  {
227  assert(is_callable($function));
228 
229  $this->validators[] = array(
230  'Function' => $function,
231  'Data' => $data,
232  );
233  }
234 
248  public function validate(XMLSecurityKey $key)
249  {
250  if (count($this->validators) === 0) {
251  return false;
252  }
253 
254  $exceptions = array();
255 
256  foreach ($this->validators as $validator) {
257  $function = $validator['Function'];
258  $data = $validator['Data'];
259 
260  try {
261  call_user_func($function, $data, $key);
262  /* We were able to validate the message with this validator. */
263 
264  return true;
265  } catch (\Exception $e) {
266  $exceptions[] = $e;
267  }
268  }
269 
270  /* No validators were able to validate the message. */
271  throw $exceptions[0];
272  }
273 
279  public function getId()
280  {
281  return $this->id;
282  }
283 
289  public function setId($id)
290  {
291  assert(is_string($id));
292 
293  $this->id = $id;
294  }
295 
301  public function getIssueInstant()
302  {
303  return $this->issueInstant;
304  }
305 
311  public function setIssueInstant($issueInstant)
312  {
313  assert(is_int($issueInstant));
314 
315  $this->issueInstant = $issueInstant;
316  }
317 
323  public function getDestination()
324  {
325  return $this->destination;
326  }
327 
333  public function setDestination($destination)
334  {
335  assert(is_string($destination) || is_null($destination));
336 
337  $this->destination = $destination;
338  }
339 
349  public function setConsent($consent)
350  {
351  assert(is_string($consent));
352 
353  $this->consent = $consent;
354  }
355 
365  public function getConsent()
366  {
367  return $this->consent;
368  }
369 
375  public function getIssuer()
376  {
377  if (is_string($this->issuer) || $this->issuer instanceof XML\saml\Issuer) {
378  return $this->issuer;
379  }
380 
381  return null;
382  }
383 
389  public function setIssuer($issuer)
390  {
391  assert(is_string($issuer) || $issuer instanceof XML\saml\Issuer || is_null($issuer));
392 
393  $this->issuer = $issuer;
394  }
395 
402  {
403  return $this->messageContainedSignatureUponConstruction;
404  }
405 
411  public function getRelayState()
412  {
413  return $this->relayState;
414  }
415 
421  public function setRelayState($relayState)
422  {
423  assert(is_string($relayState) || is_null($relayState));
424 
425  $this->relayState = $relayState;
426  }
427 
435  public function toUnsignedXML()
436  {
437  $this->document = DOMDocumentFactory::create();
438 
439  $root = $this->document->createElementNS(Constants::NS_SAMLP, 'samlp:'.$this->tagName);
440  $this->document->appendChild($root);
441 
442  /* Ugly hack to add another namespace declaration to the root element. */
443  $root->setAttributeNS(Constants::NS_SAML, 'saml:tmp', 'tmp');
444  $root->removeAttributeNS(Constants::NS_SAML, 'tmp');
445 
446  $root->setAttribute('ID', $this->id);
447  $root->setAttribute('Version', '2.0');
448  $root->setAttribute('IssueInstant', gmdate('Y-m-d\TH:i:s\Z', $this->issueInstant));
449 
450  if ($this->destination !== null) {
451  $root->setAttribute('Destination', $this->destination);
452  }
453  if ($this->consent !== null && $this->consent !== Constants::CONSENT_UNSPECIFIED) {
454  $root->setAttribute('Consent', $this->consent);
455  }
456 
457  if ($this->issuer !== null) {
458  if (is_string($this->issuer)) {
459  Utils::addString($root, Constants::NS_SAML, 'saml:Issuer', $this->issuer);
460  } elseif ($this->issuer instanceof XML\saml\Issuer) {
461  $this->issuer->toXML($root);
462  }
463  }
464 
465  if (!empty($this->extensions)) {
466  Extensions::addList($root, $this->extensions);
467  }
468 
469  return $root;
470  }
471 
472 
481  public function toSignedXML()
482  {
483  $root = $this->toUnsignedXML();
484 
485  if ($this->signatureKey === null) {
486  /* We don't have a key to sign it with. */
487 
488  return $root;
489  }
490 
491  /* Find the position we should insert the signature node at. */
492  if ($this->issuer !== null) {
493  /*
494  * We have an issuer node. The signature node should come
495  * after the issuer node.
496  */
497  $issuerNode = $root->firstChild;
498  $insertBefore = $issuerNode->nextSibling;
499  } else {
500  /* No issuer node - the signature element should be the first element. */
501  $insertBefore = $root->firstChild;
502  }
503 
504  Utils::insertSignature($this->signatureKey, $this->certificates, $root, $insertBefore);
505 
506  return $root;
507  }
508 
514  public function getSignatureKey()
515  {
516  return $this->signatureKey;
517  }
518 
526  public function setSignatureKey(XMLSecurityKey $signatureKey = null)
527  {
528  $this->signatureKey = $signatureKey;
529  }
530 
539  {
540  $this->certificates = $certificates;
541  }
542 
548  public function getCertificates()
549  {
550  return $this->certificates;
551  }
552 
562  public static function fromXML(\DOMElement $xml)
563  {
564  if ($xml->namespaceURI !== Constants::NS_SAMLP) {
565  throw new \Exception('Unknown namespace of SAML message: '.var_export($xml->namespaceURI, true));
566  }
567 
568  switch ($xml->localName) {
569  case 'AttributeQuery':
570  return new AttributeQuery($xml);
571  case 'AuthnRequest':
572  return new AuthnRequest($xml);
573  case 'LogoutResponse':
574  return new LogoutResponse($xml);
575  case 'LogoutRequest':
576  return new LogoutRequest($xml);
577  case 'Response':
578  return new Response($xml);
579  case 'ArtifactResponse':
580  return new ArtifactResponse($xml);
581  case 'ArtifactResolve':
582  return new ArtifactResolve($xml);
583  default:
584  throw new \Exception('Unknown SAML message: '.var_export($xml->localName, true));
585  }
586  }
587 
593  public function getExtensions()
594  {
595  return $this->extensions;
596  }
597 
603  public function setExtensions($extensions)
604  {
605  assert(is_array($extensions) || is_null($extensions));
606 
607  $this->extensions = $extensions;
608  }
609 
613  public function getSignatureMethod()
614  {
615  return $this->signatureMethod;
616  }
617 }
setCertificates(array $certificates)
Set the certificates that should be included in the message.
Definition: Message.php:538
__construct($tagName, \DOMElement $xml=null)
Initialize a message.
Definition: Message.php:136
getExtensions()
Retrieve the Extensions.
Definition: Message.php:593
setId($id)
Set the identifier of this message.
Definition: Message.php:289
setExtensions($extensions)
Set the Extensions.
Definition: Message.php:603
setSignatureKey(XMLSecurityKey $signatureKey=null)
Set the private key we should use to sign the message.
Definition: Message.php:526
getRelayState()
Retrieve the RelayState associated with this message.
Definition: Message.php:411
$destination
if(!array_key_exists('StateId', $_REQUEST)) $id
getIssuer()
Retrieve the issuer if this message.
Definition: Message.php:375
$certificates
Definition: metarefresh.php:39
toSignedXML()
Convert this message to a signed XML document.
Definition: Message.php:481
getSignatureKey()
Retrieve the private key we should use to sign the message.
Definition: Message.php:514
getConsent()
Set the given consent for this message.
Definition: Message.php:365
getCertificates()
Retrieve the certificates that are included in the message.
Definition: Message.php:548
Base class for all SAML 2 messages.
Definition: Message.php:18
$xml
Definition: metadata.php:240
isMessageConstructedWithSignature()
Query whether or not the message contained a signature at the root level when the object was construc...
Definition: Message.php:401
getId()
Retrieve the identifier of this message.
Definition: Message.php:279
$relayState
getIssueInstant()
Retrieve the issue timestamp of this message.
Definition: Message.php:301
getSignatureMethod()
Definition: Message.php:613
catch(Exception $e) if(!($request instanceof \SAML2\ArtifactResolve)) $issuer
Create styles array
The data for the language used.
validate(XMLSecurityKey $key)
Validate this message against a public key.
Definition: Message.php:248
toUnsignedXML()
Convert this message to an unsigned XML document.
Definition: Message.php:435
$function
Definition: cas.php:28
setDestination($destination)
Set the destination of this message.
Definition: Message.php:333
getDestination()
Retrieve the destination of this message.
Definition: Message.php:323
static fromXML(\DOMElement $xml)
Convert an XML element into a message.
Definition: Message.php:562
setConsent($consent)
Set the given consent for this message.
Definition: Message.php:349
setIssueInstant($issueInstant)
Set the issue timestamp of this message.
Definition: Message.php:311
setIssuer($issuer)
Set the issuer of this message.
Definition: Message.php:389
$exceptions
Definition: Utf8Test.php:67
$key
Definition: croninfo.php:18
setRelayState($relayState)
Set the RelayState associated with this message.
Definition: Message.php:421
addValidator($function, $data)
Add a method for validating this message.
Definition: Message.php:225