saml_idp 0.10.0 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/lib/saml_idp/configurator.rb +4 -1
- data/lib/saml_idp/controller.rb +3 -3
- data/lib/saml_idp/incoming_metadata.rb +13 -0
- data/lib/saml_idp/metadata_builder.rb +23 -8
- data/lib/saml_idp/persisted_metadata.rb +4 -0
- data/lib/saml_idp/request.rb +8 -3
- data/lib/saml_idp/service_provider.rb +1 -6
- data/lib/saml_idp/version.rb +1 -1
- data/saml_idp.gemspec +0 -1
- data/spec/lib/saml_idp/configurator_spec.rb +1 -0
- data/spec/lib/saml_idp/controller_spec.rb +24 -0
- data/spec/lib/saml_idp/incoming_metadata_spec.rb +15 -1
- data/spec/lib/saml_idp/metadata_builder_spec.rb +23 -0
- data/spec/spec_helper.rb +19 -0
- 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 +62 -3
- data/spec/support/security_helpers.rb +10 -0
- metadata +9 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f04deecaf7c0bd7c5655134d314a4b95b9438b24b67e83d7b160d9fa2232f2fc
|
4
|
+
data.tar.gz: b999a0a1f97e85e34704bfe35d3dddb89eebcfbfe1723be5e9dfcfb17e511ef5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 94921b45008f31783c0428992b9cad6b4b1098ad312fd721987d0d27f89921f286f7bd8960237b5f371f8ccb23cac1a6c8b6c7aa110fcf4318a0b63b52497e9e
|
7
|
+
data.tar.gz: e142a4c38d3604dc033d0cfef0a298fbb094d5d36939518558aa219d6bd16ca753960fcc553c076e9969e031946e56d10ef3ba0c1505fcf9df3f7ee62ecdab11
|
data/README.md
CHANGED
@@ -111,7 +111,7 @@ CERT
|
|
111
111
|
# config.organization_name = "Your Organization"
|
112
112
|
# config.organization_url = "http://example.com"
|
113
113
|
# config.base_saml_location = "#{base}/saml"
|
114
|
-
# config.reference_id_generator # Default: -> {
|
114
|
+
# config.reference_id_generator # Default: -> { SecureRandom.uuid }
|
115
115
|
# config.single_logout_service_post_location = "#{base}/saml/logout"
|
116
116
|
# config.single_logout_service_redirect_location = "#{base}/saml/logout"
|
117
117
|
# config.attribute_service_location = "#{base}/saml/attributes"
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
require 'ostruct'
|
3
|
+
require 'securerandom'
|
4
|
+
|
3
5
|
module SamlIdp
|
4
6
|
class Configurator
|
5
7
|
attr_accessor :x509_certificate
|
@@ -13,6 +15,7 @@ module SamlIdp
|
|
13
15
|
attr_accessor :reference_id_generator
|
14
16
|
attr_accessor :attribute_service_location
|
15
17
|
attr_accessor :single_service_post_location
|
18
|
+
attr_accessor :single_service_redirect_location
|
16
19
|
attr_accessor :single_logout_service_post_location
|
17
20
|
attr_accessor :single_logout_service_redirect_location
|
18
21
|
attr_accessor :attributes
|
@@ -24,7 +27,7 @@ module SamlIdp
|
|
24
27
|
self.x509_certificate = Default::X509_CERTIFICATE
|
25
28
|
self.secret_key = Default::SECRET_KEY
|
26
29
|
self.algorithm = :sha1
|
27
|
-
self.reference_id_generator = ->() {
|
30
|
+
self.reference_id_generator = ->() { SecureRandom.uuid }
|
28
31
|
self.service_provider = OpenStruct.new
|
29
32
|
self.service_provider.finder = ->(_) { Default::SERVICE_PROVIDER }
|
30
33
|
self.service_provider.metadata_persister = ->(id, settings) { }
|
data/lib/saml_idp/controller.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
require 'openssl'
|
3
3
|
require 'base64'
|
4
4
|
require 'time'
|
5
|
-
require '
|
5
|
+
require 'securerandom'
|
6
6
|
require 'saml_idp/request'
|
7
7
|
require 'saml_idp/logout_response_builder'
|
8
8
|
module SamlIdp
|
@@ -126,11 +126,11 @@ module SamlIdp
|
|
126
126
|
end
|
127
127
|
|
128
128
|
def get_saml_response_id
|
129
|
-
|
129
|
+
SecureRandom.uuid
|
130
130
|
end
|
131
131
|
|
132
132
|
def get_saml_reference_id
|
133
|
-
|
133
|
+
SecureRandom.uuid
|
134
134
|
end
|
135
135
|
|
136
136
|
def default_algorithm
|
@@ -34,6 +34,19 @@ module SamlIdp
|
|
34
34
|
end
|
35
35
|
hashable :sign_assertions
|
36
36
|
|
37
|
+
def sign_authn_request
|
38
|
+
doc = xpath(
|
39
|
+
"//md:SPSSODescriptor",
|
40
|
+
ds: signature_namespace,
|
41
|
+
md: metadata_namespace
|
42
|
+
).first
|
43
|
+
if (doc && !doc['AuthnRequestsSigned'].nil?)
|
44
|
+
return doc['AuthnRequestsSigned'].strip.downcase == 'true'
|
45
|
+
end
|
46
|
+
return false
|
47
|
+
end
|
48
|
+
hashable :sign_authn_request
|
49
|
+
|
37
50
|
def display_name
|
38
51
|
role_descriptor_document.present? ? role_descriptor_document["ServiceDisplayName"] : ""
|
39
52
|
end
|
@@ -24,13 +24,15 @@ module SamlIdp
|
|
24
24
|
|
25
25
|
entity.IDPSSODescriptor protocolSupportEnumeration: protocol_enumeration do |descriptor|
|
26
26
|
build_key_descriptor descriptor
|
27
|
-
descriptor
|
28
|
-
|
29
|
-
|
30
|
-
|
27
|
+
build_endpoint descriptor, [
|
28
|
+
{ tag: 'SingleLogoutService', url: single_logout_service_post_location, bind: 'HTTP-POST' },
|
29
|
+
{ tag: 'SingleLogoutService', url: single_logout_service_redirect_location, bind: 'HTTP-Redirect'}
|
30
|
+
]
|
31
31
|
build_name_id_formats descriptor
|
32
|
-
descriptor
|
33
|
-
|
32
|
+
build_endpoint descriptor, [
|
33
|
+
{ tag: 'SingleSignOnService', url: single_service_post_location, bind: 'HTTP-POST' },
|
34
|
+
{ tag: 'SingleSignOnService', url: single_service_redirect_location, bind: 'HTTP-Redirect'}
|
35
|
+
]
|
34
36
|
build_attribute descriptor
|
35
37
|
end
|
36
38
|
|
@@ -38,8 +40,9 @@ module SamlIdp
|
|
38
40
|
build_key_descriptor authority_descriptor
|
39
41
|
build_organization authority_descriptor
|
40
42
|
build_contact authority_descriptor
|
41
|
-
authority_descriptor
|
42
|
-
|
43
|
+
build_endpoint authority_descriptor, [
|
44
|
+
{ tag: 'AttributeService', url: attribute_service_location, bind: 'HTTP-Redirect' }
|
45
|
+
]
|
43
46
|
build_name_id_formats authority_descriptor
|
44
47
|
build_attribute authority_descriptor
|
45
48
|
end
|
@@ -69,6 +72,17 @@ module SamlIdp
|
|
69
72
|
end
|
70
73
|
private :build_name_id_formats
|
71
74
|
|
75
|
+
def build_endpoint(el, end_points)
|
76
|
+
end_points.each do |ep|
|
77
|
+
next unless ep[:url].present?
|
78
|
+
|
79
|
+
el.tag! ep[:tag],
|
80
|
+
Binding: "urn:oasis:names:tc:SAML:2.0:bindings:#{ep[:bind]}",
|
81
|
+
Location: ep[:url]
|
82
|
+
end
|
83
|
+
end
|
84
|
+
private :build_endpoint
|
85
|
+
|
72
86
|
def build_attribute(el)
|
73
87
|
attributes.each do |attribute|
|
74
88
|
el.tag! "saml:Attribute",
|
@@ -151,6 +165,7 @@ module SamlIdp
|
|
151
165
|
organization_url
|
152
166
|
attribute_service_location
|
153
167
|
single_service_post_location
|
168
|
+
single_service_redirect_location
|
154
169
|
single_logout_service_post_location
|
155
170
|
single_logout_service_redirect_location
|
156
171
|
technical_contact
|
data/lib/saml_idp/request.rb
CHANGED
@@ -115,9 +115,14 @@ module SamlIdp
|
|
115
115
|
end
|
116
116
|
|
117
117
|
def valid_signature?
|
118
|
-
# Force signatures for logout requests because there is no other
|
119
|
-
#
|
120
|
-
service_provider.
|
118
|
+
# Force signatures for logout requests because there is no other protection against a cross-site DoS.
|
119
|
+
# Validate signature when metadata specify AuthnRequest should be signed
|
120
|
+
metadata = service_provider.current_metadata
|
121
|
+
if logout_request? || authn_request? && metadata.respond_to?(:sign_authn_request?) && metadata.sign_authn_request?
|
122
|
+
document.valid_signature?(service_provider.fingerprint)
|
123
|
+
else
|
124
|
+
true
|
125
|
+
end
|
121
126
|
end
|
122
127
|
|
123
128
|
def service_provider?
|
@@ -22,18 +22,13 @@ module SamlIdp
|
|
22
22
|
end
|
23
23
|
|
24
24
|
def valid_signature?(doc, require_signature = false)
|
25
|
-
if require_signature ||
|
25
|
+
if require_signature || attributes[:validate_signature]
|
26
26
|
doc.valid_signature?(fingerprint)
|
27
27
|
else
|
28
28
|
true
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
|
-
def should_validate_signature?
|
33
|
-
attributes[:validate_signature] ||
|
34
|
-
current_metadata.respond_to?(:sign_assertions?) && current_metadata.sign_assertions?
|
35
|
-
end
|
36
|
-
|
37
32
|
def refresh_metadata
|
38
33
|
fresh = fresh_incoming_metadata
|
39
34
|
if valid_signature?(fresh.document)
|
data/lib/saml_idp/version.rb
CHANGED
data/saml_idp.gemspec
CHANGED
@@ -9,6 +9,7 @@ module SamlIdp
|
|
9
9
|
it { should respond_to :base_saml_location }
|
10
10
|
it { should respond_to :reference_id_generator }
|
11
11
|
it { should respond_to :attribute_service_location }
|
12
|
+
it { should respond_to :single_service_redirect_location }
|
12
13
|
it { should respond_to :single_service_post_location }
|
13
14
|
it { should respond_to :single_logout_service_post_location }
|
14
15
|
it { should respond_to :single_logout_service_redirect_location }
|
@@ -21,6 +21,30 @@ describe SamlIdp::Controller do
|
|
21
21
|
expect(saml_acs_url).to eq(requested_saml_acs_url)
|
22
22
|
end
|
23
23
|
|
24
|
+
context "When SP metadata required to validate auth request signature" do
|
25
|
+
before do
|
26
|
+
idp_configure("https://foo.example.com/saml/consume", true)
|
27
|
+
params[:SAMLRequest] = make_saml_request("https://foo.example.com/saml/consume", true)
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'SP metadata sign_authn_request attribute should be true' do
|
31
|
+
# Signed auth request will be true in the metadata
|
32
|
+
expect(SamlIdp.config.service_provider.persisted_metadata_getter.call(nil,nil)[:sign_authn_request]).to eq(true)
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should call xml signature validation method' do
|
36
|
+
signed_doc = SamlIdp::XMLSecurity::SignedDocument.new(params[:SAMLRequest])
|
37
|
+
allow(signed_doc).to receive(:validate).and_return(true)
|
38
|
+
allow(SamlIdp::XMLSecurity::SignedDocument).to receive(:new).and_return(signed_doc)
|
39
|
+
validate_saml_request
|
40
|
+
expect(signed_doc).to have_received(:validate).once
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'should successfully validate signature' do
|
44
|
+
expect(validate_saml_request).to eq(true)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
24
48
|
context "SAML Responses" do
|
25
49
|
let(:principal) { double email_address: "foo@example.com" }
|
26
50
|
let (:encryption_opts) do
|
@@ -3,7 +3,7 @@ module SamlIdp
|
|
3
3
|
|
4
4
|
metadata_1 = <<-eos
|
5
5
|
<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="test" entityID="https://test-saml.com/saml">
|
6
|
-
<md:SPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol" AuthnRequestsSigned="
|
6
|
+
<md:SPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol" AuthnRequestsSigned="false" WantAssertionsSigned="false">
|
7
7
|
</md:SPSSODescriptor>
|
8
8
|
</md:EntityDescriptor>
|
9
9
|
eos
|
@@ -22,10 +22,18 @@ module SamlIdp
|
|
22
22
|
</md:EntityDescriptor>
|
23
23
|
eos
|
24
24
|
|
25
|
+
metadata_4 = <<-eos
|
26
|
+
<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="test" entityID="https://test-saml.com/saml">
|
27
|
+
<md:SPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
|
28
|
+
</md:SPSSODescriptor>
|
29
|
+
</md:EntityDescriptor>
|
30
|
+
eos
|
31
|
+
|
25
32
|
describe IncomingMetadata do
|
26
33
|
it 'should properly set sign_assertions to false' do
|
27
34
|
metadata = SamlIdp::IncomingMetadata.new(metadata_1)
|
28
35
|
expect(metadata.sign_assertions).to eq(false)
|
36
|
+
expect(metadata.sign_authn_request).to eq(false)
|
29
37
|
end
|
30
38
|
|
31
39
|
it 'should properly set entity_id as https://test-saml.com/saml' do
|
@@ -36,11 +44,17 @@ module SamlIdp
|
|
36
44
|
it 'should properly set sign_assertions to true' do
|
37
45
|
metadata = SamlIdp::IncomingMetadata.new(metadata_2)
|
38
46
|
expect(metadata.sign_assertions).to eq(true)
|
47
|
+
expect(metadata.sign_authn_request).to eq(true)
|
39
48
|
end
|
40
49
|
|
41
50
|
it 'should properly set sign_assertions to false when WantAssertionsSigned is not included' do
|
42
51
|
metadata = SamlIdp::IncomingMetadata.new(metadata_3)
|
43
52
|
expect(metadata.sign_assertions).to eq(false)
|
44
53
|
end
|
54
|
+
|
55
|
+
it 'should properly set sign_authn_request to false when AuthnRequestsSigned is not included' do
|
56
|
+
metadata = SamlIdp::IncomingMetadata.new(metadata_4)
|
57
|
+
expect(metadata.sign_authn_request).to eq(false)
|
58
|
+
end
|
45
59
|
end
|
46
60
|
end
|
@@ -11,7 +11,30 @@ module SamlIdp
|
|
11
11
|
|
12
12
|
it "includes logout element" do
|
13
13
|
subject.configurator.single_logout_service_post_location = 'https://example.com/saml/logout'
|
14
|
+
subject.configurator.single_logout_service_redirect_location = 'https://example.com/saml/logout'
|
14
15
|
expect(subject.fresh).to match('<SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://example.com/saml/logout"/>')
|
16
|
+
expect(subject.fresh).to match('<SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://example.com/saml/logout"/>')
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'will not includes empty logout endpoint' do
|
20
|
+
subject.configurator.single_logout_service_post_location = ''
|
21
|
+
subject.configurator.single_logout_service_redirect_location = nil
|
22
|
+
expect(subject.fresh).not_to match('<SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"')
|
23
|
+
expect(subject.fresh).not_to match('<SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"')
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'will includes sso element' do
|
27
|
+
subject.configurator.single_service_post_location = 'https://example.com/saml/sso'
|
28
|
+
subject.configurator.single_service_redirect_location = 'https://example.com/saml/sso'
|
29
|
+
expect(subject.fresh).to match('<SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://example.com/saml/sso"/>')
|
30
|
+
expect(subject.fresh).to match('<SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://example.com/saml/sso"/>')
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'will not includes empty sso element' do
|
34
|
+
subject.configurator.single_service_post_location = ''
|
35
|
+
subject.configurator.single_service_redirect_location = nil
|
36
|
+
expect(subject.fresh).not_to match('<SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"')
|
37
|
+
expect(subject.fresh).not_to match('<SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"')
|
15
38
|
end
|
16
39
|
|
17
40
|
context "technical contact" do
|
data/spec/spec_helper.rb
CHANGED
@@ -43,6 +43,25 @@ 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'
|
@@ -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
|
|
@@ -18,7 +18,12 @@ module SamlRequestMacros
|
|
18
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"
|
@@ -26,9 +31,63 @@ module SamlRequestMacros
|
|
26
31
|
settings.assertion_consumer_logout_service_url = 'https://foo.example.com/saml/logout'
|
27
32
|
settings.idp_cert_fingerprint = SamlIdp::Default::FINGERPRINT
|
28
33
|
settings.name_identifier_format = SamlIdp::Default::NAME_ID_FORMAT
|
34
|
+
add_securty_options(settings) if enable_secure_options
|
29
35
|
settings
|
30
36
|
end
|
31
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
|
+
# Security section
|
45
|
+
settings.idp_cert = SamlIdp::Default::X509_CERTIFICATE
|
46
|
+
# Signed embedded singature
|
47
|
+
settings.security[:authn_requests_signed] = authn_requests_signed
|
48
|
+
settings.security[:embed_sign] = embed_sign
|
49
|
+
settings.security[:logout_requests_signed] = logout_requests_signed
|
50
|
+
settings.security[:logout_responses_signed] = logout_responses_signed
|
51
|
+
settings.security[:metadata_signed] = digest_method
|
52
|
+
settings.security[:digest_method] = digest_method
|
53
|
+
settings.security[:signature_method] = signature_method
|
54
|
+
settings.private_key = sp_pv_key
|
55
|
+
settings.certificate = sp_x509_cert
|
56
|
+
end
|
57
|
+
|
58
|
+
def idp_configure(saml_acs_url = "https://foo.example.com/saml/consume", enable_secure_options = false)
|
59
|
+
SamlIdp.configure do |config|
|
60
|
+
config.x509_certificate = SamlIdp::Default::X509_CERTIFICATE
|
61
|
+
config.secret_key = SamlIdp::Default::SECRET_KEY
|
62
|
+
config.password = nil
|
63
|
+
config.algorithm = :sha256
|
64
|
+
config.organization_name = 'idp.com'
|
65
|
+
config.organization_url = 'http://idp.com'
|
66
|
+
config.base_saml_location = 'http://idp.com/saml/idp'
|
67
|
+
config.single_logout_service_post_location = 'http://idp.com/saml/idp/logout'
|
68
|
+
config.single_logout_service_redirect_location = 'http://idp.com/saml/idp/logout'
|
69
|
+
config.attribute_service_location = 'http://idp.com/saml/idp/attribute'
|
70
|
+
config.single_service_post_location = 'http://idp.com/saml/idp/sso'
|
71
|
+
config.name_id.formats = SamlIdp::Default::NAME_ID_FORMAT
|
72
|
+
config.service_provider.metadata_persister = lambda { |_identifier, _service_provider|
|
73
|
+
raw_metadata = generate_sp_metadata(saml_acs_url, enable_secure_options)
|
74
|
+
SamlIdp::IncomingMetadata.new(raw_metadata).to_h
|
75
|
+
}
|
76
|
+
config.service_provider.persisted_metadata_getter = lambda { |_identifier, _settings|
|
77
|
+
raw_metadata = generate_sp_metadata(saml_acs_url, enable_secure_options)
|
78
|
+
SamlIdp::IncomingMetadata.new(raw_metadata).to_h
|
79
|
+
}
|
80
|
+
config.service_provider.finder = lambda { |_issuer_or_entity_id|
|
81
|
+
{
|
82
|
+
response_hosts: [URI(saml_acs_url).host],
|
83
|
+
acs_url: saml_acs_url,
|
84
|
+
cert: sp_x509_cert,
|
85
|
+
fingerprint: Digest::SHA256.hexdigest(OpenSSL::X509::Certificate.new(sp_x509_cert).to_der).scan(/../).join(':')
|
86
|
+
}
|
87
|
+
}
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
32
91
|
def print_pretty_xml(xml_string)
|
33
92
|
doc = REXML::Document.new xml_string
|
34
93
|
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
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: saml_idp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.11.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jon Phenow
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-10-
|
11
|
+
date: 2020-10-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -24,20 +24,6 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '3.2'
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: uuid
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - ">="
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '2.3'
|
34
|
-
type: :runtime
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - ">="
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '2.3'
|
41
27
|
- !ruby/object:Gem::Dependency
|
42
28
|
name: builder
|
43
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -333,6 +319,9 @@ files:
|
|
333
319
|
- spec/spec_helper.rb
|
334
320
|
- spec/support/certificates/certificate1
|
335
321
|
- spec/support/certificates/r1_certificate2_base64
|
322
|
+
- spec/support/certificates/sp_cert_req.csr
|
323
|
+
- spec/support/certificates/sp_private_key.pem
|
324
|
+
- spec/support/certificates/sp_x509_cert.crt
|
336
325
|
- spec/support/responses/adfs_response_sha1.xml
|
337
326
|
- spec/support/responses/adfs_response_sha256.xml
|
338
327
|
- spec/support/responses/adfs_response_sha384.xml
|
@@ -361,7 +350,7 @@ metadata:
|
|
361
350
|
homepage_uri: https://github.com/saml-idp/saml_idp
|
362
351
|
source_code_uri: https://github.com/saml-idp/saml_idp
|
363
352
|
bug_tracker_uri: https://github.com/saml-idp/saml_idp/issues
|
364
|
-
documentation_uri: http://rdoc.info/gems/saml_idp/0.
|
353
|
+
documentation_uri: http://rdoc.info/gems/saml_idp/0.11.0
|
365
354
|
post_install_message: |
|
366
355
|
If you're just recently updating saml_idp - please be aware we've changed the default
|
367
356
|
certificate. See the PR and a description of why we've done this here:
|
@@ -470,6 +459,9 @@ test_files:
|
|
470
459
|
- spec/spec_helper.rb
|
471
460
|
- spec/support/certificates/certificate1
|
472
461
|
- spec/support/certificates/r1_certificate2_base64
|
462
|
+
- spec/support/certificates/sp_cert_req.csr
|
463
|
+
- spec/support/certificates/sp_private_key.pem
|
464
|
+
- spec/support/certificates/sp_x509_cert.crt
|
473
465
|
- spec/support/responses/adfs_response_sha1.xml
|
474
466
|
- spec/support/responses/adfs_response_sha256.xml
|
475
467
|
- spec/support/responses/adfs_response_sha384.xml
|