maestrano 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +34 -0
  3. data/Gemfile +2 -0
  4. data/Gemfile.lock +43 -0
  5. data/LICENSE +21 -0
  6. data/README.md +4 -0
  7. data/Rakefile +32 -0
  8. data/bin/maestrano-console +9 -0
  9. data/lib/maestrano.rb +114 -0
  10. data/lib/maestrano/account/bill.rb +14 -0
  11. data/lib/maestrano/api/error/authentication_error.rb +8 -0
  12. data/lib/maestrano/api/error/base_error.rb +24 -0
  13. data/lib/maestrano/api/error/connection_error.rb +8 -0
  14. data/lib/maestrano/api/error/invalid_request_error.rb +14 -0
  15. data/lib/maestrano/api/list_object.rb +37 -0
  16. data/lib/maestrano/api/object.rb +187 -0
  17. data/lib/maestrano/api/operation/base.rb +216 -0
  18. data/lib/maestrano/api/operation/create.rb +18 -0
  19. data/lib/maestrano/api/operation/delete.rb +13 -0
  20. data/lib/maestrano/api/operation/list.rb +18 -0
  21. data/lib/maestrano/api/operation/update.rb +59 -0
  22. data/lib/maestrano/api/resource.rb +39 -0
  23. data/lib/maestrano/api/util.rb +121 -0
  24. data/lib/maestrano/saml/attribute_value.rb +15 -0
  25. data/lib/maestrano/saml/metadata.rb +64 -0
  26. data/lib/maestrano/saml/request.rb +93 -0
  27. data/lib/maestrano/saml/response.rb +201 -0
  28. data/lib/maestrano/saml/schemas/saml20assertion_schema.xsd +283 -0
  29. data/lib/maestrano/saml/schemas/saml20protocol_schema.xsd +302 -0
  30. data/lib/maestrano/saml/schemas/xenc_schema.xsd +146 -0
  31. data/lib/maestrano/saml/schemas/xmldsig_schema.xsd +318 -0
  32. data/lib/maestrano/saml/settings.rb +37 -0
  33. data/lib/maestrano/saml/validation_error.rb +7 -0
  34. data/lib/maestrano/sso.rb +81 -0
  35. data/lib/maestrano/sso/base_group.rb +31 -0
  36. data/lib/maestrano/sso/base_user.rb +75 -0
  37. data/lib/maestrano/sso/group.rb +24 -0
  38. data/lib/maestrano/sso/session.rb +63 -0
  39. data/lib/maestrano/sso/user.rb +34 -0
  40. data/lib/maestrano/version.rb +3 -0
  41. data/lib/maestrano/xml_security/signed_document.rb +170 -0
  42. data/maestrano.gemspec +32 -0
  43. data/test/helpers/api_helpers.rb +82 -0
  44. data/test/helpers/saml_helpers.rb +62 -0
  45. data/test/maestrano/account/bill_test.rb +48 -0
  46. data/test/maestrano/api/list_object_test.rb +20 -0
  47. data/test/maestrano/api/object_test.rb +28 -0
  48. data/test/maestrano/api/resource_test.rb +343 -0
  49. data/test/maestrano/api/util_test.rb +31 -0
  50. data/test/maestrano/maestrano_test.rb +49 -0
  51. data/test/maestrano/saml/request_test.rb +168 -0
  52. data/test/maestrano/saml/response_test.rb +290 -0
  53. data/test/maestrano/saml/settings_test.rb +51 -0
  54. data/test/maestrano/sso/base_group_test.rb +54 -0
  55. data/test/maestrano/sso/base_user_test.rb +114 -0
  56. data/test/maestrano/sso/group_test.rb +47 -0
  57. data/test/maestrano/sso/session_test.rb +108 -0
  58. data/test/maestrano/sso/user_test.rb +65 -0
  59. data/test/maestrano/sso_test.rb +81 -0
  60. data/test/maestrano/xml_security/signed_document.rb +163 -0
  61. data/test/support/saml/certificates/certificate1 +12 -0
  62. data/test/support/saml/certificates/r1_certificate2_base64 +1 -0
  63. data/test/support/saml/responses/adfs_response_sha1.xml +46 -0
  64. data/test/support/saml/responses/adfs_response_sha256.xml +46 -0
  65. data/test/support/saml/responses/adfs_response_sha384.xml +46 -0
  66. data/test/support/saml/responses/adfs_response_sha512.xml +46 -0
  67. data/test/support/saml/responses/no_signature_ns.xml +48 -0
  68. data/test/support/saml/responses/open_saml_response.xml +56 -0
  69. data/test/support/saml/responses/r1_response6.xml.base64 +1 -0
  70. data/test/support/saml/responses/response1.xml.base64 +1 -0
  71. data/test/support/saml/responses/response2.xml.base64 +79 -0
  72. data/test/support/saml/responses/response3.xml.base64 +66 -0
  73. data/test/support/saml/responses/response4.xml.base64 +93 -0
  74. data/test/support/saml/responses/response5.xml.base64 +102 -0
  75. data/test/support/saml/responses/response_with_ampersands.xml +139 -0
  76. data/test/support/saml/responses/response_with_ampersands.xml.base64 +93 -0
  77. data/test/support/saml/responses/response_with_multiple_attribute_values.xml +57 -0
  78. data/test/support/saml/responses/simple_saml_php.xml +71 -0
  79. data/test/support/saml/responses/starfield_response.xml.base64 +1 -0
  80. data/test/support/saml/responses/wrapped_response_2.xml.base64 +150 -0
  81. data/test/test_helper.rb +46 -0
  82. metadata +305 -0
