saml_idp 0.9.0 → 1.0.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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +54 -49
  3. data/lib/saml_idp/assertion_builder.rb +28 -3
  4. data/lib/saml_idp/configurator.rb +8 -3
  5. data/lib/saml_idp/controller.rb +27 -18
  6. data/lib/saml_idp/encryptor.rb +0 -1
  7. data/lib/saml_idp/fingerprint.rb +19 -0
  8. data/lib/saml_idp/incoming_metadata.rb +22 -0
  9. data/lib/saml_idp/metadata_builder.rb +25 -9
  10. data/lib/saml_idp/persisted_metadata.rb +4 -0
  11. data/lib/saml_idp/request.rb +90 -13
  12. data/lib/saml_idp/response_builder.rb +26 -6
  13. data/lib/saml_idp/saml_response.rb +62 -28
  14. data/lib/saml_idp/service_provider.rb +2 -6
  15. data/lib/saml_idp/signable.rb +1 -2
  16. data/lib/saml_idp/signature_builder.rb +2 -1
  17. data/lib/saml_idp/signed_info_builder.rb +2 -2
  18. data/lib/saml_idp/version.rb +1 -1
  19. data/lib/saml_idp/xml_security.rb +19 -14
  20. data/lib/saml_idp.rb +4 -3
  21. data/saml_idp.gemspec +32 -31
  22. data/spec/lib/saml_idp/assertion_builder_spec.rb +143 -0
  23. data/spec/lib/saml_idp/configurator_spec.rb +40 -2
  24. data/spec/lib/saml_idp/controller_spec.rb +66 -8
  25. data/spec/lib/saml_idp/fingerprint_spec.rb +14 -0
  26. data/spec/lib/saml_idp/incoming_metadata_spec.rb +89 -1
  27. data/spec/lib/saml_idp/metadata_builder_spec.rb +24 -1
  28. data/spec/lib/saml_idp/request_spec.rb +153 -64
  29. data/spec/lib/saml_idp/response_builder_spec.rb +3 -1
  30. data/spec/lib/saml_idp/saml_response_spec.rb +141 -7
  31. data/spec/rails_app/app/controllers/saml_controller.rb +1 -5
  32. data/spec/rails_app/app/controllers/saml_idp_controller.rb +55 -3
  33. data/{app → spec/rails_app/app}/views/saml_idp/idp/new.html.erb +3 -4
  34. data/{app → spec/rails_app/app}/views/saml_idp/idp/saml_post.html.erb +1 -1
  35. data/spec/rails_app/config/application.rb +1 -0
  36. data/spec/rails_app/config/boot.rb +1 -1
  37. data/spec/rails_app/config/environments/development.rb +2 -0
  38. data/spec/spec_helper.rb +20 -1
  39. data/spec/support/certificates/sp_cert_req.csr +12 -0
  40. data/spec/support/certificates/sp_private_key.pem +16 -0
  41. data/spec/support/certificates/sp_x509_cert.crt +18 -0
  42. data/spec/support/saml_request_macros.rb +105 -4
  43. data/spec/support/security_helpers.rb +12 -2
  44. data/spec/xml_security_spec.rb +11 -7
  45. metadata +96 -62
  46. data/app/controllers/saml_idp/idp_controller.rb +0 -59
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4692b2d2c5266c2db128e4942daddd534c7e22efe32f1f45b02776db2eb8b607
4
- data.tar.gz: cc0b169ea2d024b91590e270c6a6cbe742a661280651917147eb0401f821c6a8
3
+ metadata.gz: 4a0fd46437c04823ad86f269552282b0252cb888294345984c5e16b88bcfa9cf
4
+ data.tar.gz: b7c56c2516f7aaf392fe5a999e98f67af0b79f799374757f3724724605616fc4
5
5
  SHA512:
