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 +4 -4
- data/.rubocop.yml +4 -1
- data/CHANGES.md +10 -0
- data/README.md +3 -0
- data/lib/rails/auth/acl/matchers/allow_all.rb +7 -0
- data/lib/rails/auth/acl/resource.rb +15 -7
- data/lib/rails/auth/acl.rb +1 -1
- data/lib/rails/auth/version.rb +1 -1
- data/lib/rails/auth/x509/certificate.rb +10 -0
- data/lib/rails/auth/x509/matcher.rb +8 -1
- data/spec/rails/auth/acl/matchers/allow_all_spec.rb +8 -3
- data/spec/rails/auth/acl/resource_spec.rb +73 -33
- data/spec/rails/auth/x509/certificate_spec.rb +4 -0
- data/spec/rails/auth/x509/matcher_spec.rb +13 -6
- metadata +2 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 91890f235f52285df442021821f7cafe571d159d
|
4
|
+
data.tar.gz: 317be3357a00fe49de1e6f6e35e143887c6cd8ab
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5d922c273e1ea6bcc90c9dbd7b4c46a44e4d26f350162ecc48cfd5b9412b98242f5ce51a4191729a68c9d86d83bee0879c4e7c407393b8bff309ee30be0cc7bb
|
7
|
+
data.tar.gz: 50c8da3ad9fada2f4347f42090e685103b851771f58d61d14e3d4939937a062017fdf107c57c4ede0ef4c2f392f5b0522c08f493e516c486e6682a2672939c74
|
data/.rubocop.yml
CHANGED
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
|
@@ -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
|
-
|
29
|
-
|
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
|
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
|
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
|
|
data/lib/rails/auth/acl.rb
CHANGED
@@ -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.
|
64
|
+
@resources.find_all { |resource| resource.match!(env) }
|
65
65
|
end
|
66
66
|
|
67
67
|
private
|
data/lib/rails/auth/version.rb
CHANGED
@@ -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(:
|
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(
|
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(
|
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
|
-
|
61
|
-
|
62
|
-
|
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
|
-
|
65
|
-
|
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
|
-
|
70
|
-
|
70
|
+
context "without matching predicates" do
|
71
|
+
let(:predicate_matches) { false }
|
71
72
|
|
72
|
-
|
73
|
-
|
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
|
-
|
78
|
-
let(:predicate_matches) {
|
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
|
-
|
88
|
-
let(:
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
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
|
-
|
96
|
-
|
97
|
-
|
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
|
-
|
101
|
-
|
102
|
-
|
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
|
@@ -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
|
-
|
13
|
-
|
14
|
-
|
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 "
|
18
|
-
matcher = described_class.new(ou:
|
19
|
-
expect(matcher.
|
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.
|
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-
|
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:
|