From fa5c6b6cb34f235e3b0abc1cc48e5c1348e155d8 Mon Sep 17 00:00:00 2001 From: Fernando Briano Date: Mon, 28 Jun 2021 15:52:36 +0100 Subject: [PATCH 1/8] [GEM] Decouples elasticsearch-transport from elasticsearch --- .../spec/rest_yaml_tests_helper.rb | 3 +- elasticsearch-transport/Gemfile | 12 +- .../elasticsearch-transport.gemspec | 3 +- .../lib/elasticsearch/transport.rb | 48 ++--- .../lib/elasticsearch/transport/client.rb | 1 - .../transport/transport/http/faraday.rb | 10 +- .../spec/elasticsearch/transport/base_spec.rb | 6 +- .../elasticsearch/transport/client_spec.rb | 23 +-- .../transport/meta_header_spec.rb | 1 + elasticsearch-transport/spec/spec_helper.rb | 4 +- .../lib/elasticsearch/xpack.rb | 195 +++++++++--------- elasticsearch-xpack/test/test_helper.rb | 7 - 12 files changed, 139 insertions(+), 174 deletions(-) diff --git a/elasticsearch-api/spec/rest_yaml_tests_helper.rb b/elasticsearch-api/spec/rest_yaml_tests_helper.rb index 229bfe7de7..34953a6ef2 100644 --- a/elasticsearch-api/spec/rest_yaml_tests_helper.rb +++ b/elasticsearch-api/spec/rest_yaml_tests_helper.rb @@ -61,9 +61,8 @@ end end - # Skipped tests -file = File.expand_path(__dir__ + '/skipped_tests.yml') +file = File.expand_path("#{__dir__}/skipped_tests.yml") skipped_tests = YAML.load_file(file) # The directory of rest api YAML files. diff --git a/elasticsearch-transport/Gemfile b/elasticsearch-transport/Gemfile index 544a3565cd..7b1b8bdf9b 100644 --- a/elasticsearch-transport/Gemfile +++ b/elasticsearch-transport/Gemfile @@ -20,16 +20,12 @@ source 'https://rubygems.org' # Specify your gem's dependencies in elasticsearch-transport.gemspec gemspec -if File.exist? File.expand_path('../../elasticsearch-api/elasticsearch-api.gemspec', __FILE__) - gem 'elasticsearch-api', path: File.expand_path('../../elasticsearch-api', __FILE__), require: false +if File.exist? File.expand_path('../elasticsearch-api/elasticsearch-api.gemspec', __dir__) + gem 'elasticsearch-api', path: File.expand_path('../elasticsearch-api', __dir__), require: false end -if File.exist? File.expand_path('../../elasticsearch-extensions/elasticsearch-extensions.gemspec', __FILE__) - gem 'elasticsearch-extensions', path: File.expand_path('../../elasticsearch-extensions', __FILE__), require: false -end - -if File.exist? File.expand_path('../../elasticsearch/elasticsearch.gemspec', __FILE__) - gem 'elasticsearch', path: File.expand_path('../../elasticsearch', __FILE__), require: false +if File.exist? File.expand_path('../elasticsearch/elasticsearch.gemspec', __dir__) + gem 'elasticsearch', path: File.expand_path('../elasticsearch', __dir__), require: false end group :development, :test do diff --git a/elasticsearch-transport/elasticsearch-transport.gemspec b/elasticsearch-transport/elasticsearch-transport.gemspec index 40eb15ec18..08a836f662 100644 --- a/elasticsearch-transport/elasticsearch-transport.gemspec +++ b/elasticsearch-transport/elasticsearch-transport.gemspec @@ -50,7 +50,8 @@ Gem::Specification.new do |s| s.add_development_dependency 'ansi' s.add_development_dependency 'bundler' s.add_development_dependency 'cane' - s.add_development_dependency 'curb' unless defined? JRUBY_VERSION + s.add_development_dependency 'curb' unless defined? JRUBY_VERSION + s.add_development_dependency 'elasticsearch', ['>= 7', '< 8.0.0'] s.add_development_dependency 'elasticsearch-extensions' s.add_development_dependency 'hashie' s.add_development_dependency 'httpclient' diff --git a/elasticsearch-transport/lib/elasticsearch/transport.rb b/elasticsearch-transport/lib/elasticsearch/transport.rb index 696a469973..6a043602d3 100644 --- a/elasticsearch-transport/lib/elasticsearch/transport.rb +++ b/elasticsearch-transport/lib/elasticsearch/transport.rb @@ -15,35 +15,23 @@ # specific language governing permissions and limitations # under the License. -require "uri" -require "time" -require "timeout" -require "multi_json" -require "faraday" +require 'uri' +require 'time' +require 'timeout' +require 'multi_json' +require 'faraday' -require "elasticsearch/transport/transport/loggable" -require "elasticsearch/transport/transport/serializer/multi_json" -require "elasticsearch/transport/transport/sniffer" -require "elasticsearch/transport/transport/response" -require "elasticsearch/transport/transport/errors" -require "elasticsearch/transport/transport/base" -require "elasticsearch/transport/transport/connections/selector" -require "elasticsearch/transport/transport/connections/connection" -require "elasticsearch/transport/transport/connections/collection" -require "elasticsearch/transport/transport/http/faraday" -require "elasticsearch/transport/client" -require "elasticsearch/transport/redacted" +require 'elasticsearch/transport/transport/loggable' +require 'elasticsearch/transport/transport/serializer/multi_json' +require 'elasticsearch/transport/transport/sniffer' +require 'elasticsearch/transport/transport/response' +require 'elasticsearch/transport/transport/errors' +require 'elasticsearch/transport/transport/base' +require 'elasticsearch/transport/transport/connections/selector' +require 'elasticsearch/transport/transport/connections/connection' +require 'elasticsearch/transport/transport/connections/collection' +require 'elasticsearch/transport/transport/http/faraday' +require 'elasticsearch/transport/client' +require 'elasticsearch/transport/redacted' -require "elasticsearch/transport/version" - -module Elasticsearch - module Client - - # A convenience wrapper for {::Elasticsearch::Transport::Client#initialize}. - # - def new(arguments={}, &block) - Elasticsearch::Transport::Client.new(arguments, &block) - end - extend self - end -end +require 'elasticsearch/transport/version' diff --git a/elasticsearch-transport/lib/elasticsearch/transport/client.rb b/elasticsearch-transport/lib/elasticsearch/transport/client.rb index 29a216bf56..010473bc06 100644 --- a/elasticsearch-transport/lib/elasticsearch/transport/client.rb +++ b/elasticsearch-transport/lib/elasticsearch/transport/client.rb @@ -20,7 +20,6 @@ module Elasticsearch module Transport - # Handles communication with an Elasticsearch cluster. # # See {file:README.md README} for usage and code examples. diff --git a/elasticsearch-transport/lib/elasticsearch/transport/transport/http/faraday.rb b/elasticsearch-transport/lib/elasticsearch/transport/transport/http/faraday.rb index c871f1f8a7..85fad3cea2 100644 --- a/elasticsearch-transport/lib/elasticsearch/transport/transport/http/faraday.rb +++ b/elasticsearch-transport/lib/elasticsearch/transport/transport/http/faraday.rb @@ -45,10 +45,12 @@ def perform_request(method, path, params = {}, body = nil, headers = nil, opts = headers end - response = connection.connection.run_request(method.downcase.to_sym, - url, - ( body ? __convert_to_json(body) : nil ), - headers) + response = connection.connection.run_request( + method.downcase.to_sym, + url, + (body ? __convert_to_json(body) : nil), + headers + ) Response.new response.status, decompress_response(response.body), response.headers end diff --git a/elasticsearch-transport/spec/elasticsearch/transport/base_spec.rb b/elasticsearch-transport/spec/elasticsearch/transport/base_spec.rb index cdb1dc5eb0..3570802611 100644 --- a/elasticsearch-transport/spec/elasticsearch/transport/base_spec.rb +++ b/elasticsearch-transport/spec/elasticsearch/transport/base_spec.rb @@ -32,14 +32,14 @@ expect(logger).not_to receive(:error).with(/secret_password/) expect { - client.cluster.stats + client.perform_request('GET', '_cluster/stats') }.to raise_exception(Faraday::ConnectionFailed) end it 'replaces the password with the string \'REDACTED\'' do expect(logger).to receive(:error).with(/REDACTED/) expect { - client.cluster.stats + client.perform_request('GET', '_cluster/stats') }.to raise_exception(Faraday::ConnectionFailed) end end @@ -133,7 +133,7 @@ end it 'raises an exception' do - expect { client.info }.to raise_exception(Faraday::ConnectionFailed) + expect { client.perform_request('GET', '/') }.to raise_exception(Faraday::ConnectionFailed) end end diff --git a/elasticsearch-transport/spec/elasticsearch/transport/client_spec.rb b/elasticsearch-transport/spec/elasticsearch/transport/client_spec.rb index a8fe10d541..13edab0a3e 100644 --- a/elasticsearch-transport/spec/elasticsearch/transport/client_spec.rb +++ b/elasticsearch-transport/spec/elasticsearch/transport/client_spec.rb @@ -24,10 +24,6 @@ end end - it 'is aliased as Elasticsearch::Client' do - expect(Elasticsearch::Client.new).to be_a(described_class) - end - it 'has a default transport' do expect(client.transport).to be_a(Elasticsearch::Transport::Client::DEFAULT_TRANSPORT_CLASS) end @@ -1417,14 +1413,14 @@ let(:client) { described_class.new(host: hosts) } it 'doesnae raise an ArgumentError' do - expect { client.search(opaque_id: 'no_error') }.not_to raise_error + expect { client.perform_request('GET', '_search', opaque_id: 'no_error') }.not_to raise_error end it 'uses X-Opaque-Id in the header' do allow(client).to receive(:perform_request) { OpenStruct.new(body: '') } - expect { client.search(opaque_id: 'opaque_id') }.not_to raise_error + expect { client.perform_request('GET', '_search', {}, nil, opaque_id: 'opaque_id') }.not_to raise_error expect(client).to have_received(:perform_request) - .with('GET', '_search', { opaque_id: 'opaque_id' }, nil, {}) + .with('GET', '_search', {}, nil, { opaque_id: 'opaque_id' }) end end end @@ -1501,7 +1497,7 @@ it 'performs the request with the header' do allow(client).to receive(:perform_request) { OpenStruct.new(body: '') } - expect { client.search(headers: headers) }.not_to raise_error + expect { client.perform_request('GET', '_search', {}, nil, headers) }.not_to raise_error expect(client).to have_received(:perform_request) .with('GET', '_search', {}, nil, headers) end @@ -1515,7 +1511,7 @@ ) end let(:instance_headers) { { set_in_instantiation: 'header value' } } - let(:param_headers) {{'user-agent' => 'My Ruby Tests', 'set-on-method-call' => 'header value'}} + let(:param_headers) { {'user-agent' => 'My Ruby Tests', 'set-on-method-call' => 'header value'} } it 'performs the request with the header' do expected_headers = client.transport.connections.connections.first.connection.headers.merge(param_headers) @@ -1524,7 +1520,7 @@ .to receive(:run_request) .with(:get, "http://#{hosts[0]}/_search", nil, expected_headers) { OpenStruct.new(body: '')} - client.search(headers: param_headers) + client.perform_request('GET', '_search', {}, nil, param_headers) end end end @@ -1561,7 +1557,6 @@ end context 'when a request is made' do - let!(:response) do client.perform_request('GET', '_cluster/health') end @@ -1572,9 +1567,7 @@ end describe '#initialize' do - context 'when options are specified' do - let(:transport_options) do { headers: { accept: 'application/yaml', content_type: 'application/yaml' } } end @@ -1590,7 +1583,6 @@ end context 'when a block is provided' do - let(:client) do Elasticsearch::Client.new(host: ELASTICSEARCH_HOSTS.first, logger: logger) do |client| client.headers['Accept'] = 'application/yaml' @@ -1928,7 +1920,7 @@ end let(:node_names) do - client.nodes.stats['nodes'].collect do |name, stats| + client.perform_request('GET', '_nodes/stats').body('nodes').collect do |name, stats| stats['name'] end end @@ -1947,7 +1939,6 @@ end context 'when patron is used as an adapter', unless: jruby? do - before do require 'patron' end diff --git a/elasticsearch-transport/spec/elasticsearch/transport/meta_header_spec.rb b/elasticsearch-transport/spec/elasticsearch/transport/meta_header_spec.rb index c70086f930..670c66e685 100644 --- a/elasticsearch-transport/spec/elasticsearch/transport/meta_header_spec.rb +++ b/elasticsearch-transport/spec/elasticsearch/transport/meta_header_spec.rb @@ -16,6 +16,7 @@ # under the License. require 'spec_helper' +require 'elasticsearch' describe Elasticsearch::Transport::Client do context 'meta-header' do diff --git a/elasticsearch-transport/spec/spec_helper.rb b/elasticsearch-transport/spec/spec_helper.rb index 31c80bfa36..8e4b272acd 100644 --- a/elasticsearch-transport/spec/spec_helper.rb +++ b/elasticsearch-transport/spec/spec_helper.rb @@ -19,7 +19,6 @@ SimpleCov.start { add_filter %r{^/test|spec/} } end -require 'elasticsearch' require 'elasticsearch-transport' require 'logger' require 'ansi/code' @@ -61,7 +60,8 @@ def jruby? # # @since 7.0.0 def node_names - $node_names ||= default_client.nodes.stats['nodes'].collect do |name, stats| + node_stats = default_client.perform_request('GET', '_nodes/stats').body + $node_names ||= node_stats['nodes'].collect do |name, stats| stats['name'] end end diff --git a/elasticsearch-xpack/lib/elasticsearch/xpack.rb b/elasticsearch-xpack/lib/elasticsearch/xpack.rb index 0395487583..1f9bde7b8c 100644 --- a/elasticsearch-xpack/lib/elasticsearch/xpack.rb +++ b/elasticsearch-xpack/lib/elasticsearch/xpack.rb @@ -43,132 +43,127 @@ class Client Elasticsearch::API::COMMON_PARAMS.push :job_id, :datafeed_id, :filter_id, :snapshot_id, :category_id, :policy_id module Elasticsearch - module Transport - class Client - # When a method is called on the client, if it's one of the xpack root - # namespace methods, send them to the xpack client. - # E.g.: client.xpack.usage => client.usage - # Excluding `info` since OSS and XPACK both have info endpoints. - TOP_LEVEL_METHODS = [ - :usage, - :terms_enum - ].freeze - - def method_missing(method, *args, &block) - return xpack.send(method, *args, &block) if TOP_LEVEL_METHODS.include?(method) - - super - end - - def respond_to_missing?(method_name, *args) - TOP_LEVEL_METHODS.include?(method_name) || super + class Client + # When a method is called on the client, if it's one of the xpack root + # namespace methods, send them to the xpack client. + # E.g.: client.xpack.usage => client.usage + # Excluding `info` since OSS and XPACK both have info endpoints. + TOP_LEVEL_METHODS = [ + :usage, + :terms_enum + ].freeze + + TOP_LEVEL_METHODS.each do |method_name| + define_method method_name do |attr| + xpack.send(method_name, attr) end + end - def xpack - unless @xpack - warn('Deprecation notice: The elasticsearch-xpack gem will be deprecated and all the ' \ - "functionality will be available from elasticsearch-api.\n" \ - 'See https://github.com/elastic/elasticsearch-ruby/issues/1274' - ) - end - @xpack ||= Elasticsearch::XPack::API::Client.new(self) + def xpack + unless @xpack + warn( + 'Deprecation notice: The elasticsearch-xpack gem will be deprecated and all the ' \ + "functionality will be available from elasticsearch-api.\n" \ + 'See https://github.com/elastic/elasticsearch-ruby/issues/1274' + ) end + @xpack ||= Elasticsearch::XPack::API::Client.new(self) + end - def security - @security ||= xpack.security - end + def security + @security ||= xpack.security + end - def ml - @ml ||= xpack.ml - end + def ml + @ml ||= xpack.ml + end - def rollup - @rollup ||= xpack.rollup - end + def rollup + @rollup ||= xpack.rollup + end - def watcher - @watcher ||= xpack.watcher - end + def watcher + @watcher ||= xpack.watcher + end - def graph - @graph ||= xpack.graph - end + def graph + @graph ||= xpack.graph + end - def migration - @migration ||= xpack.migration - end + def migration + @migration ||= xpack.migration + end - def sql - @sql ||= xpack.sql - end + def sql + @sql ||= xpack.sql + end - def deprecation - @deprecation ||= xpack.deprecation - end + def deprecation + @deprecation ||= xpack.deprecation + end - def data_frame - @data_frame ||= xpack.data_frame - end + def data_frame + @data_frame ||= xpack.data_frame + end - def ilm - @ilm ||= xpack.ilm - end + def ilm + @ilm ||= xpack.ilm + end - def license - @license ||= xpack.license - end + def license + @license ||= xpack.license + end - def transform - @transform ||= xpack.transform - end + def transform + @transform ||= xpack.transform + end - def async_search - @async_search ||= xpack.async_search - end + def async_search + @async_search ||= xpack.async_search + end - def cat - @cat ||= xpack.cat - end + def cat + @cat ||= xpack.cat + end - def indices - @indices ||= xpack.indices - end + def indices + @indices ||= xpack.indices + end - def searchable_snapshots - @searchable_snapshots ||= xpack.searchable_snapshots - end + def searchable_snapshots + @searchable_snapshots ||= xpack.searchable_snapshots + end - def cross_cluster_replication - @cross_cluster_replication ||= xpack.cross_cluster_replication - end + def cross_cluster_replication + @cross_cluster_replication ||= xpack.cross_cluster_replication + end - def autoscaling - @autoscaling ||= xpack.autoscaling - end + def autoscaling + @autoscaling ||= xpack.autoscaling + end - def enrich - @enrich ||= xpack.enrich - end + def enrich + @enrich ||= xpack.enrich + end - def eql - @eql ||= xpack.eql - end + def eql + @eql ||= xpack.eql + end - def snapshot_lifecycle_management - @snapshot_lifecycle_management ||= xpack.snapshot_lifecycle_management - end + def snapshot_lifecycle_management + @snapshot_lifecycle_management ||= xpack.snapshot_lifecycle_management + end - def text_structure - @text_structure ||= xpack.text_structure - end + def text_structure + @text_structure ||= xpack.text_structure + end - def logstash - @logstash ||= xpack.logstash - end + def logstash + @logstash ||= xpack.logstash + end - def fleet - @fleet ||= xpack.fleet - end + def fleet + @fleet ||= xpack.fleet end end -end if defined?(Elasticsearch::Transport::Client) +end if defined?(Elasticsearch::Client) diff --git a/elasticsearch-xpack/test/test_helper.rb b/elasticsearch-xpack/test/test_helper.rb index 3fda3cc44e..e1bac212e4 100644 --- a/elasticsearch-xpack/test/test_helper.rb +++ b/elasticsearch-xpack/test/test_helper.rb @@ -56,13 +56,6 @@ def perform_request(method, path, params, body) puts "PERFORMING REQUEST:", "--> #{method.to_s.upcase} #{path} #{params} #{body}" FakeResponse.new(200, 'FAKE', {}) end - - # Top level methods: - Elasticsearch::Transport::Client::TOP_LEVEL_METHODS.each do |method| - define_method method do |*args| - xpack.send(method, *args) - end - end end FakeResponse = Struct.new(:status, :body, :headers) do From 11c8a85ffb165f540e4e68a23429e817020b0543 Mon Sep 17 00:00:00 2001 From: Fernando Briano Date: Mon, 28 Jun 2021 15:57:10 +0100 Subject: [PATCH 2/8] [GEM] Adds validation for Elasticsearch --- elasticsearch/Rakefile | 4 +- elasticsearch/elasticsearch.gemspec | 1 + elasticsearch/lib/elasticsearch.rb | 71 +++- .../integration/client_integration_spec.rb | 8 +- .../validation_integration_spec.rb | 30 ++ elasticsearch/spec/spec_helper.rb | 4 +- .../elasticsearch_product_validation_spec.rb | 371 ++++++++++++++++++ elasticsearch/spec/unit/wrapper_gem_spec.rb | 1 + 8 files changed, 477 insertions(+), 13 deletions(-) create mode 100644 elasticsearch/spec/integration/validation_integration_spec.rb create mode 100644 elasticsearch/spec/unit/elasticsearch_product_validation_spec.rb diff --git a/elasticsearch/Rakefile b/elasticsearch/Rakefile index dba2c74470..7bb0a25b2c 100644 --- a/elasticsearch/Rakefile +++ b/elasticsearch/Rakefile @@ -15,7 +15,9 @@ # specific language governing permissions and limitations # under the License. -require "bundler/gem_tasks" +require 'bundler/gem_tasks' + +task(:default) { system 'rake --tasks' } desc 'Run unit tests' task test: 'test:spec' diff --git a/elasticsearch/elasticsearch.gemspec b/elasticsearch/elasticsearch.gemspec index 69a8898fa7..909c3679f6 100644 --- a/elasticsearch/elasticsearch.gemspec +++ b/elasticsearch/elasticsearch.gemspec @@ -56,6 +56,7 @@ Gem::Specification.new do |s| s.add_development_dependency 'rspec' s.add_development_dependency 'ruby-prof' unless defined?(JRUBY_VERSION) || defined?(Rubinius) s.add_development_dependency 'simplecov' + s.add_development_dependency 'webmock' s.add_development_dependency 'yard' s.description = <<-DESC.gsub(/^ /, '') diff --git a/elasticsearch/lib/elasticsearch.rb b/elasticsearch/lib/elasticsearch.rb index 03d8f3dbd5..05808c398f 100644 --- a/elasticsearch/lib/elasticsearch.rb +++ b/elasticsearch/lib/elasticsearch.rb @@ -15,18 +15,79 @@ # specific language governing permissions and limitations # under the License. -require "elasticsearch/version" - +require 'elasticsearch/version' require 'elasticsearch/transport' require 'elasticsearch/api' module Elasticsearch - module Transport - class Client - include Elasticsearch::API + SECURITY_PRIVILEGES_VALIDATION_WARNING = 'The client is unable to verify that the server is Elasticsearch due to security privileges on the server side.'.freeze + NOT_ELASTICSEARCH_WARNING = 'The client noticed that the server is not Elasticsearch and we do not support this unknown product.'.freeze + YOU_KNOW_FOR_SEARCH = 'You know, for Search'.freeze + + class Client + include Elasticsearch::API + + def initialize(arguments = {}, &block) + @verified = false + @transport = Elasticsearch::Transport::Client.new(arguments, &block) + end + + def method_missing(name, *args, &block) + if name == :perform_request + verify_elasticsearch unless @verified + @transport.perform_request(*args, &block) + else + super + end + end + + private + + def verify_elasticsearch + begin + response = @transport.perform_request('GET', '/') + rescue Elasticsearch::Transport::Transport::Errors::Unauthorized, + Elasticsearch::Transport::Transport::Errors::Forbidden + @verified = true + warn(SECURITY_PRIVILEGES_VALIDATION_WARNING) + return + end + + verify_with_version_or_header(response) + end + + def verify_with_version_or_header(response) + version = response.body.dig('version', 'number') + raise Elasticsearch::NotElasticsearchError if version.nil? || version < '6.0.0' + + if version == '7.x-SNAPSHOT' || Gem::Version.new(version) >= Gem::Version.new('7.14-SNAPSHOT') + raise Elasticsearch::NotElasticsearchError unless response.headers['x-elastic-product'] == 'Elasticsearch' + + @verified = true + elsif Gem::Version.new(version) > Gem::Version.new('6.0.0') && + Gem::Version.new(version) < Gem::Version.new('7.0.0') + raise Elasticsearch::NotElasticsearchError unless + response.body.dig('version', 'tagline') == YOU_KNOW_FOR_SEARCH + + @verified = true + elsif Gem::Version.new(version) >= Gem::Version.new('7.0.0') && + Gem::Version.new(version) < Gem::Version.new('7.14-SNAPSHOT') + raise Elasticsearch::NotElasticsearchError unless + response.body.dig('version', 'tagline') == YOU_KNOW_FOR_SEARCH && + response.body.dig('version', 'build_flavor') == 'default' + + @verified = true + end + end + end + + class NotElasticsearchError < StandardError + def initialize + super(NOT_ELASTICSEARCH_WARNING) end end end + module Elastic # If the version is X.X.X.pre/alpha/beta, use X.X.Xp for the meta-header: def self.client_meta_version diff --git a/elasticsearch/spec/integration/client_integration_spec.rb b/elasticsearch/spec/integration/client_integration_spec.rb index 2a55c1c8f7..c55b714bb9 100644 --- a/elasticsearch/spec/integration/client_integration_spec.rb +++ b/elasticsearch/spec/integration/client_integration_spec.rb @@ -14,12 +14,8 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. -ELASTICSEARCH_URL = ENV['TEST_ES_SERVER'] || "http://localhost:#{(ENV['PORT'] || 9200)}" -raise URI::InvalidURIError unless ELASTICSEARCH_URL =~ /\A#{URI::DEFAULT_PARSER.make_regexp}\z/ - -require 'elasticsearch' +require 'spec_helper' require 'logger' -require 'rspec' context 'Elasticsearch client' do let(:logger) { Logger.new($stderr) } @@ -53,7 +49,7 @@ end context 'Reports the right meta header' do - it 'Reports es service name and gem versio' do + it 'Reports es service name and gem version' do headers = client.transport.connections.first.connection.headers expect(headers['x-elastic-client-meta']).to match /^es=#{Elasticsearch::VERSION}/ end diff --git a/elasticsearch/spec/integration/validation_integration_spec.rb b/elasticsearch/spec/integration/validation_integration_spec.rb new file mode 100644 index 0000000000..99d713b0a3 --- /dev/null +++ b/elasticsearch/spec/integration/validation_integration_spec.rb @@ -0,0 +1,30 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you under +# the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +require 'spec_helper' +require 'logger' + +describe 'Elasticsearch validation integration' do + it 'Validates for Elasticsearch > 7.14' do + client = Elasticsearch::Client.new( + host: ELASTICSEARCH_URL, + logger: Logger.new($stderr) + ) + expect(client.instance_variable_get('@verified')).to be false + client.count + expect(client.instance_variable_get('@verified')).to be true + end +end diff --git a/elasticsearch/spec/spec_helper.rb b/elasticsearch/spec/spec_helper.rb index 72356526c2..9ea9e6e3f0 100644 --- a/elasticsearch/spec/spec_helper.rb +++ b/elasticsearch/spec/spec_helper.rb @@ -17,7 +17,9 @@ require 'elasticsearch' require 'rspec' -require 'webmock/rspec' + +ELASTICSEARCH_URL = ENV['TEST_ES_SERVER'] || "http://localhost:#{(ENV['PORT'] || 9200)}" +raise URI::InvalidURIError unless ELASTICSEARCH_URL =~ /\A#{URI::DEFAULT_PARSER.make_regexp}\z/ RSpec.configure do |config| config.formatter = :documentation diff --git a/elasticsearch/spec/unit/elasticsearch_product_validation_spec.rb b/elasticsearch/spec/unit/elasticsearch_product_validation_spec.rb new file mode 100644 index 0000000000..e4fbaeca07 --- /dev/null +++ b/elasticsearch/spec/unit/elasticsearch_product_validation_spec.rb @@ -0,0 +1,371 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you under +# the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +require 'spec_helper' +require 'webmock/rspec' + +describe 'Elasticsearch: Validation' do + let(:host) { 'http://localhost:9200' } + let(:verify_request_stub) do + stub_request(:get, host) + .to_return(status: status, body: body, headers: headers) + end + let(:count_request_stub) do + stub_request(:post, "#{host}/_count") + .to_return(status: 200, body: nil, headers: {}) + end + let(:status) { 200 } + let(:body) { {}.to_json } + let(:client) { Elasticsearch::Client.new } + let(:headers) do + { 'content-type' => 'json' } + end + + def error_requests_and_expectations + expect { client.count }.to raise_error Elasticsearch::NotElasticsearchError + assert_requested :get, host + assert_not_requested :post, "#{host}/_count" + expect { client.cluster.health }.to raise_error Elasticsearch::NotElasticsearchError + expect(client.instance_variable_get('@verified')).to be false + expect { client.cluster.health }.to raise_error Elasticsearch::NotElasticsearchError + end + + def valid_requests_and_expectations + expect(client.instance_variable_get('@verified')).to be false + assert_not_requested :get, host + + client.count + expect(client.instance_variable_get('@verified')) + assert_requested :get, host + assert_requested :post, "#{host}/_count" + end + + context 'When Elasticsearch replies with status 401' do + let(:status) { 401 } + let(:body) { {}.to_json } + + it 'Verifies the request but shows a warning' do + stderr = $stderr + fake_stderr = StringIO.new + $stderr = fake_stderr + + verify_request_stub + count_request_stub + + valid_requests_and_expectations + + fake_stderr.rewind + expect(fake_stderr.string).to eq("#{Elasticsearch::SECURITY_PRIVILEGES_VALIDATION_WARNING}\n") + ensure + $stderr = stderr + end + end + + context 'When Elasticsearch replies with status 403' do + let(:status) { 403 } + let(:body) { {}.to_json } + + it 'Verifies the request but shows a warning' do + stderr = $stderr + fake_stderr = StringIO.new + $stderr = fake_stderr + + verify_request_stub + count_request_stub + + valid_requests_and_expectations + + fake_stderr.rewind + expect(fake_stderr.string).to eq("#{Elasticsearch::SECURITY_PRIVILEGES_VALIDATION_WARNING}\n") + ensure + $stderr = stderr + end + end + + context 'When the Elasticsearch version is >= 7.14' do + context 'With a valid Elasticsearch response' do + let(:body) { { 'version' => { 'number' => '7.14.0' } }.to_json } + let(:headers) do + { + 'X-Elastic-Product' => 'Elasticsearch', + 'content-type' => 'json' + } + end + it 'Makes requests and passes validation' do + verify_request_stub + count_request_stub + + valid_requests_and_expectations + end + end + + context 'When the header is not present' do + it 'Fails validation' do + verify_request_stub + + expect(client.instance_variable_get('@verified')).to be false + assert_not_requested :get, host + + error_requests_and_expectations + end + end + end + + context 'When the Elasticsearch version is >= 7.14-SNAPSHOT' do + context 'With a valid Elasticsearch response' do + let(:body) { { 'version' => { 'number' => '7.14-SNAPSHOT' } }.to_json } + let(:headers) do + { + 'X-Elastic-Product' => 'Elasticsearch', + 'content-type' => 'json' + } + end + it 'Makes requests and passes validation' do + verify_request_stub + count_request_stub + + valid_requests_and_expectations + end + end + + context 'When the header is not present' do + it 'Fails validation' do + verify_request_stub + + expect(client.instance_variable_get('@verified')).to be false + assert_not_requested :get, host + + error_requests_and_expectations + end + end + end + + context 'When the Elasticsearch version is >= 7.15-SNAPSHOT' do + context 'With a valid Elasticsearch response' do + let(:body) { { 'version' => { 'number' => '7.15-SNAPSHOT' } }.to_json } + let(:headers) do + { + 'X-Elastic-Product' => 'Elasticsearch', + 'content-type' => 'json' + } + end + it 'Makes requests and passes validation' do + verify_request_stub + count_request_stub + + valid_requests_and_expectations + end + end + + context 'When the header is not present' do + it 'Fails validation' do + verify_request_stub + + expect(client.instance_variable_get('@verified')).to be false + assert_not_requested :get, host + + error_requests_and_expectations + end + end + end + + context 'When the version is 7.x-SNAPSHOT' do + let(:body) { { 'version' => { 'number' => '7.x-SNAPSHOT' } }.to_json } + + context 'When the header is not present' do + it 'Fails validation' do + verify_request_stub + count_request_stub + + error_requests_and_expectations + end + end + + context 'With a valid Elasticsearch response' do + let(:headers) do + { + 'X-Elastic-Product' => 'Elasticsearch', + 'content-type' => 'json' + } + end + + it 'Makes requests and passes validation' do + verify_request_stub + count_request_stub + + valid_requests_and_expectations + end + end + end + + context 'When Elasticsearch version is 7.4.0' do + context 'When tagline is not present' do + let(:body) { { 'version' => { 'number' => '7.4.0', 'build_flavor' => 'default' } }.to_json } + + it 'Fails validation' do + verify_request_stub + count_request_stub + + error_requests_and_expectations + end + end + + context 'When build flavor is not present' do + let(:body) do + { + 'version' => { + 'number' => '7.4.0', + 'tagline' => Elasticsearch::YOU_KNOW_FOR_SEARCH + } + }.to_json + end + + it 'Fails validation' do + verify_request_stub + count_request_stub + + error_requests_and_expectations + end + end + + context 'With a valid Elasticsearch response' do + let(:body) do + { + 'version' => { + 'number' => '7.4.0', + 'tagline' => Elasticsearch::YOU_KNOW_FOR_SEARCH, + 'build_flavor' => 'default' + } + }.to_json + end + + it 'Makes requests and passes validation' do + verify_request_stub + count_request_stub + + valid_requests_and_expectations + end + end + end + + context 'When Elasticsearch version is < 6.0.0' do + let(:body) { { 'version' => { 'number' => '5.0.0' } }.to_json } + + it 'Raises an exception and client doesnae work' do + verify_request_stub + error_requests_and_expectations + end + end + + context 'When there is no version data' do + let(:body) { {}.to_json } + it 'Raises an exception and client doesnae work' do + verify_request_stub + error_requests_and_expectations + end + end + + context 'When Elasticsearch version is between 6.0.0 and 7.0.0' do + context 'With an Elasticsearch valid response' do + let(:body) do + { + 'version' => { + 'number' => '6.8.10', + 'tagline' => Elasticsearch::YOU_KNOW_FOR_SEARCH + } + }.to_json + end + + it 'Makes requests and passes validation' do + verify_request_stub + count_request_stub + + valid_requests_and_expectations + end + end + + context 'With no tagline' do + let(:body) do + { 'version' => { 'number' => '6.8.10' } }.to_json + end + + it 'Fails validation' do + verify_request_stub + count_request_stub + + error_requests_and_expectations + end + end + end + + context 'When Elasticsearch version is between 7.0.0 and 7.14.0' do + context 'With a valid Elasticsearch response' do + let(:body) do + { + 'version' => { + 'number' => '7.10.0', + 'tagline' => Elasticsearch::YOU_KNOW_FOR_SEARCH, + 'build_flavor' => 'default' + } + }.to_json + end + + it 'Makes requests and passes validation' do + verify_request_stub + count_request_stub + + valid_requests_and_expectations + end + end + + context 'When the tagline is not present' do + let(:body) do + { + 'version' => { + 'number' => '7.10.0', + 'build_flavor' => 'default' + } + }.to_json + end + + it 'Fails validation' do + verify_request_stub + count_request_stub + + error_requests_and_expectations + end + end + + context 'When the build_flavor is not present' do + let(:body) do + { + 'version' => { + 'number' => '7.10.0', + 'tagline' => Elasticsearch::YOU_KNOW_FOR_SEARCH + } + }.to_json + end + + it 'Fails validation' do + verify_request_stub + count_request_stub + + error_requests_and_expectations + end + end + end +end diff --git a/elasticsearch/spec/unit/wrapper_gem_spec.rb b/elasticsearch/spec/unit/wrapper_gem_spec.rb index 149c3a5c00..14328212c7 100644 --- a/elasticsearch/spec/unit/wrapper_gem_spec.rb +++ b/elasticsearch/spec/unit/wrapper_gem_spec.rb @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. require 'elasticsearch' +require 'webmock/rspec' describe 'Elasticsearch: wrapper gem' do it 'requires all neccessary subgems' do From 6026cd8ea7410efc1849879b2689dcf57aaf20da Mon Sep 17 00:00:00 2001 From: Fernando Briano Date: Tue, 29 Jun 2021 14:01:37 +0100 Subject: [PATCH 3/8] [GEM] Test for YAML response in validation --- elasticsearch/lib/elasticsearch.rb | 28 +++++++++++++------ .../elasticsearch_product_validation_spec.rb | 15 ++++++++++ 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/elasticsearch/lib/elasticsearch.rb b/elasticsearch/lib/elasticsearch.rb index 05808c398f..738d3e2c71 100644 --- a/elasticsearch/lib/elasticsearch.rb +++ b/elasticsearch/lib/elasticsearch.rb @@ -45,7 +45,7 @@ def method_missing(name, *args, &block) def verify_elasticsearch begin - response = @transport.perform_request('GET', '/') + response = elasticsearch_validation_request rescue Elasticsearch::Transport::Transport::Errors::Unauthorized, Elasticsearch::Transport::Transport::Errors::Forbidden @verified = true @@ -53,32 +53,42 @@ def verify_elasticsearch return end - verify_with_version_or_header(response) + body = if response.headers['content-type'] == 'application/yaml' + require 'yaml' + YAML.load(response.body) + else + response.body + end + version = body.dig('version', 'number') + verify_with_version_or_header(body, version, response.headers) end - def verify_with_version_or_header(response) - version = response.body.dig('version', 'number') + def verify_with_version_or_header(body, version, headers) raise Elasticsearch::NotElasticsearchError if version.nil? || version < '6.0.0' if version == '7.x-SNAPSHOT' || Gem::Version.new(version) >= Gem::Version.new('7.14-SNAPSHOT') - raise Elasticsearch::NotElasticsearchError unless response.headers['x-elastic-product'] == 'Elasticsearch' + raise Elasticsearch::NotElasticsearchError unless headers['x-elastic-product'] == 'Elasticsearch' @verified = true elsif Gem::Version.new(version) > Gem::Version.new('6.0.0') && - Gem::Version.new(version) < Gem::Version.new('7.0.0') + Gem::Version.new(version) < Gem::Version.new('7.0.0') raise Elasticsearch::NotElasticsearchError unless - response.body.dig('version', 'tagline') == YOU_KNOW_FOR_SEARCH + body.dig('version', 'tagline') == YOU_KNOW_FOR_SEARCH @verified = true elsif Gem::Version.new(version) >= Gem::Version.new('7.0.0') && Gem::Version.new(version) < Gem::Version.new('7.14-SNAPSHOT') raise Elasticsearch::NotElasticsearchError unless - response.body.dig('version', 'tagline') == YOU_KNOW_FOR_SEARCH && - response.body.dig('version', 'build_flavor') == 'default' + body.dig('version', 'tagline') == YOU_KNOW_FOR_SEARCH && + body.dig('version', 'build_flavor') == 'default' @verified = true end end + + def elasticsearch_validation_request + @transport.perform_request('GET', '/') + end end class NotElasticsearchError < StandardError diff --git a/elasticsearch/spec/unit/elasticsearch_product_validation_spec.rb b/elasticsearch/spec/unit/elasticsearch_product_validation_spec.rb index e4fbaeca07..54f20c4380 100644 --- a/elasticsearch/spec/unit/elasticsearch_product_validation_spec.rb +++ b/elasticsearch/spec/unit/elasticsearch_product_validation_spec.rb @@ -368,4 +368,19 @@ def valid_requests_and_expectations end end end + + context 'When doing a yaml content-type request' do + let(:client) do + Elasticsearch::Client.new(transport_options: {headers: { accept: 'application/yaml', content_type: 'application/yaml' }}) + end + + let(:headers) { { 'content-type' => 'application/yaml', 'X-Elastic-Product' => 'Elasticsearch' } } + let(:body) { "---\nversion:\n number: \"7.14.0-SNAPSHOT\"\n" } + + it 'validates' do + verify_request_stub + count_request_stub + valid_requests_and_expectations + end + end end From 3597601a9b354c8a672428a8284e3df2f6111d63 Mon Sep 17 00:00:00 2001 From: Fernando Briano Date: Wed, 30 Jun 2021 09:58:22 +0100 Subject: [PATCH 4/8] [Client] Updates curb implementation --- .../transport/transport/http/curb.rb | 68 +++++++++++-------- .../elasticsearch/transport/client_spec.rb | 14 ++-- .../transport/meta_header_spec.rb | 14 ++-- .../test/unit/transport_curb_test.rb | 1 - 4 files changed, 53 insertions(+), 44 deletions(-) diff --git a/elasticsearch-transport/lib/elasticsearch/transport/transport/http/curb.rb b/elasticsearch-transport/lib/elasticsearch/transport/transport/http/curb.rb index 9c41959ff6..d881b2fb7b 100644 --- a/elasticsearch-transport/lib/elasticsearch/transport/transport/http/curb.rb +++ b/elasticsearch-transport/lib/elasticsearch/transport/transport/http/curb.rb @@ -37,33 +37,44 @@ def perform_request(method, path, params={}, body=nil, headers=nil, opts={}) connection.connection.url = connection.full_url(path, params) case method - when 'HEAD' - connection.connection.set :nobody, true - when 'GET', 'POST', 'PUT', 'DELETE' - connection.connection.set :nobody, false - - connection.connection.put_data = __convert_to_json(body) if body - - if headers - if connection.connection.headers - connection.connection.headers.merge!(headers) - else - connection.connection.headers = headers - end + when 'HEAD' + connection.connection.set :nobody, true + when 'GET', 'POST', 'PUT', 'DELETE' + connection.connection.set :nobody, false + connection.connection.put_data = __convert_to_json(body) if body + if headers + if connection.connection.headers + connection.connection.headers.merge!(headers) + else + connection.connection.headers = headers end - - else raise ArgumentError, "Unsupported HTTP method: #{method}" + end + else + raise ArgumentError, "Unsupported HTTP method: #{method}" end connection.connection.http(method.to_sym) - response_headers = {} - response_headers['content-type'] = 'application/json' if connection.connection.header_str =~ /\/json/ + Response.new( + connection.connection.response_code, + decompress_response(connection.connection.body_str), + headers(connection) + ) + end + end - Response.new connection.connection.response_code, - decompress_response(connection.connection.body_str), - response_headers + def headers(connection) + headers_string = connection.connection.header_str + return nil if headers_string.nil? + + response_headers = headers_string&.split(/\\r\\n|\r\n/).reject(&:empty?) + response_headers.shift # Removes HTTP status string + processed_header = response_headers.flat_map { |s| s.scan(/^(\S+): (.+)/) } + headers_hash = Hash[processed_header].transform_keys(&:downcase) + if headers_hash['content-type']&.match?(/application\/json/) + headers_hash['content-type'] = 'application/json' end + headers_hash end # Builds and returns a connection @@ -72,9 +83,8 @@ def perform_request(method, path, params={}, body=nil, headers=nil, opts={}) # def __build_connection(host, options={}, block=nil) client = ::Curl::Easy.new - apply_headers(client, options) - client.url = __full_url(host) + client.url = __full_url(host) if host[:user] client.http_auth_types = host[:auth_type] || :basic @@ -106,13 +116,13 @@ def host_unreachable_exceptions def user_agent_header(client) @user_agent ||= begin - meta = ["RUBY_VERSION: #{RUBY_VERSION}"] - if RbConfig::CONFIG && RbConfig::CONFIG['host_os'] - meta << "#{RbConfig::CONFIG['host_os'].split('_').first[/[a-z]+/i].downcase} #{RbConfig::CONFIG['target_cpu']}" - end - meta << "Curb #{Curl::CURB_VERSION}" - "elasticsearch-ruby/#{VERSION} (#{meta.join('; ')})" - end + meta = ["RUBY_VERSION: #{RUBY_VERSION}"] + if RbConfig::CONFIG && RbConfig::CONFIG['host_os'] + meta << "#{RbConfig::CONFIG['host_os'].split('_').first[/[a-z]+/i].downcase} #{RbConfig::CONFIG['target_cpu']}" + end + meta << "Curb #{Curl::CURB_VERSION}" + "elasticsearch-ruby/#{VERSION} (#{meta.join('; ')})" + end end end end diff --git a/elasticsearch-transport/spec/elasticsearch/transport/client_spec.rb b/elasticsearch-transport/spec/elasticsearch/transport/client_spec.rb index 13edab0a3e..13de50b32f 100644 --- a/elasticsearch-transport/spec/elasticsearch/transport/client_spec.rb +++ b/elasticsearch-transport/spec/elasticsearch/transport/client_spec.rb @@ -1814,11 +1814,12 @@ end context 'when using Curb as the transport', unless: jruby? do - let(:client) do - described_class.new(hosts: ELASTICSEARCH_HOSTS, - compression: true, - transport_class: Elasticsearch::Transport::Transport::HTTP::Curb) + described_class.new( + hosts: ELASTICSEARCH_HOSTS, + compression: true, + transport_class: Elasticsearch::Transport::Transport::HTTP::Curb + ) end it 'compresses the request and decompresses the response' do @@ -1835,7 +1836,6 @@ end context 'when using Manticore as the transport', if: jruby? do - let(:client) do described_class.new(hosts: ELASTICSEARCH_HOSTS, compression: true, @@ -1849,9 +1849,7 @@ end describe '#perform_request' do - context 'when a request is made' do - before do client.perform_request('DELETE', '_all') client.perform_request('DELETE', 'myindex') rescue @@ -1874,7 +1872,6 @@ end context 'when an invalid url is specified' do - it 'raises an exception' do expect { client.perform_request('GET', 'myindex/mydoc/1?routing=FOOBARBAZ') @@ -1883,7 +1880,6 @@ end context 'when the \'ignore\' parameter is specified' do - let(:response) do client.perform_request('PUT', '_foobar', ignore: 400) end diff --git a/elasticsearch-transport/spec/elasticsearch/transport/meta_header_spec.rb b/elasticsearch-transport/spec/elasticsearch/transport/meta_header_spec.rb index 670c66e685..e041a98c9f 100644 --- a/elasticsearch-transport/spec/elasticsearch/transport/meta_header_spec.rb +++ b/elasticsearch-transport/spec/elasticsearch/transport/meta_header_spec.rb @@ -255,12 +255,16 @@ def meta_version end context 'when using custom transport implementation' do - class MyTransport - include Elasticsearch::Transport::Transport::Base - def initialize(args); end + let (:transport_class) do + Class.new do + include Elasticsearch::Transport::Transport::Base + + def initialize(args) + end + end end - let(:client) { Elasticsearch::Client.new(transport_class: MyTransport) } - let(:subject){ client.instance_variable_get("@arguments")[:transport_options][:headers] } + let(:client) { Elasticsearch::Client.new(transport_class: transport_class) } + let(:subject) { client.instance_variable_get('@arguments')[:transport_options][:headers] } let(:meta_header) do if jruby? "es=#{meta_version},rb=#{RUBY_VERSION},t=#{Elasticsearch::Transport::VERSION},jv=#{ENV_JAVA['java.version']},jr=#{JRUBY_VERSION}" diff --git a/elasticsearch-transport/test/unit/transport_curb_test.rb b/elasticsearch-transport/test/unit/transport_curb_test.rb index 2ec789075e..2e89dc576e 100644 --- a/elasticsearch-transport/test/unit/transport_curb_test.rb +++ b/elasticsearch-transport/test/unit/transport_curb_test.rb @@ -84,7 +84,6 @@ class Elasticsearch::Transport::Transport::HTTP::FaradayTest < Minitest::Test @transport.connections.first.connection.expects(:http).with(:GET).returns(stub_everything) @transport.connections.first.connection.expects(:body_str).returns('{"foo":"bar"}') @transport.connections.first.connection.expects(:header_str).returns('HTTP/1.1 200 OK\r\nContent-Type: application/json; charset=UTF-8\r\nContent-Length: 311\r\n\r\n') - response = @transport.perform_request 'GET', '/' assert_equal 'application/json', response.headers['content-type'] From a0a3640a6d60a03eb7688053211eee9c243a35c2 Mon Sep 17 00:00:00 2001 From: Fernando Briano Date: Wed, 30 Jun 2021 15:54:08 +0100 Subject: [PATCH 5/8] [Client] Fixes tests --- api-spec-testing/test_file.rb | 4 ++-- elasticsearch-transport/Rakefile | 1 - .../spec/elasticsearch/transport/base_spec.rb | 8 ++++---- .../elasticsearch/transport/client_spec.rb | 4 ++-- .../transport/meta_header_spec.rb | 20 ++++++++++++------- 5 files changed, 21 insertions(+), 16 deletions(-) diff --git a/api-spec-testing/test_file.rb b/api-spec-testing/test_file.rb index ad1859c756..220d5c1959 100644 --- a/api-spec-testing/test_file.rb +++ b/api-spec-testing/test_file.rb @@ -414,10 +414,10 @@ def clear_datastreams(client) client.xpack.indices.delete_data_stream(name: datastream['name'], expand_wildcards: 'all') end begin - client.indices.delete_data_stream(name: '*', expand_wildcards: 'all') + client.xpack.indices.delete_data_stream(name: '*', expand_wildcards: 'all') rescue StandardError => e LOGGER.error "Caught exception attempting to delete data streams: #{e}" - client.indices.delete_data_stream(name: '*') + client.xpack.indices.delete_data_stream(name: '*') end end diff --git a/elasticsearch-transport/Rakefile b/elasticsearch-transport/Rakefile index 1f2f55a1d6..2b625eb789 100644 --- a/elasticsearch-transport/Rakefile +++ b/elasticsearch-transport/Rakefile @@ -32,7 +32,6 @@ namespace :test do sh '../scripts/wait-cluster.sh' end - task spec: :wait_for_green RSpec::Core::RakeTask.new(:spec) Rake::TestTask.new(:unit) do |test| diff --git a/elasticsearch-transport/spec/elasticsearch/transport/base_spec.rb b/elasticsearch-transport/spec/elasticsearch/transport/base_spec.rb index 3570802611..3151ac415b 100644 --- a/elasticsearch-transport/spec/elasticsearch/transport/base_spec.rb +++ b/elasticsearch-transport/spec/elasticsearch/transport/base_spec.rb @@ -73,14 +73,14 @@ expect(logger).not_to receive(:fatal).with(/secret_password/) expect { - client.cluster.stats + client.perform_request('GET', '_cluster/stats') }.to raise_exception(Faraday::SSLError) end it 'replaces the password with the string \'REDACTED\'' do expect(logger).to receive(:fatal).with(/REDACTED/) expect { - client.cluster.stats + client.perform_request('GET', '_cluster/stats') }.to raise_exception(Faraday::SSLError) end else @@ -103,14 +103,14 @@ expect(logger).not_to receive(:fatal).with(/secret_password/) expect { - client.cluster.stats + client.perform_request('GET', '_cluster/stats') }.to raise_exception(Faraday::SSLError) end it 'replaces the password with the string \'REDACTED\'' do expect(logger).to receive(:fatal).with(/REDACTED/) expect { - client.cluster.stats + client.perform_request('GET', '_cluster/stats') }.to raise_exception(Faraday::SSLError) end else diff --git a/elasticsearch-transport/spec/elasticsearch/transport/client_spec.rb b/elasticsearch-transport/spec/elasticsearch/transport/client_spec.rb index 13de50b32f..c1c891046b 100644 --- a/elasticsearch-transport/spec/elasticsearch/transport/client_spec.rb +++ b/elasticsearch-transport/spec/elasticsearch/transport/client_spec.rb @@ -1584,7 +1584,7 @@ context 'when a block is provided' do let(:client) do - Elasticsearch::Client.new(host: ELASTICSEARCH_HOSTS.first, logger: logger) do |client| + described_class.new(host: ELASTICSEARCH_HOSTS.first, logger: logger) do |client| client.headers['Accept'] = 'application/yaml' end end @@ -1600,7 +1600,7 @@ context 'when the Faraday adapter is set in the block' do let(:client) do - Elasticsearch::Client.new(host: ELASTICSEARCH_HOSTS.first, logger: logger) do |client| + described_class.new(host: ELASTICSEARCH_HOSTS.first, logger: logger) do |client| client.adapter(:net_http_persistent) end end diff --git a/elasticsearch-transport/spec/elasticsearch/transport/meta_header_spec.rb b/elasticsearch-transport/spec/elasticsearch/transport/meta_header_spec.rb index e041a98c9f..c1a5e7aaf4 100644 --- a/elasticsearch-transport/spec/elasticsearch/transport/meta_header_spec.rb +++ b/elasticsearch-transport/spec/elasticsearch/transport/meta_header_spec.rb @@ -232,7 +232,9 @@ def meta_version if defined?(JRUBY_VERSION) context 'when using manticore' do let(:client) do - Elasticsearch::Client.new(transport_class: Elasticsearch::Transport::Transport::HTTP::Manticore) + described_class.new(transport_class: Elasticsearch::Transport::Transport::HTTP::Manticore).tap do |client| + client.instance_variable_set('@verified', true) + end end let(:subject) { client.transport.connections.first.connection.instance_variable_get("@options")[:headers]} @@ -244,7 +246,9 @@ def meta_version else context 'when using curb' do let(:client) do - Elasticsearch::Client.new(transport_class: Elasticsearch::Transport::Transport::HTTP::Curb) + described_class.new(transport_class: Elasticsearch::Transport::Transport::HTTP::Curb).tap do |client| + client.instance_variable_set('@verified', true) + end end it 'sets curb in the metaheader' do @@ -255,15 +259,13 @@ def meta_version end context 'when using custom transport implementation' do - let (:transport_class) do + let(:transport_class) do Class.new do - include Elasticsearch::Transport::Transport::Base - def initialize(args) end end end - let(:client) { Elasticsearch::Client.new(transport_class: transport_class) } + let(:client) { Elasticsearch::Transport::Client.new(transport_class: transport_class) } let(:subject) { client.instance_variable_get('@arguments')[:transport_options][:headers] } let(:meta_header) do if jruby? @@ -284,7 +286,11 @@ def initialize(args) stub_const('Elastic::ELASTICSEARCH_SERVICE_VERSION', [:ent, '8.0.0']) end - let(:client) { Elasticsearch::Client.new } + let(:client) do + described_class.new.tap do |client| + client.instance_variable_set('@verified', true) + end + end it 'sets the service version in the metaheader' do expect(subject['x-elastic-client-meta']).to match(regexp) From 1900e495e5dd635188da8a78c9c5357a9d1477ab Mon Sep 17 00:00:00 2001 From: Fernando Briano Date: Tue, 6 Jul 2021 11:19:45 +0100 Subject: [PATCH 6/8] [API] Refactors SAML endpoints --- elasticsearch-api/lib/elasticsearch/api.rb | 3 +- .../api/actions/security/params_registry.rb | 60 ------------------- .../api/actions/security/saml_authenticate.rb | 46 -------------- .../actions/security/saml_complete_logout.rb | 46 -------------- .../api/actions/security/saml_invalidate.rb | 46 -------------- .../api/actions/security/saml_logout.rb | 46 -------------- .../security/saml_prepare_authentication.rb | 46 -------------- .../saml_service_provider_metadata.rb | 48 --------------- .../api/actions/security/saml_authenticate.rb | 48 +++++++++++++++ .../actions/security/saml_complete_logout.rb | 48 +++++++++++++++ .../api/actions/security/saml_invalidate.rb | 48 +++++++++++++++ .../xpack/api/actions/security/saml_logout.rb | 48 +++++++++++++++ .../security/saml_prepare_authentication.rb | 48 +++++++++++++++ .../saml_service_provider_metadata.rb | 50 ++++++++++++++++ .../xpack}/security/saml_authenticate_spec.rb | 2 +- .../security/saml_complete_logout_spec.rb | 2 +- .../xpack/security/saml_invalidate_spec.rb | 44 ++++++++++++++ .../spec/xpack/security/saml_logout_spec.rb | 44 ++++++++++++++ .../saml_prepare_authentication_spec.rb | 44 ++++++++++++++ .../saml_service_provider_metadata_spec.rb | 46 ++++++++++++++ 20 files changed, 471 insertions(+), 342 deletions(-) delete mode 100644 elasticsearch-api/lib/elasticsearch/api/actions/security/params_registry.rb delete mode 100644 elasticsearch-api/lib/elasticsearch/api/actions/security/saml_authenticate.rb delete mode 100644 elasticsearch-api/lib/elasticsearch/api/actions/security/saml_complete_logout.rb delete mode 100644 elasticsearch-api/lib/elasticsearch/api/actions/security/saml_invalidate.rb delete mode 100644 elasticsearch-api/lib/elasticsearch/api/actions/security/saml_logout.rb delete mode 100644 elasticsearch-api/lib/elasticsearch/api/actions/security/saml_prepare_authentication.rb delete mode 100644 elasticsearch-api/lib/elasticsearch/api/actions/security/saml_service_provider_metadata.rb create mode 100644 elasticsearch-xpack/lib/elasticsearch/xpack/api/actions/security/saml_authenticate.rb create mode 100644 elasticsearch-xpack/lib/elasticsearch/xpack/api/actions/security/saml_complete_logout.rb create mode 100644 elasticsearch-xpack/lib/elasticsearch/xpack/api/actions/security/saml_invalidate.rb create mode 100644 elasticsearch-xpack/lib/elasticsearch/xpack/api/actions/security/saml_logout.rb create mode 100644 elasticsearch-xpack/lib/elasticsearch/xpack/api/actions/security/saml_prepare_authentication.rb create mode 100644 elasticsearch-xpack/lib/elasticsearch/xpack/api/actions/security/saml_service_provider_metadata.rb rename {elasticsearch-api/spec/elasticsearch/api/actions => elasticsearch-xpack/spec/xpack}/security/saml_authenticate_spec.rb (95%) rename {elasticsearch-api/spec/elasticsearch/api/actions => elasticsearch-xpack/spec/xpack}/security/saml_complete_logout_spec.rb (95%) create mode 100644 elasticsearch-xpack/spec/xpack/security/saml_invalidate_spec.rb create mode 100644 elasticsearch-xpack/spec/xpack/security/saml_logout_spec.rb create mode 100644 elasticsearch-xpack/spec/xpack/security/saml_prepare_authentication_spec.rb create mode 100644 elasticsearch-xpack/spec/xpack/security/saml_service_provider_metadata_spec.rb diff --git a/elasticsearch-api/lib/elasticsearch/api.rb b/elasticsearch-api/lib/elasticsearch/api.rb index 3c321a9927..fb3f6442ae 100644 --- a/elasticsearch-api/lib/elasticsearch/api.rb +++ b/elasticsearch-api/lib/elasticsearch/api.rb @@ -74,8 +74,7 @@ def self.included(base) Elasticsearch::API::Remote, Elasticsearch::API::DanglingIndices, Elasticsearch::API::Features, - Elasticsearch::API::Shutdown, - Elasticsearch::API::Security + Elasticsearch::API::Shutdown end # The serializer class diff --git a/elasticsearch-api/lib/elasticsearch/api/actions/security/params_registry.rb b/elasticsearch-api/lib/elasticsearch/api/actions/security/params_registry.rb deleted file mode 100644 index 589f0e5a68..0000000000 --- a/elasticsearch-api/lib/elasticsearch/api/actions/security/params_registry.rb +++ /dev/null @@ -1,60 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -module Elasticsearch - module API - module Security - module Actions - module ParamsRegistry - extend self - - # A Mapping of all the actions to their list of valid params. - # - # @since 6.1.1 - PARAMS = {} - - # Register an action with its list of valid params. - # - # @example Register the action. - # ParamsRegistry.register(:benchmark, [ :verbose ]) - # - # @param [ Symbol ] action The action to register. - # @param [ Array[Symbol] ] valid_params The list of valid params. - # - # @since 6.1.1 - def register(action, valid_params) - PARAMS[action.to_sym] = valid_params - end - - # Get the list of valid params for a given action. - # - # @example Get the list of valid params. - # ParamsRegistry.get(:benchmark) - # - # @param [ Symbol ] action The action. - # - # @return [ Array ] The list of valid params for the action. - # - # @since 6.1.1 - def get(action) - PARAMS.fetch(action, []) - end - end - end - end - end -end diff --git a/elasticsearch-api/lib/elasticsearch/api/actions/security/saml_authenticate.rb b/elasticsearch-api/lib/elasticsearch/api/actions/security/saml_authenticate.rb deleted file mode 100644 index efe6c27867..0000000000 --- a/elasticsearch-api/lib/elasticsearch/api/actions/security/saml_authenticate.rb +++ /dev/null @@ -1,46 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -module Elasticsearch - module API - module Security - module Actions - # Exchanges a SAML Response message for an Elasticsearch access token and refresh token pair - # - # @option arguments [Hash] :headers Custom HTTP headers - # @option arguments [Hash] :body The SAML response to authenticate (*Required*) - # - # @see https://www.elastic.co/guide/en/elasticsearch/reference/7.x/security-api-saml-authenticate.html - # - def saml_authenticate(arguments = {}) - raise ArgumentError, "Required argument 'body' missing" unless arguments[:body] - - headers = arguments.delete(:headers) || {} - - arguments = arguments.clone - - method = Elasticsearch::API::HTTP_POST - path = "_security/saml/authenticate" - params = {} - - body = arguments[:body] - perform_request(method, path, params, body, headers).body - end - end - end - end -end diff --git a/elasticsearch-api/lib/elasticsearch/api/actions/security/saml_complete_logout.rb b/elasticsearch-api/lib/elasticsearch/api/actions/security/saml_complete_logout.rb deleted file mode 100644 index 9c2675782e..0000000000 --- a/elasticsearch-api/lib/elasticsearch/api/actions/security/saml_complete_logout.rb +++ /dev/null @@ -1,46 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -module Elasticsearch - module API - module Security - module Actions - # Verifies the logout response sent from the SAML IdP - # - # @option arguments [Hash] :headers Custom HTTP headers - # @option arguments [Hash] :body The logout response to verify (*Required*) - # - # @see https://www.elastic.co/guide/en/elasticsearch/reference/7.x/security-api-saml-complete-logout.html - # - def saml_complete_logout(arguments = {}) - raise ArgumentError, "Required argument 'body' missing" unless arguments[:body] - - headers = arguments.delete(:headers) || {} - - arguments = arguments.clone - - method = Elasticsearch::API::HTTP_POST - path = "_security/saml/complete_logout" - params = {} - - body = arguments[:body] - perform_request(method, path, params, body, headers).body - end - end - end - end -end diff --git a/elasticsearch-api/lib/elasticsearch/api/actions/security/saml_invalidate.rb b/elasticsearch-api/lib/elasticsearch/api/actions/security/saml_invalidate.rb deleted file mode 100644 index 46cfb1dc2b..0000000000 --- a/elasticsearch-api/lib/elasticsearch/api/actions/security/saml_invalidate.rb +++ /dev/null @@ -1,46 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -module Elasticsearch - module API - module Security - module Actions - # Consumes a SAML LogoutRequest - # - # @option arguments [Hash] :headers Custom HTTP headers - # @option arguments [Hash] :body The LogoutRequest message (*Required*) - # - # @see https://www.elastic.co/guide/en/elasticsearch/reference/7.x/security-api-saml-invalidate.html - # - def saml_invalidate(arguments = {}) - raise ArgumentError, "Required argument 'body' missing" unless arguments[:body] - - headers = arguments.delete(:headers) || {} - - arguments = arguments.clone - - method = Elasticsearch::API::HTTP_POST - path = "_security/saml/invalidate" - params = {} - - body = arguments[:body] - perform_request(method, path, params, body, headers).body - end - end - end - end -end diff --git a/elasticsearch-api/lib/elasticsearch/api/actions/security/saml_logout.rb b/elasticsearch-api/lib/elasticsearch/api/actions/security/saml_logout.rb deleted file mode 100644 index b595fb0d03..0000000000 --- a/elasticsearch-api/lib/elasticsearch/api/actions/security/saml_logout.rb +++ /dev/null @@ -1,46 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -module Elasticsearch - module API - module Security - module Actions - # Invalidates an access token and a refresh token that were generated via the SAML Authenticate API - # - # @option arguments [Hash] :headers Custom HTTP headers - # @option arguments [Hash] :body The tokens to invalidate (*Required*) - # - # @see https://www.elastic.co/guide/en/elasticsearch/reference/7.x/security-api-saml-logout.html - # - def saml_logout(arguments = {}) - raise ArgumentError, "Required argument 'body' missing" unless arguments[:body] - - headers = arguments.delete(:headers) || {} - - arguments = arguments.clone - - method = Elasticsearch::API::HTTP_POST - path = "_security/saml/logout" - params = {} - - body = arguments[:body] - perform_request(method, path, params, body, headers).body - end - end - end - end -end diff --git a/elasticsearch-api/lib/elasticsearch/api/actions/security/saml_prepare_authentication.rb b/elasticsearch-api/lib/elasticsearch/api/actions/security/saml_prepare_authentication.rb deleted file mode 100644 index bf958eb913..0000000000 --- a/elasticsearch-api/lib/elasticsearch/api/actions/security/saml_prepare_authentication.rb +++ /dev/null @@ -1,46 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -module Elasticsearch - module API - module Security - module Actions - # Creates a SAML authentication request - # - # @option arguments [Hash] :headers Custom HTTP headers - # @option arguments [Hash] :body The realm for which to create the authentication request, identified by either its name or the ACS URL (*Required*) - # - # @see https://www.elastic.co/guide/en/elasticsearch/reference/7.x/security-api-saml-prepare-authentication.html - # - def saml_prepare_authentication(arguments = {}) - raise ArgumentError, "Required argument 'body' missing" unless arguments[:body] - - headers = arguments.delete(:headers) || {} - - arguments = arguments.clone - - method = Elasticsearch::API::HTTP_POST - path = "_security/saml/prepare" - params = {} - - body = arguments[:body] - perform_request(method, path, params, body, headers).body - end - end - end - end -end diff --git a/elasticsearch-api/lib/elasticsearch/api/actions/security/saml_service_provider_metadata.rb b/elasticsearch-api/lib/elasticsearch/api/actions/security/saml_service_provider_metadata.rb deleted file mode 100644 index 10e339de4b..0000000000 --- a/elasticsearch-api/lib/elasticsearch/api/actions/security/saml_service_provider_metadata.rb +++ /dev/null @@ -1,48 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -module Elasticsearch - module API - module Security - module Actions - # Generates SAML metadata for the Elastic stack SAML 2.0 Service Provider - # - # @option arguments [String] :realm_name The name of the SAML realm to get the metadata for - # @option arguments [Hash] :headers Custom HTTP headers - # - # @see https://www.elastic.co/guide/en/elasticsearch/reference/7.x/security-api-saml-sp-metadata.html - # - def saml_service_provider_metadata(arguments = {}) - raise ArgumentError, "Required argument 'realm_name' missing" unless arguments[:realm_name] - - headers = arguments.delete(:headers) || {} - - arguments = arguments.clone - - _realm_name = arguments.delete(:realm_name) - - method = Elasticsearch::API::HTTP_GET - path = "_security/saml/metadata/#{Elasticsearch::API::Utils.__listify(_realm_name)}" - params = {} - - body = nil - perform_request(method, path, params, body, headers).body - end - end - end - end -end diff --git a/elasticsearch-xpack/lib/elasticsearch/xpack/api/actions/security/saml_authenticate.rb b/elasticsearch-xpack/lib/elasticsearch/xpack/api/actions/security/saml_authenticate.rb new file mode 100644 index 0000000000..3242065c73 --- /dev/null +++ b/elasticsearch-xpack/lib/elasticsearch/xpack/api/actions/security/saml_authenticate.rb @@ -0,0 +1,48 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you under +# the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +module Elasticsearch + module XPack + module API + module Security + module Actions + # Exchanges a SAML Response message for an Elasticsearch access token and refresh token pair + # + # @option arguments [Hash] :headers Custom HTTP headers + # @option arguments [Hash] :body The SAML response to authenticate (*Required*) + # + # @see https://www.elastic.co/guide/en/elasticsearch/reference/7.x/security-api-saml-authenticate.html + # + def saml_authenticate(arguments = {}) + raise ArgumentError, "Required argument 'body' missing" unless arguments[:body] + + headers = arguments.delete(:headers) || {} + + arguments = arguments.clone + + method = Elasticsearch::API::HTTP_POST + path = "_security/saml/authenticate" + params = {} + + body = arguments[:body] + perform_request(method, path, params, body, headers).body + end + end + end + end + end +end diff --git a/elasticsearch-xpack/lib/elasticsearch/xpack/api/actions/security/saml_complete_logout.rb b/elasticsearch-xpack/lib/elasticsearch/xpack/api/actions/security/saml_complete_logout.rb new file mode 100644 index 0000000000..763161161d --- /dev/null +++ b/elasticsearch-xpack/lib/elasticsearch/xpack/api/actions/security/saml_complete_logout.rb @@ -0,0 +1,48 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you under +# the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +module Elasticsearch + module XPack + module API + module Security + module Actions + # Verifies the logout response sent from the SAML IdP + # + # @option arguments [Hash] :headers Custom HTTP headers + # @option arguments [Hash] :body The logout response to verify (*Required*) + # + # @see https://www.elastic.co/guide/en/elasticsearch/reference/7.x/security-api-saml-complete-logout.html + # + def saml_complete_logout(arguments = {}) + raise ArgumentError, "Required argument 'body' missing" unless arguments[:body] + + headers = arguments.delete(:headers) || {} + + arguments = arguments.clone + + method = Elasticsearch::API::HTTP_POST + path = "_security/saml/complete_logout" + params = {} + + body = arguments[:body] + perform_request(method, path, params, body, headers).body + end + end + end + end + end +end diff --git a/elasticsearch-xpack/lib/elasticsearch/xpack/api/actions/security/saml_invalidate.rb b/elasticsearch-xpack/lib/elasticsearch/xpack/api/actions/security/saml_invalidate.rb new file mode 100644 index 0000000000..c8ab928ea6 --- /dev/null +++ b/elasticsearch-xpack/lib/elasticsearch/xpack/api/actions/security/saml_invalidate.rb @@ -0,0 +1,48 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you under +# the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +module Elasticsearch + module XPack + module API + module Security + module Actions + # Consumes a SAML LogoutRequest + # + # @option arguments [Hash] :headers Custom HTTP headers + # @option arguments [Hash] :body The LogoutRequest message (*Required*) + # + # @see https://www.elastic.co/guide/en/elasticsearch/reference/7.x/security-api-saml-invalidate.html + # + def saml_invalidate(arguments = {}) + raise ArgumentError, "Required argument 'body' missing" unless arguments[:body] + + headers = arguments.delete(:headers) || {} + + arguments = arguments.clone + + method = Elasticsearch::API::HTTP_POST + path = "_security/saml/invalidate" + params = {} + + body = arguments[:body] + perform_request(method, path, params, body, headers).body + end + end + end + end + end +end diff --git a/elasticsearch-xpack/lib/elasticsearch/xpack/api/actions/security/saml_logout.rb b/elasticsearch-xpack/lib/elasticsearch/xpack/api/actions/security/saml_logout.rb new file mode 100644 index 0000000000..957f3d7703 --- /dev/null +++ b/elasticsearch-xpack/lib/elasticsearch/xpack/api/actions/security/saml_logout.rb @@ -0,0 +1,48 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you under +# the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +module Elasticsearch + module XPack + module API + module Security + module Actions + # Invalidates an access token and a refresh token that were generated via the SAML Authenticate API + # + # @option arguments [Hash] :headers Custom HTTP headers + # @option arguments [Hash] :body The tokens to invalidate (*Required*) + # + # @see https://www.elastic.co/guide/en/elasticsearch/reference/7.x/security-api-saml-logout.html + # + def saml_logout(arguments = {}) + raise ArgumentError, "Required argument 'body' missing" unless arguments[:body] + + headers = arguments.delete(:headers) || {} + + arguments = arguments.clone + + method = Elasticsearch::API::HTTP_POST + path = "_security/saml/logout" + params = {} + + body = arguments[:body] + perform_request(method, path, params, body, headers).body + end + end + end + end + end +end diff --git a/elasticsearch-xpack/lib/elasticsearch/xpack/api/actions/security/saml_prepare_authentication.rb b/elasticsearch-xpack/lib/elasticsearch/xpack/api/actions/security/saml_prepare_authentication.rb new file mode 100644 index 0000000000..d4646fe2f4 --- /dev/null +++ b/elasticsearch-xpack/lib/elasticsearch/xpack/api/actions/security/saml_prepare_authentication.rb @@ -0,0 +1,48 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you under +# the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +module Elasticsearch + module XPack + module API + module Security + module Actions + # Creates a SAML authentication request + # + # @option arguments [Hash] :headers Custom HTTP headers + # @option arguments [Hash] :body The realm for which to create the authentication request, identified by either its name or the ACS URL (*Required*) + # + # @see https://www.elastic.co/guide/en/elasticsearch/reference/7.x/security-api-saml-prepare-authentication.html + # + def saml_prepare_authentication(arguments = {}) + raise ArgumentError, "Required argument 'body' missing" unless arguments[:body] + + headers = arguments.delete(:headers) || {} + + arguments = arguments.clone + + method = Elasticsearch::API::HTTP_POST + path = "_security/saml/prepare" + params = {} + + body = arguments[:body] + perform_request(method, path, params, body, headers).body + end + end + end + end + end +end diff --git a/elasticsearch-xpack/lib/elasticsearch/xpack/api/actions/security/saml_service_provider_metadata.rb b/elasticsearch-xpack/lib/elasticsearch/xpack/api/actions/security/saml_service_provider_metadata.rb new file mode 100644 index 0000000000..ed808ca5bb --- /dev/null +++ b/elasticsearch-xpack/lib/elasticsearch/xpack/api/actions/security/saml_service_provider_metadata.rb @@ -0,0 +1,50 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you under +# the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +module Elasticsearch + module XPack + module API + module Security + module Actions + # Generates SAML metadata for the Elastic stack SAML 2.0 Service Provider + # + # @option arguments [String] :realm_name The name of the SAML realm to get the metadata for + # @option arguments [Hash] :headers Custom HTTP headers + # + # @see https://www.elastic.co/guide/en/elasticsearch/reference/7.x/security-api-saml-sp-metadata.html + # + def saml_service_provider_metadata(arguments = {}) + raise ArgumentError, "Required argument 'realm_name' missing" unless arguments[:realm_name] + + headers = arguments.delete(:headers) || {} + + arguments = arguments.clone + + _realm_name = arguments.delete(:realm_name) + + method = Elasticsearch::API::HTTP_GET + path = "_security/saml/metadata/#{Elasticsearch::API::Utils.__listify(_realm_name)}" + params = {} + + body = nil + perform_request(method, path, params, body, headers).body + end + end + end + end + end +end diff --git a/elasticsearch-api/spec/elasticsearch/api/actions/security/saml_authenticate_spec.rb b/elasticsearch-xpack/spec/xpack/security/saml_authenticate_spec.rb similarity index 95% rename from elasticsearch-api/spec/elasticsearch/api/actions/security/saml_authenticate_spec.rb rename to elasticsearch-xpack/spec/xpack/security/saml_authenticate_spec.rb index 05e0b1ce62..17185b09aa 100644 --- a/elasticsearch-api/spec/elasticsearch/api/actions/security/saml_authenticate_spec.rb +++ b/elasticsearch-xpack/spec/xpack/security/saml_authenticate_spec.rb @@ -33,7 +33,7 @@ end let(:client) do - Class.new { include Elasticsearch::API }.new + Class.new { include Elasticsearch::XPack::API }.new end it 'raises an error if no body is provided' do diff --git a/elasticsearch-api/spec/elasticsearch/api/actions/security/saml_complete_logout_spec.rb b/elasticsearch-xpack/spec/xpack/security/saml_complete_logout_spec.rb similarity index 95% rename from elasticsearch-api/spec/elasticsearch/api/actions/security/saml_complete_logout_spec.rb rename to elasticsearch-xpack/spec/xpack/security/saml_complete_logout_spec.rb index 4265b4ef28..4f999c7c22 100644 --- a/elasticsearch-api/spec/elasticsearch/api/actions/security/saml_complete_logout_spec.rb +++ b/elasticsearch-xpack/spec/xpack/security/saml_complete_logout_spec.rb @@ -33,7 +33,7 @@ end let(:client) do - Class.new { include Elasticsearch::API }.new + Class.new { include Elasticsearch::XPack::API }.new end it 'requires the :namespace argument' do diff --git a/elasticsearch-xpack/spec/xpack/security/saml_invalidate_spec.rb b/elasticsearch-xpack/spec/xpack/security/saml_invalidate_spec.rb new file mode 100644 index 0000000000..b3b6bdef0b --- /dev/null +++ b/elasticsearch-xpack/spec/xpack/security/saml_invalidate_spec.rb @@ -0,0 +1,44 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you under +# the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +require 'spec_helper' + +describe 'client.security#saml_invalidate' do + let(:expected_args) do + [ + 'POST', + '_security/saml/invalidate', + {}, + {}, + {} + ] + end + + it 'performs the request' do + expect(client_double.security.saml_invalidate(body: {})).to eq({}) + end + + let(:client) do + Class.new { include Elasticsearch::XPack::API }.new + end + + it 'raises an error if no body is provided' do + expect do + client.security.saml_invalidate + end.to raise_exception(ArgumentError) + end +end diff --git a/elasticsearch-xpack/spec/xpack/security/saml_logout_spec.rb b/elasticsearch-xpack/spec/xpack/security/saml_logout_spec.rb new file mode 100644 index 0000000000..87a4c254db --- /dev/null +++ b/elasticsearch-xpack/spec/xpack/security/saml_logout_spec.rb @@ -0,0 +1,44 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you under +# the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +require 'spec_helper' + +describe 'client.security#saml_logout' do + let(:expected_args) do + [ + 'POST', + '_security/saml/logout', + {}, + {}, + {} + ] + end + + it 'performs the request' do + expect(client_double.security.saml_logout(body: {})).to eq({}) + end + + let(:client) do + Class.new { include Elasticsearch::XPack::API }.new + end + + it 'raises an error if no body is provided' do + expect do + client.security.saml_logout + end.to raise_exception(ArgumentError) + end +end diff --git a/elasticsearch-xpack/spec/xpack/security/saml_prepare_authentication_spec.rb b/elasticsearch-xpack/spec/xpack/security/saml_prepare_authentication_spec.rb new file mode 100644 index 0000000000..567593f2e1 --- /dev/null +++ b/elasticsearch-xpack/spec/xpack/security/saml_prepare_authentication_spec.rb @@ -0,0 +1,44 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you under +# the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +require 'spec_helper' + +describe 'client.security#saml_prepare_authentication' do + let(:expected_args) do + [ + 'POST', + '_security/saml/logout', + {}, + {}, + {} + ] + end + + it 'performs the request' do + expect(client_double.security.saml_logout(body: {})).to eq({}) + end + + let(:client) do + Class.new { include Elasticsearch::XPack::API }.new + end + + it 'raises an error if no body is provided' do + expect do + client.security.saml_logout + end.to raise_exception(ArgumentError) + end +end diff --git a/elasticsearch-xpack/spec/xpack/security/saml_service_provider_metadata_spec.rb b/elasticsearch-xpack/spec/xpack/security/saml_service_provider_metadata_spec.rb new file mode 100644 index 0000000000..b37c24e719 --- /dev/null +++ b/elasticsearch-xpack/spec/xpack/security/saml_service_provider_metadata_spec.rb @@ -0,0 +1,46 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you under +# the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +require 'spec_helper' + +describe 'client.security#saml_service_provider_metadata' do + let(:expected_args) do + [ + 'GET', + "_security/saml/metadata/#{realm_name}", + {}, + nil, + {} + ] + end + + let(:realm_name) { 'foo' } + + it 'performs the request' do + expect(client_double.security.saml_service_provider_metadata(realm_name: realm_name)).to eq({}) + end + + let(:client) do + Class.new { include Elasticsearch::XPack::API }.new + end + + it 'raises an error if no realm name is provided' do + expect do + client.security.saml_service_provider_metadata + end.to raise_exception(ArgumentError) + end +end From d267036852a453744ab50933a3fe8abdfb09e4ff Mon Sep 17 00:00:00 2001 From: Fernando Briano Date: Wed, 7 Jul 2021 10:54:04 +0100 Subject: [PATCH 7/8] [Gem] Adds attr_accessor to transport in Client --- .../lib/elasticsearch/transport/client.rb | 2 +- .../lib/elasticsearch/transport/transport/http/faraday.rb | 1 - elasticsearch/lib/elasticsearch.rb | 2 ++ elasticsearch/spec/integration/client_integration_spec.rb | 2 +- elasticsearch/spec/unit/wrapper_gem_spec.rb | 6 ++++++ 5 files changed, 10 insertions(+), 3 deletions(-) diff --git a/elasticsearch-transport/lib/elasticsearch/transport/client.rb b/elasticsearch-transport/lib/elasticsearch/transport/client.rb index 010473bc06..071c491bce 100644 --- a/elasticsearch-transport/lib/elasticsearch/transport/client.rb +++ b/elasticsearch-transport/lib/elasticsearch/transport/client.rb @@ -26,7 +26,7 @@ module Transport # class Client include MetaHeader - DEFAULT_TRANSPORT_CLASS = Transport::HTTP::Faraday + DEFAULT_TRANSPORT_CLASS = Transport::HTTP::Faraday DEFAULT_LOGGER = lambda do require 'logger' diff --git a/elasticsearch-transport/lib/elasticsearch/transport/transport/http/faraday.rb b/elasticsearch-transport/lib/elasticsearch/transport/transport/http/faraday.rb index 85fad3cea2..860d77d4d9 100644 --- a/elasticsearch-transport/lib/elasticsearch/transport/transport/http/faraday.rb +++ b/elasticsearch-transport/lib/elasticsearch/transport/transport/http/faraday.rb @@ -19,7 +19,6 @@ module Elasticsearch module Transport module Transport module HTTP - # The default transport implementation, using the [_Faraday_](https://rubygems.org/gems/faraday) # library for abstracting the HTTP client. # diff --git a/elasticsearch/lib/elasticsearch.rb b/elasticsearch/lib/elasticsearch.rb index 738d3e2c71..374a800992 100644 --- a/elasticsearch/lib/elasticsearch.rb +++ b/elasticsearch/lib/elasticsearch.rb @@ -26,7 +26,9 @@ module Elasticsearch class Client include Elasticsearch::API + attr_accessor :transport + # See Elasticsearch::Transport::Client for initializer parameters def initialize(arguments = {}, &block) @verified = false @transport = Elasticsearch::Transport::Client.new(arguments, &block) diff --git a/elasticsearch/spec/integration/client_integration_spec.rb b/elasticsearch/spec/integration/client_integration_spec.rb index c55b714bb9..6414fe2f89 100644 --- a/elasticsearch/spec/integration/client_integration_spec.rb +++ b/elasticsearch/spec/integration/client_integration_spec.rb @@ -50,7 +50,7 @@ context 'Reports the right meta header' do it 'Reports es service name and gem version' do - headers = client.transport.connections.first.connection.headers + headers = client.transport.transport.connections.first.connection.headers expect(headers['x-elastic-client-meta']).to match /^es=#{Elasticsearch::VERSION}/ end end diff --git a/elasticsearch/spec/unit/wrapper_gem_spec.rb b/elasticsearch/spec/unit/wrapper_gem_spec.rb index 14328212c7..bfdfd105b9 100644 --- a/elasticsearch/spec/unit/wrapper_gem_spec.rb +++ b/elasticsearch/spec/unit/wrapper_gem_spec.rb @@ -30,4 +30,10 @@ expect(client).to respond_to(:cluster) expect(client).to respond_to(:indices) end + + it 'can access the client transport' do + client = Elasticsearch::Client.new + expect(client.transport).to be_a(Elasticsearch::Transport::Client) + expect(client.transport.transport).to be_a(Elasticsearch::Transport::Transport::HTTP::Faraday) + end end From 2db4e3fe7e5c29e63e31f023762154c1399c4815 Mon Sep 17 00:00:00 2001 From: Fernando Briano Date: Mon, 12 Jul 2021 09:03:22 +0100 Subject: [PATCH 8/8] Fixes tagline check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: João Duarte --- elasticsearch/lib/elasticsearch.rb | 4 +-- .../elasticsearch_product_validation_spec.rb | 26 +++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/elasticsearch/lib/elasticsearch.rb b/elasticsearch/lib/elasticsearch.rb index 374a800992..a36139d92c 100644 --- a/elasticsearch/lib/elasticsearch.rb +++ b/elasticsearch/lib/elasticsearch.rb @@ -75,13 +75,13 @@ def verify_with_version_or_header(body, version, headers) elsif Gem::Version.new(version) > Gem::Version.new('6.0.0') && Gem::Version.new(version) < Gem::Version.new('7.0.0') raise Elasticsearch::NotElasticsearchError unless - body.dig('version', 'tagline') == YOU_KNOW_FOR_SEARCH + body['tagline'] == YOU_KNOW_FOR_SEARCH @verified = true elsif Gem::Version.new(version) >= Gem::Version.new('7.0.0') && Gem::Version.new(version) < Gem::Version.new('7.14-SNAPSHOT') raise Elasticsearch::NotElasticsearchError unless - body.dig('version', 'tagline') == YOU_KNOW_FOR_SEARCH && + body['tagline'] == YOU_KNOW_FOR_SEARCH && body.dig('version', 'build_flavor') == 'default' @verified = true diff --git a/elasticsearch/spec/unit/elasticsearch_product_validation_spec.rb b/elasticsearch/spec/unit/elasticsearch_product_validation_spec.rb index 54f20c4380..f69af44c5e 100644 --- a/elasticsearch/spec/unit/elasticsearch_product_validation_spec.rb +++ b/elasticsearch/spec/unit/elasticsearch_product_validation_spec.rb @@ -228,9 +228,9 @@ def valid_requests_and_expectations let(:body) do { 'version' => { - 'number' => '7.4.0', - 'tagline' => Elasticsearch::YOU_KNOW_FOR_SEARCH - } + 'number' => '7.4.0' + }, + 'tagline' => Elasticsearch::YOU_KNOW_FOR_SEARCH }.to_json end @@ -247,9 +247,9 @@ def valid_requests_and_expectations { 'version' => { 'number' => '7.4.0', - 'tagline' => Elasticsearch::YOU_KNOW_FOR_SEARCH, 'build_flavor' => 'default' - } + }, + 'tagline' => Elasticsearch::YOU_KNOW_FOR_SEARCH }.to_json end @@ -284,9 +284,9 @@ def valid_requests_and_expectations let(:body) do { 'version' => { - 'number' => '6.8.10', - 'tagline' => Elasticsearch::YOU_KNOW_FOR_SEARCH - } + 'number' => '6.8.10' + }, + 'tagline' => Elasticsearch::YOU_KNOW_FOR_SEARCH }.to_json end @@ -318,9 +318,9 @@ def valid_requests_and_expectations { 'version' => { 'number' => '7.10.0', - 'tagline' => Elasticsearch::YOU_KNOW_FOR_SEARCH, 'build_flavor' => 'default' - } + }, + 'tagline' => Elasticsearch::YOU_KNOW_FOR_SEARCH }.to_json end @@ -354,9 +354,9 @@ def valid_requests_and_expectations let(:body) do { 'version' => { - 'number' => '7.10.0', - 'tagline' => Elasticsearch::YOU_KNOW_FOR_SEARCH - } + 'number' => '7.10.0' + }, + 'tagline' => Elasticsearch::YOU_KNOW_FOR_SEARCH }.to_json end