@@ -0,0 +1,31 @@
1
+ require File.expand_path('../../../test_helper', __FILE__)
2
+
3
+ module Maestrano
4
+ module API
5
+ class UtilTest < Test::Unit::TestCase
6
+ should "symbolize_names should convert names to symbols" do
7
+ start = {
8
+ 'foo' => 'bar',
9
+ 'array' => [{ 'foo' => 'bar' }],
10
+ 'nested' => {
11
+ 1 => 2,
12
+ :symbol => 9,
13
+ 'string' => nil
14
+ }
15
+ }
16
+ finish = {
17
+ :foo => 'bar',
18
+ :array => [{ :foo => 'bar' }],
19
+ :nested => {
20
+ 1 => 2,
21
+ :symbol => 9,
22
+ :string => nil
23
+ }
24
+ }
25
+
26
+ symbolized = Maestrano::API::Util.symbolize_names(start)
27
+ assert_equal(finish, symbolized)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,49 @@
1
+ require File.expand_path('../../test_helper', __FILE__)
2
+
3
+ class MaestranoTest < Test::Unit::TestCase
4
+ setup do
5
+ @config = {
6
+ environment: 'production',
7
+ api_key: 'someapikey',
8
+ sso_enabled: false,
9
+ app_host: 'http://mysuperapp.com',
10
+ sso_app_init_path: '/mno/sso/init',
11
+ sso_app_consume_path: '/mno/sso/consume',
12
+ user_creation_mode: 'real',
13
+ }
14
+
15
+ Maestrano.configure do |config|
16
+ config.environment = @config[:environment]
17
+ config.api_key = @config[:api_key]
18
+ config.sso_enabled = @config[:sso_enabled]
19
+ config.app_host = @config[:app_host]
20
+ config.sso_app_init_path = @config[:sso_app_init_path]
21
+ config.sso_app_consume_path = @config[:sso_app_consume_path]
22
+ config.user_creation_mode = @config[:user_creation_mode]
23
+ end
24
+ end
25
+
26
+ should "return the specified parameters" do
27
+ @config.keys.each do |key|
28
+ assert Maestrano.param(key) == @config[key]
29
+ end
30
+ end
31
+
32
+ context "configuration" do
33
+ should "return the right test parameters" do
34
+ Maestrano.configure { |config| config.environment = 'test' }
35
+
36
+ ['api_host','api_base','sso_name_id_format', 'sso_x509_certificate'].each do |parameter|
37
+ assert Maestrano.param(parameter) == Maestrano::Configuration::EVT_CONFIG[:test][parameter.to_sym]
38
+ end
39
+ end
40
+
41
+ should "return the right production parameters" do
42
+ Maestrano.configure { |config| config.environment = 'production' }
43
+
44
+ ['api_host','api_base','sso_name_id_format', 'sso_x509_certificate'].each do |parameter|
45
+ assert Maestrano.param(parameter) == Maestrano::Configuration::EVT_CONFIG[:production][parameter.to_sym]
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,168 @@
1
+ require File.expand_path('../../../test_helper', __FILE__)
2
+
3
+ module Maestrano
4
+ module Saml
5
+ class RequestTest < Test::Unit::TestCase
6
+
7
+ context "Request" do
8
+ should "create the deflated SAMLRequest URL parameter" do
9
+ settings = Maestrano::Saml::Settings.new
10
+ settings.idp_sso_target_url = "http://example.com"
11
+ request = Maestrano::Saml::Request.new
12
+ request.settings = settings
13
+ auth_url = request.redirect_url
14
+ assert auth_url =~ /^http:\/\/example\.com\?SAMLRequest=/
15
+
16
+ payload = CGI.unescape(auth_url.split("=").last)
17
+ decoded = Base64.decode64(payload)
18
+
19
+ zstream = Zlib::Inflate.new(-Zlib::MAX_WBITS)
20
+ inflated = zstream.inflate(decoded)
21
+ zstream.finish
22
+ zstream.close
23
+ assert_match /^<samlp:AuthnRequest/, inflated
24
+ end
25
+
26
+ should "create the deflated SAMLRequest URL parameter including the Destination" do
27
+ settings = Maestrano::Saml::Settings.new
28
+ settings.idp_sso_target_url = "http://example.com"
29
+ request = Maestrano::Saml::Request.new
30
+ request.settings = settings
31
+ auth_url = request.redirect_url
32
+ payload = CGI.unescape(auth_url.split("=").last)
33
+ decoded = Base64.decode64(payload)
34
+
35
+ zstream = Zlib::Inflate.new(-Zlib::MAX_WBITS)
36
+ inflated = zstream.inflate(decoded)
37
+ zstream.finish
38
+ zstream.close
39
+
40
+ assert_match /<samlp:AuthnRequest[^<]* Destination='http:\/\/example.com'/, inflated
41
+ end
42
+
43
+ should "create the SAMLRequest URL parameter without deflating" do
44
+ settings = Maestrano::Saml::Settings.new
45
+ settings.compress_request = false
46
+ settings.idp_sso_target_url = "http://example.com"
47
+ request = Maestrano::Saml::Request.new
48
+ request.settings = settings
49
+ auth_url = request.redirect_url
50
+ assert auth_url =~ /^http:\/\/example\.com\?SAMLRequest=/
51
+
52
+ payload = CGI.unescape(auth_url.split("=").last)
53
+ decoded = Base64.decode64(payload)
54
+ assert_match /^<samlp:AuthnRequest/, decoded
55
+ end
56
+
57
+ should "create the SAMLRequest URL parameter with IsPassive" do
58
+ settings = Maestrano::Saml::Settings.new
59
+ settings.idp_sso_target_url = "http://example.com"
60
+ settings.passive = true
61
+ request = Maestrano::Saml::Request.new
62
+ request.settings = settings
63
+ auth_url = request.redirect_url
64
+ assert auth_url =~ /^http:\/\/example\.com\?SAMLRequest=/
65
+ payload = CGI.unescape(auth_url.split("=").last)
66
+ decoded = Base64.decode64(payload)
67
+
68
+ zstream = Zlib::Inflate.new(-Zlib::MAX_WBITS)
69
+ inflated = zstream.inflate(decoded)
70
+ zstream.finish
71
+ zstream.close
72
+
73
+ assert_match /<samlp:AuthnRequest[^<]* IsPassive='true'/, inflated
74
+ end
75
+
76
+ should "create the SAMLRequest URL parameter with ProtocolBinding" do
77
+ settings = Maestrano::Saml::Settings.new
78
+ settings.idp_sso_target_url = "http://example.com"
79
+ settings.protocol_binding = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'
80
+ request = Maestrano::Saml::Request.new
81
+ request.settings = settings
82
+ auth_url = request.redirect_url
83
+ assert auth_url =~ /^http:\/\/example\.com\?SAMLRequest=/
84
+ payload = CGI.unescape(auth_url.split("=").last)
85
+ decoded = Base64.decode64(payload)
86
+
87
+ zstream = Zlib::Inflate.new(-Zlib::MAX_WBITS)
88
+ inflated = zstream.inflate(decoded)
89
+ zstream.finish
90
+ zstream.close
91
+
92
+ assert_match /<samlp:AuthnRequest[^<]* ProtocolBinding='urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'/, inflated
93
+ end
94
+
95
+ should "accept extra parameters" do
96
+ settings = Maestrano::Saml::Settings.new
97
+ settings.idp_sso_target_url = "http://example.com"
98
+
99
+ request = Maestrano::Saml::Request.new
100
+ request.settings = settings
101
+ request.params = { :hello => "there" }
102
+ auth_url = request.redirect_url
103
+ assert auth_url =~ /&hello=there$/
104
+
105
+ request = Maestrano::Saml::Request.new
106
+ request.settings = settings
107
+ request.params = { :hello => nil }
108
+ auth_url = request.redirect_url
109
+ assert auth_url =~ /&hello=$/
110
+ end
111
+
112
+ context "when the target url doesn't contain a query string" do
113
+ should "create the SAMLRequest parameter correctly" do
114
+ settings = Maestrano::Saml::Settings.new
115
+ settings.idp_sso_target_url = "http://example.com"
116
+
117
+ request = Maestrano::Saml::Request.new
118
+ request.settings = settings
119
+ auth_url = request.redirect_url
120
+ assert auth_url =~ /^http:\/\/example.com\?SAMLRequest/
121
+ end
122
+ end
123
+
124
+ context "when the target url contains a query string" do
125
+ should "create the SAMLRequest parameter correctly" do
126
+ settings = Maestrano::Saml::Settings.new
127
+ settings.idp_sso_target_url = "http://example.com?field=value"
128
+
129
+ request = Maestrano::Saml::Request.new
130
+ request.settings = settings
131
+ auth_url = request.redirect_url
132
+ assert auth_url =~ /^http:\/\/example.com\?field=value&SAMLRequest/
133
+ end
134
+ end
135
+
136
+ context "with session" do
137
+ should "pass the group_id from session to the url" do
138
+ settings = Maestrano::Saml::Settings.new
139
+ settings.idp_sso_target_url = "http://example.com"
140
+ session = {mno_group_uid: 'cld-1'}
141
+
142
+ request = Maestrano::Saml::Request.new
143
+ request.settings = settings
144
+ request.session = session
145
+ auth_url = request.redirect_url
146
+ assert auth_url =~ /&group_id=cld-1/
147
+ end
148
+
149
+ should "pass ignore the group_id from session if already present in the params" do
150
+ settings = Maestrano::Saml::Settings.new
151
+ settings.idp_sso_target_url = "http://example.com"
152
+ session = {mno_group_uid: 'cld-1'}
153
+
154
+ request = Maestrano::Saml::Request.new
155
+ request.settings = settings
156
+ request.params = {group_id: 'cld-2'}
157
+ request.session = session
158
+ auth_url = request.redirect_url
159
+
160
+ assert auth_url =~ /&group_id=cld-2/
161
+ assert auth_url !~ /&group_id=cld-1/
162
+ end
163
+ end
164
+
165
+ end
166
+ end
167
+ end
168
+ end
@@ -0,0 +1,290 @@
1
+ require File.expand_path('../../../test_helper', __FILE__)
2
+
3
+ module Maestrano
4
+ module Saml
5
+ class SamlTest < Test::Unit::TestCase
6
+ include SamlTestHelper
7
+
8
+ context "Response" do
9
+ should "raise an exception when response is initialized with nil" do
10
+ assert_raises(ArgumentError) { Maestrano::Saml::Response.new(nil) }
11
+ end
12
+
13
+ should "be able to parse a document which contains ampersands" do
14
+ Maestrano::XMLSecurity::SignedDocument.any_instance.stubs(:digests_match?).returns(true)
15
+ Maestrano::Saml::Response.any_instance.stubs(:validate_conditions).returns(true)
16
+
17
+ response = Maestrano::Saml::Response.new(ampersands_response)
18
+ settings = Maestrano::Saml::Settings.new
19
+ settings.idp_cert_fingerprint = 'c51985d947f1be57082025050846eb27f6cab783'
20
+ response.settings = settings
21
+ response.validate!
22
+ end
23
+
24
+ should "adapt namespace" do
25
+ response = Maestrano::Saml::Response.new(response_document)
26
+ assert !response.name_id.nil?
27
+ response = Maestrano::Saml::Response.new(response_document_2)
28
+ assert !response.name_id.nil?
29
+ response = Maestrano::Saml::Response.new(response_document_3)
30
+ assert !response.name_id.nil?
31
+ end
32
+
33
+ should "default to raw input when a response is not Base64 encoded" do
34
+ decoded = Base64.decode64(response_document_2)
35
+ response = Maestrano::Saml::Response.new(decoded)
36
+ assert response.document
37
+ end
38
+
39
+ context "Assertion" do
40
+ should "only retreive an assertion with an ID that matches the signature's reference URI" do
41
+ response = Maestrano::Saml::Response.new(wrapped_response_2)
42
+ response.stubs(:conditions).returns(nil)
43
+ settings = Maestrano::Saml::Settings.new
44
+ settings.idp_cert_fingerprint = signature_fingerprint_1
45
+ response.settings = settings
46
+ assert response.name_id.nil?
47
+ end
48
+ end
49
+
50
+ context "#validate!" do
51
+ should "raise when encountering a condition that prevents the document from being valid" do
52
+ response = Maestrano::Saml::Response.new(response_document)
53
+ assert_raise(Maestrano::Saml::ValidationError) do
54
+ response.validate!
55
+ end
56
+ end
57
+ end
58
+
59
+ context "#is_valid?" do
60
+ should "return false when response is initialized with blank data" do
61
+ response = Maestrano::Saml::Response.new('')
62
+ assert !response.is_valid?
63
+ end
64
+
65
+ should "return false if settings have not been set" do
66
+ response = Maestrano::Saml::Response.new(response_document)
67
+ assert !response.is_valid?
68
+ end
69
+
70
+ should "return true when the response is initialized with valid data" do
71
+ response = Maestrano::Saml::Response.new(response_document_4)
72
+ response.stubs(:conditions).returns(nil)
73
+ assert !response.is_valid?
74
+ settings = Maestrano::Saml::Settings.new
75
+ assert !response.is_valid?
76
+ response.settings = settings
77
+ assert !response.is_valid?
78
+ settings.idp_cert_fingerprint = signature_fingerprint_1
79
+ assert response.is_valid?
80
+ end
81
+
82
+ should "should be idempotent when the response is initialized with invalid data" do
83
+ response = Maestrano::Saml::Response.new(response_document_4)
84
+ response.stubs(:conditions).returns(nil)
85
+ settings = Maestrano::Saml::Settings.new
86
+ response.settings = settings
87
+ assert !response.is_valid?
88
+ assert !response.is_valid?
89
+ end
90
+
91
+ should "should be idempotent when the response is initialized with valid data" do
92
+ response = Maestrano::Saml::Response.new(response_document_4)
93
+ response.stubs(:conditions).returns(nil)
94
+ settings = Maestrano::Saml::Settings.new
95
+ response.settings = settings
96
+ settings.idp_cert_fingerprint = signature_fingerprint_1
97
+ assert response.is_valid?
98
+ assert response.is_valid?
99
+ end
100
+
101
+ should "return true when using certificate instead of fingerprint" do
102
+ response = Maestrano::Saml::Response.new(response_document_4)
103
+ response.stubs(:conditions).returns(nil)
104
+ settings = Maestrano::Saml::Settings.new
105
+ response.settings = settings
106
+ settings.idp_cert = signature_1
107
+ assert response.is_valid?
108
+ end
109
+
110
+ should "not allow signature wrapping attack" do
111
+ response = Maestrano::Saml::Response.new(response_document_4)
112
+ response.stubs(:conditions).returns(nil)
113
+ settings = Maestrano::Saml::Settings.new
114
+ settings.idp_cert_fingerprint = signature_fingerprint_1
115
+ response.settings = settings
116
+ assert response.is_valid?
117
+ assert response.name_id == "test@onelogin.com"
118
+ end
119
+
120
+ should "support dynamic namespace resolution on signature elements" do
121
+ response = Maestrano::Saml::Response.new(fixture("no_signature_ns.xml"))
122
+ response.stubs(:conditions).returns(nil)
123
+ settings = Maestrano::Saml::Settings.new
124
+ response.settings = settings
125
+ settings.idp_cert_fingerprint = "28:74:9B:E8:1F:E8:10:9C:A8:7C:A9:C3:E3:C5:01:6C:92:1C:B4:BA"
126
+ Maestrano::XMLSecurity::SignedDocument.any_instance.expects(:validate_signature).returns(true)
127
+ assert response.validate!
128
+ end
129
+
130
+ should "validate ADFS assertions" do
131
+ response = Maestrano::Saml::Response.new(fixture(:adfs_response_sha256))
132
+ response.stubs(:conditions).returns(nil)
133
+ settings = Maestrano::Saml::Settings.new
134
+ settings.idp_cert_fingerprint = "28:74:9B:E8:1F:E8:10:9C:A8:7C:A9:C3:E3:C5:01:6C:92:1C:B4:BA"
135
+ response.settings = settings
136
+ assert response.validate!
137
+ end
138
+
139
+ should "validate the digest" do
140
+ response = Maestrano::Saml::Response.new(r1_response_document_6)
141
+ response.stubs(:conditions).returns(nil)
142
+ settings = Maestrano::Saml::Settings.new
143
+ settings.idp_cert = Base64.decode64(r1_signature_2)
144
+ response.settings = settings
145
+ assert response.validate!
146
+ end
147
+
148
+ should "validate SAML 2.0 XML structure" do
149
+ resp_xml = Base64.decode64(response_document_4).gsub(/emailAddress/,'test')
150
+ response = Maestrano::Saml::Response.new(Base64.encode64(resp_xml))
151
+ response.stubs(:conditions).returns(nil)
152
+ settings = Maestrano::Saml::Settings.new
153
+ settings.idp_cert_fingerprint = signature_fingerprint_1
154
+ response.settings = settings
155
+ assert_raises(Maestrano::Saml::ValidationError, 'Digest mismatch'){ response.validate! }
156
+ end
157
+ end
158
+
159
+ context "#name_id" do
160
+ should "extract the value of the name id element" do
161
+ response = Maestrano::Saml::Response.new(response_document)
162
+ assert_equal "support@onelogin.com", response.name_id
163
+
164
+ response = Maestrano::Saml::Response.new(response_document_3)
165
+ assert_equal "someone@example.com", response.name_id
166
+ end
167
+
168
+ should "be extractable from an OpenSAML response" do
169
+ response = Maestrano::Saml::Response.new(fixture(:open_saml))
170
+ assert_equal "someone@example.org", response.name_id
171
+ end
172
+
173
+ should "be extractable from a Simple SAML PHP response" do
174
+ response = Maestrano::Saml::Response.new(fixture(:simple_saml_php))
175
+ assert_equal "someone@example.com", response.name_id
176
+ end
177
+ end
178
+
179
+ context "#check_conditions" do
180
+ should "check time conditions" do
181
+ response = Maestrano::Saml::Response.new(response_document)
182
+ assert !response.send(:validate_conditions, true)
183
+ response = Maestrano::Saml::Response.new(response_document_6)
184
+ assert response.send(:validate_conditions, true)
185
+ time = Time.parse("2011-06-14T18:25:01.516Z")
186
+ Time.stubs(:now).returns(time)
187
+ response = Maestrano::Saml::Response.new(response_document_5)
188
+ assert response.send(:validate_conditions, true)
189
+ end
190
+
191
+ should "optionally allow for clock drift" do
192
+ # The NotBefore condition in the document is 2011-06-14T18:21:01.516Z
193
+ Time.stubs(:now).returns(Time.parse("2011-06-14T18:21:01Z"))
194
+ response = Maestrano::Saml::Response.new(response_document_5, :allowed_clock_drift => 0.515)
195
+ assert !response.send(:validate_conditions, true)
196
+
197
+ Time.stubs(:now).returns(Time.parse("2011-06-14T18:21:01Z"))
198
+ response = Maestrano::Saml::Response.new(response_document_5, :allowed_clock_drift => 0.516)
199
+ assert response.send(:validate_conditions, true)
200
+ end
201
+ end
202
+
203
+ context "#attributes" do
204
+ should "extract the first attribute in a hash accessed via its symbol" do
205
+ response = Maestrano::Saml::Response.new(response_document)
206
+ assert_equal "demo", response.attributes[:uid]
207
+ end
208
+
209
+ should "extract the first attribute in a hash accessed via its name" do
210
+ response = Maestrano::Saml::Response.new(response_document)
211
+ assert_equal "demo", response.attributes["uid"]
212
+ end
213
+
214
+ should "extract all attributes" do
215
+ response = Maestrano::Saml::Response.new(response_document)
216
+ assert_equal "demo", response.attributes[:uid]
217
+ assert_equal "value", response.attributes[:another_value]
218
+ end
219
+
220
+ should "work for implicit namespaces" do
221
+ response = Maestrano::Saml::Response.new(response_document_3)
222
+ assert_equal "someone@example.com", response.attributes["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"]
223
+ end
224
+
225
+ should "not raise on responses without attributes" do
226
+ response = Maestrano::Saml::Response.new(response_document_4)
227
+ assert_equal Hash.new, response.attributes
228
+ end
229
+
230
+ context "#multiple values" do
231
+ should "extract single value as string" do
232
+ response = Maestrano::Saml::Response.new(fixture(:response_with_multiple_attribute_values))
233
+ assert_equal "demo", response.attributes[:uid]
234
+ end
235
+
236
+ should "extract first of multiple values as string for b/w compatibility" do
237
+ response = Maestrano::Saml::Response.new(fixture(:response_with_multiple_attribute_values))
238
+ assert_equal 'value1', response.attributes[:another_value]
239
+ end
240
+
241
+ should "return array with all attributes when asked" do
242
+ response = Maestrano::Saml::Response.new(fixture(:response_with_multiple_attribute_values))
243
+ assert_equal ['value2', 'value1'], response.attributes[:another_value].values
244
+ end
245
+
246
+ should "return last of multiple values when multiple Attribute tags in XML" do
247
+ response = Maestrano::Saml::Response.new(fixture(:response_with_multiple_attribute_values))
248
+ assert_equal 'role2', response.attributes[:role]
249
+ end
250
+
251
+ should "return all of multiple values in reverse order when multiple Attribute tags in XML" do
252
+ response = Maestrano::Saml::Response.new(fixture(:response_with_multiple_attribute_values))
253
+ assert_equal ['role2', 'role1'], response.attributes[:role].values
254
+ end
255
+ end
256
+ end
257
+
258
+ context "#session_expires_at" do
259
+ should "extract the value of the SessionNotOnOrAfter attribute" do
260
+ response = Maestrano::Saml::Response.new(response_document)
261
+ assert response.session_expires_at.is_a?(Time)
262
+
263
+ response = Maestrano::Saml::Response.new(response_document_2)
264
+ assert response.session_expires_at.nil?
265
+ end
266
+ end
267
+
268
+ context "#issuer" do
269
+ should "return the issuer inside the response assertion" do
270
+ response = Maestrano::Saml::Response.new(response_document)
271
+ assert_equal "https://app.onelogin.com/saml/metadata/13590", response.issuer
272
+ end
273
+
274
+ should "return the issuer inside the response" do
275
+ response = Maestrano::Saml::Response.new(response_document_2)
276
+ assert_equal "wibble", response.issuer
277
+ end
278
+ end
279
+
280
+ context "#success" do
281
+ should "find a status code that says success" do
282
+ response = Maestrano::Saml::Response.new(response_document)
283
+ response.success?
284
+ end
285
+ end
286
+
287
+ end
288
+ end
289
+ end
290
+ end