From 8a189713dc562a75521fd2bfbfc72c64b68a04e3 Mon Sep 17 00:00:00 2001 From: Jeremy Evans Date: Wed, 5 Jun 2024 15:39:08 -0700 Subject: [PATCH] Support :connect_opts_proc Database option for late binding options Some modern authentication schemes automatically rotate usernames and/or passwords on a regular basis without user involvement or control. Attempt to support this type of authentication case with this option, so you can do something like: Sequel.connect('postgres://user@host/database', connect_opts_proc: lambda do |h| h[:password] = SomeAuthLibrary.get_current_password(h[:user]) end) The way this is implemented, the :connect_opts_proc is always called before a new connection is made, so directly before the connection is made, it will get the current password for the account. Note that for the jdbc adapter, because it generally works directly on JDBC URIs, the :connect_opts_proc should probably set the :uri key in the hash appropriately. --- CHANGELOG | 4 ++++ doc/opening_databases.rdoc | 3 +++ lib/sequel/database/connecting.rb | 5 +++++ lib/sequel/database/misc.rb | 3 +++ spec/core/database_spec.rb | 15 +++++++++++++++ 5 files changed, 30 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index f9a0ce592..1fc8dac8a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,7 @@ +=== master + +* Support :connect_opts_proc Database option for late binding options (jeremyevans) (#2164) + === 5.81.0 (2024-06-01) * Fix ignored block warnings in a couple plugin apply methods on Ruby 3.4 (jeremyevans) diff --git a/doc/opening_databases.rdoc b/doc/opening_databases.rdoc index 656ff6d5e..231a072e4 100644 --- a/doc/opening_databases.rdoc +++ b/doc/opening_databases.rdoc @@ -79,6 +79,9 @@ These options are shared by all adapters unless otherwise noted. or string with extensions separated by columns. These extensions are loaded after connections are made by the :preconnect option. :cache_schema :: Whether schema should be cached for this database (true by default) +:connect_opts_proc :: Callable object for modifying options hash used when connecting, designed for + cases where the option values (e.g. password) are automatically rotated on + a regular basis without involvement from the application using Sequel. :default_string_column_size :: The default size for string columns (255 by default) :host :: The hostname of the database server to which to connect :keep_reference :: Whether to keep a reference to the database in Sequel::DATABASES (true by default) diff --git a/lib/sequel/database/connecting.rb b/lib/sequel/database/connecting.rb index a7c659d34..6657b8a60 100644 --- a/lib/sequel/database/connecting.rb +++ b/lib/sequel/database/connecting.rb @@ -335,6 +335,11 @@ def server_opts(server) else @opts.dup end + + if pr = opts[:connect_opts_proc] + pr.call(opts) + end + opts.delete(:servers) opts end diff --git a/lib/sequel/database/misc.rb b/lib/sequel/database/misc.rb index f77e051e2..417bc465f 100644 --- a/lib/sequel/database/misc.rb +++ b/lib/sequel/database/misc.rb @@ -108,6 +108,9 @@ def self.uri_to_options(uri) # :cache_schema :: Whether schema should be cached for this Database instance # :check_string_typecast_bytesize :: Whether to check the bytesize of strings before typecasting. # :connect_sqls :: An array of sql strings to execute on each new connection, after :after_connect runs. + # :connect_opts_proc :: Callable object for modifying options hash used when connecting, designed for + # cases where the option values (e.g. password) are automatically rotated on + # a regular basis without involvement from the application using Sequel. # :default_string_column_size :: The default size of string columns, 255 by default. # :extensions :: Extensions to load into this Database instance. Can be a symbol, array of symbols, # or string with extensions separated by columns. These extensions are loaded after diff --git a/spec/core/database_spec.rb b/spec/core/database_spec.rb index 8b485c3d5..339294c97 100644 --- a/spec/core/database_spec.rb +++ b/spec/core/database_spec.rb @@ -23,6 +23,21 @@ Sequel::Database.new(1 => 2, :logger => [4], :loggers => [3]).loggers.must_equal [4,3] end + it "should support :connect_opts_proc option to modify options passed when connecting" do + c = Class.new(Sequel::Database) do + alias connect server_opts + end + opts = {:a => 1, :connect_opts_proc=>lambda{|h| h[:a] += 2; h[:b] = 4}} + c.new(opts).synchronize do |c| + c[:a].must_equal 3 + c[:b].must_equal 4 + end + c.new(opts.merge(:servers=>{:default=>{:a=>5}})).synchronize do |c| + c[:a].must_equal 7 + c[:b].must_equal 4 + end + end + it "should support :preconnect option to preconnect to database" do @db.pool.size.must_equal 0 c = Class.new(Sequel::Database) do