Commit fadbb7a
Changed files (7)
lib
saml
spec
lib/saml/kit/binding.rb
@@ -32,6 +32,15 @@ module Saml
end
end
+ def deserialize(params)
+ if params['SAMLRequest'].present?
+ Saml::Kit::Request.deserialize(CGI.unescape(params['SAMLRequest']))
+ elsif params['SAMLResponse'].present?
+ Saml::Kit::Response.deserialize(CGI.unescape(params['SAMLResponse']))
+ else
+ end
+ end
+
def http_redirect?
binding == Namespaces::HTTP_REDIRECT
end
lib/saml/kit/invalid_request.rb
@@ -1,6 +1,6 @@
module Saml
module Kit
- class InvalidRequest
+ class InvalidDocument
include ActiveModel::Validations
include XsdValidatable
attr_reader :raw, :name
@@ -9,14 +9,26 @@ module Saml
model.errors[:base] << model.error_message(:invalid)
end
- def initialize(raw)
+ def initialize(raw, name)
@raw = raw
- @name = "InvalidRequest"
end
def to_xml
raw
end
+
+ end
+
+ class InvalidRequest < InvalidDocument
+ def initialize(raw)
+ super raw, "InvalidRequest"
+ end
+ end
+
+ class InvalidResponse < InvalidDocument
+ def initialize(raw)
+ super raw, "InvalidResponse"
+ end
end
end
end
lib/saml/kit/response.rb
@@ -122,7 +122,17 @@ module Saml
class << self
def deserialize(saml_response)
- new(Saml::Kit::Content.deserialize(saml_response))
+ xml = Saml::Kit::Content.deserialize(saml_response)
+ hash = Hash.from_xml(xml)
+ if hash['Response'].present?
+ new(xml)
+ else
+ LogoutResponse.new(xml)
+ end
+ rescue => error
+ Saml::Kit.logger.error(error)
+ Saml::Kit.logger.error(error.backtrace.join("\n"))
+ InvalidResponse.new(saml_response)
end
end
@@ -209,7 +219,7 @@ module Saml
attr_reader :user, :request
attr_accessor :id, :reference_id, :now
attr_accessor :version, :status_code
- attr_accessor :issuer
+ attr_accessor :issuer, :sign, :destination
def initialize(user, request)
@user = user
@@ -220,6 +230,8 @@ module Saml
@version = "2.0"
@status_code = Namespaces::SUCCESS
@issuer = configuration.issuer
+ @destination = request.acs_url
+ @sign = want_assertions_signed
end
def want_assertions_signed
@@ -230,7 +242,7 @@ module Saml
end
def to_xml
- Signature.sign(id, sign: want_assertions_signed) do |xml, signature|
+ Signature.sign(id, sign: sign) do |xml, signature|
xml.Response response_options do
xml.Issuer(issuer, xmlns: Namespaces::ASSERTION)
signature.template(xml)
@@ -285,7 +297,7 @@ module Saml
ID: id.present? ? "_#{id}" : nil,
Version: version,
IssueInstant: now.iso8601,
- Destination: request.acs_url,
+ Destination: destination,
Consent: Namespaces::UNSPECIFIED,
InResponseTo: request.id,
xmlns: Namespaces::PROTOCOL,
spec/saml/binding_spec.rb
@@ -1,9 +1,10 @@
require 'spec_helper'
RSpec.describe Saml::Kit::Binding do
+ let(:location) { FFaker::Internet.http_url }
+
describe "#serialize" do
let(:relay_state) { "ECHO" }
- let(:location) { FFaker::Internet.http_url }
describe "HTTP-REDIRECT BINDING" do
let(:subject) { Saml::Kit::Binding.new(binding: Saml::Kit::Namespaces::HTTP_REDIRECT, location: location) }
@@ -77,4 +78,49 @@ RSpec.describe Saml::Kit::Binding do
expect(subject.serialize(Saml::Kit::AuthenticationRequest)).to be_empty
end
end
+
+ describe "#deserialize" do
+ describe "HTTP-Redirect binding" do
+ let(:subject) { Saml::Kit::Binding.new(binding: Saml::Kit::Namespaces::HTTP_REDIRECT, location: location) }
+
+ it 'deserializes the SAMLRequest to an AuthnRequest' do
+ url, _ = subject.serialize(Saml::Kit::AuthenticationRequest::Builder.new)
+ result = subject.deserialize(query_params_from(url))
+ expect(result).to be_instance_of(Saml::Kit::AuthenticationRequest)
+ end
+
+ it 'deserializes the SAMLRequest to a LogoutRequest' do
+ user = double(:user, name_id_for: SecureRandom.uuid)
+ url, _ = subject.serialize(Saml::Kit::LogoutRequest::Builder.new(user))
+ result = subject.deserialize(query_params_from(url))
+ expect(result).to be_instance_of(Saml::Kit::LogoutRequest)
+ end
+
+ it 'returns an invalid request when the SAMLRequest is invalid' do
+ result = subject.deserialize({ 'SAMLRequest' => "nonsense" })
+ expect(result).to be_instance_of(Saml::Kit::InvalidRequest)
+ end
+
+ it 'deserializes the SAMLResponse to a Response' do
+ user = double(:user, name_id_for: SecureRandom.uuid, assertion_attributes_for: [])
+ request = double(:request, id: SecureRandom.uuid, provider: nil, acs_url: FFaker::Internet.http_url, name_id_format: Saml::Kit::Namespaces::PERSISTENT, issuer: FFaker::Internet.http_url)
+ url, _ = subject.serialize(Saml::Kit::Response::Builder.new(user, request))
+ result = subject.deserialize(query_params_from(url))
+ expect(result).to be_instance_of(Saml::Kit::Response)
+ end
+
+ it 'deserializes the SAMLResponse to a LogoutResponse' do
+ user = double(:user, name_id_for: SecureRandom.uuid, assertion_attributes_for: [])
+ request = double(:request, id: SecureRandom.uuid, provider: nil, acs_url: FFaker::Internet.http_url, name_id_format: Saml::Kit::Namespaces::PERSISTENT, issuer: FFaker::Internet.http_url)
+ url, _ = subject.serialize(Saml::Kit::LogoutResponse::Builder.new(user, request))
+ result = subject.deserialize(query_params_from(url))
+ expect(result).to be_instance_of(Saml::Kit::LogoutResponse)
+ end
+
+ it 'returns an invalid response when the SAMLResponse is invalid' do
+ result = subject.deserialize({ 'SAMLResponse' => "nonsense" })
+ expect(result).to be_instance_of(Saml::Kit::InvalidResponse)
+ end
+ end
+ end
end
spec/support/matchers/have_query_param.rb
@@ -1,13 +1,5 @@
RSpec::Matchers.define :have_query_param do |key|
match do |url|
- query_params(url)['SAMLRequest'].present?
- end
-
- def query_params(url)
- Hash[uri_for(url).query.split("&").map { |x| x.split('=', 2) }]
- end
-
- def uri_for(url)
- URI.parse(url)
+ query_params_from(url)['SAMLRequest'].present?
end
end
spec/support/test_helpers.rb
@@ -0,0 +1,9 @@
+module TestHelpers
+ def query_params_from(url)
+ Hash[uri_for(url).query.split("&").map { |x| x.split('=', 2) }]
+ end
+
+ def uri_for(url)
+ URI.parse(url)
+ end
+end
spec/spec_helper.rb
@@ -9,6 +9,7 @@ Saml::Kit.configuration.logger.level = :fatal
Dir[File.join(Dir.pwd, 'spec/support/**/*.rb')].each { |f| require f }
RSpec.configure do |config|
config.include ActiveSupport::Testing::TimeHelpers
+ config.include TestHelpers
# Enable flags like --only-failures and --next-failure
config.example_status_persistence_file_path = ".rspec_status"