saml_idp 0.8.0 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 33008cf3468ff0038f5308fb5820edc2023c071d1d0a046c6778f9fd393d96da
4
- data.tar.gz: 95e3adfcc852d0384ae3fa5c8740a820f16db073f83aef78bdba524d3581f8ed
3
+ metadata.gz: 73712903d3949f895e57a13b138c007e0ae74715d7d546b4415f278829e59054
4
+ data.tar.gz: fbab7e28d01ea3fc7624e52e20a234f6f997a51643905170e6809cb3f7beeec7
5
5
  SHA512:
6
- metadata.gz: e66b93acbc0ab6b965258a6ba2c205e4563e197206b94c50dbd5a7603a36ebda7c2dc47d57932b821dcbd4a4e3e033616d2c6879141222b0496a0faa1811af6f
7
- data.tar.gz: d3aee5f5466e2b7c70cb9434eb6c0fe3036d55335ec65555c489124ce4d4889e4051969d8f1cc87e2b3a523f1fbbc447a8a609a86e62a4ffd53f0cf4a7f31285
6
+ metadata.gz: 80a4683963e04b8b7f68051d15b12a5f0a098300cbfba9b72c4ed3940338ab3505c47cda0b091a9a36c1605f4396b5f1732bed7d3356438dd944f83541573a47
7
+ data.tar.gz: b01be29f645e31f9987afef74bb8b368ccfc86340b67e29524f582fe139e99b56cd827315d5a4e73d34b69e2c3c5b89d881c89f6d29b68c4b3cd2ff5499a39c3
data/README.md CHANGED
@@ -1,4 +1,5 @@
1
1
  # Ruby SAML Identity Provider (IdP)
2
+
2
3
  Forked from https://github.com/lawrencepit/ruby-saml-idp
3
4
 
