Skip to content

Commit 6d12c10

Browse files
authored
Merge pull request #671 from SAML-Toolkits/slo_encrypted_nameid
Add support on LogoutRequest with Encrypted NameID
2 parents 5193314 + 8f9976e commit 6d12c10

File tree

5 files changed

+79
-8
lines changed

5 files changed

+79
-8
lines changed

lib/onelogin/ruby-saml/slo_logoutrequest.rb

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -62,26 +62,57 @@ def is_valid?(collect_errors = false)
6262
# @return [String] Gets the NameID of the Logout Request.
6363
#
6464
def name_id
65-
@name_id ||= begin
66-
node = REXML::XPath.first(document, "/p:LogoutRequest/a:NameID", { "p" => PROTOCOL, "a" => ASSERTION })
67-
Utils.element_text(node)
68-
end
65+
@name_id ||= Utils.element_text(name_id_node)
6966
end
7067

7168
alias_method :nameid, :name_id
7269

7370
# @return [String] Gets the NameID Format of the Logout Request.
7471
#
7572
def name_id_format
76-
@name_id_node ||= REXML::XPath.first(document, "/p:LogoutRequest/a:NameID", { "p" => PROTOCOL, "a" => ASSERTION })
7773
@name_id_format ||=
78-
if @name_id_node && @name_id_node.attribute("Format")
79-
@name_id_node.attribute("Format").value
74+
if name_id_node && name_id_node.attribute("Format")
75+
name_id_node.attribute("Format").value
8076
end
8177
end
8278

8379
alias_method :nameid_format, :name_id_format
8480

81+
def name_id_node
82+
@name_id_node ||=
83+
begin
84+
encrypted_node = REXML::XPath.first(document, "/p:LogoutRequest/a:EncryptedID", { "p" => PROTOCOL, "a" => ASSERTION })
85+
if encrypted_node
86+
node = decrypt_nameid(encrypted_node)
87+
else
88+
node = REXML::XPath.first(document, "/p:LogoutRequest/a:NameID", { "p" => PROTOCOL, "a" => ASSERTION })
89+
end
90+
end
91+
end
92+
93+
# Decrypts an EncryptedID element
94+
# @param encryptedid_node [REXML::Element] The EncryptedID element
95+
# @return [REXML::Document] The decrypted EncrypedtID element
96+
#
97+
def decrypt_nameid(encrypt_node)
98+
99+
if settings.nil? || !settings.get_sp_key
100+
raise ValidationError.new('An ' + encrypt_node.name + ' found and no SP private key found on the settings to decrypt it')
101+
end
102+
103+
elem_plaintext = OneLogin::RubySaml::Utils.decrypt_data(encrypt_node, settings.get_sp_key)
104+
# If we get some problematic noise in the plaintext after decrypting.
105+
# This quick regexp parse will grab only the Element and discard the noise.
106+
elem_plaintext = elem_plaintext.match(/(.*<\/(\w+:)?NameID>)/m)[0]
107+
108+
# To avoid namespace errors if saml namespace is not defined
109+
# create a parent node first with the namespace defined
110+
node_header = '<node xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">'
111+
elem_plaintext = node_header + elem_plaintext + '</node>'
112+
doc = REXML::Document.new(elem_plaintext)
113+
doc.root[0]
114+
end
115+
85116
# @return [String|nil] Gets the ID attribute from the Logout Request. if exists.
86117
#
87118
def id

ruby-saml.gemspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ Gem::Specification.new do |s|
6666
s.add_development_dependency('simplecov-lcov', '>0.7.0')
6767
end
6868

69-
s.add_development_dependency('minitest', '~> 5.5')
69+
s.add_development_dependency('minitest', '~> 5.5', '<5.19.0')
7070
s.add_development_dependency('mocha', '~> 0.14')
7171

