rails-auth 2.1.2 → 2.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +5 -5
  2. data/.rubocop.yml +14 -1
  3. data/.travis.yml +8 -5
  4. data/BUG-BOUNTY.md +3 -3
  5. data/CHANGES.md +45 -2
  6. data/CONTRIBUTING.md +11 -10
  7. data/Gemfile +6 -5
  8. data/Guardfile +2 -0
  9. data/Rakefile +3 -1
  10. data/lib/rails/auth/acl.rb +4 -0
  11. data/lib/rails/auth/acl/matchers/allow_all.rb +3 -0
  12. data/lib/rails/auth/acl/middleware.rb +3 -0
  13. data/lib/rails/auth/acl/resource.rb +7 -5
  14. data/lib/rails/auth/config_builder.rb +5 -1
  15. data/lib/rails/auth/controller_methods.rb +4 -0
  16. data/lib/rails/auth/credentials.rb +3 -1
  17. data/lib/rails/auth/credentials/injector_middleware.rb +6 -2
  18. data/lib/rails/auth/env.rb +4 -3
  19. data/lib/rails/auth/error_page/debug_middleware.rb +1 -1
  20. data/lib/rails/auth/error_page/middleware.rb +3 -0
  21. data/lib/rails/auth/exceptions.rb +2 -0
  22. data/lib/rails/auth/helpers.rb +3 -1
  23. data/lib/rails/auth/installed_constraint.rb +2 -0
  24. data/lib/rails/auth/monitor/middleware.rb +2 -0
  25. data/lib/rails/auth/rack.rb +1 -0
  26. data/lib/rails/auth/rspec.rb +2 -0
  27. data/lib/rails/auth/rspec/helper_methods.rb +6 -5
  28. data/lib/rails/auth/rspec/matchers/acl_matchers.rb +4 -2
  29. data/lib/rails/auth/version.rb +1 -1
  30. data/lib/rails/auth/x509/certificate.rb +35 -5
  31. data/lib/rails/auth/x509/filter/java.rb +4 -12
  32. data/lib/rails/auth/x509/filter/pem.rb +10 -1
  33. data/lib/rails/auth/x509/matcher.rb +2 -0
  34. data/lib/rails/auth/x509/middleware.rb +11 -9
  35. data/lib/rails/auth/x509/subject_alt_name_extension.rb +29 -0
  36. data/rails-auth.gemspec +5 -4
  37. data/spec/rails/auth/acl/matchers/allow_all_spec.rb +2 -0
  38. data/spec/rails/auth/acl/middleware_spec.rb +2 -0
  39. data/spec/rails/auth/acl/resource_spec.rb +2 -0
  40. data/spec/rails/auth/acl_spec.rb +2 -0
  41. data/spec/rails/auth/controller_methods_spec.rb +2 -0
  42. data/spec/rails/auth/credentials/injector_middleware_spec.rb +15 -0
  43. data/spec/rails/auth/credentials_spec.rb +2 -0
  44. data/spec/rails/auth/env_spec.rb +2 -0
  45. data/spec/rails/auth/error_page/debug_middleware_spec.rb +2 -0
  46. data/spec/rails/auth/error_page/middleware_spec.rb +2 -0
  47. data/spec/rails/auth/monitor/middleware_spec.rb +2 -0
  48. data/spec/rails/auth/rspec/helper_methods_spec.rb +2 -0
  49. data/spec/rails/auth/rspec/matchers/acl_matchers_spec.rb +12 -1
  50. data/spec/rails/auth/x509/certificate_spec.rb +103 -20
  51. data/spec/rails/auth/x509/matcher_spec.rb +2 -0
  52. data/spec/rails/auth/x509/middleware_spec.rb +13 -2
  53. data/spec/rails/auth/x509/subject_alt_name_extension_spec.rb +39 -0
  54. data/spec/rails/auth_spec.rb +2 -0
  55. data/spec/spec_helper.rb +5 -3
  56. data/spec/support/claims_matcher.rb +2 -0
  57. data/spec/support/create_certs.rb +57 -2
  58. metadata +14 -7
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rails
2
4
  module Auth
3
5
  # Rails constraint to make sure the ACLs have been installed
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rails
2
4
  module Auth
3
5
  module Monitor
@@ -27,3 +27,4 @@ require "rails/auth/x509/filter/pem"
27
27
  require "rails/auth/x509/filter/java" if defined?(JRUBY_VERSION)
28
28
  require "rails/auth/x509/matcher"
