rails-auth 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 80fe77f1e603f206962d83d87f214d2f57e1dee9
4
- data.tar.gz: 1c3eb52e07760479ad2254428bce28527b4deab4
3
+ metadata.gz: 91890f235f52285df442021821f7cafe571d159d
4
+ data.tar.gz: 317be3357a00fe49de1e6f6e35e143887c6cd8ab
5
5
  SHA512:
6
- metadata.gz: 18bdd7c51f0aa9844b0f7305f3a4455e93a6e3830722633924d433dd95ec608cd45354195007ca26a7020e9eb433f82ff8630a9bfe0b8b3a7a000f4024cb8bb9
7
- data.tar.gz: 4d3b4b5146d98fbd5335944bbb5bba3a233f886b325430058415e4e5ca6961f273dfb9cd80744e8f0ccd839d4a12c1779b208cb6864f2df819be9fa1a2d511cc
6
+ metadata.gz: 5d922c273e1ea6bcc90c9dbd7b4c46a44e4d26f350162ecc48cfd5b9412b98242f5ce51a4191729a68c9d86d83bee0879c4e7c407393b8bff309ee30be0cc7bb
7
+ data.tar.gz: 50c8da3ad9fada2f4347f42090e685103b851771f58d61d14e3d4939937a062017fdf107c57c4ede0ef4c2f392f5b0522c08f493e516c486e6682a2672939c74
data/.rubocop.yml CHANGED
@@ -15,4 +15,7 @@ Metrics/MethodLength:
15
15
  Max: 25
16
16
 
17
17
  Metrics/AbcSize:
