Skip to content

Commit 05e22f0

Browse files
committed
Merge pull request #160 from onelogin/attrs
Extended solution for Attributes method [] can raise NoMethodError
2 parents 8fa4c1d + 53a0228 commit 05e22f0

4 files changed

Lines changed: 164 additions & 7 deletions

File tree

Gemfile

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@ group :test do
66
if RUBY_VERSION < "1.9"
77
gem "nokogiri", "~> 1.5.0"
88
gem "ruby-debug", "~> 0.10.4"
9+
elsif RUBY_VERSION < "2.0"
10+
gem "debugger", "~> 1.1"
911
else
10-
gem "debugger", "~> 1.1"
12+
gem "byebug", "~> 2.1.1"
1113
end
1214
gem "shoulda", "~> 2.11"
1315
gem "rake", "~> 10"

README.md

Lines changed: 101 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,8 +154,7 @@ The following attributes are set:
154154
* idp_slo_target_url
155155
* id_cert_fingerpint
156156

157-
If are using saml:AttributeStatement to transfer metadata, like the user name, you can access all the attributes through response.attributes. It
158-
contains all the saml:AttributeStatement with its 'Name' as a indifferent key and the one saml:AttributeValue as value.
157+
If are using saml:AttributeStatement to transfer metadata, like the user name, you can access all the attributes through response.attributes. It contains all the saml:AttributeStatement with its 'Name' as a indifferent key and the one saml:AttributeValue as value.
159158

160159
```ruby
161160
response = OneLogin::RubySaml::Response.new(params[:SAMLResponse])
@@ -164,6 +163,106 @@ response.settings = saml_settings
164163
response.attributes[:username]
165164
```
166165

166+
Imagine this saml:AttributeStatement
167+
168+
```xml
169+
<saml:AttributeStatement>
170+
<saml:Attribute Name="uid">
171+
<saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">demo</saml:AttributeValue>
172+
</saml:Attribute>
173+
<saml:Attribute Name="another_value">
174+
<saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">value1</saml:AttributeValue>
175+
<saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">value2</saml:AttributeValue>
176+
</saml:Attribute>
177+
<saml:Attribute Name="role">
178+
<saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">role1</saml:AttributeValue>
179+
</saml:Attribute>
180+
<saml:Attribute Name="role">
181+
<saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">role2</saml:AttributeValue>
182+
<saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">role3</saml:AttributeValue>
183+
</saml:Attribute>
184+
<saml:Attribute Name="attribute_with_nil_value">
185+
<saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
186+
</saml:Attribute>
187+
<saml:Attribute Name="attribute_with_nils_and_empty_strings">
188+
<saml:AttributeValue/>
189+
<saml:AttributeValue>valuePresent</saml:AttributeValue>
190+
<saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
191+
<saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="1"/>
192+
</saml:Attribute>
193+
</saml:AttributeStatement>
194+
```
195+
196+
```ruby
197+
pp(response.attributes) # is an OneLogin::RubySaml::Attributes object
198+
# => @attributes=
199+
{"uid"=>["demo"],
200+
"another_value"=>["value1", "value2"],
201+
"role"=>["role1", "role2", "role3"],
202+
"attribute_with_nil_value"=>[nil],
203+
"attribute_with_nils_and_empty_strings"=>["", "valuePresent", nil, nil]}>
204+
205+
# Active single_value_compatibility
206+
OneLogin::RubySaml::Attributes.single_value_compatibility = true
207+
208+
pp(response.attributes[:uid])
209+
# => "demo"
210+
211+
pp(response.attributes[:role])
212+
# => "role1"
213+
214+
pp(response.attributes.single(:role))
215+
# => "role1"
216+
217+
pp(response.attributes.multi(:role))
218+
# => ["role1", "role2", "role3"]
219+
220+
pp(response.attributes[:attribute_with_nil_value])
221+
# => nil
222+
223+
pp(response.attributes[:attribute_with_nils_and_empty_strings])
224+
# => ""
225+
226+
pp(response.attributes[:not_exists])
227+
# => nil
228+
229+
pp(response.attributes.single(:not_exists))
230+
# => nil
231+
232+
pp(response.attributes.multi(:not_exists))
233+
# => nil
234+
235+
# Deactive single_value_compatibility
236+
OneLogin::RubySaml::Attributes.single_value_compatibility = false
237+
238+
pp(response.attributes[:uid])
239+
# => ["demo"]
240+
241+
pp(response.attributes[:role])
242+
# => ["role1", "role2", "role3"]
243+
244+
pp(response.attributes.single(:role))
245+
# => "role1"
246+
247+
pp(response.attributes.multi(:role))
248+
# => ["role1", "role2", "role3"]
249+
250+
pp(response.attributes[:attribute_with_nil_value])
251+
# => [nil]
252+
253+
pp(response.attributes[:attribute_with_nils_and_empty_strings])
254+
# => ["", "valuePresent", nil, nil]
255+
256+
pp(response.attributes[:not_exists])
257+
# => nil
258+
259+
pp(response.attributes.single(:not_exists))
260+
# => nil
261+
262+
pp(response.attributes.multi(:not_exists))
263+
# => nil
264+
```
265+
167266
## Service Provider Metadata
168267

