rails-auth 2.1.4 → 2.2.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 (58) hide show
  1. checksums.yaml +5 -5
  2. data/.rubocop.yml +14 -1
  3. data/.travis.yml +8 -5
  4. data/BUG-BOUNTY.md +3 -3
  5. data/CHANGES.md +21 -2
  6. data/CONTRIBUTING.md +11 -10
  7. data/Gemfile +6 -5
  8. data/Guardfile +2 -0
  9. data/Rakefile +3 -1
  10. data/lib/rails/auth/acl.rb +4 -0
  11. data/lib/rails/auth/acl/matchers/allow_all.rb +3 -0
  12. data/lib/rails/auth/acl/middleware.rb +3 -0
  13. data/lib/rails/auth/acl/resource.rb +7 -5
  14. data/lib/rails/auth/config_builder.rb +4 -0
  15. data/lib/rails/auth/controller_methods.rb +4 -0
  16. data/lib/rails/auth/credentials.rb +2 -0
  17. data/lib/rails/auth/credentials/injector_middleware.rb +6 -2
  18. data/lib/rails/auth/env.rb +4 -3
  19. data/lib/rails/auth/error_page/debug_middleware.rb +1 -1
  20. data/lib/rails/auth/error_page/middleware.rb +3 -0
  21. data/lib/rails/auth/exceptions.rb +2 -0
  22. data/lib/rails/auth/helpers.rb +2 -0
  23. data/lib/rails/auth/installed_constraint.rb +2 -0
  24. data/lib/rails/auth/monitor/middleware.rb +2 -0
  25. data/lib/rails/auth/rack.rb +1 -0
  26. data/lib/rails/auth/rspec.rb +2 -0
  27. data/lib/rails/auth/rspec/helper_methods.rb +6 -5
  28. data/lib/rails/auth/rspec/matchers/acl_matchers.rb +2 -0
  29. data/lib/rails/auth/version.rb +1 -1
  30. data/lib/rails/auth/x509/certificate.rb +35 -5
  31. data/lib/rails/auth/x509/filter/java.rb +4 -12
  32. data/lib/rails/auth/x509/filter/pem.rb +2 -0
  33. data/lib/rails/auth/x509/matcher.rb +2 -0
  34. data/lib/rails/auth/x509/middleware.rb +4 -3
  35. data/lib/rails/auth/x509/subject_alt_name_extension.rb +29 -0
  36. data/rails-auth.gemspec +5 -4
  37. data/spec/rails/auth/acl/matchers/allow_all_spec.rb +2 -0
  38. data/spec/rails/auth/acl/middleware_spec.rb +2 -0
  39. data/spec/rails/auth/acl/resource_spec.rb +2 -0
  40. data/spec/rails/auth/acl_spec.rb +2 -0
  41. data/spec/rails/auth/controller_methods_spec.rb +2 -0
  42. data/spec/rails/auth/credentials/injector_middleware_spec.rb +15 -0
  43. data/spec/rails/auth/credentials_spec.rb +2 -0
  44. data/spec/rails/auth/env_spec.rb +2 -0
  45. data/spec/rails/auth/error_page/debug_middleware_spec.rb +2 -0
  46. data/spec/rails/auth/error_page/middleware_spec.rb +2 -0
  47. data/spec/rails/auth/monitor/middleware_spec.rb +2 -0
  48. data/spec/rails/auth/rspec/helper_methods_spec.rb +2 -0
  49. data/spec/rails/auth/rspec/matchers/acl_matchers_spec.rb +2 -0
  50. data/spec/rails/auth/x509/certificate_spec.rb +103 -20
  51. data/spec/rails/auth/x509/matcher_spec.rb +2 -0
  52. data/spec/rails/auth/x509/middleware_spec.rb +6 -2
  53. data/spec/rails/auth/x509/subject_alt_name_extension_spec.rb +39 -0
  54. data/spec/rails/auth_spec.rb +2 -0
  55. data/spec/spec_helper.rb +5 -3
  56. data/spec/support/claims_matcher.rb +2 -0
  57. data/spec/support/create_certs.rb +57 -2
  58. metadata +14 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 6d86f415a45113f3337b8446a3b5b271682c6a4d
4
- data.tar.gz: 79c560e82cb196908a94b836697398af20a04ad0
2
+ SHA256:
3
+ metadata.gz: c22c652d804b973313277d33b6c08dd0f3ae6389dfa0ebeaba73fd84488084b3
4
+ data.tar.gz: 95f4c1eab88b2ee3f8f608682690e70c8ca3a4c1b9925b537f14d33c88b91f7c
5
5
  SHA512:
6
- metadata.gz: b030ade8c37b4f91fba160037f8f951ce8729884dc18fd1d6d038464b7b8a9af10bb38934d1d1f4bdf8672ef66425555abf49e12af8c603e2bf80c5dbc7e03c6
7
- data.tar.gz: 0c0a1c9edd16819f0ab1ed10b5e98d734193fb14151586e2ab752088c706074fdb3d98cfa027635be16dc79bb7d3ec94c4f2e0be3de3f0252a3c0d0eb1fffab9
6
+ metadata.gz: a78bb6b55dd36517e9fd325f433210d3367d2aab18541507ac9feb607cdc92c4f2ce4b26df69bdd093c918b98bfc8c37c75add8ce572a0eb07025d96f2bc95f7
7
+ data.tar.gz: c5363dfa5f09bef450d827bdfa24226c4e080c47ff8c4b5c81da94192c834ac4d3fa97531d9dec11b5907949d249f59cf9856dd1b727f40d89b1ce2379ce60ac
@@ -1,12 +1,16 @@
1
1
  AllCops:
2
2
  DisplayCopNames: true
3
+ TargetRubyVersion: 2.3
3
4
 
4
5
  Style/StringLiterals:
5
6
  EnforcedStyle: double_quotes
6
7
 
7
- Style/ModuleFunction:
8
+ Layout/HashAlignment:
8
9
  Enabled: false
9
10
 
11
+ Metrics/BlockLength:
12
+ ExcludedMethods: ['describe', 'context']
13
+
10
14
  Metrics/ParameterLists:
11
15
  Max: 5
12
16
  CountKeywordArgs: false
@@ -22,3 +26,12 @@ Metrics/AbcSize:
22
26
 
