ruby-saml 0.4.7 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of ruby-saml might be problematic. Click here for more details.
- data/README.rdoc +25 -2
- data/Rakefile +1 -22
- data/lib/onelogin/ruby-saml/authrequest.rb +74 -0
- data/lib/onelogin/ruby-saml/logging.rb +22 -0
- data/lib/onelogin/ruby-saml/metadata.rb +47 -0
- data/lib/onelogin/ruby-saml/response.rb +146 -0
- data/lib/onelogin/ruby-saml/settings.rb +9 -0
- data/lib/onelogin/{saml → ruby-saml}/validation_error.rb +0 -0
- data/lib/onelogin/ruby-saml/version.rb +5 -0
- data/lib/ruby-saml.rb +7 -5
- data/lib/xml_security.rb +7 -3
- data/ruby-saml.gemspec +7 -43
- data/test/request_test.rb +20 -0
- data/test/response_test.rb +29 -1
- data/test/responses/response_with_ampersands.xml +139 -0
- data/test/responses/response_with_ampersands.xml.base64 +93 -0
- data/test/responses/wrapped_response_2.xml.base64 +150 -0
- data/test/test_helper.rb +8 -0
- metadata +28 -26
- data/lib/onelogin/saml.rb +0 -5
- data/lib/onelogin/saml/authrequest.rb +0 -33
- data/lib/onelogin/saml/response.rb +0 -137
- data/lib/onelogin/saml/settings.rb +0 -6
data/README.rdoc
CHANGED
@@ -36,7 +36,9 @@ In the above there are a few assumptions in place, one being that the response.n
|
|
36
36
|
settings.idp_sso_target_url = "https://app.onelogin.com/saml/signon/#{OneLoginAppId}"
|
37
37
|
settings.idp_cert_fingerprint = OneLoginAppCertFingerPrint
|
38
38
|
settings.name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
|
39
|
-
|
39
|
+
# Optional for most SAML IdPs
|
40
|
+
settings.authn_context = "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport"
|
41
|
+
|
40
42
|
settings
|
41
43
|
end
|
42
44
|
|
@@ -70,7 +72,9 @@ What's left at this point, is to wrap it all up in a controller and point the in
|
|
70
72
|
settings.idp_sso_target_url = "https://app.onelogin.com/saml/signon/#{OneLoginAppId}"
|
71
73
|
settings.idp_cert_fingerprint = OneLoginAppCertFingerPrint
|
72
74
|
settings.name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
|
73
|
-
|
75
|
+
# Optional for most SAML IdPs
|
76
|
+
settings.authn_context = "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport"
|
77
|
+
|
74
78
|
settings
|
75
79
|
end
|
76
80
|
end
|
@@ -83,6 +87,25 @@ contains all the saml:AttributeStatement with its 'Name' as a indifferent key an
|
|
83
87
|
|
84
88
|
response.attributes[:username]
|
85
89
|
|
90
|
+
== Service Provider Metadata
|
91
|
+
|
92
|
+
To form a trusted pair relationship with the IdP, the SP (you) need to provide metadata XML
|
93
|
+
to the IdP for various good reasons. (Caching, certificate lookups, relying party permissions, etc)
|
94
|
+
|
95
|
+
The class Onelogin::Saml::Metdata takes care of this by reading the Settings and returning XML. All
|
96
|
+
you have to do is add a controller to return the data, then give this URL to the IdP administrator.
|
97
|
+
The metdata will be polled by the IdP every few minutes, so updating your settings should propagate
|
98
|
+
to the IdP settings.
|
99
|
+
|
100
|
+
class SamlController < ApplicationController
|
101
|
+
# ... the rest of your controller definitions ...
|
102
|
+
def metadata
|
103
|
+
settings = Account.get_saml_settings
|
104
|
+
meta = Onelogin::Saml::Metadata.new
|
105
|
+
render :xml => meta.create(settings)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
86
109
|
|
87
110
|
= Full Example
|
88
111
|
|
data/Rakefile
CHANGED
@@ -1,27 +1,6 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'rake'
|
3
3
|
|
4
|
-
begin
|
5
|
-
require 'jeweler'
|
6
|
-
Jeweler::Tasks.new do |gem|
|
7
|
-
gem.name = "ruby-saml"
|
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("canonix","~> 0.1")
|
14
|
-
gem.add_dependency("uuid","~> 2.3")
|
15
|
-
gem.add_development_dependency "shoulda"
|
16
|
-
gem.add_development_dependency "ruby-debug"
|
17
|
-
gem.add_development_dependency "mocha"
|
18
|
-
#gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
19
|
-
end
|
20
|
-
Jeweler::GemcutterTasks.new
|
21
|
-
rescue LoadError
|
22
|
-
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
23
|
-
end
|
24
|
-
|
25
4
|
#not being used yet.
|
26
5
|
require 'rake/testtask'
|
27
6
|
Rake::TestTask.new(:test) do |test|
|
@@ -43,7 +22,7 @@ rescue LoadError
|
|
43
22
|
end
|
44
23
|
end
|
45
24
|
|
46
|
-
task :test
|
25
|
+
task :test
|
47
26
|
|
48
27
|
task :default => :test
|
49
28
|
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require "base64"
|
2
|
+
require "uuid"
|
3
|
+
require "zlib"
|
4
|
+
require "cgi"
|
5
|
+
require "rexml/document"
|
6
|
+
require "rexml/xpath"
|
7
|
+
|
8
|
+
module Onelogin
|
9
|
+
module Saml
|
10
|
+
include REXML
|
11
|
+
class Authrequest
|
12
|
+
def create(settings, params = {})
|
13
|
+
uuid = "_" + UUID.new.generate
|
14
|
+
time = Time.now.utc.strftime("%Y-%m-%dT%H:%M:%SZ")
|
15
|
+
# Create AuthnRequest root element using REXML
|
16
|
+
request_doc = REXML::Document.new
|
17
|
+
|
18
|
+
root = request_doc.add_element "samlp:AuthnRequest", { "xmlns:samlp" => "urn:oasis:names:tc:SAML:2.0:protocol" }
|
19
|
+
root.attributes['ID'] = uuid
|
20
|
+
root.attributes['IssueInstant'] = time
|
21
|
+
root.attributes['Version'] = "2.0"
|
22
|
+
|
23
|
+
# Conditionally defined elements based on settings
|
24
|
+
if settings.assertion_consumer_service_url != nil
|
25
|
+
root.attributes["AssertionConsumerServiceURL"] = settings.assertion_consumer_service_url
|
26
|
+
end
|
27
|
+
if settings.issuer != nil
|
28
|
+
issuer = root.add_element "saml:Issuer", { "xmlns:saml" => "urn:oasis:names:tc:SAML:2.0:assertion" }
|
29
|
+
issuer.text = settings.issuer
|
30
|
+
end
|
31
|
+
if settings.name_identifier_format != nil
|
32
|
+
root.add_element "samlp:NameIDPolicy", {
|
33
|
+
"xmlns:samlp" => "urn:oasis:names:tc:SAML:2.0:protocol",
|
34
|
+
# Might want to make AllowCreate a setting?
|
35
|
+
"AllowCreate" => "true",
|
36
|
+
"Format" => settings.name_identifier_format
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
# BUG fix here -- if an authn_context is defined, add the tags with an "exact"
|
41
|
+
# match required for authentication to succeed. If this is not defined,
|
42
|
+
# the IdP will choose default rules for authentication. (Shibboleth IdP)
|
43
|
+
if settings.authn_context != nil
|
44
|
+
requested_context = root.add_element "samlp:RequestedAuthnContext", {
|
45
|
+
"xmlns:samlp" => "urn:oasis:names:tc:SAML:2.0:protocol",
|
46
|
+
"Comparison" => "exact",
|
47
|
+
}
|
48
|
+
class_ref = requested_context.add_element "saml:AuthnContextClassRef", {
|
49
|
+
"xmlns:saml" => "urn:oasis:names:tc:SAML:2.0:assertion",
|
50
|
+
}
|
51
|
+
class_ref.text = settings.authn_context
|
52
|
+
end
|
53
|
+
|
54
|
+
request = ""
|
55
|
+
request_doc.write(request)
|
56
|
+
|
57
|
+
Logging.debug "Created AuthnRequest: #{request}"
|
58
|
+
|
59
|
+
deflated_request = Zlib::Deflate.deflate(request, 9)[2..-5]
|
60
|
+
base64_request = Base64.encode64(deflated_request)
|
61
|
+
encoded_request = CGI.escape(base64_request)
|
62
|
+
params_prefix = (settings.idp_sso_target_url =~ /\?/) ? '&' : '?'
|
63
|
+
request_params = "#{params_prefix}SAMLRequest=#{encoded_request}"
|
64
|
+
|
65
|
+
params.each_pair do |key, value|
|
66
|
+
request_params << "&#{key}=#{CGI.escape(value.to_s)}"
|
67
|
+
end
|
68
|
+
|
69
|
+
settings.idp_sso_target_url + request_params
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# Simplistic log class when we're running in Rails
|
2
|
+
module Onelogin
|
3
|
+
module Saml
|
4
|
+
class Logging
|
5
|
+
def self.debug(message)
|
6
|
+
if defined? Rails
|
7
|
+
Rails.logger.debug message
|
8
|
+
else
|
9
|
+
puts message
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.info(message)
|
14
|
+
if defined? Rails
|
15
|
+
Rails.logger.info message
|
16
|
+
else
|
17
|
+
puts message
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require "rexml/document"
|
2
|
+
require "rexml/xpath"
|
3
|
+
require "uri"
|
4
|
+
|
5
|
+
# Class to return SP metadata based on the settings requested.
|
6
|
+
# Return this XML in a controller, then give that URL to the the
|
7
|
+
# IdP administrator. The IdP will poll the URL and your settings
|
8
|
+
# will be updated automatically
|
9
|
+
module Onelogin
|
10
|
+
module Saml
|
11
|
+
include REXML
|
12
|
+
class Metadata
|
13
|
+
def generate(settings)
|
14
|
+
meta_doc = REXML::Document.new
|
15
|
+
root = meta_doc.add_element "md:EntityDescriptor", {
|
16
|
+
"xmlns:md" => "urn:oasis:names:tc:SAML:2.0:metadata"
|
17
|
+
}
|
18
|
+
sp_sso = root.add_element "md:SPSSODescriptor", {
|
19
|
+
"protocolSupportEnumeration" => "urn:oasis:names:tc:SAML:2.0:protocol"
|
20
|
+
}
|
21
|
+
if settings.issuer != nil
|
22
|
+
root.attributes["entityID"] = settings.issuer
|
23
|
+
end
|
24
|
+
if settings.name_identifier_format != nil
|
25
|
+
name_id = sp_sso.add_element "md:NameIDFormat"
|
26
|
+
name_id.text = settings.name_identifier_format
|
27
|
+
end
|
28
|
+
if settings.assertion_consumer_service_url != nil
|
29
|
+
sp_sso.add_element "md:AssertionConsumerService", {
|
30
|
+
# Add this as a setting to create different bindings?
|
31
|
+
"Binding" => "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST",
|
32
|
+
"Location" => settings.assertion_consumer_service_url
|
33
|
+
}
|
34
|
+
end
|
35
|
+
meta_doc << REXML::XMLDecl.new
|
36
|
+
ret = ""
|
37
|
+
# pretty print the XML so IdP administrators can easily see what the SP supports
|
38
|
+
meta_doc.write(ret, 1)
|
39
|
+
|
40
|
+
Logging.debug "Generated metadata:\n#{ret}"
|
41
|
+
|
42
|
+
return ret
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
require "xml_security"
|
2
|
+
require "time"
|
3
|
+
|
4
|
+
module Onelogin
|
5
|
+
module Saml
|
6
|
+
|
7
|
+
class Response
|
8
|
+
ASSERTION = "urn:oasis:names:tc:SAML:2.0:assertion"
|
9
|
+
PROTOCOL = "urn:oasis:names:tc:SAML:2.0:protocol"
|
10
|
+
DSIG = "http://www.w3.org/2000/09/xmldsig#"
|
11
|
+
|
12
|
+
attr_accessor :options, :response, :document, :settings
|
13
|
+
|
14
|
+
def initialize(response, options = {})
|
15
|
+
raise ArgumentError.new("Response cannot be nil") if response.nil?
|
16
|
+
self.options = options
|
17
|
+
self.response = response
|
18
|
+
self.document = XMLSecurity::SignedDocument.new(Base64.decode64(response))
|
19
|
+
end
|
20
|
+
|
21
|
+
def is_valid?
|
22
|
+
validate(soft = true)
|
23
|
+
end
|
24
|
+
|
25
|
+
def validate!
|
26
|
+
validate(soft = false)
|
27
|
+
end
|
28
|
+
|
29
|
+
# The value of the user identifier as designated by the initialization request response
|
30
|
+
def name_id
|
31
|
+
@name_id ||= begin
|
32
|
+
node = REXML::XPath.first(document, "/p:Response/a:Assertion[@ID='#{document.signed_element_id[1,document.signed_element_id.size]}']/a:Subject/a:NameID", { "p" => PROTOCOL, "a" => ASSERTION })
|
33
|
+
node ||= REXML::XPath.first(document, "/p:Response[@ID='#{document.signed_element_id[1,document.signed_element_id.size]}']/a:Assertion/a:Subject/a:NameID", { "p" => PROTOCOL, "a" => ASSERTION })
|
34
|
+
node.nil? ? nil : node.text
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# A hash of alle the attributes with the response. Assuming there is only one value for each key
|
39
|
+
def attributes
|
40
|
+
@attr_statements ||= begin
|
41
|
+
result = {}
|
42
|
+
|
43
|
+
stmt_element = REXML::XPath.first(document, "/p:Response/a:Assertion/a:AttributeStatement", { "p" => PROTOCOL, "a" => ASSERTION })
|
44
|
+
return {} if stmt_element.nil?
|
45
|
+
|
46
|
+
stmt_element.elements.each do |attr_element|
|
47
|
+
name = attr_element.attributes["Name"]
|
48
|
+
value = attr_element.elements.first.text
|
49
|
+
|
50
|
+
result[name] = value
|
51
|
+
end
|
52
|
+
|
53
|
+
result.keys.each do |key|
|
54
|
+
result[key.intern] = result[key]
|
55
|
+
end
|
56
|
+
|
57
|
+
result
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# When this user session should expire at latest
|
62
|
+
def session_expires_at
|
63
|
+
@expires_at ||= begin
|
64
|
+
node = REXML::XPath.first(document, "/p:Response/a:Assertion/a:AuthnStatement", { "p" => PROTOCOL, "a" => ASSERTION })
|
65
|
+
parse_time(node, "SessionNotOnOrAfter")
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Conditions (if any) for the assertion to run
|
70
|
+
def conditions
|
71
|
+
@conditions ||= begin
|
72
|
+
REXML::XPath.first(document, "/p:Response/a:Assertion[@ID='#{document.signed_element_id[1,document.signed_element_id.size]}']/a:Conditions", { "p" => PROTOCOL, "a" => ASSERTION })
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def issuer
|
77
|
+
@issuer ||= begin
|
78
|
+
node = REXML::XPath.first(document, "/p:Response/a:Issuer", { "p" => PROTOCOL, "a" => ASSERTION })
|
79
|
+
node.nil? ? nil : node.text
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
def validation_error(message)
|
86
|
+
raise ValidationError.new(message)
|
87
|
+
end
|
88
|
+
|
89
|
+
def validate(soft = true)
|
90
|
+
validate_response_state(soft) &&
|
91
|
+
validate_conditions(soft) &&
|
92
|
+
document.validate(get_fingerprint, soft)
|
93
|
+
end
|
94
|
+
|
95
|
+
def validate_response_state(soft = true)
|
96
|
+
if response.empty?
|
97
|
+
return soft ? false : validation_error("Blank response")
|
98
|
+
end
|
99
|
+
|
100
|
+
if settings.nil?
|
101
|
+
return soft ? false : validation_error("No settings on response")
|
102
|
+
end
|
103
|
+
|
104
|
+
if settings.idp_cert_fingerprint.nil? && settings.idp_cert.nil?
|
105
|
+
return soft ? false : validation_error("No fingerprint or certificate on settings")
|
106
|
+
end
|
107
|
+
|
108
|
+
true
|
109
|
+
end
|
110
|
+
|
111
|
+
def get_fingerprint
|
112
|
+
if settings.idp_cert
|
113
|
+
cert = OpenSSL::X509::Certificate.new(settings.idp_cert)
|
114
|
+
Digest::SHA1.hexdigest(cert.to_der).upcase.scan(/../).join(":")
|
115
|
+
else
|
116
|
+
settings.idp_cert_fingerprint
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def validate_conditions(soft = true)
|
121
|
+
return true if conditions.nil?
|
122
|
+
return true if options[:skip_conditions]
|
123
|
+
|
124
|
+
if not_before = parse_time(conditions, "NotBefore")
|
125
|
+
if Time.now.utc < not_before
|
126
|
+
return soft ? false : validation_error("Current time is earlier than NotBefore condition")
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
if not_on_or_after = parse_time(conditions, "NotOnOrAfter")
|
131
|
+
if Time.now.utc >= not_on_or_after
|
132
|
+
return soft ? false : validation_error("Current time is on or after NotOnOrAfter condition")
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
true
|
137
|
+
end
|
138
|
+
|
139
|
+
def parse_time(node, attribute)
|
140
|
+
if node && node.attributes[attribute]
|
141
|
+
Time.parse(node.attributes[attribute])
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
module Onelogin
|
2
|
+
module Saml
|
3
|
+
class Settings
|
4
|
+
attr_accessor :assertion_consumer_service_url, :issuer, :sp_name_qualifier
|
5
|
+
attr_accessor :idp_sso_target_url, :idp_cert_fingerprint, :idp_cert, :name_identifier_format
|
6
|
+
attr_accessor :authn_context
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
File without changes
|
data/lib/ruby-saml.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
require 'onelogin/saml'
|
5
|
-
|
1
|
+
require 'onelogin/ruby-saml/logging'
|
2
|
+
require 'onelogin/ruby-saml/authrequest'
|
3
|
+
require 'onelogin/ruby-saml/response'
|
4
|
+
require 'onelogin/ruby-saml/settings'
|
5
|
+
require 'onelogin/ruby-saml/validation_error'
|
6
|
+
require 'onelogin/ruby-saml/metadata'
|
7
|
+
require 'onelogin/ruby-saml/version'
|
data/lib/xml_security.rb
CHANGED
@@ -44,7 +44,7 @@ module XMLSecurity
|
|
44
44
|
|
45
45
|
def validate(idp_cert_fingerprint, soft = true)
|
46
46
|
# get cert from response
|
47
|
-
base64_cert = self
|
47
|
+
base64_cert = REXML::XPath.first(self, "//ds:X509Certificate").text
|
48
48
|
cert_text = Base64.decode64(base64_cert)
|
49
49
|
cert = OpenSSL::X509::Certificate.new(cert_text)
|
50
50
|
|
@@ -81,11 +81,11 @@ module XMLSecurity
|
|
81
81
|
hashed_element = REXML::XPath.first(self, "//[@ID='#{uri[1,uri.size]}']")
|
82
82
|
canoner = XML::Util::XmlCanonicalizer.new(false, true)
|
83
83
|
canoner.inclusive_namespaces = inclusive_namespaces if canoner.respond_to?(:inclusive_namespaces) && !inclusive_namespaces.empty?
|
84
|
-
canon_hashed_element = canoner.canonicalize(hashed_element)
|
84
|
+
canon_hashed_element = canoner.canonicalize(hashed_element).gsub('&','&')
|
85
85
|
hash = Base64.encode64(Digest::SHA1.digest(canon_hashed_element)).chomp
|
86
86
|
digest_value = REXML::XPath.first(ref, "//ds:DigestValue", {"ds"=>"http://www.w3.org/2000/09/xmldsig#"}).text
|
87
87
|
|
88
|
-
|
88
|
+
unless digests_match?(hash, digest_value)
|
89
89
|
return soft ? false : (raise Onelogin::Saml::ValidationError.new("Digest mismatch"))
|
90
90
|
end
|
91
91
|
end
|
@@ -111,6 +111,10 @@ module XMLSecurity
|
|
111
111
|
|
112
112
|
private
|
113
113
|
|
114
|
+
def digests_match?(hash, digest_value)
|
115
|
+
hash == digest_value
|
116
|
+
end
|
117
|
+
|
114
118
|
def extract_signed_element_id
|
115
119
|
reference_element = REXML::XPath.first(self, "//ds:Signature/ds:SignedInfo/ds:Reference", {"ds"=>DSIG})
|
116
120
|
self.signed_element_id = reference_element.attribute("URI").value unless reference_element.nil?
|
data/ruby-saml.gemspec
CHANGED
@@ -1,11 +1,9 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
-
# -*- encoding: utf-8 -*-
|
1
|
+
$LOAD_PATH.push File.expand_path('../lib', __FILE__)
|
2
|
+
require 'onelogin/ruby-saml/version'
|
5
3
|
|
6
4
|
Gem::Specification.new do |s|
|
7
|
-
s.name =
|
8
|
-
s.version =
|
5
|
+
s.name = 'ruby-saml'
|
6
|
+
s.version = Onelogin::Saml::VERSION
|
9
7
|
|
10
8
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
9
|
s.authors = ["OneLogin LLC"]
|
@@ -16,48 +14,14 @@ Gem::Specification.new do |s|
|
|
16
14
|
"LICENSE",
|
17
15
|
"README.rdoc"
|
18
16
|
]
|
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/onelogin/saml/validation_error.rb",
|
31
|
-
"lib/ruby-saml.rb",
|
32
|
-
"lib/xml_security.rb",
|
33
|
-
"ruby-saml.gemspec",
|
34
|
-
"test/certificates/certificate1",
|
35
|
-
"test/request_test.rb",
|
36
|
-
"test/response_test.rb",
|
37
|
-
"test/responses/adfs_response.xml.base64",
|
38
|
-
"test/responses/open_saml_response.xml",
|
39
|
-
"test/responses/response1.xml.base64",
|
40
|
-
"test/responses/response2.xml.base64",
|
41
|
-
"test/responses/response3.xml.base64",
|
42
|
-
"test/responses/response4.xml.base64",
|
43
|
-
"test/responses/response5.xml.base64",
|
44
|
-
"test/responses/simple_saml_php.xml",
|
45
|
-
"test/settings_test.rb",
|
46
|
-
"test/test_helper.rb",
|
47
|
-
"test/xml_security_test.rb"
|
48
|
-
]
|
17
|
+
s.files = `git ls-files`.split("\n")
|
49
18
|
s.homepage = %q{http://github.com/onelogin/ruby-saml}
|
19
|
+
s.rubyforge_project = %q{http://www.rubygems.org/gems/ruby-saml}
|
50
20
|
s.rdoc_options = ["--charset=UTF-8"]
|
51
21
|
s.require_paths = ["lib"]
|
52
22
|
s.rubygems_version = %q{1.3.7}
|
53
23
|
s.summary = %q{SAML Ruby Tookit}
|
54
|
-
s.test_files =
|
55
|
-
"test/request_test.rb",
|
56
|
-
"test/response_test.rb",
|
57
|
-
"test/settings_test.rb",
|
58
|
-
"test/test_helper.rb",
|
59
|
-
"test/xml_security_test.rb"
|
60
|
-
]
|
24
|
+
s.test_files = `git ls-files test/*`.split("\n")
|
61
25
|
|
62
26
|
if s.respond_to? :specification_version then
|
63
27
|
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|