saml_idp 0.7.2 → 0.16.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.
- checksums.yaml +5 -5
- data/Gemfile +1 -1
- data/README.md +59 -52
- data/lib/saml_idp/assertion_builder.rb +28 -3
- data/lib/saml_idp/configurator.rb +7 -1
- data/lib/saml_idp/controller.rb +21 -13
- data/lib/saml_idp/encryptor.rb +0 -1
- data/lib/saml_idp/fingerprint.rb +19 -0
- data/lib/saml_idp/incoming_metadata.rb +22 -1
- data/lib/saml_idp/metadata_builder.rb +23 -8
- data/lib/saml_idp/persisted_metadata.rb +4 -0
- data/lib/saml_idp/request.rb +26 -6
- data/lib/saml_idp/response_builder.rb +26 -6
- data/lib/saml_idp/saml_response.rb +62 -28
- data/lib/saml_idp/service_provider.rb +15 -6
- data/lib/saml_idp/signable.rb +1 -2
- data/lib/saml_idp/version.rb +1 -1
- data/lib/saml_idp/xml_security.rb +1 -1
- data/lib/saml_idp.rb +2 -1
- data/saml_idp.gemspec +45 -42
- data/spec/acceptance/idp_controller_spec.rb +5 -4
- data/spec/lib/saml_idp/algorithmable_spec.rb +6 -6
- data/spec/lib/saml_idp/assertion_builder_spec.rb +151 -8
- data/spec/lib/saml_idp/attribute_decorator_spec.rb +8 -8
- data/spec/lib/saml_idp/configurator_spec.rb +9 -7
- data/spec/lib/saml_idp/controller_spec.rb +53 -20
- data/spec/lib/saml_idp/encryptor_spec.rb +4 -4
- data/spec/lib/saml_idp/fingerprint_spec.rb +14 -0
- data/spec/lib/saml_idp/incoming_metadata_spec.rb +60 -0
- data/spec/lib/saml_idp/metadata_builder_spec.rb +30 -17
- data/spec/lib/saml_idp/name_id_formatter_spec.rb +3 -3
- data/spec/lib/saml_idp/request_spec.rb +78 -27
- data/spec/lib/saml_idp/response_builder_spec.rb +5 -3
- data/spec/lib/saml_idp/saml_response_spec.rb +127 -12
- data/spec/lib/saml_idp/service_provider_spec.rb +2 -2
- data/spec/lib/saml_idp/signable_spec.rb +1 -1
- data/spec/lib/saml_idp/signature_builder_spec.rb +2 -2
- data/spec/lib/saml_idp/signed_info_builder_spec.rb +3 -3
- data/spec/rails_app/app/controllers/saml_controller.rb +1 -1
- data/spec/rails_app/app/controllers/saml_idp_controller.rb +55 -3
- data/{app → spec/rails_app/app}/views/saml_idp/idp/new.html.erb +1 -5
- data/{app → spec/rails_app/app}/views/saml_idp/idp/saml_post.html.erb +1 -1
- data/spec/rails_app/config/application.rb +1 -6
- data/spec/rails_app/config/boot.rb +1 -1
- data/spec/rails_app/config/environments/development.rb +2 -5
- data/spec/rails_app/config/environments/production.rb +1 -0
- data/spec/rails_app/config/environments/test.rb +1 -0
- data/spec/spec_helper.rb +23 -1
- data/spec/support/certificates/sp_cert_req.csr +12 -0
- data/spec/support/certificates/sp_private_key.pem +16 -0
- data/spec/support/certificates/sp_x509_cert.crt +18 -0
- data/spec/support/saml_request_macros.rb +66 -4
- data/spec/support/security_helpers.rb +10 -0
- data/spec/xml_security_spec.rb +12 -12
- metadata +135 -81
- data/app/controllers/saml_idp/idp_controller.rb +0 -59
- data/spec/lib/saml_idp/.assertion_builder_spec.rb.swp +0 -0
@@ -1,9 +1,61 @@
|
|
1
|
-
class SamlIdpController <
|
1
|
+
class SamlIdpController < ApplicationController
|
2
|
+
include SamlIdp::Controller
|
3
|
+
|
4
|
+
if Rails::VERSION::MAJOR >= 4
|
5
|
+
before_action :add_view_path, only: [:new, :create, :logout]
|
6
|
+
before_action :validate_saml_request, only: [:new, :create, :logout]
|
7
|
+
else
|
8
|
+
before_filter :add_view_path, only: [:new, :create, :logout]
|
9
|
+
before_filter :validate_saml_request, only: [:new, :create, :logout]
|
10
|
+
end
|
11
|
+
|
12
|
+
def new
|
13
|
+
render template: "saml_idp/idp/new"
|
14
|
+
end
|
15
|
+
|
16
|
+
def show
|
17
|
+
render xml: SamlIdp.metadata.signed
|
18
|
+
end
|
19
|
+
|
20
|
+
def create
|
21
|
+
unless params[:email].blank? && params[:password].blank?
|
22
|
+
person = idp_authenticate(params[:email], params[:password])
|
23
|
+
if person.nil?
|
24
|
+
@saml_idp_fail_msg = "Incorrect email or password."
|
25
|
+
else
|
26
|
+
@saml_response = idp_make_saml_response(person)
|
27
|
+
render :template => "saml_idp/idp/saml_post", :layout => false
|
28
|
+
return
|
29
|
+
end
|
30
|
+
end
|
31
|
+
render :template => "saml_idp/idp/new"
|
32
|
+
end
|
33
|
+
|
34
|
+
def logout
|
35
|
+
idp_logout
|
36
|
+
@saml_response = idp_make_saml_response(nil)
|
37
|
+
render :template => "saml_idp/idp/saml_post", :layout => false
|
38
|
+
end
|
39
|
+
|
40
|
+
def idp_logout
|
41
|
+
raise NotImplementedError
|
42
|
+
end
|
43
|
+
private :idp_logout
|
44
|
+
|
2
45
|
def idp_authenticate(email, password)
|
3
46
|
{ :email => email }
|
4
47
|
end
|
48
|
+
protected :idp_authenticate
|
5
49
|
|
6
|
-
def idp_make_saml_response(
|
7
|
-
encode_response(
|
50
|
+
def idp_make_saml_response(person)
|
51
|
+
encode_response(person[:email])
|
8
52
|
end
|
53
|
+
protected :idp_make_saml_response
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def add_view_path
|
58
|
+
prepend_view_path("app/views")
|
59
|
+
end
|
60
|
+
|
9
61
|
end
|
@@ -1,22 +1,18 @@
|
|
1
1
|
<% if @saml_idp_fail_msg %>
|
2
2
|
<div id="saml_idp_fail_msg" class="flash error"><%= @saml_idp_fail_msg %></div>
|
3
3
|
<% end %>
|
4
|
-
|
5
4
|
<%= form_tag do %>
|
6
5
|
<%= hidden_field_tag("SAMLRequest", params[:SAMLRequest]) %>
|
7
6
|
<%= hidden_field_tag("RelayState", params[:RelayState]) %>
|
8
|
-
|
9
7
|
<p>
|
10
8
|
<%= label_tag :email %>
|
11
9
|
<%= email_field_tag :email, params[:email], :autocapitalize => "off", :autocorrect => "off", :autofocus => "autofocus", :spellcheck => "false", :size => 30, :class => "email_pwd txt" %>
|
12
10
|
</p>
|
13
|
-
|
14
11
|
<p>
|
15
12
|
<%= label_tag :password %>
|
16
13
|
<%= password_field_tag :password, params[:password], :autocapitalize => "off", :autocorrect => "off", :spellcheck => "false", :size => 30, :class => "email_pwd txt" %>
|
17
14
|
</p>
|
18
|
-
|
19
15
|
<p>
|
20
16
|
<%= submit_tag "Sign in", :class => "button big blueish" %>
|
21
17
|
</p>
|
22
|
-
<% end %>
|
18
|
+
<% end %>
|
@@ -18,6 +18,7 @@ module RailsApp
|
|
18
18
|
|
19
19
|
# Custom directories with classes and modules you want to be autoloadable.
|
20
20
|
# config.autoload_paths += %W(#{config.root}/extras)
|
21
|
+
config.autoload_paths += %w[app/controllers]
|
21
22
|
|
22
23
|
# Only load the plugins named here, in the order given (default is alphabetical).
|
23
24
|
# :all can be used as a placeholder for all plugins not explicitly named.
|
@@ -50,11 +51,5 @@ module RailsApp
|
|
50
51
|
# in your app. As such, your models will need to explicitly whitelist or blacklist accessible
|
51
52
|
# parameters by using an attr_accessible or attr_protected declaration.
|
52
53
|
# config.active_record.whitelist_attributes = true
|
53
|
-
|
54
|
-
# Enable the asset pipeline
|
55
|
-
config.assets.enabled = true
|
56
|
-
|
57
|
-
# Version of your assets, change this if you want to expire all your assets
|
58
|
-
config.assets.version = '1.0'
|
59
54
|
end
|
60
55
|
end
|
@@ -4,6 +4,7 @@ RailsApp::Application.configure do
|
|
4
4
|
# In the development environment your application's code is reloaded on
|
5
5
|
# every request. This slows down response time but is perfect for development
|
6
6
|
# since you don't have to restart the web server when you make code changes.
|
7
|
+
config.eager_load = false if config.respond_to?(:eager_load)
|
7
8
|
config.cache_classes = false
|
8
9
|
|
9
10
|
# Log error messages when you accidentally call methods on nil.
|
@@ -29,9 +30,5 @@ RailsApp::Application.configure do
|
|
29
30
|
# with SQLite, MySQL, and PostgreSQL)
|
30
31
|
#config.active_record.auto_explain_threshold_in_seconds = 0.5
|
31
32
|
|
32
|
-
|
33
|
-
config.assets.compress = false
|
34
|
-
|
35
|
-
# Expands the lines which load the assets
|
36
|
-
config.assets.debug = true
|
33
|
+
config.hosts << "foo.example.com" if config.respond_to?(:hosts)
|
37
34
|
end
|
@@ -2,6 +2,7 @@ RailsApp::Application.configure do
|
|
2
2
|
# Settings specified here will take precedence over those in config/application.rb
|
3
3
|
|
4
4
|
# Code is not reloaded between requests
|
5
|
+
config.eager_load = true if config.respond_to?(:eager_load)
|
5
6
|
config.cache_classes = true
|
6
7
|
|
7
8
|
# Full error reports are disabled and caching is turned on
|
@@ -5,6 +5,7 @@ RailsApp::Application.configure do
|
|
5
5
|
# test suite. You never need to work with it otherwise. Remember that
|
6
6
|
# your test database is "scratch space" for the test suite and is wiped
|
7
7
|
# and recreated between test runs. Don't rely on the data there!
|
8
|
+
config.eager_load = true if config.respond_to?(:eager_load)
|
8
9
|
config.cache_classes = true
|
9
10
|
|
10
11
|
# Configure static asset server for tests with Cache-Control for performance
|
data/spec/spec_helper.rb
CHANGED
@@ -43,6 +43,28 @@ RSpec.configure do |config|
|
|
43
43
|
}
|
44
44
|
end
|
45
45
|
end
|
46
|
+
|
47
|
+
# To reset to default config
|
48
|
+
config.after do
|
49
|
+
SamlIdp.instance_variable_set(:@config, nil)
|
50
|
+
SamlIdp.configure do |c|
|
51
|
+
c.attributes = {
|
52
|
+
emailAddress: {
|
53
|
+
name: "email-address",
|
54
|
+
getter: ->(p) { "foo@example.com" }
|
55
|
+
}
|
56
|
+
}
|
57
|
+
|
58
|
+
c.name_id.formats = {
|
59
|
+
"1.1" => {
|
60
|
+
email_address: ->(p) { "foo@example.com" }
|
61
|
+
}
|
62
|
+
}
|
63
|
+
end
|
64
|
+
end
|
46
65
|
end
|
47
66
|
|
48
|
-
|
67
|
+
SamlIdp::Default::SERVICE_PROVIDER[:metadata_url] = 'https://example.com/meta'
|
68
|
+
SamlIdp::Default::SERVICE_PROVIDER[:response_hosts] = ['foo.example.com']
|
69
|
+
SamlIdp::Default::SERVICE_PROVIDER[:assertion_consumer_logout_service_url] = 'https://foo.example.com/saml/logout'
|
70
|
+
Capybara.default_host = "https://foo.example.com"
|
@@ -0,0 +1,12 @@
|
|
1
|
+
-----BEGIN CERTIFICATE REQUEST-----
|
2
|
+
MIIByTCCATICAQAwgYgxCzAJBgNVBAYTAmpwMQ4wDAYDVQQIDAVUb2t5bzELMAkG
|
3
|
+
A1UECgwCR1MxIDAeBgNVBAMMF2h0dHBzOi8vZm9vLmV4YW1wbGUuY29tMQwwCgYD
|
4
|
+
VQQHDANGb28xDDAKBgNVBAsMA0JvbzEeMBwGCSqGSIb3DQEJARYPZm9vQGV4YW1w
|
5
|
+
bGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8DVj2mVLQV7AjT+cn
|
6
|
+
Lv3kDnQFvAo3RdUeGGhplsYFacYByzNRD/jeguu1ahrvznDyZN8p3yB7OPbmt0r0
|
7
|
+
aGr+yYzPh6brgkf5u6FMtWTj94vLQuT/uyQGuzdBkiLb5mAWRMtm43oHXDK0v25J
|
8
|
+
tsG1PJnntkXfBDpFP1eWLO+jZwIDAQABoAAwDQYJKoZIhvcNAQENBQADgYEAd/J6
|
9
|
+
5zjrMhgjxuaMuWCiNN7IS4F9SKy+gEmhkpNVCpChbpggruaEIoERjDP/TkZn2dgL
|
10
|
+
VUeHTZB92t+wWfQbHNvEfbzqlV3XkuHkxewCwofnIV/k+8zG1Al5ELSKHehItxig
|
11
|
+
rnTuBrFYsd2j4HEVqLzm4NyCfL+xzn/D4U2ec50=
|
12
|
+
-----END CERTIFICATE REQUEST-----
|
@@ -0,0 +1,16 @@
|
|
1
|
+
-----BEGIN PRIVATE KEY-----
|
2
|
+
MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBALwNWPaZUtBXsCNP
|
3
|
+
5ycu/eQOdAW8CjdF1R4YaGmWxgVpxgHLM1EP+N6C67VqGu/OcPJk3ynfIHs49ua3
|
4
|
+
SvRoav7JjM+HpuuCR/m7oUy1ZOP3i8tC5P+7JAa7N0GSItvmYBZEy2bjegdcMrS/
|
5
|
+
bkm2wbU8mee2Rd8EOkU/V5Ys76NnAgMBAAECgYEArwclVHCkebIECPnnxbqhKNCj
|
6
|
+
AGtifsuKbrZ9CDoDGSq31xeQLdTV6BSm2nVlmOnmilWEuG4qx0Xf2CGlrBI78kmv
|
7
|
+
vHCfFdaGnTxbmYnD0HN0u4RK2trsxWO+rEkJk14JE2eVD6ZRPrq1UOSMgGPrQSMb
|
8
|
+
SuwAHUu/j94eL8BXuhECQQD3jTlo3Y4VPWttP6XPNqKDP+jRYJs5G0Bch//S9Qy7
|
9
|
+
QzmU9/yAUk0BEOyqYcLxinjJhoq6bR2fiIibn+77z3jtAkEAwnhLwkGYOb7Nt3V6
|
10
|
+
dQLKx1BP9dnYH7qG/sCmAs7GHPv4LGluaz4zsh2pdEDF/Xar4gwTzUpxYo8FpkCH
|
11
|
+
rf4nIwJAVfWnGr/cR4nVVNFGHUcGdXbqvFHEdLb+yWK8NZ+79Qap5w2Zk2GAtb8P
|
12
|
+
vzZFQCRqPuhGIegj4jLB5PBLRwtLHQJBAJiWyWL4ExikRUhBTr/HXBL+Sm9u6i0j
|
13
|
+
L89unBQx6LNPZhB6/Z/6Y5fLvG2ycWgLGJ06usLnOYaLEHS9x3hXpp8CQQCdtQHw
|
14
|
+
xeLBPhRDpfWWbSmFr+bFxyD/4iQHTHToIs3kaecn6OJ4rczIFpGm2Bm7f4X7F3H3
|
15
|
+
DDy4jZ0R6iDqCcQD
|
16
|
+
-----END PRIVATE KEY-----
|
@@ -0,0 +1,18 @@
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
2
|
+
MIIC2DCCAkGgAwIBAgIBADANBgkqhkiG9w0BAQ0FADCBiDELMAkGA1UEBhMCanAx
|
3
|
+
DjAMBgNVBAgMBVRva3lvMQswCQYDVQQKDAJHUzEgMB4GA1UEAwwXaHR0cHM6Ly9m
|
4
|
+
b28uZXhhbXBsZS5jb20xDDAKBgNVBAcMA0ZvbzEMMAoGA1UECwwDQm9vMR4wHAYJ
|
5
|
+
KoZIhvcNAQkBFg9mb29AZXhhbXBsZS5jb20wHhcNMjAwMTIzMDYyMzI5WhcNNDcw
|
6
|
+
NjA5MDYyMzI5WjCBiDELMAkGA1UEBhMCanAxDjAMBgNVBAgMBVRva3lvMQswCQYD
|
7
|
+
VQQKDAJHUzEgMB4GA1UEAwwXaHR0cHM6Ly9mb28uZXhhbXBsZS5jb20xDDAKBgNV
|
8
|
+
BAcMA0ZvbzEMMAoGA1UECwwDQm9vMR4wHAYJKoZIhvcNAQkBFg9mb29AZXhhbXBs
|
9
|
+
ZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALwNWPaZUtBXsCNP5ycu
|
10
|
+
/eQOdAW8CjdF1R4YaGmWxgVpxgHLM1EP+N6C67VqGu/OcPJk3ynfIHs49ua3SvRo
|
11
|
+
av7JjM+HpuuCR/m7oUy1ZOP3i8tC5P+7JAa7N0GSItvmYBZEy2bjegdcMrS/bkm2
|
12
|
+
wbU8mee2Rd8EOkU/V5Ys76NnAgMBAAGjUDBOMB0GA1UdDgQWBBQMtOtrh2VS/mh4
|
13
|
+
awGbKA37vVnw+zAfBgNVHSMEGDAWgBQMtOtrh2VS/mh4awGbKA37vVnw+zAMBgNV
|
14
|
+
HRMEBTADAQH/MA0GCSqGSIb3DQEBDQUAA4GBAHjTTm4Hyx1rfzygknc6q1dYwpEv
|
15
|
+
/3AsPiTnF4AfH/5kGIIXNzwg0ADsziFMJYRRR9eMu97CHQbr8gHt99P8uaen6cmJ
|
16
|
+
4VCwJLP2N8gZrycssimA3M83DWRRVZbxZhpuUWNajtYIxwyUbB7eRSJgz3Tc0opF
|
17
|
+
933YwucWuFzKSqn3
|
18
|
+
-----END CERTIFICATE-----
|
@@ -1,9 +1,9 @@
|
|
1
1
|
require 'saml_idp/logout_request_builder'
|
2
2
|
|
3
3
|
module SamlRequestMacros
|
4
|
-
def make_saml_request(requested_saml_acs_url = "https://foo.example.com/saml/consume")
|
4
|
+
def make_saml_request(requested_saml_acs_url = "https://foo.example.com/saml/consume", enable_secure_options = false)
|
5
5
|
auth_request = OneLogin::RubySaml::Authrequest.new
|
6
|
-
auth_url = auth_request.create(saml_settings(requested_saml_acs_url))
|
6
|
+
auth_url = auth_request.create(saml_settings(requested_saml_acs_url, enable_secure_options))
|
7
7
|
CGI.unescape(auth_url.split("=").last)
|
8
8
|
end
|
9
9
|
|
@@ -15,19 +15,81 @@ module SamlRequestMacros
|
|
15
15
|
'some_name_id',
|
16
16
|
OpenSSL::Digest::SHA256
|
17
17
|
)
|
18
|
-
request_builder.
|
18
|
+
Base64.strict_encode64(request_builder.signed)
|
19
19
|
end
|
20
20
|
|
21
|
-
def
|
21
|
+
def generate_sp_metadata(saml_acs_url = "https://foo.example.com/saml/consume", enable_secure_options = false)
|
22
|
+
sp_metadata = OneLogin::RubySaml::Metadata.new
|
23
|
+
sp_metadata.generate(saml_settings(saml_acs_url, enable_secure_options), true)
|
24
|
+
end
|
25
|
+
|
26
|
+
def saml_settings(saml_acs_url = "https://foo.example.com/saml/consume", enable_secure_options = false)
|
22
27
|
settings = OneLogin::RubySaml::Settings.new
|
23
28
|
settings.assertion_consumer_service_url = saml_acs_url
|
24
29
|
settings.issuer = "http://example.com/issuer"
|
25
30
|
settings.idp_sso_target_url = "http://idp.com/saml/idp"
|
31
|
+
settings.assertion_consumer_logout_service_url = 'https://foo.example.com/saml/logout'
|
26
32
|
settings.idp_cert_fingerprint = SamlIdp::Default::FINGERPRINT
|
27
33
|
settings.name_identifier_format = SamlIdp::Default::NAME_ID_FORMAT
|
34
|
+
add_securty_options(settings) if enable_secure_options
|
28
35
|
settings
|
29
36
|
end
|
30
37
|
|
38
|
+
def add_securty_options(settings, authn_requests_signed: true,
|
39
|
+
embed_sign: true,
|
40
|
+
logout_requests_signed: true,
|
41
|
+
logout_responses_signed: true,
|
42
|
+
digest_method: XMLSecurity::Document::SHA256,
|
43
|
+
signature_method: XMLSecurity::Document::RSA_SHA256,
|
44
|
+
assertions_signed: true)
|
45
|
+
# Security section
|
46
|
+
settings.idp_cert = SamlIdp::Default::X509_CERTIFICATE
|
47
|
+
# Signed embedded singature
|
48
|
+
settings.security[:authn_requests_signed] = authn_requests_signed
|
49
|
+
settings.security[:embed_sign] = embed_sign
|
50
|
+
settings.security[:logout_requests_signed] = logout_requests_signed
|
51
|
+
settings.security[:logout_responses_signed] = logout_responses_signed
|
52
|
+
settings.security[:metadata_signed] = digest_method
|
53
|
+
settings.security[:digest_method] = digest_method
|
54
|
+
settings.security[:signature_method] = signature_method
|
55
|
+
settings.security[:want_assertions_signed] = assertions_signed
|
56
|
+
settings.private_key = sp_pv_key
|
57
|
+
settings.certificate = sp_x509_cert
|
58
|
+
end
|
59
|
+
|
60
|
+
def idp_configure(saml_acs_url = "https://foo.example.com/saml/consume", enable_secure_options = false)
|
61
|
+
SamlIdp.configure do |config|
|
62
|
+
config.x509_certificate = SamlIdp::Default::X509_CERTIFICATE
|
63
|
+
config.secret_key = SamlIdp::Default::SECRET_KEY
|
64
|
+
config.password = nil
|
65
|
+
config.algorithm = :sha256
|
66
|
+
config.organization_name = 'idp.com'
|
67
|
+
config.organization_url = 'http://idp.com'
|
68
|
+
config.base_saml_location = 'http://idp.com/saml/idp'
|
69
|
+
config.single_logout_service_post_location = 'http://idp.com/saml/idp/logout'
|
70
|
+
config.single_logout_service_redirect_location = 'http://idp.com/saml/idp/logout'
|
71
|
+
config.attribute_service_location = 'http://idp.com/saml/idp/attribute'
|
72
|
+
config.single_service_post_location = 'http://idp.com/saml/idp/sso'
|
73
|
+
config.name_id.formats = SamlIdp::Default::NAME_ID_FORMAT
|
74
|
+
config.service_provider.metadata_persister = lambda { |_identifier, _service_provider|
|
75
|
+
raw_metadata = generate_sp_metadata(saml_acs_url, enable_secure_options)
|
76
|
+
SamlIdp::IncomingMetadata.new(raw_metadata).to_h
|
77
|
+
}
|
78
|
+
config.service_provider.persisted_metadata_getter = lambda { |_identifier, _settings|
|
79
|
+
raw_metadata = generate_sp_metadata(saml_acs_url, enable_secure_options)
|
80
|
+
SamlIdp::IncomingMetadata.new(raw_metadata).to_h
|
81
|
+
}
|
82
|
+
config.service_provider.finder = lambda { |_issuer_or_entity_id|
|
83
|
+
{
|
84
|
+
response_hosts: [URI(saml_acs_url).host],
|
85
|
+
acs_url: saml_acs_url,
|
86
|
+
cert: sp_x509_cert,
|
87
|
+
fingerprint: SamlIdp::Fingerprint.certificate_digest(sp_x509_cert)
|
88
|
+
}
|
89
|
+
}
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
31
93
|
def print_pretty_xml(xml_string)
|
32
94
|
doc = REXML::Document.new xml_string
|
33
95
|
outbuf = ""
|
@@ -58,4 +58,14 @@ module SecurityHelpers
|
|
58
58
|
def r1_signature_2
|
59
59
|
@signature2 ||= File.read(File.join(File.dirname(__FILE__), 'certificates', 'r1_certificate2_base64'))
|
60
60
|
end
|
61
|
+
|
62
|
+
# Generated by SAML tool https://www.samltool.com/self_signed_certs.php
|
63
|
+
def sp_pv_key
|
64
|
+
@sp_pv_key ||= File.read(File.join(File.dirname(__FILE__), 'certificates', 'sp_private_key.pem'))
|
65
|
+
end
|
66
|
+
|
67
|
+
# Generated by SAML tool https://www.samltool.com/self_signed_certs.php, expired date is 9999
|
68
|
+
def sp_x509_cert
|
69
|
+
@sp_x509_cert ||= File.read(File.join(File.dirname(__FILE__), 'certificates', 'sp_x509_cert.crt'))
|
70
|
+
end
|
61
71
|
end
|
data/spec/xml_security_spec.rb
CHANGED
@@ -7,7 +7,7 @@ module SamlIdp
|
|
7
7
|
let(:base64cert) { document.elements["//ds:X509Certificate"].text }
|
8
8
|
|
9
9
|
it "it run validate without throwing NS related exceptions" do
|
10
|
-
document.validate_doc(base64cert, true).
|
10
|
+
expect(document.validate_doc(base64cert, true)).to be_falsey
|
11
11
|
end
|
12
12
|
|
13
13
|
it "it run validate with throwing NS related exceptions" do
|
@@ -57,22 +57,22 @@ module SamlIdp
|
|
57
57
|
describe "Algorithms" do
|
58
58
|
it "validate using SHA1" do
|
59
59
|
document = XMLSecurity::SignedDocument.new(fixture(:adfs_response_sha1, false))
|
60
|
-
document.validate("F1:3C:6B:80:90:5A:03:0E:6C:91:3E:5D:15:FA:DD:B0:16:45:48:72").
|
60
|
+
expect(document.validate("F1:3C:6B:80:90:5A:03:0E:6C:91:3E:5D:15:FA:DD:B0:16:45:48:72")).to be_truthy
|
61
61
|
end
|
62
62
|
|
63
63
|
it "validate using SHA256" do
|
64
64
|
document = XMLSecurity::SignedDocument.new(fixture(:adfs_response_sha256, false))
|
65
|
-
document.validate("28:74:9B:E8:1F:E8:10:9C:A8:7C:A9:C3:E3:C5:01:6C:92:1C:B4:BA").
|
65
|
+
expect(document.validate("28:74:9B:E8:1F:E8:10:9C:A8:7C:A9:C3:E3:C5:01:6C:92:1C:B4:BA")).to be_truthy
|
66
66
|
end
|
67
67
|
|
68
68
|
it "validate using SHA384" do
|
69
69
|
document = XMLSecurity::SignedDocument.new(fixture(:adfs_response_sha384, false))
|
70
|
-
document.validate("F1:3C:6B:80:90:5A:03:0E:6C:91:3E:5D:15:FA:DD:B0:16:45:48:72").
|
70
|
+
expect(document.validate("F1:3C:6B:80:90:5A:03:0E:6C:91:3E:5D:15:FA:DD:B0:16:45:48:72")).to be_truthy
|
71
71
|
end
|
72
72
|
|
73
73
|
it "validate using SHA512" do
|
74
74
|
document = XMLSecurity::SignedDocument.new(fixture(:adfs_response_sha512, false))
|
75
|
-
document.validate("F1:3C:6B:80:90:5A:03:0E:6C:91:3E:5D:15:FA:DD:B0:16:45:48:72").
|
75
|
+
expect(document.validate("F1:3C:6B:80:90:5A:03:0E:6C:91:3E:5D:15:FA:DD:B0:16:45:48:72")).to be_truthy
|
76
76
|
end
|
77
77
|
end
|
78
78
|
|
@@ -83,7 +83,7 @@ module SamlIdp
|
|
83
83
|
document = XMLSecurity::SignedDocument.new(response)
|
84
84
|
inclusive_namespaces = document.send(:extract_inclusive_namespaces)
|
85
85
|
|
86
|
-
inclusive_namespaces.
|
86
|
+
expect(inclusive_namespaces).to eq %w[xs]
|
87
87
|
end
|
88
88
|
|
89
89
|
it "support implicit namespace resolution for exclusive canonicalization" do
|
@@ -91,7 +91,7 @@ module SamlIdp
|
|
91
91
|
document = XMLSecurity::SignedDocument.new(response)
|
92
92
|
inclusive_namespaces = document.send(:extract_inclusive_namespaces)
|
93
93
|
|
94
|
-
inclusive_namespaces.
|
94
|
+
expect(inclusive_namespaces).to eq %w[#default saml ds xs xsi]
|
95
95
|
end
|
96
96
|
|
97
97
|
it "return an empty list when inclusive namespace element is missing" do
|
@@ -101,7 +101,7 @@ module SamlIdp
|
|
101
101
|
document = XMLSecurity::SignedDocument.new(response)
|
102
102
|
inclusive_namespaces = document.send(:extract_inclusive_namespaces)
|
103
103
|
|
104
|
-
inclusive_namespaces.
|
104
|
+
expect(inclusive_namespaces).to be_empty
|
105
105
|
end
|
106
106
|
end
|
107
107
|
|
@@ -116,20 +116,20 @@ module SamlIdp
|
|
116
116
|
|
117
117
|
it "be able to validate a good response" do
|
118
118
|
Timecop.freeze Time.parse('2012-11-28 17:55:00 UTC') do
|
119
|
-
response.
|
120
|
-
response.
|
119
|
+
allow(response).to receive(:validate_subject_confirmation).and_return(true)
|
120
|
+
expect(response).to be_is_valid
|
121
121
|
end
|
122
122
|
end
|
123
123
|
|
124
124
|
it "fail before response is valid" do
|
125
125
|
Timecop.freeze Time.parse('2012-11-20 17:55:00 UTC') do
|
126
|
-
response.
|
126
|
+
expect(response).to_not be_is_valid
|
127
127
|
end
|
128
128
|
end
|
129
129
|
|
130
130
|
it "fail after response expires" do
|
131
131
|
Timecop.freeze Time.parse('2012-11-30 17:55:00 UTC') do
|
132
|
-
response.
|
132
|
+
expect(response).to_not be_is_valid
|
133
133
|
end
|
134
134
|
end
|
135
135
|
end
|