4
5
  [![Build Status](https://travis-ci.org/saml-idp/saml_idp.svg)](https://travis-ci.org/saml-idp/saml_idp)
@@ -19,6 +20,7 @@ Add this to your Gemfile:
19
20
  gem 'saml_idp'
20
21
 
21
22
  ## Not using rails?
23
+
22
24
  Include `SamlIdp::Controller` and see the examples that use rails. It should be straightforward for you.
23
25
 
24
26
  Basically you call `decode_request(params[:SAMLRequest])` on an incoming request and then use the value
@@ -30,9 +32,10 @@ posting to `saml_acs_url` the parameter `SAMLResponse` with the return value fro
30
32
  `encode_response(user_email)`.
31
33
 
32
34
  ## Using rails?
35
+
33
36
  Add to your `routes.rb` file, for example:
34
37
 
35
- ``` ruby
38
+ ```ruby
36
39
  get '/saml/auth' => 'saml_idp#new'
37
40
  get '/saml/metadata' => 'saml_idp#show'
38
41
  post '/saml/auth' => 'saml_idp#create'
@@ -41,7 +44,7 @@ match '/saml/logout' => 'saml_idp#logout', via: [:get, :post, :delete]
41
44
 
42
45
  Create a controller that looks like this, customize to your own situation:
43
46
 
44
- ``` ruby
47
+ ```ruby
45
48
  class SamlIdpController < SamlIdp::IdpController
46
49
  def idp_authenticate(email, password) # not using params intentionally
47
50
  user = User.by_email(email).first
@@ -69,6 +72,22 @@ end
69
72
 
70
73
  ## Configuration
71
74
 
75
+ #### Signed assertions and Signed Response
76
+
77
+ 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.
78
+
79
+ Signing SAML Response is optional, but some security perspective SP services might require Response message itself must be signed.
80
+ For that, you can enable it with `config.signed_message` option. [More about SAML spec](https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf#page=68)
81
+
82
+ #### Signing algorithm
83
+
84
+ Following algorithms you can set in your response signing algorithm
85
+ :sha1 - RSA-SHA1 default value but not recommended to production environment
86
+ Highly recommended to use one of following algorithm, suit with your computing power.
87
+ :sha256 - RSA-SHA256
88
+ :sha384 - RSA-SHA384
89
+ :sha512 - RSA-SHA512
90
+
72
91
  Be sure to load a file like this during your app initialization:
73
92
 
74
93
  ```ruby
@@ -88,20 +107,21 @@ KEY DATA
88
107
  CERT
89
108
 
90
109
  # config.password = "secret_key_password"
91
- # config.algorithm = :sha256
110
+ # config.algorithm = :sha256 # Default: sha1 only for development.
92
111
  # config.organization_name = "Your Organization"
93
112
  # config.organization_url = "http://example.com"
94
113
  # config.base_saml_location = "#{base}/saml"
95
- # config.reference_id_generator # Default: -> { UUID.generate }
114
+ # config.reference_id_generator # Default: -> { SecureRandom.uuid }
96
115
  # config.single_logout_service_post_location = "#{base}/saml/logout"
97
116
  # config.single_logout_service_redirect_location = "#{base}/saml/logout"
98
117
  # config.attribute_service_location = "#{base}/saml/attributes"
99
118
  # config.single_service_post_location = "#{base}/saml/auth"
100
119
  # config.session_expiry = 86400 # Default: 0 which means never
120
+ # config.signed_message = true # Default: false which means unsigned SAML Response
101
121
 
102
122
  # Principal (e.g. User) is passed in when you `encode_response`
103
123
  #
104
- # config.name_id.formats # =>
124
+ # config.name_id.formats =
105
125
  # { # All 2.0
106
126
  # email_address: -> (principal) { principal.email_address },
107
127
  # transient: -> (principal) { principal.id },
@@ -211,6 +231,7 @@ end
211
231
  ```
212
232
 
213
233
  # Keys and Secrets
234
+
214
235
  To generate the SAML Response it uses a default X.509 certificate and secret key... which isn't so secret.
215
236
  You can find them in `SamlIdp::Default`. The X.509 certificate is valid until year 2032.
216
237
  Obviously you shouldn't use these if you intend to use this in production environments. In that case,
@@ -224,18 +245,28 @@ The fingerprint to use, if you use the default X.509 certificate of this gem, is
224
245
  9E:65:2E:03:06:8D:80:F2:86:C7:6C:77:A1:D9:14:97:0A:4D:F4:4D
225
246
  ```
226
247
 
248
+ # Fingerprint
249
+
250
+ The gem provides an helper to generate a fingerprint for a X.509 certificate.
251
+ The second parameter is optional and default to your configuration `SamlIdp.config.algorithm`
252
+
253
+ ```ruby
254
+ Fingerprint.certificate_digest(x509_cert, :sha512)
255
+ ```
227
256
 
228
257
  # Service Providers
258
+
229
259
  To act as a Service Provider which generates SAML Requests and can react to SAML Responses use the
230
260
  excellent [ruby-saml](https://github.com/onelogin/ruby-saml) gem.
231
261
 
232
-
233
262
  # Author
234
- Jon Phenow, me@jphenow.com
263
+
264
+ Jon Phenow, jon@jphenow.com, jphenow.com, @jphenow
235
265
 
236
266
  Lawrence Pit, lawrence.pit@gmail.com, lawrencepit.com, @lawrencepit
237
267
 
238
268
  # Copyright
269
+
239
270
  Copyright (c) 2012 Sport Ngin.
240
271
  Portions Copyright (c) 2010 OneLogin, LLC
241
272
  Portions Copyright (c) 2012 Lawrence Pit (http://lawrencepit.com)
@@ -8,6 +8,7 @@ module SamlIdp
8
8
  require 'saml_idp/default'
9
9
  require 'saml_idp/metadata_builder'
10
10
  require 'saml_idp/version'
11
+ require 'saml_idp/fingerprint'
11
12
  require 'saml_idp/engine' if defined?(::Rails) && Rails::VERSION::MAJOR > 2
12
13
 
13
14
  def self.config
@@ -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
@@ -36,10 +36,12 @@ module SamlIdp
36
36
  def validate_saml_request(raw_saml_request = params[:SAMLRequest])
37
37
  decode_request(raw_saml_request)
38
38
  return true if valid_saml_request?
39
- if Rails::VERSION::MAJOR >= 4
40
- head :forbidden
41
- else
42
- render nothing: true, status: :forbidden
39
+ if defined?(::Rails)
40
+ if Rails::VERSION::MAJOR >= 4
41
+ head :forbidden
42
+ else
43
+ render nothing: true, status: :forbidden
44
+ end
43
45
  end
44
46
  false
45
47
  end
@@ -62,6 +64,7 @@ module SamlIdp
62
64
  expiry = opts[:expiry] || 60*60
63
65
  session_expiry = opts[:session_expiry]
64
66
  encryption_opts = opts[:encryption] || nil
67
+ signed_message_opts = opts[:signed_message] || false
65
68
 
66
69
  SamlResponse.new(
67
70
  reference_id,
@@ -75,7 +78,8 @@ module SamlIdp
75
78
  my_authn_context_classref,
76
79
  expiry,
77
80
  encryption_opts,
78
- session_expiry
81
+ session_expiry,
82
+ signed_message_opts
79
83
  ).build
80
84
  end
81
85
 
@@ -122,11 +126,11 @@ module SamlIdp
122
126
  end
123
127
 
124
128
  def get_saml_response_id
125
- UUID.generate
129
+ SecureRandom.uuid
126
130
  end
127
131
 
128
132
  def get_saml_reference_id
129
- UUID.generate
133
+ SecureRandom.uuid
130
134
  end
131
135
 
132
136
  def default_algorithm
@@ -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
@@ -16,6 +16,11 @@ module SamlIdp
16
16
  @document ||= Saml::XML::Document.parse raw
17
17
  end
18
18
 
19
+ def entity_id
20
+ xpath('//md:EntityDescriptor/@entityID', md: metadata_namespace).first.try(:content).to_s
21
+ end
22
+ hashable :entity_id
23
+
19
24
  def sign_assertions
20
25
  doc = xpath(
21
26
  "//md:SPSSODescriptor",
@@ -29,6 +34,19 @@ module SamlIdp
29
34
  end
30
35
  hashable :sign_assertions
31
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
+
32
50
  def display_name
33
51
  role_descriptor_document.present? ? role_descriptor_document["ServiceDisplayName"] : ""
34
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
@@ -106,6 +106,7 @@ module SamlIdp
106
106
  end
107
107
 
108
108
  if !service_provider.acceptable_response_hosts.include?(response_host)
109
+ log "#{service_provider.acceptable_response_hosts} compare to #{response_host}"
109
110
  log "No acceptable AssertionConsumerServiceURL, either configure them via config.service_provider.response_hosts or match to your metadata_url host"
110
111
  return false
111
112
  end
@@ -114,9 +115,14 @@ module SamlIdp
114
115
  end
115
116
 
116
117
  def valid_signature?
117
- # Force signatures for logout requests because there is no other
118
- # protection against a cross-site DoS.
119
- 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
120
126
  end
121
127
 
122
128
  def service_provider?
@@ -1,32 +1,45 @@
1
1
  require 'builder'
2
+ require 'saml_idp/algorithmable'
3
+ require 'saml_idp/signable'
2
4
  module SamlIdp
3
5
  class ResponseBuilder
6
+ include Algorithmable
7
+ include Signable
4
8
  attr_accessor :response_id
5
9
  attr_accessor :issuer_uri
6
10
  attr_accessor :saml_acs_url
7
11
  attr_accessor :saml_request_id
8
12
  attr_accessor :assertion_and_signature
13
+ attr_accessor :raw_algorithm
9
14
 
10
- def initialize(response_id, issuer_uri, saml_acs_url, saml_request_id, assertion_and_signature)
15
+ alias_method :reference_id, :response_id
16
+
17
+ def initialize(response_id, issuer_uri, saml_acs_url, saml_request_id, assertion_and_signature, raw_algorithm)
11
18
  self.response_id = response_id
12
19
  self.issuer_uri = issuer_uri
13
20
  self.saml_acs_url = saml_acs_url
14
21
  self.saml_request_id = saml_request_id
15
22
  self.assertion_and_signature = assertion_and_signature
23
+ self.raw_algorithm = raw_algorithm
16
24
  end
17
25
 
18
- def encoded
19
- @encoded ||= encode
26
+ def encoded(signed_message: false)
27
+ @encoded ||= signed_message ? encode_signed_message : encode_raw_message
20
28
  end
21
29
 
22
30
  def raw
23
31
  build
24
32
  end
25
33
 
26
- def encode
34
+ def encode_raw_message
27
35
  Base64.strict_encode64(raw)
28
36
  end
29
- private :encode
37
+ private :encode_raw_message
38
+
39
+ def encode_signed_message
40
+ Base64.strict_encode64(signed)
41
+ end
42
+ private :encode_signed_message
30
43
 
31
44
  def build
32
45
  resp_options = {}
@@ -41,6 +54,7 @@ module SamlIdp
41
54
  builder = Builder::XmlMarkup.new
42
55
  builder.tag! "samlp:Response", resp_options do |response|
43
56
  response.Issuer issuer_uri, xmlns: Saml::XML::Namespaces::ASSERTION
57
+ sign response
44
58
  response.tag! "samlp:Status" do |status|
45
59
  status.tag! "samlp:StatusCode", Value: Saml::XML::Namespaces::Statuses::SUCCESS
46
60
  end
@@ -17,6 +17,7 @@ module SamlIdp
17
17
  attr_accessor :expiry
18
18
  attr_accessor :encryption_opts
19
19
  attr_accessor :session_expiry
20
+ attr_accessor :signed_message_opts
20
21
 
21
22
  def initialize(reference_id,
22
23
  response_id,
@@ -29,7 +30,8 @@ module SamlIdp
29
30
  authn_context_classref,
30
31
  expiry=60*60,
31
32
  encryption_opts=nil,
32
- session_expiry=0
33
+ session_expiry=0,
34
+ signed_message_opts
33
35
  )
34
36
  self.reference_id = reference_id
35
37
  self.response_id = response_id
@@ -45,10 +47,11 @@ module SamlIdp
45
47
  self.expiry = expiry
46
48
  self.encryption_opts = encryption_opts
47
49
  self.session_expiry = session_expiry
50
+ self.signed_message_opts = signed_message_opts
48
51
  end
49
52
 
50
53
  def build
51
- @built ||= response_builder.encoded
54
+ @built ||= encoded_message
52
55
  end
53
56
 
54
57
  def signed_assertion
@@ -60,8 +63,17 @@ module SamlIdp
60
63
  end
61
64
  private :signed_assertion
62
65
 
66
+ def encoded_message
67
+ if signed_message_opts
68
+ response_builder.encoded(signed_message: true)
69
+ else
70
+ response_builder.encoded(signed_message: false)
71
+ end
72
+ end
73
+ private :encoded_message
74
+
63
75
  def response_builder
64
- ResponseBuilder.new(response_id, issuer_uri, saml_acs_url, saml_request_id, signed_assertion)
76
+ ResponseBuilder.new(response_id, issuer_uri, saml_acs_url, saml_request_id, signed_assertion, algorithm)
65
77
  end
66
78
  private :response_builder
67
79
 
@@ -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)
@@ -108,8 +108,7 @@ module SamlIdp
108
108
  canon_algorithm = Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0
109
109
  canon_hashed_element = noko_raw.canonicalize(canon_algorithm, inclusive_namespaces)
110
110
  digest_algorithm = get_algorithm
111
-
112
- hash = digest_algorithm.digest(canon_hashed_element)
111
+ hash = digest_algorithm.digest(canon_hashed_element)
113
112
  Base64.strict_encode64(hash).gsub(/\n/, '')
114
113
  end
115
114
  private :digest
@@ -1,4 +1,4 @@
1
1
  # encoding: utf-8
2
2
  module SamlIdp
3
- VERSION = '0.8.0'
3
+ VERSION = '0.12.0'
4
4
  end
@@ -108,7 +108,7 @@ module SamlIdp
108
108
  canon_algorithm = canon_algorithm REXML::XPath.first(ref, '//ds:CanonicalizationMethod', 'ds' => DSIG)
109
109
  canon_hashed_element = hashed_element.canonicalize(canon_algorithm, inclusive_namespaces)
110
110
 
111
- digest_algorithm = algorithm(REXML::XPath.first(ref, "//ds:DigestMethod"))
111
+ digest_algorithm = algorithm(REXML::XPath.first(ref, "//ds:DigestMethod", {'ds' => DSIG}))
112
112
 
113
113
  hash = digest_algorithm.digest(canon_hashed_element)
114
114
  digest_value = Base64.decode64(REXML::XPath.first(ref, "//ds:DigestValue", {"ds"=>DSIG}).text)
@@ -44,19 +44,18 @@ 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
 
51
50
  s.add_development_dependency('rake')
52
51
  s.add_development_dependency('simplecov')
53
52
  s.add_development_dependency('rspec', '>= 3.7.0')
54
- s.add_development_dependency('ruby-saml', '>= 1.5')
53
+ s.add_development_dependency('ruby-saml', '>= 1.7.2')
55
54
  s.add_development_dependency('rails', '>= 3.2')
56
55
  s.add_development_dependency('activeresource', '>= 3.2')
57
56
  s.add_development_dependency('capybara', '>= 2.16')
58
57
  s.add_development_dependency('timecop', '>= 0.8')
59
58
  s.add_development_dependency('xmlenc', '>= 0.6.4')
60
59
  s.add_development_dependency('appraisal')
60
+ s.add_development_dependency('byebug')
61
61
  end
62
-
@@ -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
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+
3
+ module SamlIdp
4
+ describe Fingerprint do
5
+ describe "certificate_digest" do
6
+ let(:cert) { sp_x509_cert }
7
+ let(:fingerprint) { "a2:cb:f6:6b:bc:2a:33:b9:4f:f3:c3:7e:26:a4:21:cd:41:83:ef:26:88:fa:ba:71:37:40:07:3e:d5:76:04:b7" }
8
+
9
+ it "returns the fingerprint string" do
10
+ expect(Fingerprint.certificate_digest(cert, :sha256)).to eq(fingerprint)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -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,20 +22,39 @@ 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)
37
+ end
38
+
39
+ it 'should properly set entity_id as https://test-saml.com/saml' do
40
+ metadata = SamlIdp::IncomingMetadata.new(metadata_1)
41
+ expect(metadata.entity_id).to eq('https://test-saml.com/saml')
29
42
  end
30
43
 
31
44
  it 'should properly set sign_assertions to true' do
32
45
  metadata = SamlIdp::IncomingMetadata.new(metadata_2)
33
46
  expect(metadata.sign_assertions).to eq(true)
47
+ expect(metadata.sign_authn_request).to eq(true)
34
48
  end
35
49
 
36
50
  it 'should properly set sign_assertions to false when WantAssertionsSigned is not included' do
37
51
  metadata = SamlIdp::IncomingMetadata.new(metadata_3)
38
52
  expect(metadata.sign_assertions).to eq(false)
39
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
40
59
  end
41
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
@@ -6,12 +6,14 @@ module SamlIdp
6
6
  let(:saml_acs_url) { "http://sportngin.com" }
7
7
  let(:saml_request_id) { "134" }
8
8
  let(:assertion_and_signature) { "<Assertion xmlns=\"urn:oasis:names:tc:SAML:2.0:assertion\" ID=\"_abc\" IssueInstant=\"2013-07-31T05:00:00Z\" Version=\"2.0\"><Issuer>http://sportngin.com</Issuer><signature>stuff</signature><Subject><NameID Format=\"urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress\">jon.phenow@sportngin.com</NameID><SubjectConfirmation Method=\"urn:oasis:names:tc:SAML:2.0:cm:bearer\"><SubjectConfirmationData InResponseTo=\"123\" NotOnOrAfter=\"2013-07-31T05:03:00Z\" Recipient=\"http://saml.acs.url\"/></SubjectConfirmation></Subject><Conditions NotBefore=\"2013-07-31T04:59:55Z\" NotOnOrAfter=\"2013-07-31T06:00:00Z\"><AudienceRestriction><Audience>http://example.com</Audience></AudienceRestriction></Conditions><AttributeStatement><Attribute Name=\"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress\"><AttributeValue>jon.phenow@sportngin.com</AttributeValue></Attribute></AttributeStatement><AuthnStatment AuthnInstant=\"2013-07-31T05:00:00Z\" SessionIndex=\"_abc\"><AuthnContext><AuthnContextClassRef>urn:federation:authentication:windows</AuthnContextClassRef></AuthnContext></AuthnStatment></Assertion>" }
9
+ let(:algorithm) { :sha256 }
9
10
  subject { described_class.new(
10
11
  response_id,
11
12
  issuer_uri,
12
13
  saml_acs_url,
13
14
  saml_request_id,
14
- assertion_and_signature
15
+ assertion_and_signature,
16
+ algorithm
15
17
  ) }
16
18
 
17
19
  before do
@@ -24,6 +24,8 @@ module SamlIdp
24
24
  key_transport: 'rsa-oaep-mgf1p',
25
25
  }
26
26
  end
27
+ let(:signed_response_opts) { true }
28
+ let(:unsigned_response_opts) { false }
27
29
  let(:subject_encrypted) { described_class.new(reference_id,
28
30
  response_id,
29
31
  issuer_uri,
@@ -35,7 +37,8 @@ module SamlIdp
35
37
  authn_context_classref,
36
38
  expiry,
37
39
  encryption_opts,
38
- session_expiry
40
+ session_expiry,
41
+ unsigned_response_opts
39
42
  )
40
43
  }
41
44
 
@@ -50,7 +53,8 @@ module SamlIdp
50
53
  authn_context_classref,
51
54
  expiry,
52
55
  nil,
53
- session_expiry
56
+ session_expiry,
57
+ signed_response_opts
54
58
  )
55
59
  }
56
60
 
@@ -77,6 +81,25 @@ module SamlIdp
77
81
  expect(saml_resp.is_valid?).to eq(true)
78
82
  end
79
83
 
84
+ it "will build signed valid response" do
85
+ expect { subject.build }.not_to raise_error
86
+ signed_encoded_xml = subject.build
87
+ resp_settings = saml_settings(saml_acs_url)
88
+ resp_settings.private_key = Default::SECRET_KEY
89
+ resp_settings.issuer = audience_uri
90
+ saml_resp = OneLogin::RubySaml::Response.new(signed_encoded_xml, settings: resp_settings)
91
+ expect(
92
+ Nokogiri::XML(saml_resp.response).at_xpath(
93
+ "//p:Response//ds:Signature",
94
+ {
95
+ "p" => "urn:oasis:names:tc:SAML:2.0:protocol",
96
+ "ds" => "http://www.w3.org/2000/09/xmldsig#"
97
+ }
98
+ )).to be_present
99
+ expect(saml_resp.send(:validate_signature)).to eq(true)
100
+ expect(saml_resp.is_valid?).to eq(true)
101
+ end
102
+
80
103
  it "sets session expiration" do
81
104
  saml_resp = OneLogin::RubySaml::Response.new(subject.build)
82
105
  expect(saml_resp.session_expires_at).to eq Time.local(1990, "jan", 2).iso8601
@@ -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: SamlIdp::Fingerprint.certificate_digest(sp_x509_cert)
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.8.0
4
+ version: 0.12.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: 2018-11-09 00:00:00.000000000 Z
11
+ date: 2020-11-20 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
@@ -114,14 +100,14 @@ dependencies:
114
100
  requirements:
115
101
  - - ">="
116
102
  - !ruby/object:Gem::Version
117
- version: '1.5'
103
+ version: 1.7.2
118
104
  type: :development
119
105
  prerelease: false
120
106
  version_requirements: !ruby/object:Gem::Requirement
121
107
  requirements:
122
108
  - - ">="
123
109
  - !ruby/object:Gem::Version
124
- version: '1.5'
110
+ version: 1.7.2
125
111
  - !ruby/object:Gem::Dependency
126
112
  name: rails
127
113
  requirement: !ruby/object:Gem::Requirement
@@ -206,6 +192,20 @@ dependencies:
206
192
  - - ">="
207
193
  - !ruby/object:Gem::Version
208
194
  version: '0'
195
+ - !ruby/object:Gem::Dependency
196
+ name: byebug
197
+ requirement: !ruby/object:Gem::Requirement
198
+ requirements:
199
+ - - ">="
200
+ - !ruby/object:Gem::Version
201
+ version: '0'
202
+ type: :development
203
+ prerelease: false
204
+ version_requirements: !ruby/object:Gem::Requirement
205
+ requirements:
206
+ - - ">="
207
+ - !ruby/object:Gem::Version
208
+ version: '0'
209
209
  description: SAML IdP (Identity Provider) Library for Ruby
210
210
  email: jon.phenow@sportngin.com
211
211
  executables: []
@@ -228,6 +228,7 @@ files:
228
228
  - lib/saml_idp/default.rb
229
229
  - lib/saml_idp/encryptor.rb
230
230
  - lib/saml_idp/engine.rb
231
+ - lib/saml_idp/fingerprint.rb
231
232
  - lib/saml_idp/hashable.rb
232
233
  - lib/saml_idp/incoming_metadata.rb
233
234
  - lib/saml_idp/logout_builder.rb
@@ -254,6 +255,7 @@ files:
254
255
  - spec/lib/saml_idp/configurator_spec.rb
255
256
  - spec/lib/saml_idp/controller_spec.rb
256
257
  - spec/lib/saml_idp/encryptor_spec.rb
258
+ - spec/lib/saml_idp/fingerprint_spec.rb
257
259
  - spec/lib/saml_idp/incoming_metadata_spec.rb
258
260
  - spec/lib/saml_idp/logout_request_builder_spec.rb
259
261
  - spec/lib/saml_idp/logout_response_builder_spec.rb
@@ -319,6 +321,9 @@ files:
319
321
  - spec/spec_helper.rb
320
322
  - spec/support/certificates/certificate1
321
323
  - spec/support/certificates/r1_certificate2_base64
324
+ - spec/support/certificates/sp_cert_req.csr
325
+ - spec/support/certificates/sp_private_key.pem
326
+ - spec/support/certificates/sp_x509_cert.crt
322
327
  - spec/support/responses/adfs_response_sha1.xml
323
328
  - spec/support/responses/adfs_response_sha256.xml
324
329
  - spec/support/responses/adfs_response_sha384.xml
@@ -347,7 +352,7 @@ metadata:
347
352
  homepage_uri: https://github.com/saml-idp/saml_idp
348
353
  source_code_uri: https://github.com/saml-idp/saml_idp
349
354
  bug_tracker_uri: https://github.com/saml-idp/saml_idp/issues
350
- documentation_uri: http://rdoc.info/gems/saml_idp/0.8.0
355
+ documentation_uri: http://rdoc.info/gems/saml_idp/0.12.0
351
356
  post_install_message: |
352
357
  If you're just recently updating saml_idp - please be aware we've changed the default
353
358
  certificate. See the PR and a description of why we've done this here:
@@ -378,8 +383,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
378
383
  - !ruby/object:Gem::Version
379
384
  version: '0'
380
385
  requirements: []
381
- rubyforge_project:
382
- rubygems_version: 2.7.6
386
+ rubygems_version: 3.1.2
383
387
  signing_key:
384
388
  specification_version: 4
385
389
  summary: SAML Indentity Provider for Ruby
@@ -392,6 +396,7 @@ test_files:
392
396
  - spec/lib/saml_idp/configurator_spec.rb
393
397
  - spec/lib/saml_idp/controller_spec.rb
394
398
  - spec/lib/saml_idp/encryptor_spec.rb
399
+ - spec/lib/saml_idp/fingerprint_spec.rb
395
400
  - spec/lib/saml_idp/incoming_metadata_spec.rb
396
401
  - spec/lib/saml_idp/logout_request_builder_spec.rb
397
402
  - spec/lib/saml_idp/logout_response_builder_spec.rb
@@ -457,6 +462,9 @@ test_files:
457
462
  - spec/spec_helper.rb
458
463
  - spec/support/certificates/certificate1
459
464
  - spec/support/certificates/r1_certificate2_base64
465
+ - spec/support/certificates/sp_cert_req.csr
466
+ - spec/support/certificates/sp_private_key.pem
467
+ - spec/support/certificates/sp_x509_cert.crt
460
468
  - spec/support/responses/adfs_response_sha1.xml
461
469
  - spec/support/responses/adfs_response_sha256.xml
462
470
  - spec/support/responses/adfs_response_sha384.xml