rails-auth 1.3.0 → 2.0.1

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.
@@ -0,0 +1,28 @@
1
+ module Rails
2
+ module Auth
3
+ module Monitor
4
+ # Fires a user-specified callback which reports on authorization success
5
+ # or failure. Useful for logging or monitoring systems for AuthZ failures
6
+ class Middleware
7
+ def initialize(app, callback)
8
+ raise TypeError, "expected Proc callback, got #{callback.class}" unless callback.is_a?(Proc)
9
+
10
+ @app = app
11
+ @callback = callback
12
+ end
13
+
14
+ def call(env)
15
+ begin
16
+ result = @app.call(env)
17
+ rescue Rails::Auth::NotAuthorizedError
18
+ @callback.call(env, false)
19
+ raise
20
+ end
21
+
22
+ @callback.call(env, true)
23
+ result
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -6,9 +6,9 @@ require "openssl"
6
6
 
7
7
  require "rails/auth/version"
8
8
 
9
+ require "rails/auth/env"
9
10
  require "rails/auth/exceptions"
10
-
11
- require "rails/auth/override"
11
+ require "rails/auth/helpers"
12
12
 
13
13
  require "rails/auth/acl"
14
14
  require "rails/auth/acl/middleware"
@@ -20,6 +20,8 @@ require "rails/auth/credentials/injector_middleware"
20
20
  require "rails/auth/error_page/middleware"
21
21
  require "rails/auth/error_page/debug_middleware"
22
22
 
23
+ require "rails/auth/monitor/middleware"
24
+
23
25
  require "rails/auth/x509/certificate"
24
26
  require "rails/auth/x509/filter/pem"
25
27
  require "rails/auth/x509/filter/java" if defined?(JRUBY_VERSION)
@@ -3,6 +3,24 @@ module Rails
3
3
  module RSpec
4
4
  # RSpec helper methods
5
5
  module HelperMethods
6
+ # Credentials to be injected into the request during tests
7
+ def test_credentials
8
+ Rails.configuration.x.rails_auth.test_credentials
9
+ end
10
+
11
+ # Perform a test with the given credentials
12
+ # NOTE: Credentials will be *cleared* after the block. Nesting is not allowed.
13
+ def with_credentials(credentials = {})
14
+ raise TypeError, "expected Hash of credentials, got #{credentials.class}" unless credentials.is_a?(Hash)
15
+ test_credentials.clear
16
+
17
+ credentials.each do |type, value|
18
+ test_credentials[type.to_s] = value
19
+ end
20
+ ensure
21
+ test_credentials.clear
22
+ end
23
+
6
24
  # Creates an Rails::Auth::X509::Certificate instance double
7
25
  def x509_certificate(cn: nil, ou: nil)
8
26
  subject = ""
@@ -25,7 +43,7 @@ module Rails
25
43
  end
26
44
 
27
45
  Rails::Auth::ACL::Resource::HTTP_METHODS.each do |method|
28
- define_method("#{method.downcase}_request") do |certificates: {}|
46
+ define_method("#{method.downcase}_request") do |credentials: {}|
29
47
  path = self.class.description
30
48
 
31
49
  # Warn if methods are improperly used
@@ -38,7 +56,7 @@ module Rails
38
56
  "PATH_INFO" => self.class.description
39
57
  }
40
58
 
41
- certificates.each do |type, value|
59
+ credentials.each do |type, value|
42
60
  Rails::Auth.add_credential(env, type.to_s, value)
43
61
  end
44
62
 
@@ -3,6 +3,6 @@
3
3
  module Rails
4
4
  # Pluggable authentication and authorization for Rack/Rails
5
5
  module Auth
6
- VERSION = "1.3.0".freeze
6
+ VERSION = "2.0.1".freeze
7
7
  end
8
8
  end
@@ -1,7 +1,7 @@
1
1
  module Rails
2
2
  module Auth
3
3
  module X509
4
- # Predicate matcher for making assertions about X.509 certificates
4
+ # Matcher for making assertions about X.509 certificates
5
5
  class Matcher
6
6
  # @option options [String] cn Common Name of the subject
7
7
  # @option options [String] ou Organizational Unit of the subject
