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.
- checksums.yaml +5 -5
- data/.rubocop.yml +14 -1
- data/.travis.yml +8 -5
- data/BUG-BOUNTY.md +3 -3
- data/CHANGES.md +21 -2
- data/CONTRIBUTING.md +11 -10
- data/Gemfile +6 -5
- data/Guardfile +2 -0
- data/Rakefile +3 -1
- data/lib/rails/auth/acl.rb +4 -0
- data/lib/rails/auth/acl/matchers/allow_all.rb +3 -0
- data/lib/rails/auth/acl/middleware.rb +3 -0
- data/lib/rails/auth/acl/resource.rb +7 -5
- data/lib/rails/auth/config_builder.rb +4 -0
- data/lib/rails/auth/controller_methods.rb +4 -0
- data/lib/rails/auth/credentials.rb +2 -0
- data/lib/rails/auth/credentials/injector_middleware.rb +6 -2
- data/lib/rails/auth/env.rb +4 -3
- data/lib/rails/auth/error_page/debug_middleware.rb +1 -1
- data/lib/rails/auth/error_page/middleware.rb +3 -0
- data/lib/rails/auth/exceptions.rb +2 -0
- data/lib/rails/auth/helpers.rb +2 -0
- data/lib/rails/auth/installed_constraint.rb +2 -0
- data/lib/rails/auth/monitor/middleware.rb +2 -0
- data/lib/rails/auth/rack.rb +1 -0
- data/lib/rails/auth/rspec.rb +2 -0
- data/lib/rails/auth/rspec/helper_methods.rb +6 -5
- data/lib/rails/auth/rspec/matchers/acl_matchers.rb +2 -0
- data/lib/rails/auth/version.rb +1 -1
- data/lib/rails/auth/x509/certificate.rb +35 -5
- data/lib/rails/auth/x509/filter/java.rb +4 -12
- data/lib/rails/auth/x509/filter/pem.rb +2 -0
- data/lib/rails/auth/x509/matcher.rb +2 -0
- data/lib/rails/auth/x509/middleware.rb +4 -3
- data/lib/rails/auth/x509/subject_alt_name_extension.rb +29 -0
- data/rails-auth.gemspec +5 -4
- data/spec/rails/auth/acl/matchers/allow_all_spec.rb +2 -0
- data/spec/rails/auth/acl/middleware_spec.rb +2 -0
- data/spec/rails/auth/acl/resource_spec.rb +2 -0
- data/spec/rails/auth/acl_spec.rb +2 -0
- data/spec/rails/auth/controller_methods_spec.rb +2 -0
- data/spec/rails/auth/credentials/injector_middleware_spec.rb +15 -0
- data/spec/rails/auth/credentials_spec.rb +2 -0
- data/spec/rails/auth/env_spec.rb +2 -0
- data/spec/rails/auth/error_page/debug_middleware_spec.rb +2 -0
- data/spec/rails/auth/error_page/middleware_spec.rb +2 -0
- data/spec/rails/auth/monitor/middleware_spec.rb +2 -0
- data/spec/rails/auth/rspec/helper_methods_spec.rb +2 -0
- data/spec/rails/auth/rspec/matchers/acl_matchers_spec.rb +2 -0
- data/spec/rails/auth/x509/certificate_spec.rb +103 -20
- data/spec/rails/auth/x509/matcher_spec.rb +2 -0
- data/spec/rails/auth/x509/middleware_spec.rb +6 -2
- data/spec/rails/auth/x509/subject_alt_name_extension_spec.rb +39 -0
- data/spec/rails/auth_spec.rb +2 -0
- data/spec/spec_helper.rb +5 -3
- data/spec/support/claims_matcher.rb +2 -0
- data/spec/support/create_certs.rb +57 -2
- metadata +14 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: c22c652d804b973313277d33b6c08dd0f3ae6389dfa0ebeaba73fd84488084b3
|
|
4
|
+
data.tar.gz: 95f4c1eab88b2ee3f8f608682690e70c8ca3a4c1b9925b537f14d33c88b91f7c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a78bb6b55dd36517e9fd325f433210d3367d2aab18541507ac9feb607cdc92c4f2ce4b26df69bdd093c918b98bfc8c37c75add8ce572a0eb07025d96f2bc95f7
|
|
7
|
+
data.tar.gz: c5363dfa5f09bef450d827bdfa24226c4e080c47ff8c4b5c81da94192c834ac4d3fa97531d9dec11b5907949d249f59cf9856dd1b727f40d89b1ce2379ce60ac
|
data/.rubocop.yml
CHANGED
|
@@ -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
|
-
|
|
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
|
data/.travis.yml
CHANGED
|
@@ -10,12 +10,15 @@ before_install:
|
|
|
10
10
|
bundler_args: --without development
|
|
11
11
|
|
|
12
12
|
rvm:
|
|
13
|
-
- 2.
|
|
14
|
-
- 2.
|
|
15
|
-
- 2.
|
|
16
|
-
- 2.3.0
|
|
13
|
+
- 2.4
|
|
14
|
+
- 2.5
|
|
15
|
+
- 2.6
|
|
17
16
|
matrix:
|
|
18
17
|
include:
|
|
19
|
-
- rvm: jruby
|
|
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
|
data/BUG-BOUNTY.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
|
|
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://
|
|
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
|
data/CONTRIBUTING.md
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
|
|
1
|
+
# Contributing
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
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
|
-
|
|
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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
|
-
[
|
|
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.
|
|
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
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
|
|
10
|
+
task default: %w[spec rubocop]
|
data/lib/rails/auth/acl.rb
CHANGED
|
@@ -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
|
|
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
|
|
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"
|
|
62
|
-
return false unless @path =~ env["PATH_INFO"
|
|
63
|
-
return false unless @host.nil? || @host =~ env["HTTP_HOST"
|
|
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
|
-
|
|
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
|
data/lib/rails/auth/env.rb
CHANGED
|
@@ -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"
|
|
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"
|
|
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"
|
|
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
|
|
|
@@ -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
|
data/lib/rails/auth/helpers.rb
CHANGED
data/lib/rails/auth/rack.rb
CHANGED
data/lib/rails/auth/rspec.rb
CHANGED
|
@@ -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
|
|
28
|
-
subject
|
|
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,
|
data/lib/rails/auth/version.rb
CHANGED
|
@@ -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"
|
|
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"
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
7
|
+
# Extract OpenSSL::X509::Certificates from java.security.cert.Certificate
|
|
9
8
|
class Java
|
|
10
9
|
def call(certs)
|
|
11
|
-
return
|
|
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
|
-
|
|
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
|
|
@@ -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"
|
|
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 =>
|
|
76
|
-
@logger.debug("rails-auth: Certificate error: #{
|
|
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
|
data/rails-auth.gemspec
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
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.
|
|
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", "
|
|
33
|
+
spec.add_development_dependency "bundler", ">= 1.10", "< 3"
|
|
33
34
|
spec.add_development_dependency "rake", "~> 10.0"
|
|
34
35
|
end
|
data/spec/rails/auth/acl_spec.rb
CHANGED
|
@@ -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
|
data/spec/rails/auth/env_spec.rb
CHANGED
|
@@ -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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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 "
|
|
15
|
-
expect(example_certificate
|
|
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
|
-
|
|
21
|
-
|
|
22
|
-
|
|
33
|
+
it "has no #dns_names" do
|
|
34
|
+
expect(example_certificate.dns_names).to be_empty
|
|
35
|
+
end
|
|
23
36
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
29
|
-
|
|
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
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
-
|
|
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
|
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::
|
|
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
|
|
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
|
data/spec/rails/auth_spec.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
|
@@ -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("
|
|
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("
|
|
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("
|
|
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,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("
|
|
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
|
|
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.
|
|
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:
|
|
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.
|
|
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
|
-
|
|
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
|