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

RegistrationInfo::validateArray()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 24
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

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