|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
namespace SAML2\XML\md; |
|
4
|
|
|
|
|
5
|
|
|
use SAML2\Constants; |
|
6
|
|
|
use SAML2\DOMDocumentFactory; |
|
7
|
|
|
use SAML2\SignedElementHelper; |
|
8
|
|
|
use SAML2\Utils; |
|
9
|
|
|
|
|
10
|
|
|
/** |
|
11
|
|
|
* Class representing SAML 2 EntityDescriptor element. |
|
12
|
|
|
* |
|
13
|
|
|
* @package SimpleSAMLphp |
|
14
|
|
|
*/ |
|
15
|
|
|
class EntityDescriptor extends SignedElementHelper |
|
16
|
|
|
{ |
|
17
|
|
|
/** |
|
18
|
|
|
* The entityID this EntityDescriptor represents. |
|
19
|
|
|
* |
|
20
|
|
|
* @var string |
|
21
|
|
|
*/ |
|
22
|
|
|
public $entityID; |
|
23
|
|
|
|
|
24
|
|
|
/** |
|
25
|
|
|
* The ID of this element. |
|
26
|
|
|
* |
|
27
|
|
|
* @var string|null |
|
28
|
|
|
*/ |
|
29
|
|
|
public $ID; |
|
30
|
|
|
|
|
31
|
|
|
/** |
|
32
|
|
|
* How long this element is valid, as a unix timestamp. |
|
33
|
|
|
* |
|
34
|
|
|
* @var int|null |
|
35
|
|
|
*/ |
|
36
|
|
|
public $validUntil; |
|
37
|
|
|
|
|
38
|
|
|
/** |
|
39
|
|
|
* The length of time this element can be cached, as string. |
|
40
|
|
|
* |
|
41
|
|
|
* @var string|null |
|
42
|
|
|
*/ |
|
43
|
|
|
public $cacheDuration; |
|
44
|
|
|
|
|
45
|
|
|
/** |
|
46
|
|
|
* Extensions on this element. |
|
47
|
|
|
* |
|
48
|
|
|
* Array of extension elements. |
|
49
|
|
|
* |
|
50
|
|
|
* @var array |
|
51
|
|
|
*/ |
|
52
|
|
|
public $Extensions = array(); |
|
53
|
|
|
|
|
54
|
|
|
/** |
|
55
|
|
|
* Array with all roles for this entity. |
|
56
|
|
|
* |
|
57
|
|
|
* Array of \SAML2\XML\md\RoleDescriptor objects (and subclasses of RoleDescriptor). |
|
58
|
|
|
* |
|
59
|
|
|
* @var (\SAML2\XML\md\UnknownRoleDescriptor|\SAML2\XML\md\IDPSSODescriptor|\SAML2\XML\md\SPSSODescriptor|\SAML2\XML\md\AuthnAuthorityDescriptor|\SAML2\XML\md\AttributeAuthorityDescriptor|\SAML2\XML\md\PDPDescriptor)[] |
|
60
|
|
|
*/ |
|
61
|
|
|
public $RoleDescriptor = array(); |
|
62
|
|
|
|
|
63
|
|
|
/** |
|
64
|
|
|
* AffiliationDescriptor of this entity. |
|
65
|
|
|
* |
|
66
|
|
|
* @var \SAML2\XML\md\AffiliationDescriptor|null |
|
67
|
|
|
*/ |
|
68
|
|
|
public $AffiliationDescriptor = null; |
|
69
|
|
|
|
|
70
|
|
|
/** |
|
71
|
|
|
* Organization of this entity. |
|
72
|
|
|
* |
|
73
|
|
|
* @var \SAML2\XML\md\Organization|null |
|
74
|
|
|
*/ |
|
75
|
|
|
public $Organization = null; |
|
76
|
|
|
|
|
77
|
|
|
/** |
|
78
|
|
|
* ContactPerson elements for this entity. |
|
79
|
|
|
* |
|
80
|
|
|
* @var \SAML2\XML\md\ContactPerson[] |
|
81
|
|
|
*/ |
|
82
|
|
|
public $ContactPerson = array(); |
|
83
|
|
|
|
|
84
|
|
|
/** |
|
85
|
|
|
* AdditionalMetadataLocation elements for this entity. |
|
86
|
|
|
* |
|
87
|
|
|
* @var \SAML2\XML\md\AdditionalMetadataLocation[] |
|
88
|
|
|
*/ |
|
89
|
|
|
public $AdditionalMetadataLocation = array(); |
|
90
|
|
|
|
|
91
|
|
|
/** |
|
92
|
|
|
* Initialize an EntitiyDescriptor. |
|
93
|
|
|
* |
|
94
|
|
|
* @param \DOMElement|null $xml The XML element we should load. |
|
95
|
|
|
* @throws \Exception |
|
96
|
|
|
*/ |
|
97
|
|
|
public function __construct(\DOMElement $xml = null) |
|
98
|
|
|
{ |
|
99
|
|
|
parent::__construct($xml); |
|
100
|
|
|
|
|
101
|
|
|
if ($xml === null) { |
|
102
|
|
|
return; |
|
103
|
|
|
} |
|
104
|
|
|
|
|
105
|
|
|
if (!$xml->hasAttribute('entityID')) { |
|
106
|
|
|
throw new \Exception('Missing required attribute entityID on EntityDescriptor.'); |
|
107
|
|
|
} |
|
108
|
|
|
$this->entityID = $xml->getAttribute('entityID'); |
|
109
|
|
|
|
|
110
|
|
|
if ($xml->hasAttribute('ID')) { |
|
111
|
|
|
$this->ID = $xml->getAttribute('ID'); |
|
112
|
|
|
} |
|
113
|
|
|
if ($xml->hasAttribute('validUntil')) { |
|
114
|
|
|
$this->validUntil = Utils::xsDateTimeToTimestamp($xml->getAttribute('validUntil')); |
|
115
|
|
|
} |
|
116
|
|
|
if ($xml->hasAttribute('cacheDuration')) { |
|
117
|
|
|
$this->cacheDuration = $xml->getAttribute('cacheDuration'); |
|
118
|
|
|
} |
|
119
|
|
|
|
|
120
|
|
|
$this->Extensions = Extensions::getList($xml); |
|
121
|
|
|
|
|
122
|
|
|
for ($node = $xml->firstChild; $node !== null; $node = $node->nextSibling) { |
|
123
|
|
|
if (!($node instanceof \DOMElement)) { |
|
124
|
|
|
continue; |
|
125
|
|
|
} |
|
126
|
|
|
|
|
127
|
|
|
if ($node->namespaceURI !== Constants::NS_MD) { |
|
128
|
|
|
continue; |
|
129
|
|
|
} |
|
130
|
|
|
|
|
131
|
|
|
switch ($node->localName) { |
|
132
|
|
|
case 'RoleDescriptor': |
|
133
|
|
|
$this->RoleDescriptor[] = new UnknownRoleDescriptor($node); |
|
134
|
|
|
break; |
|
135
|
|
|
case 'IDPSSODescriptor': |
|
136
|
|
|
$this->RoleDescriptor[] = new IDPSSODescriptor($node); |
|
137
|
|
|
break; |
|
138
|
|
|
case 'SPSSODescriptor': |
|
139
|
|
|
$this->RoleDescriptor[] = new SPSSODescriptor($node); |
|
140
|
|
|
break; |
|
141
|
|
|
case 'AuthnAuthorityDescriptor': |
|
142
|
|
|
$this->RoleDescriptor[] = new AuthnAuthorityDescriptor($node); |
|
143
|
|
|
break; |
|
144
|
|
|
case 'AttributeAuthorityDescriptor': |
|
145
|
|
|
$this->RoleDescriptor[] = new AttributeAuthorityDescriptor($node); |
|
146
|
|
|
break; |
|
147
|
|
|
case 'PDPDescriptor': |
|
148
|
|
|
$this->RoleDescriptor[] = new PDPDescriptor($node); |
|
149
|
|
|
break; |
|
150
|
|
|
} |
|
151
|
|
|
} |
|
152
|
|
|
|
|
153
|
|
|
$affiliationDescriptor = Utils::xpQuery($xml, './saml_metadata:AffiliationDescriptor'); |
|
154
|
|
|
if (count($affiliationDescriptor) > 1) { |
|
155
|
|
|
throw new \Exception('More than one AffiliationDescriptor in the entity.'); |
|
156
|
|
|
} elseif (!empty($affiliationDescriptor)) { |
|
157
|
|
|
$this->AffiliationDescriptor = new AffiliationDescriptor($affiliationDescriptor[0]); |
|
158
|
|
|
} |
|
159
|
|
|
|
|
160
|
|
|
if (empty($this->RoleDescriptor) && is_null($this->AffiliationDescriptor)) { |
|
161
|
|
|
throw new \Exception('Must have either one of the RoleDescriptors or an AffiliationDescriptor in EntityDescriptor.'); |
|
162
|
|
|
} elseif (!empty($this->RoleDescriptor) && !is_null($this->AffiliationDescriptor)) { |
|
163
|
|
|
throw new \Exception('AffiliationDescriptor cannot be combined with other RoleDescriptor elements in EntityDescriptor.'); |
|
164
|
|
|
} |
|
165
|
|
|
|
|
166
|
|
|
$organization = Utils::xpQuery($xml, './saml_metadata:Organization'); |
|
167
|
|
View Code Duplication |
if (count($organization) > 1) { |
|
|
|
|
|
|
168
|
|
|
throw new \Exception('More than one Organization in the entity.'); |
|
169
|
|
|
} elseif (!empty($organization)) { |
|
170
|
|
|
$this->Organization = new Organization($organization[0]); |
|
171
|
|
|
} |
|
172
|
|
|
|
|
173
|
|
|
foreach (Utils::xpQuery($xml, './saml_metadata:ContactPerson') as $cp) { |
|
174
|
|
|
$this->ContactPerson[] = new ContactPerson($cp); |
|
175
|
|
|
} |
|
176
|
|
|
|
|
177
|
|
|
foreach (Utils::xpQuery($xml, './saml_metadata:AdditionalMetadataLocation') as $aml) { |
|
178
|
|
|
$this->AdditionalMetadataLocation[] = new AdditionalMetadataLocation($aml); |
|
179
|
|
|
} |
|
180
|
|
|
} |
|
181
|
|
|
|
|
182
|
|
|
/** |
|
183
|
|
|
* Create this EntityDescriptor. |
|
184
|
|
|
* |
|
185
|
|
|
* @param \DOMElement|null $parent The EntitiesDescriptor we should append this EntityDescriptor to. |
|
186
|
|
|
* @return \DOMElement |
|
187
|
|
|
*/ |
|
188
|
|
|
public function toXML(\DOMElement $parent = null) |
|
189
|
|
|
{ |
|
190
|
|
|
assert('is_string($this->entityID)'); |
|
191
|
|
|
assert('is_null($this->ID) || is_string($this->ID)'); |
|
192
|
|
|
assert('is_null($this->validUntil) || is_int($this->validUntil)'); |
|
193
|
|
|
assert('is_null($this->cacheDuration) || is_string($this->cacheDuration)'); |
|
194
|
|
|
assert('is_array($this->Extensions)'); |
|
195
|
|
|
assert('is_array($this->RoleDescriptor)'); |
|
196
|
|
|
assert('is_null($this->AffiliationDescriptor) || $this->AffiliationDescriptor instanceof \SAML2\XML\md\AffiliationDescriptor'); |
|
197
|
|
|
assert('is_null($this->Organization) || $this->Organization instanceof \SAML2\XML\md\Organization'); |
|
198
|
|
|
assert('is_array($this->ContactPerson)'); |
|
199
|
|
|
assert('is_array($this->AdditionalMetadataLocation)'); |
|
200
|
|
|
|
|
201
|
|
View Code Duplication |
if ($parent === null) { |
|
|
|
|
|
|
202
|
|
|
$doc = DOMDocumentFactory::create(); |
|
203
|
|
|
$e = $doc->createElementNS(Constants::NS_MD, 'md:EntityDescriptor'); |
|
204
|
|
|
$doc->appendChild($e); |
|
205
|
|
|
} else { |
|
206
|
|
|
$e = $parent->ownerDocument->createElementNS(Constants::NS_MD, 'md:EntityDescriptor'); |
|
207
|
|
|
$parent->appendChild($e); |
|
208
|
|
|
} |
|
209
|
|
|
|
|
210
|
|
|
$e->setAttribute('entityID', $this->entityID); |
|
211
|
|
|
|
|
212
|
|
|
if (isset($this->ID)) { |
|
213
|
|
|
$e->setAttribute('ID', $this->ID); |
|
214
|
|
|
} |
|
215
|
|
|
|
|
216
|
|
|
if (isset($this->validUntil)) { |
|
217
|
|
|
$e->setAttribute('validUntil', gmdate('Y-m-d\TH:i:s\Z', $this->validUntil)); |
|
218
|
|
|
} |
|
219
|
|
|
|
|
220
|
|
|
if (isset($this->cacheDuration)) { |
|
221
|
|
|
$e->setAttribute('cacheDuration', $this->cacheDuration); |
|
222
|
|
|
} |
|
223
|
|
|
|
|
224
|
|
|
Extensions::addList($e, $this->Extensions); |
|
225
|
|
|
|
|
226
|
|
|
/** @var \SAML2\XML\md\UnknownRoleDescriptor|\SAML2\XML\md\IDPSSODescriptor|\SAML2\XML\md\SPSSODescriptor|\SAML2\XML\md\AuthnAuthorityDescriptor|\SAML2\XML\md\AttributeAuthorityDescriptor|\SAML2\XML\md\PDPDescriptor $n */ |
|
227
|
|
|
foreach ($this->RoleDescriptor as $n) { |
|
228
|
|
|
$n->toXML($e); |
|
229
|
|
|
} |
|
230
|
|
|
|
|
231
|
|
|
if (isset($this->AffiliationDescriptor)) { |
|
232
|
|
|
$this->AffiliationDescriptor->toXML($e); |
|
233
|
|
|
} |
|
234
|
|
|
|
|
235
|
|
|
if (isset($this->Organization)) { |
|
236
|
|
|
$this->Organization->toXML($e); |
|
237
|
|
|
} |
|
238
|
|
|
|
|
239
|
|
|
foreach ($this->ContactPerson as $cp) { |
|
240
|
|
|
$cp->toXML($e); |
|
241
|
|
|
} |
|
242
|
|
|
|
|
243
|
|
|
foreach ($this->AdditionalMetadataLocation as $n) { |
|
244
|
|
|
$n->toXML($e); |
|
245
|
|
|
} |
|
246
|
|
|
|
|
247
|
|
|
$this->signElement($e, $e->firstChild); |
|
|
|
|
|
|
248
|
|
|
|
|
249
|
|
|
return $e; |
|
250
|
|
|
} |
|
251
|
|
|
} |
|
252
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.