saml_idp 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/Gemfile +2 -0
- data/LICENSE +22 -0
- data/README.md +197 -0
- data/app/controllers/saml_idp/idp_controller.rb +42 -0
- data/app/views/saml_idp/idp/new.html.erb +21 -0
- data/app/views/saml_idp/idp/saml_post.html.erb +13 -0
- data/lib/saml_idp.rb +92 -0
- data/lib/saml_idp/algorithmable.rb +19 -0
- data/lib/saml_idp/assertion_builder.rb +144 -0
- data/lib/saml_idp/attribute_decorator.rb +27 -0
- data/lib/saml_idp/attributeable.rb +24 -0
- data/lib/saml_idp/configurator.rb +45 -0
- data/lib/saml_idp/controller.rb +71 -0
- data/lib/saml_idp/default.rb +28 -0
- data/lib/saml_idp/engine.rb +5 -0
- data/lib/saml_idp/hashable.rb +26 -0
- data/lib/saml_idp/incoming_metadata.rb +144 -0
- data/lib/saml_idp/metadata_builder.rb +158 -0
- data/lib/saml_idp/name_id_formatter.rb +68 -0
- data/lib/saml_idp/persisted_metadata.rb +10 -0
- data/lib/saml_idp/request.rb +79 -0
- data/lib/saml_idp/response_builder.rb +60 -0
- data/lib/saml_idp/saml_response.rb +63 -0
- data/lib/saml_idp/service_provider.rb +68 -0
- data/lib/saml_idp/signable.rb +131 -0
- data/lib/saml_idp/signature_builder.rb +42 -0
- data/lib/saml_idp/signed_info_builder.rb +51 -0
- data/lib/saml_idp/version.rb +4 -0
- data/lib/saml_idp/xml_security.rb +168 -0
- data/saml_idp.gemspec +38 -0
- data/spec/acceptance/acceptance_helper.rb +9 -0
- data/spec/acceptance/idp_controller_spec.rb +16 -0
- data/spec/lib/saml_idp/algorithmable_spec.rb +48 -0
- data/spec/lib/saml_idp/assertion_builder_spec.rb +27 -0
- data/spec/lib/saml_idp/attribute_decorator_spec.rb +31 -0
- data/spec/lib/saml_idp/configurator_spec.rb +30 -0
- data/spec/lib/saml_idp/controller_spec.rb +51 -0
- data/spec/lib/saml_idp/metadata_builder_spec.rb +9 -0
- data/spec/lib/saml_idp/name_id_formatter_spec.rb +39 -0
- data/spec/lib/saml_idp/request_spec.rb +14 -0
- data/spec/lib/saml_idp/response_builder_spec.rb +32 -0
- data/spec/lib/saml_idp/saml_response_spec.rb +27 -0
- data/spec/lib/saml_idp/service_provider_spec.rb +21 -0
- data/spec/lib/saml_idp/signable_spec.rb +74 -0
- data/spec/lib/saml_idp/signature_builder_spec.rb +19 -0
- data/spec/lib/saml_idp/signed_info_builder_spec.rb +25 -0
- data/spec/rails_app/.gitignore +15 -0
- data/spec/rails_app/README.rdoc +261 -0
- data/spec/rails_app/Rakefile +7 -0
- data/spec/rails_app/app/assets/images/rails.png +0 -0
- data/spec/rails_app/app/assets/javascripts/application.js +15 -0
- data/spec/rails_app/app/assets/stylesheets/application.css +13 -0
- data/spec/rails_app/app/controllers/application_controller.rb +3 -0
- data/spec/rails_app/app/controllers/saml_controller.rb +8 -0
- data/spec/rails_app/app/controllers/saml_idp_controller.rb +11 -0
- data/spec/rails_app/app/helpers/application_helper.rb +2 -0
- data/spec/rails_app/app/mailers/.gitkeep +0 -0
- data/spec/rails_app/app/models/.gitkeep +0 -0
- data/spec/rails_app/app/views/layouts/application.html.erb +14 -0
- data/spec/rails_app/config.ru +4 -0
- data/spec/rails_app/config/application.rb +60 -0
- data/spec/rails_app/config/boot.rb +6 -0
- data/spec/rails_app/config/database.yml +25 -0
- data/spec/rails_app/config/environment.rb +5 -0
- data/spec/rails_app/config/environments/development.rb +37 -0
- data/spec/rails_app/config/environments/production.rb +67 -0
- data/spec/rails_app/config/environments/test.rb +37 -0
- data/spec/rails_app/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/rails_app/config/initializers/inflections.rb +15 -0
- data/spec/rails_app/config/initializers/mime_types.rb +5 -0
- data/spec/rails_app/config/initializers/secret_token.rb +7 -0
- data/spec/rails_app/config/initializers/session_store.rb +8 -0
- data/spec/rails_app/config/initializers/wrap_parameters.rb +14 -0
- data/spec/rails_app/config/locales/en.yml +5 -0
- data/spec/rails_app/config/routes.rb +6 -0
- data/spec/rails_app/db/seeds.rb +7 -0
- data/spec/rails_app/doc/README_FOR_APP +2 -0
- data/spec/rails_app/lib/assets/.gitkeep +0 -0
- data/spec/rails_app/lib/tasks/.gitkeep +0 -0
- data/spec/rails_app/log/.gitkeep +0 -0
- data/spec/rails_app/public/404.html +26 -0
- data/spec/rails_app/public/422.html +26 -0
- data/spec/rails_app/public/500.html +25 -0
- data/spec/rails_app/public/favicon.ico +0 -0
- data/spec/rails_app/public/index.html +241 -0
- data/spec/rails_app/public/robots.txt +5 -0
- data/spec/rails_app/script/rails +6 -0
- data/spec/rails_app/test/fixtures/.gitkeep +0 -0
- data/spec/rails_app/test/functional/.gitkeep +0 -0
- data/spec/rails_app/test/integration/.gitkeep +0 -0
- data/spec/rails_app/test/performance/browsing_test.rb +12 -0
- data/spec/rails_app/test/test_helper.rb +13 -0
- data/spec/rails_app/test/unit/.gitkeep +0 -0
- data/spec/rails_app/vendor/assets/javascripts/.gitkeep +0 -0
- data/spec/rails_app/vendor/assets/stylesheets/.gitkeep +0 -0
- data/spec/rails_app/vendor/plugins/.gitkeep +0 -0
- data/spec/spec_helper.rb +49 -0
- data/spec/support/certificates/certificate1 +12 -0
- data/spec/support/certificates/r1_certificate2_base64 +1 -0
- data/spec/support/responses/adfs_response_sha1.xml +46 -0
- data/spec/support/responses/adfs_response_sha256.xml +46 -0
- data/spec/support/responses/adfs_response_sha384.xml +46 -0
- data/spec/support/responses/adfs_response_sha512.xml +46 -0
- data/spec/support/responses/logoutresponse_fixtures.rb +67 -0
- data/spec/support/responses/no_signature_ns.xml +48 -0
- data/spec/support/responses/open_saml_response.xml +56 -0
- data/spec/support/responses/r1_response6.xml.base64 +1 -0
- data/spec/support/responses/response1.xml.base64 +1 -0
- data/spec/support/responses/response2.xml.base64 +79 -0
- data/spec/support/responses/response3.xml.base64 +66 -0
- data/spec/support/responses/response4.xml.base64 +93 -0
- data/spec/support/responses/response5.xml.base64 +102 -0
- data/spec/support/responses/response_with_ampersands.xml +139 -0
- data/spec/support/responses/response_with_ampersands.xml.base64 +93 -0
- data/spec/support/responses/simple_saml_php.xml +71 -0
- data/spec/support/responses/starfield_response.xml.base64 +1 -0
- data/spec/support/responses/wrapped_response_2.xml.base64 +150 -0
- data/spec/support/saml_request_macros.rb +19 -0
- data/spec/support/security_helpers.rb +61 -0
- data/spec/xml_security_spec.rb +136 -0
- metadata +407 -0
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'builder'
|
2
|
+
module SamlIdp
|
3
|
+
class SignatureBuilder
|
4
|
+
attr_accessor :signed_info_builder
|
5
|
+
|
6
|
+
def initialize(signed_info_builder)
|
7
|
+
self.signed_info_builder = signed_info_builder
|
8
|
+
end
|
9
|
+
|
10
|
+
def raw
|
11
|
+
builder = Builder::XmlMarkup.new
|
12
|
+
builder.tag! "ds:Signature", "xmlns:ds" => "http://www.w3.org/2000/09/xmldsig#" do |signature|
|
13
|
+
signature << signed_info
|
14
|
+
signature.tag! "ds:SignatureValue", signature_value
|
15
|
+
signature.KeyInfo xmlns: "http://www.w3.org/2000/09/xmldsig#" do |key_info|
|
16
|
+
key_info.tag! "ds:X509Data" do |x509|
|
17
|
+
x509.tag! "ds:X509Certificate", x509_certificate
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def x509_certificate
|
24
|
+
SamlIdp.config.x509_certificate
|
25
|
+
.to_s
|
26
|
+
.gsub(/-----BEGIN CERTIFICATE-----/,"")
|
27
|
+
.gsub(/-----END CERTIFICATE-----/,"")
|
28
|
+
.gsub(/\n/, "")
|
29
|
+
end
|
30
|
+
private :x509_certificate
|
31
|
+
|
32
|
+
def signed_info
|
33
|
+
signed_info_builder.raw
|
34
|
+
end
|
35
|
+
private :signed_info
|
36
|
+
|
37
|
+
def signature_value
|
38
|
+
signed_info_builder.signed
|
39
|
+
end
|
40
|
+
private :signature_value
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'builder'
|
2
|
+
module SamlIdp
|
3
|
+
class SignedInfoBuilder
|
4
|
+
include Algorithmable
|
5
|
+
attr_accessor :reference_id
|
6
|
+
attr_accessor :digest_value
|
7
|
+
attr_accessor :raw_algorithm
|
8
|
+
|
9
|
+
def initialize(reference_id, digest_value, raw_algorithm)
|
10
|
+
self.reference_id = reference_id
|
11
|
+
self.digest_value = digest_value
|
12
|
+
self.raw_algorithm = raw_algorithm
|
13
|
+
end
|
14
|
+
|
15
|
+
def raw
|
16
|
+
builder = Builder::XmlMarkup.new
|
17
|
+
builder.tag! "ds:SignedInfo", "xmlns:ds" => "http://www.w3.org/2000/09/xmldsig#" do |signed_info|
|
18
|
+
signed_info.tag!("ds:CanonicalizationMethod", Algorithm: "http://www.w3.org/2001/10/xml-exc-c14n#") {}
|
19
|
+
signed_info.tag!("ds:SignatureMethod", Algorithm: "http://www.w3.org/2000/09/xmldsig#rsa-#{algorithm_name}") {}
|
20
|
+
signed_info.tag! "ds:Reference", URI: reference_string do |reference|
|
21
|
+
reference.tag! "ds:Transforms" do |transforms|
|
22
|
+
transforms.tag!("ds:Transform", Algorithm: "http://www.w3.org/2000/09/xmldsig#enveloped-signature") {}
|
23
|
+
transforms.tag!("ds:Transform", Algorithm: "http://www.w3.org/2001/10/xml-exc-c14n#") {}
|
24
|
+
end
|
25
|
+
reference.tag!("ds:DigestMethod", Algorithm: "http://www.w3.org/2000/09/xmldsig##{algorithm_name}") {}
|
26
|
+
reference.tag! "ds:DigestValue", digest_value
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def signed
|
32
|
+
encoded.gsub(/\n/, "")
|
33
|
+
end
|
34
|
+
|
35
|
+
def secret_key
|
36
|
+
SamlIdp.config.secret_key
|
37
|
+
end
|
38
|
+
private :secret_key
|
39
|
+
|
40
|
+
def encoded
|
41
|
+
key = OpenSSL::PKey::RSA.new(secret_key)
|
42
|
+
Base64.encode64(key.sign(algorithm.new, raw))
|
43
|
+
end
|
44
|
+
private :encoded
|
45
|
+
|
46
|
+
def reference_string
|
47
|
+
"#_#{reference_id}"
|
48
|
+
end
|
49
|
+
private :reference_string
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,168 @@
|
|
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 "rexml/document"
|
26
|
+
require "rexml/xpath"
|
27
|
+
require "openssl"
|
28
|
+
require 'nokogiri'
|
29
|
+
require "digest/sha1"
|
30
|
+
require "digest/sha2"
|
31
|
+
|
32
|
+
module SamlIdp
|
33
|
+
module XMLSecurity
|
34
|
+
class SignedDocument < REXML::Document
|
35
|
+
ValidationError = Class.new(StandardError)
|
36
|
+
C14N = "http://www.w3.org/2001/10/xml-exc-c14n#"
|
37
|
+
DSIG = "http://www.w3.org/2000/09/xmldsig#"
|
38
|
+
|
39
|
+
attr_accessor :signed_element_id
|
40
|
+
|
41
|
+
def initialize(response)
|
42
|
+
super(response)
|
43
|
+
extract_signed_element_id
|
44
|
+
end
|
45
|
+
|
46
|
+
def validate(idp_cert_fingerprint, soft = true)
|
47
|
+
# get cert from response
|
48
|
+
cert_element = REXML::XPath.first(self, "//ds:X509Certificate", { "ds"=>DSIG })
|
49
|
+
raise ValidationError.new("Certificate element missing in response (ds:X509Certificate)") unless cert_element
|
50
|
+
base64_cert = cert_element.text
|
51
|
+
cert_text = Base64.decode64(base64_cert)
|
52
|
+
cert = OpenSSL::X509::Certificate.new(cert_text)
|
53
|
+
|
54
|
+
# check cert matches registered idp cert
|
55
|
+
fingerprint = Digest::SHA1.hexdigest(cert.to_der)
|
56
|
+
|
57
|
+
if fingerprint != idp_cert_fingerprint.gsub(/[^a-zA-Z0-9]/,"").downcase
|
58
|
+
return soft ? false : (raise ValidationError.new("Fingerprint mismatch"))
|
59
|
+
end
|
60
|
+
|
61
|
+
validate_doc(base64_cert, soft)
|
62
|
+
end
|
63
|
+
|
64
|
+
def validate_doc(base64_cert, soft = true)
|
65
|
+
# validate references
|
66
|
+
|
67
|
+
# check for inclusive namespaces
|
68
|
+
inclusive_namespaces = extract_inclusive_namespaces
|
69
|
+
|
70
|
+
document = Nokogiri.parse(self.to_s)
|
71
|
+
|
72
|
+
# create a working copy so we don't modify the original
|
73
|
+
@working_copy ||= REXML::Document.new(self.to_s).root
|
74
|
+
|
75
|
+
# store and remove signature node
|
76
|
+
@sig_element ||= begin
|
77
|
+
element = REXML::XPath.first(@working_copy, "//ds:Signature", {"ds"=>DSIG})
|
78
|
+
element.remove
|
79
|
+
end
|
80
|
+
|
81
|
+
|
82
|
+
# verify signature
|
83
|
+
signed_info_element = REXML::XPath.first(@sig_element, "//ds:SignedInfo", {"ds"=>DSIG})
|
84
|
+
noko_sig_element = document.at_xpath('//ds:Signature', 'ds' => DSIG)
|
85
|
+
noko_signed_info_element = noko_sig_element.at_xpath('./ds:SignedInfo', 'ds' => DSIG)
|
86
|
+
canon_algorithm = canon_algorithm REXML::XPath.first(@sig_element, '//ds:CanonicalizationMethod', 'ds' => DSIG)
|
87
|
+
canon_string = noko_signed_info_element.canonicalize(canon_algorithm)
|
88
|
+
noko_sig_element.remove
|
89
|
+
|
90
|
+
# check digests
|
91
|
+
REXML::XPath.each(@sig_element, "//ds:Reference", {"ds"=>DSIG}) do |ref|
|
92
|
+
uri = ref.attributes.get_attribute("URI").value
|
93
|
+
|
94
|
+
hashed_element = document.at_xpath("//*[@ID='#{uri[1..-1]}']")
|
95
|
+
canon_algorithm = canon_algorithm REXML::XPath.first(ref, '//ds:CanonicalizationMethod', 'ds' => DSIG)
|
96
|
+
canon_hashed_element = hashed_element.canonicalize(canon_algorithm, inclusive_namespaces)
|
97
|
+
|
98
|
+
digest_algorithm = algorithm(REXML::XPath.first(ref, "//ds:DigestMethod"))
|
99
|
+
|
100
|
+
hash = digest_algorithm.digest(canon_hashed_element)
|
101
|
+
digest_value = Base64.decode64(REXML::XPath.first(ref, "//ds:DigestValue", {"ds"=>DSIG}).text)
|
102
|
+
|
103
|
+
unless digests_match?(hash, digest_value)
|
104
|
+
return soft ? false : (raise ValidationError.new("Digest mismatch"))
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
base64_signature = REXML::XPath.first(@sig_element, "//ds:SignatureValue", {"ds"=>DSIG}).text
|
109
|
+
signature = Base64.decode64(base64_signature)
|
110
|
+
|
111
|
+
# get certificate object
|
112
|
+
cert_text = Base64.decode64(base64_cert)
|
113
|
+
cert = OpenSSL::X509::Certificate.new(cert_text)
|
114
|
+
|
115
|
+
# signature method
|
116
|
+
signature_algorithm = algorithm(REXML::XPath.first(signed_info_element, "//ds:SignatureMethod", {"ds"=>DSIG}))
|
117
|
+
|
118
|
+
unless cert.public_key.verify(signature_algorithm.new, signature, canon_string)
|
119
|
+
return soft ? false : (raise ValidationError.new("Key validation error"))
|
120
|
+
end
|
121
|
+
|
122
|
+
return true
|
123
|
+
end
|
124
|
+
|
125
|
+
private
|
126
|
+
|
127
|
+
def digests_match?(hash, digest_value)
|
128
|
+
hash == digest_value
|
129
|
+
end
|
130
|
+
|
131
|
+
def extract_signed_element_id
|
132
|
+
reference_element = REXML::XPath.first(self, "//ds:Signature/ds:SignedInfo/ds:Reference", {"ds"=>DSIG})
|
133
|
+
self.signed_element_id = reference_element.attribute("URI").value[1..-1] unless reference_element.nil?
|
134
|
+
end
|
135
|
+
|
136
|
+
def canon_algorithm(element)
|
137
|
+
algorithm = element.attribute('Algorithm').value if element
|
138
|
+
case algorithm
|
139
|
+
when "http://www.w3.org/2001/10/xml-exc-c14n#" then Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0
|
140
|
+
when "http://www.w3.org/TR/2001/REC-xml-c14n-20010315" then Nokogiri::XML::XML_C14N_1_0
|
141
|
+
when "http://www.w3.org/2006/12/xml-c14n11" then Nokogiri::XML::XML_C14N_1_1
|
142
|
+
else Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def algorithm(element)
|
147
|
+
algorithm = element.attribute("Algorithm").value if element
|
148
|
+
algorithm = algorithm && algorithm =~ /sha(.*?)$/i && $1.to_i
|
149
|
+
case algorithm
|
150
|
+
when 256 then OpenSSL::Digest::SHA256
|
151
|
+
when 384 then OpenSSL::Digest::SHA384
|
152
|
+
when 512 then OpenSSL::Digest::SHA512
|
153
|
+
else
|
154
|
+
OpenSSL::Digest::SHA1
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def extract_inclusive_namespaces
|
159
|
+
if element = REXML::XPath.first(self, "//ec:InclusiveNamespaces", { "ec" => C14N })
|
160
|
+
prefix_list = element.attributes.get_attribute("PrefixList").value
|
161
|
+
prefix_list.split(" ")
|
162
|
+
else
|
163
|
+
[]
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
data/saml_idp.gemspec
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "saml_idp/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = %q{saml_idp}
|
7
|
+
s.version = SamlIdp::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Jon Phenow"]
|
10
|
+
s.email = %q{jon.phenow@sportngin.com}
|
11
|
+
s.homepage = %q{http://github.com/sportngin/saml_idp}
|
12
|
+
s.summary = %q{SAML Indentity Provider in ruby}
|
13
|
+
s.description = %q{SAML IdP (Identity Provider) library in ruby}
|
14
|
+
s.date = Time.now.utc.strftime("%Y-%m-%d")
|
15
|
+
s.files = Dir.glob("app/**/*") + Dir.glob("lib/**/*") + [
|
16
|
+
"LICENSE",
|
17
|
+
"README.md",
|
18
|
+
"Gemfile",
|
19
|
+
"saml_idp.gemspec"
|
20
|
+
]
|
21
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
22
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
23
|
+
s.require_paths = ["lib"]
|
24
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
25
|
+
s.add_dependency('activesupport')
|
26
|
+
s.add_dependency('uuid')
|
27
|
+
s.add_dependency('builder')
|
28
|
+
s.add_dependency('httparty')
|
29
|
+
|
30
|
+
s.add_development_dependency "rake"
|
31
|
+
s.add_development_dependency "simplecov"
|
32
|
+
s.add_development_dependency "rspec"
|
33
|
+
s.add_development_dependency "ruby-saml"
|
34
|
+
s.add_development_dependency("rails", "~> 3.2")
|
35
|
+
s.add_development_dependency("capybara")
|
36
|
+
s.add_development_dependency("timecop")
|
37
|
+
end
|
38
|
+
|
@@ -0,0 +1,9 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
|
2
|
+
require 'capybara/rspec'
|
3
|
+
|
4
|
+
# Put your acceptance spec helpers inside /spec/acceptance/support
|
5
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
6
|
+
|
7
|
+
RSpec.configure do |config|
|
8
|
+
config.include Rails.application.routes.url_helpers, :type => :request
|
9
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/acceptance_helper')
|
2
|
+
|
3
|
+
feature 'IdpController' do
|
4
|
+
|
5
|
+
scenario 'Login via default signup page' do
|
6
|
+
saml_request = make_saml_request("http://foo.example.com/saml/consume")
|
7
|
+
visit "/saml/auth?SAMLRequest=#{CGI.escape(saml_request)}"
|
8
|
+
fill_in 'Email', :with => "foo@example.com"
|
9
|
+
fill_in 'Password', :with => "okidoki"
|
10
|
+
click_button 'Sign in'
|
11
|
+
click_button 'Submit' # simulating onload
|
12
|
+
current_url.should == 'http://foo.example.com/saml/consume'
|
13
|
+
page.should have_content "foo@example.com"
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
module SamlIdp
|
3
|
+
describe "Algorithmable" do
|
4
|
+
include Algorithmable
|
5
|
+
|
6
|
+
describe "named raw algorithm" do
|
7
|
+
def raw_algorithm
|
8
|
+
:sha256
|
9
|
+
end
|
10
|
+
|
11
|
+
it "finds algorithm class" do
|
12
|
+
algorithm.should == OpenSSL::Digest::SHA256
|
13
|
+
end
|
14
|
+
|
15
|
+
it "finds the name" do
|
16
|
+
algorithm_name.should == "sha256"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "class raw algorithm" do
|
21
|
+
def raw_algorithm
|
22
|
+
OpenSSL::Digest::SHA512
|
23
|
+
end
|
24
|
+
|
25
|
+
it "finds algorithm class" do
|
26
|
+
algorithm.should == OpenSSL::Digest::SHA512
|
27
|
+
end
|
28
|
+
|
29
|
+
it "finds the name" do
|
30
|
+
algorithm_name.should == "sha512"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "nonexistent raw algorithm" do
|
35
|
+
def raw_algorithm
|
36
|
+
:sha1024
|
37
|
+
end
|
38
|
+
|
39
|
+
it "finds algorithm class" do
|
40
|
+
algorithm.should == OpenSSL::Digest::SHA1
|
41
|
+
end
|
42
|
+
|
43
|
+
it "finds the name" do
|
44
|
+
algorithm_name.should == "sha1"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
module SamlIdp
|
3
|
+
describe AssertionBuilder do
|
4
|
+
let(:reference_id) { "abc" }
|
5
|
+
let(:issuer_uri) { "http://sportngin.com" }
|
6
|
+
let(:name_id) { "jon.phenow@sportngin.com" }
|
7
|
+
let(:audience_uri) { "http://example.com" }
|
8
|
+
let(:saml_request_id) { "123" }
|
9
|
+
let(:saml_acs_url) { "http://saml.acs.url" }
|
10
|
+
let(:algorithm) { :sha256 }
|
11
|
+
subject { described_class.new(
|
12
|
+
reference_id,
|
13
|
+
issuer_uri,
|
14
|
+
name_id,
|
15
|
+
audience_uri,
|
16
|
+
saml_request_id,
|
17
|
+
saml_acs_url,
|
18
|
+
algorithm
|
19
|
+
) }
|
20
|
+
|
21
|
+
it "builds a legit raw XML file" do
|
22
|
+
Timecop.travel(Time.zone.local(2010, 6, 1, 13, 0, 0)) do
|
23
|
+
subject.raw.should == "<Assertion xmlns=\"urn:oasis:names:tc:SAML:2.0:assertion\" ID=\"_abc\" IssueInstant=\"2010-06-01T13:00:00Z\" Version=\"2.0\"><Issuer>http://sportngin.com</Issuer><Subject><NameID Format=\"urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress\">foo@example.com</NameID><SubjectConfirmation Method=\"urn:oasis:names:tc:SAML:2.0:cm:bearer\"><SubjectConfirmationData InResponseTo=\"123\" NotOnOrAfter=\"2010-06-01T13:03:00Z\" Recipient=\"http://saml.acs.url\"></SubjectConfirmationData></SubjectConfirmation></Subject><Conditions NotBefore=\"2010-06-01T12:59:55Z\" NotOnOrAfter=\"2010-06-01T14:00:00Z\"><AudienceRestriction><Audience>http://example.com</Audience></AudienceRestriction></Conditions><AttributeStatement><Attribute Name=\"email-address\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:uri\" FriendlyName=\"emailAddress\"><AttributeValue>foo@example.com</AttributeValue></Attribute></AttributeStatement><AuthnStatement AuthnInstant=\"2010-06-01T13:00:00Z\" SessionIndex=\"_abc\"><AuthnContext><AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</AuthnContextClassRef></AuthnContext></AuthnStatement></Assertion>"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
module SamlIdp
|
3
|
+
describe AttributeDecorator do
|
4
|
+
subject { described_class.new name: name,
|
5
|
+
friendly_name: friendly_name,
|
6
|
+
name_format: name_format,
|
7
|
+
values: values
|
8
|
+
}
|
9
|
+
let(:name) { nil }
|
10
|
+
let(:friendly_name) { nil }
|
11
|
+
let(:name_format) { nil }
|
12
|
+
let(:values) { nil }
|
13
|
+
|
14
|
+
its(:name) { should be_nil }
|
15
|
+
its(:friendly_name) { should be_nil }
|
16
|
+
its(:name_format) { should == Saml::XML::Namespaces::Formats::Attr::URI }
|
17
|
+
its(:values) { should == [] }
|
18
|
+
|
19
|
+
describe "with values set" do
|
20
|
+
let(:name) { "test" }
|
21
|
+
let(:friendly_name) { "test too" }
|
22
|
+
let(:name_format) { "some format" }
|
23
|
+
let(:values) { :val }
|
24
|
+
|
25
|
+
its(:name) { should == name }
|
26
|
+
its(:friendly_name) { should == friendly_name }
|
27
|
+
its(:name_format) { should == name_format }
|
28
|
+
its(:values) { should == [values] }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|