169268
To form a trusted pair relationship with the IdP, the SP (you) need to provide metadata XML

lib/onelogin/ruby-saml/attributes.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ def include?(name)
4747

4848
# Return first value for an attribute
4949
def single(name)
50-
attributes[canonize_name(name)].first
50+
attributes[canonize_name(name)].first if include?(name)
5151
end
5252

5353
# Return all values for an attribute
@@ -106,4 +106,4 @@ def attributes
106106
end
107107
end
108108
end
109-
end
109+
end

test/response_test.rb

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -243,43 +243,99 @@ class RubySamlTest < Test::Unit::TestCase
243243
assert_equal "demo", response.attributes[:uid]
244244
end
245245

246+
should "extract single value as string in compatibility mode off" do
247+
response = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
248+
OneLogin::RubySaml::Attributes.single_value_compatibility = false
249+
assert_equal ["demo"], response.attributes[:uid]
250+
# classes are not reloaded between tests so restore default
251+
OneLogin::RubySaml::Attributes.single_value_compatibility = true
252+
end
253+
246254
should "extract first of multiple values as string for b/w compatibility" do
247255
response = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
248256
assert_equal 'value1', response.attributes[:another_value]
249257
end
250258

259+
should "extract first of multiple values as string for b/w compatibility in compatibility mode off" do
260+
response = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
261+
OneLogin::RubySaml::Attributes.single_value_compatibility = false
262+
assert_equal ['value1', 'value2'], response.attributes[:another_value]
263+
OneLogin::RubySaml::Attributes.single_value_compatibility = true
264+
end
265+
251266
should "return array with all attributes when asked in XML order" do
252267
response = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
253268
assert_equal ['value1', 'value2'], response.attributes.multi(:another_value)
254269
end
255270

271+
should "return array with all attributes when asked in XML order in compatibility mode off" do
272+
response = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
273+
OneLogin::RubySaml::Attributes.single_value_compatibility = false
274+
assert_equal ['value1', 'value2'], response.attributes.multi(:another_value)
275+
OneLogin::RubySaml::Attributes.single_value_compatibility = true
276+
end
277+
256278
should "return first of multiple values when multiple Attribute tags in XML" do
257279
response = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
258280
assert_equal 'role1', response.attributes[:role]
259281
end
260282

283+
should "return first of multiple values when multiple Attribute tags in XML in compatibility mode off" do
284+
response = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
285+
OneLogin::RubySaml::Attributes.single_value_compatibility = false
286+
assert_equal ['role1', 'role2', 'role3'], response.attributes[:role]
287+
OneLogin::RubySaml::Attributes.single_value_compatibility = true
288+
end
289+
261290
should "return all of multiple values in reverse order when multiple Attribute tags in XML" do
262291
response = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
263292
assert_equal ['role1', 'role2', 'role3'], response.attributes.multi(:role)
264293
end
265294

295+
should "return all of multiple values in reverse order when multiple Attribute tags in XML in compatibility mode off" do
296+
response = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
297+
OneLogin::RubySaml::Attributes.single_value_compatibility = false
298+
assert_equal ['role1', 'role2', 'role3'], response.attributes.multi(:role)
299+
OneLogin::RubySaml::Attributes.single_value_compatibility = true
300+
end
301+
266302
should "return nil value correctly" do
267303
response = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
268304
assert_nil response.attributes[:attribute_with_nil_value]
269305
end
270306

307+
should "return nil value correctly when not in compatibility mode off" do
308+
response = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
309+
OneLogin::RubySaml::Attributes.single_value_compatibility = false
310+
assert_equal [nil], response.attributes[:attribute_with_nil_value]
311+
OneLogin::RubySaml::Attributes.single_value_compatibility = true
312+
end
313+
271314
should "return multiple values including nil and empty string" do
272315
response = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
273316
assert_equal ["", "valuePresent", nil, nil], response.attributes.multi(:attribute_with_nils_and_empty_strings)
274317
end
275318

276-
should "return multiple values from [] when not in compatibility mode" do
319+
should "return multiple values from [] when not in compatibility mode off" do
277320
response = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
278321
OneLogin::RubySaml::Attributes.single_value_compatibility = false
279322
assert_equal ["", "valuePresent", nil, nil], response.attributes[:attribute_with_nils_and_empty_strings]
280-
# classes are not reloaded between tests so restore default
281323
OneLogin::RubySaml::Attributes.single_value_compatibility = true
282324
end
325+
326+
should "check what happens when trying retrieve attribute that does not exists" do
327+
response = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
328+
assert_equal nil, response.attributes[:attribute_not_exists]
329+
assert_equal nil, response.attributes.single(:attribute_not_exists)
330+
assert_equal nil, response.attributes.multi(:attribute_not_exists)
331+
332+
OneLogin::RubySaml::Attributes.single_value_compatibility = false
333+
assert_equal nil, response.attributes[:attribute_not_exists]
334+
assert_equal nil, response.attributes.single(:attribute_not_exists)
335+
assert_equal nil, response.attributes.multi(:attribute_not_exists)
336+
OneLogin::RubySaml::Attributes.single_value_compatibility = true
337+
end
338+
283339
end
284340
end
285341

0 commit comments

Comments
 (0)