Completed
Push — master ( 982a03...efd8b3 )
by Marek
02:27
created

SamlpExtensions::addRequestedAttributeParams()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 5
nc 1
nop 4
dl 0
loc 7
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace OMSAML2;
4
5
use DOMElement;
6
use InvalidArgumentException;
7
use SAML2\Utils;
8
9
/**
10
 * Class implementing samlp(urn:oasis:names:tc:SAML:2.0:protocol):Extensions with (http://eidas.europa.eu/saml-extensions)
11
 * eidas:SPType and eidas:RequestedAttributes, for simplesamlphp/saml2 library
12
 *
13
 * @package OMSAML2
14
 */
15
class SamlpExtensions
16
{
17
    const NAME_FORMAT_URI = 'urn:oasis:names:tc:SAML:2.0:attrname-format:uri';
18
19
    public static $ATTR_PERSON_IDENTIFIER = [
20
        'Name' => 'http://eidas.europa.eu/attributes/naturalperson/PersonIdentifier'
21
    ];
22
    public static $ATTR_CURRENT_GIVEN_NAME = [
23
        'Name' => 'http://eidas.europa.eu/attributes/naturalperson/CurrentGivenName'
24
    ];
25
    public static $ATTR_CURRENT_FAMILY_NAME = [
26
        'Name' => 'http://eidas.europa.eu/attributes/naturalperson/CurrentFamilyName'
27
    ];
28
    public static $ATTR_CURRENT_ADDRESS = [
29
        'Name' => 'http://eidas.europa.eu/attributes/naturalperson/CurrentAddress'
30
    ];
31
    public static $ATTR_DATE_OF_BIRTH = [
32
        'Name' => 'http://eidas.europa.eu/attributes/naturalperson/DateOfBirth'
33
    ];
34
    public static $ATTR_PLACE_OF_BIRTH = [
35
        'Name' => 'http://eidas.europa.eu/attributes/naturalperson/PlaceOfBirth'
36
    ];
37
    public static $ATTR_COUNTRY_CODE_OF_BIRTH = [
38
        'Name' => 'http://www.stork.gov.eu/1.0/countryCodeOfBirth'
39
    ];
40
    public static $ATTR_EMAIL = [
41
        'Name' => 'http://www.stork.gov.eu/1.0/eMail'
42
    ];
43
    public static $ATTR_AGE = [
44
        'Name' => 'http://www.stork.gov.eu/1.0/age'
45
    ];
46
    public static $ATTR_IS_AGE_OVER_18 = [
47
        'Name' => 'http://www.stork.gov.eu/1.0/isAgeOver',
48
        'AttributeValue' => 18
49
    ];
50
    public static $ATTR_CZMORIS_PHONE_NUMBER = [
51
        'Name' => 'http://schemas.eidentity.cz/moris/2016/identity/claims/phonenumber'
52
    ];
53
    public static $ATTR_CZMORIS_TR_ADRESA_ID = [
54
        'Name' => 'http://schemas.eidentita.cz/moris/2016/identity/claims/tradresaid'
55
    ];
56
    public static $ATTR_CZMORIS_ID_TYPE = [
57
        'Name' => 'http://schemas.eidentita.cz/moris/2016/identity/claims/idtype'
58
    ];
59
    public static $ATTR_CZMORIS_ID_NUMBER = [
60
        'Name' => 'http://schemas.eidentita.cz/moris/2016/identity/claims/idnumber'
61
    ];
62
63
    private $dom = false;
64
    private $sptype = 'public';
65
    private $requested_attributes = [];
66
67
    /**
68
     * You should use existing (ie. AuthRequest) DOMElement, to which, as children,
69
     * will the samlp:Extensions with relevant data be added
70
     *
71
     * @param DOMElement $dom
72
     */
73
    public function __construct(DOMelement $dom)
74
    {
75
        $this->dom = $dom;
76
    }
77
78
    public function getSPType(): string
79
    {
80
        return $this->sptype;
81
    }
82
83
    /**
84
     * Allowed values for SPType (Service Provider Type) are "public" and "private",
85
     * invalid values will be replaced by default value "public"
86
     *
87
     * @param string $sptype
88
     * @return SamlpExtensions
89
     */
90
    public function setSPType(string $sptype): SamlpExtensions
91
    {
92
        $this->sptype = in_array($sptype, ['public', 'private']) ? $sptype : 'public';
93
        return $this;
94
    }
95
96
    public function addRequestedAttributeParams(string $Name, ?string $NameFormat = self::NAME_FORMAT_URI, bool $isRequired = false, $AttributeValue = null): SamlpExtensions
97
    {
98
        return $this->addRequestedAttribute([
99
            'Name' => $Name,
100
            'NameFormat' => $NameFormat,
101
            'isRequired' => $isRequired,
102
            'AttributeValue' => $AttributeValue
103
        ]);
104
    }
105
106
    /**
107
     * Adds requested attribute by single array definition, array must contain only 'Name' key with string value, all other keys are optional
108
     * Defaults are NameFormat=${NAME_FORMAT_URI} isRequired=false and no AttributeValue
109
     *
110
     * @param array $attribute
111
     * @return SamlpExtensions
112
     * @throws InvalidArgumentException if any required argument is missing or if provided argument type is invalid
113
     */
114
    public function addRequestedAttribute(array $attribute): SamlpExtensions
115
    {
116
        if (empty($attribute['Name'])) {
117
            throw new InvalidArgumentException("Required attribute Name is missing");
118
        } else if (!is_string($attribute['Name'])) {
119
            throw new InvalidArgumentException("Attribute Name must be string");
120
        }
121
        if (!empty($attribute['NameFormat']) && !is_string($attribute['NameFormat'])) {
122
            throw new InvalidArgumentException("Attribute NameFormat must be string");
123
        }
124
        if (!empty($attribute['isRequired']) && !is_bool($attribute['isRequired'])) {
125
            throw new InvalidArgumentException("Attribute isRequired must be boolean");
126
        }
127
        if (!empty($attribute['AttributeValue']) && !is_scalar($attribute['AttributeValue'])) {
128
            throw new InvalidArgumentException("AttributeValue should be primitive type, such as string, number or boolean");
129
        }
130
131
        // set default values for not-defined attributes
132
        if (empty($attribute['NameFormat'])) {
133
            $attribute['NameFormat'] = self::NAME_FORMAT_URI;
134
        }
135
        if (empty($attribute['isRequired'])) {
136
            $attribute['isRequired'] = false;
137
        }
138
139
        // add to queue
140
        $this->requested_attributes[] = $attribute;
141
142
        // return $this for chaining
143
        return $this;
144
    }
145
146
    /**
147
     * Adds all pre-defined attributes (from ${getAllDefaultAttributes}) to attributes,
148
     * that should be added into DOMElement later (using ${toXML})
149
     *
150
     * @return SamlpExtensions
151
     * @see toXML
152
     * @see getAllDefaultAttributes
153
     */
154
    public function addAllDefaultAttributes(): SamlpExtensions
155
    {
156
        foreach ($this->getAllDefaultAttributes() as $attrArray) {
157
            $this->addRequestedAttribute($attrArray);
158
        }
159
        return $this;
160
    }
161
162
    /**
163
     * Returns array of all pre-defined attributes,
164
     * each attribute as an array compatible with this class method ${addRequestedAttribute}
165
     *
166
     * @return array
167
     * @see addRequestedAttribute
168
     */
169
    public function getAllDefaultAttributes(): array
170
    {
171
        return [
172
            self::$ATTR_AGE,
173
            self::$ATTR_COUNTRY_CODE_OF_BIRTH,
174
            self::$ATTR_CURRENT_ADDRESS,
175
            self::$ATTR_CURRENT_FAMILY_NAME,
176
            self::$ATTR_CURRENT_GIVEN_NAME,
177
            self::$ATTR_DATE_OF_BIRTH,
178
            self::$ATTR_EMAIL,
179
            self::$ATTR_IS_AGE_OVER_18,
180
            self::$ATTR_PERSON_IDENTIFIER,
181
            self::$ATTR_CZMORIS_ID_NUMBER,
182
            self::$ATTR_CZMORIS_ID_TYPE,
183
            self::$ATTR_CZMORIS_TR_ADRESA_ID,
184
            self::$ATTR_CZMORIS_PHONE_NUMBER,
185
            self::$ATTR_PLACE_OF_BIRTH
186
        ];
187
    }
188
189
    public function toXML(): DOMElement
190
    {
191
        // will throw TypeError on empty or non-compatible $this->dom value
192
        $dom = Utils::copyElement($this->dom);
193
        $doc = $dom->ownerDocument;
194
195
        $extensions = $doc->createElementNS('urn:oasis:names:tc:SAML:2.0:protocol', 'samlp:Extensions');
196
197
        // set SPType always
198
        $sptype = $doc->createElementNS('http://eidas.europa.eu/saml-extensions', 'eidas:SPType');
199
        $sptype->nodeValue = $this->sptype;
200
        $extensions->appendChild($sptype);
201
202
        // set eidas:RequestedAttributes if any defined
203
        if (!empty($this->getRequestedAttributes())) {
204
            $requested_attributes = $doc->createElementNS('http://eidas.europa.eu/saml-extensions', 'eidas:RequestedAttributes');
205
            foreach ($this->getRequestedAttributes() as $attrArray) {
206
                $attrElement = $doc->createElementNS('http://eidas.europa.eu/saml-extensions', 'eidas:RequestedAttribute');
207
                $attrElement->setAttribute('Name', $attrArray['Name']);
208
                $attrElement->setAttribute('isRequired', $attrArray['isRequired'] ? 'true' : 'false');
209
                $attrElement->setAttribute('NameFormat', $attrArray['NameFormat']);
210
                if (isset($attrArray['AttributeValue']) && (!empty($attrArray['AttributeValue']) || $attrArray['AttributeValue'] === false)) {
211
                    $attrValueElement = $doc->createElementNS('http://eidas.europa.eu/saml-extensions', 'eidas:AttributeValue');
212
                    $attrValueElement->nodeValue = (string)$attrArray['AttributeValue'];
213
                    $attrElement->appendChild($attrValueElement);
214
                }
215
                $requested_attributes->appendChild($attrElement);
216
            }
217
            $extensions->appendChild($requested_attributes);
218
        }
219
220
        $dom->appendChild($extensions);
221
222
        return Utils::copyElement($dom);
223
    }
224
225
    /**
226
     * Return currently queued RequestedAttributes in form of array configuration
227
     *
228
     * @return array
229
     */
230
    public function getRequestedAttributes(): array
231
    {
232
        return $this->requested_attributes;
233
    }
234
235
    /**
236
     * Removes all queued RequestedAttributes
237
     *
238
     * @return SamlpExtensions
239
     */
240
    public function removeAllRequestedAttributes(): SamlpExtensions
241
    {
242
        $this->requested_attributes = [];
243
        return $this;
244
    }
245
}