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

PublicationInfo   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 231
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 72
dl 0
loc 231
rs 10
c 1
b 0
f 0
wmc 22

10 Methods

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