Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 38 additions & 7 deletions lib/onelogin/ruby-saml/slo_logoutrequest.rb
Original file line number Diff line number Diff line change
Expand Up @@ -62,26 +62,57 @@ def is_valid?(collect_errors = false)
# @return [String] Gets the NameID of the Logout Request.
#
def name_id
@name_id ||= begin
node = REXML::XPath.first(document, "/p:LogoutRequest/a:NameID", { "p" => PROTOCOL, "a" => ASSERTION })
Utils.element_text(node)
end
@name_id ||= Utils.element_text(name_id_node)
end

alias_method :nameid, :name_id

# @return [String] Gets the NameID Format of the Logout Request.
#
def name_id_format
@name_id_node ||= REXML::XPath.first(document, "/p:LogoutRequest/a:NameID", { "p" => PROTOCOL, "a" => ASSERTION })
@name_id_format ||=
if @name_id_node && @name_id_node.attribute("Format")
@name_id_node.attribute("Format").value
if name_id_node && name_id_node.attribute("Format")
name_id_node.attribute("Format").value
end
end

alias_method :nameid_format, :name_id_format

def name_id_node
@name_id_node ||=
begin
encrypted_node = REXML::XPath.first(document, "/p:LogoutRequest/a:EncryptedID", { "p" => PROTOCOL, "a" => ASSERTION })
if encrypted_node
node = decrypt_nameid(encrypted_node)
else
node = REXML::XPath.first(document, "/p:LogoutRequest/a:NameID", { "p" => PROTOCOL, "a" => ASSERTION })
end
end
end

# Decrypts an EncryptedID element
# @param encryptedid_node [REXML::Element] The EncryptedID element
# @return [REXML::Document] The decrypted EncrypedtID element
#
def decrypt_nameid(encrypt_node)

if settings.nil? || !settings.get_sp_key
raise ValidationError.new('An ' + encrypt_node.name + ' found and no SP private key found on the settings to decrypt it')
end

elem_plaintext = OneLogin::RubySaml::Utils.decrypt_data(encrypt_node, settings.get_sp_key)
# If we get some problematic noise in the plaintext after decrypting.
# This quick regexp parse will grab only the Element and discard the noise.
elem_plaintext = elem_plaintext.match(/(.*<\/(\w+:)?NameID>)/m)[0]

Check failure

Code scanning / CodeQL

Polynomial regular expression used on uncontrolled data

This [regular expression](1) that depends on a [library input](2) may run slow on strings with many repetitions of 'a'.

# To avoid namespace errors if saml namespace is not defined
# create a parent node first with the namespace defined
node_header = '<node xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">'
elem_plaintext = node_header + elem_plaintext + '</node>'
doc = REXML::Document.new(elem_plaintext)
doc.root[0]
end

# @return [String|nil] Gets the ID attribute from the Logout Request. if exists.
#
def id
Expand Down
2 changes: 1 addition & 1 deletion ruby-saml.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ Gem::Specification.new do |s|
s.add_development_dependency('simplecov-lcov', '>0.7.0')
end

s.add_development_dependency('minitest', '~> 5.5')
s.add_development_dependency('minitest', '~> 5.5', '<5.19.0')
s.add_development_dependency('mocha', '~> 0.14')

if RUBY_VERSION < '2.0'
Expand Down
6 changes: 6 additions & 0 deletions test/logout_requests/slo_request_encrypted_nameid.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<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">
<saml:Issuer>https://app.onelogin.com/saml/metadata/SOMEACCOUNT</saml:Issuer>
<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>
<xenc:CipherData><xenc:CipherValue>+dLZt52QiV39ltBeRNUev0jlD9ReI7lM3EDgfktPgKeIs6bvsLz9feWhlnydd+NjbwXTsBQjEhm80/O8szYZZZpQB3H+khA76HJoFeDdhDgnVMqeXVWVkeSjcDFHg6TPLPyydSNcsBPBOqP093xCF7je0PUgkK45cj6aN/hs7TckxCbeuOv/klz6jxc24TyNoGg3Z1TA/HlS2ePVY77LhQgqhsZIL52LTG3BjAHVvpzSXyuYbeR5OeiYIM028Xyl</xenc:CipherValue>
</xenc:CipherData>
</xenc:EncryptedData></saml:EncryptedID></samlp:LogoutRequest>
25 changes: 25 additions & 0 deletions test/slo_logoutrequest_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class RubySamlTest < Minitest::Test

let(:settings) { OneLogin::RubySaml::Settings.new }
let(:logout_request) { OneLogin::RubySaml::SloLogoutrequest.new(logout_request_document) }
let(:logout_request_encrypted_nameid) { OneLogin::RubySaml::SloLogoutrequest.new(logout_request_encrypted_nameid_document) }
let(:invalid_logout_request) { OneLogin::RubySaml::SloLogoutrequest.new(invalid_logout_request_document) }

before do
Expand Down Expand Up @@ -87,6 +88,18 @@ class RubySamlTest < Minitest::Test
it "extract the value of the name id element" do
assert_equal "someone@example.org", logout_request.nameid
end

it 'is not possible when encryptID but no private key' do
assert_raises(OneLogin::RubySaml::ValidationError, "An EncryptedID found and no SP private key found on the settings to decrypt it") do
assert_equal "someone@example.org", logout_request_encrypted_nameid.nameid
end
end

it "extract the value of the name id element inside an EncryptedId" do
settings.private_key = ruby_saml_key_text
logout_request_encrypted_nameid.settings = settings
assert_equal "someone@example.org", logout_request_encrypted_nameid.nameid
end
end

describe "#nameid_format" do
Expand All @@ -95,6 +108,18 @@ class RubySamlTest < Minitest::Test
it "extract the format attribute of the name id element" do
assert_equal "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", logout_request.nameid_format
end

it 'is not possible when encryptID but no private key' do
assert_raises(OneLogin::RubySaml::ValidationError, "An EncryptedID found and no SP private key found on the settings to decrypt it") do
assert_equal "someone@example.org", logout_request_encrypted_nameid.nameid
end
end

it "extract the format attribute of the name id element" do
settings.private_key = ruby_saml_key_text
logout_request_encrypted_nameid.settings = settings
assert_equal "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", logout_request_encrypted_nameid.nameid_format
end
end

describe "#issuer" do
Expand Down
9 changes: 9 additions & 0 deletions test/test_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,15 @@ def logout_request_document_with_name_id_format
@logout_request_document_with_name_id_format
end

def logout_request_encrypted_nameid_document
unless @logout_request_encrypted_nameid_document
xml = read_logout_request("slo_request_encrypted_nameid.xml")
deflated = Zlib::Deflate.deflate(xml, 9)[2..-5]
@logout_request_encrypted_nameid_document = Base64.encode64(deflated)
end
@logout_request_encrypted_nameid_document
end

def logout_request_xml_with_session_index
@logout_request_xml_with_session_index ||= File.read(File.join(File.dirname(__FILE__), 'logout_requests', 'slo_request_with_session_index.xml'))
end
Expand Down