ruby-saml 0.4.7 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
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
|