11# frozen_string_literal: true
22
3- require "rexml/document"
4-
53require "ruby_saml/logging"
64require "ruby_saml/saml_message"
75require "ruby_saml/utils"
86require "ruby_saml/setting_error"
97
10- # Only supports SAML 2.0
118module RubySaml
12-
139 # SAML2 Authentication. AuthNRequest (SSO SP initiated, Builder)
14- #
1510 class Authrequest < SamlMessage
1611
1712 # AuthNRequest ID
@@ -22,7 +17,6 @@ class Authrequest < SamlMessage
2217 # @param settings [RubySaml::Settings|nil] Toolkit settings
2318 # @param params [Hash] Some extra parameters to be added in the GET for example the RelayState
2419 # @return [String] AuthNRequest string that includes the SAMLRequest
25- #
2620 def create ( settings , params = { } )
2721 assign_uuid ( settings )
2822 params = create_params ( settings , params )
@@ -32,15 +26,14 @@ def create(settings, params = {})
3226 params . each_pair do |key , value |
3327 request_params << "&#{ key } =#{ CGI . escape ( value . to_s ) } "
3428 end
35- raise SettingError . new "Invalid settings, idp_sso_service_url is not set!" if settings . idp_sso_service_url . nil? or settings . idp_sso_service_url . empty?
29+ raise SettingError . new "Invalid settings, idp_sso_service_url is not set!" if settings . idp_sso_service_url . nil? || settings . idp_sso_service_url . empty?
3630 @login_url = settings . idp_sso_service_url + request_params
3731 end
3832
3933 # Creates the Get parameters for the request.
4034 # @param settings [RubySaml::Settings|nil] Toolkit settings
4135 # @param params [Hash] Some extra parameters to be added in the GET for example the RelayState
4236 # @return [Hash] Parameters
43- #
4437 def create_params ( settings , params = { } )
4538 # The method expects :RelayState but sometimes we get 'RelayState' instead.
4639 # Based on the HashWithIndifferentAccess value in Rails we could experience
@@ -54,10 +47,7 @@ def create_params(settings, params={})
5447 end
5548
5649 request_doc = create_authentication_xml_doc ( settings )
57- request_doc . context [ :attribute_quote ] = :quote
58-
59- request = +""
60- request_doc . write ( request )
50+ request = request_doc . to_xml ( save_with : Nokogiri ::XML ::Node ::SaveOptions ::AS_XML )
6151
6252 Logging . debug "Created AuthnRequest: #{ request } "
6353
@@ -89,97 +79,75 @@ def create_params(settings, params={})
8979 # Creates the SAMLRequest String.
9080 # @param settings [RubySaml::Settings|nil] Toolkit settings
9181 # @return [String] The SAMLRequest String.
92- #
9382 def create_authentication_xml_doc ( settings )
94- document = create_xml_document ( settings )
95- sign_document ( document , settings )
83+ noko = create_xml_document ( settings )
84+ sign_document ( noko , settings )
9685 end
9786
9887 def create_xml_document ( settings )
9988 time = Time . now . utc . strftime ( "%Y-%m-%dT%H:%M:%SZ" )
10089 assign_uuid ( settings )
90+ root_attributes = {
91+ 'xmlns:samlp' => 'urn:oasis:names:tc:SAML:2.0:protocol' ,
92+ 'xmlns:saml' => 'urn:oasis:names:tc:SAML:2.0:assertion' ,
93+ 'ID' => uuid ,
94+ 'IssueInstant' => time ,
95+ 'Version' => '2.0' ,
96+ 'Destination' => settings . idp_sso_service_url ,
97+ 'IsPassive' => settings . passive ,
98+ 'ProtocolBinding' => settings . protocol_binding ,
99+ 'AttributeConsumingServiceIndex' => settings . attributes_index ,
100+ 'ForceAuthn' => settings . force_authn ,
101+ 'AssertionConsumerServiceURL' => settings . assertion_consumer_service_url
102+ } . compact . reject { |_ , v | v . respond_to? ( :empty? ) && v . empty? }
103+
104+ builder = Nokogiri ::XML ::Builder . new do |xml |
105+ xml [ 'samlp' ] . AuthnRequest ( root_attributes ) do
106+ # Add Issuer element if sp_entity_id is present
107+ xml [ 'saml' ] . Issuer ( settings . sp_entity_id ) if settings . sp_entity_id
108+
109+ # Add Subject element if name_identifier_value_requested is present
110+ if settings . name_identifier_value_requested
111+ xml [ 'saml' ] . Subject do
112+ nameid_attrs = { }
113+ nameid_attrs [ 'Format' ] = settings . name_identifier_format if settings . name_identifier_format
114+ xml [ 'saml' ] . NameID ( settings . name_identifier_value_requested , nameid_attrs )
115+ xml [ 'saml' ] . SubjectConfirmation ( Method : 'urn:oasis:names:tc:SAML:2.0:cm:bearer' )
116+ end
117+ end
101118
102- request_doc = RubySaml ::XML ::Document . new
103- request_doc . context [ :attribute_quote ] = :quote
104-
105- root = request_doc . add_element "samlp:AuthnRequest" , { "xmlns:samlp" => "urn:oasis:names:tc:SAML:2.0:protocol" , "xmlns:saml" => "urn:oasis:names:tc:SAML:2.0:assertion" }
106- root . attributes [ 'ID' ] = uuid
107- root . attributes [ 'IssueInstant' ] = time
108- root . attributes [ 'Version' ] = "2.0"
109- root . attributes [ 'Destination' ] = settings . idp_sso_service_url unless settings . idp_sso_service_url . nil? or settings . idp_sso_service_url . empty?
110- root . attributes [ 'IsPassive' ] = settings . passive unless settings . passive . nil?
111- root . attributes [ 'ProtocolBinding' ] = settings . protocol_binding unless settings . protocol_binding . nil?
112- root . attributes [ "AttributeConsumingServiceIndex" ] = settings . attributes_index unless settings . attributes_index . nil?
113- root . attributes [ 'ForceAuthn' ] = settings . force_authn unless settings . force_authn . nil?
114-
115- # Conditionally defined elements based on settings
116- unless settings . assertion_consumer_service_url . nil?
117- root . attributes [ "AssertionConsumerServiceURL" ] = settings . assertion_consumer_service_url
118- end
119-
120- unless settings . sp_entity_id . nil?
121- issuer = root . add_element "saml:Issuer"
122- issuer . text = settings . sp_entity_id
123- end
124-
125- unless settings . name_identifier_value_requested . nil?
126- subject = root . add_element "saml:Subject"
127-
128- nameid = subject . add_element "saml:NameID"
129- nameid . attributes [ 'Format' ] = settings . name_identifier_format if settings . name_identifier_format
130- nameid . text = settings . name_identifier_value_requested
131-
132- subject_confirmation = subject . add_element "saml:SubjectConfirmation"
133- subject_confirmation . attributes [ 'Method' ] = "urn:oasis:names:tc:SAML:2.0:cm:bearer"
134- end
135-
136- unless settings . name_identifier_format . nil?
137- root . add_element "samlp:NameIDPolicy" , {
138- # Might want to make AllowCreate a setting?
139- "AllowCreate" => "true" ,
140- "Format" => settings . name_identifier_format
141- }
142- end
143-
144- if settings . authn_context || settings . authn_context_decl_ref
145-
146- if settings . authn_context_comparison . nil?
147- comparison = 'exact'
148- else
149- comparison = settings . authn_context_comparison
150- end
119+ # Add NameIDPolicy element if name_identifier_format is present
120+ if settings . name_identifier_format
121+ xml [ 'samlp' ] . NameIDPolicy ( AllowCreate : 'true' , Format : settings . name_identifier_format )
122+ end
151123
152- requested_context = root . add_element "samlp: RequestedAuthnContext" , {
153- "Comparison" => comparison
154- }
124+ # Add RequestedAuthnContext if authn_context or authn_context_decl_ref is present
125+ if settings . authn_context || settings . authn_context_decl_ref
126+ comparison = settings . authn_context_comparison || 'exact'
155127
156- unless settings . authn_context . nil?
157- authn_contexts_class_ref = settings . authn_context . is_a? ( Array ) ? settings . authn_context : [ settings . authn_context ]
158- authn_contexts_class_ref . each do |authn_context_class_ref |
159- class_ref = requested_context . add_element "saml:AuthnContextClassRef"
160- class_ref . text = authn_context_class_ref
161- end
162- end
128+ xml [ 'samlp' ] . RequestedAuthnContext ( Comparison : comparison ) do
129+ Array ( settings . authn_context ) . each do |authn_context_class_ref |
130+ xml [ 'saml' ] . AuthnContextClassRef ( authn_context_class_ref )
131+ end
163132
164- unless settings . authn_context_decl_ref . nil?
165- authn_contexts_decl_refs = settings . authn_context_decl_ref . is_a? ( Array ) ? settings . authn_context_decl_ref : [ settings . authn_context_decl_ref ]
166- authn_contexts_decl_refs . each do |authn_context_decl_ref |
167- decl_ref = requested_context . add_element "saml:AuthnContextDeclRef"
168- decl_ref . text = authn_context_decl_ref
133+ Array ( settings . authn_context_decl_ref ) . each do |authn_context_decl_ref |
134+ xml [ 'saml' ] . AuthnContextDeclRef ( authn_context_decl_ref )
135+ end
136+ end
169137 end
170138 end
171139 end
172140
173- request_doc
141+ builder . doc
174142 end
175143
176- def sign_document ( document , settings )
144+ def sign_document ( noko , settings )
177145 cert , private_key = settings . get_sp_signing_pair
178146 if settings . idp_sso_service_binding == Utils ::BINDINGS [ :post ] && settings . security [ :authn_requests_signed ] && private_key && cert
179- document . sign_document ( private_key , cert , settings . get_sp_signature_method , settings . get_sp_digest_method )
147+ RubySaml ::XML ::DocumentSigner . sign_document! ( noko , private_key , cert , settings . get_sp_signature_method , settings . get_sp_digest_method )
148+ else
149+ noko
180150 end
181-
182- document
183151 end
184152
185153 def assign_uuid ( settings )
0 commit comments