6
- metadata.gz: 0d1eaa0e214b1c2cb17970987fc0956c991e33831a05f7bf40936180f6a4fc2a22d5539051b456f7a920a49b7cbd98e83296ae55858025351e9fb693a6f6d595
7
- data.tar.gz: 587c1ca1bc298dc8381bd50e49bd3b361fd011b4ae7509107f2336ec2b9cbc30bd625915a76d5833ebbe7c36eb9181acbee2f1f8b60dae0cb71f27a0436a9fec
6
+ metadata.gz: 558df23c6ffefed06a205df4ea72cc530f7a963c64741e0bb86cae16f7ffd26a822299793a6b4d7a587addf49428a71e3449f46650d1a26990ec563b690a6cac
7
+ data.tar.gz: ee6eec4ee019c3e01443bbc6d383e4eb6dcd422e3eb5dddcbefd10666799d9604e094e9dec22b213780a415f9efaaebfe54f8b512fef90322f6ce3cc89e4d751
data/README.md CHANGED
@@ -1,8 +1,7 @@
1
1
  # Ruby SAML Identity Provider (IdP)
2
2
 
3
- Forked from https://github.com/lawrencepit/ruby-saml-idp
3
+ Forked from <https://github.com/lawrencepit/ruby-saml-idp>
4
4
 
