Skip to content

Commit f0f819f

Browse files
committed
Merge pull request #10 from zendesk/sdavidovitz/audience_verification
Add audience verification to assertion/condition verify
2 parents d10bce4 + 0f6ef04 commit f0f819f

File tree

4 files changed

+98
-14
lines changed

4 files changed

+98
-14
lines changed

lib/samlr/assertion.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ def name_id
5151
@name_id ||= assertion.at("./saml:Subject/saml:NameID", NS_MAP).text
5252
end
5353

54+
def conditions
55+
@conditions ||= Condition.new(assertion.at("./saml:Conditions", NS_MAP), options)
56+
end
57+
5458
private
5559

5660
def assertion
@@ -68,10 +72,6 @@ def skip_conditions?
6872
!!options[:skip_conditions]
6973
end
7074

71-
def conditions
72-
@conditions ||= Condition.new(assertion.at("./saml:Conditions", NS_MAP))
73-
end
74-
7575
def verify_conditions!
7676
conditions.verify!
7777
end

lib/samlr/condition.rb

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
module Samlr
22
class Condition
3-
attr_reader :not_before, :not_on_or_after
3+
attr_reader :audience, :not_before, :not_on_or_after, :options
44

5-
def initialize(condition)
5+
def initialize(condition, options)
6+
@options = options
67
@not_before = (condition || {})["NotBefore"]
78
@not_on_or_after = (condition || {})["NotOnOrAfter"]
9+
@audience = extract_audience(condition)
810
end
911

1012
def verify!
@@ -16,6 +18,10 @@ def verify!
1618
raise Samlr::ConditionsError.new("Not on or after violation, now #{Samlr::Tools::Timestamp.stamp} vs. at latest #{not_on_or_after}")
1719
end
1820

21+
unless audience_satisfied?
22+
raise Samlr::ConditionsError.new("Audience violation, expected #{options[:audience]} vs. #{audience}")
23+
end
24+
1925
true
2026
end
2127

@@ -26,6 +32,22 @@ def not_before_satisfied?
2632
def not_on_or_after_satisfied?
2733
not_on_or_after.nil? || Samlr::Tools::Timestamp.not_on_or_after?(Samlr::Tools::Timestamp.parse(not_on_or_after))
2834
end
29-
end
3035

36+
def audience_satisfied?
37+
audience.nil? || options[:audience].nil? ||
38+
audience == options[:audience]
39+
end
40+
41+
private
42+
43+
def extract_audience(condition)
44+
return unless condition
45+
46+
audience_node = condition.at("./saml:AudienceRestriction/saml:Audience", NS_MAP)
47+
48+
return unless audience_node
49+
50+
audience_node.text
51+
end
52+
end
3153
end

test/unit/test_assertion.rb

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,16 @@
2929
end
3030

3131
describe "#verify!" do
32+
let(:condition) do
33+
Class.new do
34+
def verify!
35+
raise Samlr::ConditionsError, 'error'
36+
end
37+
end
38+
end
39+
3240
before do
33-
@unsatisfied_condition = Samlr::Condition.new("NotBefore" => Samlr::Tools::Timestamp.stamp(Time.now + 60))
41+
@unsatisfied_condition = condition.new
3442
end
3543

3644
describe "when conditions are not met" do

test/unit/test_condition.rb

Lines changed: 60 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
require File.expand_path("test/test_helper")
22

33
def condition(before, after)
4-
Samlr::Condition.new(
5-
"NotBefore" => before ? before.utc.iso8601 : nil,
6-
"NotOnOrAfter" => after ? after.utc.iso8601 : nil
7-
)
4+
element = Nokogiri::XML::Element.new('saml:Condition', Nokogiri::XML(''))
5+
element["NotBefore"] = before.utc.iso8601 if before
6+
element["NotOnOrAfter"] = after.utc.iso8601 if after
7+
8+
Samlr::Condition.new(element, {})
89
end
910

1011
describe Samlr::Condition do
@@ -14,6 +15,44 @@ def condition(before, after)
1415
end
1516

1617
describe "verify!" do
18+
describe "audience verification" do
19+
let(:response) { fixed_saml_response }
20+
subject { response.assertion.conditions }
21+
22+
describe "when it is wrong" do
23+
before do
24+
response.options[:audience] = 'example.com'
25+
end
26+
27+
it "raises an exception" do
28+
Time.stub(:now, Time.at(1344379365)) do
29+
assert subject.not_on_or_after_satisfied?
30+
assert subject.not_before_satisfied?
31+
refute subject.audience_satisfied?
32+
33+
begin
34+
subject.verify!
35+
flunk "Expected exception"
36+
rescue Samlr::ConditionsError => e
37+
assert_match /Audience/, e.message
38+
end
39+
end
40+
end
41+
end
42+
43+
describe "when it is right" do
44+
before do
45+
response.options[:audience] = 'example.org'
46+
end
47+
48+
it "does not raise an exception" do
49+
Time.stub(:now, Time.at(1344379365)) do
50+
assert subject.verify!
51+
end
52+
end
53+
end
54+
end
55+
1756
describe "when the lower time has not been met" do
1857
before { @not_before = (Time.now + 5*60) }
1958
subject { condition(@not_before, @not_after) }
@@ -57,15 +96,30 @@ def condition(before, after)
5796
end
5897
end
5998

99+
describe "#audience_satisfied?" do
100+
it "returns true when audience is a nil value" do
101+
element = Nokogiri::XML::Node.new('saml:Condition', Nokogiri::XML(''))
102+
assert Samlr::Condition.new(element, {}).audience_satisfied?
103+
end
104+
105+
it "returns true when passed a nil audience" do
106+
condition = fixed_saml_response.assertion.conditions
107+
assert_equal 'example.org', condition.audience
108+
assert condition.audience_satisfied?
109+
end
110+
end
111+
60112
describe "#not_before_satisfied?" do
61113
it "returns true when passed a nil value" do
62-
assert Samlr::Condition.new({}).not_before_satisfied?
114+
element = Nokogiri::XML::Node.new('saml:Condition', Nokogiri::XML(''))
115+
assert Samlr::Condition.new(element, {}).not_before_satisfied?
63116
end
64117
end
65118

66119
describe "#not_on_or_after_satisfied?" do
67120
it "returns true when passed a nil value" do
68-
assert Samlr::Condition.new({}).not_on_or_after_satisfied?
121+
element = Nokogiri::XML::Node.new('saml:Condition', Nokogiri::XML(''))
122+
assert Samlr::Condition.new(element, {}).not_on_or_after_satisfied?
69123
end
70124
end
71125
end

0 commit comments

Comments
 (0)