23
27
  Metrics/CyclomaticComplexity:
24
28
  Max: 8
29
+
30
+ Naming/MethodParameterName:
31
+ MinNameLength: 2
32
+
33
+ Style/ModuleFunction:
34
+ Enabled: false
35
+
36
+ Style/SafeNavigation:
37
+ Enabled: false
@@ -10,12 +10,15 @@ before_install:
10
10
  bundler_args: --without development
11
11
 
12
12
  rvm:
13
- - 2.0.0
14
- - 2.1.10
15
- - 2.2.4
16
- - 2.3.0
13
+ - 2.4
14
+ - 2.5
15
+ - 2.6
17
16
  matrix:
18
17
  include:
19
- - rvm: jruby-9.0.5.0
18
+ - rvm: jruby
19
+ jdk: openjdk8
20
+ env: JRUBY_OPTS="--debug" # for simplecov
21
+ - rvm: jruby
22
+ jdk: openjdk11
20
23
  env: JRUBY_OPTS="--debug" # for simplecov
21
24
  fast_finish: true
@@ -1,9 +1,9 @@
1
- Break me and win a prize!
2
- =========================
1
+ Serious about security
2
+ ======================
3
3
 
4
4
  Square recognizes the important contributions the security research community
5
5
  can make. We therefore encourage reporting security issues with the code
6
6
  contained in this repository.
7
7
 
8
8
  If you believe you have discovered a security vulnerability, please follow the