5
- [![Build Status](https://travis-ci.org/saml-idp/saml_idp.svg)](https://travis-ci.org/saml-idp/saml_idp)
6
5
  [![Gem Version](https://badge.fury.io/rb/saml_idp.svg)](http://badge.fury.io/rb/saml_idp)
7
6
 
8
7
  The ruby SAML Identity Provider library is for implementing the server side of SAML authentication. It allows
@@ -13,64 +12,53 @@ protocol. It provides a means for managing authentication requests and confirmat
13
12
  This was originally setup by @lawrencepit to test SAML Clients. I took it closer to a real
14
13
  SAML IDP implementation.
15
14
 
16
- # Installation and Usage
15
+ ## Installation and Usage
17
16
 
18
17
  Add this to your Gemfile:
19
18
 
19
+ ```ruby
20
20
  gem 'saml_idp'
21
+ ```
21
22
 
22
- ## Not using rails?
23
+ ### Not using rails?
23
24
 
24
25
  Include `SamlIdp::Controller` and see the examples that use rails. It should be straightforward for you.
25
26
 
26
- Basically you call `decode_request(params[:SAMLRequest])` on an incoming request and then use the value
27
- `saml_acs_url` to determine the source for which you need to authenticate a user. How you authenticate
28
- a user is entirely up to you.
27
+ Basically, you call `decode_request(params[:SAMLRequest])` on an incoming request and then use the value
28
+ `saml_acs_url` to determine the source for which you need to authenticate a user.
29
+ If the signature (`Signature`) and signing algorithm (`SigAlg`) are provided as external parameters in the request,
30
+ you can pass those parameters as `decode_request(params[:SAMLRequest], params[:Signature], params[:SigAlg], params[:RelayState])`.
31
+ Then, you can verify the request signature with the `valid?` method.
32
+
33
+ How you authenticate a user is entirely up to you.
29
34
 
30
- Once a user has successfully authenticated on your system send the Service Provider a SAMLReponse by
35
+ Once a user has successfully authenticated on your system send the Service Provider a SAMLResponse by
31
36
  posting to `saml_acs_url` the parameter `SAMLResponse` with the return value from a call to
32
37
  `encode_response(user_email)`.
33
38
 
34
- ## Using rails?
39
+ ### Using rails?
35
40
 
36
- Add to your `routes.rb` file, for example:
41
+ Check out our Wiki page for Rails integration
42
+ [Rails Integration guide](https://github.com/saml-idp/saml_idp/wiki/Rails_Integration)
37
43
 
38
- ```ruby
39
- get '/saml/auth' => 'saml_idp#new'
40
- get '/saml/metadata' => 'saml_idp#show'
41
- post '/saml/auth' => 'saml_idp#create'
42
- match '/saml/logout' => 'saml_idp#logout', via: [:get, :post, :delete]
43
- ```
44
+ ### Configuration
44
45
 
45
- Create a controller that looks like this, customize to your own situation:
46
+ #### Signed assertions and Signed Response
46
47
 
47
- ```ruby
48
- class SamlIdpController < SamlIdp::IdpController
49
- def idp_authenticate(email, password) # not using params intentionally
50
- user = User.by_email(email).first
51
- user && user.valid_password?(password) ? user : nil
52
- end
53
- private :idp_authenticate
54
-
55
- def idp_make_saml_response(found_user) # not using params intentionally
56
- # NOTE encryption is optional
57
- encode_response found_user, encryption: {
58
- cert: saml_request.service_provider.cert,
59
- block_encryption: 'aes256-cbc',
60
- key_transport: 'rsa-oaep-mgf1p'
61
- }
62
- end
63
- private :idp_make_saml_response
48
+ By default SAML Assertion will be signed with an algorithm which defined to `config.algorithm`, because SAML assertions contain secure information used for authentication such as NameID.
49
+ Besides that, signing assertions could be optional and can be defined with `config.signed_assertion` option. Setting this configuration flag to `false` will add raw assertions on the response instead of signed ones. If the response is encrypted the `config.signed_assertion` will be ignored and all assertions will be signed.
64
50
 
65
- def idp_logout
66
- user = User.by_email(saml_request.name_id)
67
- user.logout
68
- end
69
- private :idp_logout
70
- end
71
- ```
51
+ Signing SAML Response is optional, but some security perspective SP services might require Response message itself must be signed.
52
+ For that, you can enable it with `signed_message: true` option for `encode_response(user_email, signed_message: true)` method. [More about SAML spec](https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf#page=68)
72
53
 
73
- ## Configuration
54
+ #### Signing algorithm
55
+
56
+ Following algorithms you can set in your response signing algorithm
57
+ :sha1 - RSA-SHA1 default value but not recommended to production environment
58
+ Highly recommended to use one of following algorithm, suit with your computing power.
59
+ :sha256 - RSA-SHA256
60
+ :sha384 - RSA-SHA384
61
+ :sha512 - RSA-SHA512
74
62
 
75
63
  Be sure to load a file like this during your app initialization:
76
64
 
@@ -90,17 +78,25 @@ KEY DATA
90
78
  -----END RSA PRIVATE KEY-----
91
79
  CERT
92
80
 
81
+ # x509_certificate, secret_key, and password may also be set from within a proc, for example:
82
+ # config.x509_certificate = -> { File.read("cert.pem") }
83
+ # config.secret_key = -> { SecretKeyFinder.key_for(id: 1) }
84
+ # config.password = -> { "password" }
85
+
93
86
  # config.password = "secret_key_password"
94
- # config.algorithm = :sha256
87
+ # config.algorithm = :sha256 # Default: sha1 only for development.
95
88
  # config.organization_name = "Your Organization"
96
89
  # config.organization_url = "http://example.com"
97
90
  # config.base_saml_location = "#{base}/saml"
98
- # config.reference_id_generator # Default: -> { UUID.generate }
91
+ # config.reference_id_generator # Default: -> { SecureRandom.uuid }
99
92
  # config.single_logout_service_post_location = "#{base}/saml/logout"
100
93
  # config.single_logout_service_redirect_location = "#{base}/saml/logout"
101
94
  # config.attribute_service_location = "#{base}/saml/attributes"
102
95
  # config.single_service_post_location = "#{base}/saml/auth"
103
96
  # config.session_expiry = 86400 # Default: 0 which means never
97
+ # config.signed_assertion = false # Default: true which means signed assertions on the SAML Response
98
+ # config.compress = true # Default: false which means the SAML Response is not being compressed
99
+ # config.logger = ::Logger.new($stdout) # Default: if in Rails context - Rails.logger, else ->(msg) { puts msg }. Works with either a Ruby Logger or a lambda
104
100
 
105
101
  # Principal (e.g. User) is passed in when you `encode_response`
106
102
  #
@@ -213,7 +209,7 @@ CERT
213
209
  end
214
210
  ```
215
211
 
216
- # Keys and Secrets
212
+ ## Keys and Secrets
217
213
 
218
214
  To generate the SAML Response it uses a default X.509 certificate and secret key... which isn't so secret.
219
215
  You can find them in `SamlIdp::Default`. The X.509 certificate is valid until year 2032.
@@ -224,22 +220,31 @@ and `SamlIdp.config.secret_key` properties.
224
220
 
225
221
  The fingerprint to use, if you use the default X.509 certificate of this gem, is:
226
222
 
223
+ ```bash
224
+ 9E:65:2E:03:06:8D:80:F2:86:C7:6C:77:A1:D9:14:97:0A:4D:F4:4D
227
225
  ```
228
- 9E:65:2E:03:06:8D:80:F2:86:C7:6C:77:A1:D9:14:97:0A:4D:F4:4D
226
+
227
+ ## Fingerprint
228
+
229
+ The gem provides an helper to generate a fingerprint for a X.509 certificate.
230
+ The second parameter is optional and default to your configuration `SamlIdp.config.algorithm`
231
+
232
+ ```ruby
233
+ SamlIdp::Fingerprint.certificate_digest(x509_cert, :sha512)
229
234
  ```
230
235
 
231
- # Service Providers
236
+ ## Service Providers
232
237
 
233
238
  To act as a Service Provider which generates SAML Requests and can react to SAML Responses use the
234
239
  excellent [ruby-saml](https://github.com/onelogin/ruby-saml) gem.
235
240
 
236
- # Author
241
+ ## Author
237
242
 
238
243
  Jon Phenow, jon@jphenow.com, jphenow.com, @jphenow
239
244
 
240
245
  Lawrence Pit, lawrence.pit@gmail.com, lawrencepit.com, @lawrencepit
241
246
 
242
- # Copyright
247
+ ## Copyright
243
248
 
244
249
  Copyright (c) 2012 Sport Ngin.
245
250
  Portions Copyright (c) 2010 OneLogin, LLC
@@ -16,10 +16,26 @@ module SamlIdp
16
16
  attr_accessor :expiry
17
17
  attr_accessor :encryption_opts
18
18
  attr_accessor :session_expiry
19
+ attr_accessor :name_id_formats_opts
20
+ attr_accessor :asserted_attributes_opts
19
21
 
20
22
  delegate :config, to: :SamlIdp
21
23
 
22
- def initialize(reference_id, issuer_uri, principal, audience_uri, saml_request_id, saml_acs_url, raw_algorithm, authn_context_classref, expiry=60*60, encryption_opts=nil, session_expiry=nil)
24
+ def initialize(
25
+ reference_id,
26
+ issuer_uri,
27
+ principal,
28
+ audience_uri,
29
+ saml_request_id,
30
+ saml_acs_url,
31
+ raw_algorithm,
32
+ authn_context_classref,
33
+ expiry=60*60,
34
+ encryption_opts=nil,
35
+ session_expiry=nil,
36
+ name_id_formats_opts = nil,
37
+ asserted_attributes_opts = nil
38
+ )
23
39
  self.reference_id = reference_id
24
40
  self.issuer_uri = issuer_uri
25
41
  self.principal = principal
@@ -31,6 +47,8 @@ module SamlIdp
31
47
  self.expiry = expiry
32
48
  self.encryption_opts = encryption_opts
33
49
  self.session_expiry = session_expiry.nil? ? config.session_expiry : session_expiry
50
+ self.name_id_formats_opts = name_id_formats_opts
51
+ self.asserted_attributes_opts = asserted_attributes_opts
34
52
  end
35
53
 
36
54
  def fresh
@@ -98,7 +116,9 @@ module SamlIdp
98
116
  end
99
117
 
100
118
  def asserted_attributes
101
- if principal.respond_to?(:asserted_attributes)
119
+ if asserted_attributes_opts.present? && !asserted_attributes_opts.empty?
120
+ asserted_attributes_opts
121
+ elsif principal.respond_to?(:asserted_attributes)
102
122
  principal.send(:asserted_attributes)
103
123
  elsif !config.attributes.nil? && !config.attributes.empty?
104
124
  config.attributes
@@ -139,10 +159,15 @@ module SamlIdp
139
159
  private :name_id_getter
140
160
 
141
161
  def name_id_format
142
- @name_id_format ||= NameIdFormatter.new(config.name_id.formats).chosen
162
+ @name_id_format ||= NameIdFormatter.new(name_id_formats).chosen
143
163
  end
144
164
  private :name_id_format
145
165
 
166
+ def name_id_formats
167
+ @name_id_formats ||= (name_id_formats_opts || config.name_id.formats)
168
+ end
169
+ private :name_id_formats
170
+
146
171
  def reference_string
147
172
  "_#{reference_id}"
148
173
  end
@@ -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,24 +15,27 @@ 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
19
22
  attr_accessor :service_provider
20
23
  attr_accessor :assertion_consumer_service_hosts
21
24
  attr_accessor :session_expiry
25
+ attr_accessor :logger
22
26
 
23
27
  def initialize
24
- self.x509_certificate = Default::X509_CERTIFICATE
25
- self.secret_key = Default::SECRET_KEY
28
+ self.x509_certificate = -> { Default::X509_CERTIFICATE }
29
+ self.secret_key = -> { Default::SECRET_KEY }
26
30
  self.algorithm = :sha1
27
- self.reference_id_generator = ->() { UUID.generate }
31
+ self.reference_id_generator = ->() { SecureRandom.uuid }
28
32
  self.service_provider = OpenStruct.new
29
33
  self.service_provider.finder = ->(_) { Default::SERVICE_PROVIDER }
30
34
  self.service_provider.metadata_persister = ->(id, settings) { }
31
35
  self.service_provider.persisted_metadata_getter = ->(id, service_provider) { }
32
36
  self.session_expiry = 0
33
37
  self.attributes = {}
38
+ self.logger = (defined?(::Rails) && Rails.respond_to?(:logger)) ? Rails.logger : ->(msg) { puts msg }
34
39
  end
35
40
 
36
41
  # formats
@@ -1,8 +1,7 @@
1
- # encoding: utf-8
2
1
  require 'openssl'
3
2
  require 'base64'
4
3
  require 'time'
5
- require 'uuid'
4
+ require 'securerandom'
6
5
  require 'saml_idp/request'
7
6
  require 'saml_idp/logout_response_builder'
8
7
  module SamlIdp
@@ -34,20 +33,18 @@ module SamlIdp
34
33
  end
35
34
 
36
35
  def validate_saml_request(raw_saml_request = params[:SAMLRequest])
37
- decode_request(raw_saml_request)
38
- return true if valid_saml_request?
39
- if defined?(::Rails)
40
- if Rails::VERSION::MAJOR >= 4
41
- head :forbidden
42
- else
43
- render nothing: true, status: :forbidden
44
- end
45
- end
46
- false
36
+ decode_request(raw_saml_request, params[:Signature], params[:SigAlg], params[:RelayState])
37
+ valid_saml_request?
47
38
  end
48
39
 
49
- def decode_request(raw_saml_request)
50
- @saml_request = Request.from_deflated_request(raw_saml_request)
40
+ def decode_request(raw_saml_request, signature, sig_algorithm, relay_state)
41
+ @saml_request = Request.from_deflated_request(
42
+ raw_saml_request,
43
+ saml_request: raw_saml_request,
44
+ signature: signature,
45
+ sig_algorithm: sig_algorithm,
46
+ relay_state: relay_state
47
+ )
51
48
  end
52
49
 
53
50
  def authn_context_classref
@@ -64,6 +61,13 @@ module SamlIdp
64
61
  expiry = opts[:expiry] || 60*60
65
62
  session_expiry = opts[:session_expiry]
66
63
  encryption_opts = opts[:encryption] || nil
64
+ name_id_formats_opts = opts[:name_id_formats] || nil
65
+ asserted_attributes_opts = opts[:attributes] || nil
66
+ signed_message_opts = opts[:signed_message] || false
67
+ name_id_formats_opts = opts[:name_id_formats] || nil
68
+ asserted_attributes_opts = opts[:attributes] || nil
69
+ signed_assertion_opts = opts[:signed_assertion].nil? ? true : opts[:signed_assertion]
70
+ compress_opts = opts[:compress] || false
67
71
 
68
72
  SamlResponse.new(
69
73
  reference_id,
@@ -77,11 +81,16 @@ module SamlIdp
77
81
  my_authn_context_classref,
78
82
  expiry,
79
83
  encryption_opts,
80
- session_expiry
84
+ session_expiry,
85
+ name_id_formats_opts,
86
+ asserted_attributes_opts,
87
+ signed_message_opts,
88
+ signed_assertion_opts,
89
+ compress_opts
81
90
  ).build
82
91
  end
83
92
 
84
- def encode_logout_response(principal, opts = {})
93
+ def encode_logout_response(_principal, opts = {})
85
94
  SamlIdp::LogoutResponseBuilder.new(
86
95
  get_saml_response_id,
87
96
  (opts[:issuer_uri] || issuer_uri),
@@ -124,11 +133,11 @@ module SamlIdp
124
133
  end
125
134
 
126
135
  def get_saml_response_id
127
- UUID.generate
136
+ SecureRandom.uuid
128
137
  end
129
138
 
130
139
  def get_saml_reference_id
131
- UUID.generate
140
+ SecureRandom.uuid
132
141
  end
133
142
 
134
143
  def default_algorithm
@@ -61,7 +61,6 @@ module SamlIdp
61
61
  key_info.EncryptedKey Id: 'EK', xmlns: 'http://www.w3.org/2001/04/xmlenc#' do |enc_key|
62
62
  enc_key.EncryptionMethod Algorithm: key_transport_ns
63
63
  enc_key.tag! 'ds:KeyInfo', 'xmlns:ds' => 'http://www.w3.org/2000/09/xmldsig#' do |key_info2|
64
- key_info2.tag! 'ds:KeyName'
65
64
  key_info2.tag! 'ds:X509Data' do |x509_data|
66
65
  x509_data.tag! 'ds:X509Certificate' do |x509_cert|
67
66
  x509_cert << cert.to_s.gsub(/-+(BEGIN|END) CERTIFICATE-+/, '')
@@ -0,0 +1,19 @@
1
+ module SamlIdp
2
+ module Fingerprint
3
+ def self.certificate_digest(cert, sha_size = nil)
4
+ sha_size ||= SamlIdp.config.algorithm
5
+ digest_sha_class(sha_size).hexdigest(OpenSSL::X509::Certificate.new(cert).to_der).scan(/../).join(':')
6
+ end
7
+
8
+ def self.digest_sha_class(sha_size)
9
+ case sha_size
10
+ when :sha256
11
+ Digest::SHA256
12
+ when :sha512
13
+ Digest::SHA512
14
+ else
15
+ raise ArgumentError, "Unsupported sha size parameter: #{sha_size}"
16
+ end
17
+ end
18
+ end
19
+ end
@@ -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
@@ -50,6 +63,15 @@ module SamlIdp
50
63
  end
51
64
  hashable :contact_person
52
65
 
66
+ def unspecified_certificate
67
+ xpath(
68
+ "//md:SPSSODescriptor/md:KeyDescriptor[not(@use)]/ds:KeyInfo/ds:X509Data/ds:X509Certificate",
69
+ ds: signature_namespace,
70
+ md: metadata_namespace
71
+ ).first.try(:content).to_s
72
+ end
73
+ hashable :unspecified_certificate
74
+
53
75
  def signing_certificate
54
76
  xpath(
55
77
  "//md:SPSSODescriptor/md:KeyDescriptor[@use='signing']/ds:KeyInfo/ds:X509Data/ds:X509Certificate",
@@ -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",
@@ -138,7 +152,8 @@ module SamlIdp
138
152
  private :raw_algorithm
139
153
 
140
154
  def x509_certificate
141
- SamlIdp.config.x509_certificate
155
+ certificate = SamlIdp.config.x509_certificate.is_a?(Proc) ? SamlIdp.config.x509_certificate.call : SamlIdp.config.x509_certificate
156
+ certificate
142
157
  .to_s
143
158
  .gsub(/-----BEGIN CERTIFICATE-----/,"")
144
159
  .gsub(/-----END CERTIFICATE-----/,"")
@@ -151,6 +166,7 @@ module SamlIdp
151
166
  organization_url
152
167
  attribute_service_location
153
168
  single_service_post_location
169
+ single_service_redirect_location
154
170
  single_logout_service_post_location
155
171
  single_logout_service_redirect_location
156
172
  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