diff --git a/src/mongocxx/include/mongocxx/v1/data_key_options.hpp b/src/mongocxx/include/mongocxx/v1/data_key_options.hpp index 5275ffa417..f4a0b7d252 100644 --- a/src/mongocxx/include/mongocxx/v1/data_key_options.hpp +++ b/src/mongocxx/include/mongocxx/v1/data_key_options.hpp @@ -127,7 +127,9 @@ class data_key_options { /// /// Return the current "keyMaterial" field. /// - MONGOCXX_ABI_EXPORT_CDECL(bsoncxx::v1::stdx::optional) key_material(); + MONGOCXX_ABI_EXPORT_CDECL(bsoncxx::v1::stdx::optional) key_material() const; + + class internal; }; } // namespace v1 diff --git a/src/mongocxx/include/mongocxx/v_noabi/mongocxx/options/data_key-fwd.hpp b/src/mongocxx/include/mongocxx/v_noabi/mongocxx/options/data_key-fwd.hpp index e2b3a795ab..fb1aad9aa0 100644 --- a/src/mongocxx/include/mongocxx/v_noabi/mongocxx/options/data_key-fwd.hpp +++ b/src/mongocxx/include/mongocxx/v_noabi/mongocxx/options/data_key-fwd.hpp @@ -14,6 +14,8 @@ #pragma once +#include + #include namespace mongocxx { @@ -29,7 +31,7 @@ class data_key; namespace mongocxx { namespace options { -using ::mongocxx::v_noabi::options::data_key; +using v_noabi::options::data_key; } // namespace options } // namespace mongocxx @@ -40,3 +42,6 @@ using ::mongocxx::v_noabi::options::data_key; /// @file /// Declares @ref mongocxx::v_noabi::options::data_key. /// +/// @par Includes +/// - @ref mongocxx/v1/data_key_options-fwd.hpp +/// diff --git a/src/mongocxx/include/mongocxx/v_noabi/mongocxx/options/data_key.hpp b/src/mongocxx/include/mongocxx/v_noabi/mongocxx/options/data_key.hpp index 33d3d224b2..731051434b 100644 --- a/src/mongocxx/include/mongocxx/v_noabi/mongocxx/options/data_key.hpp +++ b/src/mongocxx/include/mongocxx/v_noabi/mongocxx/options/data_key.hpp @@ -14,12 +14,22 @@ #pragma once +#include // IWYU pragma: export + +// + +#include + +#include // IWYU pragma: export + +#include #include +#include #include -#include -#include // IWYU pragma: export +#include // IWYU pragma: keep: backward compatibility, to be removed. +#include #include #include @@ -34,6 +44,35 @@ namespace options { /// class data_key { public: + /// + /// Default initialization. + /// + data_key() = default; + + /// + /// Construct with the @ref mongocxx::v1 equivalent. + /// + /* explicit(false) */ MONGOCXX_ABI_EXPORT_CDECL() data_key(v1::data_key_options key); + + /// + /// Convert to the @ref mongocxx::v1 equivalent. + /// + explicit operator v1::data_key_options() const { + v1::data_key_options ret; + + if (_master_key) { + ret.master_key(bsoncxx::v1::document::value{bsoncxx::v_noabi::to_v1(_master_key->view())}); + } + + ret.key_alt_names(_key_alt_names); + + if (_key_material) { + ret.key_material(*_key_material); + } + + return ret; + } + /// /// Sets a KMS-specific key used to encrypt the new data key. /// @@ -99,8 +138,10 @@ class data_key { /// @see /// - https://www.mongodb.com/docs/manual/core/security-client-side-encryption-key-management/ /// - MONGOCXX_ABI_EXPORT_CDECL(data_key&) - master_key(bsoncxx::v_noabi::document::view_or_value master_key); + data_key& master_key(bsoncxx::v_noabi::document::view_or_value master_key) { + _master_key = std::move(master_key); + return *this; + } /// /// Gets the master key. @@ -108,8 +149,9 @@ class data_key { /// @return /// An optional document containing the master key. /// - MONGOCXX_ABI_EXPORT_CDECL(bsoncxx::v_noabi::stdx::optional const&) - master_key() const; + bsoncxx::v_noabi::stdx::optional const& master_key() const { + return _master_key; + } /// /// Sets an optional list of string alternate names used to reference the key. @@ -125,7 +167,10 @@ class data_key { /// @see /// - https://www.mongodb.com/docs/manual/reference/method/getClientEncryption/ /// - MONGOCXX_ABI_EXPORT_CDECL(data_key&) key_alt_names(std::vector key_alt_names); + data_key& key_alt_names(std::vector key_alt_names) { + _key_alt_names = std::move(key_alt_names); + return *this; + } /// /// Gets the alternate names for the data key. @@ -133,12 +178,14 @@ class data_key { /// @return /// The alternate names for the data key. /// - MONGOCXX_ABI_EXPORT_CDECL(std::vector const&) key_alt_names() const; + std::vector const& key_alt_names() const { + return _key_alt_names; + } /// /// Represents binary data used to represent key material. /// - using key_material_type = std::vector; + using key_material_type = std::vector; /// /// Sets the binary data for the key material @@ -159,7 +206,10 @@ class data_key { /// @see /// - https://www.mongodb.com/docs/v6.0/reference/method/KeyVault.createKey/ /// - MONGOCXX_ABI_EXPORT_CDECL(data_key&) key_material(key_material_type key_material); + data_key& key_material(key_material_type key_material) { + _key_material = std::move(key_material); + return *this; + } /// /// Gets the keyMaterial as binary data @@ -170,14 +220,13 @@ class data_key { /// @see /// - https://www.mongodb.com/docs/v6.0/reference/method/KeyVault.createKey/ /// - MONGOCXX_ABI_EXPORT_CDECL(bsoncxx::v_noabi::stdx::optional const&) - key_material(); + bsoncxx::v_noabi::stdx::optional const& key_material() { + return _key_material; + } - private: - friend ::mongocxx::v_noabi::client_encryption; - - void* convert() const; + class internal; + private: bsoncxx::v_noabi::stdx::optional _master_key; std::vector _key_alt_names; bsoncxx::v_noabi::stdx::optional _key_material; @@ -187,9 +236,31 @@ class data_key { } // namespace v_noabi } // namespace mongocxx +namespace mongocxx { +namespace v_noabi { + +/// +/// Convert to the @ref mongocxx::v_noabi equivalent of `v`. +/// +inline v_noabi::options::data_key from_v1(v1::data_key_options v) { + return {std::move(v)}; +} + +/// Convert to the @ref mongocxx::v1 equivalent of `v`. +/// +inline v1::data_key_options to_v1(v_noabi::options::data_key v) { + return v1::data_key_options{std::move(v)}; +} + +} // namespace v_noabi +} // namespace mongocxx + #include /// /// @file /// Provides @ref mongocxx::v_noabi::options::data_key. /// +/// @par Includes +/// - @ref mongocxx/v1/data_key_options.hpp +/// diff --git a/src/mongocxx/lib/mongocxx/v1/data_key_options.cpp b/src/mongocxx/lib/mongocxx/v1/data_key_options.cpp index 74a458e8f4..dbb548bbb4 100644 --- a/src/mongocxx/lib/mongocxx/v1/data_key_options.cpp +++ b/src/mongocxx/lib/mongocxx/v1/data_key_options.cpp @@ -12,4 +12,156 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include +#include + +// + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace mongocxx { +namespace v1 { + +class data_key_options::impl { + public: + bsoncxx::v1::stdx::optional _master_key; + std::vector _key_alt_names; + bsoncxx::v1::stdx::optional _key_material; + + static impl const& with(data_key_options const& self) { + return *static_cast(self._impl); + } + + static impl const* with(data_key_options const* self) { + return static_cast(self->_impl); + } + + static impl& with(data_key_options& self) { + return *static_cast(self._impl); + } + + static impl* with(data_key_options* self) { + return static_cast(self->_impl); + } + + static impl* with(void* ptr) { + return static_cast(ptr); + } +}; + +data_key_options::~data_key_options() { + delete impl::with(this); +} + +data_key_options::data_key_options(data_key_options&& other) noexcept : _impl{exchange(other._impl, nullptr)} {} + +data_key_options& data_key_options::operator=(data_key_options&& other) noexcept { + if (this != &other) { + delete impl::with(exchange(_impl, exchange(other._impl, nullptr))); + } + + return *this; +} + +data_key_options::data_key_options(data_key_options const& other) : _impl{new impl{impl::with(other)}} {} + +data_key_options& data_key_options::operator=(data_key_options const& other) { + if (this != &other) { + delete impl::with(exchange(_impl, new impl{impl::with(other)})); + } + + return *this; +} + +data_key_options::data_key_options() : _impl{new impl{}} {} + +data_key_options& data_key_options::master_key(bsoncxx::v1::document::value master_key) { + impl::with(this)->_master_key = std::move(master_key); + return *this; +} + +bsoncxx::v1::stdx::optional data_key_options::master_key() const { + return impl::with(this)->_master_key; +} + +data_key_options& data_key_options::key_alt_names(std::vector key_alt_names) { + impl::with(this)->_key_alt_names = std::move(key_alt_names); + return *this; +} + +std::vector data_key_options::key_alt_names() const { + return impl::with(this)->_key_alt_names; +} + +data_key_options& data_key_options::key_material(key_material_type key_material) { + impl::with(this)->_key_material = std::move(key_material); + return *this; +} + +bsoncxx::v1::stdx::optional data_key_options::key_material() const { + return impl::with(this)->_key_material; +} + +std::unique_ptr +data_key_options::internal::to_mongoc(data_key_options const& self) { + std::unique_ptr ret{ + libmongoc::client_encryption_datakey_opts_new()}; + + auto const opts = ret.get(); + auto& _impl = impl::with(self); + + if (auto const& opt = _impl._master_key) { + libmongoc::client_encryption_datakey_opts_set_masterkey(opts, scoped_bson_view{opt->view()}.bson()); + } + + if (!_impl._key_alt_names.empty()) { + std::vector names; + + names.reserve(_impl._key_alt_names.size()); + std::transform( + _impl._key_alt_names.begin(), + _impl._key_alt_names.end(), + std::back_inserter(names), + [](std::string const& name) { + return const_cast(name.c_str()); // For copy only. + }); + + libmongoc::client_encryption_datakey_opts_set_keyaltnames( + opts, names.data(), static_cast(_impl._key_alt_names.size())); + } + + if (auto const& opt = _impl._key_material) { + libmongoc::client_encryption_datakey_opts_set_keymaterial( + opts, opt->data(), static_cast(opt->size())); + } + + return ret; +} + +bsoncxx::v1::stdx::optional& data_key_options::internal::master_key( + data_key_options& self) { + return impl::with(self)._master_key; +} + +std::vector& data_key_options::internal::key_alt_names(data_key_options& self) { + return impl::with(self)._key_alt_names; +} + +bsoncxx::v1::stdx::optional& data_key_options::internal::key_material( + data_key_options& self) { + return impl::with(self)._key_material; +} + +} // namespace v1 +} // namespace mongocxx diff --git a/src/mongocxx/lib/mongocxx/v1/data_key_options.hh b/src/mongocxx/lib/mongocxx/v1/data_key_options.hh new file mode 100644 index 0000000000..b23a0b1814 --- /dev/null +++ b/src/mongocxx/lib/mongocxx/v1/data_key_options.hh @@ -0,0 +1,58 @@ +// Copyright 2009-present MongoDB, Inc. +// +// Licensed 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. + +#pragma once + +#include // IWYU pragma: export + +// + +#include + +#include + +#include +#include +#include + +#include +#include + +namespace mongocxx { +namespace v1 { + +class data_key_options::internal { + public: + struct deleter_type { + void operator()(mongoc_client_encryption_datakey_opts_t* opts) const noexcept { + libmongoc::client_encryption_datakey_opts_destroy(opts); + } + }; + + using unique_ptr_type = std::unique_ptr; + + static MONGOCXX_ABI_EXPORT_CDECL_TESTING(unique_ptr_type) to_mongoc(data_key_options const& self); + + static bsoncxx::v1::stdx::optional& master_key(data_key_options& self); + static std::vector& key_alt_names(data_key_options& self); + static bsoncxx::v1::stdx::optional& key_material(data_key_options& self); +}; + +} // namespace v1 +} // namespace mongocxx + +/// +/// @file +/// Provides @ref mongocxx::v1::data_key_options. +/// diff --git a/src/mongocxx/lib/mongocxx/v_noabi/mongocxx/client_encryption.hh b/src/mongocxx/lib/mongocxx/v_noabi/mongocxx/client_encryption.hh index 32bedd3e51..7795f71bda 100644 --- a/src/mongocxx/lib/mongocxx/v_noabi/mongocxx/client_encryption.hh +++ b/src/mongocxx/lib/mongocxx/v_noabi/mongocxx/client_encryption.hh @@ -25,15 +25,16 @@ #include // IWYU pragma: export #include +#include +#include +#include +#include #include #include #include -#include -#include #include -#include #include namespace mongocxx { @@ -74,24 +75,16 @@ class client_encryption::impl { bsoncxx::v_noabi::types::bson_value::value create_data_key( std::string kms_provider, - options::data_key const& opts) { - using opts_type = mongoc_client_encryption_datakey_opts_t; - - struct opts_deleter { - void operator()(opts_type* ptr) noexcept { - libmongoc::client_encryption_datakey_opts_destroy(ptr); - } - }; - - using datakey_opts_ptr = std::unique_ptr; - - auto const datakey_opts = datakey_opts_ptr(static_cast(opts.convert())); - + v_noabi::options::data_key const& opts) { detail::scoped_bson_value keyid; bson_error_t error; if (!libmongoc::client_encryption_create_datakey( - _client_encryption.get(), kms_provider.c_str(), datakey_opts.get(), keyid.value_for_init(), &error)) { + _client_encryption.get(), + kms_provider.c_str(), + v_noabi::options::data_key::internal::to_mongoc(opts).get(), + keyid.value_for_init(), + &error)) { throw_exception(error); } diff --git a/src/mongocxx/lib/mongocxx/v_noabi/mongocxx/options/data_key.cpp b/src/mongocxx/lib/mongocxx/v_noabi/mongocxx/options/data_key.cpp index 4a7d5369e3..656bb10b2c 100644 --- a/src/mongocxx/lib/mongocxx/v_noabi/mongocxx/options/data_key.cpp +++ b/src/mongocxx/lib/mongocxx/v_noabi/mongocxx/options/data_key.cpp @@ -12,71 +12,33 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include +#include -#include +// + +#include + +#include -#include +#include namespace mongocxx { namespace v_noabi { namespace options { -data_key& data_key::master_key(bsoncxx::v_noabi::document::view_or_value master_key) { - _master_key = std::move(master_key); - return *this; -} - -bsoncxx::v_noabi::stdx::optional const& data_key::master_key() const { - return _master_key; -} - -data_key& data_key::key_alt_names(std::vector key_alt_names) { - _key_alt_names = std::move(key_alt_names); - - return *this; -} - -std::vector const& data_key::key_alt_names() const { - return _key_alt_names; -} - -void* data_key::convert() const { - mongoc_client_encryption_datakey_opts_t* opts_t = libmongoc::client_encryption_datakey_opts_new(); - - if (_master_key) { - libmongoc::client_encryption_datakey_opts_set_masterkey(opts_t, to_scoped_bson_view(_master_key->view())); - } - - if (!_key_alt_names.empty()) { - auto altnames = _key_alt_names; - auto names = static_cast(bson_malloc(sizeof(char*) * altnames.size())); - uint32_t i = 0; - - for (auto&& name : altnames) { - names[i++] = const_cast(name.data()); - } - - libmongoc::client_encryption_datakey_opts_set_keyaltnames(opts_t, names, i); - - bson_free(names); - } - - if (_key_material) { - uint32_t size = static_cast(_key_material->size()); - libmongoc::client_encryption_datakey_opts_set_keymaterial(opts_t, _key_material->data(), size); - } - - return opts_t; -} - -data_key& data_key::key_material(data_key::key_material_type key_material) { - _key_material = std::move(key_material); - return *this; -} - -bsoncxx::v_noabi::stdx::optional const& data_key::key_material() { - return _key_material; +data_key::data_key(v1::data_key_options key) + : _master_key{[&]() -> decltype(_master_key) { + decltype(_master_key) ret; + if (auto& opt = v1::data_key_options::internal::master_key(key)) { + ret = bsoncxx::v_noabi::from_v1(std::move(*opt)); + } + return ret; + }()}, + _key_alt_names{std::move(v1::data_key_options::internal::key_alt_names(key))}, + _key_material{std::move(v1::data_key_options::internal::key_material(key))} {} + +v1::data_key_options::internal::unique_ptr_type data_key::internal::to_mongoc(data_key const& self) { + return v1::data_key_options::internal::to_mongoc(to_v1(self)); } } // namespace options diff --git a/src/mongocxx/lib/mongocxx/v_noabi/mongocxx/options/data_key.hh b/src/mongocxx/lib/mongocxx/v_noabi/mongocxx/options/data_key.hh new file mode 100644 index 0000000000..20e3911509 --- /dev/null +++ b/src/mongocxx/lib/mongocxx/v_noabi/mongocxx/options/data_key.hh @@ -0,0 +1,34 @@ +// Copyright 2009-present MongoDB, Inc. +// +// Licensed 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. + +#pragma once + +#include // IWYU pragma: export + +// + +#include + +namespace mongocxx { +namespace v_noabi { +namespace options { + +class data_key::internal { + public: + static v1::data_key_options::internal::unique_ptr_type to_mongoc(data_key const& self); +}; + +} // namespace options +} // namespace v_noabi +} // namespace mongocxx diff --git a/src/mongocxx/test/CMakeLists.txt b/src/mongocxx/test/CMakeLists.txt index d072dab439..012875045f 100644 --- a/src/mongocxx/test/CMakeLists.txt +++ b/src/mongocxx/test/CMakeLists.txt @@ -102,6 +102,7 @@ set(mongocxx_test_sources_v_noabi set(mongocxx_test_sources_v1 v1/bsoncxx.cpp + v1/data_key_options.cpp v1/exception.cpp v1/logger.cpp v1/read_concern.cpp diff --git a/src/mongocxx/test/v1/data_key_options.cpp b/src/mongocxx/test/v1/data_key_options.cpp new file mode 100644 index 0000000000..6172b196b7 --- /dev/null +++ b/src/mongocxx/test/v1/data_key_options.cpp @@ -0,0 +1,186 @@ +// Copyright 2009-present MongoDB, Inc. +// +// Licensed 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. + +#include + +// + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include + +namespace mongocxx { +namespace v1 { + +TEST_CASE("ownership", "[mongocxx][v1][data_key_options]") { + auto const source_key = scoped_bson{R"({"provider": "source"})"}; + auto const target_key = scoped_bson{R"({"provider": "target"})"}; + + data_key_options source; + data_key_options target; + + source.master_key(source_key.value()); + target.master_key(target_key.value()); + + SECTION("move") { + auto move = std::move(source); + + // source is in an assign-or-move-only state. + + CHECK(move.master_key() == source_key.view()); + + target = std::move(move); + + // source is in an assign-or-move-only state. + + CHECK(target.master_key() == source_key.view()); + } + + SECTION("copy") { + auto copy = source; + + CHECK(source.master_key() == source_key.view()); + CHECK(copy.master_key() == source_key.view()); + + target = copy; + + CHECK(copy.master_key() == source_key.view()); + CHECK(target.master_key() == source_key.view()); + } +} + +TEST_CASE("default", "[mongocxx][v1][data_key_options]") { + data_key_options const key; + + CHECK_FALSE(key.master_key().has_value()); + CHECK(key.key_alt_names().empty()); + CHECK_FALSE(key.key_material().has_value()); +} + +TEST_CASE("master_key", "[mongocxx][v1][data_key_options]") { + auto const v = GENERATE(values({ + scoped_bson{}, + scoped_bson{R"({"provider": "key"})"}, + })); + + CHECK(data_key_options{}.master_key(v.value()).master_key() == v.view()); +} + +TEST_CASE("key_alt_names", "[mongocxx][v1][data_key_options]") { + std::vector v; + + std::tie(v) = GENERATE( + table>({ + {}, + {{"x"}}, + {{"a", "b", "c"}}, + })); + + CHECK(data_key_options{}.key_alt_names(v).key_alt_names() == v); +} + +TEST_CASE("key_material", "[mongocxx][v1][data_key_options]") { + using type = data_key_options::key_material_type; + + type v; + + std::tie(v) = GENERATE( + table({ + {}, + {{1, 2, 3, 4, 5}}, + })); + + CHECK(data_key_options{}.key_material(v).key_material() == v); +} + +TEST_CASE("to_mongoc", "[mongocxx][v1][data_key_options]") { + data_key_options key; + + auto const master_key = scoped_bson{R"({" provider ": " master "})"}; + auto const key_alt_names = std::vector({"a", "b", "c"}); + auto const key_material = std::vector({1, 2, 3}); + + key.master_key(master_key.value()); + key.key_alt_names(key_alt_names); + key.key_material(key_material); + + auto const set_masterkey = libmongoc::client_encryption_datakey_opts_set_masterkey.create_instance(); + auto const set_keyaltnames = libmongoc::client_encryption_datakey_opts_set_keyaltnames.create_instance(); + auto const set_keymaterial = libmongoc::client_encryption_datakey_opts_set_keymaterial.create_instance(); + + mongoc_client_encryption_datakey_opts_t* masterkey_ptr = nullptr; + mongoc_client_encryption_datakey_opts_t* keyaltnames_ptr = nullptr; + mongoc_client_encryption_datakey_opts_t* keymaterial_ptr = nullptr; + + set_masterkey->interpose([&](mongoc_client_encryption_datakey_opts_t* ptr, bson_t const* masterkey) { + CHECK(ptr); + masterkey_ptr = ptr; + + REQUIRE(masterkey); + CHECK(scoped_bson_view{masterkey}.view() == master_key.view()); + }); + + set_keyaltnames->interpose( + [&](mongoc_client_encryption_datakey_opts_t* ptr, char** keyaltnames, std::uint32_t keyaltnames_count) { + CHECK(ptr); + keyaltnames_ptr = ptr; + + REQUIRE(keyaltnames); + REQUIRE(keyaltnames_count == key_alt_names.size()); + + for (std::uint32_t idx = 0u; idx < keyaltnames_count; ++idx) { + auto const name = keyaltnames[idx]; + REQUIRE(name); + CHECK(key_alt_names[idx] == name); + } + }); + + set_keymaterial->interpose( + [&](mongoc_client_encryption_datakey_opts_t* ptr, std::uint8_t const* data, std::uint32_t len) { + CHECK(ptr); + keymaterial_ptr = ptr; + + REQUIRE(data); + REQUIRE(len == key_material.size()); + + CHECK(std::vector(data, data + len) == key_material); + }); + + auto const opts = v1::data_key_options::internal::to_mongoc(key); + CHECK(opts.get() != nullptr); + CHECK(opts.get() == masterkey_ptr); + CHECK(opts.get() == keyaltnames_ptr); + CHECK(opts.get() == keymaterial_ptr); +} + +} // namespace v1 +} // namespace mongocxx