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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +2 -0
  3. data/.rubocop.yml +18 -0
  4. data/.travis.yml +9 -1
  5. data/CONDUCT.md +5 -0
  6. data/CONTRIBUTING.md +14 -0
  7. data/Gemfile +11 -2
  8. data/Guardfile +5 -0
  9. data/LICENSE +202 -0
  10. data/README.md +267 -7
  11. data/Rakefile +3 -1
  12. data/lib/rails/auth.rb +3 -6
  13. data/lib/rails/auth/acl.rb +88 -0
  14. data/lib/rails/auth/acl/matchers/allow_all.rb +23 -0
  15. data/lib/rails/auth/acl/middleware.rb +31 -0
  16. data/lib/rails/auth/acl/resource.rb +74 -0
  17. data/lib/rails/auth/exceptions.rb +9 -0
  18. data/lib/rails/auth/principals.rb +36 -0
  19. data/lib/rails/auth/rack.rb +19 -0
  20. data/lib/rails/auth/rspec.rb +6 -0
  21. data/lib/rails/auth/rspec/helper_methods.rb +51 -0
  22. data/lib/rails/auth/rspec/matchers/acl_matchers.rb +13 -0
  23. data/lib/rails/auth/version.rb +4 -1
  24. data/lib/rails/auth/x509/filter/java.rb +25 -0
  25. data/lib/rails/auth/x509/filter/pem.rb +14 -0
  26. data/lib/rails/auth/x509/matcher.rb +22 -0
  27. data/lib/rails/auth/x509/middleware.rb +78 -0
  28. data/lib/rails/auth/x509/principal.rb +41 -0
  29. data/rails-auth.gemspec +20 -9
  30. data/spec/fixtures/example_acl.yml +27 -0
  31. data/spec/rails/auth/acl/matchers/allow_all_spec.rb +32 -0
  32. data/spec/rails/auth/acl/middleware_spec.rb +24 -0
  33. data/spec/rails/auth/acl/resource_spec.rb +105 -0
  34. data/spec/rails/auth/acl_spec.rb +28 -0
  35. data/spec/rails/auth/principals_spec.rb +36 -0
  36. data/spec/rails/auth/rspec/helper_methods_spec.rb +42 -0
  37. data/spec/rails/auth/rspec/matchers/acl_matchers_spec.rb +20 -0
  38. data/spec/rails/auth/x509/matcher_spec.rb +21 -0
  39. data/spec/rails/auth/x509/middleware_spec.rb +74 -0
  40. data/spec/rails/auth/x509/principal_spec.rb +27 -0
  41. data/spec/rails/auth_spec.rb +5 -0
  42. data/spec/spec_helper.rb +23 -0
  43. data/spec/support/claims_predicate.rb +11 -0
  44. data/spec/support/create_certs.rb +59 -0
  45. metadata +60 -24
  46. data/bin/console +0 -14
  47. 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
@@ -0,0 +1,5 @@
1
+ RSpec.describe Rails::Auth do
2
+ it "has a version number" do
3
+ expect(Rails::Auth::VERSION).not_to be nil
4
+ end
5
+ end
@@ -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,11 @@
1
+ # An additional predicate class for tests
2
+ class ClaimsPredicate
3
+ def initialize(options)
4
+ @options = options
5
+ end
6
+
7
+ def match(_env)
8
+ # Pretend like our principal is in the "example" group
9
+ @options["group"] == "example"
10
+ end
11
+ 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.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-04 00:00:00.000000000 Z
11
+ date: 2016-01-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: bundler
14
+ name: rack
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '1.11'
20
- type: :development
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: '1.11'
26
+ version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: rake
28
+ name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '10.0'
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.0'
40
+ version: '1.10'
41
41
  - !ruby/object:Gem::Dependency
42
- name: rspec
42
+ name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '3.0'
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: '3.0'
55
- description: An exciting gem awaits this name really soon now!
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
- - bascule@gmail.com
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
- homepage:
74
- licenses: []
75
- metadata: {}
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: '0'
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: Placeholder for an upcoming gem. Stay tuned!
132
+ summary: Modular resource-oriented authentication and authorization for Rails/Rack
96
133
  test_files: []
97
- has_rdoc:
@@ -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
data/bin/setup DELETED
@@ -1,8 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
- IFS=$'\n\t'
4
- set -vx
5
-
6
- bundle install
7
-
8
- # Do any other automated setup that you need to do here