ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
Assertion.php
Go to the documentation of this file.
1<?php
2
3namespace SAML2;
4
11
17class Assertion implements SignedElement
18{
24 private $id;
25
32
41 private $issuer;
42
50 private $nameId;
51
60
69
76
82 private $notBefore;
83
90
101
108
117
124
131
141
150
157
174 private $attributes;
175
192
201 private $nameFormat;
202
211
218
225
233
240
244 protected $wasSignedAtConstruction = false;
245
250
257 public function __construct(\DOMElement $xml = null)
258 {
259 $this->id = Utils::getContainer()->generateId();
260 $this->issueInstant = Temporal::getTime();
261 $this->issuer = '';
262 $this->authnInstant = Temporal::getTime();
263 $this->attributes = array();
264 $this->nameFormat = Constants::NAMEFORMAT_UNSPECIFIED;
265 $this->certificates = array();
266 $this->AuthenticatingAuthority = array();
267 $this->SubjectConfirmation = array();
268 $this->requiredEncAttributes = false;
269
270 if ($xml === null) {
271 return;
272 }
273
274 if (!$xml->hasAttribute('ID')) {
275 throw new \Exception('Missing ID attribute on SAML assertion.');
276 }
277 $this->id = $xml->getAttribute('ID');
278
279 if ($xml->getAttribute('Version') !== '2.0') {
280 /* Currently a very strict check. */
281 throw new \Exception('Unsupported version: ' . $xml->getAttribute('Version'));
282 }
283
284 $this->issueInstant = Utils::xsDateTimeToTimestamp($xml->getAttribute('IssueInstant'));
285
286 $issuer = Utils::xpQuery($xml, './saml_assertion:Issuer');
287 if (empty($issuer)) {
288 throw new \Exception('Missing <saml:Issuer> in assertion.');
289 }
290 $this->issuer = new XML\saml\Issuer($issuer[0]);
291 if ($this->issuer->Format === Constants::NAMEID_ENTITY) {
292 $this->issuer = $this->issuer->value;
293 }
294
295 $this->parseSubject($xml);
296 $this->parseConditions($xml);
297 $this->parseAuthnStatement($xml);
298 $this->parseAttributes($xml);
299 $this->parseEncryptedAttributes($xml);
300 $this->parseSignature($xml);
301 }
302
309 private function parseSubject(\DOMElement $xml)
310 {
311 $subject = Utils::xpQuery($xml, './saml_assertion:Subject');
312 if (empty($subject)) {
313 /* No Subject node. */
314
315 return;
316 } elseif (count($subject) > 1) {
317 throw new \Exception('More than one <saml:Subject> in <saml:Assertion>.');
318 }
319 $subject = $subject[0];
320
321 $nameId = Utils::xpQuery(
322 $subject,
323 './saml_assertion:NameID | ./saml_assertion:EncryptedID/xenc:EncryptedData'
324 );
325 if (count($nameId) > 1) {
326 throw new \Exception('More than one <saml:NameID> or <saml:EncryptedID> in <saml:Subject>.');
327 } elseif (!empty($nameId)) {
328 $nameId = $nameId[0];
329 if ($nameId->localName === 'EncryptedData') {
330 /* The NameID element is encrypted. */
331 $this->encryptedNameId = $nameId;
332 } else {
333 $this->nameId = new XML\saml\NameID($nameId);
334 }
335 }
336
337 $subjectConfirmation = Utils::xpQuery($subject, './saml_assertion:SubjectConfirmation');
338 if (empty($subjectConfirmation) && empty($nameId)) {
339 throw new \Exception('Missing <saml:SubjectConfirmation> in <saml:Subject>.');
340 }
341
342 foreach ($subjectConfirmation as $sc) {
344 }
345 }
346
353 private function parseConditions(\DOMElement $xml)
354 {
355 $conditions = Utils::xpQuery($xml, './saml_assertion:Conditions');
356 if (empty($conditions)) {
357 /* No <saml:Conditions> node. */
358
359 return;
360 } elseif (count($conditions) > 1) {
361 throw new \Exception('More than one <saml:Conditions> in <saml:Assertion>.');
362 }
363 $conditions = $conditions[0];
364
365 if ($conditions->hasAttribute('NotBefore')) {
366 $notBefore = Utils::xsDateTimeToTimestamp($conditions->getAttribute('NotBefore'));
367 if ($this->notBefore === null || $this->notBefore < $notBefore) {
368 $this->notBefore = $notBefore;
369 }
370 }
371 if ($conditions->hasAttribute('NotOnOrAfter')) {
372 $notOnOrAfter = Utils::xsDateTimeToTimestamp($conditions->getAttribute('NotOnOrAfter'));
373 if ($this->notOnOrAfter === null || $this->notOnOrAfter > $notOnOrAfter) {
374 $this->notOnOrAfter = $notOnOrAfter;
375 }
376 }
377
378 for ($node = $conditions->firstChild; $node !== null; $node = $node->nextSibling) {
379 if ($node instanceof \DOMText) {
380 continue;
381 }
382 if ($node->namespaceURI !== Constants::NS_SAML) {
383 throw new \Exception('Unknown namespace of condition: ' . var_export($node->namespaceURI, true));
384 }
385 switch ($node->localName) {
386 case 'AudienceRestriction':
387 $audiences = Utils::extractStrings($node, Constants::NS_SAML, 'Audience');
388 if ($this->validAudiences === null) {
389 /* The first (and probably last) AudienceRestriction element. */
390 $this->validAudiences = $audiences;
391 } else {
392 /*
393 * The set of AudienceRestriction are ANDed together, so we need
394 * the subset that are present in all of them.
395 */
396 $this->validAudiences = array_intersect($this->validAudiences, $audiences);
397 }
398 break;
399 case 'OneTimeUse':
400 /* Currently ignored. */
401 break;
402 case 'ProxyRestriction':
403 /* Currently ignored. */
404 break;
405 default:
406 throw new \Exception('Unknown condition: ' . var_export($node->localName, true));
407 }
408 }
409 }
410
417 private function parseAuthnStatement(\DOMElement $xml)
418 {
419 $authnStatements = Utils::xpQuery($xml, './saml_assertion:AuthnStatement');
420 if (empty($authnStatements)) {
421 $this->authnInstant = null;
422
423 return;
424 } elseif (count($authnStatements) > 1) {
425 throw new \Exception('More than one <saml:AuthnStatement> in <saml:Assertion> not supported.');
426 }
427 $authnStatement = $authnStatements[0];
428
429 if (!$authnStatement->hasAttribute('AuthnInstant')) {
430 throw new \Exception('Missing required AuthnInstant attribute on <saml:AuthnStatement>.');
431 }
432 $this->authnInstant = Utils::xsDateTimeToTimestamp($authnStatement->getAttribute('AuthnInstant'));
433
434 if ($authnStatement->hasAttribute('SessionNotOnOrAfter')) {
435 $this->sessionNotOnOrAfter = Utils::xsDateTimeToTimestamp($authnStatement->getAttribute('SessionNotOnOrAfter'));
436 }
437
438 if ($authnStatement->hasAttribute('SessionIndex')) {
439 $this->sessionIndex = $authnStatement->getAttribute('SessionIndex');
440 }
441
442 $this->parseAuthnContext($authnStatement);
443 }
444
451 private function parseAuthnContext(\DOMElement $authnStatementEl)
452 {
453 // Get the AuthnContext element
454 $authnContexts = Utils::xpQuery($authnStatementEl, './saml_assertion:AuthnContext');
455 if (count($authnContexts) > 1) {
456 throw new \Exception('More than one <saml:AuthnContext> in <saml:AuthnStatement>.');
457 } elseif (empty($authnContexts)) {
458 throw new \Exception('Missing required <saml:AuthnContext> in <saml:AuthnStatement>.');
459 }
460 $authnContextEl = $authnContexts[0];
461
462 // Get the AuthnContextDeclRef (if available)
463 $authnContextDeclRefs = Utils::xpQuery($authnContextEl, './saml_assertion:AuthnContextDeclRef');
464 if (count($authnContextDeclRefs) > 1) {
465 throw new \Exception(
466 'More than one <saml:AuthnContextDeclRef> found?'
467 );
468 } elseif (count($authnContextDeclRefs) === 1) {
469 $this->setAuthnContextDeclRef(trim($authnContextDeclRefs[0]->textContent));
470 }
471
472 // Get the AuthnContextDecl (if available)
473 $authnContextDecls = Utils::xpQuery($authnContextEl, './saml_assertion:AuthnContextDecl');
474 if (count($authnContextDecls) > 1) {
475 throw new \Exception(
476 'More than one <saml:AuthnContextDecl> found?'
477 );
478 } elseif (count($authnContextDecls) === 1) {
479 $this->setAuthnContextDecl(new Chunk($authnContextDecls[0]));
480 }
481
482 // Get the AuthnContextClassRef (if available)
483 $authnContextClassRefs = Utils::xpQuery($authnContextEl, './saml_assertion:AuthnContextClassRef');
484 if (count($authnContextClassRefs) > 1) {
485 throw new \Exception('More than one <saml:AuthnContextClassRef> in <saml:AuthnContext>.');
486 } elseif (count($authnContextClassRefs) === 1) {
487 $this->setAuthnContextClassRef(trim($authnContextClassRefs[0]->textContent));
488 }
489
490 // Constraint from XSD: MUST have one of the three
491 if (empty($this->authnContextClassRef) && empty($this->authnContextDecl) && empty($this->authnContextDeclRef)) {
492 throw new \Exception(
493 'Missing either <saml:AuthnContextClassRef> or <saml:AuthnContextDeclRef> or <saml:AuthnContextDecl>'
494 );
495 }
496
497 $this->AuthenticatingAuthority = Utils::extractStrings(
498 $authnContextEl,
499 Constants::NS_SAML,
500 'AuthenticatingAuthority'
501 );
502 }
503
510 private function parseAttributes(\DOMElement $xml)
511 {
512 $firstAttribute = true;
513 $attributes = Utils::xpQuery($xml, './saml_assertion:AttributeStatement/saml_assertion:Attribute');
514 foreach ($attributes as $attribute) {
515 if (!$attribute->hasAttribute('Name')) {
516 throw new \Exception('Missing name on <saml:Attribute> element.');
517 }
518 $name = $attribute->getAttribute('Name');
519
520 if ($attribute->hasAttribute('NameFormat')) {
521 $nameFormat = $attribute->getAttribute('NameFormat');
522 } else {
523 $nameFormat = Constants::NAMEFORMAT_UNSPECIFIED;
524 }
525
526 if ($firstAttribute) {
527 $this->nameFormat = $nameFormat;
528 $firstAttribute = false;
529 } else {
530 if ($this->nameFormat !== $nameFormat) {
531 $this->nameFormat = Constants::NAMEFORMAT_UNSPECIFIED;
532 }
533 }
534
535 if (!array_key_exists($name, $this->attributes)) {
536 $this->attributes[$name] = array();
537 $this->attributesValueTypes[$name] = array();
538 }
539
540 $this->parseAttributeValue($attribute, $name);
541 }
542 }
543
548 private function parseAttributeValue($attribute, $attributeName)
549 {
551 $values = Utils::xpQuery($attribute, './saml_assertion:AttributeValue');
552
553 if ($attributeName === Constants::EPTI_URN_MACE || $attributeName === Constants::EPTI_URN_OID) {
554 foreach ($values as $index => $eptiAttributeValue) {
555 $eptiNameId = Utils::xpQuery($eptiAttributeValue, './saml_assertion:NameID');
556
557 if (count($eptiNameId) !== 1) {
558 throw new RuntimeException(sprintf(
559 'A "%s" (EPTI) attribute value must be a NameID, none found for value no. "%d"',
560 $attributeName,
561 $index
562 ));
563 }
564
565 $this->attributes[$attributeName][] = new XML\saml\NameID($eptiNameId[0]);
566 }
567
568 return;
569 }
570
571 foreach ($values as $value) {
572 $hasNonTextChildElements = false;
573 foreach ($value->childNodes as $childNode) {
575 if ($childNode->nodeType !== XML_TEXT_NODE) {
576 $hasNonTextChildElements = true;
577 break;
578 }
579 }
580
581 $type = $value->getAttribute('xsi:type');
582 if ($type === '') {
583 $type = null;
584 }
585 $this->attributesValueTypes[$attributeName][] = $type;
586
587 if ($hasNonTextChildElements) {
588 $this->attributes[$attributeName][] = $value->childNodes;
589 continue;
590 }
591
592 if ($type === 'xs:integer') {
593 $this->attributes[$attributeName][] = (int)$value->textContent;
594 } else {
595 $this->attributes[$attributeName][] = trim($value->textContent);
596 }
597 }
598 }
599
605 private function parseEncryptedAttributes(\DOMElement $xml)
606 {
607 $this->encryptedAttributes = Utils::xpQuery(
608 $xml,
609 './saml_assertion:AttributeStatement/saml_assertion:EncryptedAttribute'
610 );
611 }
612
618 private function parseSignature(\DOMElement $xml)
619 {
621 $signatureMethod = Utils::xpQuery($xml, './ds:Signature/ds:SignedInfo/ds:SignatureMethod/@Algorithm');
622
623 /* Validate the signature element of the message. */
624 $sig = Utils::validateElement($xml);
625 if ($sig !== false) {
626 $this->wasSignedAtConstruction = true;
627 $this->certificates = $sig['Certificates'];
628 $this->signatureData = $sig;
629 $this->signatureMethod = $signatureMethod[0]->value;
630 }
631 }
632
643 public function validate(XMLSecurityKey $key)
644 {
645 assert($key->type === \RobRichards\XMLSecLibs\XMLSecurityKey::RSA_SHA256);
646
647 if ($this->signatureData === null) {
648 return false;
649 }
650
651 Utils::validateSignature($this->signatureData, $key);
652
653 return true;
654 }
655
661 public function getId()
662 {
663 return $this->id;
664 }
665
671 public function setId($id)
672 {
673 assert(is_string($id));
674
675 $this->id = $id;
676 }
677
683 public function getIssueInstant()
684 {
685 return $this->issueInstant;
686 }
687
693 public function setIssueInstant($issueInstant)
694 {
695 assert(is_int($issueInstant));
696
697 $this->issueInstant = $issueInstant;
698 }
699
705 public function getIssuer()
706 {
707 return $this->issuer;
708 }
709
715 public function setIssuer($issuer)
716 {
717 assert(is_string($issuer) || $issuer instanceof XML\saml\Issuer);
718
719 $this->issuer = $issuer;
720 }
721
728 public function getNameId()
729 {
730 if ($this->encryptedNameId !== null) {
731 throw new \Exception('Attempted to retrieve encrypted NameID without decrypting it first.');
732 }
733
734 return $this->nameId;
735 }
736
746 public function setNameId($nameId)
747 {
748 assert(is_array($nameId) || is_null($nameId) || $nameId instanceof XML\saml\NameID);
749
750 if (is_array($nameId)) {
751 $nameId = XML\saml\NameID::fromArray($nameId);
752 }
753 $this->nameId = $nameId;
754 }
755
761 public function isNameIdEncrypted()
762 {
763 return $this->encryptedNameId !== null;
764 }
765
772 {
773 /* First create a XML representation of the NameID. */
774 $doc = DOMDocumentFactory::create();
775 $root = $doc->createElement('root');
776 $doc->appendChild($root);
777 $this->nameId->toXML($root);
778 $nameId = $root->firstChild;
779
780 Utils::getContainer()->debugMessage($nameId, 'encrypt');
781
782 /* Encrypt the NameID. */
783 $enc = new XMLSecEnc();
784 $enc->setNode($nameId);
785 // @codingStandardsIgnoreStart
786 $enc->type = XMLSecEnc::Element;
787 // @codingStandardsIgnoreEnd
788
789 $symmetricKey = new XMLSecurityKey(XMLSecurityKey::AES128_CBC);
790 $symmetricKey->generateSessionKey();
791 $enc->encryptKey($key, $symmetricKey);
792
793 $this->encryptedNameId = $enc->encryptNode($symmetricKey);
794 $this->nameId = null;
795 }
796
803 public function decryptNameId(XMLSecurityKey $key, array $blacklist = array())
804 {
805 if ($this->encryptedNameId === null) {
806 /* No NameID to decrypt. */
807
808 return;
809 }
810
811 $nameId = Utils::decryptElement($this->encryptedNameId, $key, $blacklist);
812 Utils::getContainer()->debugMessage($nameId, 'decrypt');
813 $this->nameId = new XML\saml\NameID($nameId);
814
815 $this->encryptedNameId = null;
816 }
817
823 public function hasEncryptedAttributes()
824 {
825 return $this->encryptedAttributes !== [];
826 }
827
835 public function decryptAttributes(XMLSecurityKey $key, array $blacklist = array())
836 {
837 if (!$this->hasEncryptedAttributes()) {
838 return;
839 }
840 $firstAttribute = true;
841 $attributes = $this->encryptedAttributes;
842 foreach ($attributes as $attributeEnc) {
843 /*Decrypt node <EncryptedAttribute>*/
844 $attribute = Utils::decryptElement(
845 $attributeEnc->getElementsByTagName('EncryptedData')->item(0),
846 $key,
847 $blacklist
848 );
849
850 if (!$attribute->hasAttribute('Name')) {
851 throw new \Exception('Missing name on <saml:Attribute> element.');
852 }
853 $name = $attribute->getAttribute('Name');
854
855 if ($attribute->hasAttribute('NameFormat')) {
856 $nameFormat = $attribute->getAttribute('NameFormat');
857 } else {
858 $nameFormat = Constants::NAMEFORMAT_UNSPECIFIED;
859 }
860
861 if ($firstAttribute) {
862 $this->nameFormat = $nameFormat;
863 $firstAttribute = false;
864 } else {
865 if ($this->nameFormat !== $nameFormat) {
866 $this->nameFormat = Constants::NAMEFORMAT_UNSPECIFIED;
867 }
868 }
869
870 if (!array_key_exists($name, $this->attributes)) {
871 $this->attributes[$name] = array();
872 }
873
874 $this->parseAttributeValue($attribute, $name);
875 }
876 }
877
886 public function getNotBefore()
887 {
888 return $this->notBefore;
889 }
890
898 public function setNotBefore($notBefore)
899 {
900 assert(is_int($notBefore) || is_null($notBefore));
901
902 $this->notBefore = $notBefore;
903 }
904
913 public function getNotOnOrAfter()
914 {
915 return $this->notOnOrAfter;
916 }
917
925 public function setNotOnOrAfter($notOnOrAfter)
926 {
927 assert(is_int($notOnOrAfter) || is_null($notOnOrAfter));
928
929 $this->notOnOrAfter = $notOnOrAfter;
930 }
931
937 public function setEncryptedAttributes($ea)
938 {
939 $this->requiredEncAttributes = $ea;
940 }
941
949 public function getValidAudiences()
950 {
951 return $this->validAudiences;
952 }
953
961 public function setValidAudiences(array $validAudiences = null)
962 {
963 $this->validAudiences = $validAudiences;
964 }
965
971 public function getAuthnInstant()
972 {
973 return $this->authnInstant;
974 }
975
976
982 public function setAuthnInstant($authnInstant)
983 {
984 assert(is_int($authnInstant) || is_null($authnInstant));
985
986 $this->authnInstant = $authnInstant;
987 }
988
997 public function getSessionNotOnOrAfter()
998 {
999 return $this->sessionNotOnOrAfter;
1000 }
1001
1009 public function setSessionNotOnOrAfter($sessionNotOnOrAfter)
1010 {
1011 assert(is_int($sessionNotOnOrAfter) || is_null($sessionNotOnOrAfter));
1012
1013 $this->sessionNotOnOrAfter = $sessionNotOnOrAfter;
1014 }
1015
1021 public function getSessionIndex()
1022 {
1023 return $this->sessionIndex;
1024 }
1025
1035 {
1036 assert(is_string($sessionIndex) || is_null($sessionIndex));
1037
1038 $this->sessionIndex = $sessionIndex;
1039 }
1040
1055 public function getAuthnContext()
1056 {
1057 if (!empty($this->authnContextClassRef)) {
1058 return $this->authnContextClassRef;
1059 }
1060 if (!empty($this->authnContextDeclRef)) {
1061 return $this->authnContextDeclRef;
1062 }
1063 return null;
1064 }
1065
1075 public function setAuthnContext($authnContext)
1076 {
1077 $this->setAuthnContextClassRef($authnContext);
1078 }
1079
1088 public function getAuthnContextClassRef()
1089 {
1090 return $this->authnContextClassRef;
1091 }
1092
1101 public function setAuthnContextClassRef($authnContextClassRef)
1102 {
1103 assert(is_string($authnContextClassRef) || is_null($authnContextClassRef));
1104
1105 $this->authnContextClassRef = $authnContextClassRef;
1106 }
1107
1114 public function setAuthnContextDecl(Chunk $authnContextDecl)
1115 {
1116 if (!empty($this->authnContextDeclRef)) {
1117 throw new \Exception(
1118 'AuthnContextDeclRef is already registered! May only have either a Decl or a DeclRef, not both!'
1119 );
1120 }
1121
1122 $this->authnContextDecl = $authnContextDecl;
1123 }
1124
1133 public function getAuthnContextDecl()
1134 {
1135 return $this->authnContextDecl;
1136 }
1137
1144 public function setAuthnContextDeclRef($authnContextDeclRef)
1145 {
1146 if (!empty($this->authnContextDecl)) {
1147 throw new \Exception(
1148 'AuthnContextDecl is already registered! May only have either a Decl or a DeclRef, not both!'
1149 );
1150 }
1151
1152 $this->authnContextDeclRef = $authnContextDeclRef;
1153 }
1154
1163 public function getAuthnContextDeclRef()
1164 {
1165 return $this->authnContextDeclRef;
1166 }
1167
1175 {
1176 return $this->AuthenticatingAuthority;
1177 }
1178
1186 {
1187 $this->AuthenticatingAuthority = $authenticatingAuthority;
1188 }
1189
1195 public function getAttributes()
1196 {
1197 return $this->attributes;
1198 }
1199
1205 public function setAttributes(array $attributes)
1206 {
1207 $this->attributes = $attributes;
1208 }
1209
1215 public function getAttributesValueTypes()
1216 {
1217 return $this->attributesValueTypes;
1218 }
1219
1225 public function setAttributesValueTypes(array $attributesValueTypes)
1226 {
1227 $this->attributesValueTypes = $attributesValueTypes;
1228 }
1229
1238 public function getAttributeNameFormat()
1239 {
1240 return $this->nameFormat;
1241 }
1242
1248 public function setAttributeNameFormat($nameFormat)
1249 {
1250 assert(is_string($nameFormat));
1251
1252 $this->nameFormat = $nameFormat;
1253 }
1254
1260 public function getSubjectConfirmation()
1261 {
1262 return $this->SubjectConfirmation;
1263 }
1264
1270 public function setSubjectConfirmation(array $SubjectConfirmation)
1271 {
1272 $this->SubjectConfirmation = $SubjectConfirmation;
1273 }
1274
1280 public function getSignatureKey()
1281 {
1282 return $this->signatureKey;
1283 }
1284
1292 public function setSignatureKey(XMLSecurityKey $signatureKey = null)
1293 {
1294 $this->signatureKey = $signatureKey;
1295 }
1296
1303 public function getEncryptionKey()
1304 {
1305 return $this->encryptionKey;
1306 }
1307
1313 public function setEncryptionKey(XMLSecurityKey $Key = null)
1314 {
1315 $this->encryptionKey = $Key;
1316 }
1317
1325 public function setCertificates(array $certificates)
1326 {
1327 $this->certificates = $certificates;
1328 }
1329
1335 public function getCertificates()
1336 {
1337 return $this->certificates;
1338 }
1339
1344 {
1345 return $this->wasSignedAtConstruction;
1346 }
1347
1351 public function getSignatureMethod()
1352 {
1353 return $this->signatureMethod;
1354 }
1355
1362 public function toXML(\DOMNode $parentElement = null)
1363 {
1364 if ($parentElement === null) {
1365 $document = DOMDocumentFactory::create();
1366 $parentElement = $document;
1367 } else {
1368 $document = $parentElement->ownerDocument;
1369 }
1370
1371 $root = $document->createElementNS(Constants::NS_SAML, 'saml:' . 'Assertion');
1372 $parentElement->appendChild($root);
1373
1374 /* Ugly hack to add another namespace declaration to the root element. */
1375 $root->setAttributeNS(Constants::NS_SAMLP, 'samlp:tmp', 'tmp');
1376 $root->removeAttributeNS(Constants::NS_SAMLP, 'tmp');
1377 $root->setAttributeNS(Constants::NS_XSI, 'xsi:tmp', 'tmp');
1378 $root->removeAttributeNS(Constants::NS_XSI, 'tmp');
1379 $root->setAttributeNS(Constants::NS_XS, 'xs:tmp', 'tmp');
1380 $root->removeAttributeNS(Constants::NS_XS, 'tmp');
1381
1382 $root->setAttribute('ID', $this->id);
1383 $root->setAttribute('Version', '2.0');
1384 $root->setAttribute('IssueInstant', gmdate('Y-m-d\TH:i:s\Z', $this->issueInstant));
1385
1386 if (is_string($this->issuer)) {
1387 $issuer = Utils::addString($root, Constants::NS_SAML, 'saml:Issuer', $this->issuer);
1388 } elseif ($this->issuer instanceof XML\saml\Issuer) {
1389 $issuer = $this->issuer->toXML($root);
1390 }
1391
1392 $this->addSubject($root);
1393 $this->addConditions($root);
1394 $this->addAuthnStatement($root);
1395 if ($this->requiredEncAttributes === false) {
1396 $this->addAttributeStatement($root);
1397 } else {
1398 $this->addEncryptedAttributeStatement($root);
1399 }
1400
1401 if ($this->signatureKey !== null) {
1402 Utils::insertSignature($this->signatureKey, $this->certificates, $root, $issuer->nextSibling);
1403 }
1404
1405 return $root;
1406 }
1407
1413 private function addSubject(\DOMElement $root)
1414 {
1415 if ($this->nameId === null && $this->encryptedNameId === null) {
1416 /* We don't have anything to create a Subject node for. */
1417
1418 return;
1419 }
1420
1421 $subject = $root->ownerDocument->createElementNS(Constants::NS_SAML, 'saml:Subject');
1422 $root->appendChild($subject);
1423
1424 if ($this->encryptedNameId === null) {
1425 $this->nameId->toXML($subject);
1426 } else {
1427 $eid = $subject->ownerDocument->createElementNS(Constants::NS_SAML, 'saml:' . 'EncryptedID');
1428 $subject->appendChild($eid);
1429 $eid->appendChild($subject->ownerDocument->importNode($this->encryptedNameId, true));
1430 }
1431
1432 foreach ($this->SubjectConfirmation as $sc) {
1433 $sc->toXML($subject);
1434 }
1435 }
1436
1437
1443 private function addConditions(\DOMElement $root)
1444 {
1445 $document = $root->ownerDocument;
1446
1447 $conditions = $document->createElementNS(Constants::NS_SAML, 'saml:Conditions');
1448 $root->appendChild($conditions);
1449
1450 if ($this->notBefore !== null) {
1451 $conditions->setAttribute('NotBefore', gmdate('Y-m-d\TH:i:s\Z', $this->notBefore));
1452 }
1453 if ($this->notOnOrAfter !== null) {
1454 $conditions->setAttribute('NotOnOrAfter', gmdate('Y-m-d\TH:i:s\Z', $this->notOnOrAfter));
1455 }
1456
1457 if ($this->validAudiences !== null) {
1458 $ar = $document->createElementNS(Constants::NS_SAML, 'saml:AudienceRestriction');
1459 $conditions->appendChild($ar);
1460
1461 Utils::addStrings($ar, Constants::NS_SAML, 'saml:Audience', false, $this->validAudiences);
1462 }
1463 }
1464
1465
1471 private function addAuthnStatement(\DOMElement $root)
1472 {
1473 if ($this->authnInstant === null ||
1474 (
1475 $this->authnContextClassRef === null &&
1476 $this->authnContextDecl === null &&
1477 $this->authnContextDeclRef === null
1478 )
1479 ) {
1480 /* No authentication context or AuthnInstant => no authentication statement. */
1481
1482 return;
1483 }
1484
1485 $document = $root->ownerDocument;
1486
1487 $authnStatementEl = $document->createElementNS(Constants::NS_SAML, 'saml:AuthnStatement');
1488 $root->appendChild($authnStatementEl);
1489
1490 $authnStatementEl->setAttribute('AuthnInstant', gmdate('Y-m-d\TH:i:s\Z', $this->authnInstant));
1491
1492 if ($this->sessionNotOnOrAfter !== null) {
1493 $authnStatementEl->setAttribute('SessionNotOnOrAfter', gmdate('Y-m-d\TH:i:s\Z', $this->sessionNotOnOrAfter));
1494 }
1495 if ($this->sessionIndex !== null) {
1496 $authnStatementEl->setAttribute('SessionIndex', $this->sessionIndex);
1497 }
1498
1499 $authnContextEl = $document->createElementNS(Constants::NS_SAML, 'saml:AuthnContext');
1500 $authnStatementEl->appendChild($authnContextEl);
1501
1502 if (!empty($this->authnContextClassRef)) {
1503 Utils::addString(
1504 $authnContextEl,
1505 Constants::NS_SAML,
1506 'saml:AuthnContextClassRef',
1507 $this->authnContextClassRef
1508 );
1509 }
1510 if (!empty($this->authnContextDecl)) {
1511 $this->authnContextDecl->toXML($authnContextEl);
1512 }
1513 if (!empty($this->authnContextDeclRef)) {
1514 Utils::addString(
1515 $authnContextEl,
1516 Constants::NS_SAML,
1517 'saml:AuthnContextDeclRef',
1518 $this->authnContextDeclRef
1519 );
1520 }
1521
1522 Utils::addStrings(
1523 $authnContextEl,
1524 Constants::NS_SAML,
1525 'saml:AuthenticatingAuthority',
1526 false,
1527 $this->AuthenticatingAuthority
1528 );
1529 }
1530
1531
1537 private function addAttributeStatement(\DOMElement $root)
1538 {
1539 if (empty($this->attributes)) {
1540 return;
1541 }
1542
1543 $document = $root->ownerDocument;
1544
1545 $attributeStatement = $document->createElementNS(Constants::NS_SAML, 'saml:AttributeStatement');
1546 $root->appendChild($attributeStatement);
1547
1548 foreach ($this->attributes as $name => $values) {
1549 $attribute = $document->createElementNS(Constants::NS_SAML, 'saml:Attribute');
1550 $attributeStatement->appendChild($attribute);
1551 $attribute->setAttribute('Name', $name);
1552
1553 if ($this->nameFormat !== Constants::NAMEFORMAT_UNSPECIFIED) {
1554 $attribute->setAttribute('NameFormat', $this->nameFormat);
1555 }
1556
1557 // make sure eduPersonTargetedID can be handled properly as a NameID
1558 if ($name === Constants::EPTI_URN_MACE || $name === Constants::EPTI_URN_OID) {
1559 foreach ($values as $eptiValue) {
1560 $attributeValue = $document->createElementNS(Constants::NS_SAML, 'saml:AttributeValue');
1561 $attribute->appendChild($attributeValue);
1562 if ($eptiValue instanceof XML\saml\NameID) {
1563 $eptiValue->toXML($attributeValue);
1564 } elseif ($eptiValue instanceof \DOMNodeList) {
1565 $node = $root->ownerDocument->importNode($eptiValue->item(0), true);
1566 $attributeValue->appendChild($node);
1567 } else {
1568 $attributeValue->textContent = $eptiValue;
1569 }
1570 }
1571
1572 continue;
1573 }
1574
1575 // get value type(s) for the current attribute
1576 if (is_array($this->attributesValueTypes) && array_key_exists($name, $this->attributesValueTypes)) {
1577 $valueTypes = $this->attributesValueTypes[$name];
1578 if (is_array($valueTypes) && count($valueTypes) != count($values)) {
1579 throw new \Exception('Array of value types and array of values have different size for attribute '. var_export($name, true));
1580 }
1581 } else {
1582 // if no type(s), default behaviour
1583 $valueTypes = null;
1584 }
1585
1586 $vidx = -1;
1587 foreach ($values as $value) {
1588 $vidx++;
1589
1590 // try to get type from current types
1591 $type = null;
1592 if (!is_null($valueTypes)) {
1593 if (is_array($valueTypes)) {
1594 $type = $valueTypes[$vidx];
1595 } else {
1596 $type = $valueTypes;
1597 }
1598 }
1599
1600 // if no type get from types, use default behaviour
1601 if (is_null($type)) {
1602 if (is_string($value)) {
1603 $type = 'xs:string';
1604 } elseif (is_int($value)) {
1605 $type = 'xs:integer';
1606 } else {
1607 $type = null;
1608 }
1609 }
1610
1611 $attributeValue = $document->createElementNS(Constants::NS_SAML, 'saml:AttributeValue');
1612 $attribute->appendChild($attributeValue);
1613 if ($type !== null) {
1614 $attributeValue->setAttributeNS(Constants::NS_XSI, 'xsi:type', $type);
1615 }
1616 if (is_null($value)) {
1617 $attributeValue->setAttributeNS(Constants::NS_XSI, 'xsi:nil', 'true');
1618 }
1619
1620 if ($value instanceof \DOMNodeList) {
1621 for ($i = 0; $i < $value->length; $i++) {
1622 $node = $document->importNode($value->item($i), true);
1623 $attributeValue->appendChild($node);
1624 }
1625 } else {
1626 $attributeValue->appendChild($document->createTextNode($value));
1627 }
1628 }
1629 }
1630 }
1631
1632
1638 private function addEncryptedAttributeStatement(\DOMElement $root)
1639 {
1640 if ($this->requiredEncAttributes === false) {
1641 return;
1642 }
1643
1644 $document = $root->ownerDocument;
1645
1646 $attributeStatement = $document->createElementNS(Constants::NS_SAML, 'saml:AttributeStatement');
1647 $root->appendChild($attributeStatement);
1648
1649 foreach ($this->attributes as $name => $values) {
1650 $document2 = DOMDocumentFactory::create();
1651 $attribute = $document2->createElementNS(Constants::NS_SAML, 'saml:Attribute');
1652 $attribute->setAttribute('Name', $name);
1653 $document2->appendChild($attribute);
1654
1655 if ($this->nameFormat !== Constants::NAMEFORMAT_UNSPECIFIED) {
1656 $attribute->setAttribute('NameFormat', $this->nameFormat);
1657 }
1658
1659 foreach ($values as $value) {
1660 if (is_string($value)) {
1661 $type = 'xs:string';
1662 } elseif (is_int($value)) {
1663 $type = 'xs:integer';
1664 } else {
1665 $type = null;
1666 }
1667
1668 $attributeValue = $document2->createElementNS(Constants::NS_SAML, 'saml:AttributeValue');
1669 $attribute->appendChild($attributeValue);
1670 if ($type !== null) {
1671 $attributeValue->setAttributeNS(Constants::NS_XSI, 'xsi:type', $type);
1672 }
1673
1674 if ($value instanceof \DOMNodeList) {
1675 for ($i = 0; $i < $value->length; $i++) {
1676 $node = $document2->importNode($value->item($i), true);
1677 $attributeValue->appendChild($node);
1678 }
1679 } else {
1680 $attributeValue->appendChild($document2->createTextNode($value));
1681 }
1682 }
1683 /*Once the attribute nodes are built, the are encrypted*/
1684 $EncAssert = new XMLSecEnc();
1685 $EncAssert->setNode($document2->documentElement);
1686 $EncAssert->type = 'http://www.w3.org/2001/04/xmlenc#Element';
1687 /*
1688 * Attributes are encrypted with a session key and this one with
1689 * $EncryptionKey
1690 */
1691 $symmetricKey = new XMLSecurityKey(XMLSecurityKey::AES256_CBC);
1692 $symmetricKey->generateSessionKey();
1693 $EncAssert->encryptKey($this->encryptionKey, $symmetricKey);
1694 $EncrNode = $EncAssert->encryptNode($symmetricKey);
1695
1696 $EncAttribute = $document->createElementNS(Constants::NS_SAML, 'saml:EncryptedAttribute');
1697 $attributeStatement->appendChild($EncAttribute);
1698 $n = $document->importNode($EncrNode, true);
1699 $EncAttribute->appendChild($n);
1700 }
1701 }
1702}
catch(Exception $e) if(!($request instanceof \SAML2\ArtifactResolve)) $issuer
getTime()
Definition: MetaLoader.php:492
$n
Definition: RandomTest.php:85
An exception for terminatinating execution or to throw for unit testing.
getNotBefore()
Retrieve the earliest timestamp this assertion is valid.
Definition: Assertion.php:886
setNotOnOrAfter($notOnOrAfter)
Set the expiration timestamp of this assertion.
Definition: Assertion.php:925
setAttributesValueTypes(array $attributesValueTypes)
Replace all attributes value types.
Definition: Assertion.php:1225
getAuthenticatingAuthority()
Retrieve the AuthenticatingAuthority.
Definition: Assertion.php:1174
setAuthnInstant($authnInstant)
Set the AuthnInstant of the assertion.
Definition: Assertion.php:982
setIssueInstant($issueInstant)
Set the issue timestamp of this assertion.
Definition: Assertion.php:693
setId($id)
Set the identifier of this assertion.
Definition: Assertion.php:671
addAuthnStatement(\DOMElement $root)
Add a AuthnStatement-node to the assertion.
Definition: Assertion.php:1471
parseEncryptedAttributes(\DOMElement $xml)
Parse encrypted attribute statements in assertion.
Definition: Assertion.php:605
addConditions(\DOMElement $root)
Add a Conditions-node to the assertion.
Definition: Assertion.php:1443
getNameId()
Retrieve the NameId of the subject in the assertion.
Definition: Assertion.php:728
getAuthnContextDecl()
Get the authentication context declaration.
Definition: Assertion.php:1133
addAttributeStatement(\DOMElement $root)
Add an AttributeStatement-node to the assertion.
Definition: Assertion.php:1537
setAuthnContext($authnContext)
Set the authentication method used to authenticate the user.
Definition: Assertion.php:1075
isNameIdEncrypted()
Check whether the NameId is encrypted.
Definition: Assertion.php:761
getValidAudiences()
Retrieve the audiences that are allowed to receive this assertion.
Definition: Assertion.php:949
setNotBefore($notBefore)
Set the earliest timestamp this assertion can be used.
Definition: Assertion.php:898
setNameId($nameId)
Set the NameId of the subject in the assertion.
Definition: Assertion.php:746
validate(XMLSecurityKey $key)
Validate this assertion against a public key.
Definition: Assertion.php:643
getIssuer()
Retrieve the issuer if this assertion.
Definition: Assertion.php:705
getSessionNotOnOrAfter()
Retrieve the session expiration timestamp.
Definition: Assertion.php:997
getAuthnContext()
Retrieve the authentication method used to authenticate the user.
Definition: Assertion.php:1055
parseConditions(\DOMElement $xml)
Parse conditions in assertion.
Definition: Assertion.php:353
getAttributeNameFormat()
Retrieve the NameFormat used on all attributes.
Definition: Assertion.php:1238
setEncryptionKey(XMLSecurityKey $Key=null)
Set the private key we should use to encrypt the attributes.
Definition: Assertion.php:1313
setSignatureKey(XMLSecurityKey $signatureKey=null)
Set the private key we should use to sign the assertion.
Definition: Assertion.php:1292
getSignatureKey()
Retrieve the private key we should use to sign the assertion.
Definition: Assertion.php:1280
setAuthnContextDeclRef($authnContextDeclRef)
Set the authentication context declaration reference.
Definition: Assertion.php:1144
getAuthnContextClassRef()
Retrieve the authentication method used to authenticate the user.
Definition: Assertion.php:1088
setSubjectConfirmation(array $SubjectConfirmation)
Set the SubjectConfirmation elements that should be included in the assertion.
Definition: Assertion.php:1270
decryptAttributes(XMLSecurityKey $key, array $blacklist=array())
Decrypt the assertion attributes.
Definition: Assertion.php:835
setAuthnContextClassRef($authnContextClassRef)
Set the authentication method used to authenticate the user.
Definition: Assertion.php:1101
decryptNameId(XMLSecurityKey $key, array $blacklist=array())
Decrypt the NameId of the subject in the assertion.
Definition: Assertion.php:803
parseAttributes(\DOMElement $xml)
Parse attribute statements in assertion.
Definition: Assertion.php:510
parseAuthnStatement(\DOMElement $xml)
Parse AuthnStatement in assertion.
Definition: Assertion.php:417
getSubjectConfirmation()
Retrieve the SubjectConfirmation elements we have in our Subject element.
Definition: Assertion.php:1260
setSessionNotOnOrAfter($sessionNotOnOrAfter)
Set the session expiration timestamp.
Definition: Assertion.php:1009
addSubject(\DOMElement $root)
Add a Subject-node to the assertion.
Definition: Assertion.php:1413
parseAuthnContext(\DOMElement $authnStatementEl)
Parse AuthnContext in AuthnStatement.
Definition: Assertion.php:451
setAuthnContextDecl(Chunk $authnContextDecl)
Set the authentication context declaration.
Definition: Assertion.php:1114
setAttributes(array $attributes)
Replace all attributes.
Definition: Assertion.php:1205
getIssueInstant()
Retrieve the issue timestamp of this assertion.
Definition: Assertion.php:683
getNotOnOrAfter()
Retrieve the expiration timestamp of this assertion.
Definition: Assertion.php:913
setAuthenticatingAuthority($authenticatingAuthority)
Set the AuthenticatingAuthority.
Definition: Assertion.php:1185
setCertificates(array $certificates)
Set the certificates that should be included in the assertion.
Definition: Assertion.php:1325
setAttributeNameFormat($nameFormat)
Set the NameFormat used on all attributes.
Definition: Assertion.php:1248
toXML(\DOMNode $parentElement=null)
Convert this assertion to an XML element.
Definition: Assertion.php:1362
setIssuer($issuer)
Set the issuer of this message.
Definition: Assertion.php:715
getSessionIndex()
Retrieve the session index of the user at the IdP.
Definition: Assertion.php:1021
getCertificates()
Retrieve the certificates that are included in the assertion.
Definition: Assertion.php:1335
getAuthnContextDeclRef()
Get the authentication context declaration reference.
Definition: Assertion.php:1163
getAuthnInstant()
Retrieve the AuthnInstant of the assertion.
Definition: Assertion.php:971
addEncryptedAttributeStatement(\DOMElement $root)
Add an EncryptedAttribute Statement-node to the assertion.
Definition: Assertion.php:1638
parseSubject(\DOMElement $xml)
Parse subject in assertion.
Definition: Assertion.php:309
setValidAudiences(array $validAudiences=null)
Set the audiences that are allowed to receive this assertion.
Definition: Assertion.php:961
getAttributes()
Retrieve all attributes.
Definition: Assertion.php:1195
getId()
Retrieve the identifier of this assertion.
Definition: Assertion.php:661
__construct(\DOMElement $xml=null)
Constructor for SAML 2 assertions.
Definition: Assertion.php:257
getAttributesValueTypes()
Retrieve all attributes value types.
Definition: Assertion.php:1215
setEncryptedAttributes($ea)
Set $EncryptedAttributes if attributes will send encrypted.
Definition: Assertion.php:937
encryptNameId(XMLSecurityKey $key)
Encrypt the NameID in the Assertion.
Definition: Assertion.php:771
getEncryptionKey()
Return the key we should use to encrypt the assertion.
Definition: Assertion.php:1303
getWasSignedAtConstruction()
Definition: Assertion.php:1343
setSessionIndex($sessionIndex)
Set the session index of the user at the IdP.
Definition: Assertion.php:1034
hasEncryptedAttributes()
Did this Assertion contain encrypted Attributes?
Definition: Assertion.php:823
$key
Definition: croninfo.php:18
$i
Definition: disco.tpl.php:19
if(!array_key_exists('StateId', $_REQUEST)) $id
if(array_key_exists('yes', $_REQUEST)) $attributes
Definition: getconsent.php:85
$index
Definition: metadata.php:60
$nameId
Definition: saml2-acs.php:138
$sessionIndex
Definition: saml2-acs.php:139
catch(sspmod_saml_Error $e) $authenticatingAuthority
Definition: saml2-acs.php:137
$certificates
Definition: metarefresh.php:39
$type
$root
Definition: sabredav.php:45
$values