From 32770aec7ae51f59367cdabbfd5938874ee2f041 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Lundstr=C3=B6m?= Date: Thu, 24 May 2018 13:41:32 +0200 Subject: [PATCH] Add cookies and basic_auth_eager options Make automatic cookie management and to use eager/preemptive Basic Authentication configurable. --- lib/logstash/outputs/elasticsearch.rb | 6 +++ .../outputs/elasticsearch/http_client.rb | 1 + .../http_client/manticore_adapter.rb | 4 +- .../elasticsearch/http_client_builder.rb | 2 + spec/unit/http_client_builder_spec.rb | 45 ++++++++++++++++++- .../http_client/manticore_adapter_spec.rb | 13 ++++-- 6 files changed, 64 insertions(+), 7 deletions(-) diff --git a/lib/logstash/outputs/elasticsearch.rb b/lib/logstash/outputs/elasticsearch.rb index 69e5b1d66..c2d34ffe7 100644 --- a/lib/logstash/outputs/elasticsearch.rb +++ b/lib/logstash/outputs/elasticsearch.rb @@ -229,6 +229,12 @@ class LogStash::Outputs::ElasticSearch < LogStash::Outputs::Base # Custom Headers to send on each request to elasticsearch nodes config :custom_headers, :validate => :hash, :default => {} + # Enable automatic cookie management between requests + config :cookies, :validate => :boolean, :default => false + + # Eagerly offer the Authorization header before the server challenges for it when using Basic Auth + config :basic_auth_eager, :validate => :boolean, :default => true + def build_client params["metric"] = metric @client ||= ::LogStash::Outputs::ElasticSearch::HttpClientBuilder.build(@logger, @hosts, params) diff --git a/lib/logstash/outputs/elasticsearch/http_client.rb b/lib/logstash/outputs/elasticsearch/http_client.rb index b46f4be0a..c8c701233 100644 --- a/lib/logstash/outputs/elasticsearch/http_client.rb +++ b/lib/logstash/outputs/elasticsearch/http_client.rb @@ -261,6 +261,7 @@ def build_adapter(options) } adapter_options[:proxy] = client_settings[:proxy] if client_settings[:proxy] + adapter_options[:cookies] = client_settings[:cookies] if client_settings[:cookies] adapter_options[:check_connection_timeout] = client_settings[:check_connection_timeout] if client_settings[:check_connection_timeout] diff --git a/lib/logstash/outputs/elasticsearch/http_client/manticore_adapter.rb b/lib/logstash/outputs/elasticsearch/http_client/manticore_adapter.rb index 6e12db126..d969dec44 100644 --- a/lib/logstash/outputs/elasticsearch/http_client/manticore_adapter.rb +++ b/lib/logstash/outputs/elasticsearch/http_client/manticore_adapter.rb @@ -14,8 +14,6 @@ def initialize(logger, options={}) # We manage our own retries directly, so let's disable them here options[:automatic_retries] = 0 - # We definitely don't need cookies - options[:cookies] = false @client_params = {:headers => DEFAULT_HEADERS.merge(options[:headers] || {})} @@ -60,7 +58,7 @@ def perform_request(url, method, path, params={}, body=nil) # We have to unescape the password here since manticore won't do it # for us unless its part of the URL :password => CGI.unescape(url.password), - :eager => true + :eager => params["basic_auth_eager"] } end diff --git a/lib/logstash/outputs/elasticsearch/http_client_builder.rb b/lib/logstash/outputs/elasticsearch/http_client_builder.rb index 3cb60bc9b..048d5c44e 100644 --- a/lib/logstash/outputs/elasticsearch/http_client_builder.rb +++ b/lib/logstash/outputs/elasticsearch/http_client_builder.rb @@ -4,6 +4,7 @@ module LogStash; module Outputs; class ElasticSearch; module HttpClientBuilder def self.build(logger, hosts, params) client_settings = { + :cookies => params["cookies"], :pool_max => params["pool_max"], :pool_max_per_route => params["pool_max_per_route"], :check_connection_timeout => params["validate_after_inactivity"], @@ -146,6 +147,7 @@ def self.setup_basic_auth(logger, params) return {} unless user && password && password.value { + :basic_auth_eager => params["basic_auth_eager"], :user => CGI.escape(user), :password => CGI.escape(password.value) } diff --git a/spec/unit/http_client_builder_spec.rb b/spec/unit/http_client_builder_spec.rb index acd9c769e..1bd402571 100644 --- a/spec/unit/http_client_builder_spec.rb +++ b/spec/unit/http_client_builder_spec.rb @@ -13,9 +13,10 @@ allow(secured).to receive(:value).and_return(password) secured end + let(:basic_auth_eager) { true } let(:options) { {"user" => user, "password" => password} } let(:logger) { mock("logger") } - let(:auth_setup) { klass.setup_basic_auth(double("logger"), {"user" => user, "password" => password_secured}) } + let(:auth_setup) { klass.setup_basic_auth(double("logger"), {"user" => user, "password" => password_secured, "basic_auth_eager" => basic_auth_eager}) } it "should return the user escaped" do expect(auth_setup[:user]).to eql(CGI.escape(user)) @@ -26,6 +27,48 @@ end end + describe "auth without eager basic auth" do + let(:hosts) { [ ::LogStash::Util::SafeURI.new("http://localhost:9200") ] } + let(:options) { { + "basic_auth_eager" => false, + "hosts" => hosts, + } } + let(:logger) { double("logger") } + before :each do + [:debug, :debug?, :info?, :info, :warn].each do |level| + allow(logger).to receive(level) + end + end + + it "Attempt to connect without auth, fake the response requesting credentials, check that your code then retries with the auth" do + end + + context "with cookies" do + let(:options) { super.merge("cookies" => true) } + + it "should use cookies when configured" do + expect(described_class).to receive(:create_http_client) do |options| + expect(options[:client_settings][:cookies]).to eq(true) + end + described_class.build(logger, hosts, options) + end + + it "Attempt to connect with basic auth, fake the response setting a cookie, check that your code saves the cookie" do + end + + context "already saved" do + it "Attempt to connect when the cookie is saved in memory, check that the request will actually send the cookie with the request" do + end + end + + context "expired" do + it "Attempt to connect when an expired cookie is saved in memory, fake the response requesting credentials, check that your code retries with the auth" do + end + end + end + + end + describe "customizing action paths" do let(:hosts) { [ ::LogStash::Util::SafeURI.new("http://localhost:9200") ] } let(:options) { {"hosts" => hosts } } diff --git a/spec/unit/outputs/elasticsearch/http_client/manticore_adapter_spec.rb b/spec/unit/outputs/elasticsearch/http_client/manticore_adapter_spec.rb index f9279e39a..e0380a268 100644 --- a/spec/unit/outputs/elasticsearch/http_client/manticore_adapter_spec.rb +++ b/spec/unit/outputs/elasticsearch/http_client/manticore_adapter_spec.rb @@ -3,7 +3,9 @@ describe LogStash::Outputs::ElasticSearch::HttpClient::ManticoreAdapter do let(:logger) { Cabin::Channel.get } - let(:options) { {} } + let(:options) { { + "basic_auth_eager" => true + } } subject { described_class.new(logger, options) } @@ -19,6 +21,10 @@ describe "auth" do let(:user) { "myuser" } let(:password) { "mypassword" } + let(:basic_auth_eager) { true } + let(:params) { { + "basic_auth_eager" => basic_auth_eager + } } let(:noauth_uri) { clone = uri.clone; clone.user=nil; clone.password=nil; clone } let(:uri) { ::LogStash::Util::SafeURI.new("http://#{user}:#{password}@localhost:9200") } @@ -32,15 +38,16 @@ expect(subject.manticore).to receive(:get). with(expected_uri.to_s, { + "basic_auth_eager" => basic_auth_eager, :headers => {"Content-Type" => "application/json"}, :auth => { :user => user, :password => password, - :eager => true + :eager => basic_auth_eager } }).and_return resp - subject.perform_request(uri, :get, "/") + subject.perform_request(uri, :get, "/", params) end end