Class: Samlurai::Response

Inherits:
Object
  • Object
show all
Defined in:
lib/samlurai/response.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(response, settings = nil, validation_options = {}) ⇒ Response

Returns a new instance of Response.



10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# File 'lib/samlurai/response.rb', line 10

def initialize(response, settings=nil, validation_options={})
  @response = response
  @validation_options = validation_options

  begin
    @xml = Base64.decode64(@response)
    @document = LibXML::XML::Document.string(@xml)
  rescue => e
    # could not parse document, everything is invalid
    @response = nil
    return
  end

  @issuer = @document.find_first("/samlp:Response/saml:Issuer", Samlurai::NAMESPACES).content rescue nil
  @status_code = @document.find_first("/samlp:Response/samlp:Status/samlp:StatusCode", Samlurai::NAMESPACES)["Value"] rescue nil

  process(settings) if settings
end

Instance Attribute Details

#decrypted_documentObject (readonly)

Returns the value of attribute decrypted_document.



4
5
6
# File 'lib/samlurai/response.rb', line 4

def decrypted_document
  @decrypted_document
end

#destinationObject (readonly)

Returns the value of attribute destination.



7
8
9
# File 'lib/samlurai/response.rb', line 7

def destination
  @destination
end

#documentObject (readonly)

Returns the value of attribute document.



4
5
6
# File 'lib/samlurai/response.rb', line 4

def document
  @document
end

#in_response_toObject (readonly)

Returns the value of attribute in_response_to.



7
8
9
# File 'lib/samlurai/response.rb', line 7

def in_response_to
  @in_response_to
end

#issuerObject (readonly)

Returns the value of attribute issuer.



7
8
9
# File 'lib/samlurai/response.rb', line 7

def issuer
  @issuer
end

#name_idObject (readonly)

Returns the value of attribute name_id.



5
6
7
# File 'lib/samlurai/response.rb', line 5

def name_id
  @name_id
end

#name_qualifierObject (readonly)

Returns the value of attribute name_qualifier.



5
6
7
# File 'lib/samlurai/response.rb', line 5

def name_qualifier
  @name_qualifier
end

#responseObject (readonly)

Returns the value of attribute response.



4
5
6
# File 'lib/samlurai/response.rb', line 4

def response
  @response
end

#saml_attributesObject (readonly)

Returns the value of attribute saml_attributes.



5
6
7
# File 'lib/samlurai/response.rb', line 5

def saml_attributes
  @saml_attributes
end

#session_indexObject (readonly)

Returns the value of attribute session_index.



5
6
7
# File 'lib/samlurai/response.rb', line 5

def session_index
  @session_index
end

#settingsObject

Returns the value of attribute settings.



3
4
5
# File 'lib/samlurai/response.rb', line 3

def settings
  @settings
end

#status_codeObject (readonly)

Returns the value of attribute status_code.



6
7
8
# File 'lib/samlurai/response.rb', line 6

def status_code
  @status_code
end

#status_messageObject (readonly)

Returns the value of attribute status_message.



6
7
8
# File 'lib/samlurai/response.rb', line 6

def status_message
  @status_message
end

#validation_errorObject (readonly)

Returns the value of attribute validation_error.



8
9
10
# File 'lib/samlurai/response.rb', line 8

def validation_error
  @validation_error
end

#xmlObject (readonly)

Returns the value of attribute xml.



4
5
6
# File 'lib/samlurai/response.rb', line 4

def xml
  @xml
end

Class Method Details

.decrypt(encrypted_document, settings) ⇒ Object

replaces EncryptedData nodes with decrypted copies



119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/samlurai/response.rb', line 119

def self.decrypt(encrypted_document, settings)
  if settings.encryption_configured?
    decrypted_xml = nil
    begin
      decrypted_xml = XMLSecurity.decrypt(encrypted_document.to_s(:indent => false), settings.xmlsec_privatekey)
    rescue Exception => e
      # rescuing all exceptions here; bad. need to create superclass in xmlsecurity
    end
    if decrypted_xml
      return LibXML::XML::Document.string(decrypted_xml)
    end
  end
  encrypted_document
end

.validate(document, options = {}) ⇒ Object



110
111
112
113
114
115
116
# File 'lib/samlurai/response.rb', line 110

