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

PublicationInfo::validateArray()   A

Complexity

Conditions 5
Paths 8

Size

Total Lines 29
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

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