Passed
Pull Request — master (#374)
by Tim
02:41
created

UIInfo::getDisplayName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace SimpleSAML\SAML2\XML\mdui;
6
7
use DOMElement;
8
use SimpleSAML\SAML2\Assert\Assert;
9
use SimpleSAML\SAML2\Exception\{ArrayValidationException, ProtocolViolationException};
10
use SimpleSAML\XML\ArrayizableElementInterface;
11
use SimpleSAML\XML\Chunk;
12
use SimpleSAML\XML\Constants as C;
13
use SimpleSAML\XML\Exception\InvalidDOMElementException;
14
use SimpleSAML\XML\ExtendableElementTrait;
15
use SimpleSAML\XML\{SchemaValidatableElementInterface, SchemaValidatableElementTrait, SerializableElementInterface};
16
use SimpleSAML\XML\XsNamespace as NS;
17
18
use function array_filter;
19
use function array_key_exists;
20
use function array_keys;
21
use function array_map;
22
use function array_merge;
23
use function array_unique;
24
25
/**
26
 * Class for handling the metadata extensions for login and discovery user interface
27
 *
28
 * @link: http://docs.oasis-open.org/security/saml/Post2.0/sstc-saml-metadata-ui/v1.0/sstc-saml-metadata-ui-v1.0.pdf
29
 * @package simplesamlphp/saml2
30
 */
31
final class UIInfo extends AbstractMduiElement implements
32
    ArrayizableElementInterface,
33
    SchemaValidatableElementInterface
34
{
35
    use ExtendableElementTrait;
36
    use SchemaValidatableElementTrait;
37
38
    /** The namespace-attribute for the xs:any element */
39
    public const XS_ANY_ELT_NAMESPACE = NS::OTHER;
40
41
    /**
42
     * Create a UIInfo element.
43
     *
44
     * @param \SimpleSAML\SAML2\XML\mdui\DisplayName[] $displayName
45
     * @param \SimpleSAML\SAML2\XML\mdui\Description[] $description
46
     * @param \SimpleSAML\SAML2\XML\mdui\InformationURL[] $informationURL
47
     * @param \SimpleSAML\SAML2\XML\mdui\PrivacyStatementURL[] $privacyStatementURL
48
     * @param \SimpleSAML\SAML2\XML\mdui\Keywords[] $keywords
49
     * @param \SimpleSAML\SAML2\XML\mdui\Logo[] $logo
50
     * @param \SimpleSAML\XML\Chunk[] $children
51
     */
52
    public function __construct(
53
        protected array $displayName = [],
54
        protected array $description = [],
55
        protected array $informationURL = [],
56
        protected array $privacyStatementURL = [],
57
        protected array $keywords = [],
58
        protected array $logo = [],
59
        array $children = [],
60
    ) {
61
        Assert::maxCount($displayName, C::UNBOUNDED_LIMIT);
62
        Assert::allIsInstanceOf($displayName, DisplayName::class);
63
        /**
64
         * 2.1.2:  There MUST NOT be more than one <mdui:DisplayName>,
65
         *         within a given <mdui:UIInfo>, for a given language
66
         */
67
        $this->testLocalizedElements($displayName);
68
69
        Assert::maxCount($description, C::UNBOUNDED_LIMIT);
70
        Assert::allIsInstanceOf($description, Description::class);
71
        /**
72
         * 2.1.3:  There MUST NOT be more than one <mdui:Description>,
73
         *         within a given <mdui:UIInfo>, for a given language
74
         */
75
        $this->testLocalizedElements($description);
76
77
        Assert::maxCount($keywords, C::UNBOUNDED_LIMIT);
78
        Assert::allIsInstanceOf($keywords, Keywords::class);
79
        /**
80
         * 2.1.4:  There MUST NOT be more than one <mdui:Keywords>,
81
         *         within a given <mdui:UIInfo>, for a given language
82
         */
83
        $this->testLocalizedElements($keywords);
84
85
        Assert::maxCount($informationURL, C::UNBOUNDED_LIMIT);
86
        Assert::allIsInstanceOf($informationURL, InformationURL::class);
87
        /**
88
         * 2.1.6:  There MUST NOT be more than one <mdui:InformationURL>,
89
         *         within a given <mdui:UIInfo>, for a given language
90
         */
91
        $this->testLocalizedElements($informationURL);
92
93
        Assert::maxCount($privacyStatementURL, C::UNBOUNDED_LIMIT);
94
        Assert::allIsInstanceOf($privacyStatementURL, PrivacyStatementURL::class);
95
        /**
96
         * 2.1.7:  There MUST NOT be more than one <mdui:PrivacyStatementURL>,
97
         *         within a given <mdui:UIInfo>, for a given language
98
         */
99
        $this->testLocalizedElements($privacyStatementURL);
100
101
        Assert::maxCount($logo, C::UNBOUNDED_LIMIT);
102
        Assert::allIsInstanceOf($logo, Logo::class);
103
104
        $this->setElements($children);
105
    }
106
107
108
    /**
109
     * Collect the value of the Keywords-property
110
     *
111
     * @return \SimpleSAML\SAML2\XML\mdui\Keywords[]
112
     */
113
    public function getKeywords(): array
114
    {
115
        return $this->keywords;
116
    }
117
118
119
    /**
120
     * Add the value to the Keywords-property
121
     *
122
     * @param \SimpleSAML\SAML2\XML\mdui\Keywords $keyword
123
     */
124
    public function addKeyword(Keywords $keyword): void
125
    {
126
        /**
127
         * 2.1.4:  There MUST NOT be more than one <mdui:Keywords>,
128
         *         within a given <mdui:UIInfo>, for a given language
129
         */
130
        $keywords = array_merge($this->keywords, [$keyword]);
131
        $this->testLocalizedElements($keywords);
132
        $this->keywords = $keywords;
133
    }
134
135
136
    /**
137
     * Collect the value of the DisplayName-property
138
     *
139
     * @return \SimpleSAML\SAML2\XML\mdui\DisplayName[]
140
     */
141
    public function getDisplayName(): array
142
    {
143
        return $this->displayName;
144
    }
145
146
147
    /**
148
     * Collect the value of the Description-property
149
     *
150
     * @return \SimpleSAML\SAML2\XML\mdui\Description[]
151
     */
152
    public function getDescription(): array
153
    {
154
        return $this->description;
155
    }
156
157
158
    /**
159
     * Collect the value of the InformationURL-property
160
     * @return \SimpleSAML\SAML2\XML\mdui\InformationURL[]
161
     */
162
    public function getInformationURL(): array
163
    {
164
        return $this->informationURL;
165
    }
166
167
168
    /**
169
     * Collect the value of the PrivacyStatementURL-property
170
     *
171
     * @return \SimpleSAML\SAML2\XML\mdui\PrivacyStatementURL[]
172
     */
173
    public function getPrivacyStatementURL(): array
174
    {
175
        return $this->privacyStatementURL;
176
    }
177
178
179
    /**
180
     * Collect the value of the Logo-property
181
     *
182
     * @return \SimpleSAML\SAML2\XML\mdui\Logo[]
183
     */
184
    public function getLogo(): array
185
    {
186
        return $this->logo;
187
    }
188
189
190
    /**
191
     * Add the value to the Logo-property
192
     *
193
     * @param \SimpleSAML\SAML2\XML\mdui\Logo $logo
194
     */
195
    public function addLogo(Logo $logo): void
196
    {
197
        $this->logo[] = $logo;
198
    }
199
200
201
    /**
202
     * Add the value to the elements-property
203
     *
204
     * @param \SimpleSAML\XML\Chunk $child
205
     */
206
    public function addChild(Chunk $child): void
207
    {
208
        $this->elements[] = $child;
209
    }
210
211
212
    /**
213
     * Test if an object, at the state it's in, would produce an empty XML-element
214
     *
215
     * @return bool
216
     */
217
    public function isEmptyElement(): bool
218
    {
219
        return empty($this->getDisplayName())
220
            && empty($this->getDescription())
221
            && empty($this->getInformationURL())
222
            && empty($this->getPrivacyStatementURL())
223
            && empty($this->getKeywords())
224
            && empty($this->getLogo())
225
            && empty($this->getElements());
226
    }
227
228
229
    /**
230
     * Test localized elements for multiple items with the same language
231
     *
232
     * @param (\SimpleSAML\SAML2\XML\md\AbstractLocalizedURI|
233
     *         \SimpleSAML\SAML2\XML\md\AbstractLocalizedName|
234
     *         \SimpleSAML\SAML2\XML\mdui\Keywords)[] $elements
235
     * @return void
236
     */
237
    private function testLocalizedElements(array $elements)
238
    {
239
        if (!empty($elements)) {
240
            $types = array_map('get_class', $elements);
241
            Assert::maxCount(array_unique($types), 1, 'Multiple class types cannot be used.');
242
243
            $languages = array_map(
244
                function ($elt) {
245
                    return $elt->getLanguage();
246
                },
247
                $elements,
248
            );
249
            Assert::uniqueValues(
250
                $languages,
251
                'There MUST NOT be more than one <' . $elements[0]->getQualifiedName() . '>,'
252
                . ' within a given <mdui:UIInfo>, for a given language',
253
                ProtocolViolationException::class,
254
            );
255
        }
256
    }
257
258
259
    /**
260
     * Convert XML into a UIInfo
261
     *
262
     * @param \DOMElement $xml The XML element we should load
263
     * @return static
264
     *
265
     * @throws \SimpleSAML\XML\Exception\InvalidDOMElementException
266
     *   if the qualified name of the supplied element is wrong
267
     */
268
    public static function fromXML(DOMElement $xml): static
269
    {
270
        Assert::same($xml->localName, 'UIInfo', InvalidDOMElementException::class);
271
        Assert::same($xml->namespaceURI, UIInfo::NS, InvalidDOMElementException::class);
272
273
        $DisplayName = DisplayName::getChildrenOfClass($xml);
274
        $Description = Description::getChildrenOfClass($xml);
275
        $InformationURL = InformationURL::getChildrenOfClass($xml);
276
        $PrivacyStatementURL = PrivacyStatementURL::getChildrenOfClass($xml);
277
        $Keywords = Keywords::getChildrenOfClass($xml);
278
        $Logo = Logo::getChildrenOfClass($xml);
279
        $children = self::getChildElementsFromXML($xml);
280
281
        return new static(
282
            $DisplayName,
283
            $Description,
284
            $InformationURL,
285
            $PrivacyStatementURL,
286
            $Keywords,
287
            $Logo,
288
            $children,
289
        );
290
    }
291
292
293
    /**
294
     * Convert this UIInfo to XML.
295
     *
296
     * @param \DOMElement|null $parent The element we should append to.
297
     * @return \DOMElement
298
     */
299
    public function toXML(?DOMElement $parent = null): DOMElement
300
    {
301
        $e = $this->instantiateParentElement($parent);
302
303
        foreach ($this->getDisplayName() as $child) {
304
            $child->toXML($e);
305
        }
306
307
        foreach ($this->getDescription() as $child) {
308
            $child->toXML($e);
309
        }
310
311
        foreach ($this->getInformationURL() as $child) {
312
            $child->toXML($e);
313
        }
314
315
        foreach ($this->getPrivacyStatementURL() as $child) {
316
            $child->toXML($e);
317
        }
318
319
        foreach ($this->getKeywords() as $child) {
320
            $child->toXML($e);
321
        }
322
323
        foreach ($this->getLogo() as $child) {
324
            $child->toXML($e);
325
        }
326
327
        /** @var \SimpleSAML\XML\SerializableElementInterface $child */
328
        foreach ($this->getElements() as $child) {
329
            $child->toXML($e);
330
        }
331
332
        return $e;
333
    }
334
335
336
    /**
337
     * Create a class from an array
338
     *
339
     * NOTE: this method does not support passing additional child-objects
340
     *
341
     * @param array $data
342
     * @return static
343
     */
344
    public static function fromArray(array $data): static
345
    {
346
        $data = self::processArrayContents($data);
347
348
        return new static(
349
            $data['DisplayName'] ?? [],
350
            $data['Description'] ?? [],
351
            $data['InformationURL'] ?? [],
352
            $data['PrivacyStatementURL'] ?? [],
353
            $data['Keywords'] ?? [],
354
            $data['Logo'] ?? [],
355
            $data['children'] ?? [],
356
        );
357
    }
358
359
360
    /**
361
     * Validates an array representation of this object and returns the same array with
362
     * rationalized keys (casing) and parsed sub-elements.
363
     *
364
     * @param array $data
365
     * @return array $data
366
     */
367
    private static function processArrayContents(array $data): array
368
    {
369
        $data = array_change_key_case($data, CASE_LOWER);
370
371
        // Make sure the array keys are known for this kind of object
372
        Assert::allOneOf(
373
            array_keys($data),
374
            [
375
                'displayname',
376
                'description',
377
                'informationurl',
378
                'privacystatementurl',
379
                'keywords',
380
                'logo',
381
                'children',
382
            ],
383
            ArrayValidationException::class,
384
        );
385
386
        $retval = [];
387
388
        if (array_key_exists('displayname', $data)) {
389
            foreach ($data['displayname'] as $l => $displayName) {
390
                $retval['DisplayName'][] = DisplayName::fromArray([$l => $displayName]);
391
            }
392
        }
393
394
        if (array_key_exists('description', $data)) {
395
            foreach ($data['description'] as $l => $description) {
396
                $retval['Description'][] = Description::fromArray([$l => $description]);
397
            }
398
        }
399
400
        if (array_key_exists('informationurl', $data)) {
401
            foreach ($data['informationurl'] as $l => $iu) {
402
                $retval['InformationURL'][] = InformationURL::fromArray([$l => $iu]);
403
            }
404
        }
405
406
        if (array_key_exists('privacystatementurl', $data)) {
407
            foreach ($data['privacystatementurl'] as $l => $psu) {
408
                $retval['PrivacyStatementURL'][] = PrivacyStatementURL::fromArray([$l => $psu]);
409
            }
410
        }
411
412
        if (array_key_exists('keywords', $data)) {
413
            foreach ($data['keywords'] as $l => $keywords) {
414
                $retval['Keywords'][] = Keywords::fromArray([$l => $keywords]);
415
            }
416
        }
417
418
        if (array_key_exists('logo', $data)) {
419
            foreach ($data['logo'] as $logo) {
420
                $retval['Logo'][] = Logo::fromArray($logo);
421
            }
422
        }
423
424
        if (array_key_exists('children', $data)) {
425
            Assert::isArray($data['children'], ArrayValidationException::class);
426
            Assert::allIsInstanceOf(
427
                $data['children'],
428
                SerializableElementInterface::class,
429
                ArrayValidationException::class,
430
            );
431
            $retval['children'] = $data['children'];
432
        }
433
434
        return array_filter($retval);
435
    }
436
437
438
    /**
439
     * Create an array from this class
440
     *
441
     * NOTE: this method does not support passing additional child-objects
442
     *
443
     * @return array
444
     */
445
    public function toArray(): array
446
    {
447
        $displayName = [];
448
        foreach ($this->getDisplayName() as $child) {
449
            $displayName = array_merge($displayName, $child->toArray());
450
        }
451
452
        $description = [];
453
        foreach ($this->getDescription() as $child) {
454
            $description = array_merge($description, $child->toArray());
455
        }
456
457
        $infoUrl = [];
458
        foreach ($this->getInformationURL() as $child) {
459
            $infoUrl = array_merge($infoUrl, $child->toArray());
460
        }
461
462
        $privacyUrl = [];
463
        foreach ($this->getPrivacyStatementURL() as $child) {
464
            $privacyUrl = array_merge($privacyUrl, $child->toArray());
465
        }
466
467
        $keywords = [];
468
        foreach ($this->getKeywords() as $child) {
469
            $keywords = array_merge($keywords, $child->toArray());
470
        }
471
472
        $logo = [];
473
        foreach ($this->getLogo() as $child) {
474
            $logo[] = $child->toArray();
475
        }
476
477
        $children = $this->getElements();
478
479
        return [] +
480
            (empty($displayName) ? [] : ['DisplayName' => $displayName]) +
481
            (empty($description) ? [] : ['Description' => $description]) +
482
            (empty($infoUrl) ? [] : ['InformationURL' => $infoUrl]) +
483
            (empty($privacyUrl) ? [] : ['PrivacyStatementURL' => $privacyUrl]) +
484
            (empty($keywords) ? [] : ['Keywords' => $keywords]) +
485
            (empty($logo) ? [] : ['Logo' => $logo]) +
486
            (empty($children) ? [] : ['children' => $children]);
487
    }
488
}
489