Skip to content

Commit 20f9706

Browse files
committed
Refactor error handling; allow multiple error messages with soft
1 parent 3736861 commit 20f9706

File tree

6 files changed

+92
-112
lines changed

6 files changed

+92
-112
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
require "onelogin/ruby-saml/validation_error"
2+
3+
module OneLogin
4+
module RubySaml
5+
module ErrorHandling
6+
attr_accessor :errors
7+
8+
# Append the cause to the errors array, and based on the value of soft, return false or raise
9+
# an exception. soft_override is provided as a means of overriding the object's notion of
10+
# soft for just this invocation.
11+
def append_error(error_msg, soft_override = nil)
12+
@errors << error_msg
13+
14+
unless soft_override.nil? ? soft : soft_override
15+
raise ValidationError.new(error_msg)
16+
end
17+
18+
false
19+
end
20+
21+
# Reset the errors array
22+
def reset_errors!
23+
@errors = []
24+
end
25+
end
26+
end
27+
end

lib/onelogin/ruby-saml/logoutresponse.rb

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
require "xml_security"
22
require "onelogin/ruby-saml/saml_message"
3+
require "onelogin/ruby-saml/error_handling"
34

45
require "time"
56

@@ -10,13 +11,11 @@ module RubySaml
1011
# SAML2 Logout Response (SLO IdP initiated, Parser)
1112
#
1213
class Logoutresponse < SamlMessage
14+
include ErrorHandling
1315

1416
# OneLogin::RubySaml::Settings Toolkit settings
1517
attr_accessor :settings
1618

17-
# Array with the causes
18-
attr_accessor :errors
19-
2019
attr_reader :document
2120
attr_reader :response
2221
attr_reader :options
@@ -47,18 +46,6 @@ def initialize(response, settings = nil, options = {})
4746
@document = XMLSecurity::SignedDocument.new(@response)
4847
end
4948

50-
# Append the cause to the errors array, and based on the value of soft, return false or raise
51-
# an exception
52-
def append_error(error_msg)
53-
@errors << error_msg
54-
return soft ? false : validation_error(error_msg)
55-
end
56-
57-
# Reset the errors array
58-
def reset_errors!
59-
@errors = []
60-
end
61-
6249
# Checks if the Status has the "Success" code
6350
# @return [Boolean] True if the StatusCode is Sucess
6451
# @raise [ValidationError] if soft == false and validation fails
@@ -123,12 +110,14 @@ def status_message
123110
def validate
124111
reset_errors!
125112

126-
valid_state? &&
127-
validate_success_status &&
128-
validate_structure &&
129-
valid_in_response_to? &&
130-
valid_issuer? &&
113+
valid_state?
114+
validate_success_status
115+
validate_structure
116+
valid_in_response_to?
117+
valid_issuer?
131118
validate_signature
119+
120+
@errors.empty?
132121
end
133122

134123
private

lib/onelogin/ruby-saml/response.rb

Lines changed: 27 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ module RubySaml
1111
# SAML2 Authentication Response. SAML Response
1212
#
1313
class Response < SamlMessage
14+
include ErrorHandling
15+
1416
ASSERTION = "urn:oasis:names:tc:SAML:2.0:assertion"
1517
PROTOCOL = "urn:oasis:names:tc:SAML:2.0:protocol"
1618
DSIG = "http://www.w3.org/2000/09/xmldsig#"
@@ -21,9 +23,6 @@ class Response < SamlMessage
2123
# OneLogin::RubySaml::Settings Toolkit settings
2224
attr_accessor :settings
2325

24-
# Array with the causes [Array of strings]
25-
attr_accessor :errors
26-
2726
attr_reader :document
2827
attr_reader :decrypted_document
2928
attr_reader :response
@@ -39,16 +38,15 @@ class Response < SamlMessage
3938
# or :matches_request_id that will validate that the response matches the ID of the request,
4039
# or skip the subject confirmation validation with the :skip_subject_confirmation option
4140
def initialize(response, options = {})
42-
@errors = []
43-
4441
raise ArgumentError.new("Response cannot be nil") if response.nil?
45-
@options = options
4642

