|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
namespace SAML2; |
|
4
|
|
|
|
|
5
|
|
|
/** |
|
6
|
|
|
* Class for SAML 2 attribute query messages. |
|
7
|
|
|
* |
|
8
|
|
|
* An attribute query asks for a set of attributes. The following |
|
9
|
|
|
* rules apply: |
|
10
|
|
|
* |
|
11
|
|
|
* - If no attributes are present in the query, all attributes should be |
|
12
|
|
|
* returned. |
|
13
|
|
|
* - If any attributes are present, only those attributes which are present |
|
14
|
|
|
* in the query should be returned. |
|
15
|
|
|
* - If an attribute contains any attribute values, only the attribute values |
|
16
|
|
|
* which match those in the query should be returned. |
|
17
|
|
|
* |
|
18
|
|
|
* @package SimpleSAMLphp |
|
19
|
|
|
*/ |
|
20
|
|
|
class AttributeQuery extends SubjectQuery |
|
21
|
|
|
{ |
|
22
|
|
|
/** |
|
23
|
|
|
* The attributes, as an associative array. |
|
24
|
|
|
* |
|
25
|
|
|
* @var array |
|
26
|
|
|
*/ |
|
27
|
|
|
private $attributes; |
|
28
|
|
|
|
|
29
|
|
|
/** |
|
30
|
|
|
* The NameFormat used on all attributes. |
|
31
|
|
|
* |
|
32
|
|
|
* If more than one NameFormat is used, this will contain |
|
33
|
|
|
* the unspecified nameformat. |
|
34
|
|
|
* |
|
35
|
|
|
* @var string |
|
36
|
|
|
*/ |
|
37
|
|
|
private $nameFormat; |
|
38
|
|
|
|
|
39
|
|
|
/** |
|
40
|
|
|
* Constructor for SAML 2 attribute query messages. |
|
41
|
|
|
* |
|
42
|
|
|
* @param \DOMElement|null $xml The input message. |
|
43
|
|
|
* @throws \Exception |
|
44
|
|
|
*/ |
|
45
|
|
|
public function __construct(\DOMElement $xml = null) |
|
46
|
|
|
{ |
|
47
|
|
|
parent::__construct('AttributeQuery', $xml); |
|
48
|
|
|
|
|
49
|
|
|
$this->attributes = array(); |
|
50
|
|
|
$this->nameFormat = Constants::NAMEFORMAT_UNSPECIFIED; |
|
51
|
|
|
|
|
52
|
|
|
if ($xml === null) { |
|
53
|
|
|
return; |
|
54
|
|
|
} |
|
55
|
|
|
|
|
56
|
|
|
$firstAttribute = true; |
|
57
|
|
|
$attributes = Utils::xpQuery($xml, './saml_assertion:Attribute'); |
|
58
|
|
View Code Duplication |
foreach ($attributes as $attribute) { |
|
|
|
|
|
|
59
|
|
|
if (!$attribute->hasAttribute('Name')) { |
|
60
|
|
|
throw new \Exception('Missing name on <saml:Attribute> element.'); |
|
61
|
|
|
} |
|
62
|
|
|
$name = $attribute->getAttribute('Name'); |
|
63
|
|
|
|
|
64
|
|
|
if ($attribute->hasAttribute('NameFormat')) { |
|
65
|
|
|
$nameFormat = $attribute->getAttribute('NameFormat'); |
|
66
|
|
|
} else { |
|
67
|
|
|
$nameFormat = Constants::NAMEFORMAT_UNSPECIFIED; |
|
68
|
|
|
} |
|
69
|
|
|
|
|
70
|
|
|
if ($firstAttribute) { |
|
71
|
|
|
$this->nameFormat = $nameFormat; |
|
72
|
|
|
$firstAttribute = false; |
|
73
|
|
|
} else { |
|
74
|
|
|
if ($this->nameFormat !== $nameFormat) { |
|
75
|
|
|
$this->nameFormat = Constants::NAMEFORMAT_UNSPECIFIED; |
|
76
|
|
|
} |
|
77
|
|
|
} |
|
78
|
|
|
|
|
79
|
|
|
if (!array_key_exists($name, $this->attributes)) { |
|
80
|
|
|
$this->attributes[$name] = array(); |
|
81
|
|
|
} |
|
82
|
|
|
|
|
83
|
|
|
$values = Utils::xpQuery($attribute, './saml_assertion:AttributeValue'); |
|
84
|
|
|
foreach ($values as $value) { |
|
85
|
|
|
$this->attributes[$name][] = trim($value->textContent); |
|
86
|
|
|
} |
|
87
|
|
|
} |
|
88
|
|
|
} |
|
89
|
|
|
|
|
90
|
|
|
/** |
|
91
|
|
|
* Retrieve all requested attributes. |
|
92
|
|
|
* |
|
93
|
|
|
* @return array All requested attributes, as an associative array. |
|
94
|
|
|
*/ |
|
95
|
|
|
public function getAttributes() |
|
96
|
|
|
{ |
|
97
|
|
|
return $this->attributes; |
|
98
|
|
|
} |
|
99
|
|
|
|
|
100
|
|
|
/** |
|
101
|
|
|
* Set all requested attributes. |
|
102
|
|
|
* |
|
103
|
|
|
* @param array $attributes All requested attributes, as an associative array. |
|
104
|
|
|
*/ |
|
105
|
|
|
public function setAttributes(array $attributes) |
|
106
|
|
|
{ |
|
107
|
|
|
$this->attributes = $attributes; |
|
108
|
|
|
} |
|
109
|
|
|
|
|
110
|
|
|
/** |
|
111
|
|
|
* Retrieve the NameFormat used on all attributes. |
|
112
|
|
|
* |
|
113
|
|
|
* If more than one NameFormat is used in the received attributes, this |
|
114
|
|
|
* returns the unspecified NameFormat. |
|
115
|
|
|
* |
|
116
|
|
|
* @return string The NameFormat used on all attributes. |
|
117
|
|
|
*/ |
|
118
|
|
|
public function getAttributeNameFormat() |
|
119
|
|
|
{ |
|
120
|
|
|
return $this->nameFormat; |
|
121
|
|
|
} |
|
122
|
|
|
|
|
123
|
|
|
/** |
|
124
|
|
|
* Set the NameFormat used on all attributes. |
|
125
|
|
|
* |
|
126
|
|
|
* @param string $nameFormat The NameFormat used on all attributes. |
|
127
|
|
|
*/ |
|
128
|
|
|
public function setAttributeNameFormat($nameFormat) |
|
129
|
|
|
{ |
|
130
|
|
|
assert('is_string($nameFormat)'); |
|
131
|
|
|
|
|
132
|
|
|
$this->nameFormat = $nameFormat; |
|
133
|
|
|
} |
|
134
|
|
|
|
|
135
|
|
|
/** |
|
136
|
|
|
* Convert the attribute query message to an XML element. |
|
137
|
|
|
* |
|
138
|
|
|
* @return \DOMElement This attribute query. |
|
139
|
|
|
*/ |
|
140
|
|
|
public function toUnsignedXML() |
|
141
|
|
|
{ |
|
142
|
|
|
$root = parent::toUnsignedXML(); |
|
143
|
|
|
|
|
144
|
|
|
foreach ($this->attributes as $name => $values) { |
|
145
|
|
|
$attribute = $root->ownerDocument->createElementNS(Constants::NS_SAML, 'saml:Attribute'); |
|
146
|
|
|
$root->appendChild($attribute); |
|
147
|
|
|
$attribute->setAttribute('Name', $name); |
|
148
|
|
|
|
|
149
|
|
|
if ($this->nameFormat !== Constants::NAMEFORMAT_UNSPECIFIED) { |
|
150
|
|
|
$attribute->setAttribute('NameFormat', $this->nameFormat); |
|
151
|
|
|
} |
|
152
|
|
|
|
|
153
|
|
|
foreach ($values as $value) { |
|
154
|
|
View Code Duplication |
if (is_string($value)) { |
|
|
|
|
|
|
155
|
|
|
$type = 'xs:string'; |
|
156
|
|
|
} elseif (is_int($value)) { |
|
157
|
|
|
$type = 'xs:integer'; |
|
158
|
|
|
} else { |
|
159
|
|
|
$type = null; |
|
160
|
|
|
} |
|
161
|
|
|
|
|
162
|
|
|
$attributeValue = Utils::addString($attribute, Constants::NS_SAML, 'saml:AttributeValue', $value); |
|
163
|
|
|
if ($type !== null) { |
|
164
|
|
|
$attributeValue->setAttributeNS(Constants::NS_XSI, 'xsi:type', $type); |
|
165
|
|
|
} |
|
166
|
|
|
} |
|
167
|
|
|
} |
|
168
|
|
|
|
|
169
|
|
|
return $root; |
|
170
|
|
|
} |
|
171
|
|
|
} |
|
172
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.