rails-auth 0.3.0 → 0.4.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
  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: