rsaml 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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,55 @@
1
+ module RSAML #:nodoc:
2
+ module Identifier #:nodoc:
3
+ # A Name identifier.
4
+ class Name < Base
5
+ # The following identifiers MAY be used to refer to the classification of the attribute name
6
+ # for purposes of interpreting the name.
7
+ def self.formats
8
+ {
9
+ :unspecified => 'urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified',
10
+ :email_address => 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress',
11
+ :x509_subject_name => 'urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName',
12
+ :windows_domain_qualified_name => 'urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName',
13
+ :kerberos => 'urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos',
14
+ :entity => 'urn:oasis:names:tc:SAML:2.0:nameid-format:entity',
15
+ :persistent => 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent',
16
+ :transient => 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient'
17
+ }
18
+ end
19
+
20
+ # A URI reference representing the classification of string-based identifier information.
21
+ attr_accessor :format
22
+
23
+ # A name identifier established by a service provider or affiliation of providers for the entity, if
24
+ # different from the primary name identifier given
25
+ attr_accessor :sp_provided_id
26
+
27
+ # The value of the identifier
28
+ attr_accessor :value
29
+
30
+ # Initialize the identifier with the given value
31
+ def initialize(value)
32
+ @value = value
33
+ end
34
+
35
+ # The format of the name.
36
+ def format
37
+ @format ||= Name.formats[:unspecified]
38
+ end
39
+
40
+ # Construct an XML fragment representing the name
41
+ def to_xml(xml=Builder::XmlMarkup.new)
42
+ attributes = {'Format' => format}
43
+ attributes['NameQualifier'] = name_qualifier unless name_qualifier.nil?
44
+ attributes['SPNameQualifier'] = sp_name_qualifier unless sp_name_qualifier.nil?
45
+ attributes['SPProvidedID'] = sp_provided_id unless sp_provided_id.nil?
46
+ xml.tag!('saml:NameID', value, attributes)
47
+ end
48
+
49
+ def self.from_xml(element)
50
+ Name.new(element.text)
51
+ end
52
+
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,23 @@
1
+ module RSAML #:nodoc:
2
+ class Parser
3
+ # Parse the given SAML message and return a Ruby object structure representing
4
+ # the message. This may include protocol and core classes.
5
+ def parse(xml)
6
+ messages = []
7
+ xml = REXML::Document.new(xml) if xml.is_a?(String)
8
+
9
+ if attribute_query_elements = xml.get_elements('samlp:AttributeQuery')
10
+ attribute_query_elements.each do |attribute_query_element|
11
+ messages << Protocol::Query::AttributeQuery.from_xml(attribute_query_element)
12
+ end
13
+ end
14
+
15
+ case messages.length
16
+ when 1: messages.first
17
+ when 0: nil
18
+ else
19
+ messages
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,21 @@
1
+ module RSAML #:nodoc:
2
+ # The protocol module contains request and response classes for the SAML protocol implementation
3
+ module Protocol
4
+ end
5
+ end
6
+
7
+ require 'rsaml/protocol/message'
8
+ require 'rsaml/protocol/status_code'
9
+ require 'rsaml/protocol/status'
10
+ require 'rsaml/protocol/request'
11
+ require 'rsaml/protocol/response'
12
+
13
+ require 'rsaml/protocol/name_id_policy'
14
+ require 'rsaml/protocol/scoping'
15
+ require 'rsaml/protocol/idp_list'
16
+ require 'rsaml/protocol/idp_entry'
17
+
18
+ require 'rsaml/protocol/assertion_id_request'
19
+ require 'rsaml/protocol/authn_request'
20
+
21
+ require 'rsaml/protocol/query'
@@ -0,0 +1,14 @@
1
+ module RSAML #:nodoc:
2
+ module Protocol #:nodoc:
3
+ # The ArtifactResolve message is used to request that a SAML protocol message be returned in an
4
+ # <ArtifactResponse> message by specifying an artifact that represents the SAML protocol message.
5
+ # The original transmission of the artifact is governed by the specific protocol binding that is
6
+ # being used; see [SAMLBind] for more information on the use of artifacts in bindings.
7
+ #
8
+ # The <ArtifactResolve> message SHOULD be signed or otherwise authenticated and integrity
9
+ # protected by the protocol binding used to deliver the message.
10
+ class ArtifactResolve
11
+
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,18 @@
1
+ module RSAML #:nodoc:
2
+ module Protocol #:nodoc:
3
+ # Request to return assertions with the given ids
4
+ class AssertionIDRequest
5
+ # Specify each assertion to return.
6
+ def assertion_id_refs
7
+ @assertion_id_refs ||= []
8
+ end
9
+
10
+ # Construct an XML fragment representing the assertion id request
11
+ def to_xml(xml=Builder::XmlMarkup.new)
12
+ xml.tag!('samlp:AssertionIDRequest') {
13
+ assertion_id_refs.each { |assertion_id_ref| xml << assertion_id_ref.to_xml }
14
+ }
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,91 @@
1
+ module RSAML #:nodoc:
2
+ module Protocol #:nodoc:
3
+ # To request that an identity provider issue an assertion with an authentication statement, a presenter
4
+ # authenticates to that identity provider (or relies on an existing security context) and sends it an
5
+ # <AuthnRequest> message that describes the properties that the resulting assertion needs to have to
6
+ # satisfy its purpose. Among these properties may be information that relates to the content of the assertion
7
+ # and/or information that relates to how the resulting <Response> message should be delivered to the
8
+ # requester. The process of authentication of the presenter may take place before, during, or after the initial
9
+ # delivery of the <AuthnRequest> message.
10
+ #
11
+ # The requester might not be the same as the presenter of the request if, for example, the requester is a
12
+ # relying party that intends to use the resulting assertion to authenticate or authorize the requested subject
13
+ # so that the relying party can decide whether to provide a service.
14
+ class AuthnRequest < Request
15
+ # Specifies the requested subject of the resulting assertion(s).
16
+ attr_accessor :subject
17
+
18
+ # Specifies constraints on the name identifier to be used to represent the requested subject. If omitted,
19
+ # then any type of identifier supported by the identity provider for the requested subject can be used,
20
+ # constrained by any relevant deployment-specific policies, with respect to privacy, for example.
21
+ attr_accessor :name_id_policy
22
+
23
+ # Specifies the SAML conditions the requester expects to limit the validity and/or use of the resulting
24
+ # assertion(s). The responder MAY modify or supplement this set as it deems necessary. The
25
+ # information in this element is used as input to the process of constructing the assertion, rather than as
26
+ # conditions on the use of the request itself.
27
+ attr_accessor :conditions
28
+
29
+ # Specifies the requirements, if any, that the requester places on the authentication context that applies
30
+ # to the responding provider's authentication of the presenter.
31
+ attr_accessor :requested_authn_context
32
+
33
+ # Specifies a set of identity providers trusted by the requester to authenticate the presenter, as well as
34
+ # limitations and context related to proxying of the <Au message to subsequent identity providers by the
35
+ # responder.
36
+ attr_accessor :scoping
37
+
38
+ # A Boolean value. If "true", the identity provider MUST authenticate the presenter directly rather than
39
+ # rely on a previous security context. If a value is not provided, the default is "false". However, if both
40
+ # ForceAuthn and IsPassive are "true", the identity provider MUST NOT freshly authenticate the
41
+ # presenter unless the constraints of IsPassive can be met.
42
+ attr_accessor :force_authn
43
+
44
+ # A Boolean value. If "true", the identity provider and the user agent itself MUST NOT visibly take control
45
+ # of the user interface from the requester and interact with the presenter in a noticeable fashion. If a
46
+ # value is not provided, the default is "false".
47
+ attr_accessor :is_passive
48
+
49
+ attr_accessor :assertion_consumer_service_index
50
+
51
+ attr_accessor :assertion_consumer_service_url
52
+
53
+ # A URI reference that identifies a SAML protocol binding to be used when returning the response message.
54
+ attr_accessor :protocol_binding
55
+
56
+ # Indirectly identifies information associated with the requester describing the SAML attributes the
57
+ # requester desires or requires to be supplied by the identity provider in the <Response> message. The
58
+ # identity provider MUST have a trusted means to map the index value in the attribute to information
59
+ # associated with the requester.
60
+ attr_accessor :attribute_consuming_service_url
61
+
62
+ # Specifies the human-readable name of the requester for use by the presenter's user agent or the
63
+ # identity provider
64
+ attr_accessor :provider_name
65
+
66
+ # Validate the authentication request.
67
+ def validate
68
+ raise ValidationError, "Conditions must be of type Conditions" if conditions && !conditions.is_a?(Conditions)
69
+ end
70
+
71
+ # Construct an XML fragment representing the authentication request
72
+ def to_xml(xml=Builder::XmlMarkup.new)
73
+ attributes = {}
74
+ attributes['ForceAuthn'] = force_authn unless force_authn.nil?
75
+ attributes['IsPassive'] = is_passive unless is_passive.nil?
76
+ # TODO implement assertion consumer service index
77
+ # TODO implement assertion consumer service URL
78
+ attributes['ProtocolBinding'] = protocol_binding unless protocol_binding.nil?
79
+ attributes['AttributeConsumingServiceURL'] = attribute_consuming_service_url unless attribute_consuming_service_url.nil?
80
+ attributes['ProviderName'] = provider_name unless provider_name.nil?
81
+ xml.tag!('samlp:AuthnRequest', attributes) {
82
+ xml << subject.to_xml unless subject.nil?
83
+ xml << name_id_policy.to_xml unless name_id_policy.nil?
84
+ xml << conditions.to_xml unless conditions.nil?
85
+ xml << requested_authn_context unless requested_authn_context.nil?
86
+ xml << scoping.to_xml unless scoping.nil?
87
+ }
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,18 @@
1
+ module RSAML #:nodoc:
2
+ module Protocol #:nodoc:
3
+ class IDPEntry
4
+ attr_accessor :provider_id
5
+
6
+ # Initialize the IDP entry
7
+ def initialize(provider_id)
8
+ @provider_id = provider_id
9
+ end
10
+
11
+ # Construct an XML fragment representing the idp list
12
+ def to_xml(xml=Builder::XmlMarkup.new)
13
+ attributes = {'ProviderID' => provider_id}
14
+ xml.tag!('samlp:IDPEntry', attributes)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,28 @@
1
+ module RSAML #:nodoc:
2
+ module Protocol #:nodoc:
3
+ # Specifies the identity providers trusted by the requester to authenticate the presenter.
4
+ class IDPList
5
+ # Initialize the IDP list
6
+ def initialize(*idp_entries)
7
+ @idp_entries = idp_entries
8
+ end
9
+
10
+ # Information about identity providers.
11
+ def idp_entries
12
+ @idp_entries ||= []
13
+ end
14
+
15
+ # Validate the IDPList structure.
16
+ def validate
17
+ raise ValidationError, "At least one IDP entry is required" if idp_entries.empty?
18
+ end
19
+
20
+ # Construct an XML fragment representing the idp list
21
+ def to_xml(xml=Builder::XmlMarkup.new)
22
+ xml.tag!('samlp:IDPList') {
23
+ idp_entries.each { |idp_entry| xml << idp_entry.to_xml }
24
+ }
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,65 @@
1
+ module RSAML #:nodoc:
2
+ module Protocol #:nodoc:
3
+ # Base class for messages. This class should not be instantiated directly, rather the Request and Response
4
+ # classes should be used.
5
+ class Message
6
+
7
+ # An identifier for the message. It is of type xs:ID and MUST follow the requirements specified in Section
8
+ # 1.3.4 of the SAML 2.0 specification for identifier uniqueness.
9
+ attr_accessor :id
10
+
11
+ # The version of this message. The identifier for the version of SAML defined in this specification is "2.0".
12
+ attr_accessor :version
13
+
14
+ # The time instant of issue of the message. The time value must be encoded in UTC.
15
+ attr_accessor :issue_instant
16
+
17
+ # A URI reference indicating the address to which this message has been sent. This is useful to prevent
18
+ # malicious forwarding of messages to unintended recipients, a protection that is required by some
19
+ # protocol bindings. If it is present, the actual recipient MUST check that the URI reference identifies the
20
+ # location at which the message was sent or received. If it does not, the response MUST be discarded. Some
21
+ # protocol bindings may require the use of this attribute.
22
+ attr_accessor :destination
23
+
24
+ # Indicates whether or not (and under what conditions) consent has been obtained from a principal in
25
+ # the sending of this message. If no Consent value is provided, the identifier
26
+ # urn:oasis:names:tc:SAML:2.0:consent:unspecified is in effect.
27
+ attr_accessor :consent
28
+
29
+ # Identifies the entity that generated the message.
30
+ attr_accessor :issuer
31
+
32
+ # An XML Signature that authenticates the requestor or responder and provides message integrity.
33
+ attr_accessor :signature
34
+
35
+ # Initialize the message instance
36
+ def initialize
37
+ @id = UUID.new.generate
38
+ @version = "2.0"
39
+ @issue_instant = Time.now.utc
40
+ end
41
+
42
+ # This extension point contains optional protocol message extension elements that are agreed on
43
+ # between the communicating parties.
44
+ def extensions
45
+ @extionsion ||= []
46
+ end
47
+
48
+ # Validate the request structure
49
+ def validate
50
+ raise ValidationError, "ID is required" if id.nil?
51
+ raise ValidationError, "Version is required" if version.nil?
52
+ raise ValidationError, "Issue instant is required" if issue_instant.nil?
53
+ raise ValidationError, "Issue instant must be UTC" unless issue_instant.utc?
54
+ end
55
+
56
+ protected
57
+ # Add XML Namespace attributes
58
+ def add_xmlns(attributes)
59
+ attributes['xmlns:samlp'] = "urn:oasis:names:tc:SAML:2.0:protocol"
60
+ attributes['xmlns:saml'] = "urn:oasis:names:tc:SAML:2.0:assertion"
61
+ attributes
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,31 @@
1
+ module RSAML #:nodoc:
2
+ module Protocol #:nodoc:
3
+ # Tailors the name identifier in the subjects of assertions resulting from an authentication request.
4
+ class NameIdPolicy
5
+ # Specifies the URI reference corresponding to a name identifier format
6
+ attr_accessor :format
7
+
8
+ # Optionally specifies that the assertion subject's identifier be returned (or created) in the namespace of
9
+ # a service provider other than the requester, or in the namespace of an affiliation group of service
10
+ # providers.
11
+ attr_accessor :sp_name_qualifier
12
+
13
+ # A Boolean value used to indicate whether the identity provider is allowed, in the course of fulfilling the
14
+ # request, to create a new identifier to represent the principal. Defaults to "false". When "false", the
15
+ # requester constrains the identity provider to only issue an assertion to it if an acceptable identifier for
16
+ # the principal has already been established. Note that this does not prevent the identity provider from
17
+ # creating such identifiers outside the context of this specific request (for example, in advance for a
18
+ # large number of principals).
19
+ attr_accessor :allow_create
20
+
21
+ # Construct an XML fragment representing the name id policy
22
+ def to_xml(xml=Builder::XmlMarkup.new)
23
+ attributes = {}
24
+ attributes['Format'] = format unless format.nil?
25
+ attributes['SPNameQualifier'] = sp_name_qualifier unless sp_name_qualifier.nil?
26
+ attributes['AllowCreate'] = allow_create unless allow_create.nil?
27
+ xml.tag!('samlp:NameIDPolicy', attributes)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,12 @@
1
+ module RSAML #:nodoc:
2
+ module Protocol #:nodoc:
3
+ # Module containing SAML query classes.
4
+ module Query
5
+ end
6
+ end
7
+ end
8
+
9
+ require 'rsaml/protocol/query/subject_query'
10
+ require 'rsaml/protocol/query/authn_query'
11
+ require 'rsaml/protocol/query/attribute_query'
12
+ require 'rsaml/protocol/query/authz_decision_query'
@@ -0,0 +1,56 @@
1
+ module RSAML #:nodoc:
2
+ module Protocol #:nodoc:
3
+ module Query #:nodoc:
4
+ # used to make the query "Return the requested attributes for this subject." A successful response
5
+ # will be in the form of assertions containing attribute statements, to the extent allowed by policy.
6
+ class AttributeQuery < SubjectQuery
7
+ # Each attribute element specifies an attribute whose value(s) are to be returned. If no
8
+ # attributes are specified, it indicates that all attributes allowed by policy are requested.
9
+ # If a given attribute element contains one or more AttributeValue elements, then if that attribute
10
+ # is returned in the response, it MUST NOT contain any values that are not equal to the values
11
+ # specified in the query. In the absence of equality rules specified by particular profiles or
12
+ # attributes, equality is defined as an identical XML representation of the value. Each value in
13
+ # the array MUST be an Attribute instance.
14
+ def attributes
15
+ @attributes ||= []
16
+ end
17
+
18
+ # Validate the structure of the attribute query.
19
+ def validate
20
+ matched = {}
21
+ duplicated_attributes = []
22
+ attributes.each do |attribute|
23
+ if matched.has_key?(attribute.name) && matched[attribute.name] == attribute.name_format
24
+ duplicated_attributes << attribute.name unless duplicated_attributes.include?(attribute.name)
25
+ else
26
+ matched[attribute.name] = attribute.name_format
27
+ end
28
+ end
29
+ if !duplicated_attributes.empty?
30
+ raise ValidationError, "An attribute with the same name and name format may only be specified once. The following attributes were specified multiple times: #{duplicated_attributes.join(',')}"
31
+ end
32
+ end
33
+
34
+ # Construct an XML fragment representing the attribute query
35
+ def to_xml(xml=Builder::XmlMarkup.new)
36
+ xml_attributes = {}
37
+ xml.tag!('samlp:AttributeQuery', xml_attributes) {
38
+ xml << subject.to_xml unless subject.nil?
39
+ attributes.each { |attribute| xml << attribute.to_xml }
40
+ }
41
+ end
42
+
43
+ # Construct an AttributeQuery instance from the XML Element.
44
+ def self.from_xml(element)
45
+ element = REXML::Document.new(element).root if element.is_a?(String)
46
+ subject = Subject.from_xml(element.get_elements('saml:Subject').first)
47
+ attribute_query = AttributeQuery.new(subject)
48
+ element.get_elements('saml:Attribute').each do |attribute_element|
49
+ attribute_query.attributes << Attribute.from_xml(attribute_element)
50
+ end
51
+ attribute_query
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end