Skip to content

Commit

Permalink
Version bump to 0.0.1
Browse files Browse the repository at this point in the history
  • Loading branch information
Marcelo de Moraes Serpa committed Jul 20, 2010
1 parent a771d01 commit f9fac68
Show file tree
Hide file tree
Showing 7 changed files with 211 additions and 1 deletion.
2 changes: 1 addition & 1 deletion VERSION
@@ -1 +1 @@
0.0.0
0.0.1
6 changes: 6 additions & 0 deletions lib/onelogin/saml.rb
@@ -0,0 +1,6 @@
require 'onelogin/saml/authrequest'
require 'onelogin/saml/response'
require 'onelogin/saml/settings'

module Onelogin::Saml
end
38 changes: 38 additions & 0 deletions lib/onelogin/saml/authrequest.rb
@@ -0,0 +1,38 @@
require "base64"

module Onelogin::Saml
class Authrequest
def create(settings)
id = Onelogin::Saml::Authrequest.generateUniqueID(42)
issue_instant = Onelogin::Saml::Authrequest.getTimestamp

request =
"<samlp:AuthnRequest xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" ID=\"#{id}\" Version=\"2.0\" IssueInstant=\"#{issue_instant}\" ProtocolBinding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\" AssertionConsumerServiceURL=\"#{settings.assertion_consumer_service_url}\">" +
"<saml:Issuer xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\">#{settings.issuer}</saml:Issuer>\n" +
"<samlp:NameIDPolicy xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" Format=\"#{settings.name_identifier_format}\" AllowCreate=\"true\"></samlp:NameIDPolicy>\n" +
"<samlp:RequestedAuthnContext xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" Comparison=\"exact\">" +
"<saml:AuthnContextClassRef xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\">urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef></samlp:RequestedAuthnContext>\n" +
"</samlp:AuthnRequest>"

deflated_request = Zlib::Deflate.deflate(request, 9)[2..-5]
base64_request = Base64.encode64(deflated_request)
encoded_request = CGI.escape(base64_request)

settings.idp_sso_target_url + "?SAMLRequest=" + encoded_request
end

private

def self.generateUniqueID(length)
chars = ("a".."f").to_a + ("0".."9").to_a
chars_len = chars.size
uniqueID = ""
1.upto(length) { |i| uniqueID << chars[rand(chars_len-1)] }
uniqueID
end

def self.getTimestamp
Time.new().strftime("%Y-%m-%dT%H:%M:%SZ")
end
end
end
29 changes: 29 additions & 0 deletions lib/onelogin/saml/response.rb
@@ -0,0 +1,29 @@
require "rexml/document"
require "xml_sec"

module Onelogin::Saml
class Response
def initialize(response)
@response = response
@document = XMLSecurity::SignedDocument.new(Base64.decode64(@response))
end

def logger=(val)
@logger = val
end

def settings=(_settings)
@settings = _settings
end

def is_valid?
unless @response.blank?
@document.validate(@settings.idp_cert_fingerprint, @logger) unless !@settings.idp_cert_fingerprint
end
end

def name_id
@document.elements["/samlp:Response/saml:Assertion/saml:Subject/saml:NameID"].text
end
end
end
45 changes: 45 additions & 0 deletions lib/onelogin/saml/settings.rb
@@ -0,0 +1,45 @@
module Onelogin::Saml
class Settings
def assertion_consumer_service_url
@assertion_consumer_service_url
end
def assertion_consumer_service_url=(val)
@assertion_consumer_service_url = val
end

def issuer
@issuer
end
def issuer=(val)
@issuer = val
end

def sp_name_qualifier
@sp_name_qualifier
end
def sp_name_qualifier=(val)
@sp_name_qualifier = val
end

def idp_sso_target_url
@idp_sso_target_url
end
def idp_sso_target_url=(val)
@idp_sso_target_url = val
end

def idp_cert_fingerprint
@idp_cert_fingerprint
end
def idp_cert_fingerprint=(val)
@idp_cert_fingerprint = val
end

def name_identifier_format
@name_identifier_format
end
def name_identifier_format=(val)
@name_identifier_format = val
end
end
end
1 change: 1 addition & 0 deletions lib/ruby-saml.rb
@@ -0,0 +1 @@
require 'onelogin/saml'
91 changes: 91 additions & 0 deletions lib/xml_sec.rb
@@ -0,0 +1,91 @@
# 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 "xmlcanonicalizer"
require "digest/sha1"

module XMLSecurity

class SignedDocument < REXML::Document

def validate (idp_cert_fingerprint, logger = nil)
# get cert from response
base64_cert = self.elements["//ds:X509Certificate"].text
cert_text = Base64.decode64(base64_cert)
cert = OpenSSL::X509::Certificate.new(cert_text)

# check cert matches registered idp cert
fingerprint = Digest::SHA1.hexdigest(cert.to_der)
valid_flag = fingerprint == idp_cert_fingerprint.gsub(":", "").downcase

return valid_flag if !valid_flag

validate_doc(base64_cert, logger)
end

def validate_doc(base64_cert, logger)
# validate references

# remove signature node
sig_element = XPath.first(self, "//ds:Signature", {"ds"=>"http://www.w3.org/2000/09/xmldsig#"})
sig_element.remove

#check digests
XPath.each(sig_element, "//ds:Reference", {"ds"=>"http://www.w3.org/2000/09/xmldsig#"}) do | ref |

uri = ref.attributes.get_attribute("URI").value
hashed_element = XPath.first(self, "//[@ID='#{uri[1,uri.size]}']")
canoner = XML::Util::XmlCanonicalizer.new(false, true)
canon_hashed_element = canoner.canonicalize(hashed_element)
hash = Base64.encode64(Digest::SHA1.digest(canon_hashed_element)).chomp
digest_value = XPath.first(ref, "//ds:DigestValue", {"ds"=>"http://www.w3.org/2000/09/xmldsig#"}).text

valid_flag = hash == digest_value

return valid_flag if !valid_flag
end

# verify signature
canoner = XML::Util::XmlCanonicalizer.new(false, true)
signed_info_element = XPath.first(sig_element, "//ds:SignedInfo", {"ds"=>"http://www.w3.org/2000/09/xmldsig#"})
canon_string = canoner.canonicalize(signed_info_element)

base64_signature = XPath.first(sig_element, "//ds:SignatureValue", {"ds"=>"http://www.w3.org/2000/09/xmldsig#"}).text
signature = Base64.decode64(base64_signature)

# get certificate object
cert_text = Base64.decode64(base64_cert)
cert = OpenSSL::X509::Certificate.new(cert_text)

valid_flag = cert.public_key.verify(OpenSSL::Digest::SHA1.new, signature, canon_string)

return valid_flag
end

end
end

0 comments on commit f9fac68

Please sign in to comment.