From a5265baef7de0669e0ec819e8b42998bed44c25a Mon Sep 17 00:00:00 2001 From: Peter Cai <222655+pcai@users.noreply.github.com> Date: Mon, 14 Sep 2026 20:23:50 -0400 Subject: [PATCH] Allow providing a custom certificate store Add an opt-in :cert_store option to EWS::Connection so callers can pass a fully-built OpenSSL::X509::Store (e.g. one with system default paths) that is assigned directly to the HTTPClient ssl config, skipping the :trust_ca handling. Useful in environments where the default store fails to verify an otherwise valid chain. Add specs covering the passthrough. --- lib/ews/connection.rb | 9 ++++++++- spec/ews/connection_spec.rb | 31 +++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 spec/ews/connection_spec.rb diff --git a/lib/ews/connection.rb b/lib/ews/connection.rb index ad526a99..c1bc9c07 100644 --- a/lib/ews/connection.rb +++ b/lib/ews/connection.rb @@ -36,6 +36,7 @@ class Connection # seconds # @option opts [Fixnum] :connect_timeout override the default connect timeout # seconds + # @option opts [OpenSSL::X509::Store] :cert_store a custom cert store # @option opts [Array] :trust_ca an array of hashed dir paths or a file # @option opts [String] :user_agent the http user agent to use in all requests def initialize(endpoint, opts = {}) @@ -44,7 +45,13 @@ def initialize(endpoint, opts = {}) httpclient_opts = opts.slice(*SUPPORTED_HTTPCLIENT_OPTS) @httpcli = HTTPClient.new(**httpclient_opts) - if opts[:trust_ca] + if opts[:cert_store].is_a?(OpenSSL::X509::Store) + @log.debug 'Applying custom cert_store provided via opts[:cert_store]' + # Assign the provided store directly + @httpcli.ssl_config.cert_store = opts[:cert_store] + + @log.debug 'Skipping original :trust_ca handling due to provided :cert_store.' + elsif opts[:trust_ca] @httpcli.ssl_config.clear_cert_store opts[:trust_ca].each do |ca| @httpcli.ssl_config.add_trust_ca ca diff --git a/spec/ews/connection_spec.rb b/spec/ews/connection_spec.rb new file mode 100644 index 00000000..48664c6e --- /dev/null +++ b/spec/ews/connection_spec.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Viewpoint::EWS::Connection do + let(:endpoint) { 'https://example.com/ews/Exchange.asmx' } + + def ssl_config_for(opts) + described_class.new(endpoint, opts).instance_variable_get(:@httpcli).ssl_config + end + + describe ':cert_store option' do + it 'assigns a custom OpenSSL::X509::Store to the http client' do + store = OpenSSL::X509::Store.new + store.set_default_paths + expect(ssl_config_for(cert_store: store).cert_store).to be(store) + end + + it 'skips :trust_ca handling when a custom store is given' do + store = OpenSSL::X509::Store.new + # A bogus :trust_ca path would raise if it were processed. + config = ssl_config_for(cert_store: store, trust_ca: ['/definitely/not/a/ca/path']) + expect(config.cert_store).to be(store) + end + + it 'ignores a :cert_store value that is not an OpenSSL::X509::Store' do + config = ssl_config_for(cert_store: 'not-a-store') + expect(config.cert_store).not_to be('not-a-store') + end + end +end