rsaml 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. data/LICENSE +0 -0
  2. data/README +13 -0
  3. data/Rakefile +136 -0
  4. data/lib/rsaml.rb +57 -0
  5. data/lib/rsaml/action.rb +57 -0
  6. data/lib/rsaml/action_namespace.rb +63 -0
  7. data/lib/rsaml/advice.rb +34 -0
  8. data/lib/rsaml/assertion.rb +192 -0
  9. data/lib/rsaml/attribute.rb +76 -0
  10. data/lib/rsaml/audience.rb +19 -0
  11. data/lib/rsaml/authentication_context.rb +34 -0
  12. data/lib/rsaml/authn_context/README +1 -0
  13. data/lib/rsaml/authn_context/authentication_context_declaration.rb +42 -0
  14. data/lib/rsaml/authn_context/identification.rb +10 -0
  15. data/lib/rsaml/authn_context/physical_verification.rb +24 -0
  16. data/lib/rsaml/condition.rb +13 -0
  17. data/lib/rsaml/conditions.rb +107 -0
  18. data/lib/rsaml/encrypted.rb +12 -0
  19. data/lib/rsaml/errors.rb +16 -0
  20. data/lib/rsaml/evidence.rb +21 -0
  21. data/lib/rsaml/ext/string.rb +5 -0
  22. data/lib/rsaml/identifier.rb +9 -0
  23. data/lib/rsaml/identifier/base.rb +23 -0
  24. data/lib/rsaml/identifier/issuer.rb +28 -0
  25. data/lib/rsaml/identifier/name.rb +55 -0
  26. data/lib/rsaml/parser.rb +23 -0
  27. data/lib/rsaml/protocol.rb +21 -0
  28. data/lib/rsaml/protocol/artifact_resolve.rb +14 -0
  29. data/lib/rsaml/protocol/assertion_id_request.rb +18 -0
  30. data/lib/rsaml/protocol/authn_request.rb +91 -0
  31. data/lib/rsaml/protocol/idp_entry.rb +18 -0
  32. data/lib/rsaml/protocol/idp_list.rb +28 -0
  33. data/lib/rsaml/protocol/message.rb +65 -0
  34. data/lib/rsaml/protocol/name_id_policy.rb +31 -0
  35. data/lib/rsaml/protocol/query.rb +12 -0
  36. data/lib/rsaml/protocol/query/attribute_query.rb +56 -0
  37. data/lib/rsaml/protocol/query/authn_query.rb +30 -0
  38. data/lib/rsaml/protocol/query/authz_decision_query.rb +40 -0
  39. data/lib/rsaml/protocol/query/subject_query.rb +22 -0
  40. data/lib/rsaml/protocol/request.rb +27 -0
  41. data/lib/rsaml/protocol/requested_authn_context.rb +34 -0
  42. data/lib/rsaml/protocol/response.rb +56 -0
  43. data/lib/rsaml/protocol/scoping.rb +33 -0
  44. data/lib/rsaml/protocol/status.rb +38 -0
  45. data/lib/rsaml/protocol/status_code.rb +84 -0
  46. data/lib/rsaml/proxy_restriction.rb +30 -0
  47. data/lib/rsaml/statement.rb +10 -0
  48. data/lib/rsaml/statement/attribute_statement.rb +27 -0
  49. data/lib/rsaml/statement/authentication_statement.rb +57 -0
  50. data/lib/rsaml/statement/authorization_decision_statement.rb +53 -0
  51. data/lib/rsaml/statement/base.rb +9 -0
  52. data/lib/rsaml/subject.rb +37 -0
  53. data/lib/rsaml/subject_confirmation.rb +35 -0
  54. data/lib/rsaml/subject_confirmation_data.rb +55 -0
  55. data/lib/rsaml/subject_locality.rb +27 -0
  56. data/lib/rsaml/validatable.rb +21 -0
  57. data/lib/rsaml/version.rb +9 -0
  58. data/lib/xml_enc.rb +3 -0
  59. data/lib/xml_sig.rb +11 -0
  60. data/lib/xml_sig/canonicalization_method.rb +43 -0
  61. data/lib/xml_sig/key_info.rb +55 -0
  62. data/lib/xml_sig/reference.rb +57 -0
  63. data/lib/xml_sig/signature.rb +29 -0
  64. data/lib/xml_sig/signature_method.rb +20 -0
  65. data/lib/xml_sig/signed_info.rb +27 -0
  66. data/lib/xml_sig/transform.rb +37 -0
  67. data/test/action_namespace_test.rb +93 -0
  68. data/test/action_test.rb +51 -0
  69. data/test/advice_test.rb +25 -0
  70. data/test/assertion_test.rb +192 -0
  71. data/test/attribute_test.rb +60 -0
  72. data/test/authentication_context_test.rb +26 -0
  73. data/test/conditions_test.rb +84 -0
  74. data/test/evidence_test.rb +33 -0
  75. data/test/identifier_test.rb +22 -0
  76. data/test/issuer_test.rb +33 -0
  77. data/test/name_test.rb +33 -0
  78. data/test/parser_test.rb +32 -0
  79. data/test/protocol/assertion_id_request_test.rb +19 -0
  80. data/test/protocol/attribute_query_test.rb +30 -0
  81. data/test/protocol/authn_query_test.rb +20 -0
  82. data/test/protocol/authn_request_test.rb +56 -0
  83. data/test/protocol/authz_decision_query_test.rb +31 -0
  84. data/test/protocol/idp_list_test.rb +15 -0
  85. data/test/protocol/request_test.rb +66 -0
  86. data/test/protocol/response_test.rb +68 -0
  87. data/test/protocol/scoping_test.rb +20 -0
  88. data/test/protocol/status_code_test.rb +34 -0
  89. data/test/protocol/status_test.rb +16 -0
  90. data/test/proxy_restriction_test.rb +20 -0
  91. data/test/rsaml_test.rb +12 -0
  92. data/test/statement_test.rb +101 -0
  93. data/test/subject_locality_test.rb +27 -0
  94. data/test/subject_test.rb +44 -0
  95. data/test/test_helper.rb +16 -0
  96. data/test/xml_sig/canonicalization_test.rb +19 -0
  97. metadata +187 -0
@@ -0,0 +1,29 @@
1
+ module XmlSig #:nodoc:
2
+ # An XML signature.
3
+ class Signature
4
+ attr_accessor :id
5
+ attr_accessor :signed_info
6
+ attr_accessor :signature_value
7
+ attr_accessor :key_info
8
+
9
+ def objects
10
+ @objects ||= []
11
+ end
12
+
13
+ def assert
14
+ # raise AssertionError, "An assertion signature must be valid"
15
+ end
16
+
17
+ # Construct an XML fragment representing the signature
18
+ def to_xml(xml=Builder::XmlMarkup.new)
19
+ attributes = {}
20
+ attributes['Id'] = id unless id.nil?
21
+ attributes['xmlns:ds'] = "http://www.w3.org/2000/09/xmldsig#"
22
+ xml.tag!('ds:Signature', attributes) {
23
+ xml << signed_info.to_xml if signed_info
24
+ xml.tag!('ds:SignatureValue')
25
+ xml.tag!('ds:KeyInfo') if key_info
26
+ }
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,20 @@
1
+ module XmlSig #:nodoc:
2
+ # SignatureMethod is a required element that specifies the algorithm used for
3
+ # signature generation and validation. This algorithm identifies all cryptographic
4
+ # functions involved in the signature operation (e.g. hashing, public key
5
+ # algorithms, MACs, padding, etc.).
6
+ class SignatureMethod
7
+ attr_accessor :algorithm
8
+
9
+ def validate
10
+ raise ValidationError, "Algorithm is required" if algorithm.nil?
11
+ end
12
+
13
+ def to_xml(xml=Builder::XmlMarkup.new)
14
+ attributes = {'Algorightm' => algorithm}
15
+ xml.tag!('ds:SignatureMethod', attributes) {
16
+ xml.tag!('ds:HMACOutputLength', hmac_output_length) unless hmac_output_length.nil?
17
+ }
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,27 @@
1
+ module XmlSig #:nodoc:
2
+ class SignedInfo
3
+ attr_accessor :id
4
+ attr_accessor :canonicalization_method
5
+ attr_accessor :signature_method
6
+
7
+ def references
8
+ @references ||= []
9
+ end
10
+
11
+ def validate
12
+ raise ValidationError, "Canonicalization method is required" if canonicalization_method.nil?
13
+ raise ValidationError, "Signature method is required" if signature_method.nil?
14
+ raise ValidationError, "At least one reference is required" if references.empty?
15
+ end
16
+
17
+ def to_xml(xml=Builder::XmlMarkup.new)
18
+ attributes = {}
19
+ attributes['Id'] = id unless id.nil?
20
+ xml.tag!('ds:SignedInfo', attributes) {
21
+ xml << canonicalization_method.to_xml unless canonicalization_method.nil?
22
+ xml << signature_method.to_xml unless signature_method.nil?
23
+ references.each { |reference| xml << reference.to_xml }
24
+ }
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,37 @@
1
+ module XmlSig #:nodoc:
2
+ class Transform
3
+ attr_accessor :algorithm
4
+ attr_accessor :xpath
5
+
6
+ def to_xml(xml=Builder::XmlMarkup.new)
7
+ attributes = {'Algorightm' => algorithm}
8
+ xml.tag!('ds:Transform', attributes) {
9
+ xml.tag!('ds:XPath', xpath) unless xpath.nil?
10
+ }
11
+ end
12
+ end
13
+ class Base64Transform
14
+ IDENTIFIER = 'http://www.w3.org/2000/09/xmldsig#base64'
15
+ def process(content)
16
+ content # TODO: implement
17
+ end
18
+ end
19
+ class XPathFiltering
20
+ IDENTIFIER = 'http://www.w3.org/TR/1999/REC-xpath-19991116'
21
+ def process(content)
22
+ content # TODO: implement
23
+ end
24
+ end
25
+ class EnvelopedSignatureTransform
26
+ IDENTIFIER = 'http://www.w3.org/2000/09/xmldsig#enveloped-signature'
27
+ def process(content)
28
+ content # TODO: implement
29
+ end
30
+ end
31
+ class XSLTTransform
32
+ IDENTIFIER = 'http://www.w3.org/TR/1999/REC-xslt-19991116'
33
+ def process(content)
34
+ content # TODO: implement
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,93 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ class ActionNamespaceTest < Test::Unit::TestCase
4
+ context "the ActionNamespace class" do
5
+ should "define all of the namespaces in the SAML 2.0 specification" do
6
+ namespace_uris = ActionNamespace.namespaces.values.collect { |ns| ns.uri }
7
+ assert namespace_uris.include?('urn:oasis:names:tc:SAML:1.0:action:rwedc')
8
+ assert namespace_uris.include?('urn:oasis:names:tc:SAML:1.0:action:rwedc-negation')
9
+ assert namespace_uris.include?('urn:oasis:names:tc:SAML:1.0:action:ghpp')
10
+ assert namespace_uris.include?('urn:oasis:names:tc:SAML:1.0:action:unix')
11
+ end
12
+ should "return a namespace instance given a URI" do
13
+ assert_equal(ActionNamespace.namespaces[:rwedc],
14
+ ActionNamespace.namespace_for_uri('urn:oasis:names:tc:SAML:1.0:action:rwedc')
15
+ )
16
+ assert_equal(ActionNamespace.namespaces[:rwedc_negation],
17
+ ActionNamespace.namespace_for_uri('urn:oasis:names:tc:SAML:1.0:action:rwedc-negation')
18
+ )
19
+ end
20
+ end
21
+ context "the rwdec namespace" do
22
+ setup do
23
+ @action_namespace = ActionNamespace.namespaces[:rwedc]
24
+ @expected_actions = ['Read','Write','Execute','Delete','Control']
25
+ end
26
+ should "have the correct uri" do
27
+ assert_equal 'urn:oasis:names:tc:SAML:1.0:action:rwedc', @action_namespace.uri
28
+ end
29
+ should "return the uri when to_s is invoked" do
30
+ assert_equal @action_namespace.uri, @action_namespace.to_s
31
+ end
32
+ should "have the correct actions" do
33
+ assert_equal @expected_actions, @action_namespace.action_names
34
+ end
35
+ should "return true for a valid action" do
36
+ @expected_actions.each do |action|
37
+ assert @action_namespace.valid_action?(action)
38
+ end
39
+ end
40
+ should "return false for an invalid action" do
41
+ assert_equal false, @action_namespace.valid_action?('invalid-action')
42
+ end
43
+ end
44
+ context "the rwdec-negation namespace" do
45
+ setup do
46
+ @action_namespace = ActionNamespace.namespaces[:rwedc_negation]
47
+ @expected_actions = [
48
+ 'Read','Write','Execute','Delete','Control',
49
+ '~Read','~Write','~Execute','~Delete','~Control'
50
+ ]
51
+ end
52
+ should "have the correct uri" do
53
+ assert_equal 'urn:oasis:names:tc:SAML:1.0:action:rwedc-negation', @action_namespace.uri
54
+ end
55
+ should "have the correct actions" do
56
+ assert_equal @expected_actions, @action_namespace.action_names
57
+ end
58
+ should "return true for a valid action" do
59
+ @expected_actions.each do |action|
60
+ assert @action_namespace.valid_action?(action)
61
+ end
62
+ end
63
+ should "return false for an invalid action" do
64
+ assert_equal false, @action_namespace.valid_action?('invalid-action')
65
+ end
66
+ end
67
+ context "the ghpp namespace" do
68
+ setup do
69
+ @action_namespace = ActionNamespace.namespaces[:ghpp]
70
+ @expected_actions = ['GET','HEAD','PUT','POST']
71
+ end
72
+ should "have the correct uri" do
73
+ assert_equal 'urn:oasis:names:tc:SAML:1.0:action:ghpp', @action_namespace.uri
74
+ end
75
+ should "have the correct actions" do
76
+ assert_equal @expected_actions, @action_namespace.action_names
77
+ end
78
+ should "return false for an invalid action" do
79
+ assert_equal false, @action_namespace.valid_action?('invalid-action')
80
+ end
81
+ end
82
+ context "the unix file permissions namespace" do
83
+ setup do
84
+ @action_namespace = ActionNamespace.namespaces[:unix]
85
+ end
86
+ should "have the correct uri" do
87
+ assert_equal 'urn:oasis:names:tc:SAML:1.0:action:unix', @action_namespace.uri
88
+ end
89
+ should_eventually "have the correct actions" do
90
+
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,51 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ class ActionTest < Test::Unit::TestCase
4
+ context "an action" do
5
+ setup do
6
+ @action = Action.new('Read')
7
+ end
8
+ should "have the rwedc_negation namespace by default" do
9
+ assert_equal Action.namespaces[:rwedc_negation], @action.namespace
10
+ end
11
+ should "be valid by default" do
12
+ assert @action.valid?
13
+ end
14
+ context "when producing xml" do
15
+ should "optionally have a namespace" do
16
+ assert_match(/<saml:Action Namespace="#{@action.namespace}"/, @action.to_xml)
17
+ end
18
+ end
19
+ context "when consuming xml" do
20
+ should "return a valid Action instance" do
21
+ action = Action.from_xml('<saml:Action xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">Read</saml:Action>')
22
+ assert_not_nil(action)
23
+ assert_equal 'Read', action.value
24
+ assert_equal Action.namespaces[:rwedc_negation], action.namespace
25
+ assert action.valid?
26
+ end
27
+ context "with an action namespace attribute" do
28
+ should "return a valid Action instance with an action namespace" do
29
+ action = Action.from_xml(%Q(<saml:Action xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" Namespace="#{Action.namespaces[:rwedc]}">Write</saml:Action>))
30
+ assert_not_nil(action)
31
+ assert_equal 'Write', action.value
32
+ assert_equal Action.namespaces[:rwedc], action.namespace
33
+ end
34
+ end
35
+ end
36
+ context "when validating" do
37
+ should "raise an error if no value is provided" do
38
+ assert_raise ValidationError do
39
+ @action.value = nil
40
+ @action.validate
41
+ end
42
+ end
43
+ should "raise an error if the value is not in the specified namespace" do
44
+ assert_raise ValidationError do
45
+ @action.value = 'PUT'
46
+ @action.validate
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,25 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ class AdviceTest < Test::Unit::TestCase
4
+ context "an advice" do
5
+ setup { @advice = Advice.new }
6
+ should "have 0 assertions by default" do
7
+ assert @advice.assertions.empty?
8
+ end
9
+ should "be valid if all assertions are valid" do
10
+ assert @advice.valid?
11
+ end
12
+ context "when producing xml" do
13
+ should "produce an empty advice tag when there are no assertions" do
14
+ assert_match(/<saml:Advice><\/saml:Advice>/, @advice.to_xml)
15
+ end
16
+ end
17
+ context "when consuming xml" do
18
+ should "return a valid Advice instance" do
19
+ advice = Advice.from_xml('<saml:Advice xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"></saml:Advice>')
20
+ assert_not_nil(advice)
21
+ assert advice.valid?
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,192 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ class AssertionTest < Test::Unit::TestCase
4
+ context "an assertion" do
5
+ setup do
6
+ @issuer = Identifier::Issuer.new('example')
7
+ @assertion = Assertion.new(@issuer)
8
+ end
9
+ should "require version of 2.0" do
10
+ assert_equal "2.0", @assertion.version
11
+ end
12
+ should "require ID" do
13
+ assert_not_nil @assertion.id
14
+ end
15
+ should "require issue instant" do
16
+ assert_not_nil @assertion.issue_instant
17
+ end
18
+ should "require an issuer" do
19
+ assert_not_nil @assertion.issuer
20
+ end
21
+
22
+ context "with only a subject" do
23
+ setup do
24
+ @assertion.subject = 'test'
25
+ end
26
+ should "be valid" do
27
+ assert_nothing_raised do
28
+ @assertion.validate
29
+ end
30
+ end
31
+ end
32
+ context "with an authentication statement" do
33
+ setup do
34
+ @assertion.statements << AuthenticationStatement.new(AuthenticationContext.new)
35
+ end
36
+ should "require a subject" do
37
+ assert_raise ValidationError do
38
+ @assertion.validate
39
+ end
40
+ assert !@assertion.valid?
41
+ end
42
+ end
43
+ context "with an attribute statement" do
44
+ setup do
45
+ @assertion.statements << AttributeStatement.new
46
+ end
47
+ should "require a subject" do
48
+ assert_raise ValidationError do
49
+ @assertion.validate
50
+ end
51
+ end
52
+ end
53
+ context "with a authorization decision statement" do
54
+ setup do
55
+ @assertion.statements << AuthorizationDecisionStatement.new
56
+ end
57
+ should "require a subject" do
58
+ assert_raise ValidationError do
59
+ @assertion.validate
60
+ end
61
+ end
62
+ end
63
+
64
+ context "when producing xml" do
65
+ # TODO: implement tests for XML results
66
+ should "always include version, id and issue instant attributes and an issuer child element" do
67
+ xml = @assertion.to_xml
68
+ assert_match(/^<saml:Assertion/, xml)
69
+ assert_match(/Version="2.0"/, xml)
70
+ assert_match(/ID="#{uuid_match}"/, xml)
71
+ assert_match(/IssueInstant="#{date_match}"/, xml)
72
+ assert_match(/<saml:Issuer/, xml)
73
+ end
74
+ should "optionally include a signature" do
75
+ @assertion.signature = XmlSig::Signature.new
76
+ xml = @assertion.to_xml
77
+ assert_match(/<ds:Signature/, xml)
78
+ end
79
+ should "optionally include a subject" do
80
+ @assertion.subject = Subject.new(Identifier::Name.new('The Subject'))
81
+ xml = @assertion.to_xml
82
+ assert_match(%Q(<saml:Subject><saml:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">The Subject</saml:NameID></saml:Subject>), xml)
83
+ end
84
+ should "optionally include conditions" do
85
+ @assertion.conditions << Condition.new
86
+ xml = @assertion.to_xml
87
+ assert_match(/<saml:Conditions>/, xml)
88
+ end
89
+ should "optionally include advice" do
90
+ uri = 'http://example.com/some_advice'
91
+ advice = Advice.new
92
+ advice.assertions << AssertionIDRef.new(UUID.new.generate)
93
+ advice.assertions << AssertionURIRef.new(uri) # a URI
94
+ @assertion.advice << advice
95
+ xml = @assertion.to_xml
96
+ assert_match(/<saml:Advice><saml:AssertionIDRef>#{uuid_match}<\/saml:AssertionIDRef>/, xml)
97
+ assert_match(/<saml:AssertionURIRef>#{uri}<\/saml:AssertionURIRef><\/saml:Advice>/, xml)
98
+ end
99
+ should "optionally include statements" do
100
+ @assertion.statements << AuthenticationStatement.new(AuthenticationContext.new)
101
+ xml = @assertion.to_xml
102
+ assert_match(/<saml:AuthnStatement AuthnInstant="#{date_match}"/, xml)
103
+ end
104
+ end
105
+
106
+ context "when consuming xml" do
107
+ should "return a valid Assertion instance" do
108
+ xml_fragment = %Q(
109
+ <saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">
110
+ <saml:Issuer>Example</saml:Issuer>
111
+ <saml:Subject>Anthony</saml:Subject>
112
+ </saml:Assertion>
113
+ )
114
+ assertion = Assertion.from_xml(xml_fragment)
115
+ assert_not_nil assertion
116
+ assert_not_nil assertion.issuer
117
+ assert_equal 'Example', assertion.issuer.value
118
+ assert_nothing_raised do
119
+ assertion.validate
120
+ end
121
+ end
122
+ context "where there is no saml:Subject element" do
123
+ should "raise a validation error" do
124
+ xml_fragment = %Q(
125
+ <saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">
126
+ <saml:Issuer>Example</saml:Issuer>
127
+ </saml:Assertion>
128
+ )
129
+ assertion = Assertion.from_xml(xml_fragment)
130
+ assert_raise ValidationError do
131
+ assertion.validate
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end
137
+ context "an assertion URI ref" do
138
+ setup do
139
+ @assertion_uri_ref = AssertionURIRef.new('some_uri')
140
+ end
141
+ should "provide a uri accessor" do
142
+ assert_equal 'some_uri', @assertion_uri_ref.uri
143
+ end
144
+ context "when validating" do
145
+ should "raise an error if no uri is provided" do
146
+ assert_raise ValidationError do
147
+ @assertion_uri_ref.uri = nil
148
+ @assertion_uri_ref.validate
149
+ end
150
+ end
151
+ end
152
+ context "when producing xml" do
153
+ should "have the uri as the value" do
154
+ assert_match(/<saml:AssertionURIRef>some_uri<\/saml:AssertionURIRef>/, @assertion_uri_ref.to_xml)
155
+ end
156
+ end
157
+ context "when consuming xml" do
158
+ should "return a valid AssertionURIRef instance" do
159
+ assertion_ref = AssertionURIRef.from_xml('<saml:AssertionURIRef xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">some_uri</saml:AssertionURIRef>')
160
+ assert_not_nil assertion_ref
161
+ assert_equal 'some_uri', assertion_ref.uri
162
+ assert assertion_ref.valid?
163
+ end
164
+ end
165
+ end
166
+ context "an assertion ID ref" do
167
+ setup do
168
+ @assertion_id_ref = AssertionIDRef.new('some_id')
169
+ end
170
+ context "when validating" do
171
+ should "raise an error if no id is provided" do
172
+ assert_raise ValidationError do
173
+ @assertion_id_ref.id = nil
174
+ @assertion_id_ref.validate
175
+ end
176
+ end
177
+ end
178
+ context "when producing xml" do
179
+ should "have an id as the value" do
180
+ assert_match(/<saml:AssertionIDRef>some_id<\/saml:AssertionIDRef>/, @assertion_id_ref.to_xml)
181
+ end
182
+ end
183
+ context "when consuming xml" do
184
+ should "return a valid AssertionIDRef instance" do
185
+ assertion_ref = AssertionIDRef.from_xml('<saml:AssertionIDRef xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">some_id</saml:AssertionIDRef>')
186
+ assert_not_nil assertion_ref
187
+ assert_equal 'some_id', assertion_ref.id
188
+ assert assertion_ref.valid?
189
+ end
190
+ end
191
+ end
192
+ end