9
- guidelines at https://hackerone.com/square-open-source
9
+ guidelines at <https://bugcrowd.com/squareopensource>.
data/CHANGES.md CHANGED
@@ -1,3 +1,19 @@
1
+ ### 2.2.0 (2019-12-05)
2
+
3
+ * [#55](https://github.com/square/rails-auth/pull/55)
4
+ Allow dynamic injection of credentials.
5
+ ([@drcapulet])
6
+
7
+ * [#59](https://github.com/square/rails-auth/pull/59)
8
+ Expose X.509 Subject Alternative Name extension
9
+ in the Rails::Auth::X509::Certificate and provide a convenience
10
+ method `spiffe_id` to expose [SPIFFE ID](https://spiffe.io).
11
+ ([@mbyczkowski])
12
+
13
+ * [#57](https://github.com/square/rails-auth/pull/57)
14
+ Add support for latest versions of Ruby, JRuby and Bundler 2.
15
+ ([@mbyczkowski])
16
+
1
17
  ### 2.1.4 (2018-07-12)
2
18
 
3
19
  * [#51](https://github.com/square/rails-auth/pull/51)
@@ -184,6 +200,9 @@
184
200
  * Vaporware release to claim the "rails-auth" gem name
185
201
 
186
202
 
187
- [@tarcieri]: https://github.com/tarcieri
188
- [@ewr]: https://github.com/ewr
189
203
  [@drcapulet]: https://github.com/drcapulet
204
+ [@ewr]: https://github.com/ewr
205
+ [@mbyczkowski]: https://github.com/mbyczkowski
206
+ [@nerdrew]: https://github.com/nerdrew
207
+ [@tarcieri]: https://github.com/tarcieri
208
+ [@yellow-beard]: https://github.com/yellow-beard
@@ -1,14 +1,15 @@
1
- ### Sign the CLA
1
+ # Contributing
2
2
 
3
- Any contributors to the master *rails-auth* repository must sign the
4
- [Individual Contributor License Agreement (CLA)]. It's a short form that covers
5
- our bases and makes sure you're eligible to contribute.
3
+ If you would like to contribute code to *rails-auth* you can do so through GitHub by
4
+ forking the repository and sending a pull request.
6
5
 
7
- ### Submitting a Pull Request
6
+ When submitting code, please make every effort to follow existing conventions
7
+ and style in order to keep the code as readable as possible. Please also make
8
+ sure all tests pass by running `bundle exec rspec spec`, and format your code
9
+ according to `rubocop` rules.
8
10
 
9
- When you have a change you'd like to see in the master repository, send a
10
- [pull request]. Before we merge your request, we'll make sure you're in the list
11
- of people who have signed a CLA.
11
+ Before your code can be accepted into the project you must also sign the
12
+ Individual Contributor License Agreement. We use [cla-assistant.io][1] and you
13
+ will be prompted to sign once a pull request is opened.
12
14
 
13
- [Individual Contributor License Agreement (CLA)]: https://spreadsheets.google.com/spreadsheet/viewform?formkey=dDViT2xzUHAwRkI3X3k5Z0lQM091OGc6MQ&ndplr=1
14
- [pull request]: https://github.com/square/rails-auth/pulls
15
+ [1]: https://cla-assistant.io/
data/Gemfile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source "https://rubygems.org"
2
4
 
3
5
  group :development do
@@ -5,15 +7,14 @@ group :development do
5
7
  end
6
8
 
7
9
  group :development, :test do
10
+ gem "activesupport", "~> 4"
11
+ gem "certificate_authority", require: false
12
+ gem "coveralls", require: false
8
13
  # Workaround for: https://github.com/bundler/bundler/pull/4650
9
14
  gem "rack", "~> 1.x"
10
- gem "activesupport", "~> 4"
11
-
12
15
  gem "rake"
13
16
  gem "rspec"
14
- gem "rubocop", "0.38.0"
15
- gem "coveralls", require: false
16
- gem "certificate_authority", require: false
17
+ gem "rubocop", "0.77.0"
17
18
  end
18
19
 
19
20
  gemspec
data/Guardfile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  guard :rspec, cmd: "bundle exec rspec" do
2
4
  watch(%r{^spec/.+_spec\.rb$})
3
5
  watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
data/Rakefile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "bundler/gem_tasks"
2
4
  require "rspec/core/rake_task"
3
5
  require "rubocop/rake_task"
@@ -5,4 +7,4 @@ require "rubocop/rake_task"
5
7
  RSpec::Core::RakeTask.new(:spec)
6
8
  RuboCop::RakeTask.new
7
9
 
8
- task default: %w(spec rubocop)
10
+ task default: %w[spec rubocop]
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Pull in default matchers
2
4
  require "rails/auth/acl/matchers/allow_all"
3
5
 
@@ -17,7 +19,9 @@ module Rails
17
19
  # @param [String] :yaml serialized YAML to load an ACL from
18
20
  def self.from_yaml(yaml, **args)
19
21
  require "yaml"
22
+ # rubocop:todo Security/YAMLLoad
20
23
  new(YAML.load(yaml), **args)
24
+ # rubocop:enable Security/YAMLLoad
21
25
  end
22
26
 
23
27
  # @param [Array<Hash>] :acl Access Control List configuration
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rails
2
4
  module Auth
3
5
  class ACL
@@ -7,6 +9,7 @@ module Rails
7
9
  class AllowAll
8
10
  def initialize(enabled)
9
11
  raise ArgumentError, "enabled must be true/false" unless [true, false].include?(enabled)
12
+
10
13
  @enabled = enabled
11
14
  end
12
15
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rails
2
4
  module Auth
3
5
  class ACL
@@ -25,6 +27,7 @@ module Rails
25
27
  unless Rails::Auth.authorized?(env)
26
28
  matcher_name = @acl.match(env)
27
29
  raise NotAuthorizedError, "unauthorized request" unless matcher_name
30
+
28
31
  Rails::Auth.set_allowed_by(env, "matcher:#{matcher_name}")
29
32
  end
30
33
 
@@ -8,10 +8,10 @@ module Rails
8
8
  attr_reader :http_methods, :path, :host, :matchers
9
9
 
10
10
  # Valid HTTP methods
11
- HTTP_METHODS = %w(GET HEAD PUT POST DELETE OPTIONS PATCH LINK UNLINK).freeze
11
+ HTTP_METHODS = %w[GET HEAD PUT POST DELETE OPTIONS PATCH LINK UNLINK].freeze
12
12
 
13
13
  # Options allowed for resource matchers
14
- VALID_OPTIONS = %w(method path host).freeze
14
+ VALID_OPTIONS = %w[method path host].freeze
15
15
 
16
16
  # @option :options [String] :method HTTP method allowed ("ALL" for all methods)
17
17
  # @option :options [String] :path path to the resource (regex syntax allowed)
@@ -46,6 +46,7 @@ module Rails
46
46
  #
47
47
  def match(env)
48
48
  return nil unless match!(env)
49
+
49
50
  name, = @matchers.find { |_name, matcher| matcher.match(env) }
50
51
  name
51
52
  end
@@ -58,9 +59,10 @@ module Rails
58
59
  # @return [Boolean] method and path *only* match the given environment
59
60
  #
60
61
  def match!(env)
61
- return false unless @http_methods.include?(env["REQUEST_METHOD".freeze])
62
- return false unless @path =~ env["PATH_INFO".freeze]
63
- return false unless @host.nil? || @host =~ env["HTTP_HOST".freeze]
62
+ return false unless @http_methods.include?(env["REQUEST_METHOD"])
63
+ return false unless @path =~ env["PATH_INFO"]
64
+ return false unless @host.nil? || @host =~ env["HTTP_HOST"]
65
+
64
66
  true
65
67
  end
66
68
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rails
2
4
  module Auth
3
5
  # Configures Rails::Auth middleware for use in a Rails application
@@ -49,6 +51,7 @@ module Rails
49
51
  end
50
52
 
51
53
  return unless monitor
54
+
52
55
  config.middleware.insert_before Rails::Auth::ACL::Middleware,
53
56
  Rails::Auth::Monitor::Middleware,
54
57
  monitor
@@ -68,6 +71,7 @@ module Rails
68
71
  Rails::Auth::ErrorPage::Middleware,
69
72
  page_body: Pathname(error_page).read
70
73
  when FalseClass, NilClass
74
+ nil
71
75
  else raise TypeError, "bad error page mode: #{mode.inspect}"
72
76
  end
73
77
  end
@@ -1,5 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_support/hash_with_indifferent_access"
2
4
 
5
+ # rubocop:disable Naming/MemoizedInstanceVariableName
3
6
  module Rails
4
7
  module Auth
5
8
  # Convenience methods designed to be included in an ActionController::Base subclass
@@ -18,3 +21,4 @@ module Rails
18
21
  end
19
22
  end
20
23
  end
24
+ # rubocop:enable Naming/MemoizedInstanceVariableName
@@ -18,6 +18,7 @@ module Rails
18
18
 
19
19
  def initialize(credentials = {})
20
20
  raise TypeError, "expected Hash, got #{credentials.class}" unless credentials.is_a?(Hash)
21
+
21
22
  @credentials = credentials
22
23
  end
23
24
 
@@ -25,6 +26,7 @@ module Rails
25
26
  return if @credentials.key?(type) && @credentials[type] == value
26
27
  raise TypeError, "expected String for type, got #{type.class}" unless type.is_a?(String)
27
28
  raise AlreadyAuthorizedError, "credential '#{type}' has already been set" if @credentials.key?(type)
29
+
28
30
  @credentials[type] = value
29
31
  end
30
32
 
@@ -1,9 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rails
2
4
  module Auth
3
5
  class Credentials
4
6
  # A middleware for injecting an arbitrary credentials hash into the Rack environment
5
7
  # This is intended for development and testing purposes where you would like to
6
- # simulate a given X.509 certificate being used in a request or user logged in
8
+ # simulate a given X.509 certificate being used in a request or user logged in.
9
+ # The credentials argument should either be a hash or a proc that returns one.
7
10
  class InjectorMiddleware
8
11
  def initialize(app, credentials)
9
12
  @app = app
@@ -11,7 +14,8 @@ module Rails
11
14
  end
12
15
 
13
16
  def call(env)
14
- env[Rails::Auth::Env::CREDENTIALS_ENV_KEY] = @credentials
17
+ credentials = @credentials.respond_to?(:call) ? @credentials.call(env) : @credentials
18
+ env[Rails::Auth::Env::CREDENTIALS_ENV_KEY] = credentials
15
19
  @app.call(env)
16
20
  end
17
21
  end
@@ -5,13 +5,13 @@ module Rails
5
5
  # Wrapper for Rack environments with Rails::Auth helpers
6
6
  class Env
7
7
  # Rack environment key for marking external authorization
8
- AUTHORIZED_ENV_KEY = "rails-auth.authorized".freeze
8
+ AUTHORIZED_ENV_KEY = "rails-auth.authorized"
9
9
 
10
10
  # Rack environment key for storing what allowed the request
11
- ALLOWED_BY_ENV_KEY = "rails-auth.allowed-by".freeze
11
+ ALLOWED_BY_ENV_KEY = "rails-auth.allowed-by"
12
12
 
13
13
  # Rack environment key for all rails-auth credentials
14
- CREDENTIALS_ENV_KEY = "rails-auth.credentials".freeze
14
+ CREDENTIALS_ENV_KEY = "rails-auth.credentials"
15
15
 
16
16
  attr_reader :allowed_by, :credentials
17
17
 
@@ -44,6 +44,7 @@ module Rails
44
44
  def allowed_by=(allowed_by)
45
45
  raise AlreadyAuthorizedError, "already allowed by #{@allowed_by.inspect}" if @allowed_by
46
46
  raise TypeError, "expected String for allowed_by, got #{allowed_by.class}" unless allowed_by.is_a?(String)
47
+
47
48
  @allowed_by = allowed_by
48
49
  end
49
50
 
@@ -26,7 +26,7 @@ module Rails
26
26
 
27
27
  @app = app
28
28
  @acl = acl
29
- @erb = ERB.new(File.read(File.expand_path("../debug_page.html.erb", __FILE__))).freeze
29
+ @erb = ERB.new(File.read(File.expand_path("debug_page.html.erb", __dir__))).freeze
30
30
  end
31
31
 
32
32
  def call(env)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rails
2
4
  module Auth
3
5
  module ErrorPage
@@ -32,6 +34,7 @@ module Rails
32
34
  accept_format = env["HTTP_ACCEPT"]
33
35
  return :json if accept_format && accept_format.downcase.start_with?("application/json")
34
36
  return :json if env["PATH_INFO"] && env["PATH_INFO"].end_with?(".json")
37
+
35
38
  nil
36
39
  end
37
40
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rails
2
4
  module Auth
3
5
  # Base class of all Rails::Auth errors
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rails
2
4
  # Modular resource-based authentication and authorization for Rails/Rack
3
5
  module Auth
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rails
2
4
  module Auth
3
5
  # Rails constraint to make sure the ACLs have been installed
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rails
2
4
  module Auth
3
5
  module Monitor
@@ -27,3 +27,4 @@ require "rails/auth/x509/filter/pem"
27
27
  require "rails/auth/x509/filter/java" if defined?(JRUBY_VERSION)
28
28
  require "rails/auth/x509/matcher"
29
29
  require "rails/auth/x509/middleware"
30
+ require "rails/auth/x509/subject_alt_name_extension"
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "rails/auth/rspec/helper_methods"
2
4
  require "rails/auth/rspec/matchers/acl_matchers"
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rails
2
4
  module Auth
3
5
  module RSpec
@@ -12,6 +14,7 @@ module Rails
12
14
  # NOTE: Credentials will be *cleared* after the block. Nesting is not allowed.
13
15
  def with_credentials(credentials = {})
14
16
  raise TypeError, "expected Hash of credentials, got #{credentials.class}" unless credentials.is_a?(Hash)
17
+
15
18
  test_credentials.clear
16
19
 
17
20
  credentials.each do |type, value|
@@ -24,8 +27,8 @@ module Rails
24
27
  # Creates an Rails::Auth::X509::Certificate instance double
25
28
  def x509_certificate(cn: nil, ou: nil)
26
29
  subject = ""
27
- subject << "CN=#{cn}" if cn
28
- subject << "OU=#{ou}" if ou
30
+ subject += "CN=#{cn}" if cn
31
+ subject += "OU=#{ou}" if ou
29
32
 
30
33
  instance_double(Rails::Auth::X509::Certificate, subject, cn: cn, ou: ou).tap do |certificate|
31
34
  allow(certificate).to receive(:[]) do |key|
@@ -47,9 +50,7 @@ module Rails
47
50
  path = self.class.description
48
51
 
49
52
  # Warn if methods are improperly used
50
- unless path.chars[0] == "/"
51
- raise ArgumentError, "expected #{path} to start with '/'"
52
- end
53
+ raise ArgumentError, "expected #{path} to start with '/'" unless path.chars[0] == "/"
53
54
 
54
55
  env = {
55
56
  "REQUEST_METHOD" => method,
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  RSpec::Matchers.define(:permit) do |env|
2
4
  description do
3
5
  method = env["REQUEST_METHOD"]
@@ -3,6 +3,6 @@
3
3
  module Rails
4
4
  # Pluggable authentication and authorization for Rack/Rails
5
5
  module Auth
6
- VERSION = "2.1.4".freeze
6
+ VERSION = "2.2.0"
7
7
  end
8
8
  end
@@ -18,7 +18,8 @@ module Rails
18
18
  @certificate.subject.to_a.each do |name, data, _type|
19
19
  @subject[name.freeze] = data.freeze
20
20
  end
21
-
21
+ @subject_alt_names = SubjectAltNameExtension.new(certificate)
22
+ @subject_alt_names.freeze
22
23
  @subject.freeze
23
24
  end
24
25
 
@@ -27,23 +28,52 @@ module Rails
27
28
  end
28
29
 
29
30
  def cn
30
- @subject["CN".freeze]
31
+ @subject["CN"]
31
32
  end
32
33
  alias common_name cn
33
34
 
35
+ def dns_names
36
+ @subject_alt_names.dns_names
37
+ end
38
+
39
+ def ips
40
+ @subject_alt_names.ips
41
+ end
42
+
34
43
  def ou
35
- @subject["OU".freeze]
44
+ @subject["OU"]
36
45
  end
37
46
  alias organizational_unit ou
38
47
 
48
+ def uris
49
+ @subject_alt_names.uris
50
+ end
51
+
52
+ # According to the SPIFFE standard only one SPIFFE ID can exist in the URI
53
+ # SAN:
54
+ # (https://github.com/spiffe/spiffe/blob/master/standards/X509-SVID.md#2-spiffe-id)
55
+ #
56
+ # @return [String, nil] string containing SPIFFE ID if one is present
57
+ # in the certificate
58
+ def spiffe_id
59
+ uris.detect { |uri| uri.start_with?("spiffe://") }
60
+ end
61
+
39
62
  # Generates inspectable attributes for debugging
40
63
  #
41
64
  # @return [Hash] hash containing parts of the certificate subject (cn, ou)
65
+ # and subject alternative name extension (uris, dns_names) as well
66
+ # as SPIFFE ID (spiffe_id), which is just a convenience since those
67
+ # are already included in the uris
42
68
  def attributes
43
69
  {
44
70
  cn: cn,
45
- ou: ou
46
- }
71
+ dns_names: dns_names,
72
+ ips: ips,
73
+ ou: ou,
74
+ spiffe_id: spiffe_id,
75
+ uris: uris
76
+ }.reject { |_, v| v.nil? || v.empty? }
47
77
  end
48
78
 
49
79
  # Compare ourself to another object by ensuring that it has the same type
@@ -1,23 +1,15 @@
1
- require "java"
2
- require "stringio"
1
+ # frozen_string_literal: true
3
2
 
4
3
  module Rails
5
4
  module Auth
6
5
  module X509
7
6
  module Filter
8
- # Extract OpenSSL::X509::Certificates from Java's sun.security.x509.X509CertImpl
7
+ # Extract OpenSSL::X509::Certificates from java.security.cert.Certificate
9
8
  class Java
10
9
  def call(certs)
11
- return unless certs
12
- OpenSSL::X509::Certificate.new(extract_der(certs[0])).freeze
13
- end
14
-
15
- private
10
+ return if certs.nil? || certs.empty?
16
11
 
17
- def extract_der(cert)
18
- stringio = StringIO.new
19
- cert.derEncode(stringio.to_outputstream)
20
- stringio.string
12
+ OpenSSL::X509::Certificate.new(certs[0].get_encoded).freeze
21
13
  end
22
14
  end
23
15
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rails
2
4
  module Auth
3
5
  module X509
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rails
2
4
  module Auth
3
5
  module X509
@@ -40,7 +40,7 @@ module Rails
40
40
 
41
41
  def call(env)
42
42
  credential = extract_credential(env)
43
- Rails::Auth.add_credential(env, "x509".freeze, credential.freeze) if credential
43
+ Rails::Auth.add_credential(env, "x509", credential.freeze) if credential
44
44
 
45
45
  @app.call(env)
46
46
  end
@@ -62,6 +62,7 @@ module Rails
62
62
  end
63
63
 
64
64
  raise CertificateVerifyFailed, "no client certificate in request" if @require_cert
65
+
65
66
  nil
66
67
  end
67
68
 
@@ -72,8 +73,8 @@ module Rails
72
73
  end
73
74
 
74
75
  filter.call(raw_cert)
75
- rescue => ex
76
- @logger.debug("rails-auth: Certificate error: #{ex.class}: #{ex.message}") if @logger
76
+ rescue StandardError => e
77
+ @logger.debug("rails-auth: Certificate error: #{e.class}: #{e.message}") if @logger
77
78
  nil
78
79
  end
79
80
 
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rails
4
+ module Auth
5
+ module X509
6
+ # Provides convenience methods for subjectAltName extension of X.509 certificates
7
+ class SubjectAltNameExtension
8
+ attr_reader :dns_names, :ips, :uris
9
+
10
+ DNS_REGEX = /^DNS:/i.freeze
11
+ IP_REGEX = /^IP( Address)?:/i.freeze
12
+ URI_REGEX = /^URI:/i.freeze
13
+
14
+ def initialize(certificate)
15
+ unless certificate.is_a?(OpenSSL::X509::Certificate)
16
+ raise TypeError, "expecting OpenSSL::X509::Certificate, got #{certificate.class}"
17
+ end
18
+
19
+ extension = certificate.extensions.detect { |ext| ext.oid == "subjectAltName" }
20
+ values = (extension&.value&.split(",") || []).map(&:strip)
21
+
22
+ @dns_names = values.grep(DNS_REGEX) { |v| v.sub(DNS_REGEX, "") }.freeze
23
+ @ips = values.grep(IP_REGEX) { |v| v.sub(IP_REGEX, "") }.freeze
24
+ @uris = values.grep(URI_REGEX) { |v| v.sub(URI_REGEX, "") }.freeze
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -1,5 +1,6 @@
1
- # coding: utf-8
2
- lib = File.expand_path("../lib", __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path("lib", __dir__)
3
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
5
  require "rails/auth/version"
5
6
 
@@ -25,10 +26,10 @@ Gem::Specification.new do |spec|
25
26
  spec.bindir = "exe"
26
27
  spec.require_paths = ["lib"]
27
28
 
28
- spec.required_ruby_version = ">= 2.0.0"
29
+ spec.required_ruby_version = ">= 2.3.0"
29
30
 
30
31
  spec.add_runtime_dependency "rack"
31
32
 
32
- spec.add_development_dependency "bundler", "~> 1.10"
33
+ spec.add_development_dependency "bundler", ">= 1.10", "< 3"
33
34
  spec.add_development_dependency "rake", "~> 10.0"
34
35
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  RSpec.describe Rails::Auth::ACL::Matchers::AllowAll do
2
4
  let(:matcher) { described_class.new(enabled) }
3
5
  let(:example_env) { env_for(:get, "/") }
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "logger"
2
4
 
3
5
  RSpec.describe Rails::Auth::ACL::Middleware do
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  RSpec.describe Rails::Auth::ACL::Resource do
2
4
  let(:example_method) { "GET" }
3
5
  let(:another_method) { "POST" }
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  RSpec.describe Rails::Auth::ACL do
2
4
  let(:example_config) { fixture_path("example_acl.yml").read }
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  RSpec.describe Rails::Auth::ControllerMethods do
2
4
  let(:controller_class) do
3
5
  Class.new do
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  RSpec.describe Rails::Auth::Credentials::InjectorMiddleware do
2
4
  let(:request) { Rack::MockRequest.env_for("https://www.example.com") }
3
5
  let(:app) { ->(env) { [200, env, "Hello, world!"] } }
@@ -8,4 +10,17 @@ RSpec.describe Rails::Auth::Credentials::InjectorMiddleware do
8
10
  _response, env = middleware.call(request)
9
11
  expect(env[Rails::Auth::Env::CREDENTIALS_ENV_KEY]).to eq credentials
10
12
  end
13
+
14
+ context "with a proc for credentials" do
15
+ let(:credentials_proc) { instance_double(Proc) }
16
+ let(:middleware) { described_class.new(app, credentials_proc) }
17
+
18
+ it "overrides rails-auth credentials in the rack environment" do
19
+ expect(credentials_proc).to receive(:call).with(request).and_return(credentials)
20
+
21
+ _response, env = middleware.call(request)
22
+
23
+ expect(env[Rails::Auth::Env::CREDENTIALS_ENV_KEY]).to eq credentials
24
+ end
25
+ end
11
26
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  RSpec.describe Rails::Auth::Credentials do
2
4
  let(:rack_env) { Rack::MockRequest.env_for("https://www.example.com") }
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  RSpec.describe Rails::Auth::Env do
2
4
  let(:rack_env) { Rack::MockRequest.env_for("https://www.example.com") }
3
5
  let(:example_authority) { "some-authority" }
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  RSpec.describe Rails::Auth::ErrorPage::DebugMiddleware do
2
4
  let(:request) { Rack::MockRequest.env_for("https://www.example.com") }
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  RSpec.describe Rails::Auth::ErrorPage::Middleware do
2
4
  let(:request) { Rack::MockRequest.env_for("https://www.example.com") }
3
5
  let(:error_page) { "<h1> Unauthorized!!! </h1>" }
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  RSpec.describe Rails::Auth::Monitor::Middleware do
2
4
  let(:request) { Rack::MockRequest.env_for("https://www.example.com") }
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "ostruct"
2
4
 
3
5
  RSpec.describe Rails::Auth::RSpec::HelperMethods, acl_spec: true do
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  RSpec.describe "RSpec ACL matchers", acl_spec: true do
2
4
  let(:example_certificate) { x509_certificate_hash(ou: "ponycopter") }
3
5
  let(:another_certificate) { x509_certificate_hash(ou: "derpderp") }
@@ -1,38 +1,121 @@
1
+ # frozen_string_literal: true
2
+
1
3
  RSpec.describe Rails::Auth::X509::Certificate do
2
4
  let(:example_cert) { OpenSSL::X509::Certificate.new(cert_path("valid.crt").read) }
5
+ let(:example_cert_with_extension) { OpenSSL::X509::Certificate.new(cert_path("valid_with_ext.crt").read) }
3
6
  let(:example_certificate) { described_class.new(example_cert) }
7
+ let(:example_certificate_with_extension) { described_class.new(example_cert_with_extension) }
4
8
 
5
9
  let(:example_cn) { "127.0.0.1" }
10
+ let(:example_dns_names) { %w[example.com exemplar.com somethingelse.com] }
11
+ let(:example_ips) { %w[0.0.0.0 127.0.0.1 192.168.1.1] }
6
12
  let(:example_ou) { "ponycopter" }
13
+ let(:example_spiffe) { "spiffe://example.com/exemplar" }
14
+ let(:example_uris) { [example_spiffe, "https://www.example.com/page1", "https://www.example.com/page2"] }
15
+
16
+ describe "without extensions" do
17
+ describe "#[]" do
18
+ it "allows access to subject components via strings" do
19
+ expect(example_certificate["CN"]).to eq example_cn
20
+ expect(example_certificate["OU"]).to eq example_ou
21
+ end
7
22
 
8
- describe "#[]" do
9
- it "allows access to subject components via strings" do
10
- expect(example_certificate["CN"]).to eq example_cn
11
- expect(example_certificate["OU"]).to eq example_ou
23
+ it "allows access to subject components via symbols" do
24
+ expect(example_certificate[:cn]).to eq example_cn
25
+ expect(example_certificate[:ou]).to eq example_ou
26
+ end
12
27
  end
13
28
 
14
- it "allows access to subject components via symbols" do
15
- expect(example_certificate[:cn]).to eq example_cn
16
- expect(example_certificate[:ou]).to eq example_ou
29
+ it "knows its #cn" do
30
+ expect(example_certificate.cn).to eq example_cn
17
31
  end
18
- end
19
32
 
20
- it "knows its #cn" do
21
- expect(example_certificate.cn).to eq example_cn
22
- end
33
+ it "has no #dns_names" do
34
+ expect(example_certificate.dns_names).to be_empty
35
+ end
23
36
 
24
- it "knows its #ou" do
25
- expect(example_certificate.ou).to eq example_ou
26
- end
37
+ it "has no #ips" do
38
+ expect(example_certificate.ips).to be_empty
39
+ end
40
+
41
+ it "knows its #ou" do
42
+ expect(example_certificate.ou).to eq example_ou
43
+ end
27
44
 
28
- it "knows its attributes" do
29
- expect(example_certificate.attributes).to eq(cn: example_cn, ou: example_ou)
45
+ it "has no #uris" do
46
+ expect(example_certificate.uris).to be_empty
47
+ end
48
+
49
+ it "has no #spiffe_id" do
50
+ expect(example_certificate.spiffe_id).to be_nil
51
+ end
52
+
53
+ it "knows its attributes" do
54
+ expect(example_certificate.attributes).to eq(cn: example_cn, ou: example_ou)
55
+ end
56
+
57
+ it "compares certificate objects by comparing their certificates" do
58
+ second_cert = OpenSSL::X509::Certificate.new(cert_path("valid.crt").read)
59
+ second_certificate = described_class.new(second_cert)
60
+
61
+ expect(example_certificate).to be_eql second_certificate
62
+ end
30
63
  end
31
64
 
32
- it "compares certificate objects by comparing their certificates" do
33
- second_cert = OpenSSL::X509::Certificate.new(cert_path("valid.crt").read)
34
- second_certificate = described_class.new(second_cert)
65
+ describe "with extensions" do
66
+ describe "#[]" do
67
+ it "allows access to subject components via strings" do
68
+ expect(example_certificate_with_extension["CN"]).to eq example_cn
69
+ expect(example_certificate_with_extension["OU"]).to eq example_ou
70
+ end
35
71
 
36
- expect(example_certificate).to be_eql second_certificate
72
+ it "allows access to subject components via symbols" do
73
+ expect(example_certificate_with_extension[:cn]).to eq example_cn
74
+ expect(example_certificate_with_extension[:ou]).to eq example_ou
75
+ end
76
+ end
77
+
78
+ it "knows its #cn" do
79
+ expect(example_certificate_with_extension.cn).to eq example_cn
80
+ end
81
+
82
+ it "knows its #dns_names" do
83
+ expect(example_certificate_with_extension.dns_names).to eq example_dns_names
84
+ end
85
+
86
+ it "knows its #ips" do
87
+ expect(example_certificate_with_extension.ips).to eq example_ips
88
+ end
89
+
90
+ it "knows its #ou" do
91
+ expect(example_certificate_with_extension.ou).to eq example_ou
92
+ end
93
+
94
+ it "knows its #spiffe_id" do
95
+ expect(example_certificate_with_extension.spiffe_id).to eq example_spiffe
96
+ end
97
+
98
+ it "knows its #uris" do
99
+ expect(example_certificate_with_extension.uris).to eq example_uris
100
+ end
101
+
102
+ it "knows its attributes" do
103
+ expected_attrs = {
104
+ cn: example_cn,
105
+ dns_names: example_dns_names,
106
+ ips: example_ips,
107
+ ou: example_ou,
108
+ spiffe_id: example_spiffe,
109
+ uris: example_uris
110
+ }
111
+ expect(example_certificate_with_extension.attributes).to eq(expected_attrs)
112
+ end
113
+
114
+ it "compares certificate objects by comparing their certificates" do
115
+ second_cert = OpenSSL::X509::Certificate.new(cert_path("valid_with_ext.crt").read)
116
+ second_certificate = described_class.new(second_cert)
117
+
118
+ expect(example_certificate_with_extension).to be_eql second_certificate
119
+ end
37
120
  end
38
121
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  RSpec.describe Rails::Auth::X509::Matcher do
2
4
  let(:example_cert) { OpenSSL::X509::Certificate.new(cert_path("valid.crt").read) }
3
5
  let(:example_certificate) { Rails::Auth::X509::Certificate.new(example_cert) }
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "logger"
2
4
 
3
5
  RSpec.describe Rails::Auth::X509::Middleware do
@@ -49,10 +51,12 @@ RSpec.describe Rails::Auth::X509::Middleware do
49
51
 
50
52
  let(:java_cert) do
51
53
  ruby_cert = OpenSSL::X509::Certificate.new(valid_cert_pem)
52
- Java::SunSecurityX509::X509CertImpl.new(ruby_cert.to_der.to_java_bytes)
54
+ input_stream = Java::JavaIO::ByteArrayInputStream.new(ruby_cert.to_der.to_java_bytes)
55
+ java_cert_klass = Java::JavaSecurityCert::CertificateFactory.getInstance("X.509")
56
+ java_cert_klass.generateCertificate(input_stream)
53
57
  end
54
58
 
55
- it "extracts Rails::Auth::Credential::X509 from a Java::SunSecurityX509::X509CertImpl" do
59
+ it "extracts Rails::Auth::Credential::X509 from a java.security.cert.Certificate" do
56
60
  skip "JRuby only" unless defined?(JRUBY_VERSION)
57
61
 
58
62
  _response, env = middleware.call(request.merge(example_key => [java_cert]))
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Rails::Auth::X509::SubjectAltNameExtension do
4
+ let(:example_cert) { OpenSSL::X509::Certificate.new(cert_path("valid.crt").read) }
5
+ let(:example_cert_with_extension) { OpenSSL::X509::Certificate.new(cert_path("valid_with_ext.crt").read) }
6
+ let(:extension_for_cert) { described_class.new(example_cert) }
7
+ let(:extension_for_cert_with_san) { described_class.new(example_cert_with_extension) }
8
+ let(:example_dns_names) { %w[example.com exemplar.com somethingelse.com] }
9
+ let(:example_ips) { %w[0.0.0.0 127.0.0.1 192.168.1.1] }
10
+ let(:example_uris) { %w[spiffe://example.com/exemplar https://www.example.com/page1 https://www.example.com/page2] }
11
+
12
+ describe "for cert without extensions" do
13
+ it "returns no DNS names" do
14
+ expect(extension_for_cert.dns_names).to be_empty
15
+ end
16
+
17
+ it "returns no IPs" do
18
+ expect(extension_for_cert.ips).to be_empty
19
+ end
20
+
21
+ it "returns no URIs" do
22
+ expect(extension_for_cert.uris).to be_empty
23
+ end
24
+ end
25
+
26
+ describe "for cert with extensions" do
27
+ it "knows its DNS names" do
28
+ expect(extension_for_cert_with_san.dns_names).to eq example_dns_names
29
+ end
30
+
31
+ it "knows its IPs" do
32
+ expect(extension_for_cert_with_san.ips).to eq example_ips
33
+ end
34
+
35
+ it "knows its URIs" do
36
+ expect(extension_for_cert_with_san.uris).to eq example_uris
37
+ end
38
+ end
39
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  RSpec.describe Rails::Auth do
2
4
  it "has a version number" do
3
5
  expect(Rails::Auth::VERSION).not_to be nil
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "coveralls"
2
4
  Coveralls.wear!
3
5
 
4
- $LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
6
+ $LOAD_PATH.unshift File.expand_path("../lib", __dir__)
5
7
  require "rails/auth"
6
8
  require "rails/auth/rspec"
7
9
  require "support/create_certs"
@@ -11,11 +13,11 @@ require "pathname"
11
13
  RSpec.configure(&:disable_monkey_patching!)
12
14
 
13
15
  def cert_path(name)
14
- Pathname.new(File.expand_path("../../tmp/certs", __FILE__)).join(name)
16
+ Pathname.new(File.expand_path("../tmp/certs", __dir__)).join(name)
15
17
  end
16
18
 
17
19
  def fixture_path(*args)
18
- Pathname.new(File.expand_path("../fixtures", __FILE__)).join(*args)
20
+ Pathname.new(File.expand_path("fixtures", __dir__)).join(*args)
19
21
  end
20
22
 
21
23
  def env_for(method, path, host = "127.0.0.1")
@@ -1,4 +1,6 @@
1
1
  # A strawman matcher for claims-based credentials for use in tests
2
+ # frozen_string_literal: true
3
+
2
4
  class ClaimsMatcher
3
5
  def initialize(options)
4
6
  @options = options
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "certificate_authority"
2
4
  require "fileutils"
3
5
 
4
- cert_path = File.expand_path("../../../tmp/certs", __FILE__)
6
+ cert_path = File.expand_path("../../tmp/certs", __dir__)
5
7
  FileUtils.mkdir_p(cert_path)
6
8
 
7
9
  #
@@ -15,7 +17,7 @@ ca.serial_number.number = 1
15
17
  ca.key_material.generate_key
16
18
  ca.signing_entity = true
17
19
 
18
- ca.sign! "extensions" => { "keyUsage" => { "usage" => %w(critical keyCertSign) } }
20
+ ca.sign! "extensions" => { "keyUsage" => { "usage" => %w[critical keyCertSign] } }
19
21
 
20
22
  ca_cert_path = File.join(cert_path, "ca.crt")
21
23
  ca_key_path = File.join(cert_path, "ca.key")
@@ -41,6 +43,59 @@ valid_key_path = File.join(cert_path, "valid.key")
41
43
  File.write valid_cert_path, valid_cert.to_pem
42
44
  File.write valid_key_path, valid_cert.key_material.private_key.to_pem
43
45
 
46
+ #
47
+ # Valid client certificate with extensions
48
+ #
49
+
50
+ valid_cert_with_ext = CertificateAuthority::Certificate.new
51
+ valid_cert_with_ext.subject.common_name = "127.0.0.1"
52
+ valid_cert_with_ext.subject.organizational_unit = "ponycopter"
53
+ valid_cert_with_ext.serial_number.number = 3
54
+ valid_cert_with_ext.key_material.generate_key
55
+ signing_profile = {
56
+ "extensions" => {
57
+ "basicConstraints" => {
58
+ "ca" => false
59
+ },
60
+ "crlDistributionPoints" => {
61
+ "uri" => "http://notme.com/other.crl"
62
+ },
63
+ "subjectKeyIdentifier" => {},
64
+ "authorityKeyIdentifier" => {},
65
+ "authorityInfoAccess" => {
66
+ "ocsp" => %w[http://youFillThisOut/ocsp/]
67
+ },
68
+ "keyUsage" => {
69
+ "usage" => %w[digitalSignature keyEncipherment dataEncipherment]
70
+ },
71
+ "extendedKeyUsage" => {
72
+ "usage" => %w[serverAuth clientAuth]
73
+ },
74
+ "subjectAltName" => {
75
+ "uris" => %w[spiffe://example.com/exemplar https://www.example.com/page1 https://www.example.com/page2],
76
+ "ips" => %w[0.0.0.0 127.0.0.1 192.168.1.1],
77
+ "dns_names" => %w[example.com exemplar.com somethingelse.com]
78
+ },
79
+ "certificatePolicies" => {
80
+ "policy_identifier" => "1.3.5.8",
81
+ "cps_uris" => %w[http://my.host.name/ http://my.your.name/],
82
+ "user_notice" => {
83
+ "explicit_text" => "Explicit Text Here",
84
+ "organization" => "Organization name",
85
+ "notice_numbers" => "1,2,3,4"
86
+ }
87
+ }
88
+ }
89
+ }
90
+ valid_cert_with_ext.parent = ca
91
+ valid_cert_with_ext.sign!(signing_profile)
92
+
93
+ valid_cert_with_ext_path = File.join(cert_path, "valid_with_ext.crt")
94
+ valid_key_with_ext_path = File.join(cert_path, "valid_with_ext.key")
95
+
96
+ File.write valid_cert_with_ext_path, valid_cert_with_ext.to_pem
97
+ File.write valid_key_with_ext_path, valid_cert_with_ext.key_material.private_key.to_pem
98
+
44
99
  #
45
100
  # Create evil MitM self-signed certificate
46
101
  #
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails-auth
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.4
4
+ version: 2.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tony Arcieri
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-07-17 00:00:00.000000000 Z
11
+ date: 2019-12-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -28,16 +28,22 @@ dependencies:
28
28
  name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
33
  version: '1.10'
34
+ - - "<"
35
+ - !ruby/object:Gem::Version
36
+ version: '3'
34
37
  type: :development
35
38
  prerelease: false
36
39
  version_requirements: !ruby/object:Gem::Requirement
37
40
  requirements:
38
- - - "~>"
41
+ - - ">="
39
42
  - !ruby/object:Gem::Version
40
43
  version: '1.10'
44
+ - - "<"
45
+ - !ruby/object:Gem::Version
46
+ version: '3'
41
47
  - !ruby/object:Gem::Dependency
42
48
  name: rake
43
49
  requirement: !ruby/object:Gem::Requirement
@@ -102,6 +108,7 @@ files:
102
108
  - lib/rails/auth/x509/filter/pem.rb
103
109
  - lib/rails/auth/x509/matcher.rb
104
110
  - lib/rails/auth/x509/middleware.rb
111
+ - lib/rails/auth/x509/subject_alt_name_extension.rb
105
112
  - rails-auth.gemspec
106
113
  - spec/fixtures/example_acl.yml
107
114
  - spec/rails/auth/acl/matchers/allow_all_spec.rb
@@ -120,6 +127,7 @@ files:
120
127
  - spec/rails/auth/x509/certificate_spec.rb
121
128
  - spec/rails/auth/x509/matcher_spec.rb
122
129
  - spec/rails/auth/x509/middleware_spec.rb
130
+ - spec/rails/auth/x509/subject_alt_name_extension_spec.rb
123
131
  - spec/rails/auth_spec.rb
124
132
  - spec/spec_helper.rb
125
133
  - spec/support/claims_matcher.rb
@@ -137,15 +145,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
137
145
  requirements:
138
146
  - - ">="
139
147
  - !ruby/object:Gem::Version
140
- version: 2.0.0
148
+ version: 2.3.0
141
149
  required_rubygems_version: !ruby/object:Gem::Requirement
142
150
  requirements:
143
151
  - - ">="
144
152
  - !ruby/object:Gem::Version
145
153
  version: '0'
146
154
  requirements: []
147
- rubyforge_project:
148
- rubygems_version: 2.4.8
155
+ rubygems_version: 3.0.3
149
156
  signing_key:
150
157
  specification_version: 4
151
158
  summary: Modular resource-oriented authentication and authorization for Rails/Rack