7272
if RUBY_VERSION < '2.0'
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<samlp:LogoutRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" Version="2.0" ID="_c0348950-935b-0131-1060-782bcb56fcaa" IssueInstant="2014-03-21T19:20:13">
2+
<saml:Issuer>https://app.onelogin.com/saml/metadata/SOMEACCOUNT</saml:Issuer>
3+
<saml:EncryptedID><xenc:EncryptedData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#" Type="http://www.w3.org/2001/04/xmlenc#Element"><xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc"/><dsig:KeyInfo xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><xenc:EncryptedKey><xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5"/><xenc:CipherData><xenc:CipherValue>L99BsKQL2iq5chjY+wRj6AH3jYxv9L4tscPJaDdsPWvPG47toC903oxEhjd1p9EMWkSPqD/HclvRhjcNVmhfUa3clTMM5PpZS1+oin2cDNFgKDkEaCXsGRgfn44uUKbEfUHNaljC72qh0lBLnoJe7ZkJHbFMbsA8Cd4UBtHzp4Y=</xenc:CipherValue></xenc:CipherData></xenc:EncryptedKey></dsig:KeyInfo>
4+
<xenc:CipherData><xenc:CipherValue>+dLZt52QiV39ltBeRNUev0jlD9ReI7lM3EDgfktPgKeIs6bvsLz9feWhlnydd+NjbwXTsBQjEhm80/O8szYZZZpQB3H+khA76HJoFeDdhDgnVMqeXVWVkeSjcDFHg6TPLPyydSNcsBPBOqP093xCF7je0PUgkK45cj6aN/hs7TckxCbeuOv/klz6jxc24TyNoGg3Z1TA/HlS2ePVY77LhQgqhsZIL52LTG3BjAHVvpzSXyuYbeR5OeiYIM028Xyl</xenc:CipherValue>
5+
</xenc:CipherData>
6+
</xenc:EncryptedData></saml:EncryptedID></samlp:LogoutRequest>

test/slo_logoutrequest_test.rb

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ class RubySamlTest < Minitest::Test
1010

1111
let(:settings) { OneLogin::RubySaml::Settings.new }
1212
let(:logout_request) { OneLogin::RubySaml::SloLogoutrequest.new(logout_request_document) }
13+
let(:logout_request_encrypted_nameid) { OneLogin::RubySaml::SloLogoutrequest.new(logout_request_encrypted_nameid_document) }
1314
let(:invalid_logout_request) { OneLogin::RubySaml::SloLogoutrequest.new(invalid_logout_request_document) }
1415

1516
before do
@@ -87,6 +88,18 @@ class RubySamlTest < Minitest::Test
8788
it "extract the value of the name id element" do
8889
assert_equal "someone@example.org", logout_request.nameid
8990
end
91+
92+
it 'is not possible when encryptID but no private key' do
93+
assert_raises(OneLogin::RubySaml::ValidationError, "An EncryptedID found and no SP private key found on the settings to decrypt it") do
94+
assert_equal "someone@example.org", logout_request_encrypted_nameid.nameid
95+
end
96+
end
97+
98+
it "extract the value of the name id element inside an EncryptedId" do
99+
settings.private_key = ruby_saml_key_text
100+
logout_request_encrypted_nameid.settings = settings
101+
assert_equal "someone@example.org", logout_request_encrypted_nameid.nameid
102+
end
90103
end
91104

92105
describe "#nameid_format" do
@@ -95,6 +108,18 @@ class RubySamlTest < Minitest::Test
95108
it "extract the format attribute of the name id element" do
96109
assert_equal "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", logout_request.nameid_format
97110
end
111+
112+
it 'is not possible when encryptID but no private key' do
113+
assert_raises(OneLogin::RubySaml::ValidationError, "An EncryptedID found and no SP private key found on the settings to decrypt it") do
114+
assert_equal "someone@example.org", logout_request_encrypted_nameid.nameid
115+
end
116+
end
117+
118+
it "extract the format attribute of the name id element" do
119+
settings.private_key = ruby_saml_key_text
120+
logout_request_encrypted_nameid.settings = settings
121+
assert_equal "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", logout_request_encrypted_nameid.nameid_format
122+
end
98123
end
99124

100125
describe "#issuer" do

test/test_helper.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,15 @@ def logout_request_document_with_name_id_format
237237
@logout_request_document_with_name_id_format
238238
end
239239

240+
def logout_request_encrypted_nameid_document
241+
unless @logout_request_encrypted_nameid_document
242+
xml = read_logout_request("slo_request_encrypted_nameid.xml")
243+
deflated = Zlib::Deflate.deflate(xml, 9)[2..-5]
244+
@logout_request_encrypted_nameid_document = Base64.encode64(deflated)
245+
end
246+
@logout_request_encrypted_nameid_document
247+
end
248+
240249
def logout_request_xml_with_session_index
241250
@logout_request_xml_with_session_index ||= File.read(File.join(File.dirname(__FILE__), 'logout_requests', 'slo_request_with_session_index.xml'))
242251
end

0 commit comments

Comments
 (0)