Skip to content

Commit

Permalink
Merge pull request #2 from amoose/feature/logout
Browse files Browse the repository at this point in the history
Updates logout request builder
  • Loading branch information
amoose committed Jul 24, 2015
2 parents 90cc1bf + 50ee508 commit 801ad02
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 37 deletions.
16 changes: 10 additions & 6 deletions lib/onelogin/ruby-saml/logoutrequest.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
require "uuid"

require "onelogin/ruby-saml/logging"
require "onelogin/ruby-saml/saml_message"

module OneLogin
module RubySaml
Expand All @@ -24,7 +25,10 @@ def create(settings, params={})
end

def create_params(settings, params={})
params = {} if params.nil?
# The method expects :RelayState but sometimes we get 'RelayState' instead.
# Based on the HashWithIndifferentAccess value in Rails we could experience
# conflicts so this line will solve them.
relay_state = params[:RelayState] || params['RelayState']

request_doc = create_logout_request_xml_doc(settings)
request_doc.context[:attribute_quote] = :quote if settings.double_quote_xml_attribute_values
Expand All @@ -39,10 +43,10 @@ def create_params(settings, params={})
request_params = {"SAMLRequest" => base64_request}

if settings.security[:logout_requests_signed] && !settings.security[:embed_sign] && settings.private_key
params['SigAlg'] = XMLSecurity::Document::SHA1
params['SigAlg'] = settings.security[:signature_method]
url_string = "SAMLRequest=#{CGI.escape(base64_request)}"
url_string += "&RelayState=#{CGI.escape(params['RelayState'])}" if params['RelayState']
url_string += "&SigAlg=#{CGI.escape(params['SigAlg'])}"
url_string << "&RelayState=#{CGI.escape(relay_state)}" if relay_state
url_string << "&SigAlg=#{CGI.escape(params['SigAlg'])}"
private_key = settings.get_sp_key()
signature = private_key.sign(XMLSecurity::BaseDocument.new.algorithm(settings.security[:signature_method]).new, url_string)
params['Signature'] = encode(signature)
Expand Down Expand Up @@ -88,7 +92,7 @@ def create_logout_request_xml_doc(settings)
sessionindex.text = settings.sessionindex
end

# embebed sign
# embed signature
if settings.security[:logout_requests_signed] && settings.private_key && settings.certificate && settings.security[:embed_sign]
private_key = settings.get_sp_key()
cert = settings.get_sp_cert()
Expand All @@ -99,4 +103,4 @@ def create_logout_request_xml_doc(settings)
end
end
end
end
end
76 changes: 45 additions & 31 deletions lib/onelogin/ruby-saml/saml_message.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
require 'cgi'
require 'zlib'
require 'base64'
require "rexml/document"
require "rexml/xpath"
require 'nokogiri'
require 'rexml/document'
require 'rexml/xpath'
require 'thread'

module OneLogin
module RubySaml
Expand All @@ -12,15 +14,22 @@ class SamlMessage
ASSERTION = "urn:oasis:names:tc:SAML:2.0:assertion"
PROTOCOL = "urn:oasis:names:tc:SAML:2.0:protocol"

def valid_saml?(document, soft = true)
Dir.chdir(File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'schemas'))) do
@schema = Nokogiri::XML::Schema(IO.read('saml-schema-protocol-2.0.xsd'))
@xml = Nokogiri::XML(document.to_s)
BASE64_FORMAT = %r(\A[A-Za-z0-9+/]{4}*[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=?\Z)

def self.schema
@schema ||= Mutex.new.synchronize do
Dir.chdir(File.expand_path("../../../schemas", __FILE__)) do
::Nokogiri::XML::Schema(File.read("saml-schema-protocol-2.0.xsd"))
end
end
if soft
@schema.validate(@xml).map{ return false }
else
@schema.validate(@xml).map{ |error| validation_error("#{error.message}\n\n#{@xml.to_s}") }
end

def valid_saml?(document, soft = true)
xml = Nokogiri::XML(document.to_s)

SamlMessage.schema.validate(xml).map do |error|
break false if soft
validation_error("#{error.message}\n\n#{xml.to_s}")
end
end

Expand All @@ -30,22 +39,29 @@ def validation_error(message)

private

##
# Take a SAML object provided by +saml+, determine its status and return
# a decoded XML as a String.
#
# Since SAML decided to use the RFC1951 and therefor has no zlib markers,
# the only reliable method of deciding whether we have a zlib stream or not
# is to try and inflate it and fall back to the base64 decoded string if
# the stream contains errors.
def decode_raw_saml(saml)
if saml =~ /^</
return saml
elsif (decoded = decode(saml)) =~ /^</
return decoded
elsif (inflated = inflate(decoded)) =~ /^</
return inflated
end
return saml unless base64_encoded?(saml)

return nil
decoded = decode(saml)
begin
inflate(decoded)
rescue
decoded
end
end

def encode_raw_saml(saml, settings)
saml = Zlib::Deflate.deflate(saml, 9)[2..-5] if settings.compress_request
base64_saml = Base64.encode64(saml)
return CGI.escape(base64_saml)
saml = deflate(saml) if settings.compress_request

CGI.escape(Base64.encode64(saml))
end

def decode(encoded)
Expand All @@ -56,23 +72,21 @@ def encode(encoded)
Base64.encode64(encoded).gsub(/\n/, "")
end

def escape(unescaped)
CGI.escape(unescaped)
end

def unescape(escaped)
CGI.unescape(escaped)
# Check if a string is base64 encoded
#
# @param string [String] string to check the encoding of
# @return [true, false] whether or not the string is base64 encoded
def base64_encoded?(string)
!!string.gsub(/[\r\n]|\\r|\\n/, "").match(BASE64_FORMAT)
end

def inflate(deflated)
zlib = Zlib::Inflate.new(-Zlib::MAX_WBITS)
zlib.inflate(deflated)
Zlib::Inflate.new(-Zlib::MAX_WBITS).inflate(deflated)
end

def deflate(inflated)
Zlib::Deflate.deflate(inflated, 9)[2..-5]
end

end
end
end
end

0 comments on commit 801ad02

Please sign in to comment.