rails-auth 0.0.0 → 0.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.
- checksums.yaml +4 -4
- data/.rspec +2 -0
- data/.rubocop.yml +18 -0
- data/.travis.yml +9 -1
- data/CONDUCT.md +5 -0
- data/CONTRIBUTING.md +14 -0
- data/Gemfile +11 -2
- data/Guardfile +5 -0
- data/LICENSE +202 -0
- data/README.md +267 -7
- data/Rakefile +3 -1
- data/lib/rails/auth.rb +3 -6
- data/lib/rails/auth/acl.rb +88 -0
- data/lib/rails/auth/acl/matchers/allow_all.rb +23 -0
- data/lib/rails/auth/acl/middleware.rb +31 -0
- data/lib/rails/auth/acl/resource.rb +74 -0
- data/lib/rails/auth/exceptions.rb +9 -0
- data/lib/rails/auth/principals.rb +36 -0
- data/lib/rails/auth/rack.rb +19 -0
- data/lib/rails/auth/rspec.rb +6 -0
- data/lib/rails/auth/rspec/helper_methods.rb +51 -0
- data/lib/rails/auth/rspec/matchers/acl_matchers.rb +13 -0
- data/lib/rails/auth/version.rb +4 -1
- data/lib/rails/auth/x509/filter/java.rb +25 -0
- data/lib/rails/auth/x509/filter/pem.rb +14 -0
- data/lib/rails/auth/x509/matcher.rb +22 -0
- data/lib/rails/auth/x509/middleware.rb +78 -0
- data/lib/rails/auth/x509/principal.rb +41 -0
- data/rails-auth.gemspec +20 -9
- data/spec/fixtures/example_acl.yml +27 -0
- data/spec/rails/auth/acl/matchers/allow_all_spec.rb +32 -0
- data/spec/rails/auth/acl/middleware_spec.rb +24 -0
- data/spec/rails/auth/acl/resource_spec.rb +105 -0
- data/spec/rails/auth/acl_spec.rb +28 -0
- data/spec/rails/auth/principals_spec.rb +36 -0
- data/spec/rails/auth/rspec/helper_methods_spec.rb +42 -0
- data/spec/rails/auth/rspec/matchers/acl_matchers_spec.rb +20 -0
- data/spec/rails/auth/x509/matcher_spec.rb +21 -0
- data/spec/rails/auth/x509/middleware_spec.rb +74 -0
- data/spec/rails/auth/x509/principal_spec.rb +27 -0
- data/spec/rails/auth_spec.rb +5 -0
- data/spec/spec_helper.rb +23 -0
- data/spec/support/claims_predicate.rb +11 -0
- data/spec/support/create_certs.rb +59 -0
- metadata +60 -24
- data/bin/console +0 -14
- data/bin/setup +0 -8
@@ -0,0 +1,20 @@
|
|
1
|
+
RSpec.describe "RSpec ACL matchers", acl_spec: true do
|
2
|
+
let(:example_principal) { x509_principal_hash(ou: "ponycopter") }
|
3
|
+
let(:another_principal) { x509_principal_hash(ou: "derpderp") }
|
4
|
+
|
5
|
+
subject do
|
6
|
+
Rails::Auth::ACL.from_yaml(
|
7
|
+
fixture_path("example_acl.yml").read,
|
8
|
+
matchers: {
|
9
|
+
allow_x509_subject: Rails::Auth::X509::Matcher,
|
10
|
+
allow_claims: ClaimsPredicate
|
11
|
+
}
|
12
|
+
)
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "/baz/quux" do
|
16
|
+
it { is_expected.to permit get_request(principals: example_principal) }
|
17
|
+
it { is_expected.not_to permit get_request(principals: another_principal) }
|
18
|
+
it { is_expected.not_to permit get_request }
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
RSpec.describe Rails::Auth::X509::Matcher do
|
2
|
+
let(:example_cert) { OpenSSL::X509::Certificate.new(cert_path("valid.crt").read) }
|
3
|
+
let(:example_principal) { Rails::Auth::X509::Principal.new(example_cert) }
|
4
|
+
|
5
|
+
let(:example_ou) { "ponycopter" }
|
6
|
+
let(:another_ou) { "somethingelse" }
|
7
|
+
|
8
|
+
let(:example_env) do
|
9
|
+
{ Rails::Auth::PRINCIPALS_ENV_KEY => { "x509" => example_principal } }
|
10
|
+
end
|
11
|
+
|
12
|
+
it "matches against a valid Rails::Auth::X509::Principal" do
|
13
|
+
predicate = described_class.new(ou: example_ou)
|
14
|
+
expect(predicate.match(example_env)).to eq true
|
15
|
+
end
|
16
|
+
|
17
|
+
it "doesn't match if the subject mismatches" do
|
18
|
+
predicate = described_class.new(ou: another_ou)
|
19
|
+
expect(predicate.match(example_env)).to eq false
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require "logger"
|
2
|
+
|
3
|
+
RSpec.describe Rails::Auth::X509::Middleware do
|
4
|
+
let(:request) { Rack::MockRequest.env_for("https://www.example.com") }
|
5
|
+
let(:app) { ->(env) { [200, env, "Hello, world!"] } }
|
6
|
+
|
7
|
+
let(:valid_cert_pem) { cert_path("valid.crt").read }
|
8
|
+
let(:bad_cert_pem) { cert_path("invalid.crt").read }
|
9
|
+
let(:cert_required) { false }
|
10
|
+
let(:cert_filter) { :pem }
|
11
|
+
let(:example_key) { "X-SSL-Client-Cert" }
|
12
|
+
|
13
|
+
let(:middleware) do
|
14
|
+
described_class.new(
|
15
|
+
app,
|
16
|
+
logger: Logger.new(STDERR),
|
17
|
+
ca_file: cert_path("ca.crt").to_s,
|
18
|
+
cert_filters: { example_key => cert_filter },
|
19
|
+
require_cert: cert_required
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
context "certificate types" do
|
24
|
+
describe "PEM certificates" do
|
25
|
+
it "extracts Rails::Auth::Principal::X509 from a PEM certificate in the Rack environment" do
|
26
|
+
_response, env = middleware.call(request.merge(example_key => valid_cert_pem))
|
27
|
+
|
28
|
+
principal = Rails::Auth.principals(env).fetch("x509")
|
29
|
+
expect(principal).to be_a Rails::Auth::X509::Principal
|
30
|
+
end
|
31
|
+
|
32
|
+
it "ignores unverified certificates" do
|
33
|
+
_response, env = middleware.call(request.merge(example_key => bad_cert_pem))
|
34
|
+
expect(Rails::Auth.principals(env)).to be_empty
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "Java certificates" do
|
39
|
+
let(:example_key) { "javax.servlet.request.X509Certificate" }
|
40
|
+
let(:cert_filter) { :java }
|
41
|
+
|
42
|
+
let(:java_cert) do
|
43
|
+
ruby_cert = OpenSSL::X509::Certificate.new(valid_cert_pem)
|
44
|
+
Java::SunSecurityX509::X509CertImpl.new(ruby_cert.to_der.to_java_bytes)
|
45
|
+
end
|
46
|
+
|
47
|
+
it "extracts Rails::Auth::Principal::X509 from a Java::SunSecurityX509::X509CertImpl" do
|
48
|
+
skip "JRuby only" unless defined?(JRUBY_VERSION)
|
49
|
+
|
50
|
+
_response, env = middleware.call(request.merge(example_key => java_cert))
|
51
|
+
|
52
|
+
principal = Rails::Auth.principals(env).fetch("x509")
|
53
|
+
expect(principal).to be_a Rails::Auth::X509::Principal
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "require_cert: true" do
|
59
|
+
let(:cert_required) { true }
|
60
|
+
|
61
|
+
it "functions normally for valid certificates" do
|
62
|
+
_response, env = middleware.call(request.merge(example_key => valid_cert_pem))
|
63
|
+
|
64
|
+
principal = Rails::Auth.principals(env).fetch("x509")
|
65
|
+
expect(principal).to be_a Rails::Auth::X509::Principal
|
66
|
+
end
|
67
|
+
|
68
|
+
it "raises Rails::Auth::X509::CertificateVerifyFailed for unverified certificates" do
|
69
|
+
expect do
|
70
|
+
middleware.call(request.merge(example_key => bad_cert_pem))
|
71
|
+
end.to raise_error Rails::Auth::X509::CertificateVerifyFailed
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
RSpec.describe Rails::Auth::X509::Principal do
|
2
|
+
let(:example_cert) { OpenSSL::X509::Certificate.new(cert_path("valid.crt").read) }
|
3
|
+
let(:example_principal) { described_class.new(example_cert) }
|
4
|
+
|
5
|
+
let(:example_cn) { "127.0.0.1" }
|
6
|
+
let(:example_ou) { "ponycopter" }
|
7
|
+
|
8
|
+
describe "#[]" do
|
9
|
+
it "allows access to subject components via strings" do
|
10
|
+
expect(example_principal["CN"]).to eq example_cn
|
11
|
+
expect(example_principal["OU"]).to eq example_ou
|
12
|
+
end
|
13
|
+
|
14
|
+
it "allows access to subject components via symbols" do
|
15
|
+
expect(example_principal[:cn]).to eq example_cn
|
16
|
+
expect(example_principal[:ou]).to eq example_ou
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
it "knows its #cn" do
|
21
|
+
expect(example_principal.cn).to eq example_cn
|
22
|
+
end
|
23
|
+
|
24
|
+
it "knows its #ou" do
|
25
|
+
expect(example_principal.ou).to eq example_ou
|
26
|
+
end
|
27
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
$LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
|
2
|
+
require "rails/auth"
|
3
|
+
require "rails/auth/rspec"
|
4
|
+
require "support/create_certs"
|
5
|
+
require "support/claims_predicate"
|
6
|
+
require "pathname"
|
7
|
+
|
8
|
+
RSpec.configure(&:disable_monkey_patching!)
|
9
|
+
|
10
|
+
def cert_path(name)
|
11
|
+
Pathname.new(File.expand_path("../../tmp/certs", __FILE__)).join(name)
|
12
|
+
end
|
13
|
+
|
14
|
+
def fixture_path(*args)
|
15
|
+
Pathname.new(File.expand_path("../fixtures", __FILE__)).join(*args)
|
16
|
+
end
|
17
|
+
|
18
|
+
def env_for(method, path)
|
19
|
+
{
|
20
|
+
"REQUEST_METHOD" => method.to_s.upcase,
|
21
|
+
"REQUEST_PATH" => path
|
22
|
+
}
|
23
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require "certificate_authority"
|
2
|
+
require "fileutils"
|
3
|
+
|
4
|
+
cert_path = File.expand_path("../../../tmp/certs", __FILE__)
|
5
|
+
FileUtils.mkdir_p(cert_path)
|
6
|
+
|
7
|
+
#
|
8
|
+
# Create CA certificate
|
9
|
+
#
|
10
|
+
|
11
|
+
ca = CertificateAuthority::Certificate.new
|
12
|
+
|
13
|
+
ca.subject.common_name = "cacertificate.com"
|
14
|
+
ca.serial_number.number = 1
|
15
|
+
ca.key_material.generate_key
|
16
|
+
ca.signing_entity = true
|
17
|
+
|
18
|
+
ca.sign! "extensions" => { "keyUsage" => { "usage" => %w(critical keyCertSign) } }
|
19
|
+
|
20
|
+
ca_cert_path = File.join(cert_path, "ca.crt")
|
21
|
+
ca_key_path = File.join(cert_path, "ca.key")
|
22
|
+
|
23
|
+
File.write ca_cert_path, ca.to_pem
|
24
|
+
File.write ca_key_path, ca.key_material.private_key.to_pem
|
25
|
+
|
26
|
+
#
|
27
|
+
# Valid client certificate
|
28
|
+
#
|
29
|
+
|
30
|
+
valid_cert = CertificateAuthority::Certificate.new
|
31
|
+
valid_cert.subject.common_name = "127.0.0.1"
|
32
|
+
valid_cert.subject.organizational_unit = "ponycopter"
|
33
|
+
valid_cert.serial_number.number = 2
|
34
|
+
valid_cert.key_material.generate_key
|
35
|
+
valid_cert.parent = ca
|
36
|
+
valid_cert.sign!
|
37
|
+
|
38
|
+
valid_cert_path = File.join(cert_path, "valid.crt")
|
39
|
+
valid_key_path = File.join(cert_path, "valid.key")
|
40
|
+
|
41
|
+
File.write valid_cert_path, valid_cert.to_pem
|
42
|
+
File.write valid_key_path, valid_cert.key_material.private_key.to_pem
|
43
|
+
|
44
|
+
#
|
45
|
+
# Create evil MitM self-signed certificate
|
46
|
+
#
|
47
|
+
|
48
|
+
self_signed_cert = CertificateAuthority::Certificate.new
|
49
|
+
self_signed_cert.subject.common_name = "127.0.0.1"
|
50
|
+
self_signed_cert.subject.organizational_unit = "ponycopter"
|
51
|
+
self_signed_cert.serial_number.number = 2
|
52
|
+
self_signed_cert.key_material.generate_key
|
53
|
+
self_signed_cert.sign!
|
54
|
+
|
55
|
+
self_signed_cert_path = File.join(cert_path, "invalid.crt")
|
56
|
+
self_signed_key_path = File.join(cert_path, "invalid.key")
|
57
|
+
|
58
|
+
File.write self_signed_cert_path, self_signed_cert.to_pem
|
59
|
+
File.write self_signed_key_path, self_signed_cert.key_material.private_key.to_pem
|
metadata
CHANGED
@@ -1,78 +1,115 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rails-auth
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.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-01-
|
11
|
+
date: 2016-01-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: rack
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
20
|
-
type: :
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: bundler
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '10
|
33
|
+
version: '1.10'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '10
|
40
|
+
version: '1.10'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: rake
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
47
|
+
version: '10.0'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
55
|
-
description:
|
54
|
+
version: '10.0'
|
55
|
+
description: A plugin-based framework for supporting multiple authentication and authorization
|
56
|
+
systems in Rails/Rack apps. Supports resource-oriented route-by-route access control
|
57
|
+
lists with TLS authentication.
|
56
58
|
email:
|
57
|
-
-
|
59
|
+
- tonyarcieri@squareup.com
|
58
60
|
executables: []
|
59
61
|
extensions: []
|
60
62
|
extra_rdoc_files: []
|
61
63
|
files:
|
62
64
|
- ".gitignore"
|
63
65
|
- ".rspec"
|
66
|
+
- ".rubocop.yml"
|
64
67
|
- ".travis.yml"
|
68
|
+
- CONDUCT.md
|
69
|
+
- CONTRIBUTING.md
|
65
70
|
- Gemfile
|
71
|
+
- Guardfile
|
72
|
+
- LICENSE
|
66
73
|
- README.md
|
67
74
|
- Rakefile
|
68
|
-
- bin/console
|
69
|
-
- bin/setup
|
70
75
|
- lib/rails/auth.rb
|
76
|
+
- lib/rails/auth/acl.rb
|
77
|
+
- lib/rails/auth/acl/matchers/allow_all.rb
|
78
|
+
- lib/rails/auth/acl/middleware.rb
|
79
|
+
- lib/rails/auth/acl/resource.rb
|
80
|
+
- lib/rails/auth/exceptions.rb
|
81
|
+
- lib/rails/auth/principals.rb
|
82
|
+
- lib/rails/auth/rack.rb
|
83
|
+
- lib/rails/auth/rspec.rb
|
84
|
+
- lib/rails/auth/rspec/helper_methods.rb
|
85
|
+
- lib/rails/auth/rspec/matchers/acl_matchers.rb
|
71
86
|
- lib/rails/auth/version.rb
|
87
|
+
- lib/rails/auth/x509/filter/java.rb
|
88
|
+
- lib/rails/auth/x509/filter/pem.rb
|
89
|
+
- lib/rails/auth/x509/matcher.rb
|
90
|
+
- lib/rails/auth/x509/middleware.rb
|
91
|
+
- lib/rails/auth/x509/principal.rb
|
72
92
|
- rails-auth.gemspec
|
73
|
-
|
74
|
-
|
75
|
-
|
93
|
+
- spec/fixtures/example_acl.yml
|
94
|
+
- spec/rails/auth/acl/matchers/allow_all_spec.rb
|
95
|
+
- spec/rails/auth/acl/middleware_spec.rb
|
96
|
+
- spec/rails/auth/acl/resource_spec.rb
|
97
|
+
- spec/rails/auth/acl_spec.rb
|
98
|
+
- spec/rails/auth/principals_spec.rb
|
99
|
+
- spec/rails/auth/rspec/helper_methods_spec.rb
|
100
|
+
- spec/rails/auth/rspec/matchers/acl_matchers_spec.rb
|
101
|
+
- spec/rails/auth/x509/matcher_spec.rb
|
102
|
+
- spec/rails/auth/x509/middleware_spec.rb
|
103
|
+
- spec/rails/auth/x509/principal_spec.rb
|
104
|
+
- spec/rails/auth_spec.rb
|
105
|
+
- spec/spec_helper.rb
|
106
|
+
- spec/support/claims_predicate.rb
|
107
|
+
- spec/support/create_certs.rb
|
108
|
+
homepage: https://github.com/square/rails-auth/
|
109
|
+
licenses:
|
110
|
+
- Apache-2.0
|
111
|
+
metadata:
|
112
|
+
allowed_push_host: https://rubygems.org
|
76
113
|
post_install_message:
|
77
114
|
rdoc_options: []
|
78
115
|
require_paths:
|
@@ -81,7 +118,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
81
118
|
requirements:
|
82
119
|
- - ">="
|
83
120
|
- !ruby/object:Gem::Version
|
84
|
-
version:
|
121
|
+
version: 2.0.0
|
85
122
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
86
123
|
requirements:
|
87
124
|
- - ">="
|
@@ -92,6 +129,5 @@ rubyforge_project:
|
|
92
129
|
rubygems_version: 2.5.1
|
93
130
|
signing_key:
|
94
131
|
specification_version: 4
|
95
|
-
summary:
|
132
|
+
summary: Modular resource-oriented authentication and authorization for Rails/Rack
|
96
133
|
test_files: []
|
97
|
-
has_rdoc:
|
data/bin/console
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require "bundler/setup"
|
4
|
-
require "rails/auth"
|
5
|
-
|
6
|
-
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
-
# with your gem easier. You can also use a different console, if you like.
|
8
|
-
|
9
|
-
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
-
# require "pry"
|
11
|
-
# Pry.start
|
12
|
-
|
13
|
-
require "irb"
|
14
|
-
IRB.start
|