29
29
  require "rails/auth/x509/middleware"
30
+ require "rails/auth/x509/subject_alt_name_extension"
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "rails/auth/rspec/helper_methods"
2
4
  require "rails/auth/rspec/matchers/acl_matchers"
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rails
2
4
  module Auth
3
5
  module RSpec
@@ -12,6 +14,7 @@ module Rails
12
14
  # NOTE: Credentials will be *cleared* after the block. Nesting is not allowed.
13
15
  def with_credentials(credentials = {})
14
16
  raise TypeError, "expected Hash of credentials, got #{credentials.class}" unless credentials.is_a?(Hash)
17
+
15
18
  test_credentials.clear
16
19
 
17
20
  credentials.each do |type, value|
@@ -24,8 +27,8 @@ module Rails
24
27
  # Creates an Rails::Auth::X509::Certificate instance double
25
28
  def x509_certificate(cn: nil, ou: nil)
26
29
  subject = ""
27
- subject << "CN=#{cn}" if cn
28
- subject << "OU=#{ou}" if ou
30
+ subject += "CN=#{cn}" if cn
31
+ subject += "OU=#{ou}" if ou
29
32
 
30
33
  instance_double(Rails::Auth::X509::Certificate, subject, cn: cn, ou: ou).tap do |certificate|
31
34
  allow(certificate).to receive(:[]) do |key|
@@ -47,9 +50,7 @@ module Rails
47
50
  path = self.class.description
48
51
 
49
52
  # Warn if methods are improperly used
50
- unless path.chars[0] == "/"
51
- raise ArgumentError, "expected #{path} to start with '/'"
52
- end
53
+ raise ArgumentError, "expected #{path} to start with '/'" unless path.chars[0] == "/"
53
54
 
54
55
  env = {
55
56
  "REQUEST_METHOD" => method,
@@ -1,12 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  RSpec::Matchers.define(:permit) do |env|
2
4
  description do
3
5
  method = env["REQUEST_METHOD"]
4
6
  credentials = Rails::Auth.credentials(env)
5
7
  message = "allow #{method}s by "
6
8
 
7
- return message << "unauthenticated clients" if credentials.count.zero?
9
+ return message + "unauthenticated clients" if credentials.count.zero?
8
10
 
9
- message << credentials.values.map(&:inspect).join(", ")
11
+ message + credentials.values.map(&:inspect).join(", ")
10
12
  end
11
13
 
12
14
  match { |acl| acl.match(env) }
@@ -3,6 +3,6 @@
3
3
  module Rails
4
4
  # Pluggable authentication and authorization for Rack/Rails
5
5
  module Auth
6
- VERSION = "2.1.2".freeze
6
+ VERSION = "2.2.2"
7
7
  end
8
8
  end
@@ -18,7 +18,8 @@ module Rails
18
18
  @certificate.subject.to_a.each do |name, data, _type|
19
19
  @subject[name.freeze] = data.freeze
20
20
  end
21
-
21
+ @subject_alt_names = SubjectAltNameExtension.new(certificate)
22
+ @subject_alt_names.freeze
22
23
  @subject.freeze
23
24
  end
24
25
 
@@ -27,23 +28,52 @@ module Rails
27
28
  end
28
29
 
29
30
  def cn
30
- @subject["CN".freeze]
31
+ @subject["CN"]
31
32
  end
32
33
  alias common_name cn
33
34
 
35
+ def dns_names
36
+ @subject_alt_names.dns_names
37
+ end
38
+
39
+ def ips
40
+ @subject_alt_names.ips
41
+ end
42
+
34
43
  def ou
35
- @subject["OU".freeze]
44
+ @subject["OU"]
36
45
  end
37
46
  alias organizational_unit ou
38
47
 
48
+ def uris
49
+ @subject_alt_names.uris
50
+ end
51
+
52
+ # According to the SPIFFE standard only one SPIFFE ID can exist in the URI
53
+ # SAN:
54
+ # (https://github.com/spiffe/spiffe/blob/master/standards/X509-SVID.md#2-spiffe-id)
55
+ #
56
+ # @return [String, nil] string containing SPIFFE ID if one is present
57
+ # in the certificate
58
+ def spiffe_id
59
+ uris.detect { |uri| uri.start_with?("spiffe://") }
60
+ end
61
+
39
62
  # Generates inspectable attributes for debugging
40
63
  #
41
64
  # @return [Hash] hash containing parts of the certificate subject (cn, ou)
65
+ # and subject alternative name extension (uris, dns_names) as well
66
+ # as SPIFFE ID (spiffe_id), which is just a convenience since those
67
+ # are already included in the uris
42
68
  def attributes
43
69
  {
44
70
  cn: cn,
45
- ou: ou
46
- }
71
+ dns_names: dns_names,
72
+ ips: ips,
73
+ ou: ou,
74
+ spiffe_id: spiffe_id,
75
+ uris: uris
76
+ }.reject { |_, v| v.nil? || v.empty? }
47
77
  end