@@ -31,7 +31,8 @@ RSpec.describe Rails::Auth::ACL::Middleware do
31
31
  end
32
32
 
33
33
  def call(env)
34
- Rails::Auth.authorized!(env)
34
+ allowed_by = "example"
35
+ Rails::Auth.authorized!(env, allowed_by)
35
36
  @app.call(env)
36
37
  end
37
38
  end
@@ -4,8 +4,8 @@ RSpec.describe Rails::Auth::ACL::Resource do
4
4
  let(:example_path) { "/foobar" }
5
5
  let(:another_path) { "/baz" }
6
6
 
7
- let(:example_predicates) { { "example" => double(:predicate, match: predicate_matches) } }
8
- let(:example_resource) { described_class.new(example_options, example_predicates) }
7
+ let(:example_matchers) { { "example" => double(:matcher, match: matcher_matches) } }
8
+ let(:example_resource) { described_class.new(example_options, example_matchers) }
9
9
  let(:example_env) { env_for(example_method, example_path) }
10
10
 
11
11
  describe "#initialize" do
@@ -59,37 +59,37 @@ RSpec.describe Rails::Auth::ACL::Resource do
59
59
  end
60
60
 
61
61
  describe "#match" do
62
- context "with matching predicates and method/path" do
63
- let(:predicate_matches) { true }
62
+ context "with matching matchers and method/path" do
63
+ let(:matcher_matches) { true }
64
64
 
65
65
  it "matches against a valid resource" do
66
- expect(example_resource.match(example_env)).to eq true
66
+ expect(example_resource.match(example_env)).to eq "example"
67
67
  end
68
68
  end
69
69
 
70
- context "without matching predicates" do
71
- let(:predicate_matches) { false }
70
+ context "without matching matchers" do
71
+ let(:matcher_matches) { false }
72
72
 
73
73
  it "doesn't match against a valid resource" do
74
- expect(example_resource.match(example_env)).to eq false
74
+ expect(example_resource.match(example_env)).to eq nil
75
75
  end
76
76
  end
77
77
 
78
78
  context "without a method/path match" do
79
- let(:predicate_matches) { true }
79
+ let(:matcher_matches) { true }
80
80
 
81
81
  it "doesn't match" do
82
82
  env = env_for(another_method, example_path)
83
- expect(example_resource.match(env)).to eq false
83
+ expect(example_resource.match(env)).to eq nil
84
84
  end
85
85
  end
86
86
  end
87
87
 
88
88
  describe "#match!" do
89
- let(:predicate_matches) { false }
89
+ let(:matcher_matches) { false }
90
90
 
91
91
  it "matches against all methods if specified" do
92
- resource = described_class.new(example_options.merge("method" => "ALL"), example_predicates)
92
+ resource = described_class.new(example_options.merge("method" => "ALL"), example_matchers)
93
93
  expect(resource.match!(example_env)).to eq true
94
94
  end
95
95
 
@@ -108,7 +108,7 @@ RSpec.describe Rails::Auth::ACL::Resource do
108
108
  context "with a host specified" do
109
109
  let(:example_host) { "www.example.com" }
110
110
  let(:bogus_host) { "www.trololol.com" }
111
- let(:predicate_matches) { true }
111
+ let(:matcher_matches) { true }
112
112
 
113
113
  let(:example_options) do
