Passed
Pull Request — master (#337)
by Tim
02:20
created

PublicationInfo::validateArray()   A

Complexity

Conditions 5
Paths 12

Size

Total Lines 20
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 12
nc 12
nop 1
dl 0
loc 20
rs 9.5555
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace SimpleSAML\SAML2\XML\mdrpi;
6
7
use DateTimeImmutable;
8
use DOMElement;
9
use SimpleSAML\Assert\Assert;
10
use SimpleSAML\SAML2\Constants as C;
11
use SimpleSAML\SAML2\Exception\ProtocolViolationException;
12
use SimpleSAML\SAML2\XML\md\AbstractLocalizedURI;
13
use SimpleSAML\XML\ArrayizableElementInterface;
14
use SimpleSAML\XML\Exception\InvalidDOMElementException;
15
16
use function preg_replace;
17
18
/**
19
 * Class for handling the mdrpi:PublicationInfo element.
20
 *
21
 * @link: http://docs.oasis-open.org/security/saml/Post2.0/saml-metadata-rpi/v1.0/saml-metadata-rpi-v1.0.pdf
22
 * @package simplesamlphp/saml2
23
 */
24
final class PublicationInfo extends AbstractMdrpiElement implements ArrayizableElementInterface
25
{
26
    /**
27
     * Create/parse a mdrpi:PublicationInfo element.
28
     *
29
     * @param string $publisher
30
     * @param \DateTimeImmutable|null $creationInstant
31
     * @param string|null $publicationId
32
     * @param \SimpleSAML\SAML2\XML\mdrpi\UsagePolicy[] $usagePolicy
33
     */
34
    public function __construct(
35
        protected string $publisher,
36
        protected ?DateTimeImmutable $creationInstant = null,
37
        protected ?string $publicationId = null,
38
        protected array $usagePolicy = [],
39
    ) {
40
        Assert::nullOrSame($creationInstant?->getTimeZone()->getName(), 'Z', ProtocolViolationException::class);
41
        Assert::allIsInstanceOf($usagePolicy, UsagePolicy::class);
42
43
        /**
44
         * 2.2.1:  There MUST NOT be more than one <mdrpi:UsagePolicy>,
45
         *         within a given <mdrpi:UsageInfo>, for a given language
46
         */
47
        $languages = array_map(
48
            function ($up) {
49
                return $up->getLanguage();
50
            },
51
            $usagePolicy,
52
        );
53
        Assert::uniqueValues(
54
            $languages,
55
            'There MUST NOT be more than one <mdrpi:UsagePolicy>,'
56
            . ' within a given <mdrpi:PublicationInfo>, for a given language',
57
            ProtocolViolationException::class,
58
        );
59
    }
60
61
62
    /**
63
     * Collect the value of the publisher-property
64
     *
65
     * @return string
66
     */
67
    public function getPublisher(): string
68
    {
69
        return $this->publisher;
70
    }
71
72
73
    /**
74
     * Collect the value of the creationInstant-property
75
     *
76
     * @return \DateTimeImmutable|null
77
     */
78
    public function getCreationInstant(): ?DateTimeImmutable
79
    {
80
        return $this->creationInstant;
81
    }
82
83
84
    /**
85
     * Collect the value of the publicationId-property
86
     *
87
     * @return string|null
88
     */
89
    public function getPublicationId(): ?string
90
    {
91
        return $this->publicationId;
92
    }
93
94
95
    /**
96
     * Collect the value of the UsagePolicy-property
97
     *
98
     * @return \SimpleSAML\SAML2\XML\mdrpi\UsagePolicy[]
99
     */
100
    public function getUsagePolicy(): array
101
    {
102
        return $this->usagePolicy;
103
    }
104
105
106
    /**
107
     * Convert XML into a PublicationInfo
108
     *
109
     * @param \DOMElement $xml The XML element we should load
110
     * @return static
111
     *
112
     * @throws \SimpleSAML\XML\Exception\InvalidDOMElementException
113
     *   if the qualified name of the supplied element is wrong
114
     * @throws \SimpleSAML\XML\Exception\MissingAttributeException
115
     *   if the supplied element is missing one of the mandatory attributes
116
     */
117
    public static function fromXML(DOMElement $xml): static
118
    {
119
        Assert::same($xml->localName, 'PublicationInfo', InvalidDOMElementException::class);
120
        Assert::same($xml->namespaceURI, PublicationInfo::NS, InvalidDOMElementException::class);
121
122
        $publisher = self::getAttribute($xml, 'publisher');
123
        $creationInstant = self::getOptionalAttribute($xml, 'creationInstant', null);
124
125
        // 2.2.1:  Time values MUST be expressed in the UTC timezone using the 'Z' timezone identifier
126
        if ($creationInstant !== null) {
127
            // Strip sub-seconds - See paragraph 1.3.3 of SAML core specifications
128
            $creationInstant = preg_replace('/([.][0-9]+Z)$/', 'Z', $creationInstant, 1);
129
130
            Assert::validDateTimeZulu($creationInstant, ProtocolViolationException::class);
131
            $creationInstant = new DateTimeImmutable($creationInstant);
132
        }
133
134
        $publicationId = self::getOptionalAttribute($xml, 'publicationId', null);
135
        $UsagePolicy = UsagePolicy::getChildrenOfClass($xml);
136
137
        return new static($publisher, $creationInstant, $publicationId, $UsagePolicy);
138
    }
139
140
141
    /**
142
     * Convert this element to XML.
143
     *
144
     * @param \DOMElement|null $parent The element we should append to.
145
     * @return \DOMElement
146
     */
147
    public function toXML(DOMElement $parent = null): DOMElement
148
    {
149
        $e = $this->instantiateParentElement($parent);
150
        $e->setAttribute('publisher', $this->getPublisher());
151
152
        if ($this->getCreationInstant() !== null) {
153
            $e->setAttribute('creationInstant', $this->getCreationInstant()->format(C::DATETIME_FORMAT));
154
        }
155
156
        if ($this->getPublicationId() !== null) {
157
            $e->setAttribute('publicationId', $this->getPublicationId());
158
        }
159
160
        foreach ($this->getUsagePolicy() as $up) {
161
            $up->toXML($e);
162
        }
163
164
        return $e;
165
    }
166
167
168
    /**
169
     * Create a class from an array
170
     *
171
     * @param array $data
172
     * @return static
173
     */
174
    public static function fromArray(array $data): static
175
    {
176
        self::validateArray($data);
177
178
        $publisher = $data['publisher'];
179
        $creationInstant = $data['creationInstant'] ?? null;
180
        $creationInstant = is_null($creationInstant) ? null : new DateTimeImmutable($creationInstant);
181
        $publicationId = $data['publicationId'] ?? null;
182
183
        $usagePolicy = [];
184
        foreach ($data['usagePolicy'] as $k => $v) {
185
            $usagePolicy[] = UsagePolicy::fromArray([$k => $v]);
186
        }
187
188
        return new static($publisher, $creationInstant, $publicationId, $usagePolicy);
189
    }
190
191
192
    /**
193
     * Validate an array
194
     *
195
     * @param array $data
196
     * @return void
197
     */
198
    public static function validateArray(array $data): void
199
    {
200
        Assert::allOneOf(array_keys($data), ['publisher', 'creationInstant', 'publicationId', 'usagePolicy']);
201
        Assert::keyExists($data, 'publisher');
202
203
        Assert::string($data['publisher']);
204
205
        if (array_key_exists('creationInstant', $data)) {
206
            Assert::string($data['creationInstant']);
207
            Assert::validDateTimeZulu($data['creationInstant']);
208
        }
209
210
        if (array_key_exists('publicationId', $data)) {
211
            Assert::string($data['publicationId']);
212
        }
213
214
        if (array_key_exists('usagePolicy', $data)) {
215
            Assert::isArray($data['usagePolicy']);
216
            foreach ($data['usagePolicy'] as $lang => $up) {
217
                AbstractLocalizedURI::validateArray([$lang => $up]);
218
            }
219
        }
220
    }
221
222
223
    /**
224
     * Create an array from this class
225
     *
226
     * @return array
227
     */
228
    public function toArray(): array
229
    {
230
        $data = [];
231
        $data['publisher'] = $this->getPublisher();
232
233
        if ($this->getCreationInstant() !== null) {
234
            $data['creationInstant'] = $this->getCreationInstant()->format(C::DATETIME_FORMAT);
235
        }
236
237
        if ($this->getPublicationId() !== null) {
238
            $data['publicationId'] = $this->getPublicationId();
239
        }
240
241
        if (!empty($this->getUsagePolicy())) {
242
            $data['usagePolicy'] = [];
243
            foreach ($this->getUsagePolicy() as $up) {
244
                $data['usagePolicy'] = array_merge($data['usagePolicy'], $up->toArray());
245
            }
246
        }
247
        return $data;
248
    }
249
}
250