Class: Saml::Kit::Response
Overview
This class is responsible for validating and parsing a SAML Response document.
require_relative './principal'
RSpec.describe "Response" do
let(:user) { Principal.new(id: SecureRandom.uuid, email: "hello@example.com") }
let(:request) { Saml::Kit::AuthenticationRequest.build }
it 'consumes a Response' do
raw_xml = <<-XML
<?xml version="1.0" encoding="UTF-8"?>
<Response xmlns="urn:oasis:names:tc:SAML:2.0:protocol" ID="_32594448-5d41-4e5b-87c5-ee32ef1f14f7" Version="2.0" IssueInstant="2017-12-23T18:13:58Z" Destination="" Consent="urn:oasis:names:tc:SAML:2.0:consent:unspecified" InResponseTo="_55236abc-636f-41d1-8c0d-81c5384786dd">
<Issuer xmlns="urn:oasis:names:tc:SAML:2.0:assertion">https://www.example.com/metadata</Issuer>
<Status>
<StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
</Status>
<Assertion xmlns="urn:oasis:names:tc:SAML:2.0:assertion" ID="_843f14bc-51e9-40d3-9861-23e59ccc8427" IssueInstant="2017-12-23T18:13:58Z" Version="2.0">
<Issuer>https://www.example.com/metadata</Issuer>
<Subject>
<NameID Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent">ed215a85-597f-4e74-a892-ac83c386190b</NameID>
<SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
<SubjectConfirmationData InResponseTo="_55236abc-636f-41d1-8c0d-81c5384786dd" NotOnOrAfter="2017-12-23T21:13:58Z" Recipient=""/>
</SubjectConfirmation>
</Subject>
<Conditions NotBefore="2017-12-23T18:13:58Z" NotOnOrAfter="2017-12-23T21:13:58Z">
<AudienceRestriction>
<Audience/>
</AudienceRestriction>
</Conditions>
<AuthnStatement AuthnInstant="2017-12-23T18:13:58Z" SessionIndex="_843f14bc-51e9-40d3-9861-23e59ccc8427" SessionNotOnOrAfter="2017-12-23T21:13:58Z">
<AuthnContext>
<AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</AuthnContextClassRef>
</AuthnContext>
</AuthnStatement>
</Assertion>
</Response>
XML
response = Saml::Kit::Response.new(raw_xml)
expect(response.assertion.name_id).to eql('ed215a85-597f-4e74-a892-ac83c386190b')
expect(response.issuer).to eql("https://www.example.com/metadata")
end
it 'builds a Response document' do
response = Saml::Kit::Response.build(user, request) do |builder|
builder.issuer = "blah"
end
expect(response.issuer).to eql("blah")
expect(response.to_xml).to have_xpath("/samlp:Response/saml:Assertion/saml:Issuer[text()=\"blah\"]")
end
it 'generates a SAMLResponse' do
xml = Saml::Kit::Metadata.build_xml do |builder|
builder.contact_email = 'hi@example.com'
builder.organization_name = "Acme, Inc"
builder.organization_url = 'https://www.example.com'
builder.build_identity_provider do |x|
x.add_single_sign_on_service('https://www.example.com/login', binding: :http_post)
x.add_single_sign_on_service('https://www.example.com/login', binding: :http_redirect)
x.add_single_logout_service('https://www.example.com/logout', binding: :http_post)
x.name_id_formats = [ Saml::Kit::Namespaces::EMAIL_ADDRESS ]
x.attributes << :id
x.attributes << :email
end
builder.build_service_provider do |x|
x.add_assertion_consumer_service('https://www.example.com/consume', binding: :http_post)
x.add_single_logout_service('https://www.example.com/logout', binding: :http_post)
end
end
idp = Saml::Kit::IdentityProviderMetadata.new(xml)
url, saml_params = idp.login_request_for(binding: :http_post)
uri = URI.parse("#{url}?#{saml_params.map { |(x, y)| "#{x}=#{y}" }.join('&')}")
sp = Saml::Kit::ServiceProviderMetadata.new(xml)
binding = idp.single_sign_on_service_for(binding: :http_post)
raw_params = Hash[uri.query.split("&").map { |x| x.split("=", 2) }].symbolize_keys
saml_request = binding.deserialize(raw_params)
allow(saml_request).to receive(:provider).and_return(sp)
url, saml_params = saml_request.response_for(user, binding: :http_post)
expect(url).to eql("https://www.example.com/consume")
expect(saml_params['SAMLResponse']).to be_present
end
end
Constant Summary
Constants inherited from Document
Document::CONSTRUCTORS, Document::XPATH
Constants included from XsdValidatable
XsdValidatable::METADATA_XSD, XsdValidatable::PROTOCOL_XSD
Constants included from XmlParseable
Instance Attribute Summary
Attributes included from Respondable
Attributes inherited from Document
Instance Method Summary collapse
- #assertion(private_keys = configuration.private_keys(use: :encryption)) ⇒ Object
-
#initialize(xml, request_id: nil, configuration: Saml::Kit.configuration) ⇒ Response
constructor
A new instance of Response.
Methods included from Respondable
#in_response_to, #status_code, #status_message, #success?
Methods inherited from Document
#destination, #id, #issue_instant, #issuer, to_saml_document, #version
Methods included from XmlParseable
#present?, #to_h, #to_s, #to_xhtml, #to_xml
Methods included from Trustable
Methods included from Validatable
Constructor Details
#initialize(xml, request_id: nil, configuration: Saml::Kit.configuration) ⇒ Response
Returns a new instance of Response.
17 18 19 20 21 22 23 24 |
# File 'lib/saml/kit/response.rb', line 17 def initialize( xml, request_id: nil, configuration: Saml::Kit.configuration ) @request_id = request_id super(xml, name: 'Response', configuration: configuration) end |
Instance Method Details
#assertion(private_keys = configuration.private_keys(use: :encryption)) ⇒ Object
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
# File 'lib/saml/kit/response.rb', line 26 def assertion(private_keys = configuration.private_keys(use: :encryption)) @assertion ||= begin node = assertion_nodes.last if node.nil? Saml::Kit::NullAssertion.new else Saml::Kit::Assertion.new( node, configuration: @configuration, private_keys: private_keys ) end end end |