114
114
  {
@@ -121,24 +121,24 @@ RSpec.describe Rails::Auth::ACL::Resource do
121
121
  describe "#match" do
122
122
  it "matches if the host matches" do
123
123
  example_env["HTTP_HOST"] = example_host
124
- expect(example_resource.match(example_env)).to eq true
124
+ expect(example_resource.match(example_env)).to eq "example"
125
125
  end
126
126
 
127
127
  it "doesn't match if the host mismatches" do
128
128
  example_env["HTTP_HOST"] = bogus_host
129
- expect(example_resource.match(example_env)).to eq false
129
+ expect(example_resource.match(example_env)).to eq nil
130
130
  end
131
131
  end
132
132
 
133
133
  describe "#match!" do
134
134
  it "matches if the host matches" do
135
135
  example_env["HTTP_HOST"] = example_host
136
- expect(example_resource.match(example_env)).to eq true
136
+ expect(example_resource.match(example_env)).to eq "example"
137
137
  end
138
138
 
139
139
  it "doesn't match if the host mismatches" do
140
140
  example_env["HTTP_HOST"] = bogus_host
141
- expect(example_resource.match(example_env)).to eq false
141
+ expect(example_resource.match(example_env)).to eq nil
142
142
  end
143
143
  end
144
144
  end
@@ -19,9 +19,9 @@ RSpec.describe Rails::Auth::ACL do
19
19
 
20
20
  describe "#match" do
21
21
  it "matches routes against the ACL" do
22
- expect(example_acl.match(env_for(:get, "/"))).to eq true
23
- expect(example_acl.match(env_for(:get, "/foo/bar/baz"))).to eq true
24
- expect(example_acl.match(env_for(:get, "/_admin"))).to eq false
22
+ expect(example_acl.match(env_for(:get, "/"))).to eq "allow_all"
23
+ expect(example_acl.match(env_for(:get, "/foo/bar/baz"))).to eq "allow_claims"
24
+ expect(example_acl.match(env_for(:get, "/_admin"))).to eq nil
25
25
  end
26
26
  end
27
27
 
@@ -6,6 +6,6 @@ RSpec.describe Rails::Auth::Credentials::InjectorMiddleware do
6
6
 
7
7
  it "overrides rails-auth credentials in the rack environment" do
8
8
  _response, env = middleware.call(request)
9
- expect(env[Rails::Auth::CREDENTIALS_ENV_KEY]).to eq credentials
9
+ expect(env[Rails::Auth::Env::CREDENTIALS_ENV_KEY]).to eq credentials
10
10
  end
11
11
  end
@@ -1,52 +1,31 @@
1
1
  RSpec.describe Rails::Auth::Credentials do
2
- describe "#credentials" do
3
- let(:example_type) { "example" }
4
- let(:example_credentials) { { example_type => double(:credential) } }
2
+ let(:rack_env) { Rack::MockRequest.env_for("https://www.example.com") }
5
3
 
6
- let(:example_env) do
7
- env_for(:get, "/").tap do |env|
8
- env[Rails::Auth::CREDENTIALS_ENV_KEY] = example_credentials
9
- end
10
- end
4
+ let(:example_cn) { "127.0.0.1" }
5
+ let(:example_ou) { "ponycopter" }
11
6
 
12
- it "extracts credentials from Rack environments" do
13
- expect(Rails::Auth.credentials(example_env)).to eq example_credentials
14
- end
15
- end
7
+ let(:example_credential_type) { "x509" }
8
+ let(:example_credential_value) { instance_double(Rails::Auth::X509::Certificate, cn: example_cn, ou: example_ou) }
16
9
 
17
- describe "#add_credential" do
18
- let(:example_type) { "example" }
19
- let(:example_credential) { double(:credential) }
20
- let(:example_env) { env_for(:get, "/") }
10
+ subject(:credentials) { described_class.new(example_credential_type => example_credential_value) }
21
11
 
22
- it "adds credentials to a Rack environment" do
23
- expect(Rails::Auth.credentials(example_env)[example_type]).to be_nil
24
- Rails::Auth.add_credential(example_env, example_type, example_credential)
25
- expect(Rails::Auth.credentials(example_env)[example_type]).to eq example_credential
12
+ describe ".from_rack_env" do
13
+ it "initializes from a Rack environment" do
14
+ expect(described_class.from_rack_env(rack_env)).to be_a described_class
26
15
  end
16
+ end
27
17
 
28
- context "when called twice for the same credential type" do
29
- let(:second_credential) { double(:credential2) }
30
-
31
- it "succeeds if the credentials are the same" do
32
- allow(example_credential).to receive(:==).and_return(true)
33
-
34
- Rails::Auth.add_credential(example_env, example_type, example_credential)
35
-
36
- expect do
37
- Rails::Auth.add_credential(example_env, example_type, second_credential)
38
- end.to_not raise_error
39
- end
40
-
41
- it "raises ArgumentError if the credentials are different" do
42
- allow(example_credential).to receive(:==).and_return(false)
43
-
44
- Rails::Auth.add_credential(example_env, example_type, example_credential)
18
+ describe "[]" do
19
+ it "allows hash-like access to credentials" do
20
+ expect(credentials[example_credential_type]).not_to be_blank
21
+ end
22
+ end
45
23
 
46
- expect do
47
- Rails::Auth.add_credential(example_env, example_type, second_credential)
48
- end.to raise_error(ArgumentError)
49
- end
24
+ describe "[]=" do
25
+ it "raises AlreadyAuthorizedError if credential has already been set" do
26
+ expect do
27
+ credentials[example_credential_type] = example_credential_value
28
+ end.to raise_error(Rails::Auth::AlreadyAuthorizedError)
50
29
  end
51
30
  end
52
31
  end
@@ -0,0 +1,31 @@
1
+ RSpec.describe Rails::Auth::Env do
2
+ let(:rack_env) { Rack::MockRequest.env_for("https://www.example.com") }
3
+ let(:example_authority) { "some-authority" }
4
+
5
+ subject(:example_env) { described_class.new(rack_env) }
6
+
7
+ it "stores authorization state in the Rack environment" do
8
+ expect(example_env).not_to be_authorized
9
+ expect(example_env.to_rack.key?(described_class::AUTHORIZED_ENV_KEY)).to eq false
10
+ expect(example_env.to_rack.key?(described_class::ALLOWED_BY_ENV_KEY)).to eq false
11
+
12
+ example_env.authorize(example_authority)
13
+ expect(example_env).to be_authorized
14
+ expect(example_env.to_rack[described_class::AUTHORIZED_ENV_KEY]).to eq true
15
+ expect(example_env.to_rack[described_class::ALLOWED_BY_ENV_KEY]).to eq example_authority
16
+ end
17
+
18
+ it "stores authorizers in the Rack environment" do
19
+ expect(example_env.allowed_by).to be_nil
20
+ expect(example_env.to_rack.key?(described_class::ALLOWED_BY_ENV_KEY)).to eq false
21
+
22
+ example_env.allowed_by = example_authority
23
+ expect(example_env.allowed_by).to eq example_authority
24
+ expect(example_env.to_rack[described_class::ALLOWED_BY_ENV_KEY]).to eq example_authority
25
+ end
26
+
27
+ # TODO: this could probably be a bit more extensive
28
+ it "stores credentials in the Rack enviroment" do
29
+ expect(example_env.credentials).to be_a Rails::Auth::Credentials
30
+ end
31
+ end
@@ -0,0 +1,39 @@
1
+ RSpec.describe Rails::Auth::Monitor::Middleware do
2
+ let(:request) { Rack::MockRequest.env_for("https://www.example.com") }
3
+
4
+ describe "access granted" do
5
+ let(:code) { 200 }
6
+ let(:app) { ->(env) { [code, env, "Hello, world!"] } }
7
+
8
+ it "fires the callback with the env and true" do
9
+ callback_fired = false
10
+
11
+ middleware = described_class.new(app, lambda do |env, success|
12
+ callback_fired = true
13
+ expect(env).to be_a Hash
14
+ expect(success).to eq true
15
+ end)
16
+
17
+ response = middleware.call(request)
18
+ expect(callback_fired).to eq true
19
+ expect(response.first).to eq code
20
+ end
21
+ end
22
+
23
+ describe "access denied" do
24
+ let(:app) { ->(_env) { raise(Rails::Auth::NotAuthorizedError, "not authorized!") } }
25
+
26
+ it "renders the error page" do
27
+ callback_fired = false
28
+
29
+ middleware = described_class.new(app, lambda do |env, success|
30
+ callback_fired = true
31
+ expect(env).to be_a Hash
32
+ expect(success).to eq false
33
+ end)
34
+
35
+ expect { middleware.call(request) }.to raise_error(Rails::Auth::NotAuthorizedError)
36
+ expect(callback_fired).to eq true
37
+ end
38
+ end
39
+ end
@@ -1,7 +1,33 @@
1
+ require "ostruct"
2
+
1
3
  RSpec.describe Rails::Auth::RSpec::HelperMethods, acl_spec: true do
2
4
  let(:example_cn) { "127.0.0.1" }
3
5
  let(:example_ou) { "ponycopter" }
4
6
 
7
+ before do
8
+ credentials = {}
9
+ rails_auth = double("config", test_credentials: credentials)
10
+ x_config = double("config", rails_auth: rails_auth)
11
+ configuration = double("config", x: x_config)
12
+
13
+ allow(Rails).to receive(:configuration).and_return(configuration)
14
+ end
15
+
16
+ describe "#with_credentials" do
17
+ let(:example_credential_type) { :x509 }
18
+ let(:example_credential_value) { x509_certificate(cn: example_cn, ou: example_ou) }
19
+
20
+ it "sets credentials in the Rails config" do
21
+ expect(test_credentials[example_credential_type]).to be_nil
22
+
23
+ with_credentials(example_credential_type => example_credential_value) do
24
+ expect(test_credentials[example_credential_type]).to be example_credential_value
25
+ end
26
+
27
+ expect(test_credentials[example_credential_type]).to be_nil
28
+ end
29
+ end
30
+
5
31
  describe "#x509_certificate" do
6
32
  subject { x509_certificate(cn: example_cn, ou: example_ou) }
7
33
 
@@ -13,8 +13,8 @@ RSpec.describe "RSpec ACL matchers", acl_spec: true do
13
13
  end
14
14
 
15
15
  describe "/baz/quux" do
16
- it { is_expected.to permit get_request(certificates: example_certificate) }
17
- it { is_expected.not_to permit get_request(certificates: another_certificate) }
16
+ it { is_expected.to permit get_request(credentials: example_certificate) }
17
+ it { is_expected.not_to permit get_request(credentials: another_certificate) }
18
18
  it { is_expected.not_to permit get_request }
19
19
  end
20
20
  end
@@ -6,7 +6,7 @@ RSpec.describe Rails::Auth::X509::Matcher do
6
6
  let(:another_ou) { "somethingelse" }
7
7
 
8
8
  let(:example_env) do
9
- { Rails::Auth::CREDENTIALS_ENV_KEY => { "x509" => example_certificate } }
9
+ { Rails::Auth::Env::CREDENTIALS_ENV_KEY => { "x509" => example_certificate } }
10
10
  end
11
11
 
12
12
  describe "#match" do
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: 1.3.0
4
+ version: 2.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tony Arcieri
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-07-16 00:00:00.000000000 Z
11
+ date: 2016-07-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -80,14 +80,17 @@ files:
80
80
  - lib/rails/auth/acl/matchers/allow_all.rb
81
81
  - lib/rails/auth/acl/middleware.rb
82
82
  - lib/rails/auth/acl/resource.rb
83
+ - lib/rails/auth/config_builder.rb
83
84
  - lib/rails/auth/controller_methods.rb
84
85
  - lib/rails/auth/credentials.rb
85
86
  - lib/rails/auth/credentials/injector_middleware.rb
87
+ - lib/rails/auth/env.rb
86
88
  - lib/rails/auth/error_page/debug_middleware.rb
87
89
  - lib/rails/auth/error_page/debug_page.html.erb
88
90
  - lib/rails/auth/error_page/middleware.rb
89
91
  - lib/rails/auth/exceptions.rb
90
- - lib/rails/auth/override.rb
92
+ - lib/rails/auth/helpers.rb
93
+ - lib/rails/auth/monitor/middleware.rb
91
94
  - lib/rails/auth/rack.rb
92
95
  - lib/rails/auth/rspec.rb
93
96
  - lib/rails/auth/rspec/helper_methods.rb
@@ -107,8 +110,10 @@ files:
107
110
  - spec/rails/auth/controller_methods_spec.rb
108
111
  - spec/rails/auth/credentials/injector_middleware_spec.rb
109
112
  - spec/rails/auth/credentials_spec.rb
113
+ - spec/rails/auth/env_spec.rb
110
114
  - spec/rails/auth/error_page/debug_middleware_spec.rb
111
115
  - spec/rails/auth/error_page/middleware_spec.rb
116
+ - spec/rails/auth/monitor/middleware_spec.rb
112
117
  - spec/rails/auth/rspec/helper_methods_spec.rb
113
118
  - spec/rails/auth/rspec/matchers/acl_matchers_spec.rb
114
119
  - spec/rails/auth/x509/certificate_spec.rb