saml_idp 0.7.1 → 0.10.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 (42) hide show
  1. checksums.yaml +5 -5
  2. data/Gemfile +1 -1
  3. data/README.md +40 -12
  4. data/lib/saml_idp/configurator.rb +1 -0
  5. data/lib/saml_idp/controller.rb +6 -2
  6. data/lib/saml_idp/encryptor.rb +1 -1
  7. data/lib/saml_idp/incoming_metadata.rb +9 -1
  8. data/lib/saml_idp/request.rb +14 -0
  9. data/lib/saml_idp/response_builder.rb +19 -5
  10. data/lib/saml_idp/saml_response.rb +15 -3
  11. data/lib/saml_idp/service_provider.rb +14 -0
  12. data/lib/saml_idp/signable.rb +1 -2
  13. data/lib/saml_idp/version.rb +1 -1
  14. data/lib/saml_idp/xml_security.rb +1 -1
  15. data/saml_idp.gemspec +26 -23
  16. data/spec/acceptance/idp_controller_spec.rb +5 -4
  17. data/spec/lib/saml_idp/algorithmable_spec.rb +6 -6
  18. data/spec/lib/saml_idp/assertion_builder_spec.rb +8 -8
  19. data/spec/lib/saml_idp/attribute_decorator_spec.rb +8 -8
  20. data/spec/lib/saml_idp/configurator_spec.rb +7 -7
  21. data/spec/lib/saml_idp/controller_spec.rb +23 -20
  22. data/spec/lib/saml_idp/encryptor_spec.rb +4 -4
  23. data/spec/lib/saml_idp/incoming_metadata_spec.rb +46 -0
  24. data/spec/lib/saml_idp/metadata_builder_spec.rb +7 -17
  25. data/spec/lib/saml_idp/name_id_formatter_spec.rb +3 -3
  26. data/spec/lib/saml_idp/request_spec.rb +22 -22
  27. data/spec/lib/saml_idp/response_builder_spec.rb +5 -3
  28. data/spec/lib/saml_idp/saml_response_spec.rb +31 -8
  29. data/spec/lib/saml_idp/service_provider_spec.rb +2 -2
  30. data/spec/lib/saml_idp/signable_spec.rb +1 -1
  31. data/spec/lib/saml_idp/signature_builder_spec.rb +2 -2
  32. data/spec/lib/saml_idp/signed_info_builder_spec.rb +3 -3
  33. data/spec/rails_app/app/controllers/saml_controller.rb +5 -1
  34. data/spec/rails_app/config/application.rb +0 -6
  35. data/spec/rails_app/config/environments/development.rb +1 -6
  36. data/spec/rails_app/config/environments/production.rb +1 -0
  37. data/spec/rails_app/config/environments/test.rb +1 -0
  38. data/spec/spec_helper.rb +3 -0
  39. data/spec/support/saml_request_macros.rb +2 -1
  40. data/spec/xml_security_spec.rb +12 -12
  41. metadata +85 -40
  42. data/spec/lib/saml_idp/.assertion_builder_spec.rb.swp +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: f5e1bd3ed3c6f24d3a0793606d08e6eb99d51eb8
4
- data.tar.gz: 29215034a6b59ee05a73da302a986f516e8e7ad8
2
+ SHA256:
3
+ metadata.gz: 1a69c8d8c945c47e87cb526d4dfcf5f30c2f687d03b33e99013eef63acbe4377
4
+ data.tar.gz: 4933af3eb5cb686111307cd86d074ab45cc879a253f7a0833aaaa3262add74db
5
5
  SHA512:
6
- metadata.gz: f2231a17b50851ee8d7c370b1b2ff4c372430ce5bae98e7c4e840a93b0985a29c4ed242c634b80ccd94fa9c47391b0a8987fb5267a718192ace7346eb18db098
7
- data.tar.gz: 71e4162bd719e566bbb85f37c7ed84c50ec241211275c405f24da83eddbffbda4fdd21918d8e8042139968cec1d4b2171358ef31ec44895827e486485e715199
6
+ metadata.gz: 61d5e37801af0e41b71cdf0bdbac6cac61e0b86969de75c27519b74ede55e3b9c2ec7c2dfc8bff9ef82ec1fa1a870c9483fe31e65e17d645fdf6cab5205ac2d1
7
+ data.tar.gz: a1594a0f1da0694a93c478259dcece4d38c30863564aa653b585bdae9ef6834699b784ff151b7569e7cccee22b41e0b5a37f0a5151a89b664feda92005f3f21f
data/Gemfile CHANGED
@@ -1,2 +1,2 @@
1
- source "http://rubygems.org"
1
+ source "https://rubygems.org"
2
2
  gemspec
