ruby-saml-1.1.2/ 0000755 0000041 0000041 00000000000 12672040544 013462 5 ustar www-data www-data ruby-saml-1.1.2/Rakefile 0000644 0000041 0000041 00000001041 12672040544 015123 0 ustar www-data www-data require 'rubygems'
require 'rake'
#not being used yet.
require 'rake/testtask'
Rake::TestTask.new(:test) do |test|
test.libs << 'lib' << 'test'
test.pattern = 'test/**/*_test.rb'
test.verbose = true
end
begin
require 'rcov/rcovtask'
Rcov::RcovTask.new do |test|
test.libs << 'test'
test.pattern = 'test/**/*_test.rb'
test.verbose = true
end
rescue LoadError
task :rcov do
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
end
end
task :test
task :default => :test
ruby-saml-1.1.2/Gemfile 0000644 0000041 0000041 00000000136 12672040544 014755 0 ustar www-data www-data #
# Please keep this file alphabetized and organized
#
source 'https://rubygems.org'
gemspec
ruby-saml-1.1.2/ruby-saml.gemspec 0000644 0000041 0000041 00000004454 12672040544 016751 0 ustar www-data www-data $LOAD_PATH.push File.expand_path('../lib', __FILE__)
require 'onelogin/ruby-saml/version'
Gem::Specification.new do |s|
s.name = 'ruby-saml'
s.version = OneLogin::RubySaml::VERSION
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.authors = ["OneLogin LLC"]
s.date = Time.now.strftime("%Y-%m-%d")
s.description = %q{SAML toolkit for Ruby on Rails}
s.email = %q{support@onelogin.com}
s.license = 'MIT'
s.extra_rdoc_files = [
"LICENSE",
"README.md"
]
s.files = `git ls-files`.split("\n")
s.homepage = %q{http://github.com/onelogin/ruby-saml}
s.rubyforge_project = %q{http://www.rubygems.org/gems/ruby-saml}
s.rdoc_options = ["--charset=UTF-8"]
s.require_paths = ["lib"]
s.rubygems_version = %q{1.3.7}
s.required_ruby_version = '>= 1.8.7'
s.summary = %q{SAML Ruby Tookit}
s.test_files = `git ls-files test/*`.split("\n")
s.add_runtime_dependency('uuid', '~> 2.3')
# Because runtime dependencies are determined at build time, we cannot make
# Nokogiri's version dependent on the Ruby version, even though we would
# have liked to constrain Ruby 1.8.7 to install only the 1.5.x versions.
if defined?(JRUBY_VERSION)
s.add_runtime_dependency('nokogiri', '>= 1.6.0')
s.add_runtime_dependency('jruby-openssl', '>= 0.9.8')
else
s.add_runtime_dependency('nokogiri', '>= 1.5.10')
end
s.add_development_dependency('minitest', '~> 5.5')
s.add_development_dependency('mocha', '~> 0.14')
s.add_development_dependency('rake', '~> 10')
s.add_development_dependency('shoulda', '~> 2.11')
s.add_development_dependency('simplecov','~> 0.9.0')
s.add_development_dependency('systemu', '~> 2')
s.add_development_dependency('timecop', '<= 0.6.0')
if defined?(JRUBY_VERSION)
# All recent versions of JRuby play well with pry
s.add_development_dependency('pry')
elsif RUBY_VERSION < '1.9'
# 1.8.7
s.add_development_dependency('ruby-debug', '~> 0.10.4')
elsif RUBY_VERSION < '2.0'
# 1.9.x
s.add_development_dependency('debugger-linecache', '~> 1.2.0')
s.add_development_dependency('debugger', '~> 1.6.4')
elsif RUBY_VERSION < '2.1'
# 2.0.x
s.add_development_dependency('byebug', '~> 2.1.1')
else
# 2.1.x, 2.2.x
s.add_development_dependency('pry-byebug')
end
end
ruby-saml-1.1.2/.travis.yml 0000644 0000041 0000041 00000000640 12672040544 015573 0 ustar www-data www-data sudo: false
language: ruby
rvm:
- 1.8.7
- 1.9.3
- 2.0.0
- 2.1.5
- 2.2.0
- ree
- jruby-1.7.21
- jruby-9.0.0.0
gemfile:
- Gemfile
- gemfiles/nokogiri-1.5.gemfile
matrix:
exclude:
- rvm: 1.8.7
gemfile: Gemfile
- rvm: ree
gemfile: Gemfile
- rvm: jruby-9.0.0.0
gemfile: gemfiles/nokogiri-1.5.gemfile
- rvm: jruby-1.7.21
gemfile: gemfiles/nokogiri-1.5.gemfile
ruby-saml-1.1.2/lib/ 0000755 0000041 0000041 00000000000 12672040544 014230 5 ustar www-data www-data ruby-saml-1.1.2/lib/xml_security.rb 0000644 0000041 0000041 00000030561 12672040544 017311 0 ustar www-data www-data # The contents of this file are subject to the terms
# of the Common Development and Distribution License
# (the License). You may not use this file except in
# compliance with the License.
#
# You can obtain a copy of the License at
# https://opensso.dev.java.net/public/CDDLv1.0.html or
# opensso/legal/CDDLv1.0.txt
# See the License for the specific language governing
# permission and limitations under the License.
#
# When distributing Covered Code, include this CDDL
# Header Notice in each file and include the License file
# at opensso/legal/CDDLv1.0.txt.
# If applicable, add the following below the CDDL Header,
# with the fields enclosed by brackets [] replaced by
# your own identifying information:
# "Portions Copyrighted [year] [name of copyright owner]"
#
# $Id: xml_sec.rb,v 1.6 2007/10/24 00:28:41 todddd Exp $
#
# Copyright 2007 Sun Microsystems Inc. All Rights Reserved
# Portions Copyrighted 2007 Todd W Saxton.
require 'rubygems'
require "rexml/document"
require "rexml/xpath"
require "openssl"
require 'nokogiri'
require "digest/sha1"
require "digest/sha2"
require "onelogin/ruby-saml/validation_error"
module XMLSecurity
class BaseDocument < REXML::Document
REXML::Document::entity_expansion_limit = 0
C14N = "http://www.w3.org/2001/10/xml-exc-c14n#"
DSIG = "http://www.w3.org/2000/09/xmldsig#"
NOKOGIRI_OPTIONS = Nokogiri::XML::ParseOptions::STRICT |
Nokogiri::XML::ParseOptions::NONET
def canon_algorithm(element)
algorithm = element
if algorithm.is_a?(REXML::Element)
algorithm = element.attribute('Algorithm').value
end
case algorithm
when "http://www.w3.org/TR/2001/REC-xml-c14n-20010315" then Nokogiri::XML::XML_C14N_1_0
when "http://www.w3.org/2006/12/xml-c14n11" then Nokogiri::XML::XML_C14N_1_1
else Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0
end
end
def algorithm(element)
algorithm = element
if algorithm.is_a?(REXML::Element)
algorithm = element.attribute("Algorithm").value
end
algorithm = algorithm && algorithm =~ /(rsa-)?sha(.*?)$/i && $2.to_i
case algorithm
when 256 then OpenSSL::Digest::SHA256
when 384 then OpenSSL::Digest::SHA384
when 512 then OpenSSL::Digest::SHA512
else
OpenSSL::Digest::SHA1
end
end
end
class Document < BaseDocument
RSA_SHA1 = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"
RSA_SHA256 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"
RSA_SHA384 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha384"
RSA_SHA512 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512"
SHA1 = "http://www.w3.org/2000/09/xmldsig#sha1"
SHA256 = "http://www.w3.org/2001/04/xmldsig-more#sha256"
SHA384 = "http://www.w3.org/2001/04/xmldsig-more#sha384"
SHA512 = "http://www.w3.org/2001/04/xmldsig-more#sha512"
ENVELOPED_SIG = "http://www.w3.org/2000/09/xmldsig#enveloped-signature"
INC_PREFIX_LIST = "#default samlp saml ds xs xsi md"
attr_accessor :uuid
def uuid
@uuid ||= begin
document.root.nil? ? nil : document.root.attributes['ID']
end
end
#
#
#
#
#
#
#
#
#
# etc.
#
#
#
#
#
def sign_document(private_key, certificate, signature_method = RSA_SHA1, digest_method = SHA1)
noko = Nokogiri.parse(self.to_s) do |options|
options = XMLSecurity::BaseDocument::NOKOGIRI_OPTIONS
end
signature_element = REXML::Element.new("ds:Signature").add_namespace('ds', DSIG)
signed_info_element = signature_element.add_element("ds:SignedInfo")
signed_info_element.add_element("ds:CanonicalizationMethod", {"Algorithm" => C14N})
signed_info_element.add_element("ds:SignatureMethod", {"Algorithm"=>signature_method})
# Add Reference
reference_element = signed_info_element.add_element("ds:Reference", {"URI" => "##{uuid}"})
# Add Transforms
transforms_element = reference_element.add_element("ds:Transforms")
transforms_element.add_element("ds:Transform", {"Algorithm" => ENVELOPED_SIG})
c14element = transforms_element.add_element("ds:Transform", {"Algorithm" => C14N})
c14element.add_element("ec:InclusiveNamespaces", {"xmlns:ec" => C14N, "PrefixList" => INC_PREFIX_LIST})
digest_method_element = reference_element.add_element("ds:DigestMethod", {"Algorithm" => digest_method})
inclusive_namespaces = INC_PREFIX_LIST.split(" ")
canon_doc = noko.canonicalize(canon_algorithm(C14N), inclusive_namespaces)
reference_element.add_element("ds:DigestValue").text = compute_digest(canon_doc, algorithm(digest_method_element))
# add SignatureValue
noko_sig_element = Nokogiri.parse(signature_element.to_s) do |options|
options = XMLSecurity::BaseDocument::NOKOGIRI_OPTIONS
end
noko_signed_info_element = noko_sig_element.at_xpath('//ds:Signature/ds:SignedInfo', 'ds' => DSIG)
canon_string = noko_signed_info_element.canonicalize(canon_algorithm(C14N))
signature = compute_signature(private_key, algorithm(signature_method).new, canon_string)
signature_element.add_element("ds:SignatureValue").text = signature
# add KeyInfo
key_info_element = signature_element.add_element("ds:KeyInfo")
x509_element = key_info_element.add_element("ds:X509Data")
x509_cert_element = x509_element.add_element("ds:X509Certificate")
if certificate.is_a?(String)
certificate = OpenSSL::X509::Certificate.new(certificate)
end
x509_cert_element.text = Base64.encode64(certificate.to_der).gsub(/\n/, "")
# add the signature
issuer_element = self.elements["//saml:Issuer"]
if issuer_element
self.root.insert_after issuer_element, signature_element
else
if sp_sso_descriptor = self.elements["/md:EntityDescriptor"]
self.root.insert_before sp_sso_descriptor, signature_element
else
self.root.add_element(signature_element)
end
end
end
protected
def compute_signature(private_key, signature_algorithm, document)
Base64.encode64(private_key.sign(signature_algorithm, document)).gsub(/\n/, "")
end
def compute_digest(document, digest_algorithm)
digest = digest_algorithm.digest(document)
Base64.encode64(digest).strip!
end
end
class SignedDocument < BaseDocument
attr_accessor :signed_element_id
attr_accessor :errors
def initialize(response, errors = [])
super(response)
@errors = errors
end
def signed_element_id
@signed_element_id ||= extract_signed_element_id
end
def validate_document(idp_cert_fingerprint, soft = true, options = {})
# get cert from response
cert_element = REXML::XPath.first(
self,
"//ds:X509Certificate",
{ "ds"=>DSIG }
)
if cert_element
base64_cert = cert_element.text
cert_text = Base64.decode64(base64_cert)
cert = OpenSSL::X509::Certificate.new(cert_text)
if options[:fingerprint_alg]
fingerprint_alg = XMLSecurity::BaseDocument.new.algorithm(options[:fingerprint_alg]).new
else
fingerprint_alg = OpenSSL::Digest::SHA1.new
end
fingerprint = fingerprint_alg.hexdigest(cert.to_der)
# check cert matches registered idp cert
if fingerprint != idp_cert_fingerprint.gsub(/[^a-zA-Z0-9]/,"").downcase
@errors << "Fingerprint mismatch"
return soft ? false : (raise OneLogin::RubySaml::ValidationError.new("Fingerprint mismatch"))
end
else
if options[:cert]
base64_cert = Base64.encode64(options[:cert].to_pem)
else
if soft
return false
else
raise OneLogin::RubySaml::ValidationError.new("Certificate element missing in response (ds:X509Certificate) and not cert provided at settings")
end
end
end
validate_signature(base64_cert, soft)
end
def validate_signature(base64_cert, soft = true)
document = Nokogiri.parse(self.to_s) do |options|
options = XMLSecurity::BaseDocument::NOKOGIRI_OPTIONS
end
# create a rexml document
@working_copy ||= REXML::Document.new(self.to_s).root
# get signature node
sig_element = REXML::XPath.first(
@working_copy,
"//ds:Signature",
{"ds"=>DSIG}
)
# signature method
sig_alg_value = REXML::XPath.first(
sig_element,
"./ds:SignedInfo/ds:SignatureMethod",
{"ds"=>DSIG}
)
signature_algorithm = algorithm(sig_alg_value)
# get signature
base64_signature = REXML::XPath.first(
sig_element,
"./ds:SignatureValue",
{"ds" => DSIG}
).text
signature = Base64.decode64(base64_signature)
# canonicalization method
canon_algorithm = canon_algorithm REXML::XPath.first(
sig_element,
'./ds:SignedInfo/ds:CanonicalizationMethod',
'ds' => DSIG
)
noko_sig_element = document.at_xpath('//ds:Signature', 'ds' => DSIG)
noko_signed_info_element = noko_sig_element.at_xpath('./ds:SignedInfo', 'ds' => DSIG)
# Handle when no URI
noko_signed_info_reference_element_uri_attr = noko_signed_info_element.at_xpath('./ds:Reference', 'ds' => DSIG).attributes["URI"]
if (noko_signed_info_reference_element_uri_attr.value.empty?)
noko_signed_info_reference_element_uri_attr.value = "##{document.root.attribute('ID')}"
end
canon_string = noko_signed_info_element.canonicalize(canon_algorithm)
noko_sig_element.remove
# get inclusive namespaces
inclusive_namespaces = extract_inclusive_namespaces
# check digests
ref = REXML::XPath.first(sig_element, "//ds:Reference", {"ds"=>DSIG})
uri = ref.attributes.get_attribute("URI").value
hashed_element = uri.empty? ? document : document.at_xpath("//*[@ID=$uri]", nil, { 'uri' => uri[1..-1] })
# hashed_element = document.at_xpath("//*[@ID=$uri]", nil, { 'uri' => uri[1..-1] })
canon_algorithm = canon_algorithm REXML::XPath.first(
ref,
'//ds:CanonicalizationMethod',
{ "ds" => DSIG }
)
canon_hashed_element = hashed_element.canonicalize(canon_algorithm, inclusive_namespaces)
digest_algorithm = algorithm(REXML::XPath.first(
ref,
"//ds:DigestMethod",
{ "ds" => DSIG }
))
hash = digest_algorithm.digest(canon_hashed_element)
encoded_digest_value = REXML::XPath.first(
ref,
"//ds:DigestValue",
{ "ds" => DSIG }
).text
digest_value = Base64.decode64(encoded_digest_value)
unless digests_match?(hash, digest_value)
@errors << "Digest mismatch"
return soft ? false : (raise OneLogin::RubySaml::ValidationError.new("Digest mismatch"))
end
# get certificate object
cert_text = Base64.decode64(base64_cert)
cert = OpenSSL::X509::Certificate.new(cert_text)
# verify signature
unless cert.public_key.verify(signature_algorithm.new, signature, canon_string)
@errors << "Key validation error"
return soft ? false : (raise OneLogin::RubySaml::ValidationError.new("Key validation error"))
end
return true
end
private
def digests_match?(hash, digest_value)
hash == digest_value
end
def extract_signed_element_id
reference_element = REXML::XPath.first(
self,
"//ds:Signature/ds:SignedInfo/ds:Reference",
{"ds"=>DSIG}
)
return nil if reference_element.nil?
sei = reference_element.attribute("URI").value[1..-1]
sei.nil? ? self.root.attribute("ID") : sei
end
def extract_inclusive_namespaces
element = REXML::XPath.first(
self,
"//ec:InclusiveNamespaces",
{ "ec" => C14N }
)
if element
prefix_list = element.attributes.get_attribute("PrefixList").value
prefix_list.split(" ")
else
nil
end
end
end
end
ruby-saml-1.1.2/lib/schemas/ 0000755 0000041 0000041 00000000000 12672040544 015653 5 ustar www-data www-data ruby-saml-1.1.2/lib/schemas/sstc-saml-metadata-ui-v1.0.xsd 0000644 0000041 0000041 00000006032 12672040544 023155 0 ustar www-data www-data
Document title: Metadata Extension Schema for SAML V2.0 Metadata Extensions for Login and Discovery User Interface Version 1.0
Document identifier: sstc-saml-metadata-ui-v1.0.xsd
Location: http://docs.oasis-open.org/security/saml/Post2.0/
Revision history:
16 November 2010:
Added Keywords element/type.
01 November 2010
Changed filename.
September 2010:
Initial version.
ruby-saml-1.1.2/lib/schemas/saml-schema-authn-context-2.0.xsd 0000644 0000041 0000041 00000001372 12672040544 023664 0 ustar www-data www-data
Document identifier: saml-schema-authn-context-2.0
Location: http://docs.oasis-open.org/security/saml/v2.0/
Revision history:
V2.0 (March, 2005):
New core authentication context schema for SAML V2.0.
This is just an include of all types from the schema
referred to in the include statement below.