48
78
 
49
79
  # Compare ourself to another object by ensuring that it has the same type
@@ -1,23 +1,15 @@
1
- require "java"
2
- require "stringio"
1
+ # frozen_string_literal: true
3
2
 
4
3
  module Rails
5
4
  module Auth
6
5
  module X509
7
6
  module Filter
8
- # Extract OpenSSL::X509::Certificates from Java's sun.security.x509.X509CertImpl
7
+ # Extract OpenSSL::X509::Certificates from java.security.cert.Certificate
9
8
  class Java
10
9
  def call(certs)
11
- return unless certs
12
- OpenSSL::X509::Certificate.new(extract_der(certs[0])).freeze
13
- end
14
-
15
- private
10
+ return if certs.nil? || certs.empty?
16
11
 
17
- def extract_der(cert)
18
- stringio = StringIO.new
19
- cert.derEncode(stringio.to_outputstream)
20
- stringio.string
12
+ OpenSSL::X509::Certificate.new(certs[0].get_encoded).freeze
21
13
  end
22
14
  end
23
15
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rails
2
4
  module Auth
3
5
  module X509
@@ -5,7 +7,14 @@ module Rails
5
7
  # Extract OpenSSL::X509::Certificates from Privacy Enhanced Mail (PEM) certificates
6
8
  class Pem
7
9
  def call(pem)
8
- OpenSSL::X509::Certificate.new(pem.delete("\t")).freeze
10
+ # Normalize the whitespace in the certificate to the exact format
11
+ # certificates are normally formatted in otherwise parsing with fail
12
+ # with a 'nested asn1 error'. split(" ") handles sequential whitespace
13
+ # characters like \t, \n, and space.
14
+ OpenSSL::X509::Certificate.new(pem.split(" ").instance_eval do
15
+ [[self[0], self[1]].join(" "), self[2...-2], [self[-2], self[-1]].join(" ")]
16
+ .flatten.join("\n")
17
+ end).freeze
9
18
  end
10
19
  end
11
20
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rails
2
4
  module Auth
3
5
  module X509
@@ -12,20 +12,21 @@ module Rails
12
12
  # Create a new X.509 Middleware object
13
13
  #
14
14
  # @param [Object] app next app in the Rack middleware chain
15
- # @param [Hash] cert_filters maps Rack environment names to cert extractors
16
15
  # @param [String] ca_file path to the CA bundle to verify client certs with
17
- # @param [OpenSSL::X509::Store] truststore (optional) provide your own truststore (for e.g. CRLs)
16
+ # @param [Hash] cert_filters maps Rack environment names to cert extractors
17
+ # @param [Logger] logger place to log verification successes & failures
18
18
  # @param [Boolean] require_cert causes middleware to raise if certs are unverified
19
+ # @param [OpenSSL::X509::Store] truststore (optional) provide your own truststore (for e.g. CRLs)
19
20
  #
20
21
  # @return [Rails::Auth::X509::Middleware] new X509 middleware instance
21
- def initialize(app, cert_filters: {}, ca_file: nil, truststore: nil, require_cert: false, logger: nil)
22
- raise ArgumentError, "no ca_file given" unless ca_file
22
+ def initialize(app, ca_file: nil, cert_filters: {}, logger: nil, require_cert: false, truststore: nil)
23
+ raise ArgumentError, "no ca_file or truststore given" unless ca_file || truststore
23
24
 
24
25
  @app = app
26
+ @cert_filters = cert_filters
25
27
  @logger = logger
26
- @truststore = truststore || OpenSSL::X509::Store.new.add_file(ca_file)
27
28
  @require_cert = require_cert
28
- @cert_filters = cert_filters
29
+ @truststore = truststore || OpenSSL::X509::Store.new.add_file(ca_file)
29
30
 
30
31
  @cert_filters.each do |key, filter|
31
32
  next unless filter.is_a?(Symbol)
@@ -40,7 +41,7 @@ module Rails
40
41
 
41
42
  def call(env)
