rails-auth 2.2.0 → 3.1.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 +4 -4
- data/.github/workflows/jruby.yml +31 -0
- data/.github/workflows/mri.yml +27 -0
- data/CHANGES.md +30 -0
- data/lib/rails/auth/config_builder.rb +0 -7
- data/lib/rails/auth/rack.rb +1 -0
- data/lib/rails/auth/rspec/matchers/acl_matchers.rb +2 -2
- data/lib/rails/auth/version.rb +1 -1
- data/lib/rails/auth/x509/filter/pem_urlencoded.rb +17 -0
- data/lib/rails/auth/x509/middleware.rb +8 -29
- data/spec/rails/auth/rspec/matchers/acl_matchers_spec.rb +10 -1
- data/spec/rails/auth/x509/middleware_spec.rb +9 -35
- data/spec/support/create_certs.rb +0 -17
- metadata +6 -4
- data/.travis.yml +0 -24
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ebb7b8700e3c8719cdb65a37efa8e1234b79a35a43fba6dffcb9d427cbf9e2e7
|
4
|
+
data.tar.gz: 46e21ea3735dde1cf16c1242ca1117288bce5a4c4ef5cf0f3669e890fecc5124
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fcec7e972d73c52fdcaeef22da67dee25849efaddbf254964b3e73f5179e849ef1bd32e80c1f6520f5502884f27811c0b6bb66278923da67320b38cdfd42464b
|
7
|
+
data.tar.gz: 12a93fbdbfc37c44da69bdbb8b163554dd145364627fb689b5acc530a4ec176f6ac5ee2fa0b502ab594a998ce0fad8664e111aafdd268e0f04571f942d9ce6e1
|
@@ -0,0 +1,31 @@
|
|
1
|
+
name: CI - JRuby
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
branches: [ master ]
|
6
|
+
pull_request:
|
7
|
+
branches: [ master ]
|
8
|
+
|
9
|
+
jobs:
|
10
|
+
test:
|
11
|
+
runs-on: ubuntu-latest
|
12
|
+
strategy:
|
13
|
+
matrix:
|
14
|
+
java-version:
|
15
|
+
- 8
|
16
|
+
- 11
|
17
|
+
|
18
|
+
steps:
|
19
|
+
- uses: actions/checkout@v2
|
20
|
+
- name: Set up Java
|
21
|
+
uses: actions/setup-java@v2
|
22
|
+
with:
|
23
|
+
distribution: temurin
|
24
|
+
java-version: ${{ matrix.java-version }}
|
25
|
+
- name: Set up Ruby
|
26
|
+
uses: ruby/setup-ruby@v1
|
27
|
+
with:
|
28
|
+
bundler-cache: true
|
29
|
+
ruby-version: jruby
|
30
|
+
- name: Run tests
|
31
|
+
run: bundle exec rake
|
@@ -0,0 +1,27 @@
|
|
1
|
+
name: CI - MRI
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
branches: [ master ]
|
6
|
+
pull_request:
|
7
|
+
branches: [ master ]
|
8
|
+
|
9
|
+
jobs:
|
10
|
+
test:
|
11
|
+
runs-on: ubuntu-latest
|
12
|
+
strategy:
|
13
|
+
matrix:
|
14
|
+
ruby-version:
|
15
|
+
- 2.6
|
16
|
+
- 2.7
|
17
|
+
- 3.0
|
18
|
+
|
19
|
+
steps:
|
20
|
+
- uses: actions/checkout@v2
|
21
|
+
- name: Set up Ruby
|
22
|
+
uses: ruby/setup-ruby@v1
|
23
|
+
with:
|
24
|
+
bundler-cache: true
|
25
|
+
ruby-version: ${{ matrix.ruby-version }}
|
26
|
+
- name: Run tests
|
27
|
+
run: bundle exec rake
|
data/CHANGES.md
CHANGED
@@ -1,3 +1,33 @@
|
|
1
|
+
### 3.1.0 (2021-10-26)
|
2
|
+
|
3
|
+
* [#70](https://github.com/square/rails-auth/pull/70)
|
4
|
+
Support URL-encoded PEMs to support new Puma header requirements.
|
5
|
+
([@drcapulet])
|
6
|
+
|
7
|
+
### 3.0.0 (2020-08-10)
|
8
|
+
|
9
|
+
* [#68](https://github.com/square/rails-auth/pull/68)
|
10
|
+
Remove `ca_file` and `require_cert` options to the config builder as we no
|
11
|
+
longer verify the certificate chain.
|
12
|
+
([@drcapulet])
|
13
|
+
|
14
|
+
* [#67](https://github.com/square/rails-auth/pull/67)
|
15
|
+
Remove `ca_file`, `require_cert`, and `truststore` options to X509 middleware
|
16
|
+
as we no longer verify the certificate chain.
|
17
|
+
([@drcapulet])
|
18
|
+
|
19
|
+
### 2.2.2 (2020-07-02)
|
20
|
+
|
21
|
+
* [#65](https://github.com/square/rails-auth/pull/65)
|
22
|
+
Fix error when passing `truststore` instead of `ca_file` to X509 middleware.
|
23
|
+
([@drcapulet])
|
24
|
+
|
25
|
+
### 2.2.1 (2020-01-08)
|
26
|
+
|
27
|
+
* [#63](https://github.com/square/rails-auth/pull/63)
|
28
|
+
Fix `FrozenError` in `permit` matcher description.
|
29
|
+
([@drcapulet])
|
30
|
+
|
1
31
|
### 2.2.0 (2019-12-05)
|
2
32
|
|
3
33
|
* [#55](https://github.com/square/rails-auth/pull/55)
|
@@ -31,22 +31,15 @@ module Rails
|
|
31
31
|
def production(
|
32
32
|
config,
|
33
33
|
cert_filters: nil,
|
34
|
-
require_cert: false,
|
35
|
-
ca_file: nil,
|
36
34
|
error_page: Rails.root.join("public/403.html"),
|
37
35
|
monitor: nil
|
38
36
|
)
|
39
|
-
raise ArgumentError, "no cert_filters given but require_cert is true" if require_cert && !cert_filters
|
40
|
-
raise ArgumentError, "no ca_file given but cert_filters were set" if cert_filters && !ca_file
|
41
|
-
|
42
37
|
error_page_middleware(config, error_page)
|
43
38
|
|
44
39
|
if cert_filters
|
45
40
|
config.middleware.insert_before Rails::Auth::ACL::Middleware,
|
46
41
|
Rails::Auth::X509::Middleware,
|
47
|
-
require_cert: require_cert,
|
48
42
|
cert_filters: cert_filters,
|
49
|
-
ca_file: ca_file,
|
50
43
|
logger: Rails.logger
|
51
44
|
end
|
52
45
|
|
data/lib/rails/auth/rack.rb
CHANGED
@@ -24,6 +24,7 @@ require "rails/auth/monitor/middleware"
|
|
24
24
|
|
25
25
|
require "rails/auth/x509/certificate"
|
26
26
|
require "rails/auth/x509/filter/pem"
|
27
|
+
require "rails/auth/x509/filter/pem_urlencoded"
|
27
28
|
require "rails/auth/x509/filter/java" if defined?(JRUBY_VERSION)
|
28
29
|
require "rails/auth/x509/matcher"
|
29
30
|
require "rails/auth/x509/middleware"
|
@@ -6,9 +6,9 @@ RSpec::Matchers.define(:permit) do |env|
|
|
6
6
|
credentials = Rails::Auth.credentials(env)
|
7
7
|
message = "allow #{method}s by "
|
8
8
|
|
9
|
-
return message
|
9
|
+
return message + "unauthenticated clients" if credentials.count.zero?
|
10
10
|
|
11
|
-
message
|
11
|
+
message + credentials.values.map(&:inspect).join(", ")
|
12
12
|
end
|
13
13
|
|
14
14
|
match { |acl| acl.match(env) }
|
data/lib/rails/auth/version.rb
CHANGED
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rails
|
4
|
+
module Auth
|
5
|
+
module X509
|
6
|
+
module Filter
|
7
|
+
# Extract OpenSSL::X509::Certificates from Privacy Enhanced Mail (PEM) certificates
|
8
|
+
# that are URL encoded ($ssl_client_escaped_cert from Nginx).
|
9
|
+
class PemUrlencoded < Pem
|
10
|
+
def call(encoded_pem)
|
11
|
+
super(URI.decode_www_form_component(encoded_pem))
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -3,29 +3,20 @@
|
|
3
3
|
module Rails
|
4
4
|
module Auth
|
5
5
|
module X509
|
6
|
-
#
|
7
|
-
|
8
|
-
|
9
|
-
# Validates X.509 client certificates and adds credential objects for valid
|
10
|
-
# clients to the rack environment as env["rails-auth.credentials"]["x509"]
|
6
|
+
# Extracts X.509 client certificates and adds credential objects to the
|
7
|
+
# rack environment as env["rails-auth.credentials"]["x509"]
|
11
8
|
class Middleware
|
12
9
|
# Create a new X.509 Middleware object
|
13
10
|
#
|
14
|
-
# @param [Object]
|
15
|
-
# @param [Hash]
|
16
|
-
# @param [
|
17
|
-
# @param [OpenSSL::X509::Store] truststore (optional) provide your own truststore (for e.g. CRLs)
|
18
|
-
# @param [Boolean] require_cert causes middleware to raise if certs are unverified
|
11
|
+
# @param [Object] app next app in the Rack middleware chain
|
12
|
+
# @param [Hash] cert_filters maps Rack environment names to cert extractors
|
13
|
+
# @param [Logger] logger place to log certificate extraction issues
|
19
14
|
#
|
20
15
|
# @return [Rails::Auth::X509::Middleware] new X509 middleware instance
|
21
|
-
def initialize(app, cert_filters: {},
|
22
|
-
raise ArgumentError, "no ca_file given" unless ca_file
|
23
|
-
|
16
|
+
def initialize(app, cert_filters: {}, logger: nil)
|
24
17
|
@app = app
|
25
|
-
@logger = logger
|
26
|
-
@truststore = truststore || OpenSSL::X509::Store.new.add_file(ca_file)
|
27
|
-
@require_cert = require_cert
|
28
18
|
@cert_filters = cert_filters
|
19
|
+
@logger = logger
|
29
20
|
|
30
21
|
@cert_filters.each do |key, filter|
|
31
22
|
next unless filter.is_a?(Symbol)
|
@@ -52,17 +43,9 @@ module Rails
|
|
52
43
|
cert = extract_certificate_with_filter(filter, env[key])
|
53
44
|
next unless cert
|
54
45
|
|
55
|
-
|
56
|
-
log("Verified", cert)
|
57
|
-
return Rails::Auth::X509::Certificate.new(cert)
|
58
|
-
else
|
59
|
-
log("Verify FAILED", cert)
|
60
|
-
raise CertificateVerifyFailed, "verify failed: #{subject(cert)}" if @require_cert
|
61
|
-
end
|
46
|
+
return Rails::Auth::X509::Certificate.new(cert)
|
62
47
|
end
|
63
48
|
|
64
|
-
raise CertificateVerifyFailed, "no client certificate in request" if @require_cert
|
65
|
-
|
66
49
|
nil
|
67
50
|
end
|
68
51
|
|
@@ -78,10 +61,6 @@ module Rails
|
|
78
61
|
nil
|
79
62
|
end
|
80
63
|
|
81
|
-
def log(message, cert)
|
82
|
-
@logger.debug("rails-auth: #{message} (#{subject(cert)})") if @logger
|
83
|
-
end
|
84
|
-
|
85
64
|
def subject(cert)
|
86
65
|
cert.subject.to_a.map { |attr, data| "#{attr}=#{data}" }.join(",")
|
87
66
|
end
|
@@ -1,8 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
RSpec.describe "RSpec ACL matchers", acl_spec: true do
|
4
|
-
let(:example_certificate) { x509_certificate_hash(ou: "ponycopter") }
|
5
4
|
let(:another_certificate) { x509_certificate_hash(ou: "derpderp") }
|
5
|
+
let(:example_certificate) { x509_certificate_hash(ou: "ponycopter") }
|
6
6
|
|
7
7
|
subject do
|
8
8
|
Rails::Auth::ACL.from_yaml(
|
@@ -18,5 +18,14 @@ RSpec.describe "RSpec ACL matchers", acl_spec: true do
|
|
18
18
|
it { is_expected.to permit get_request(credentials: example_certificate) }
|
19
19
|
it { is_expected.not_to permit get_request(credentials: another_certificate) }
|
20
20
|
it { is_expected.not_to permit get_request }
|
21
|
+
|
22
|
+
it "has the correct description" do
|
23
|
+
expect(permit(get_request(credentials: example_certificate)).description)
|
24
|
+
.to eq('allow GETs by #<InstanceDouble(Rails::Auth::X509::Certificate) "OU=ponycopter">')
|
25
|
+
expect(permit(get_request(credentials: another_certificate)).description)
|
26
|
+
.to eq('allow GETs by #<InstanceDouble(Rails::Auth::X509::Certificate) "OU=derpderp">')
|
27
|
+
expect(permit(get_request).description)
|
28
|
+
.to eq("allow GETs by unauthenticated clients")
|
29
|
+
end
|
21
30
|
end
|
22
31
|
end
|
@@ -3,41 +3,32 @@
|
|
3
3
|
require "logger"
|
4
4
|
|
5
5
|
RSpec.describe Rails::Auth::X509::Middleware do
|
6
|
-
let(:request) { Rack::MockRequest.env_for("https://www.example.com") }
|
7
6
|
let(:app) { ->(env) { [200, env, "Hello, world!"] } }
|
7
|
+
let(:request) { Rack::MockRequest.env_for("https://www.example.com") }
|
8
8
|
|
9
|
-
let(:
|
10
|
-
let(:
|
11
|
-
let(:
|
12
|
-
let(:cert_filter) { :pem }
|
13
|
-
let(:example_key) { "X-SSL-Client-Cert" }
|
9
|
+
let(:cert_filter) { :pem }
|
10
|
+
let(:cert_pem) { cert_path("valid.crt").read }
|
11
|
+
let(:example_key) { "X-SSL-Client-Cert" }
|
14
12
|
|
15
13
|
let(:middleware) do
|
16
14
|
described_class.new(
|
17
15
|
app,
|
18
|
-
logger: Logger.new(STDERR),
|
19
|
-
ca_file: cert_path("ca.crt").to_s,
|
20
16
|
cert_filters: { example_key => cert_filter },
|
21
|
-
|
17
|
+
logger: Logger.new(STDERR)
|
22
18
|
)
|
23
19
|
end
|
24
20
|
|
25
21
|
context "certificate types" do
|
26
22
|
describe "PEM certificates" do
|
27
23
|
it "extracts Rails::Auth::X509::Certificate from a PEM certificate in the Rack environment" do
|
28
|
-
_response, env = middleware.call(request.merge(example_key =>
|
24
|
+
_response, env = middleware.call(request.merge(example_key => cert_pem))
|
29
25
|
|
30
26
|
credential = Rails::Auth.credentials(env).fetch("x509")
|
31
27
|
expect(credential).to be_a Rails::Auth::X509::Certificate
|
32
28
|
end
|
33
29
|
|
34
|
-
it "ignores unverified certificates" do
|
35
|
-
_response, env = middleware.call(request.merge(example_key => bad_cert_pem))
|
36
|
-
expect(Rails::Auth.credentials(env)).to be_empty
|
37
|
-
end
|
38
|
-
|
39
30
|
it "normalizes abnormal whitespace" do
|
40
|
-
_response, env = middleware.call(request.merge(example_key =>
|
31
|
+
_response, env = middleware.call(request.merge(example_key => cert_pem.tr("\n", "\t")))
|
41
32
|
|
42
33
|
credential = Rails::Auth.credentials(env).fetch("x509")
|
43
34
|
expect(credential).to be_a Rails::Auth::X509::Certificate
|
@@ -46,11 +37,11 @@ RSpec.describe Rails::Auth::X509::Middleware do
|
|
46
37
|
|
47
38
|
# :nocov:
|
48
39
|
describe "Java certificates" do
|
49
|
-
let(:example_key) { "javax.servlet.request.X509Certificate" }
|
50
40
|
let(:cert_filter) { :java }
|
41
|
+
let(:example_key) { "javax.servlet.request.X509Certificate" }
|
51
42
|
|
52
43
|
let(:java_cert) do
|
53
|
-
ruby_cert = OpenSSL::X509::Certificate.new(
|
44
|
+
ruby_cert = OpenSSL::X509::Certificate.new(cert_pem)
|
54
45
|
input_stream = Java::JavaIO::ByteArrayInputStream.new(ruby_cert.to_der.to_java_bytes)
|
55
46
|
java_cert_klass = Java::JavaSecurityCert::CertificateFactory.getInstance("X.509")
|
56
47
|
java_cert_klass.generateCertificate(input_stream)
|
@@ -67,21 +58,4 @@ RSpec.describe Rails::Auth::X509::Middleware do
|
|
67
58
|
end
|
68
59
|
# :nocov:
|
69
60
|
end
|
70
|
-
|
71
|
-
describe "require_cert: true" do
|
72
|
-
let(:cert_required) { true }
|
73
|
-
|
74
|
-
it "functions normally for valid certificates" do
|
75
|
-
_response, env = middleware.call(request.merge(example_key => valid_cert_pem))
|
76
|
-
|
77
|
-
credential = Rails::Auth.credentials(env).fetch("x509")
|
78
|
-
expect(credential).to be_a Rails::Auth::X509::Certificate
|
79
|
-
end
|
80
|
-
|
81
|
-
it "raises Rails::Auth::X509::CertificateVerifyFailed for unverified certificates" do
|
82
|
-
expect do
|
83
|
-
middleware.call(request.merge(example_key => bad_cert_pem))
|
84
|
-
end.to raise_error Rails::Auth::X509::CertificateVerifyFailed
|
85
|
-
end
|
86
|
-
end
|
87
61
|
end
|
@@ -95,20 +95,3 @@ valid_key_with_ext_path = File.join(cert_path, "valid_with_ext.key")
|
|
95
95
|
|
96
96
|
File.write valid_cert_with_ext_path, valid_cert_with_ext.to_pem
|
97
97
|
File.write valid_key_with_ext_path, valid_cert_with_ext.key_material.private_key.to_pem
|
98
|
-
|
99
|
-
#
|
100
|
-
# Create evil MitM self-signed certificate
|
101
|
-
#
|
102
|
-
|
103
|
-
self_signed_cert = CertificateAuthority::Certificate.new
|
104
|
-
self_signed_cert.subject.common_name = "127.0.0.1"
|
105
|
-
self_signed_cert.subject.organizational_unit = "ponycopter"
|
106
|
-
self_signed_cert.serial_number.number = 2
|
107
|
-
self_signed_cert.key_material.generate_key
|
108
|
-
self_signed_cert.sign!
|
109
|
-
|
110
|
-
self_signed_cert_path = File.join(cert_path, "invalid.crt")
|
111
|
-
self_signed_key_path = File.join(cert_path, "invalid.key")
|
112
|
-
|
113
|
-
File.write self_signed_cert_path, self_signed_cert.to_pem
|
114
|
-
File.write self_signed_key_path, self_signed_cert.key_material.private_key.to_pem
|
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:
|
4
|
+
version: 3.1.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: 2021-10-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -67,10 +67,11 @@ executables: []
|
|
67
67
|
extensions: []
|
68
68
|
extra_rdoc_files: []
|
69
69
|
files:
|
70
|
+
- ".github/workflows/jruby.yml"
|
71
|
+
- ".github/workflows/mri.yml"
|
70
72
|
- ".gitignore"
|
71
73
|
- ".rspec"
|
72
74
|
- ".rubocop.yml"
|
73
|
-
- ".travis.yml"
|
74
75
|
- BUG-BOUNTY.md
|
75
76
|
- CHANGES.md
|
76
77
|
- CONDUCT.md
|
@@ -106,6 +107,7 @@ files:
|
|
106
107
|
- lib/rails/auth/x509/certificate.rb
|
107
108
|
- lib/rails/auth/x509/filter/java.rb
|
108
109
|
- lib/rails/auth/x509/filter/pem.rb
|
110
|
+
- lib/rails/auth/x509/filter/pem_urlencoded.rb
|
109
111
|
- lib/rails/auth/x509/matcher.rb
|
110
112
|
- lib/rails/auth/x509/middleware.rb
|
111
113
|
- lib/rails/auth/x509/subject_alt_name_extension.rb
|
@@ -152,7 +154,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
152
154
|
- !ruby/object:Gem::Version
|
153
155
|
version: '0'
|
154
156
|
requirements: []
|
155
|
-
rubygems_version: 3.
|
157
|
+
rubygems_version: 3.1.6
|
156
158
|
signing_key:
|
157
159
|
specification_version: 4
|
158
160
|
summary: Modular resource-oriented authentication and authorization for Rails/Rack
|
data/.travis.yml
DELETED
@@ -1,24 +0,0 @@
|
|
1
|
-
language: ruby
|
2
|
-
sudo: false
|
3
|
-
branches:
|
4
|
-
only:
|
5
|
-
- master
|
6
|
-
|
7
|
-
before_install:
|
8
|
-
- gem install bundler
|
9
|
-
|
10
|
-
bundler_args: --without development
|
11
|
-
|
12
|
-
rvm:
|
13
|
-
- 2.4
|
14
|
-
- 2.5
|
15
|
-
- 2.6
|
16
|
-
matrix:
|
17
|
-
include:
|
18
|
-
- rvm: jruby
|
19
|
-
jdk: openjdk8
|
20
|
-
env: JRUBY_OPTS="--debug" # for simplecov
|
21
|
-
- rvm: jruby
|
22
|
-
jdk: openjdk11
|
23
|
-
env: JRUBY_OPTS="--debug" # for simplecov
|
24
|
-
fast_finish: true
|