43+
@errors = []
44+
@options = options
4745
@soft = true
48-
if !options.empty? && !options[:settings].nil?
46+
unless options[:settings].nil?
4947
@settings = options[:settings]
50-
if !options[:settings].soft.nil?
51-
@soft = options[:settings].soft
48+
unless @settings.soft.nil?
49+
@soft = @settings.soft
5250
end
5351
end
5452

@@ -60,18 +58,6 @@ def initialize(response, options = {})
6058
end
6159
end
6260

63-
# Append the cause to the errors array, and based on the value of soft, return false or raise
64-
# an exception
65-
def append_error(error_msg)
66-
@errors << error_msg
67-
return soft ? false : validation_error(error_msg)
68-
end
69-
70-
# Reset the errors array
71-
def reset_errors!
72-
@errors = []
73-
end
74-
7561
# Validates the SAML Response with the default values (soft = true)
7662
# @return [Boolean] TRUE if the SAML Response is valid
7763
#
@@ -284,21 +270,23 @@ def allowed_clock_drift
284270
def validate
285271
reset_errors!
286272

287-
validate_response_state &&
288-
validate_version &&
289-
validate_id &&
290-
validate_success_status &&
291-
validate_num_assertion &&
292-
validate_no_encrypted_attributes &&
293-
validate_signed_elements &&
294-
validate_structure &&
295-
validate_in_response_to &&
296-
validate_conditions &&
297-
validate_audience &&
298-
validate_issuer &&
299-
validate_session_expiration &&
300-
validate_subject_confirmation &&
273+
return false unless validate_response_state
274+
validate_version
275+
validate_id
276+
validate_success_status
277+
validate_num_assertion
278+
validate_no_encrypted_attributes
279+
validate_signed_elements
280+
validate_structure
281+
validate_in_response_to
282+
validate_conditions
283+
validate_audience
284+
validate_issuer
285+
validate_session_expiration
286+
validate_subject_confirmation
301287
validate_signature
288+
289+
@errors.empty?
302290
end
303291

304292

@@ -585,9 +573,8 @@ def validate_signature
585573
)
586574
doc = (response_signed || decrypted_document.nil?) ? document : decrypted_document
587575

588-
unless fingerprint && doc.validate_document(fingerprint, :fingerprint_alg => settings.idp_cert_fingerprint_algorithm)
589-
error_msg = "Invalid Signature on SAML Response"
590-
return append_error(error_msg)
576+
unless fingerprint && doc.validate_document(fingerprint, true, :fingerprint_alg => settings.idp_cert_fingerprint_algorithm)
577+
return append_error("Invalid Signature on SAML Response")
591578
end
592579

593580
true
@@ -641,7 +628,7 @@ def xpath_from_signed_assertion(subelt=nil)
641628
#
642629
def generate_decrypted_document
643630
if settings.nil? || !settings.get_sp_key
644-
validation_error('An EncryptedAssertion found and no SP private key found on the settings to decrypt it. Be sure you provided the :settings parameter at the initialize method')
631+
raise ValidationError.new('An EncryptedAssertion found and no SP private key found on the settings to decrypt it. Be sure you provided the :settings parameter at the initialize method')
645632
end
646633

647634
# Marshal at Ruby 1.8.7 throw an Exception
@@ -707,7 +694,7 @@ def decrypt_nameid(encryptedid_node)
707694
#
708695
def decrypt_element(encrypt_node, rgrex)
709696
if settings.nil? || !settings.get_sp_key
710-
return validation_error('An ' + encrypt_node.name + ' found and no SP private key found on the settings to decrypt it')
697+
raise ValidationError.new('An ' + encrypt_node.name + ' found and no SP private key found on the settings to decrypt it')
711698
end
712699

713700
elem_plaintext = OneLogin::RubySaml::Utils.decrypt_data(encrypt_node, settings.get_sp_key)

lib/onelogin/ruby-saml/saml_message.rb

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -69,23 +69,15 @@ def valid_saml?(document, soft = true)
6969
end
7070
rescue Exception => error
7171
return false if soft
72-
validation_error("XML load failed: #{error.message}")
72+
raise ValidationError.new("XML load failed: #{error.message}")
7373
end
7474