42
43
  credential = extract_credential(env)
43
- Rails::Auth.add_credential(env, "x509".freeze, credential.freeze) if credential
44
+ Rails::Auth.add_credential(env, "x509", credential.freeze) if credential
44
45
 
45
46
  @app.call(env)
46
47
  end
@@ -62,6 +63,7 @@ module Rails
62
63
  end
63
64
 
64
65
  raise CertificateVerifyFailed, "no client certificate in request" if @require_cert
66
+
65
67
  nil
66
68
  end
67
69
 
@@ -72,8 +74,8 @@ module Rails
72
74
  end
73
75
 
74
76
  filter.call(raw_cert)
75
- rescue => ex
76
- @logger.debug("rails-auth: Certificate error: #{ex.class}: #{ex.message}") if @logger
77
+ rescue StandardError => e
78
+ @logger.debug("rails-auth: Certificate error: #{e.class}: #{e.message}") if @logger
77
79
  nil
78
80
  end
79
81
 
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rails
4
+ module Auth
5
+ module X509
6
+ # Provides convenience methods for subjectAltName extension of X.509 certificates
7
+ class SubjectAltNameExtension
8
+ attr_reader :dns_names, :ips, :uris
9
+
10
+ DNS_REGEX = /^DNS:/i.freeze
11
+ IP_REGEX = /^IP( Address)?:/i.freeze
12
+ URI_REGEX = /^URI:/i.freeze
13
+
14
+ def initialize(certificate)
15
+ unless certificate.is_a?(OpenSSL::X509::Certificate)
16
+ raise TypeError, "expecting OpenSSL::X509::Certificate, got #{certificate.class}"
17
+ end
18
+
19
+ extension = certificate.extensions.detect { |ext| ext.oid == "subjectAltName" }
20
+ values = (extension&.value&.split(",") || []).map(&:strip)
21
+
22
+ @dns_names = values.grep(DNS_REGEX) { |v| v.sub(DNS_REGEX, "") }.freeze
23
+ @ips = values.grep(IP_REGEX) { |v| v.sub(IP_REGEX, "") }.freeze
24
+ @uris = values.grep(URI_REGEX) { |v| v.sub(URI_REGEX, "") }.freeze
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -1,5 +1,6 @@
1
- # coding: utf-8
2
- lib = File.expand_path("../lib", __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path("lib", __dir__)
3
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
5
  require "rails/auth/version"
5
6
 
@@ -25,10 +26,10 @@ Gem::Specification.new do |spec|
25
26
  spec.bindir = "exe"
26
27
  spec.require_paths = ["lib"]
27
28
 
28
- spec.required_ruby_version = ">= 2.0.0"
29
+ spec.required_ruby_version = ">= 2.3.0"
29
30
 
30
31
  spec.add_runtime_dependency "rack"
31
32
 
32
- spec.add_development_dependency "bundler", "~> 1.10"
33
+ spec.add_development_dependency "bundler", ">= 1.10", "< 3"
33
34
  spec.add_development_dependency "rake", "~> 10.0"
34
35
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  RSpec.describe Rails::Auth::ACL::Matchers::AllowAll do
2
4
  let(:matcher) { described_class.new(enabled) }
3
5
  let(:example_env) { env_for(:get, "/") }
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "logger"
2
4
 
3
5
  RSpec.describe Rails::Auth::ACL::Middleware do
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  RSpec.describe Rails::Auth::ACL::Resource do
2
4
  let(:example_method) { "GET" }
3
5
  let(:another_method) { "POST" }
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  RSpec.describe Rails::Auth::ACL do
2
4
  let(:example_config) { fixture_path("example_acl.yml").read }
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  RSpec.describe Rails::Auth::ControllerMethods do
2
4
  let(:controller_class) do
3
5
  Class.new do
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  RSpec.describe Rails::Auth::Credentials::InjectorMiddleware do
2
4
  let(:request) { Rack::MockRequest.env_for("https://www.example.com") }
3
5
  let(:app) { ->(env) { [200, env, "Hello, world!"] } }
@@ -8,4 +10,17 @@ RSpec.describe Rails::Auth::Credentials::InjectorMiddleware do
8
10
  _response, env = middleware.call(request)
9
11
  expect(env[Rails::Auth::Env::CREDENTIALS_ENV_KEY]).to eq credentials
10
12
  end
13
+
14
+ context "with a proc for credentials" do
15
+ let(:credentials_proc) { instance_double(Proc) }
16
+ let(:middleware) { described_class.new(app, credentials_proc) }
17
+
18
+ it "overrides rails-auth credentials in the rack environment" do
19
+ expect(credentials_proc).to receive(:call).with(request).and_return(credentials)
20
+
21
+ _response, env = middleware.call(request)
22
+
23
+ expect(env[Rails::Auth::Env::CREDENTIALS_ENV_KEY]).to eq credentials
24
+ end
25
+ end
11
26
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  RSpec.describe Rails::Auth::Credentials do
2
4
  let(:rack_env) { Rack::MockRequest.env_for("https://www.example.com") }
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  RSpec.describe Rails::Auth::Env do
2
4
  let(:rack_env) { Rack::MockRequest.env_for("https://www.example.com") }
3
5
  let(:example_authority) { "some-authority" }
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  RSpec.describe Rails::Auth::ErrorPage::DebugMiddleware do
2
4
  let(:request) { Rack::MockRequest.env_for("https://www.example.com") }
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  RSpec.describe Rails::Auth::ErrorPage::Middleware do
2
4
  let(:request) { Rack::MockRequest.env_for("https://www.example.com") }
3
5
  let(:error_page) { "<h1> Unauthorized!!! </h1>" }
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  RSpec.describe Rails::Auth::Monitor::Middleware do
2
4
  let(:request) { Rack::MockRequest.env_for("https://www.example.com") }
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "ostruct"
2
4
 
3
5
  RSpec.describe Rails::Auth::RSpec::HelperMethods, acl_spec: true do
@@ -1,6 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  RSpec.describe "RSpec ACL matchers", acl_spec: true do
2
- let(:example_certificate) { x509_certificate_hash(ou: "ponycopter") }
3
4
  let(:another_certificate) { x509_certificate_hash(ou: "derpderp") }
5
+ let(:example_certificate) { x509_certificate_hash(ou: "ponycopter") }
4
6
 
5
7
  subject do
6
8
  Rails::Auth::ACL.from_yaml(
@@ -16,5 +18,14 @@ RSpec.describe "RSpec ACL matchers", acl_spec: true do
16
18
  it { is_expected.to permit get_request(credentials: example_certificate) }
17
19
  it { is_expected.not_to permit get_request(credentials: another_certificate) }
18
20
  it { is_expected.not_to permit get_request }
21
+
22
+ it "has the correct description" do
23
+ expect(permit(get_request(credentials: example_certificate)).description)
24
+ .to eq('allow GETs by #<InstanceDouble(Rails::Auth::X509::Certificate) "OU=ponycopter">')
25
+ expect(permit(get_request(credentials: another_certificate)).description)
26
+ .to eq('allow GETs by #<InstanceDouble(Rails::Auth::X509::Certificate) "OU=derpderp">')
27
+ expect(permit(get_request).description)
28
+ .to eq("allow GETs by unauthenticated clients")
29
+ end
19
30
  end
20
31
  end
@@ -1,38 +1,121 @@
1
+ # frozen_string_literal: true
2
+
1
3
  RSpec.describe Rails::Auth::X509::Certificate do
2
4
  let(:example_cert) { OpenSSL::X509::Certificate.new(cert_path("valid.crt").read) }
5
+ let(:example_cert_with_extension) { OpenSSL::X509::Certificate.new(cert_path("valid_with_ext.crt").read) }
3
6
  let(:example_certificate) { described_class.new(example_cert) }
7
+ let(:example_certificate_with_extension) { described_class.new(example_cert_with_extension) }
4
8
 
5
9
  let(:example_cn) { "127.0.0.1" }
10
+ let(:example_dns_names) { %w[example.com exemplar.com somethingelse.com] }
11
+ let(:example_ips) { %w[0.0.0.0 127.0.0.1 192.168.1.1] }
6
12
  let(:example_ou) { "ponycopter" }
13
+ let(:example_spiffe) { "spiffe://example.com/exemplar" }
14
+ let(:example_uris) { [example_spiffe, "https://www.example.com/page1", "https://www.example.com/page2"] }
15
+
16
+ describe "without extensions" do
17
+ describe "#[]" do
18
+ it "allows access to subject components via strings" do
19
+ expect(example_certificate["CN"]).to eq example_cn
20
+ expect(example_certificate["OU"]).to eq example_ou
21
+ end
7
22
 
8
- describe "#[]" do
9
- it "allows access to subject components via strings" do
10
- expect(example_certificate["CN"]).to eq example_cn
11
- expect(example_certificate["OU"]).to eq example_ou
23
+ it "allows access to subject components via symbols" do
24
+ expect(example_certificate[:cn]).to eq example_cn
25
+ expect(example_certificate[:ou]).to eq example_ou
26
+ end
12
27
  end
13
28
 
14
- it "allows access to subject components via symbols" do
15
- expect(example_certificate[:cn]).to eq example_cn
16
- expect(example_certificate[:ou]).to eq example_ou
29
+ it "knows its #cn" do
30
+ expect(example_certificate.cn).to eq example_cn
17
31
  end
18
- end
19
32
 
20
- it "knows its #cn" do
21
- expect(example_certificate.cn).to eq example_cn
22
- end
33
+ it "has no #dns_names" do
34
+ expect(example_certificate.dns_names).to be_empty
35
+ end
23
36
 
24
- it "knows its #ou" do
25
- expect(example_certificate.ou).to eq example_ou
26
- end
37
+ it "has no #ips" do
38
+ expect(example_certificate.ips).to be_empty
39
+ end
40
+
41
+ it "knows its #ou" do
42
+ expect(example_certificate.ou).to eq example_ou
43
+ end
27
44
 
28
- it "knows its attributes" do
29
- expect(example_certificate.attributes).to eq(cn: example_cn, ou: example_ou)
45
+ it "has no #uris" do
46
+ expect(example_certificate.uris).to be_empty
47
+ end
48
+
49
+ it "has no #spiffe_id" do
50
+ expect(example_certificate.spiffe_id).to be_nil
51
+ end
52
+
53
+ it "knows its attributes" do
54
+ expect(example_certificate.attributes).to eq(cn: example_cn, ou: example_ou)
55
+ end
56
+
57
+ it "compares certificate objects by comparing their certificates" do
58
+ second_cert = OpenSSL::X509::Certificate.new(cert_path("valid.crt").read)
59
+ second_certificate = described_class.new(second_cert)
60
+
61
+ expect(example_certificate).to be_eql second_certificate
62
+ end
30
63
  end
31
64
 
32
- it "compares certificate objects by comparing their certificates" do
33
- second_cert = OpenSSL::X509::Certificate.new(cert_path("valid.crt").read)
34
- second_certificate = described_class.new(second_cert)
65
+ describe "with extensions" do
66
+ describe "#[]" do
67
+ it "allows access to subject components via strings" do
68
+ expect(example_certificate_with_extension["CN"]).to eq example_cn
69
+ expect(example_certificate_with_extension["OU"]).to eq example_ou
70
+ end
35
71
 
36
- expect(example_certificate).to be_eql second_certificate
72
+ it "allows access to subject components via symbols" do
73
+ expect(example_certificate_with_extension[:cn]).to eq example_cn
74
+ expect(example_certificate_with_extension[:ou]).to eq example_ou
75
+ end
76
+ end
77
+
78
+ it "knows its #cn" do
79
+ expect(example_certificate_with_extension.cn).to eq example_cn
80
+ end
81
+
82
+ it "knows its #dns_names" do
83
+ expect(example_certificate_with_extension.dns_names).to eq example_dns_names
84
+ end
85
+
86
+ it "knows its #ips" do
87
+ expect(example_certificate_with_extension.ips).to eq example_ips
88
+ end
89
+
90
+ it "knows its #ou" do
91
+ expect(example_certificate_with_extension.ou).to eq example_ou
92
+ end
93
+
94
+ it "knows its #spiffe_id" do
95
+ expect(example_certificate_with_extension.spiffe_id).to eq example_spiffe
96
+ end
97
+
98
+ it "knows its #uris" do
99
+ expect(example_certificate_with_extension.uris).to eq example_uris
100
+ end
101
+
102
+ it "knows its attributes" do
103
+ expected_attrs = {
104
+ cn: example_cn,
105
+ dns_names: example_dns_names,
106
+ ips: example_ips,
107
+ ou: example_ou,
108
+ spiffe_id: example_spiffe,
109
+ uris: example_uris
110
+ }
111
+ expect(example_certificate_with_extension.attributes).to eq(expected_attrs)
112
+ end
113
+
114
+ it "compares certificate objects by comparing their certificates" do
115
+ second_cert = OpenSSL::X509::Certificate.new(cert_path("valid_with_ext.crt").read)
116
+ second_certificate = described_class.new(second_cert)
117
+
118
+ expect(example_certificate_with_extension).to be_eql second_certificate
119
+ end
37
120
  end
38
121
  end