rails-auth 1.3.0 → 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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