Passed
Push — master ( c6f9d9...f007cf )
by Marek
02:20
created

SamlpExtensions::addRequestedAttribute()   C

Complexity

Conditions 12
Paths 13

Size

Total Lines 36
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 21
dl 0
loc 36
rs 6.9666
c 0
b 0
f 0
cc 12
nc 13
nop 1

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace OMSAML2;
4
5
use DOMElement;
6
use DOMNode;
7
use InvalidArgumentException;
8
use OMSAML2\Chunks\EidasRequestedAttribute;
9
use OMSAML2\Chunks\EidasRequestedAttributes;
10
use OMSAML2\Chunks\EidasSPType;
11
use SAML2\Constants;
12
use SAML2\DOMDocumentFactory;
13
use SAML2\Utils;
14
use SAML2\XML\Chunk;
15
16
/**
17
 * Class implementing samlp(urn:oasis:names:tc:SAML:2.0:protocol):Extensions with (http://eidas.europa.eu/saml-extensions)
18
 * eidas:SPType and eidas:RequestedAttributes, for simplesamlphp/saml2 library
19
 *
20
 * @package OMSAML2
21
 */
22
class SamlpExtensions extends Chunk
23
{
24
    const NAME_FORMAT_URI = 'urn:oasis:names:tc:SAML:2.0:attrname-format:uri';
25
26
    public static $ATTR_PERSON_IDENTIFIER = [
27
        'Name' => 'http://eidas.europa.eu/attributes/naturalperson/PersonIdentifier'
28
    ];
29
    public static $ATTR_CURRENT_GIVEN_NAME = [
30
        'Name' => 'http://eidas.europa.eu/attributes/naturalperson/CurrentGivenName'
31
    ];
32
    public static $ATTR_CURRENT_FAMILY_NAME = [
33
        'Name' => 'http://eidas.europa.eu/attributes/naturalperson/CurrentFamilyName'
34
    ];
35
    public static $ATTR_CURRENT_ADDRESS = [
36
        'Name' => 'http://eidas.europa.eu/attributes/naturalperson/CurrentAddress'
37
    ];
38
    public static $ATTR_DATE_OF_BIRTH = [
39
        'Name' => 'http://eidas.europa.eu/attributes/naturalperson/DateOfBirth'
40
    ];
41
    public static $ATTR_PLACE_OF_BIRTH = [
42
        'Name' => 'http://eidas.europa.eu/attributes/naturalperson/PlaceOfBirth'
43
    ];
44
    public static $ATTR_COUNTRY_CODE_OF_BIRTH = [
45
        'Name' => 'http://www.stork.gov.eu/1.0/countryCodeOfBirth'
46
    ];
47
    public static $ATTR_EMAIL = [
48
        'Name' => 'http://www.stork.gov.eu/1.0/eMail'
49
    ];
50
    public static $ATTR_AGE = [
51
        'Name' => 'http://www.stork.gov.eu/1.0/age'
52
    ];
53
    public static $ATTR_IS_AGE_OVER_18 = [
54
        'Name' => 'http://www.stork.gov.eu/1.0/isAgeOver',
55
        'AttributeValue' => 18
56
    ];
57
    public static $ATTR_CZMORIS_PHONE_NUMBER = [
58
        'Name' => 'http://schemas.eidentity.cz/moris/2016/identity/claims/phonenumber'
59
    ];
60
    public static $ATTR_CZMORIS_TR_ADRESA_ID = [
61
        'Name' => 'http://schemas.eidentita.cz/moris/2016/identity/claims/tradresaid'
62
    ];
63
    public static $ATTR_CZMORIS_ID_TYPE = [
64
        'Name' => 'http://schemas.eidentita.cz/moris/2016/identity/claims/idtype'
65
    ];
66
    public static $ATTR_CZMORIS_ID_NUMBER = [
67
        'Name' => 'http://schemas.eidentita.cz/moris/2016/identity/claims/idnumber'
68
    ];
69
70
    /**@var EidasSPType $sptype */
71
    private $sptype = null;
72
    /**@var $requested_attributes EidasRequestedAttribute[] */
0 ignored issues
show
Documentation Bug introduced by
The doc comment $requested_attributes at position 0 could not be parsed: Unknown type name '$requested_attributes' at position 0 in $requested_attributes.
Loading history...
73
    private $requested_attributes = [];
74
75
    /**
76
     * Constructor will parse DOMElement containing samlp:Extensions for known attributes (RequestedAttributes, RequestedAttribute and SPType)
77
     *
78
     * @param DOMElement|DOMNode $dom
79
     * @throws InvalidArgumentException
80
     */
81
    public function __construct(?DOMelement $dom = null)
82
    {
83
        if ($dom === null) {
84
            $this->sptype = new EidasSPType();
85
            return;
86
        }
87
        parent::__construct($dom);
88
        $this->sptype = new EidasSPType($dom->getElementsByTagNameNS(EidasSPType::NS_EIDAS, EidasSPType::LOCAL_NAME)->item(0));
89
        $this->requested_attributes = (new EidasRequestedAttributes($dom))->requested_attributes;
90
    }
91
92
    public function addRequestedAttributeParams(string $Name, ?string $NameFormat = self::NAME_FORMAT_URI, bool $isRequired = false, $AttributeValue = null): SamlpExtensions
93
    {
94
        return $this->addRequestedAttribute([
95
            'Name' => $Name,
96
            'NameFormat' => $NameFormat,
97
            'isRequired' => $isRequired,
98
            'AttributeValue' => $AttributeValue
99
        ]);
100
    }
101
102
    /**
103
     * Adds requested attribute by single array definition, array must contain only 'Name' key with string value, all other keys are optional
104
     * Defaults are NameFormat=${NAME_FORMAT_URI} isRequired=false and no AttributeValue
105
     *
106
     * @param array $attribute
107
     * @return SamlpExtensions
108
     * @throws InvalidArgumentException if any required argument is missing or if provided argument type is invalid
109
     */
110
    public function addRequestedAttribute(array $attribute): SamlpExtensions
111
    {
112
        if (empty($attribute['Name'])) {
113
            throw new InvalidArgumentException("Required attribute Name is missing");
114
        } else if (!is_string($attribute['Name'])) {
115
            throw new InvalidArgumentException("Attribute Name must be string");
116
        }
117
        if (!empty($attribute['NameFormat']) && !is_string($attribute['NameFormat'])) {
118
            throw new InvalidArgumentException("Attribute NameFormat must be string");
119
        }
120
        if (!empty($attribute['isRequired']) && !is_bool($attribute['isRequired'])) {
121
            throw new InvalidArgumentException("Attribute isRequired must be boolean");
122
        }
123
        if (!empty($attribute['AttributeValue']) && !is_scalar($attribute['AttributeValue'])) {
124
            throw new InvalidArgumentException("AttributeValue should be primitive type, such as string, number or boolean");
125
        }
126
127
        // set default values for not-defined attributes
128
        if (empty($attribute['NameFormat'])) {
129
            $attribute['NameFormat'] = self::NAME_FORMAT_URI;
130
        }
131
        if (empty($attribute['isRequired'])) {
132
            $attribute['isRequired'] = false;
133
        }
134
135
        // add to queue
136
        $requestedAttribute = new EidasRequestedAttribute();
137
        $requestedAttribute->Name = $attribute['Name'];
138
        $requestedAttribute->NodeValue = isset($attribute['AttributeValue']) ? $attribute['AttributeValue'] : null;
139
        $requestedAttribute->NameFormat = $attribute['NameFormat'];
140
        $requestedAttribute->isRequired = $attribute['isRequired'];
141
142
        $this->requested_attributes[] = $requestedAttribute;
143
144
        // return $this for chaining
145
        return $this;
146
    }
147
148
    public function getSPType(): string
149
    {
150
        return $this->sptype->sptype;
151
    }
152
153
    /**
154
     * Allowed values for SPType (Service Provider Type) are "public" and "private",
155
     * invalid values will be replaced by default value "public"
156
     *
157
     * @param string $sptype
158
     * @return SamlpExtensions
159
     */
160
    public function setSPType(string $sptype): SamlpExtensions
161
    {
162
        $this->sptype->sptype = in_array($sptype, ['public', 'private']) ? $sptype : 'public';
163
        return $this;
164
    }
165
166
    /**
167
     * Adds all pre-defined attributes (from ${getAllDefaultAttributes}) to attributes,
168
     * that should be added into DOMElement later (using ${toXML})
169
     *
170
     * @return SamlpExtensions
171
     * @see toXML
172
     * @see getAllDefaultAttributes
173
     */
174
    public function addAllDefaultAttributes(): SamlpExtensions
175
    {
176
        foreach ($this->getAllDefaultAttributes() as $attrArray) {
177
            $this->addRequestedAttribute($attrArray);
178
        }
179
        return $this;
180
    }
181
182
    /**
183
     * Returns array of all pre-defined attributes,
184
     * each attribute as an array compatible with this class method ${addRequestedAttribute}
185
     *
186
     * @return array
187
     * @see addRequestedAttribute
188
     */
189
    public function getAllDefaultAttributes(): array
190
    {
191
        return [
192
            self::$ATTR_AGE,
193
            self::$ATTR_COUNTRY_CODE_OF_BIRTH,
194
            self::$ATTR_CURRENT_ADDRESS,
195
            self::$ATTR_CURRENT_FAMILY_NAME,
196
            self::$ATTR_CURRENT_GIVEN_NAME,
197
            self::$ATTR_DATE_OF_BIRTH,
198
            self::$ATTR_EMAIL,
199
            self::$ATTR_IS_AGE_OVER_18,
200
            self::$ATTR_PERSON_IDENTIFIER,
201
            self::$ATTR_CZMORIS_ID_NUMBER,
202
            self::$ATTR_CZMORIS_ID_TYPE,
203
            self::$ATTR_CZMORIS_TR_ADRESA_ID,
204
            self::$ATTR_CZMORIS_PHONE_NUMBER,
205
            self::$ATTR_PLACE_OF_BIRTH
206
        ];
207
    }
208
209
    public function toXML(DOMElement $parent = null): DOMElement
210
    {
211
        // will throw TypeError on empty or non-compatible $this->dom value
212
        $dom = Utils::copyElement($parent);
0 ignored issues
show
Bug introduced by
It seems like $parent can also be of type null; however, parameter $element of SAML2\Utils::copyElement() does only seem to accept DOMElement, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

212
        $dom = Utils::copyElement(/** @scrutinizer ignore-type */ $parent);
Loading history...
213
        $doc = $dom->ownerDocument;
214
215
        $extensions = $doc->createElementNS('urn:oasis:names:tc:SAML:2.0:protocol', 'samlp:Extensions');
216
217
        $this->sptype->toXML($extensions);
218
219
        // set eidas:RequestedAttributes if any defined
220
        if (!empty($this->getRequestedAttributes())) {
221
222
            $requested_attributes = new EidasRequestedAttributes();
223
            $requested_attributes->requested_attributes = $this->requested_attributes;
224
            $requested_attributes->toXML($extensions);
225
        }
226
227
        $dom->appendChild($extensions);
228
229
        return DOMDocumentFactory::fromString($dom->ownerDocument->saveXML($dom))->documentElement;
230
    }
231
232
    /**
233
     * Return currently queued RequestedAttributes in form of array configuration
234
     *
235
     * @return EidasRequestedAttribute[]
236
     */
237
    public function getRequestedAttributes(): array
238
    {
239
        return $this->requested_attributes;
240
    }
241
242
    /**
243
     * Removes all queued RequestedAttributes
244
     *
245
     * @return SamlpExtensions
246
     */
247
    public function removeAllRequestedAttributes(): SamlpExtensions
248
    {
249
        $this->requested_attributes = [];
250
        return $this;
251
    }
252
}