forked from SAML-Toolkits/ruby-saml
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Marcelo de Moraes Serpa
committed
Jul 20, 2010
1 parent
a771d01
commit f9fac68
Showing
7 changed files
with
211 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
0.0.0 | ||
0.0.1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
require 'onelogin/saml/authrequest' | ||
require 'onelogin/saml/response' | ||
require 'onelogin/saml/settings' | ||
|
||
module Onelogin::Saml | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
require 'onelogin/saml' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |