main
1# frozen_string_literal: true
2
3RSpec.describe Saml::Kit::LogoutRequest do
4 subject { described_class.build(user, configuration: configuration) }
5
6 let(:user) { User.new(name_id: name_id) }
7 let(:name_id) { SecureRandom.uuid }
8 let(:entity_id) { FFaker::Internet.uri('https') }
9 let(:registry) { instance_double(Saml::Kit::DefaultRegistry) }
10 let(:configuration) do
11 Saml::Kit::Configuration.new do |config|
12 config.entity_id = entity_id
13 config.registry = registry
14 config.generate_key_pair_for(use: :signing)
15 end
16 end
17
18 it 'parses the issuer' do
19 subject = described_class.build(user, configuration: configuration) do |builder|
20 builder.issuer = entity_id
21 end
22 expect(subject.issuer).to eql(entity_id)
23 end
24
25 it 'parses the issue instant' do
26 travel_to 1.second.from_now
27 expect(subject.issue_instant).to eql(Time.now.utc)
28 end
29
30 specify { expect(subject.version).to eql('2.0') }
31
32 it 'parses the destination' do
33 destination = FFaker::Internet.uri('https')
34 subject = described_class.build(user, configuration: configuration) do |builder|
35 builder.destination = destination
36 end
37 expect(subject.destination).to eql(destination)
38 end
39
40 specify { expect(subject.name_id).to eql(name_id) }
41 specify { expect(subject.name_id_format).to eql(Saml::Kit::Namespaces::PERSISTENT) }
42
43 describe '#valid?' do
44 let(:metadata) do
45 Saml::Kit::ServiceProviderMetadata.build(configuration: configuration) do |builder|
46 builder.entity_id = entity_id
47 builder.add_single_logout_service(FFaker::Internet.uri('https'), binding: :http_post)
48 end
49 end
50
51 before do
52 allow(registry).to receive(:metadata_for).and_return(metadata)
53 end
54
55 it 'is valid when left untampered' do
56 expect(subject).to be_valid
57 end
58
59 it 'is invalid if the document has been tampered with' do
60 issuer = FFaker::Internet.uri('https')
61 raw_xml = described_class.build(user, configuration: configuration) do |builder|
62 builder.issuer = issuer
63 end.to_xml.gsub(issuer, 'corrupt')
64
65 expect(described_class.new(raw_xml)).to be_invalid
66 end
67
68 it 'is invalid when blank' do
69 subject = described_class.new('')
70 expect(subject).to be_invalid
71 expect(subject.errors[:content]).to be_present
72 end
73
74 it 'is invalid when not a LogoutRequest' do
75 subject = described_class.new(Saml::Kit::IdentityProviderMetadata.build.to_xml)
76 expect(subject).to be_invalid
77 expect(subject.errors[:base]).to include(subject.error_message(:invalid))
78 end
79
80 it 'is invalid when the fingerprint of the certificate does not match the registered fingerprint' do
81 allow(metadata).to receive(:matches?).and_return(false)
82 expect(subject).to be_invalid
83 expect(subject.errors[:fingerprint]).to be_present
84 end
85
86 it 'is invalid when the provider is not known' do
87 allow(registry).to receive(:metadata_for).and_return(nil)
88 expect(subject).to be_invalid
89 expect(subject.errors[:provider]).to be_present
90 end
91
92 it 'is invalid when single logout service url is not provided' do
93 allow(metadata).to receive(:matches?).and_return(true)
94 allow(metadata).to receive(:single_logout_services).and_return([])
95
96 expect(subject).to be_invalid
97 expect(subject.errors[:single_logout_service]).to be_present
98 end
99
100 it 'is valid when a single logout service url is available via the registry' do
101 issuer = FFaker::Internet.uri('https')
102 allow(registry).to receive(:metadata_for).with(issuer).and_return(metadata)
103 allow(metadata).to receive(:matches?).and_return(true)
104 allow(metadata).to receive(:single_logout_services).and_return([
105 Saml::Kit::Bindings::HttpPost.new(location: FFaker::Internet.uri('https'))
106 ])
107
108 subject = described_class.build(user, configuration: configuration) do |builder|
109 builder.issuer = issuer
110 end
111 expect(subject).to be_valid
112 end
113
114 it 'validates the schema of the request' do
115 id = Xml::Kit::Id.generate
116 key_pair = ::Xml::Kit::KeyPair.generate(use: :signing)
117 signed_xml = ::Xml::Kit::Signatures.sign(key_pair: key_pair) do |xml, signature|
118 xml.LogoutRequest ID: id do
119 signature.template(id)
120 xml.Fake do
121 xml.NotAllowed 'Huh?'
122 end
123 end
124 end
125 expect(described_class.new(signed_xml)).to be_invalid
126 end
127 end
128
129 describe '#response_for' do
130 let(:provider) do
131 Saml::Kit::IdentityProviderMetadata.build do |builder|
132 builder.add_single_logout_service(FFaker::Internet.uri('https'), binding: :http_post)
133 end
134 end
135
136 it 'serializes a logout response for a particular user' do
137 allow(subject).to receive(:provider).and_return(provider)
138
139 _, saml_params = subject.response_for(binding: :http_post)
140 response_binding = provider.single_logout_service_for(binding: :http_post)
141 result = response_binding.deserialize(saml_params)
142 expect(result).to be_instance_of(Saml::Kit::LogoutResponse)
143 end
144 end
145end