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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c22c652d804b973313277d33b6c08dd0f3ae6389dfa0ebeaba73fd84488084b3
4
- data.tar.gz: 95f4c1eab88b2ee3f8f608682690e70c8ca3a4c1b9925b537f14d33c88b91f7c
3
+ metadata.gz: ebb7b8700e3c8719cdb65a37efa8e1234b79a35a43fba6dffcb9d427cbf9e2e7
4
+ data.tar.gz: 46e21ea3735dde1cf16c1242ca1117288bce5a4c4ef5cf0f3669e890fecc5124
5
5
  SHA512:
6
- metadata.gz: a78bb6b55dd36517e9fd325f433210d3367d2aab18541507ac9feb607cdc92c4f2ce4b26df69bdd093c918b98bfc8c37c75add8ce572a0eb07025d96f2bc95f7
7
- data.tar.gz: c5363dfa5f09bef450d827bdfa24226c4e080c47ff8c4b5c81da94192c834ac4d3fa97531d9dec11b5907949d249f59cf9856dd1b727f40d89b1ce2379ce60ac
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
 
@@ -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 << "unauthenticated clients" if credentials.count.zero?
9
+ return message + "unauthenticated clients" if credentials.count.zero?
10
10
 
11
- message << credentials.values.map(&:inspect).join(", ")
11
+ message + credentials.values.map(&:inspect).join(", ")
12
12
  end
13
13
 
14
14
  match { |acl| acl.match(env) }
@@ -3,6 +3,6 @@
3
3
  module Rails
4
4
  # Pluggable authentication and authorization for Rack/Rails
5
5
  module Auth
6
- VERSION = "2.2.0"
6
+ VERSION = "3.1.0"
7
7
  end
8
8
  end
@@ -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
- # Raised when certificate verification is mandatory
7
- CertificateVerifyFailed = Class.new(NotAuthorizedError)
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] app next app in the Rack middleware chain
15
- # @param [Hash] cert_filters maps Rack environment names to cert extractors
16
- # @param [String] ca_file path to the CA bundle to verify client certs with
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: {}, ca_file: nil, truststore: nil, require_cert: false, logger: nil)
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
- if @truststore.verify(cert)
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(:valid_cert_pem) { cert_path("valid.crt").read }
10
- let(:bad_cert_pem) { cert_path("invalid.crt").read }
11
- let(:cert_required) { false }
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
- require_cert: cert_required
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 => valid_cert_pem))
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 => valid_cert_pem.tr("\n", "\t")))
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(valid_cert_pem)
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: 2.2.0
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: 2019-12-05 00:00:00.000000000 Z
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.0.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