diff --git a/ext/couchbase b/ext/couchbase index ad4836b5..7446787a 160000 --- a/ext/couchbase +++ b/ext/couchbase @@ -1 +1 @@ -Subproject commit ad4836b55aedb1d475d5e87514f27f630d00913e +Subproject commit 7446787afc886c029c5a0f270aa2040a0ae6eddf diff --git a/ext/rcb_backend.cxx b/ext/rcb_backend.cxx index 87884940..c6edcc78 100644 --- a/ext/rcb_backend.cxx +++ b/ext/rcb_backend.cxx @@ -178,7 +178,8 @@ cb_Backend_allocate(VALUE klass) } auto -construct_cluster_options(VALUE credentials, bool tls_enabled) -> couchbase::cluster_options +construct_authenticator(VALUE credentials) + -> std::variant { cb_check_type(credentials, T_HASH); @@ -198,11 +199,30 @@ construct_cluster_options(VALUE credentials, bool tls_enabled) -> couchbase::clu cb_check_type(username, T_STRING); cb_check_type(password, T_STRING); - return cluster_options{ - password_authenticator{ - cb_string_new(username), - cb_string_new(password), - }, + return couchbase::password_authenticator{ + cb_string_new(username), + cb_string_new(password), + }; + } + + cb_check_type(certificate_path, T_STRING); + cb_check_type(key_path, T_STRING); + + return couchbase::certificate_authenticator{ + cb_string_new(certificate_path), + cb_string_new(key_path), + }; +} + +auto +construct_cluster_options(VALUE credentials, bool tls_enabled) -> couchbase::cluster_options +{ + std::variant + authenticator = construct_authenticator(credentials); + + if (std::holds_alternative(authenticator)) { + return couchbase::cluster_options{ + std::get(std::move(authenticator)), }; } @@ -212,14 +232,8 @@ construct_cluster_options(VALUE credentials, bool tls_enabled) -> couchbase::clu "Certificate authenticator requires TLS connection, check the connection string"); } - cb_check_type(certificate_path, T_STRING); - cb_check_type(key_path, T_STRING); - - return cluster_options{ - certificate_authenticator{ - cb_string_new(certificate_path), - cb_string_new(key_path), - }, + return couchbase::cluster_options{ + std::get(std::move(authenticator)), }; } @@ -610,6 +624,36 @@ cb_Backend_open_bucket(VALUE self, VALUE bucket, VALUE wait_until_ready) return Qnil; } +VALUE +cb_Backend_update_credentials(VALUE self, VALUE credentials) +{ + auto cluster = cb_backend_to_public_api_cluster(self); + + try { + std::variant + authenticator = construct_authenticator(credentials); + + couchbase::error err{}; + if (std::holds_alternative(authenticator)) { + err = cluster.set_authenticator( + std::get(std::move(authenticator))); + } else { + err = cluster.set_authenticator( + std::get(std::move(authenticator))); + } + if (err) { + cb_throw_error(err, "failed to update authenticator"); + } + } catch (const std::system_error& se) { + rb_exc_raise(cb_map_error_code( + se.code(), fmt::format("failed to update authenticator {}: {}", __func__, se.what()), false)); + } catch (const ruby_exception& e) { + rb_exc_raise(e.exception_object()); + } + + return Qnil; +} + } // namespace VALUE @@ -620,6 +664,7 @@ init_backend(VALUE mCouchbase) rb_define_method(cBackend, "open", cb_Backend_open, 3); rb_define_method(cBackend, "open_bucket", cb_Backend_open_bucket, 2); rb_define_method(cBackend, "close", cb_Backend_close, 0); + rb_define_method(cBackend, "update_credentials", cb_Backend_update_credentials, 1); rb_define_singleton_method(cBackend, "notify_fork", cb_Backend_notify_fork, 1); return cBackend; diff --git a/lib/couchbase/cluster.rb b/lib/couchbase/cluster.rb index 7f9ecae6..060e9c1d 100644 --- a/lib/couchbase/cluster.rb +++ b/lib/couchbase/cluster.rb @@ -90,6 +90,31 @@ def bucket(name) Bucket.new(@backend, name) end + def update_authenticator(authenticator) + credentials = {} + + case authenticator + when PasswordAuthenticator + credentials[:username] = authenticator.username + raise ArgumentError, "missing username" unless credentials[:username] + + credentials[:password] = authenticator.password + raise ArgumentError, "missing password" unless credentials[:password] + + when CertificateAuthenticator + credentials[:certificate_path] = authenticator.certificate_path + raise ArgumentError, "missing certificate path" unless credentials[:certificate_path] + + credentials[:key_path] = authenticator.key_path + raise ArgumentError, "missing key path" unless credentials[:key_path] + + else + raise ArgumentError, "argument must be an authenticator" + end + + @backend.update_credentials(credentials) + end + # Performs a query against the query (N1QL) services # # @param [String] statement the N1QL query statement @@ -332,22 +357,22 @@ def initialize(connection_string, *args) raise ArgumentError, "missing username" unless credentials[:username] raise ArgumentError, "missing password" unless credentials[:password] when Options::Cluster - open_options = options&.to_backend || {} - authenticator = options&.authenticator + open_options = options.to_backend || {} + authenticator = options.authenticator case authenticator when PasswordAuthenticator - credentials[:username] = authenticator&.username + credentials[:username] = authenticator.username raise ArgumentError, "missing username" unless credentials[:username] - credentials[:password] = authenticator&.password + credentials[:password] = authenticator.password raise ArgumentError, "missing password" unless credentials[:password] - open_options[:allowed_sasl_mechanisms] = authenticator&.allowed_sasl_mechanisms + open_options[:allowed_sasl_mechanisms] = authenticator.allowed_sasl_mechanisms when CertificateAuthenticator - credentials[:certificate_path] = authenticator&.certificate_path + credentials[:certificate_path] = authenticator.certificate_path raise ArgumentError, "missing certificate path" unless credentials[:certificate_path] - credentials[:key_path] = authenticator&.key_path + credentials[:key_path] = authenticator.key_path raise ArgumentError, "missing key path" unless credentials[:key_path] else