7575
SamlMessage.schema.validate(xml).map do |error|
7676
return false if soft
77-
validation_error("#{error.message}\n\n#{xml.to_s}")
77+
raise ValidationError.new("#{error.message}\n\n#{xml.to_s}")
7878
end
7979
end
8080

81-
# Raise a ValidationError with the provided message
82-
# @param message [String] Message of the exception
83-
# @raise [ValidationError]
84-
#
85-
def validation_error(message)
86-
raise ValidationError.new(message)
87-
end
88-
8981
private
9082

9183
# Base64 decode and try also to inflate a SAML Message

lib/onelogin/ruby-saml/slo_logoutrequest.rb

Lines changed: 16 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,11 @@ module RubySaml
1111
# SAML2 Logout Request (SLO IdP initiated, Parser)
1212
#
1313
class SloLogoutrequest < SamlMessage
14+
include ErrorHandling
1415

1516
# OneLogin::RubySaml::Settings Toolkit settings
1617
attr_accessor :settings
1718

18-
# Array with the causes [Array of strings]
19-
attr_accessor :errors
20-
2119
attr_reader :document
2220
attr_reader :request
2321
attr_reader :options
@@ -32,34 +30,22 @@ class SloLogoutrequest < SamlMessage
3230
# @raise [ArgumentError] If Request is nil
3331
#
3432
def initialize(request, options = {})
35-
@errors = []
3633
raise ArgumentError.new("Request cannot be nil") if request.nil?
37-
@options = options
3834

35+
@errors = []
36+
@options = options
3937
@soft = true
40-
if !options.empty? && !options[:settings].nil?
38+
unless options[:settings].nil?
4139
@settings = options[:settings]
42-
if !options[:settings].soft.nil?
43-
@soft = options[:settings].soft
40+
unless @settings.soft.nil?
41+
@soft = @settings.soft
4442
end
4543
end
4644

4745
@request = decode_raw_saml(request)
4846
@document = REXML::Document.new(@request)
4947
end
5048

51-
# Append the cause to the errors array, and based on the value of soft, return false or raise
52-
# an exception
53-
def append_error(error_msg)
54-
@errors << error_msg
55-
return soft ? false : validation_error(error_msg)
56-
end
57-
58-
# Reset the errors array
59-
def reset_errors!
60-
@errors = []
61-
end
62-
6349
# Validates the Logout Request with the default values (soft = true)
6450
# @return [Boolean] TRUE if the Logout Request is valid
6551
#
@@ -138,13 +124,15 @@ def session_indexes
138124
def validate
139125
reset_errors!
140126

141-
validate_request_state &&
142-
validate_id &&
143-
validate_version &&
144-
validate_structure &&
145-
validate_not_on_or_after &&
146-
validate_issuer &&
127+
validate_request_state
128+
validate_id
129+
validate_version
130+
validate_structure
131+
validate_not_on_or_after
132+
validate_issuer
147133
validate_signature
134+
135+
@errors.empty?
148136
end
149137

150138
# Validates that the Logout Request contains an ID
@@ -213,7 +201,7 @@ def validate_request_state
213201
# @raise [ValidationError] if soft == false and validation fails
214202
#
215203
def validate_issuer
216-
return true if settings.idp_entity_id.nil? || issuer.nil?
204+
return true if settings.nil? || settings.idp_entity_id.nil? || issuer.nil?
217205

218206
unless URI.parse(issuer) == URI.parse(settings.idp_entity_id)
219207
return append_error("Doesn't match the issuer, expected: <#{settings.idp_entity_id}>, but was: <#{issuer}>")
@@ -230,7 +218,7 @@ def validate_signature
230218
return true if options.nil?
231219
return true unless options.has_key? :get_params
232220
return true unless options[:get_params].has_key? 'Signature'
233-
return true if settings.nil? || settings.get_idp_cert.nil?
221+
return true if settings.get_idp_cert.nil?
234222

235223
query_string = OneLogin::RubySaml::Utils.build_query(
236224
:type => 'SAMLRequest',

0 commit comments

Comments
 (0)