data/README.md CHANGED
@@ -1,8 +1,9 @@
1
1
  # Ruby SAML Identity Provider (IdP)
2
+
2
3
  Forked from https://github.com/lawrencepit/ruby-saml-idp
3
4
 
4
- [![Build Status](https://travis-ci.org/sportngin/saml_idp.png)](https://travis-ci.org/sportngin/saml_idp)
5
- [![Gem Version](https://badge.fury.io/rb/saml_idp.png)](http://badge.fury.io/rb/saml_idp)
5
+ [![Build Status](https://travis-ci.org/saml-idp/saml_idp.svg)](https://travis-ci.org/saml-idp/saml_idp)
6
+ [![Gem Version](https://badge.fury.io/rb/saml_idp.svg)](http://badge.fury.io/rb/saml_idp)
6
7
 
7
8
  The ruby SAML Identity Provider library is for implementing the server side of SAML authentication. It allows
8
9
  your application to act as an IdP (Identity Provider) using the
@@ -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,18 +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
114
  # config.reference_id_generator # Default: -> { UUID.generate }
115
+ # config.single_logout_service_post_location = "#{base}/saml/logout"
116
+ # config.single_logout_service_redirect_location = "#{base}/saml/logout"
96
117
  # config.attribute_service_location = "#{base}/saml/attributes"
97
118
  # config.single_service_post_location = "#{base}/saml/auth"
98
119
  # config.session_expiry = 86400 # Default: 0 which means never
120
+ # config.signed_message = true # Default: false which means unsigned SAML Response
99
121
 
100
122
  # Principal (e.g. User) is passed in when you `encode_response`
101
123
  #
102
- # config.name_id.formats # =>
124
+ # config.name_id.formats =
103
125
  # { # All 2.0
104
126
  # email_address: -> (principal) { principal.email_address },
105
127
  # transient: -> (principal) { principal.id },
@@ -169,7 +191,11 @@ CERT
169
191
  service_providers = {
170
192
  "some-issuer-url.com/saml" => {
171
193
  fingerprint: "9E:65:2E:03:06:8D:80:F2:86:C7:6C:77:A1:D9:14:97:0A:4D:F4:4D",
172
- metadata_url: "http://some-issuer-url.com/saml/metadata"
194
+ metadata_url: "http://some-issuer-url.com/saml/metadata",
195
+
196
+ # We now validate AssertionConsumerServiceURL will match the MetadataURL set above.
197
+ # *If* it's not going to match your Metadata URL's Host, then set this so we can validate the host using this list
198
+ response_hosts: ["foo.some-issuer-url.com"]
173
199
  },
174
200
  }
175
201
 
@@ -177,7 +203,7 @@ CERT
177
203
  # settings is an IncomingMetadata object which has a to_h method that needs to be persisted
178
204
  config.service_provider.metadata_persister = ->(identifier, settings) {
179
205
  fname = identifier.to_s.gsub(/\/|:/,"_")
180
- `mkdir -p #{Rails.root.join("cache/saml/metadata")}`
206
+ FileUtils.mkdir_p(Rails.root.join('cache', 'saml', 'metadata').to_s)
181
207
  File.open Rails.root.join("cache/saml/metadata/#{fname}"), "r+b" do |f|
182
208
  Marshal.dump settings.to_h, f
183
209
  end
@@ -188,7 +214,7 @@ CERT
188
214
  # `service_provider` you should return the settings.to_h from above
189
215
  config.service_provider.persisted_metadata_getter = ->(identifier, service_provider){
190
216
  fname = identifier.to_s.gsub(/\/|:/,"_")
191
- `mkdir -p #{Rails.root.join("cache/saml/metadata")}`
217
+ FileUtils.mkdir_p(Rails.root.join('cache', 'saml', 'metadata').to_s)
192
218
  full_filename = Rails.root.join("cache/saml/metadata/#{fname}")
193
219
  if File.file?(full_filename)
194
220
  File.open full_filename, "rb" do |f|
@@ -205,6 +231,7 @@ end
205
231
  ```
206
232
 
207
233
  # Keys and Secrets
234
+
208
235
  To generate the SAML Response it uses a default X.509 certificate and secret key... which isn't so secret.
209
236
  You can find them in `SamlIdp::Default`. The X.509 certificate is valid until year 2032.
210
237
  Obviously you shouldn't use these if you intend to use this in production environments. In that case,
@@ -218,18 +245,19 @@ The fingerprint to use, if you use the default X.509 certificate of this gem, is
218
245
  9E:65:2E:03:06:8D:80:F2:86:C7:6C:77:A1:D9:14:97:0A:4D:F4:4D
219
246
  ```
220
247
 
221
-
222
248
  # Service Providers
249
+
223
250
  To act as a Service Provider which generates SAML Requests and can react to SAML Responses use the
224
251
  excellent [ruby-saml](https://github.com/onelogin/ruby-saml) gem.
225
252
 
226
-
227
253
  # Author
228
- Jon Phenow, me@jphenow.com
254
+
255
+ Jon Phenow, jon@jphenow.com, jphenow.com, @jphenow
229
256
 
230
257
  Lawrence Pit, lawrence.pit@gmail.com, lawrencepit.com, @lawrencepit
231
258
 
232
259
  # Copyright
260
+
233
261
  Copyright (c) 2012 Sport Ngin.
234
262
  Portions Copyright (c) 2010 OneLogin, LLC
235
263
  Portions Copyright (c) 2012 Lawrence Pit (http://lawrencepit.com)
@@ -17,6 +17,7 @@ module SamlIdp
17
17
  attr_accessor :single_logout_service_redirect_location
18
18
  attr_accessor :attributes
19
19
  attr_accessor :service_provider
20
+ attr_accessor :assertion_consumer_service_hosts
20
21
  attr_accessor :session_expiry
21
22
 
22
23
  def initialize
@@ -35,13 +35,15 @@ module SamlIdp
35
35
 
36
36
  def validate_saml_request(raw_saml_request = params[:SAMLRequest])
37
37
  decode_request(raw_saml_request)
38
- unless valid_saml_request?
38
+ return true if valid_saml_request?
39
+ if defined?(::Rails)
39
40
  if Rails::VERSION::MAJOR >= 4
40
41
  head :forbidden
41
42
  else
42
43
  render nothing: true, status: :forbidden
43
44
  end
44
45
  end
46
+ false
45
47
  end
46
48
 
47
49
  def decode_request(raw_saml_request)
@@ -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
 
@@ -72,7 +72,7 @@ module SamlIdp
72
72
  cipher_data.CipherValue
73
73
  end
74
74
  enc_key.ReferenceList do |ref_list|
75
- ref_list.DataReference URI: 'ED'
75
+ ref_list.DataReference URI: '#ED'
76
76
  end
77
77
  end
78
78
  end
@@ -16,13 +16,21 @@ 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",
22
27
  ds: signature_namespace,
23
28
  md: metadata_namespace
24
29
  ).first
25
- doc ? !!doc["WantAssertionsSigned"] : false
30
+ if (doc && !doc['WantAssertionsSigned'].nil?)
31
+ return doc['WantAssertionsSigned'].strip.downcase == 'true'
32
+ end
33
+ return false
26
34
  end
27
35
  hashable :sign_assertions
28
36
 
@@ -105,6 +105,12 @@ module SamlIdp
105
105
  return false
106
106
  end
107
107
 
108
+ if !service_provider.acceptable_response_hosts.include?(response_host)
109
+ log "#{service_provider.acceptable_response_hosts} compare to #{response_host}"
110
+ log "No acceptable AssertionConsumerServiceURL, either configure them via config.service_provider.response_hosts or match to your metadata_url host"
111
+ return false
112
+ end
113
+
108
114
  return true
109
115
  end
110
116
 
@@ -136,6 +142,14 @@ module SamlIdp
136
142
  @_session_index ||= xpath("//samlp:SessionIndex", samlp: samlp).first.try(:content)
137
143
  end
138
144
 
145
+ def response_host
146
+ uri = URI(response_url)
147
+ if uri
148
+ uri.host
149
+ end
150
+ end
151
+ private :response_host
152
+
139
153
  def document
140
154
  @_document ||= Saml::XML::Document.parse(raw_xml)
141
155
  end
@@ -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
 
@@ -13,6 +13,7 @@ module SamlIdp
13
13
  attribute :validate_signature
14
14
  attribute :acs_url
15
15
  attribute :assertion_consumer_logout_service_url
16
+ attribute :response_hosts
16
17
 
17
18
  delegate :config, to: :SamlIdp
18
19
 
@@ -46,6 +47,19 @@ module SamlIdp
46
47
  @current_metadata ||= get_current_or_build
47
48
  end
48
49
 
50
+ def acceptable_response_hosts
51
+ hosts = Array(self.response_hosts)
52
+ hosts.push(metadata_url_host) if metadata_url_host
53
+
54
+ hosts
55
+ end
56
+
57
+ def metadata_url_host
58
+ if metadata_url.present?
59
+ URI(metadata_url).host
60
+ end
61
+ end
62
+
49
63
  def get_current_or_build
50
64
  persisted = metadata_getter[identifier, self]
51
65
  if persisted.is_a? Hash
@@ -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.7.1'
3
+ VERSION = '0.10.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)
@@ -7,28 +7,29 @@ Gem::Specification.new do |s|
7
7
  s.version = SamlIdp::VERSION
8
8
  s.platform = Gem::Platform::RUBY
9
9
  s.authors = ["Jon Phenow"]
10
- s.email = %q{jon.phenow@sportngin.com}
11
- s.homepage = %q{http://github.com/sportngin/saml_idp}
12
- s.summary = %q{SAML Indentity Provider in ruby}
13
- s.description = %q{SAML IdP (Identity Provider) library in ruby}
10
+ s.email = 'jon.phenow@sportngin.com'
11
+ s.homepage = 'https://github.com/saml-idp/saml_idp'
12
+ s.summary = 'SAML Indentity Provider for Ruby'
13
+ s.description = 'SAML IdP (Identity Provider) Library for Ruby'
14
14
  s.date = Time.now.utc.strftime("%Y-%m-%d")
15
- s.files = Dir.glob("app/**/*") + Dir.glob("lib/**/*") + [
16
- "LICENSE",
17
- "README.md",
18
- "Gemfile",
19
- "saml_idp.gemspec"
20
- ]
15
+ s.files = Dir['app/**/*', 'lib/**/*', 'LICENSE', 'README.md', 'Gemfile', 'saml_idp.gemspec']
21
16
  s.required_ruby_version = '>= 2.2'
22
- s.license = "LICENSE"
17
+ s.license = 'MIT'
23
18
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
24
19
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
25
20
  s.require_paths = ["lib"]
26
- s.rdoc_options = ["--charset=UTF-8"]
21
+ s.rdoc_options = ['--charset=UTF-8']
22
+ s.metadata = {
23
+ 'homepage_uri' => 'https://github.com/saml-idp/saml_idp',
24
+ 'source_code_uri' => 'https://github.com/saml-idp/saml_idp',
25
+ 'bug_tracker_uri' => 'https://github.com/saml-idp/saml_idp/issues',
26
+ 'documentation_uri' => "http://rdoc.info/gems/saml_idp/#{SamlIdp::VERSION}"
27
+ }
27
28
 
28
29
  s.post_install_message = <<-INST
29
30
  If you're just recently updating saml_idp - please be aware we've changed the default
30
31
  certificate. See the PR and a description of why we've done this here:
31
- https://github.com/sportngin/saml_idp/pull/29
32
+ https://github.com/saml-idp/saml_idp/pull/29
32
33
 
33
34
  If you just need to see the certificate `bundle open saml_idp` and go to
34
35
  `lib/saml_idp/default.rb`
@@ -43,17 +44,19 @@ section of the README.
43
44
  INST
44
45
 
45
46
  s.add_dependency('activesupport', '>= 3.2')
46
- s.add_dependency('uuid', '~> 2.3')
47
- s.add_dependency('builder', '~> 3.0')
47
+ s.add_dependency('uuid', '>= 2.3')
48
+ s.add_dependency('builder', '>= 3.0')
48
49
  s.add_dependency('nokogiri', '>= 1.6.2')
49
50
 
50
- s.add_development_dependency('rake', '~> 10.4.2')
51
- s.add_development_dependency('simplecov', '~> 0.12')
52
- s.add_development_dependency('rspec', '~> 2.5')
53
- s.add_development_dependency('ruby-saml', '~> 1.3')
54
- s.add_development_dependency('rails', '~> 3.2')
55
- s.add_development_dependency('capybara', '~> 2.11.0')
56
- s.add_development_dependency('timecop', '~> 0.8')
51
+ s.add_development_dependency('rake')
52
+ s.add_development_dependency('simplecov')
53
+ s.add_development_dependency('rspec', '>= 3.7.0')
54
+ s.add_development_dependency('ruby-saml', '>= 1.7.2')
55
+ s.add_development_dependency('rails', '>= 3.2')
56
+ s.add_development_dependency('activeresource', '>= 3.2')
57
+ s.add_development_dependency('capybara', '>= 2.16')
58
+ s.add_development_dependency('timecop', '>= 0.8')
57
59
  s.add_development_dependency('xmlenc', '>= 0.6.4')
60
+ s.add_development_dependency('appraisal')
61
+ s.add_development_dependency('byebug')
58
62
  end
59
-