ruby-saml-bekk 0.2.4
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/LICENSE +19 -0
- data/README.rdoc +98 -0
- data/Rakefile +61 -0
- data/VERSION +1 -0
- data/lib/onelogin/saml.rb +6 -0
- data/lib/onelogin/saml/authrequest.rb +33 -0
- data/lib/onelogin/saml/entity_descriptor.rb +19 -0
- data/lib/onelogin/saml/entity_descriptor.xml.erb +14 -0
- data/lib/onelogin/saml/response.rb +56 -0
- data/lib/onelogin/saml/settings.rb +6 -0
- data/lib/ruby-saml.rb +5 -0
- data/lib/xml_security.rb +91 -0
- data/ruby-saml.gemspec +71 -0
- data/test/response.txt +1 -0
- data/test/ruby-saml_test.rb +136 -0
- data/test/test_helper.rb +14 -0
- data/test/xml_security_test.rb +16 -0
- metadata +139 -0
data/.document
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2010 OneLogin, LLC
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
= Ruby SAML
|
2
|
+
|
3
|
+
The Ruby SAML library is for implementing the client side of a SAML authorization, i.e. it provides a means for managing authorization initialization and confirmation requests from identity providers.
|
4
|
+
|
5
|
+
SAML authorization is a two step process and you are expected to implement support for both.
|
6
|
+
|
7
|
+
== The initialization phase
|
8
|
+
|
9
|
+
This is the first request you will get from the identity provider. It will hit your application at a specific URL (that you've announced as being your SAML initialization point). The response to this initialization, is a redirect back to the identity provider, which can look something like this (ignore the saml_settings method call for now):
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
request = Onelogin::Saml::Authrequest.new
|
13
|
+
redirect_to(request.create(saml_settings))
|
14
|
+
end
|
15
|
+
|
16
|
+
Once you've redirected back to the identity provider, it will ensure that the user has been authorized and redirect back to your application for final consumption, this is can look something like this (the authorize_success and authorize_failure methods are specific to your application):
|
17
|
+
|
18
|
+
def consume
|
19
|
+
response = Onelogin::Saml::Response.new(params[:SAMLResponse])
|
20
|
+
response.settings = saml_settings
|
21
|
+
|
22
|
+
if response.is_valid? && user = current_account.users.find_by_email(response.name_id)
|
23
|
+
authorize_success(user)
|
24
|
+
else
|
25
|
+
authorize_failure(user)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
In the above there are a few assumptions in place, one being that the response.name_id is an email address. This is all handled with how you specify the settings that are in play via the saml_settings method. That could be implemented along the lines of this:
|
30
|
+
|
31
|
+
def saml_settings
|
32
|
+
settings = Onelogin::Saml::Settings.new
|
33
|
+
|
34
|
+
settings.assertion_consumer_service_url = "http://#{request.host}/saml/finalize"
|
35
|
+
settings.issuer = request.host
|
36
|
+
settings.idp_sso_target_url = "https://app.onelogin.com/saml/signon/#{OneLoginAppId}"
|
37
|
+
settings.idp_cert_fingerprint = OneLoginAppCertFingerPrint
|
38
|
+
settings.name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
|
39
|
+
|
40
|
+
settings
|
41
|
+
end
|
42
|
+
|
43
|
+
What's left at this point, is to wrap it all up in a controller and point the initialization and consumption URLs in OneLogin at that. A full controller example could look like this:
|
44
|
+
|
45
|
+
# This controller expects you to use the URLs /saml/initialize and /saml/consume in your OneLogin application.
|
46
|
+
class SamlController < ApplicationController
|
47
|
+
def initialize
|
48
|
+
request = Onelogin::Saml::Authrequest.new
|
49
|
+
redirect_to(request.create(saml_settings))
|
50
|
+
end
|
51
|
+
|
52
|
+
def consume
|
53
|
+
response = Onelogin::Saml::Response.new(params[:SAMLResponse])
|
54
|
+
response.settings = saml_settings
|
55
|
+
|
56
|
+
if response.is_valid? && user = current_account.users.find_by_email(response.name_id)
|
57
|
+
authorize_success(user)
|
58
|
+
else
|
59
|
+
authorize_failure(user)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def saml_settings
|
66
|
+
settings = Onelogin::Saml::Settings.new
|
67
|
+
|
68
|
+
settings.assertion_consumer_service_url = "http://#{request.host}/saml/consume"
|
69
|
+
settings.issuer = request.host
|
70
|
+
settings.idp_sso_target_url = "https://app.onelogin.com/saml/signon/#{OneLoginAppId}"
|
71
|
+
settings.idp_cert_fingerprint = OneLoginAppCertFingerPrint
|
72
|
+
settings.name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
|
73
|
+
|
74
|
+
settings
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
If are using saml:AttributeStatement to transfare metadata, like the user name, you can access all the attributes through response.attributes. It
|
79
|
+
contains all the saml:AttributeStatement with its 'Name' as a indifferent key and the one saml:AttributeValue as value.
|
80
|
+
|
81
|
+
response = Onelogin::Saml::Response.new(params[:SAMLResponse])
|
82
|
+
response.settings = saml_settings
|
83
|
+
|
84
|
+
response.attributes[:username]
|
85
|
+
|
86
|
+
|
87
|
+
= Full Example
|
88
|
+
|
89
|
+
Please check https://github.com/onelogin/ruby-saml-example for a very basic sample Rails application using this gem.
|
90
|
+
|
91
|
+
== Note on Patches/Pull Requests
|
92
|
+
|
93
|
+
* Fork the project.
|
94
|
+
* Make your feature addition or bug fix.
|
95
|
+
* Add tests for it. This is important so I don't break it in a
|
96
|
+
future version unintentionally.
|
97
|
+
* Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
98
|
+
* Send me a pull request. Bonus points for topic branches.
|
data/Rakefile
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "ruby-saml-bekk"
|
8
|
+
gem.summary = %Q{SAML Ruby Tookit}
|
9
|
+
gem.description = %Q{SAML toolkit for Ruby on Rails}
|
10
|
+
gem.email = "support@onelogin.com"
|
11
|
+
gem.homepage = "http://github.com/onelogin/ruby-saml"
|
12
|
+
gem.authors = ["OneLogin LLC"]
|
13
|
+
gem.add_dependency("xmlcanonicalizer","= 0.1.0")
|
14
|
+
gem.add_dependency("uuid","= 2.3.1")
|
15
|
+
gem.add_development_dependency "shoulda"
|
16
|
+
gem.add_development_dependency "mocha"
|
17
|
+
#gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
18
|
+
end
|
19
|
+
Jeweler::GemcutterTasks.new
|
20
|
+
rescue LoadError
|
21
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
22
|
+
end
|
23
|
+
|
24
|
+
#not being used yet.
|
25
|
+
require 'rake/testtask'
|
26
|
+
Rake::TestTask.new(:test) do |test|
|
27
|
+
test.libs << 'lib' << 'test'
|
28
|
+
test.pattern = 'test/**/*_test.rb'
|
29
|
+
test.verbose = true
|
30
|
+
end
|
31
|
+
|
32
|
+
begin
|
33
|
+
require 'rcov/rcovtask'
|
34
|
+
Rcov::RcovTask.new do |test|
|
35
|
+
test.libs << 'test'
|
36
|
+
test.pattern = 'test/**/*_test.rb'
|
37
|
+
test.verbose = true
|
38
|
+
end
|
39
|
+
rescue LoadError
|
40
|
+
task :rcov do
|
41
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
task :test => :check_dependencies
|
46
|
+
|
47
|
+
task :default => :test
|
48
|
+
|
49
|
+
# require 'rake/rdoctask'
|
50
|
+
# Rake::RDocTask.new do |rdoc|
|
51
|
+
# if File.exist?('VERSION')
|
52
|
+
# version = File.read('VERSION')
|
53
|
+
# else
|
54
|
+
# version = ""
|
55
|
+
# end
|
56
|
+
|
57
|
+
# rdoc.rdoc_dir = 'rdoc'
|
58
|
+
# rdoc.title = "ruby-saml #{version}"
|
59
|
+
# rdoc.rdoc_files.include('README*')
|
60
|
+
# rdoc.rdoc_files.include('lib/**/*.rb')
|
61
|
+
#end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.2.4
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require "base64"
|
2
|
+
require "uuid"
|
3
|
+
require "zlib"
|
4
|
+
require "cgi"
|
5
|
+
|
6
|
+
module Onelogin::Saml
|
7
|
+
class Authrequest
|
8
|
+
def create(settings, params = {})
|
9
|
+
uuid = UUID.new.generate
|
10
|
+
time = Time.now.utc.strftime("%Y-%m-%dT%H:%M:%SZ")
|
11
|
+
|
12
|
+
request =
|
13
|
+
"<samlp:AuthnRequest xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" ID=\"#{uuid}\" Version=\"2.0\" IssueInstant=\"#{time}\" ProtocolBinding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\" AssertionConsumerServiceURL=\"#{settings.assertion_consumer_service_url}\">" +
|
14
|
+
"<saml:Issuer xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\">#{settings.issuer}</saml:Issuer>\n" +
|
15
|
+
"<samlp:NameIDPolicy xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" Format=\"#{settings.name_identifier_format}\" AllowCreate=\"true\"></samlp:NameIDPolicy>\n" +
|
16
|
+
"<samlp:RequestedAuthnContext xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" Comparison=\"exact\">" +
|
17
|
+
"<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" +
|
18
|
+
"</samlp:AuthnRequest>"
|
19
|
+
|
20
|
+
deflated_request = Zlib::Deflate.deflate(request, 9)[2..-5]
|
21
|
+
base64_request = Base64.encode64(deflated_request)
|
22
|
+
encoded_request = CGI.escape(base64_request)
|
23
|
+
request_params = "?SAMLRequest=" + encoded_request
|
24
|
+
|
25
|
+
params.each_pair do |key, value|
|
26
|
+
request_params << "&#{key}=#{CGI.escape(value.to_s)}"
|
27
|
+
end
|
28
|
+
|
29
|
+
settings.idp_sso_target_url + request_params
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'erb'
|
2
|
+
|
3
|
+
module Onelogin::Saml
|
4
|
+
class EntityDescription
|
5
|
+
def initialize()
|
6
|
+
url = "entity_descriptor.xml.erb"
|
7
|
+
@template = File.read(File.join(File.dirname(__FILE__), url))
|
8
|
+
end
|
9
|
+
|
10
|
+
def generate(values)
|
11
|
+
entity_id = values["entity_id"]
|
12
|
+
name_id_format = values["name_id_format"]
|
13
|
+
assertion_consumer_service_location = values["assertion_consumer_service_location"]
|
14
|
+
|
15
|
+
erb = ERB.new(@template)
|
16
|
+
erb.result(binding)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
2
|
+
|
3
|
+
<EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" entityID="<%= entity_id %>">
|
4
|
+
<SPSSODescriptor AuthnRequestsSigned="false" WantAssertionsSigned="true" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
|
5
|
+
<NameIDFormat><%= name_id_format %></NameIDFormat>
|
6
|
+
<AssertionConsumerService
|
7
|
+
isDefault="true"
|
8
|
+
index="0"
|
9
|
+
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
|
10
|
+
Location="<%= assertion_consumer_service_location %>"/>
|
11
|
+
</SPSSODescriptor>
|
12
|
+
<RoleDescriptor xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:query="urn:oasis:names:tc:SAML:metadata:ext:query" xsi:type="query:AttributeQueryDescriptorType" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"/>
|
13
|
+
<XACMLAuthzDecisionQueryDescriptor WantAssertionsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"/>
|
14
|
+
</EntityDescriptor>
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require "xml_security"
|
2
|
+
require "time"
|
3
|
+
|
4
|
+
module Onelogin::Saml
|
5
|
+
class Response
|
6
|
+
attr_accessor :response, :document, :logger, :settings
|
7
|
+
|
8
|
+
def initialize(response)
|
9
|
+
raise ArgumentError.new("Response cannot be nil") if response.nil?
|
10
|
+
self.response = response
|
11
|
+
self.document = XMLSecurity::SignedDocument.new(Base64.decode64(response))
|
12
|
+
end
|
13
|
+
|
14
|
+
def is_valid?
|
15
|
+
return false if response.empty?
|
16
|
+
return false if settings.nil?
|
17
|
+
return false if settings.idp_cert_fingerprint.nil?
|
18
|
+
|
19
|
+
document.validate(settings.idp_cert_fingerprint, logger)
|
20
|
+
end
|
21
|
+
|
22
|
+
# The value of the user identifier as designated by the initialization request response
|
23
|
+
def name_id
|
24
|
+
@name_id ||= document.elements["/samlp:Response/saml:Assertion/saml:Subject/saml:NameID"].text
|
25
|
+
end
|
26
|
+
|
27
|
+
# A hash of alle the attributes with the response. Assuming there is onlye one value for each key
|
28
|
+
def attributes
|
29
|
+
saml_attribute_statements = document.elements["/samlp:Response/saml:Assertion/saml:AttributeStatement"].elements
|
30
|
+
statements = saml_attribute_statements.map do |child|
|
31
|
+
child.attributes.map do |key, attribute|
|
32
|
+
[attribute, child.elements.first.text]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
hash = Hash[statements.flatten(1)]
|
37
|
+
@attributes ||= make_hash_access_indiferent(hash)
|
38
|
+
end
|
39
|
+
|
40
|
+
# When this user session should expire at latest
|
41
|
+
def session_expires_at
|
42
|
+
@expires_at ||= Time.parse(document.elements["/samlp:Response/saml:Assertion/saml:AuthnStatement"].attributes["SessionNotOnOrAfter"])
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def make_hash_access_indiferent(hash)
|
48
|
+
sym_hash = {}
|
49
|
+
hash.each do |key, value|
|
50
|
+
sym_hash[key.intern] = value
|
51
|
+
end
|
52
|
+
|
53
|
+
sym_hash.merge(hash)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
data/lib/ruby-saml.rb
ADDED
data/lib/xml_security.rb
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
# The contents of this file are subject to the terms
|
2
|
+
# of the Common Development and Distribution License
|
3
|
+
# (the License). You may not use this file except in
|
4
|
+
# compliance with the License.
|
5
|
+
#
|
6
|
+
# You can obtain a copy of the License at
|
7
|
+
# https://opensso.dev.java.net/public/CDDLv1.0.html or
|
8
|
+
# opensso/legal/CDDLv1.0.txt
|
9
|
+
# See the License for the specific language governing
|
10
|
+
# permission and limitations under the License.
|
11
|
+
#
|
12
|
+
# When distributing Covered Code, include this CDDL
|
13
|
+
# Header Notice in each file and include the License file
|
14
|
+
# at opensso/legal/CDDLv1.0.txt.
|
15
|
+
# If applicable, add the following below the CDDL Header,
|
16
|
+
# with the fields enclosed by brackets [] replaced by
|
17
|
+
# your own identifying information:
|
18
|
+
# "Portions Copyrighted [year] [name of copyright owner]"
|
19
|
+
#
|
20
|
+
# $Id: xml_sec.rb,v 1.6 2007/10/24 00:28:41 todddd Exp $
|
21
|
+
#
|
22
|
+
# Copyright 2007 Sun Microsystems Inc. All Rights Reserved
|
23
|
+
# Portions Copyrighted 2007 Todd W Saxton.
|
24
|
+
|
25
|
+
require 'rubygems'
|
26
|
+
require "rexml/document"
|
27
|
+
require "rexml/xpath"
|
28
|
+
require "openssl"
|
29
|
+
require "xmlcanonicalizer"
|
30
|
+
require "digest/sha1"
|
31
|
+
|
32
|
+
module XMLSecurity
|
33
|
+
|
34
|
+
class SignedDocument < REXML::Document
|
35
|
+
|
36
|
+
def validate (idp_cert_fingerprint, logger = nil)
|
37
|
+
# get cert from response
|
38
|
+
base64_cert = self.elements["//ds:X509Certificate"].text
|
39
|
+
cert_text = Base64.decode64(base64_cert)
|
40
|
+
cert = OpenSSL::X509::Certificate.new(cert_text)
|
41
|
+
|
42
|
+
# check cert matches registered idp cert
|
43
|
+
fingerprint = Digest::SHA1.hexdigest(cert.to_der)
|
44
|
+
valid_flag = fingerprint == idp_cert_fingerprint.gsub(":", "").downcase
|
45
|
+
|
46
|
+
return valid_flag if !valid_flag
|
47
|
+
|
48
|
+
validate_doc(base64_cert, logger)
|
49
|
+
end
|
50
|
+
|
51
|
+
def validate_doc(base64_cert, logger)
|
52
|
+
# validate references
|
53
|
+
|
54
|
+
# remove signature node
|
55
|
+
sig_element = REXML::XPath.first(self, "//ds:Signature", {"ds"=>"http://www.w3.org/2000/09/xmldsig#"})
|
56
|
+
sig_element.remove
|
57
|
+
|
58
|
+
#check digests
|
59
|
+
REXML::XPath.each(sig_element, "//ds:Reference", {"ds"=>"http://www.w3.org/2000/09/xmldsig#"}) do | ref |
|
60
|
+
|
61
|
+
uri = ref.attributes.get_attribute("URI").value
|
62
|
+
hashed_element = REXML::XPath.first(self, "//[@ID='#{uri[1,uri.size]}']")
|
63
|
+
canoner = XML::Util::XmlCanonicalizer.new(false, true)
|
64
|
+
canon_hashed_element = canoner.canonicalize(hashed_element)
|
65
|
+
hash = Base64.encode64(Digest::SHA1.digest(canon_hashed_element)).chomp
|
66
|
+
digest_value = REXML::XPath.first(ref, "//ds:DigestValue", {"ds"=>"http://www.w3.org/2000/09/xmldsig#"}).text
|
67
|
+
|
68
|
+
valid_flag = hash == digest_value
|
69
|
+
|
70
|
+
return valid_flag if !valid_flag
|
71
|
+
end
|
72
|
+
|
73
|
+
# verify signature
|
74
|
+
canoner = XML::Util::XmlCanonicalizer.new(false, true)
|
75
|
+
signed_info_element = REXML::XPath.first(sig_element, "//ds:SignedInfo", {"ds"=>"http://www.w3.org/2000/09/xmldsig#"})
|
76
|
+
canon_string = canoner.canonicalize(signed_info_element)
|
77
|
+
|
78
|
+
base64_signature = REXML::XPath.first(sig_element, "//ds:SignatureValue", {"ds"=>"http://www.w3.org/2000/09/xmldsig#"}).text
|
79
|
+
signature = Base64.decode64(base64_signature)
|
80
|
+
|
81
|
+
# get certificate object
|
82
|
+
cert_text = Base64.decode64(base64_cert)
|
83
|
+
cert = OpenSSL::X509::Certificate.new(cert_text)
|
84
|
+
|
85
|
+
valid_flag = cert.public_key.verify(OpenSSL::Digest::SHA1.new, signature, canon_string)
|
86
|
+
|
87
|
+
return valid_flag
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
end
|
data/ruby-saml.gemspec
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{ruby-saml-bekk}
|
8
|
+
s.version = "0.2.3"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["OneLogin LLC"]
|
12
|
+
s.date = %q{2011-02-21}
|
13
|
+
s.description = %q{SAML toolkit for Ruby on Rails}
|
14
|
+
s.email = %q{support@onelogin.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
".gitignore",
|
22
|
+
"LICENSE",
|
23
|
+
"README.rdoc",
|
24
|
+
"Rakefile",
|
25
|
+
"VERSION",
|
26
|
+
"lib/onelogin/saml.rb",
|
27
|
+
"lib/onelogin/saml/authrequest.rb",
|
28
|
+
"lib/onelogin/saml/response.rb",
|
29
|
+
"lib/onelogin/saml/settings.rb",
|
30
|
+
"lib/ruby-saml.rb",
|
31
|
+
"lib/xml_security.rb",
|
32
|
+
"ruby-saml.gemspec",
|
33
|
+
"test/response.txt",
|
34
|
+
"test/ruby-saml_test.rb",
|
35
|
+
"test/test_helper.rb",
|
36
|
+
"test/xml_security_test.rb"
|
37
|
+
]
|
38
|
+
s.homepage = %q{http://github.com/onelogin/ruby-saml}
|
39
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
40
|
+
s.require_paths = ["lib"]
|
41
|
+
s.rubygems_version = %q{1.3.7}
|
42
|
+
s.summary = %q{SAML Ruby Tookit}
|
43
|
+
s.test_files = [
|
44
|
+
"test/ruby-saml_test.rb",
|
45
|
+
"test/test_helper.rb",
|
46
|
+
"test/xml_security_test.rb"
|
47
|
+
]
|
48
|
+
|
49
|
+
if s.respond_to? :specification_version then
|
50
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
51
|
+
s.specification_version = 3
|
52
|
+
|
53
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
54
|
+
s.add_runtime_dependency(%q<xmlcanonicalizer>, ["= 0.1.0"])
|
55
|
+
s.add_runtime_dependency(%q<uuid>, ["= 2.3.1"])
|
56
|
+
s.add_development_dependency(%q<shoulda>, [">= 0"])
|
57
|
+
s.add_development_dependency(%q<mocha>, [">= 0"])
|
58
|
+
else
|
59
|
+
s.add_dependency(%q<xmlcanonicalizer>, ["= 0.1.0"])
|
60
|
+
s.add_dependency(%q<uuid>, ["= 2.3.1"])
|
61
|
+
s.add_dependency(%q<shoulda>, [">= 0"])
|
62
|
+
s.add_dependency(%q<mocha>, [">= 0"])
|
63
|
+
end
|
64
|
+
else
|
65
|
+
s.add_dependency(%q<xmlcanonicalizer>, ["= 0.1.0"])
|
66
|
+
s.add_dependency(%q<uuid>, ["= 2.3.1"])
|
67
|
+
s.add_dependency(%q<shoulda>, [">= 0"])
|
68
|
+
s.add_dependency(%q<mocha>, [">= 0"])
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
data/test/response.txt
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
DQo8c2FtbHA6UmVzcG9uc2UgeG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgeG1sbnM6c2FtbHA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgSUQ9IkdPU0FNTFIxMjkwMTE3NDU3MTc5NCIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTAtMTEtMThUMjE6NTc6MzdaIiBEZXN0aW5hdGlvbj0ie3JlY2lwaWVudH0iPg0KICA8c2FtbHA6U3RhdHVzPg0KICAgIDxzYW1scDpTdGF0dXNDb2RlIFZhbHVlPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6c3RhdHVzOlN1Y2Nlc3MiLz48L3NhbWxwOlN0YXR1cz4NCiAgPHNhbWw6QXNzZXJ0aW9uIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgVmVyc2lvbj0iMi4wIiBJRD0icGZ4YTQ2NTc0ZGYtYjNiMC1hMDZhLTIzYzgtNjM2NDEzMTk4NzcyIiBJc3N1ZUluc3RhbnQ9IjIwMTAtMTEtMThUMjE6NTc6MzdaIj4NCiAgICA8c2FtbDpJc3N1ZXI+aHR0cHM6Ly9hcHAub25lbG9naW4uY29tL3NhbWwvbWV0YWRhdGEvMTM1OTA8L3NhbWw6SXNzdWVyPg0KICAgIDxkczpTaWduYXR1cmUgeG1sbnM6ZHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiPg0KICAgICAgPGRzOlNpZ25lZEluZm8+DQogICAgICAgIDxkczpDYW5vbmljYWxpemF0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+DQogICAgICAgIDxkczpTaWduYXR1cmVNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjcnNhLXNoYTEiLz4NCiAgICAgICAgPGRzOlJlZmVyZW5jZSBVUkk9IiNwZnhhNDY1NzRkZi1iM2IwLWEwNmEtMjNjOC02MzY0MTMxOTg3NzIiPg0KICAgICAgICAgIDxkczpUcmFuc2Zvcm1zPg0KICAgICAgICAgICAgPGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNlbnZlbG9wZWQtc2lnbmF0dXJlIi8+DQogICAgICAgICAgICA8ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+DQogICAgICAgICAgPC9kczpUcmFuc2Zvcm1zPg0KICAgICAgICAgIDxkczpEaWdlc3RNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjc2hhMSIvPg0KICAgICAgICAgIDxkczpEaWdlc3RWYWx1ZT5wSlE3TVMvZWs0S1JSV0dtdi9INDNSZUhZTXM9PC9kczpEaWdlc3RWYWx1ZT4NCiAgICAgICAgPC9kczpSZWZlcmVuY2U+DQogICAgICA8L2RzOlNpZ25lZEluZm8+DQogICAgICA8ZHM6U2lnbmF0dXJlVmFsdWU+eWl2ZUtjUGREcHVETmo2c2hyUTNBQndyL2NBM0NyeUQycGhHL3hMWnN6S1d4VTUvbWxhS3Q4ZXdiWk9kS0t2dE9zMnBIQnk1RHVhM2s5NEFGK3p4R3llbDVnT293bW95WEpyK0FPcitrUE8wdmxpMVY4bzNoUFBVWndSZ1NYNlE5cFMxQ3FRZ2hLaUVhc1J5eWxxcUpVYVBZem1Pek9FOC9YbE1rd2lXbU8wPTwvZHM6U2lnbmF0dXJlVmFsdWU+DQogICAgICA8ZHM6S2V5SW5mbz4NCiAgICAgICAgPGRzOlg1MDlEYXRhPg0KICAgICAgICAgIDxkczpYNTA5Q2VydGlmaWNhdGU+TUlJQnJUQ0NBYUdnQXdJQkFnSUJBVEFEQmdFQU1HY3hDekFKQmdOVkJBWVRBbFZUTVJNd0VRWURWUVFJREFwRFlXeHBabTl5Ym1saE1SVXdFd1lEVlFRSERBeFRZVzUwWVNCTmIyNXBZMkV4RVRBUEJnTlZCQW9NQ0U5dVpVeHZaMmx1TVJrd0Z3WURWUVFEREJCaGNIQXViMjVsYkc5bmFXNHVZMjl0TUI0WERURXdNRE13T1RBNU5UZzBOVm9YRFRFMU1ETXdPVEE1TlRnME5Wb3daekVMTUFrR0ExVUVCaE1DVlZNeEV6QVJCZ05WQkFnTUNrTmhiR2xtYjNKdWFXRXhGVEFUQmdOVkJBY01ERk5oYm5SaElFMXZibWxqWVRFUk1BOEdBMVVFQ2d3SVQyNWxURzluYVc0eEdUQVhCZ05WQkFNTUVHRndjQzV2Ym1Wc2IyZHBiaTVqYjIwd2daOHdEUVlKS29aSWh2Y05BUUVCQlFBRGdZMEFNSUdKQW9HQkFPalN1MWZqUHk4ZDV3NFF5TDEremQ0aEl3MU1ra2ZmNFdZL1RMRzhPWmtVNVlUU1dtbUhQRDVrdllINXVvWFMvNnFRODFxWHBSMndWOENUb3daSlVMZzA5ZGRSZFJuOFFzcWoxRnlPQzVzbEUzeTJiWjJvRnVhNzJvZi80OWZwdWpuRlQ2S25RNjFDQk1xbERvVFFxT1Q2MnZHSjhuUDZNWld2QTZzeHF1ZDVBZ01CQUFFd0F3WUJBQU1CQUE9PTwvZHM6WDUwOUNlcnRpZmljYXRlPg0KICAgICAgICA8L2RzOlg1MDlEYXRhPg0KICAgICAgPC9kczpLZXlJbmZvPg0KICAgIDwvZHM6U2lnbmF0dXJlPg0KICAgIDxzYW1sOlN1YmplY3Q+DQogICAgICA8c2FtbDpOYW1lSUQgRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoxLjE6bmFtZWlkLWZvcm1hdDplbWFpbEFkZHJlc3MiPnN1cHBvcnRAb25lbG9naW4uY29tPC9zYW1sOk5hbWVJRD4NCiAgICAgIDxzYW1sOlN1YmplY3RDb25maXJtYXRpb24gTWV0aG9kPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6Y206YmVhcmVyIj4NCiAgICAgICAgPHNhbWw6U3ViamVjdENvbmZpcm1hdGlvbkRhdGEgTm90T25PckFmdGVyPSIyMDEwLTExLTE4VDIyOjAyOjM3WiIgUmVjaXBpZW50PSJ7cmVjaXBpZW50fSIvPjwvc2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uPg0KICAgIDwvc2FtbDpTdWJqZWN0Pg0KICAgIDxzYW1sOkNvbmRpdGlvbnMgTm90QmVmb3JlPSIyMDEwLTExLTE4VDIxOjUyOjM3WiIgTm90T25PckFmdGVyPSIyMDEwLTExLTE4VDIyOjAyOjM3WiI+DQogICAgICA8c2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPg0KICAgICAgICA8c2FtbDpBdWRpZW5jZT57YXVkaWVuY2V9PC9zYW1sOkF1ZGllbmNlPg0KICAgICAgPC9zYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+DQogICAgPC9zYW1sOkNvbmRpdGlvbnM+DQogICAgPHNhbWw6QXV0aG5TdGF0ZW1lbnQgQXV0aG5JbnN0YW50PSIyMDEwLTExLTE4VDIxOjU3OjM3WiIgU2Vzc2lvbk5vdE9uT3JBZnRlcj0iMjAxMC0xMS0xOVQyMTo1NzozN1oiIFNlc3Npb25JbmRleD0iXzUzMWMzMmQyODNiZGZmN2UwNGU0ODdiY2RiYzRkZDhkIj4NCiAgICAgIDxzYW1sOkF1dGhuQ29udGV4dD4NCiAgICAgICAgPHNhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+dXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFjOmNsYXNzZXM6UGFzc3dvcmQ8L3NhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+DQogICAgICA8L3NhbWw6QXV0aG5Db250ZXh0Pg0KICAgIDwvc2FtbDpBdXRoblN0YXRlbWVudD4NCiAgICA8c2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQ+DQogICAgICA8c2FtbDpBdHRyaWJ1dGUgTmFtZT0idWlkIj4NCiAgICAgICAgPHNhbWw6QXR0cmlidXRlVmFsdWUgeG1sbnM6eHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4c2k6dHlwZT0ieHM6c3RyaW5nIj5kZW1vPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPg0KICAgICAgPC9zYW1sOkF0dHJpYnV0ZT4NCiAgICAgIDxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJhbm90aGVyX3ZhbHVlIj4NCiAgICAgICAgPHNhbWw6QXR0cmlidXRlVmFsdWUgeG1sbnM6eHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4c2k6dHlwZT0ieHM6c3RyaW5nIj52YWx1ZTwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT4NCiAgICAgIDwvc2FtbDpBdHRyaWJ1dGU+DQogICAgPC9zYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD4NCiAgPC9zYW1sOkFzc2VydGlvbj4NCjwvc2FtbHA6UmVzcG9uc2U+DQo=
|
@@ -0,0 +1,136 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class RubySamlTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
context "Settings" do
|
6
|
+
setup do
|
7
|
+
@settings = Onelogin::Saml::Settings.new
|
8
|
+
end
|
9
|
+
should "should provide getters and settings" do
|
10
|
+
accessors = [
|
11
|
+
:assertion_consumer_service_url, :issuer, :sp_name_qualifier, :sp_name_qualifier,
|
12
|
+
:idp_sso_target_url, :idp_cert_fingerprint, :name_identifier_format
|
13
|
+
]
|
14
|
+
|
15
|
+
accessors.each do |accessor|
|
16
|
+
value = Kernel.rand
|
17
|
+
@settings.send("#{accessor}=".to_sym, value)
|
18
|
+
assert_equal value, @settings.send(accessor)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context "Response" do
|
24
|
+
should "provide setter for a logger" do
|
25
|
+
response = Onelogin::Saml::Response.new('')
|
26
|
+
assert response.logger = 'hello'
|
27
|
+
end
|
28
|
+
|
29
|
+
should "raise an exception when response is initialized with nil" do
|
30
|
+
assert_raises(ArgumentError) { Onelogin::Saml::Response.new(nil) }
|
31
|
+
end
|
32
|
+
|
33
|
+
context "#is_valid?" do
|
34
|
+
should "return false when response is initialized with blank data" do
|
35
|
+
response = Onelogin::Saml::Response.new('')
|
36
|
+
assert !response.is_valid?
|
37
|
+
end
|
38
|
+
|
39
|
+
should "return false if settings have not been set" do
|
40
|
+
response = Onelogin::Saml::Response.new(response_document)
|
41
|
+
assert !response.is_valid?
|
42
|
+
end
|
43
|
+
|
44
|
+
should "return true when the response is initialized with valid data" do
|
45
|
+
response = Onelogin::Saml::Response.new(response_document)
|
46
|
+
settings = Onelogin::Saml::Settings.new
|
47
|
+
settings.idp_cert_fingerprint = 'hello'
|
48
|
+
response.settings = settings
|
49
|
+
assert !response.is_valid?
|
50
|
+
document = stub()
|
51
|
+
document.stubs(:validate).returns(true)
|
52
|
+
response.document = document
|
53
|
+
assert response.is_valid?
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context "#name_id" do
|
58
|
+
should "extract the value of the name id element" do
|
59
|
+
response = Onelogin::Saml::Response.new(response_document)
|
60
|
+
assert_equal "support@onelogin.com", response.name_id
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context "#attributes" do
|
65
|
+
should "extract the first attribute in a hash accessed via its symbol" do
|
66
|
+
response = Onelogin::Saml::Response.new(response_document)
|
67
|
+
assert_equal "demo", response.attributes[:uid]
|
68
|
+
end
|
69
|
+
|
70
|
+
should "extract the first attribute in a hash accessed via its name" do
|
71
|
+
response = Onelogin::Saml::Response.new(response_document)
|
72
|
+
assert_equal "demo", response.attributes["uid"]
|
73
|
+
end
|
74
|
+
|
75
|
+
should "extract all attributes" do
|
76
|
+
response = Onelogin::Saml::Response.new(response_document)
|
77
|
+
assert_equal "demo", response.attributes[:uid]
|
78
|
+
assert_equal "value", response.attributes[:another_value]
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
context "#session_expires_at" do
|
83
|
+
should "extract the value of the SessionNotOnOrAfter attribute" do
|
84
|
+
response = Onelogin::Saml::Response.new(response_document)
|
85
|
+
assert response.session_expires_at.is_a?(Time)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
context "Authrequest" do
|
91
|
+
should "create the SAMLRequest URL parameter" do
|
92
|
+
settings = Onelogin::Saml::Settings.new
|
93
|
+
settings.idp_sso_target_url = "http://stuff.com"
|
94
|
+
auth_url = Onelogin::Saml::Authrequest.new.create(settings)
|
95
|
+
assert auth_url =~ /^http:\/\/stuff\.com\?SAMLRequest=/
|
96
|
+
payload = CGI.unescape(auth_url.split("=").last)
|
97
|
+
end
|
98
|
+
|
99
|
+
should "accept extra parameters" do
|
100
|
+
settings = Onelogin::Saml::Settings.new
|
101
|
+
settings.idp_sso_target_url = "http://stuff.com"
|
102
|
+
|
103
|
+
auth_url = Onelogin::Saml::Authrequest.new.create(settings, { :hello => "there" })
|
104
|
+
assert auth_url =~ /&hello=there$/
|
105
|
+
|
106
|
+
auth_url = Onelogin::Saml::Authrequest.new.create(settings, { :hello => nil })
|
107
|
+
assert auth_url =~ /&hello=$/
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
context "EntityDescription" do
|
112
|
+
should "generate a correct entity descriptor" do
|
113
|
+
descriptor = Onelogin::Saml::EntityDescription.new
|
114
|
+
xml = descriptor.generate({
|
115
|
+
"entity_id" => "http://test.no/",
|
116
|
+
"name_id_format" => "urn:oasis:names:tc:SAML:2.0:nameid-format:transient",
|
117
|
+
"assertion_consumer_service_location" => "http://localhost:3000/saml/consume"
|
118
|
+
})
|
119
|
+
|
120
|
+
assert_equal xml, "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>
|
121
|
+
|
122
|
+
<EntityDescriptor xmlns=\"urn:oasis:names:tc:SAML:2.0:metadata\" entityID=\"http://test.no/\">
|
123
|
+
<SPSSODescriptor AuthnRequestsSigned=\"false\" WantAssertionsSigned=\"true\" protocolSupportEnumeration=\"urn:oasis:names:tc:SAML:2.0:protocol\">
|
124
|
+
<NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</NameIDFormat>
|
125
|
+
<AssertionConsumerService
|
126
|
+
isDefault=\"true\"
|
127
|
+
index=\"0\"
|
128
|
+
Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\"
|
129
|
+
Location=\"http://localhost:3000/saml/consume\"/>
|
130
|
+
</SPSSODescriptor>
|
131
|
+
<RoleDescriptor xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:query=\"urn:oasis:names:tc:SAML:metadata:ext:query\" xsi:type=\"query:AttributeQueryDescriptorType\" protocolSupportEnumeration=\"urn:oasis:names:tc:SAML:2.0:protocol\"/>
|
132
|
+
<XACMLAuthzDecisionQueryDescriptor WantAssertionsSigned=\"false\" protocolSupportEnumeration=\"urn:oasis:names:tc:SAML:2.0:protocol\"/>
|
133
|
+
</EntityDescriptor>"
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'test/unit'
|
3
|
+
require 'shoulda'
|
4
|
+
require 'mocha'
|
5
|
+
|
6
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
7
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
8
|
+
require 'ruby-saml'
|
9
|
+
|
10
|
+
class Test::Unit::TestCase
|
11
|
+
def response_document
|
12
|
+
@response_document ||= File.read(File.join(File.dirname(__FILE__), 'response.txt'))
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'xml_security'
|
3
|
+
|
4
|
+
class XmlSecurityTest < Test::Unit::TestCase
|
5
|
+
include XMLSecurity
|
6
|
+
context "XmlSecurity" do
|
7
|
+
setup do
|
8
|
+
@document = XMLSecurity::SignedDocument.new(Base64.decode64(response_document))
|
9
|
+
end
|
10
|
+
|
11
|
+
should "should run validate without throwing NS related exceptions" do
|
12
|
+
base64cert = @document.elements["//ds:X509Certificate"].text
|
13
|
+
@document.validate_doc(base64cert, nil)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
metadata
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ruby-saml-bekk
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 2
|
8
|
+
- 4
|
9
|
+
version: 0.2.4
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- OneLogin LLC
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2011-03-04 00:00:00 +01:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: xmlcanonicalizer
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - "="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 0
|
30
|
+
- 1
|
31
|
+
- 0
|
32
|
+
version: 0.1.0
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: uuid
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - "="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
segments:
|
44
|
+
- 2
|
45
|
+
- 3
|
46
|
+
- 1
|
47
|
+
version: 2.3.1
|
48
|
+
type: :runtime
|
49
|
+
version_requirements: *id002
|
50
|
+
- !ruby/object:Gem::Dependency
|
51
|
+
name: shoulda
|
52
|
+
prerelease: false
|
53
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
54
|
+
none: false
|
55
|
+
requirements:
|
56
|
+
- - ">="
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
segments:
|
59
|
+
- 0
|
60
|
+
version: "0"
|
61
|
+
type: :development
|
62
|
+
version_requirements: *id003
|
63
|
+
- !ruby/object:Gem::Dependency
|
64
|
+
name: mocha
|
65
|
+
prerelease: false
|
66
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
67
|
+
none: false
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
segments:
|
72
|
+
- 0
|
73
|
+
version: "0"
|
74
|
+
type: :development
|
75
|
+
version_requirements: *id004
|
76
|
+
description: SAML toolkit for Ruby on Rails
|
77
|
+
email: support@onelogin.com
|
78
|
+
executables: []
|
79
|
+
|
80
|
+
extensions: []
|
81
|
+
|
82
|
+
extra_rdoc_files:
|
83
|
+
- LICENSE
|
84
|
+
- README.rdoc
|
85
|
+
files:
|
86
|
+
- .document
|
87
|
+
- LICENSE
|
88
|
+
- README.rdoc
|
89
|
+
- Rakefile
|
90
|
+
- VERSION
|
91
|
+
- lib/onelogin/saml.rb
|
92
|
+
- lib/onelogin/saml/authrequest.rb
|
93
|
+
- lib/onelogin/saml/entity_descriptor.rb
|
94
|
+
- lib/onelogin/saml/entity_descriptor.xml.erb
|
95
|
+
- lib/onelogin/saml/response.rb
|
96
|
+
- lib/onelogin/saml/settings.rb
|
97
|
+
- lib/ruby-saml.rb
|
98
|
+
- lib/xml_security.rb
|
99
|
+
- ruby-saml.gemspec
|
100
|
+
- test/response.txt
|
101
|
+
- test/ruby-saml_test.rb
|
102
|
+
- test/test_helper.rb
|
103
|
+
- test/xml_security_test.rb
|
104
|
+
has_rdoc: true
|
105
|
+
homepage: http://github.com/onelogin/ruby-saml
|
106
|
+
licenses: []
|
107
|
+
|
108
|
+
post_install_message:
|
109
|
+
rdoc_options: []
|
110
|
+
|
111
|
+
require_paths:
|
112
|
+
- lib
|
113
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
114
|
+
none: false
|
115
|
+
requirements:
|
116
|
+
- - ">="
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
segments:
|
119
|
+
- 0
|
120
|
+
version: "0"
|
121
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
122
|
+
none: false
|
123
|
+
requirements:
|
124
|
+
- - ">="
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
segments:
|
127
|
+
- 0
|
128
|
+
version: "0"
|
129
|
+
requirements: []
|
130
|
+
|
131
|
+
rubyforge_project:
|
132
|
+
rubygems_version: 1.3.7
|
133
|
+
signing_key:
|
134
|
+
specification_version: 3
|
135
|
+
summary: SAML Ruby Tookit
|
136
|
+
test_files:
|
137
|
+
- test/ruby-saml_test.rb
|
138
|
+
- test/test_helper.rb
|
139
|
+
- test/xml_security_test.rb
|