18
- Max: 20
18
+ Max: 25
19
+
20
+ Metrics/CyclomaticComplexity:
21
+ Max: 8
data/CHANGES.md CHANGED
@@ -1,3 +1,13 @@
1
+ ### 0.4.0 (2016-03-14)
2
+
3
+ * [#14](https://github.com/square/rails-auth/pull/14)
4
+ Support for optionally matching hostnames in ACL resources.
5
+ ([@tarcieri])
6
+
7
+ * [#13](https://github.com/square/rails-auth/pull/13)
8
+ Add #attributes method to matchers and X.509 certs.
9
+ ([@tarcieri])
10
+
1
11
  ### 0.3.0 (2016-03-12)
2
12
 
3
13
  * [#12](https://github.com/square/rails-auth/pull/12)
data/README.md CHANGED
@@ -216,6 +216,9 @@ Resources are defined by the following constraints:
216
216
  * **path**: A regular expression to match the path. `\A` and `\z` are added by
217
217
  default to the beginning and end of the regex to ensure the entire path and
218
218
  not a substring is matched.
219
+ * **host** (optional): a regular expression to match the `Host:` header passed
220
+ by the client. Useful if your app services traffic for more than one hostname
221
+ and you'd like to restrict ACLs by host.
219
222
 
220
223
  Once you've defined an ACL, you'll need to create a corresponding ACL object
221
224
  in Ruby and a middleware to authorize requests using that ACL. Add the
@@ -13,6 +13,13 @@ module Rails
13
13
  def match(_env)
14
14
  @enabled
15
15
  end
16
+
17
+ # Generates inspectable attributes for debugging
18
+ #
19
+ # @return [true, false] is the matcher enabled?
20
+ def attributes
21
+ @enabled
22
+ end
16
23
  end
17
24
  end
18
25
  end
@@ -5,13 +5,13 @@ module Rails
5
5
  class ACL
6
6
  # Rules for a particular route
7
7
  class Resource
8
- attr_reader :http_methods, :path, :predicates
8
+ attr_reader :http_methods, :path, :host, :predicates
9
9
 
10
10
  # Valid HTTP methods
11
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(method path).freeze
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)
@@ -25,9 +25,16 @@ module Rails
25
25
  raise ParseError, "unrecognized key in ACL resource: #{extra_keys.first}"
26
26
  end
27
27
 
28
- @http_methods = extract_methods(options["method"])
29
- @path = /\A#{options.fetch("path")}\z/
28
+ methods = options["method"] || raise(ParseError, "no 'method' key in resource: #{options.inspect}")
29
+ path = options["path"] || raise(ParseError, "no 'path' key in resource: #{options.inspect}")
30
+
31
+ @http_methods = extract_methods(methods)
32
+ @path = /\A#{path}\z/
30
33
  @predicates = predicates.freeze
34
+
35
+ # Unlike method and path, host is optional
36
+ host = options["host"]
37
+ @host = /\A#{host}\z/ if host
31
38
  end
32
39
 
33
40
  # Match this resource against the given Rack environment, checking all
@@ -38,20 +45,21 @@ module Rails
38
45
  # @return [Boolean] resource and predicates match the given request
39
46
  #
40
47
  def match(env)
41
- return false unless match_method_and_path(env)
48
+ return false unless match!(env)
42
49
  @predicates.any? { |_name, predicate| predicate.match(env) }
43
50
  end
44
51
 
45
- # Match *only* the request method/path against the given Rack environment.
52
+ # Match *only* the request method/path/host against the given Rack environment.
46
53
  # Predicates are NOT checked.
47
54
  #
48
55
  # @param [Hash] :env Rack environment
49
56
  #
50
57
  # @return [Boolean] method and path *only* match the given environment
51
58
  #
52
- def match_method_and_path(env)
59
+ def match!(env)
53
60
  return false unless @http_methods.nil? || @http_methods.include?(env["REQUEST_METHOD".freeze])
54
61
  return false unless @path =~ env["REQUEST_PATH".freeze]
62
+ return false unless @host.nil? || @host =~ env["HTTP_HOST".freeze]
55
63
  true
56
64
  end
57
65
 
@@ -61,7 +61,7 @@ module Rails
61
61
  # @return [Array<Rails::Auth::ACL::Resource>] matching resources
62
62
  #
63
63
  def matching_resources(env)
64
- @resources.find_all { |resource| resource.match_method_and_path(env) }
64
+ @resources.find_all { |resource| resource.match!(env) }
65
65
  end
66
66
 
67
67
  private
@@ -3,6 +3,6 @@
3
3
  module Rails
4
4
  # Pluggable authentication and authorization for Rack/Rails
5
5
  module Auth
6
- VERSION = "0.3.0".freeze
6
+ VERSION = "0.4.0".freeze
7
7
  end
8
8
  end
@@ -35,6 +35,16 @@ module Rails
35
35
  @subject["OU".freeze]
36
36
  end
37
37
  alias organizational_unit ou
38
+
39
+ # Generates inspectable attributes for debugging
40
+ #
41
+ # @return [Hash] hash containing parts of the certificate subject (cn, ou)
42
+ def attributes
43
+ {
44
+ cn: cn,
45
+ ou: ou
46
+ }
47
+ end
38
48
  end
39
49
  end
40
50
  end
@@ -6,7 +6,7 @@ module Rails
6
6
  # @option options [String] cn Common Name of the subject
7
7
  # @option options [String] ou Organizational Unit of the subject
8
8
  def initialize(options)
9
- @options = options
9
+ @options = options.freeze
10
10
  end
11
11
 
12
12
  # @param [Hash] env Rack environment
@@ -16,6 +16,13 @@ module Rails
16
16
 
17
17
  @options.all? { |name, value| certificate[name] == value }
18
18
  end
19
+
20
+ # Generates inspectable attributes for debugging
21
+ #
22
+ # @return [Hash] hash containing parts of the certificate subject to match (cn, ou)
23
+ def attributes
24
+ @options
25
+ end
19
26
  end
20
27
  end
21
28
  end
@@ -1,5 +1,5 @@
1
1
  RSpec.describe Rails::Auth::ACL::Matchers::AllowAll do
2
- let(:predicate) { described_class.new(enabled) }
2
+ let(:matcher) { described_class.new(enabled) }
3
3
  let(:example_env) { env_for(:get, "/") }
4
4
 
5
5
  describe "#initialize" do
@@ -17,7 +17,7 @@ RSpec.describe Rails::Auth::ACL::Matchers::AllowAll do
17
17
  let(:enabled) { true }
18
18
 
19
19
  it "allows all requests" do
20
- expect(predicate.match(example_env)).to eq true
20
+ expect(matcher.match(example_env)).to eq true
21
21
  end
22
22
  end
23
23
 
@@ -25,8 +25,13 @@ RSpec.describe Rails::Auth::ACL::Matchers::AllowAll do
25
25
  let(:enabled) { false }
26
26
 
27
27
  it "rejects all requests" do
28
- expect(predicate.match(example_env)).to eq false
28
+ expect(matcher.match(example_env)).to eq false
29
29
  end
30
30
  end
31
31
  end
32
+
33
+ it "knows its attributes" do
34
+ matcher = described_class.new(true)
35
+ expect(matcher.attributes).to eq true
36
+ end
32
37
  end
@@ -4,13 +4,6 @@ RSpec.describe Rails::Auth::ACL::Resource do
4
4
  let(:example_path) { "/foobar" }
5
5
  let(:another_path) { "/baz" }
6
6
 
7
- let(:example_options) do
8
- {
9
- "method" => example_method,
10
- "path" => example_path
11
- }
12
- end
13
-
14
7
  let(:example_predicates) { { "example" => double(:predicate, match: predicate_matches) } }
15
8
  let(:example_resource) { described_class.new(example_options, example_predicates) }
16
9
  let(:example_env) { env_for(example_method, example_path) }
@@ -57,49 +50,96 @@ RSpec.describe Rails::Auth::ACL::Resource do
57
50
  end
58
51
  end
59
52
 
60
- describe "#match" do
61
- context "with matching predicates and method/path" do
62
- let(:predicate_matches) { true }
53
+ context "without a host specified" do
54
+ let(:example_options) do
55
+ {
56
+ "method" => example_method,
57
+ "path" => example_path
58
+ }
59
+ end
63
60
 
64
- it "matches against a valid resource" do
65
- expect(example_resource.match(example_env)).to eq true
61
+ describe "#match" do
62
+ context "with matching predicates and method/path" do
63
+ let(:predicate_matches) { true }
64
+
65
+ it "matches against a valid resource" do
66
+ expect(example_resource.match(example_env)).to eq true
67
+ end
66
68
  end
67
- end
68
69
 
69
- context "without matching predicates" do
70
- let(:predicate_matches) { false }
70
+ context "without matching predicates" do
71
+ let(:predicate_matches) { false }
71
72
 
72
- it "doesn't match against a valid resource" do
73
- expect(example_resource.match(example_env)).to eq false
73
+ it "doesn't match against a valid resource" do
74
+ expect(example_resource.match(example_env)).to eq false
75
+ end
76
+ end
77
+
78
+ context "without a method/path match" do
79
+ let(:predicate_matches) { true }
80
+
81
+ it "doesn't match" do
82
+ env = env_for(another_method, example_path)
83
+ expect(example_resource.match(env)).to eq false
84
+ end
74
85
  end
75
86
  end
76
87
 
77
- context "without a method/path match" do
78
- let(:predicate_matches) { true }
88
+ describe "#match!" do
89
+ let(:predicate_matches) { false }
90
+
91
+ it "matches against all methods if specified" do
92
+ resource = described_class.new(example_options.merge("method" => "ALL"), example_predicates)
93
+ expect(resource.match!(example_env)).to eq true
94
+ end
79
95
 
80
- it "doesn't match" do
96
+ it "doesn't match if the method mismatches" do
81
97
  env = env_for(another_method, example_path)
82
- expect(example_resource.match(env)).to eq false
98
+ expect(example_resource.match!(env)).to eq false
99
+ end
100
+
101
+ it "doesn't match if the path mismatches" do
102
+ env = env_for(example_method, another_path)
103
+ expect(example_resource.match!(env)).to eq false
83
104
  end
84
105
  end
85
106
  end
86
107
 
87
- describe "#match_method_and_path" do
88
- let(:predicate_matches) { false }
89
-
90
- it "matches against all methods if specified" do
91
- resource = described_class.new(example_options.merge("method" => "ALL"), example_predicates)
92
- expect(resource.match_method_and_path(example_env)).to eq true
108
+ context "with a host specified" do
109
+ let(:example_host) { "www.example.com" }
110
+ let(:bogus_host) { "www.trololol.com" }
111
+ let(:predicate_matches) { true }
112
+
113
+ let(:example_options) do
114
+ {
115
+ "method" => example_method,
116
+ "path" => example_path,
117
+ "host" => example_host
118
+ }
93
119
  end
94
120
 
95
- it "doesn't match if the method mismatches" do
96
- env = env_for(another_method, example_path)
97
- expect(example_resource.match_method_and_path(env)).to eq false
121
+ describe "#match" do
122
+ it "matches if the host matches" do
123
+ example_env["HTTP_HOST"] = example_host
124
+ expect(example_resource.match(example_env)).to eq true
125
+ end
126
+
127
+ it "doesn't match if the host mismatches" do
128
+ example_env["HTTP_HOST"] = bogus_host
129
+ expect(example_resource.match(example_env)).to eq false
130
+ end
98
131
  end
99
132
 
100
- it "doesn't match if the path mismatches" do
101
- env = env_for(example_method, another_path)
102
- expect(example_resource.match_method_and_path(env)).to eq false
133
+ describe "#match!" do
134
+ it "matches if the host matches" do
135
+ example_env["HTTP_HOST"] = example_host
136
+ expect(example_resource.match(example_env)).to eq true
137
+ end
138
+
139
+ it "doesn't match if the host mismatches" do
140
+ example_env["HTTP_HOST"] = bogus_host
141
+ expect(example_resource.match(example_env)).to eq false
142
+ end
103
143
  end
104
144
  end
105
145
  end
@@ -24,4 +24,8 @@ RSpec.describe Rails::Auth::X509::Certificate do
24
24
  it "knows its #ou" do
25
25
  expect(example_certificate.ou).to eq example_ou
26
26
  end
27
+
28
+ it "knows its attributes" do
29
+ expect(example_certificate.attributes).to eq(cn: example_cn, ou: example_ou)
30
+ end
27
31
  end
@@ -9,13 +9,20 @@ RSpec.describe Rails::Auth::X509::Matcher do
9
9
  { Rails::Auth::CREDENTIALS_ENV_KEY => { "x509" => example_certificate } }
10
10
  end
11
11
 
12
- it "matches against a valid Rails::Auth::X509::Credential" do
13
- matcher = described_class.new(ou: example_ou)
14
- expect(matcher.match(example_env)).to eq true
12
+ describe "#match" do
13
+ it "matches against a valid Rails::Auth::X509::Credential" do
14
+ matcher = described_class.new(ou: example_ou)
15
+ expect(matcher.match(example_env)).to eq true
16
+ end
17
+
18
+ it "doesn't match if the subject mismatches" do
19
+ matcher = described_class.new(ou: another_ou)
20
+ expect(matcher.match(example_env)).to eq false
21
+ end
15
22
  end
16
23
 
17
- it "doesn't match if the subject mismatches" do
18
- matcher = described_class.new(ou: another_ou)
19
- expect(matcher.match(example_env)).to eq false
24
+ it "knows its attributes" do
25
+ matcher = described_class.new(ou: example_ou)
26
+ expect(matcher.attributes).to eq(ou: example_ou)
20
27
  end
21
28
  end
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: 0.3.0
4
+ version: 0.4.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: 2016-03-13 00:00:00.000000000 Z
11
+ date: 2016-03-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -141,4 +141,3 @@ signing_key:
141
141
  specification_version: 4
142
142
  summary: Modular resource-oriented authentication and authorization for Rails/Rack
143
143
  test_files: []
144
- has_rdoc: