saml_idp 0.10.0 → 0.11.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1a69c8d8c945c47e87cb526d4dfcf5f30c2f687d03b33e99013eef63acbe4377
4
- data.tar.gz: 4933af3eb5cb686111307cd86d074ab45cc879a253f7a0833aaaa3262add74db
3
+ metadata.gz: f04deecaf7c0bd7c5655134d314a4b95b9438b24b67e83d7b160d9fa2232f2fc
4
+ data.tar.gz: b999a0a1f97e85e34704bfe35d3dddb89eebcfbfe1723be5e9dfcfb17e511ef5
5
5
  SHA512:
6
- metadata.gz: 61d5e37801af0e41b71cdf0bdbac6cac61e0b86969de75c27519b74ede55e3b9c2ec7c2dfc8bff9ef82ec1fa1a870c9483fe31e65e17d645fdf6cab5205ac2d1
7
- data.tar.gz: a1594a0f1da0694a93c478259dcece4d38c30863564aa653b585bdae9ef6834699b784ff151b7569e7cccee22b41e0b5a37f0a5151a89b664feda92005f3f21f
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: -> { UUID.generate }
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 = ->() { UUID.generate }
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) { }
@@ -2,7 +2,7 @@
2
2
  require 'openssl'
3
3
  require 'base64'
4
4
  require 'time'
5
- require 'uuid'
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
- UUID.generate
129
+ SecureRandom.uuid
130
130
  end
131
131
 
132
132
  def get_saml_reference_id
133
- UUID.generate
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.SingleLogoutService Binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST",
28
- Location: single_logout_service_post_location
29
- descriptor.SingleLogoutService Binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect",
30
- Location: single_logout_service_redirect_location
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.SingleSignOnService Binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect",
33
- Location: single_service_post_location
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.AttributeService Binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect",
42
- Location: attribute_service_location
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
@@ -6,5 +6,9 @@ module SamlIdp
6
6
  def sign_assertions?
7
7
  !!attributes[:sign_assertions]
8
8
  end
9
+
10
+ def sign_authn_request?
11
+ !!attributes[:sign_authn_request]
12
+ end
9
13
  end
10
14
  end
@@ -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
- # protection against a cross-site DoS.
120
- service_provider.valid_signature?(document, logout_request?)
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 || should_validate_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)
@@ -1,4 +1,4 @@
1
1
  # encoding: utf-8
2
2
  module SamlIdp
3
- VERSION = '0.10.0'
3
+ VERSION = '0.11.0'
4
4
  end
@@ -44,7 +44,6 @@ section of the README.
44
44
  INST
45
45
 
46
46
  s.add_dependency('activesupport', '>= 3.2')
47
- s.add_dependency('uuid', '>= 2.3')
48
47
  s.add_dependency('builder', '>= 3.0')
49
48
  s.add_dependency('nokogiri', '>= 1.6.2')
50
49
 
@@ -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="true" WantAssertionsSigned="false">
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
@@ -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 saml_settings(saml_acs_url = "https://foo.example.com/saml/consume")
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.10.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-13 00:00:00.000000000 Z
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.10.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