def self.validate(document, options={})
  auth_statement = document.find_first('//samlp:Response', Samlurai::NAMESPACES)
  if auth_statement
    options[:as_of] ||= auth_statement["IssueInstant"]
  end
  XMLSecurity.verify_signature(document.to_s(:indent => false), options)
end

Instance Method Details

#auth_failure?Boolean

Returns:

  • (Boolean)


92
93
94
# File 'lib/samlurai/response.rb', line 92

def auth_failure?
  @status_code == Samlurai::StatusCodes::AUTHN_FAILED_URI
end

#fingerprint_from_idpObject



100
101
102
103
104
105
106
107
108
# File 'lib/samlurai/response.rb', line 100

def fingerprint_from_idp
  if base64_cert = @decrypted_document.find_first("//ds:X509Certificate", Samlurai::NAMESPACES)
    cert_text = Base64.decode64(base64_cert.content)
    cert = OpenSSL::X509::Certificate.new(cert_text)
    Digest::SHA1.hexdigest(cert.to_der)
  else
    nil
  end
end

#is_valid?Boolean

Returns:

  • (Boolean)


52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/samlurai/response.rb', line 52

def is_valid?
  if @response.nil? || @response == ""
    @validation_error = "No response to validate"
    return false
  end
  
  if !@settings.idp_cert_fingerprint
    @validation_error = "No fingerprint configured in SAML settings"
    return false
  end
  
  # Verify the original document if it has a signature, otherwise verify the signature
  # in the encrypted portion. If there is no signature, then we can't verify.
  verified = false
  if @document.find_first("//ds:Signature", Samlurai::NAMESPACES)
    verified = self.class.validate(@document, @validation_options.merge(:cert_fingerprint => @settings.idp_cert_fingerprint, :logger => @logger))
    if !verified
      return false
    end
  end

  if !verified && @decrypted_document.find_first("//ds:Signature", Samlurai::NAMESPACES)
    verified = self.class.validate(@decrypted_document, @validation_options.merge(:cert_fingerprint => @settings.idp_cert_fingerprint, :logger => @logger))
    if !verified
      return false
    end
  end
  
  if !verified
    @validation_error = "No signature found in the response"
    return false
  end
  
  true
end

#logger=(val) ⇒ Object



48
49
50
# File 'lib/samlurai/response.rb', line 48

def logger=(val)
  @logger = val
end

#no_authn_context?Boolean

Returns:

  • (Boolean)


96
97
98
# File 'lib/samlurai/response.rb', line 96

def no_authn_context?
  @status_code == Samlurai::StatusCodes::NO_AUTHN_CONTEXT_URI
end

#process(settings) ⇒ Object



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/samlurai/response.rb', line 29

def process(settings)
  @settings = settings
  return unless @response

  @decrypted_document = self.class.decrypt(@document, @settings)

  @in_response_to = @decrypted_document.find_first("/samlp:Response", Samlurai::NAMESPACES)['InResponseTo'] rescue nil
  @destination = @decrypted_document.find_first("/samlp:Response", Samlurai::NAMESPACES)['Destination'] rescue nil
  @name_id = @decrypted_document.find_first("/samlp:Response/saml:Assertion/saml:Subject/saml:NameID", Samlurai::NAMESPACES).content rescue nil
  @saml_attributes = {}
  @decrypted_document.find("//saml:Attribute", Samlurai::NAMESPACES).each do |attr|
    attrname = attr['FriendlyName'] || Samlurai::ATTRIBUTES[attr['Name']] || attr['Name']
    @saml_attributes[attrname] = attr.content.strip rescue nil
  end
  @name_qualifier = @decrypted_document.find_first("/samlp:Response//saml:Assertion/saml:Subject/saml:NameID", Samlurai::NAMESPACES)["NameQualifier"] rescue nil
  @session_index = @decrypted_document.find_first("/samlp:Response//saml:Assertion/saml:AuthnStatement", Samlurai::NAMESPACES)["SessionIndex"] rescue nil
  @status_message = @decrypted_document.find_first("/samlp:Response/samlp:Status/samlp:StatusCode", Samlurai::NAMESPACES).content rescue nil
end

#success_status?Boolean

Returns:

  • (Boolean)


88
89
90
# File 'lib/samlurai/response.rb', line 88

def success_status?
  @status_code == Samlurai::StatusCodes::SUCCESS_URI
end