main
  1# frozen_string_literal: true
  2
  3RSpec.describe Saml::Kit::Bindings::HttpPost do
  4  subject { described_class.new(location: location) }
  5
  6  let(:location) { FFaker::Internet.uri('https') }
  7
  8  specify { expect(subject.binding).to eql(Saml::Kit::Bindings::HTTP_POST) }
  9
 10  describe 'equality' do
 11    it 'is referentially equal' do
 12      expect(subject).to eql(subject)
 13    end
 14
 15    it 'is equal by value' do
 16      expect(subject).to eql(
 17        described_class.new(location: location)
 18      )
 19    end
 20
 21    it 'is not equal' do
 22      expect(subject).not_to eql(
 23        described_class.new(location: FFaker::Internet.uri('https'))
 24      )
 25    end
 26  end
 27
 28  describe '#serialize' do
 29    let(:relay_state) { 'ECHO' }
 30    let(:configuration) do
 31      Saml::Kit::Configuration.new do |config|
 32        config.generate_key_pair_for(use: :signing)
 33      end
 34    end
 35
 36    it 'encodes the request using the HTTP-POST encoding for a AuthenticationRequest' do
 37      builder = Saml::Kit::AuthenticationRequest.builder_class.new(configuration: configuration)
 38      url, saml_params = subject.serialize(builder, relay_state: relay_state)
 39
 40      expect(url).to eql(location)
 41      expect(saml_params['RelayState']).to eql(relay_state)
 42      expect(saml_params['SAMLRequest']).to be_present
 43      xml = Hash.from_xml(Base64.decode64(saml_params['SAMLRequest']))
 44      expect(xml['AuthnRequest']).to be_present
 45      expect(xml['AuthnRequest']['Destination']).to eql(location)
 46      expect(xml['AuthnRequest']['Signature']).to be_present
 47    end
 48
 49    it 'returns a SAMLRequest for a LogoutRequest' do
 50      user = User.new
 51      builder = Saml::Kit::LogoutRequest.builder_class.new(user, configuration: configuration)
 52      url, saml_params = subject.serialize(builder, relay_state: relay_state)
 53
 54      expect(url).to eql(location)
 55      expect(saml_params['RelayState']).to eql(relay_state)
 56      expect(saml_params['SAMLRequest']).to be_present
 57      xml = Hash.from_xml(Base64.decode64(saml_params['SAMLRequest']))
 58      expect(xml['LogoutRequest']).to be_present
 59      expect(xml['LogoutRequest']['Destination']).to eql(location)
 60      expect(xml['LogoutRequest']['Signature']).to be_present
 61    end
 62
 63    it 'returns a SAMLResponse for a LogoutResponse' do
 64      request = instance_double(Saml::Kit::AuthenticationRequest, id: SecureRandom.uuid)
 65      builder = Saml::Kit::LogoutResponse.builder_class.new(request, configuration: configuration)
 66      url, saml_params = subject.serialize(builder, relay_state: relay_state)
 67
 68      expect(url).to eql(location)
 69      expect(saml_params['RelayState']).to eql(relay_state)
 70      expect(saml_params['SAMLResponse']).to be_present
 71      xml = Hash.from_xml(Base64.decode64(saml_params['SAMLResponse']))
 72      expect(xml['LogoutResponse']).to be_present
 73      expect(xml['LogoutResponse']['Destination']).to eql(location)
 74      expect(xml['LogoutResponse']['Signature']).to be_present
 75    end
 76
 77    it 'excludes the RelayState when blank' do
 78      builder = Saml::Kit::AuthenticationRequest.builder_class.new
 79      url, saml_params = subject.serialize(builder)
 80
 81      expect(url).to eql(location)
 82      expect(saml_params.keys).not_to include('RelayState')
 83    end
 84  end
 85
 86  describe '#deserialize' do
 87    it 'deserializes to an AuthnRequest' do
 88      builder = Saml::Kit::AuthenticationRequest.builder_class.new
 89      _, params = subject.serialize(builder)
 90      result = subject.deserialize(params)
 91      expect(result).to be_instance_of(Saml::Kit::AuthenticationRequest)
 92    end
 93
 94    it 'deserializes to a LogoutRequest' do
 95      user = User.new
 96      builder = Saml::Kit::LogoutRequest.builder_class.new(user)
 97      _, params = subject.serialize(builder)
 98      result = subject.deserialize(params)
 99      expect(result).to be_instance_of(Saml::Kit::LogoutRequest)
100    end
101
102    it 'deserializes to a Response' do
103      user = User.new
104      request = instance_double(Saml::Kit::AuthenticationRequest, id: SecureRandom.uuid, provider: nil, assertion_consumer_service_url: FFaker::Internet.http_url, name_id_format: Saml::Kit::Namespaces::PERSISTENT, issuer: FFaker::Internet.http_url, signed?: true, trusted?: true)
105      builder = Saml::Kit::Response.builder_class.new(user, request)
106      _, params = subject.serialize(builder)
107      result = subject.deserialize(params)
108      expect(result).to be_instance_of(Saml::Kit::Response)
109    end
110
111    it 'raises an error when SAMLRequest and SAMLResponse are missing' do
112      expect do
113        subject.deserialize({})
114      end.to raise_error(/SAMLRequest or SAMLResponse parameter is required/)
115    end
116
117    it 'can deserialize a request encrypted with unknown keys' do
118      saml_params = { 'SAMLResponse' => IO.read('spec/fixtures/1555534792.3954718-9d1c5e47e1b1abc70e9774d3.saml_response') }
119      result = subject.deserialize(saml_params)
120      expect(result).to be_present
121      expect(result.assertion).to be_present
122      expect(result.assertion).not_to be_decryptable
123    end
124  end
125end