From 0394707dbafedda468d2b39e7009b9c0051be892 Mon Sep 17 00:00:00 2001 From: marcocapozzoli Date: Thu, 6 Aug 2026 09:23:30 -0300 Subject: [PATCH 01/21] WIP --- src/atomdb/AtomDB.h | 1 + src/atomdb/BUILD | 2 + src/atomdb/adapterdb/AdapterDB.cc | 5 + src/atomdb/adapterdb/AdapterDB.h | 2 + src/atomdb/auth/BUILD | 24 ++ src/atomdb/auth/ProtectedAtomDB.cc | 294 +++++++++++++++++++++++ src/atomdb/auth/ProtectedAtomDB.h | 157 ++++++++++++ src/atomdb/inmemorydb/InMemoryDB.h | 1 + src/atomdb/redis_mongodb/RedisMongoDB.cc | 12 + src/atomdb/redis_mongodb/RedisMongoDB.h | 5 + src/atomdb/remotedb/BUILD | 1 - src/atomdb/remotedb/RemoteAtomDB.cc | 9 + src/atomdb/remotedb/RemoteAtomDB.h | 1 + src/atomdb/remotedb/RemoteAtomDBPeer.cc | 4 + src/atomdb/remotedb/RemoteAtomDBPeer.h | 1 + 15 files changed, 518 insertions(+), 1 deletion(-) create mode 100644 src/atomdb/auth/BUILD create mode 100644 src/atomdb/auth/ProtectedAtomDB.cc create mode 100644 src/atomdb/auth/ProtectedAtomDB.h diff --git a/src/atomdb/AtomDB.h b/src/atomdb/AtomDB.h index babcc9135..224093896 100644 --- a/src/atomdb/AtomDB.h +++ b/src/atomdb/AtomDB.h @@ -23,6 +23,7 @@ class AtomDB : public HandleDecoder { virtual bool allow_nested_indexing() = 0; virtual bool composite_type_enabled() const = 0; + virtual bool is_protected() const = 0; virtual shared_ptr get_atom(const string& handle) = 0; // HandleDecoder interface virtual shared_ptr get_node(const string& handle) = 0; diff --git a/src/atomdb/BUILD b/src/atomdb/BUILD index bec174310..f4c937d43 100644 --- a/src/atomdb/BUILD +++ b/src/atomdb/BUILD @@ -12,6 +12,7 @@ cc_library( ":atomdb_singleton", ":atomdbutils", "//atomdb/adapterdb:adapterdb_lib", + "//atomdb/auth:protected_atomdb_lib", "//atomdb/inmemorydb:inmemorydb_lib", "//atomdb/morkdb:morkdb_lib", "//atomdb/redis_mongodb:redis_mongodb_lib", @@ -26,6 +27,7 @@ cc_library( includes = ["."], deps = [ ":atomdb", + "//atomdb/auth:protected_atomdb_lib", "//atomdb/inmemorydb", "//atomdb/morkdb", "//atomdb/redis_mongodb", diff --git a/src/atomdb/adapterdb/AdapterDB.cc b/src/atomdb/adapterdb/AdapterDB.cc index 3d782dd44..4c5855762 100644 --- a/src/atomdb/adapterdb/AdapterDB.cc +++ b/src/atomdb/adapterdb/AdapterDB.cc @@ -71,6 +71,11 @@ bool AdapterDB::composite_type_enabled() const { return this->atomdb_backend->composite_type_enabled(); } +bool AdapterDB::is_protected() const { + this->ensure_backend_ready(); + return this->atomdb_backend->is_protected(); +} + shared_ptr AdapterDB::get_atom(const string& handle) { this->ensure_backend_ready(); return this->atomdb_backend->get_atom(handle); diff --git a/src/atomdb/adapterdb/AdapterDB.h b/src/atomdb/adapterdb/AdapterDB.h index e5dc43104..b23ae8733 100644 --- a/src/atomdb/adapterdb/AdapterDB.h +++ b/src/atomdb/adapterdb/AdapterDB.h @@ -62,6 +62,8 @@ class AdapterDB : public AtomDB { */ bool composite_type_enabled() const override; + bool is_protected() const override; + shared_ptr get_atom(const string& handle) override; shared_ptr get_node(const string& handle) override; shared_ptr get_link(const string& handle) override; diff --git a/src/atomdb/auth/BUILD b/src/atomdb/auth/BUILD new file mode 100644 index 000000000..38928d78c --- /dev/null +++ b/src/atomdb/auth/BUILD @@ -0,0 +1,24 @@ +load("@rules_cc//cc:cc_library.bzl", "cc_library") + +package(default_visibility = ["//visibility:public"]) + +cc_library( + name = "protected_atomdb_lib", + includes = ["."], + deps = [ + ":protected_atomdb", + ], +) + +cc_library( + name = "protected_atomdb", + srcs = ["ProtectedAtomDB.cc"], + hdrs = ["ProtectedAtomDB.h"], + includes = ["."], + deps = [ + "//atomdb", + "//atomdb:atomdb_api_types", + "//commons:commons_lib", + "//commons/atoms:atoms_lib", + ], +) diff --git a/src/atomdb/auth/ProtectedAtomDB.cc b/src/atomdb/auth/ProtectedAtomDB.cc new file mode 100644 index 000000000..e82c220fc --- /dev/null +++ b/src/atomdb/auth/ProtectedAtomDB.cc @@ -0,0 +1,294 @@ +#include "ProtectedAtomDB.h" + +#include "Utils.h" + +using namespace atomdb; +using namespace commons; + +// -------------------------------------------------------------------------------- +// Constructors and destructors + +ProtectedAtomDB::ProtectedAtomDB(shared_ptr backend) : backend(std::move(backend)) { + if (this->backend == nullptr) { + RAISE_ERROR("ProtectedAtomDB requires a non-null backend AtomDB"); + } +} + +// -------------------------------------------------------------------------------- +// Public methods + +shared_ptr ProtectedAtomDB::get_atom(const string& handle, const string& public_key) { + RAISE_ERROR("ProtectedAtomDB::get_atom(handle, public_key) is not implemented yet"); +} + +shared_ptr ProtectedAtomDB::get_node(const string& handle, const string& public_key) { + RAISE_ERROR("ProtectedAtomDB::get_node(handle, public_key) is not implemented yet"); +} + +shared_ptr ProtectedAtomDB::get_link(const string& handle, const string& public_key) { + RAISE_ERROR("ProtectedAtomDB::get_link(handle, public_key) is not implemented yet"); +} + +vector> ProtectedAtomDB::get_matching_atoms(bool is_toplevel, + Atom& key, + const string& public_key) { + RAISE_ERROR("ProtectedAtomDB::get_matching_atoms(..., public_key) is not implemented yet"); +} + +shared_ptr ProtectedAtomDB::query_for_pattern(const LinkSchema& link_schema, + const string& public_key) { + RAISE_ERROR("ProtectedAtomDB::query_for_pattern(link_schema, public_key) is not implemented yet"); +} + +shared_ptr ProtectedAtomDB::query_for_targets(const string& handle, + const string& public_key) { + RAISE_ERROR("ProtectedAtomDB::query_for_targets(handle, public_key) is not implemented yet"); +} + +shared_ptr ProtectedAtomDB::query_for_incoming_set( + const string& handle, const string& public_key) { + RAISE_ERROR("ProtectedAtomDB::query_for_incoming_set(handle, public_key) is not implemented yet"); +} + +bool ProtectedAtomDB::atom_exists(const string& handle, const string& public_key) { + RAISE_ERROR("ProtectedAtomDB::atom_exists(handle, public_key) is not implemented yet"); +} + +bool ProtectedAtomDB::node_exists(const string& handle, const string& public_key) { + RAISE_ERROR("ProtectedAtomDB::node_exists(handle, public_key) is not implemented yet"); +} + +bool ProtectedAtomDB::link_exists(const string& handle, const string& public_key) { + RAISE_ERROR("ProtectedAtomDB::link_exists(handle, public_key) is not implemented yet"); +} + +set ProtectedAtomDB::atoms_exist(const vector& handles, const string& public_key) { + RAISE_ERROR("ProtectedAtomDB::atoms_exist(handles, public_key) is not implemented yet"); +} + +set ProtectedAtomDB::nodes_exist(const vector& handles, const string& public_key) { + RAISE_ERROR("ProtectedAtomDB::nodes_exist(handles, public_key) is not implemented yet"); +} + +set ProtectedAtomDB::links_exist(const vector& handles, const string& public_key) { + RAISE_ERROR("ProtectedAtomDB::links_exist(handles, public_key) is not implemented yet"); +} + +string ProtectedAtomDB::add_atom(const atoms::Atom* atom, + const string& public_key, + const atoms::Merger* merger) { + RAISE_ERROR("ProtectedAtomDB::add_atom(atom, public_key) is not implemented yet"); +} + +string ProtectedAtomDB::add_node(const atoms::Node* node, + const string& public_key, + const atoms::Merger* merger) { + RAISE_ERROR("ProtectedAtomDB::add_node(node, public_key) is not implemented yet"); +} + +string ProtectedAtomDB::add_link(const atoms::Link* link, + const string& public_key, + const atoms::Merger* merger) { + RAISE_ERROR("ProtectedAtomDB::add_link(link, public_key) is not implemented yet"); +} + +vector ProtectedAtomDB::add_atoms(const vector& atom_list, + const string& public_key, + bool is_transactional, + const atoms::Merger* merger) { + RAISE_ERROR("ProtectedAtomDB::add_atoms(atom_list, public_key) is not implemented yet"); +} + +vector ProtectedAtomDB::add_nodes(const vector& nodes, + const string& public_key, + bool is_transactional, + const atoms::Merger* merger) { + RAISE_ERROR("ProtectedAtomDB::add_nodes(nodes, public_key) is not implemented yet"); +} + +vector ProtectedAtomDB::add_links(const vector& links, + const string& public_key, + bool is_transactional, + const atoms::Merger* merger) { + RAISE_ERROR("ProtectedAtomDB::add_links(links, public_key) is not implemented yet"); +} + +bool ProtectedAtomDB::delete_atom(const string& handle, + const string& public_key, + bool delete_link_targets) { + RAISE_ERROR("ProtectedAtomDB::delete_atom(handle, public_key) is not implemented yet"); +} + +bool ProtectedAtomDB::delete_node(const string& handle, + const string& public_key, + bool delete_link_targets) { + RAISE_ERROR("ProtectedAtomDB::delete_node(handle, public_key) is not implemented yet"); +} + +bool ProtectedAtomDB::delete_link(const string& handle, + const string& public_key, + bool delete_link_targets) { + RAISE_ERROR("ProtectedAtomDB::delete_link(handle, public_key) is not implemented yet"); +} + +uint ProtectedAtomDB::delete_atoms(const vector& handles, + const string& public_key, + bool delete_link_targets) { + RAISE_ERROR("ProtectedAtomDB::delete_atoms(handles, public_key) is not implemented yet"); +} + +uint ProtectedAtomDB::delete_nodes(const vector& handles, + const string& public_key, + bool delete_link_targets) { + RAISE_ERROR("ProtectedAtomDB::delete_nodes(handles, public_key) is not implemented yet"); +} + +uint ProtectedAtomDB::delete_links(const vector& handles, + const string& public_key, + bool delete_link_targets) { + RAISE_ERROR("ProtectedAtomDB::delete_links(handles, public_key) is not implemented yet"); +} + +void ProtectedAtomDB::re_index_patterns(const string& public_key, bool flush_patterns) { + RAISE_ERROR("ProtectedAtomDB::re_index_patterns(public_key) is not implemented yet"); +} + +size_t ProtectedAtomDB::node_count(const string& public_key) const { + RAISE_ERROR("ProtectedAtomDB::node_count(public_key) is not implemented yet"); +} + +size_t ProtectedAtomDB::link_count(const string& public_key) const { + RAISE_ERROR("ProtectedAtomDB::link_count(public_key) is not implemented yet"); +} + +size_t ProtectedAtomDB::atom_count(const string& public_key) const { + RAISE_ERROR("ProtectedAtomDB::atom_count(public_key) is not implemented yet"); +} + +bool ProtectedAtomDB::allow_nested_indexing() { return this->backend->allow_nested_indexing(); } + +bool ProtectedAtomDB::composite_type_enabled() const { return this->backend->composite_type_enabled(); } + +bool ProtectedAtomDB::is_protected() const { return true; } + +// -------------------------------------------------------------------------------- +// Public methods (without public_key - reject the call) + +shared_ptr ProtectedAtomDB::get_atom(const string& handle) { + raise_public_key_required("get_atom"); +} + +shared_ptr ProtectedAtomDB::get_node(const string& handle) { + raise_public_key_required("get_node"); +} + +shared_ptr ProtectedAtomDB::get_link(const string& handle) { + raise_public_key_required("get_link"); +} + +vector> ProtectedAtomDB::get_matching_atoms(bool is_toplevel, Atom& key) { + raise_public_key_required("get_matching_atoms"); +} + +shared_ptr ProtectedAtomDB::query_for_pattern( + const LinkSchema& link_schema) { + raise_public_key_required("query_for_pattern"); +} + +shared_ptr ProtectedAtomDB::query_for_targets(const string& handle) { + raise_public_key_required("query_for_targets"); +} + +shared_ptr ProtectedAtomDB::query_for_incoming_set(const string& handle) { + raise_public_key_required("query_for_incoming_set"); +} + +bool ProtectedAtomDB::atom_exists(const string& handle) { raise_public_key_required("atom_exists"); } + +bool ProtectedAtomDB::node_exists(const string& handle) { raise_public_key_required("node_exists"); } + +bool ProtectedAtomDB::link_exists(const string& handle) { raise_public_key_required("link_exists"); } + +set ProtectedAtomDB::atoms_exist(const vector& handles) { + raise_public_key_required("atoms_exist"); +} + +set ProtectedAtomDB::nodes_exist(const vector& handles) { + raise_public_key_required("nodes_exist"); +} + +set ProtectedAtomDB::links_exist(const vector& handles) { + raise_public_key_required("links_exist"); +} + +string ProtectedAtomDB::add_atom(const atoms::Atom* atom, const atoms::Merger* merger) { + raise_public_key_required("add_atom"); +} + +string ProtectedAtomDB::add_node(const atoms::Node* node, const atoms::Merger* merger) { + raise_public_key_required("add_node"); +} + +string ProtectedAtomDB::add_link(const atoms::Link* link, const atoms::Merger* merger) { + raise_public_key_required("add_link"); +} + +vector ProtectedAtomDB::add_atoms(const vector& atom_list, + bool is_transactional, + const atoms::Merger* merger) { + raise_public_key_required("add_atoms"); +} + +vector ProtectedAtomDB::add_nodes(const vector& nodes, + bool is_transactional, + const atoms::Merger* merger) { + raise_public_key_required("add_nodes"); +} + +vector ProtectedAtomDB::add_links(const vector& links, + bool is_transactional, + const atoms::Merger* merger) { + raise_public_key_required("add_links"); +} + +bool ProtectedAtomDB::delete_atom(const string& handle, bool delete_link_targets) { + raise_public_key_required("delete_atom"); +} + +bool ProtectedAtomDB::delete_node(const string& handle, bool delete_link_targets) { + raise_public_key_required("delete_node"); +} + +bool ProtectedAtomDB::delete_link(const string& handle, bool delete_link_targets) { + raise_public_key_required("delete_link"); +} + +uint ProtectedAtomDB::delete_atoms(const vector& handles, bool delete_link_targets) { + raise_public_key_required("delete_atoms"); +} + +uint ProtectedAtomDB::delete_nodes(const vector& handles, bool delete_link_targets) { + raise_public_key_required("delete_nodes"); +} + +uint ProtectedAtomDB::delete_links(const vector& handles, bool delete_link_targets) { + raise_public_key_required("delete_links"); +} + +void ProtectedAtomDB::re_index_patterns(bool flush_patterns) { + raise_public_key_required("re_index_patterns"); +} + +size_t ProtectedAtomDB::node_count() const { raise_public_key_required("node_count"); } + +size_t ProtectedAtomDB::link_count() const { raise_public_key_required("link_count"); } + +size_t ProtectedAtomDB::atom_count() const { raise_public_key_required("atom_count"); } + +// -------------------------------------------------------------------------------- +// Private methods + +void ProtectedAtomDB::raise_public_key_required(const string& method_name) { + RAISE_ERROR("ProtectedAtomDB::" + method_name + + "() is unavailable without a public_key; use the overload that accepts a public_key"); +} diff --git a/src/atomdb/auth/ProtectedAtomDB.h b/src/atomdb/auth/ProtectedAtomDB.h new file mode 100644 index 000000000..456ecb64b --- /dev/null +++ b/src/atomdb/auth/ProtectedAtomDB.h @@ -0,0 +1,157 @@ +#pragma once + +#include +#include +#include +#include + +#include "AtomDB.h" + +using namespace std; +using namespace atoms; + +namespace atomdb { + +/** + * @brief Authorization wrapper around any AtomDB backend for protected databases. + * + * Data-access methods expose two forms: + * - overloads without public_key: reject the call (protected access requires a key) + * - overloads with public_key: authorize and delegate to the backend + * + */ +class ProtectedAtomDB : public AtomDB { + public: + /** + * @param backend Shared concrete AtomDB to wrap. + */ + explicit ProtectedAtomDB(shared_ptr backend); + + bool allow_nested_indexing() override; + bool composite_type_enabled() const override; + bool is_protected() const override; + + shared_ptr get_atom(const string& handle) override; + shared_ptr get_atom(const string& handle, const string& public_key); + + shared_ptr get_node(const string& handle) override; + shared_ptr get_node(const string& handle, const string& public_key); + + shared_ptr get_link(const string& handle) override; + shared_ptr get_link(const string& handle, const string& public_key); + + vector> get_matching_atoms(bool is_toplevel, Atom& key) override; + vector> get_matching_atoms(bool is_toplevel, Atom& key, const string& public_key); + + shared_ptr query_for_pattern(const LinkSchema& link_schema) override; + shared_ptr query_for_pattern(const LinkSchema& link_schema, + const string& public_key); + + shared_ptr query_for_targets(const string& handle) override; + shared_ptr query_for_targets(const string& handle, + const string& public_key); + + shared_ptr query_for_incoming_set(const string& handle) override; + shared_ptr query_for_incoming_set(const string& handle, + const string& public_key); + + bool atom_exists(const string& handle) override; + bool atom_exists(const string& handle, const string& public_key); + + bool node_exists(const string& handle) override; + bool node_exists(const string& handle, const string& public_key); + + bool link_exists(const string& handle) override; + bool link_exists(const string& handle, const string& public_key); + + set atoms_exist(const vector& handles) override; + set atoms_exist(const vector& handles, const string& public_key); + + set nodes_exist(const vector& handles) override; + set nodes_exist(const vector& handles, const string& public_key); + + set links_exist(const vector& handles) override; + set links_exist(const vector& handles, const string& public_key); + + string add_atom(const atoms::Atom* atom, const atoms::Merger* merger = NULL) override; + string add_atom(const atoms::Atom* atom, + const string& public_key, + const atoms::Merger* merger = NULL); + + string add_node(const atoms::Node* node, const atoms::Merger* merger = NULL) override; + string add_node(const atoms::Node* node, + const string& public_key, + const atoms::Merger* merger = NULL); + + string add_link(const atoms::Link* link, const atoms::Merger* merger = NULL) override; + string add_link(const atoms::Link* link, + const string& public_key, + const atoms::Merger* merger = NULL); + + vector add_atoms(const vector& atom_list, + bool is_transactional = false, + const atoms::Merger* merger = NULL) override; + vector add_atoms(const vector& atom_list, + const string& public_key, + bool is_transactional = false, + const atoms::Merger* merger = NULL); + + vector add_nodes(const vector& nodes, + bool is_transactional = false, + const atoms::Merger* merger = NULL) override; + vector add_nodes(const vector& nodes, + const string& public_key, + bool is_transactional = false, + const atoms::Merger* merger = NULL); + + vector add_links(const vector& links, + bool is_transactional = false, + const atoms::Merger* merger = NULL) override; + vector add_links(const vector& links, + const string& public_key, + bool is_transactional = false, + const atoms::Merger* merger = NULL); + + bool delete_atom(const string& handle, bool delete_link_targets = false) override; + bool delete_atom(const string& handle, const string& public_key, bool delete_link_targets = false); + + bool delete_node(const string& handle, bool delete_link_targets = false) override; + bool delete_node(const string& handle, const string& public_key, bool delete_link_targets = false); + + bool delete_link(const string& handle, bool delete_link_targets = false) override; + bool delete_link(const string& handle, const string& public_key, bool delete_link_targets = false); + + uint delete_atoms(const vector& handles, bool delete_link_targets = false) override; + uint delete_atoms(const vector& handles, + const string& public_key, + bool delete_link_targets = false); + + uint delete_nodes(const vector& handles, bool delete_link_targets = false) override; + uint delete_nodes(const vector& handles, + const string& public_key, + bool delete_link_targets = false); + + uint delete_links(const vector& handles, bool delete_link_targets = false) override; + uint delete_links(const vector& handles, + const string& public_key, + bool delete_link_targets = false); + + void re_index_patterns(bool flush_patterns = true) override; + void re_index_patterns(const string& public_key, bool flush_patterns = true); + + size_t node_count() const override; + size_t node_count(const string& public_key) const; + + size_t link_count() const override; + size_t link_count(const string& public_key) const; + + size_t atom_count() const override; + size_t atom_count(const string& public_key) const; + + private: + shared_ptr backend; + + [[noreturn]] static void raise_public_key_required(const string& method_name); +}; + +} // namespace atomdb diff --git a/src/atomdb/inmemorydb/InMemoryDB.h b/src/atomdb/inmemorydb/InMemoryDB.h index 174734432..4563605cc 100644 --- a/src/atomdb/inmemorydb/InMemoryDB.h +++ b/src/atomdb/inmemorydb/InMemoryDB.h @@ -23,6 +23,7 @@ class InMemoryDB : public AtomDB { bool allow_nested_indexing() override; bool composite_type_enabled() const override { return false; } + bool is_protected() const override { return false; } shared_ptr get_atom(const string& handle) override; shared_ptr get_node(const string& handle) override; diff --git a/src/atomdb/redis_mongodb/RedisMongoDB.cc b/src/atomdb/redis_mongodb/RedisMongoDB.cc index 80e653ea0..f2ce4c6b4 100644 --- a/src/atomdb/redis_mongodb/RedisMongoDB.cc +++ b/src/atomdb/redis_mongodb/RedisMongoDB.cc @@ -31,6 +31,7 @@ uint RedisMongoDB::REDIS_CHUNK_SIZE; string RedisMongoDB::MONGODB_DB_NAME; string RedisMongoDB::MONGODB_NODES_COLLECTION_NAME; string RedisMongoDB::MONGODB_LINKS_COLLECTION_NAME; +string RedisMongoDB::MONGODB_CONFIG_COLLECTION_NAME; string RedisMongoDB::MONGODB_PATTERN_INDEX_SCHEMA_COLLECTION_NAME; string RedisMongoDB::MONGODB_FIELD_NAME[MONGODB_FIELD::size]; uint RedisMongoDB::MONGODB_CHUNK_SIZE; @@ -55,6 +56,8 @@ RedisMongoDB::~RedisMongoDB() { bool RedisMongoDB::allow_nested_indexing() { return false; } +bool RedisMongoDB::is_protected() const { return this->protected_flag; } + void RedisMongoDB::redis_setup(const JsonConfig& config) { if (skip_redis_) return; @@ -105,6 +108,7 @@ void RedisMongoDB::mongodb_setup(const JsonConfig& config) { bsoncxx::builder::basic::make_document(bsoncxx::builder::basic::kvp("ping", 1)); mongodb.run_command(ping_cmd.view()); LOG_INFO("Connected to MongoDB at " << address); + load_protected_flag(); } catch (const std::exception& e) { RAISE_ERROR(e.what()); } @@ -1230,6 +1234,14 @@ void RedisMongoDB::add_pattern_index_schema(const string& tokens, this->pattern_index_schema_next_priority++; } +void RedisMongoDB::load_protected_flag() { + auto conn = this->mongodb_pool->acquire(); + auto config_collection = (*conn)[MONGODB_DB_NAME][MONGODB_CONFIG_COLLECTION_NAME]; + auto config_doc = config_collection.find_one( + bsoncxx::builder::basic::make_document(bsoncxx::builder::basic::kvp("protected", true))); + this->protected_flag = static_cast(config_doc); +} + void RedisMongoDB::load_pattern_index_schema() { this->pattern_index_schema_map.clear(); auto conn = this->mongodb_pool->acquire(); diff --git a/src/atomdb/redis_mongodb/RedisMongoDB.h b/src/atomdb/redis_mongodb/RedisMongoDB.h index 2e790d80d..03cc68ecc 100644 --- a/src/atomdb/redis_mongodb/RedisMongoDB.h +++ b/src/atomdb/redis_mongodb/RedisMongoDB.h @@ -32,6 +32,7 @@ class RedisMongoDB : public AtomDB { bool allow_nested_indexing() override; bool composite_type_enabled() const override { return this->composite_type_enabled_; } + bool is_protected() const override; static string REDIS_PATTERNS_PREFIX; static string REDIS_OUTGOING_PREFIX; @@ -40,6 +41,7 @@ class RedisMongoDB : public AtomDB { static string MONGODB_DB_NAME; static string MONGODB_NODES_COLLECTION_NAME; static string MONGODB_LINKS_COLLECTION_NAME; + static string MONGODB_CONFIG_COLLECTION_NAME; static string MONGODB_PATTERN_INDEX_SCHEMA_COLLECTION_NAME; static string MONGODB_FIELD_NAME[MONGODB_FIELD::size]; static uint MONGODB_CHUNK_SIZE; @@ -52,6 +54,7 @@ class RedisMongoDB : public AtomDB { MONGODB_DB_NAME = context + "das"; MONGODB_NODES_COLLECTION_NAME = context + "nodes"; MONGODB_LINKS_COLLECTION_NAME = context + "links"; + MONGODB_CONFIG_COLLECTION_NAME = context + "config"; MONGODB_PATTERN_INDEX_SCHEMA_COLLECTION_NAME = context + "pattern_index_schema"; MONGODB_FIELD_NAME[MONGODB_FIELD::ID] = "_id"; MONGODB_FIELD_NAME[MONGODB_FIELD::TARGETS] = "targets"; @@ -154,6 +157,7 @@ class RedisMongoDB : public AtomDB { bool skip_redis_; bool composite_type_enabled_; bool cluster_flag; + bool protected_flag; RedisContextPool* redis_pool; mongocxx::pool* mongodb_pool; atomic patterns_next_score{0}; @@ -196,6 +200,7 @@ class RedisMongoDB : public AtomDB { void update_incoming_set(const string& key, const string& value); void load_pattern_index_schema(); + void load_protected_flag(); vector match_pattern_index_schema(const Link* link); vector> index_entries_combinations(unsigned int arity); diff --git a/src/atomdb/remotedb/BUILD b/src/atomdb/remotedb/BUILD index 5a5f17d69..1addcd6e9 100644 --- a/src/atomdb/remotedb/BUILD +++ b/src/atomdb/remotedb/BUILD @@ -25,7 +25,6 @@ cc_library( "//atomdb", "//atomdb:atomdb_api_types", "//atomdb:atomdb_factory", - "//atomdb/inmemorydb", "//atomdb/inmemorydb:inmemorydb_api_types", "//commons:commons_lib", "//commons/atoms:atoms_lib", diff --git a/src/atomdb/remotedb/RemoteAtomDB.cc b/src/atomdb/remotedb/RemoteAtomDB.cc index 79ea95c03..b426e185b 100644 --- a/src/atomdb/remotedb/RemoteAtomDB.cc +++ b/src/atomdb/remotedb/RemoteAtomDB.cc @@ -66,6 +66,15 @@ bool RemoteAtomDB::composite_type_enabled() const { return false; } +bool RemoteAtomDB::is_protected() const { + for (auto& [uid, peer] : remote_db_) { + if (peer->is_protected()) { + return true; + } + } + return false; +} + void RemoteAtomDB::derive_nested_indexing() { // Derive the aggregated nested-indexing capability from the peers. A single global boolean // cannot describe a heterogeneous result set, so mixed configurations are normalized to the diff --git a/src/atomdb/remotedb/RemoteAtomDB.h b/src/atomdb/remotedb/RemoteAtomDB.h index d1de2218f..e2912c933 100644 --- a/src/atomdb/remotedb/RemoteAtomDB.h +++ b/src/atomdb/remotedb/RemoteAtomDB.h @@ -29,6 +29,7 @@ class RemoteAtomDB : public AtomDB { bool allow_nested_indexing() override; bool composite_type_enabled() const override; + bool is_protected() const override; shared_ptr get_atom(const string& handle) override; shared_ptr get_node(const string& handle) override; diff --git a/src/atomdb/remotedb/RemoteAtomDBPeer.cc b/src/atomdb/remotedb/RemoteAtomDBPeer.cc index 669d8f272..05a041007 100644 --- a/src/atomdb/remotedb/RemoteAtomDBPeer.cc +++ b/src/atomdb/remotedb/RemoteAtomDBPeer.cc @@ -40,6 +40,10 @@ bool RemoteAtomDBPeer::composite_type_enabled() const { return local_persistence_ && local_persistence_->composite_type_enabled(); } +bool RemoteAtomDBPeer::is_protected() const { + return (local_persistence_ && local_persistence_->is_protected()) || + (atomdb_ && atomdb_->is_protected()); +} shared_ptr RemoteAtomDBPeer::get_atom(const string& handle) { auto atom = cache_.get_atom(handle); if (atom) return atom; diff --git a/src/atomdb/remotedb/RemoteAtomDBPeer.h b/src/atomdb/remotedb/RemoteAtomDBPeer.h index 713c498dc..9305329a6 100644 --- a/src/atomdb/remotedb/RemoteAtomDBPeer.h +++ b/src/atomdb/remotedb/RemoteAtomDBPeer.h @@ -30,6 +30,7 @@ class RemoteAtomDBPeer : public AtomDB, public processor::ThreadMethod { bool allow_nested_indexing() override; bool composite_type_enabled() const override; + bool is_protected() const override; shared_ptr get_atom(const string& handle) override; shared_ptr get_node(const string& handle) override; From 92e65cccd4cff9a1c39f1d789069495d14b937d3 Mon Sep 17 00:00:00 2001 From: marcocapozzoli Date: Thu, 6 Aug 2026 10:09:30 -0300 Subject: [PATCH 02/21] implement wrap_is_protected() --- src/atomdb/AtomDBFactory.cc | 12 ++++++++++-- src/atomdb/auth/ProtectedAtomDB.cc | 2 ++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/atomdb/AtomDBFactory.cc b/src/atomdb/AtomDBFactory.cc index 01b7e65f1..8ee3d141f 100644 --- a/src/atomdb/AtomDBFactory.cc +++ b/src/atomdb/AtomDBFactory.cc @@ -33,6 +33,14 @@ shared_ptr AtomDBFactory::create_backend(const JsonConfig& config, const } shared_ptr AtomDBFactory::wrap_if_protected(shared_ptr backend) { - // AtomDBFactory::wrap_if_protected() is not implemented yet. - return backend; + if (!backend) { + RAISE_ERROR("AtomDBFactory::wrap_if_protected() received null backend"); + } + if (!backend->is_protected()) { + return backend; + } + if (dynamic_pointer_cast(backend)) { + return backend; + } + return shared_ptr(new ProtectedAtomDB(backend)); } \ No newline at end of file diff --git a/src/atomdb/auth/ProtectedAtomDB.cc b/src/atomdb/auth/ProtectedAtomDB.cc index e82c220fc..1a4af79a9 100644 --- a/src/atomdb/auth/ProtectedAtomDB.cc +++ b/src/atomdb/auth/ProtectedAtomDB.cc @@ -1,5 +1,6 @@ #include "ProtectedAtomDB.h" +#include "Logger.h" #include "Utils.h" using namespace atomdb; @@ -12,6 +13,7 @@ ProtectedAtomDB::ProtectedAtomDB(shared_ptr backend) : backend(std::move if (this->backend == nullptr) { RAISE_ERROR("ProtectedAtomDB requires a non-null backend AtomDB"); } + LOG_INFO("ProtectedAtomDB initialized!"); } // -------------------------------------------------------------------------------- From a3be60c92ee16588e6f0d4df8154ef96b731c35e Mon Sep 17 00:00:00 2001 From: marcocapozzoli Date: Thu, 6 Aug 2026 10:26:24 -0300 Subject: [PATCH 03/21] Add tests --- src/atomdb/AtomDBFactory.cc | 1 + src/tests/cpp/BUILD | 18 +++++ src/tests/cpp/atomdb_factory_test.cc | 42 +++++++++--- src/tests/cpp/protected_atomdb_test.cc | 67 +++++++++++++++++++ src/tests/cpp/test_commons/mocks/MockAtomDB.h | 2 + 5 files changed, 122 insertions(+), 8 deletions(-) create mode 100644 src/tests/cpp/protected_atomdb_test.cc diff --git a/src/atomdb/AtomDBFactory.cc b/src/atomdb/AtomDBFactory.cc index 8ee3d141f..4f15d20b5 100644 --- a/src/atomdb/AtomDBFactory.cc +++ b/src/atomdb/AtomDBFactory.cc @@ -2,6 +2,7 @@ #include "InMemoryDB.h" #include "MorkDB.h" +#include "ProtectedAtomDB.h" #include "RedisMongoDB.h" #include "Utils.h" diff --git a/src/tests/cpp/BUILD b/src/tests/cpp/BUILD index aa28542ca..dbf5ca1e7 100644 --- a/src/tests/cpp/BUILD +++ b/src/tests/cpp/BUILD @@ -850,6 +850,23 @@ cc_test( ], ) +cc_test( + name = "protected_atomdb_test", + size = "small", + srcs = ["protected_atomdb_test.cc"], + copts = [ + "-Iexternal/gtest/googletest/include", + "-Iexternal/gtest/googletest", + ], + linkstatic = 1, + deps = [ + "//atomdb/auth:protected_atomdb_lib", + "//atomdb/inmemorydb:inmemorydb_lib", + "//commons/atoms:atoms_lib", + "@com_github_google_googletest//:gtest_main", + ], +) + cc_test( name = "atomdb_factory_test", size = "medium", @@ -868,6 +885,7 @@ cc_test( linkstatic = 1, deps = [ "//atomdb:atomdb_factory", + "//atomdb/auth:protected_atomdb_lib", "//atomdb/inmemorydb:inmemorydb_lib", "//atomdb/morkdb:morkdb_lib", "//tests/cpp/test_commons:test_atomdb_json_config", diff --git a/src/tests/cpp/atomdb_factory_test.cc b/src/tests/cpp/atomdb_factory_test.cc index d786e36c4..66f5fe945 100644 --- a/src/tests/cpp/atomdb_factory_test.cc +++ b/src/tests/cpp/atomdb_factory_test.cc @@ -8,6 +8,7 @@ #include "JsonConfig.h" #include "MockAtomDB.h" #include "MorkDB.h" +#include "ProtectedAtomDB.h" #include "TestAtomDBJsonConfig.h" using namespace atomdb; @@ -57,9 +58,14 @@ TEST(AtomDBFactoryTest, CreateAndCreateBackendAreCompatibleForMorkDB) { ASSERT_NE(backend, nullptr); ASSERT_NE(created, nullptr); EXPECT_NE(dynamic_pointer_cast(backend), nullptr); - EXPECT_NE(dynamic_pointer_cast(created), nullptr); - // wrap_if_protected is currently a no-op, so create keeps the backend type. - EXPECT_EQ(AtomDBFactory::wrap_if_protected(backend).get(), backend.get()); + // create() only wraps when the backend is protected. + if (backend->is_protected()) { + EXPECT_NE(dynamic_pointer_cast(created), nullptr); + EXPECT_TRUE(created->is_protected()); + } else { + EXPECT_NE(dynamic_pointer_cast(created), nullptr); + EXPECT_EQ(AtomDBFactory::wrap_if_protected(backend).get(), backend.get()); + } } TEST(AtomDBFactoryTest, CreateBackendRejectsMissingAndUnknownTypes) { @@ -70,24 +76,44 @@ TEST(AtomDBFactoryTest, CreateBackendRejectsMissingAndUnknownTypes) { EXPECT_THROW(AtomDBFactory::create_backend(config_with_type("adapterdb")), runtime_error); } -TEST(AtomDBFactoryTest, WrapIfProtectedNullReturnsNull) { - EXPECT_EQ(AtomDBFactory::wrap_if_protected(nullptr), nullptr); +TEST(AtomDBFactoryTest, WrapIfProtectedNullThrows) { + EXPECT_THROW(AtomDBFactory::wrap_if_protected(nullptr), runtime_error); } -TEST(AtomDBFactoryTest, WrapIfProtectedReturnsSameInstance) { +TEST(AtomDBFactoryTest, WrapIfProtectedLeavesUnprotectedBackend) { auto backend = make_shared(); auto wrapped = AtomDBFactory::wrap_if_protected(backend); EXPECT_EQ(wrapped.get(), backend.get()); + EXPECT_FALSE(wrapped->is_protected()); +} + +TEST(AtomDBFactoryTest, WrapIfProtectedWrapsProtectedBackend) { + auto backend = make_shared<::testing::NiceMock>(); + EXPECT_CALL(*backend, is_protected()).WillRepeatedly(::testing::Return(true)); + + auto wrapped = AtomDBFactory::wrap_if_protected(backend); + ASSERT_NE(wrapped, nullptr); + EXPECT_NE(wrapped.get(), backend.get()); + EXPECT_NE(dynamic_pointer_cast(wrapped), nullptr); + EXPECT_TRUE(wrapped->is_protected()); } TEST(AtomDBFactoryTest, WrapIfProtectedIsIdempotent) { - auto backend = make_shared(); + auto backend = make_shared<::testing::NiceMock>(); + EXPECT_CALL(*backend, is_protected()).WillRepeatedly(::testing::Return(true)); auto wrapped = AtomDBFactory::wrap_if_protected(backend); ASSERT_NE(wrapped, nullptr); - EXPECT_EQ(wrapped.get(), backend.get()); + EXPECT_NE(dynamic_pointer_cast(wrapped), nullptr); auto wrapped_again = AtomDBFactory::wrap_if_protected(wrapped); EXPECT_EQ(wrapped_again.get(), wrapped.get()); } + +TEST(AtomDBFactoryTest, CreateInMemoryDBIsNotProtected) { + auto db = AtomDBFactory::create(config_with_type("inmemorydb"), "factory_unprotected_"); + ASSERT_NE(db, nullptr); + EXPECT_FALSE(db->is_protected()); + EXPECT_EQ(dynamic_pointer_cast(db), nullptr); +} diff --git a/src/tests/cpp/protected_atomdb_test.cc b/src/tests/cpp/protected_atomdb_test.cc new file mode 100644 index 000000000..85db3547b --- /dev/null +++ b/src/tests/cpp/protected_atomdb_test.cc @@ -0,0 +1,67 @@ +#include + +#include +#include +#include + +#include "InMemoryDB.h" +#include "Link.h" +#include "Node.h" +#include "ProtectedAtomDB.h" + +using namespace atomdb; +using namespace atoms; +using namespace std; + +namespace { + +shared_ptr make_protected_db(const string& context = "protected_atomdb_test_") { + return make_shared(make_shared(context)); +} + +} // namespace + +TEST(ProtectedAtomDBTest, RejectsNullBackend) { EXPECT_THROW(ProtectedAtomDB(nullptr), runtime_error); } + +TEST(ProtectedAtomDBTest, IsProtected) { + auto db = make_protected_db(); + EXPECT_TRUE(db->is_protected()); +} + +TEST(ProtectedAtomDBTest, BackendIsNotProtected) { + auto backend = make_shared("protected_backend_"); + EXPECT_FALSE(backend->is_protected()); +} + +TEST(ProtectedAtomDBTest, DelegatesCapabilityFlagsToBackend) { + auto backend = make_shared("protected_flags_"); + ProtectedAtomDB db(backend); + + EXPECT_EQ(db.allow_nested_indexing(), backend->allow_nested_indexing()); + EXPECT_EQ(db.composite_type_enabled(), backend->composite_type_enabled()); +} + +TEST(ProtectedAtomDBTest, RejectsAccessWithoutPublicKey) { + auto db = make_protected_db("protected_no_key_"); + Node node("Symbol", "\"x\""); + Link link("Expression", {"a", "b"}); + + EXPECT_THROW(db->get_atom("handle"), runtime_error); + EXPECT_THROW(db->atom_exists("handle"), runtime_error); + EXPECT_THROW(db->add_node(&node), runtime_error); + EXPECT_THROW(db->add_link(&link), runtime_error); + EXPECT_THROW(db->delete_atom("handle"), runtime_error); + EXPECT_THROW(db->atom_count(), runtime_error); +} + +TEST(ProtectedAtomDBTest, PublicKeyOverloadsAreNotImplementedYet) { + auto db = make_protected_db("protected_with_key_"); + Node node("Symbol", "\"n\""); + const string key = "public_key"; + + EXPECT_THROW(db->get_atom("handle", key), runtime_error); + EXPECT_THROW(db->atom_exists("handle", key), runtime_error); + EXPECT_THROW(db->add_node(&node, key), runtime_error); + EXPECT_THROW(db->delete_atom("handle", key), runtime_error); + EXPECT_THROW(db->atom_count(key), runtime_error); +} diff --git a/src/tests/cpp/test_commons/mocks/MockAtomDB.h b/src/tests/cpp/test_commons/mocks/MockAtomDB.h index 85c82d1c8..1f9dbe958 100644 --- a/src/tests/cpp/test_commons/mocks/MockAtomDB.h +++ b/src/tests/cpp/test_commons/mocks/MockAtomDB.h @@ -25,6 +25,7 @@ class AtomDBMock : public AtomDB { public: MOCK_METHOD(bool, allow_nested_indexing, (), (override)); MOCK_METHOD(bool, composite_type_enabled, (), (const, override)); + MOCK_METHOD(bool, is_protected, (), (const, override)); MOCK_METHOD(shared_ptr, get_atom, (const string& handle), (override)); MOCK_METHOD(shared_ptr, get_node, (const string& handle), (override)); MOCK_METHOD(shared_ptr, get_link, (const string& handle), (override)); @@ -94,6 +95,7 @@ class AtomDBMock : public AtomDB { AtomDBMock() { ON_CALL(*this, composite_type_enabled()).WillByDefault(::testing::Return(true)); + ON_CALL(*this, is_protected()).WillByDefault(::testing::Return(false)); ON_CALL(*this, get_atom(testing::_)) .WillByDefault(::testing::Return(make_shared("Node", "TestNode"))); ON_CALL(*this, get_node(testing::_)) From c9a62f3808b77893552e3317a7320f757c7cba53 Mon Sep 17 00:00:00 2001 From: marcocapozzoli Date: Thu, 6 Aug 2026 11:49:10 -0300 Subject: [PATCH 04/21] add get_backend() --- src/atomdb/auth/ProtectedAtomDB.cc | 2 + src/atomdb/auth/ProtectedAtomDB.h | 9 +++ src/atomdb/redis_mongodb/RedisMongoDB.cc | 3 +- src/atomdb/remotedb/RemoteAtomDBPeer.cc | 1 + src/main/BUILD | 1 + src/main/db_loader.cc | 4 ++ src/tests/cpp/adapterdb_test.cc | 7 +++ src/tests/cpp/protected_atomdb_test.cc | 7 +++ src/tests/cpp/remote_atomdb_test.cc | 72 ++++++++++++++++++++++++ 9 files changed, 105 insertions(+), 1 deletion(-) diff --git a/src/atomdb/auth/ProtectedAtomDB.cc b/src/atomdb/auth/ProtectedAtomDB.cc index 1a4af79a9..2bcbf366f 100644 --- a/src/atomdb/auth/ProtectedAtomDB.cc +++ b/src/atomdb/auth/ProtectedAtomDB.cc @@ -19,6 +19,8 @@ ProtectedAtomDB::ProtectedAtomDB(shared_ptr backend) : backend(std::move // -------------------------------------------------------------------------------- // Public methods +shared_ptr ProtectedAtomDB::get_backend() const { return this->backend; } + shared_ptr ProtectedAtomDB::get_atom(const string& handle, const string& public_key) { RAISE_ERROR("ProtectedAtomDB::get_atom(handle, public_key) is not implemented yet"); } diff --git a/src/atomdb/auth/ProtectedAtomDB.h b/src/atomdb/auth/ProtectedAtomDB.h index 456ecb64b..e4de63fac 100644 --- a/src/atomdb/auth/ProtectedAtomDB.h +++ b/src/atomdb/auth/ProtectedAtomDB.h @@ -27,6 +27,15 @@ class ProtectedAtomDB : public AtomDB { */ explicit ProtectedAtomDB(shared_ptr backend); + /** + * @brief Returns the wrapped backend. + * + * Every method inherited from AtomDB rejects calls made without a public_key, including the + * HandleDecoder interface (get_atom(handle)). Callers that need the concrete backend type or a + * usable HandleDecoder must go through the backend returned here. + */ + shared_ptr get_backend() const; + bool allow_nested_indexing() override; bool composite_type_enabled() const override; bool is_protected() const override; diff --git a/src/atomdb/redis_mongodb/RedisMongoDB.cc b/src/atomdb/redis_mongodb/RedisMongoDB.cc index f2ce4c6b4..31040379a 100644 --- a/src/atomdb/redis_mongodb/RedisMongoDB.cc +++ b/src/atomdb/redis_mongodb/RedisMongoDB.cc @@ -40,7 +40,8 @@ RedisMongoDB::RedisMongoDB(const string& context, bool skip_redis, const JsonCon : context(context), skip_redis_(skip_redis), composite_type_enabled_(config.at_path("composite_type_enabled").get_or(true)), - cluster_flag(false) { + cluster_flag(false), + protected_flag(false) { initialize_statics(context); mongodb_setup(config); load_pattern_index_schema(); diff --git a/src/atomdb/remotedb/RemoteAtomDBPeer.cc b/src/atomdb/remotedb/RemoteAtomDBPeer.cc index 05a041007..497b4cdc2 100644 --- a/src/atomdb/remotedb/RemoteAtomDBPeer.cc +++ b/src/atomdb/remotedb/RemoteAtomDBPeer.cc @@ -44,6 +44,7 @@ bool RemoteAtomDBPeer::is_protected() const { return (local_persistence_ && local_persistence_->is_protected()) || (atomdb_ && atomdb_->is_protected()); } + shared_ptr RemoteAtomDBPeer::get_atom(const string& handle) { auto atom = cache_.get_atom(handle); if (atom) return atom; diff --git a/src/main/BUILD b/src/main/BUILD index a67e259f4..e51a83fdb 100644 --- a/src/main/BUILD +++ b/src/main/BUILD @@ -61,6 +61,7 @@ cc_library( "//atomdb:atomdb_factory", "//atomdb:atomdb_singleton", "//atomdb/adapterdb:adapterdb_lib", + "//atomdb/auth:protected_atomdb_lib", "//atomdb/redis_mongodb:redis_mongodb_lib", "//atomdb/remotedb:remotedb_lib", "//commons:commons_lib", diff --git a/src/main/db_loader.cc b/src/main/db_loader.cc index 9c5fb9295..4f08d233d 100644 --- a/src/main/db_loader.cc +++ b/src/main/db_loader.cc @@ -14,6 +14,7 @@ #include "JsonConfigParser.h" #include "MettaParser.h" #include "MettaParserActions.h" +#include "ProtectedAtomDB.h" #include "RedisMongoDB.h" #include "RemoteAtomDB.h" #include "Utils.h" @@ -212,6 +213,9 @@ int main(int argc, char* argv[]) { } else { auto atomdb = AtomDBSingleton::get_instance(); + if (auto protected_db = dynamic_pointer_cast(atomdb)) { + atomdb = protected_db->get_backend(); + } auto db = dynamic_pointer_cast(atomdb); if (db != nullptr) { try { diff --git a/src/tests/cpp/adapterdb_test.cc b/src/tests/cpp/adapterdb_test.cc index 954aeb6d5..37ce18206 100644 --- a/src/tests/cpp/adapterdb_test.cc +++ b/src/tests/cpp/adapterdb_test.cc @@ -251,6 +251,13 @@ TEST_P(AdapterDBTest, ReloadDoesNotThrowAndKeepsBackendUsable) { }); } +TEST_P(AdapterDBTest, IsProtectedDelegatesToBackend) { + auto db = create_current_adapter(); + ASSERT_NE(db, nullptr); + + EXPECT_EQ(db->is_protected(), backend->is_protected()); +} + TEST_P(AdapterDBTest, NeedsSyncIsNotImplemented) { auto db = create_current_adapter(); ASSERT_NE(db, nullptr); diff --git a/src/tests/cpp/protected_atomdb_test.cc b/src/tests/cpp/protected_atomdb_test.cc index 85db3547b..7748099d1 100644 --- a/src/tests/cpp/protected_atomdb_test.cc +++ b/src/tests/cpp/protected_atomdb_test.cc @@ -33,6 +33,13 @@ TEST(ProtectedAtomDBTest, BackendIsNotProtected) { EXPECT_FALSE(backend->is_protected()); } +TEST(ProtectedAtomDBTest, ExposesWrappedBackend) { + auto backend = make_shared("protected_backend_access_"); + ProtectedAtomDB db(backend); + + EXPECT_EQ(db.get_backend().get(), backend.get()); +} + TEST(ProtectedAtomDBTest, DelegatesCapabilityFlagsToBackend) { auto backend = make_shared("protected_flags_"); ProtectedAtomDB db(backend); diff --git a/src/tests/cpp/remote_atomdb_test.cc b/src/tests/cpp/remote_atomdb_test.cc index 454ee8c15..45d74ee7e 100644 --- a/src/tests/cpp/remote_atomdb_test.cc +++ b/src/tests/cpp/remote_atomdb_test.cc @@ -539,6 +539,14 @@ class CompositeTypeEnabledInMemoryDB : public InMemoryDB { bool composite_type_enabled() const override { return true; } }; +// Backend pointing at a protected database, like a RedisMongoDB whose Mongo config flags it. +class ProtectedInMemoryDB : public InMemoryDB { + public: + explicit ProtectedInMemoryDB(const string& context) : InMemoryDB(context) {} + + bool is_protected() const override { return true; } +}; + // Builds an Inheritance(x, "mammal") pattern that matches two links in the // helper-populated backends below. static LinkSchema inheritance_mammal_schema() { @@ -677,6 +685,70 @@ TEST(RemoteAtomDBFederationTest, CompositeTypeEnabledAggregation) { } } +TEST(RemoteAtomDBFederationTest, PeerIsProtectedWhenEitherBackendIsProtected) { + // Neither the remote backend nor the local persistence is protected. + { + auto remote = make_shared("prot_none_remote_"); + auto local = make_shared("prot_none_local_"); + auto peer = make_shared(remote, local, "peer"); + EXPECT_FALSE(peer->is_protected()); + } + + // Read-only peer (no local persistence) over an unprotected remote. + { + auto remote = make_shared("prot_readonly_remote_"); + auto peer = make_shared(remote, nullptr, "peer"); + EXPECT_FALSE(peer->is_protected()); + } + + // Protected remote backend. + { + auto remote = make_shared("prot_remote_remote_"); + auto local = make_shared("prot_remote_local_"); + auto peer = make_shared(remote, local, "peer"); + EXPECT_TRUE(peer->is_protected()); + } + + // Protected local persistence. + { + auto remote = make_shared("prot_local_remote_"); + auto local = make_shared("prot_local_local_"); + auto peer = make_shared(remote, local, "peer"); + EXPECT_TRUE(peer->is_protected()); + } +} + +TEST(RemoteAtomDBFederationTest, IsProtectedWhenAnyPeerIsProtected) { + // No peers -> nothing to protect. + { + map> peers; + auto db = make_shared(peers); + EXPECT_FALSE(db->is_protected()); + } + + // All peers unprotected. + { + auto remote1 = make_shared("fed_prot_off_remote1_"); + auto remote2 = make_shared("fed_prot_off_remote2_"); + map> peers; + peers["peer1"] = make_shared(remote1, nullptr, "peer1"); + peers["peer2"] = make_shared(remote2, nullptr, "peer2"); + auto db = make_shared(peers); + EXPECT_FALSE(db->is_protected()); + } + + // A single protected peer protects the whole facade. + { + auto unprotected_remote = make_shared("fed_prot_mixed_remote_"); + auto protected_remote = make_shared("fed_prot_mixed_protected_"); + map> peers; + peers["unprotected"] = make_shared(unprotected_remote, nullptr, "unprotected"); + peers["protected"] = make_shared(protected_remote, nullptr, "protected"); + auto db = make_shared(peers); + EXPECT_TRUE(db->is_protected()); + } +} + TEST(RemoteAtomDBFederationTest, CacheFirstProbingAcrossPeers) { // An atom that exists only in peer2's backend must still resolve via the facade, // and the resolving peer must cache it so subsequent probes are served from cache. From 604ed2501fb5c1c837f3b686bc95e07e825dfa3b Mon Sep 17 00:00:00 2001 From: marcocapozzoli Date: Thu, 6 Aug 2026 14:05:20 -0300 Subject: [PATCH 05/21] Remove get_backend() --- src/atomdb/auth/ProtectedAtomDB.cc | 2 -- src/atomdb/auth/ProtectedAtomDB.h | 9 --------- src/main/BUILD | 1 - src/main/db_loader.cc | 4 ---- src/tests/cpp/adapterdb_test.cc | 7 ------- src/tests/cpp/protected_atomdb_test.cc | 7 ------- 6 files changed, 30 deletions(-) diff --git a/src/atomdb/auth/ProtectedAtomDB.cc b/src/atomdb/auth/ProtectedAtomDB.cc index 2bcbf366f..1a4af79a9 100644 --- a/src/atomdb/auth/ProtectedAtomDB.cc +++ b/src/atomdb/auth/ProtectedAtomDB.cc @@ -19,8 +19,6 @@ ProtectedAtomDB::ProtectedAtomDB(shared_ptr backend) : backend(std::move // -------------------------------------------------------------------------------- // Public methods -shared_ptr ProtectedAtomDB::get_backend() const { return this->backend; } - shared_ptr ProtectedAtomDB::get_atom(const string& handle, const string& public_key) { RAISE_ERROR("ProtectedAtomDB::get_atom(handle, public_key) is not implemented yet"); } diff --git a/src/atomdb/auth/ProtectedAtomDB.h b/src/atomdb/auth/ProtectedAtomDB.h index e4de63fac..456ecb64b 100644 --- a/src/atomdb/auth/ProtectedAtomDB.h +++ b/src/atomdb/auth/ProtectedAtomDB.h @@ -27,15 +27,6 @@ class ProtectedAtomDB : public AtomDB { */ explicit ProtectedAtomDB(shared_ptr backend); - /** - * @brief Returns the wrapped backend. - * - * Every method inherited from AtomDB rejects calls made without a public_key, including the - * HandleDecoder interface (get_atom(handle)). Callers that need the concrete backend type or a - * usable HandleDecoder must go through the backend returned here. - */ - shared_ptr get_backend() const; - bool allow_nested_indexing() override; bool composite_type_enabled() const override; bool is_protected() const override; diff --git a/src/main/BUILD b/src/main/BUILD index e51a83fdb..a67e259f4 100644 --- a/src/main/BUILD +++ b/src/main/BUILD @@ -61,7 +61,6 @@ cc_library( "//atomdb:atomdb_factory", "//atomdb:atomdb_singleton", "//atomdb/adapterdb:adapterdb_lib", - "//atomdb/auth:protected_atomdb_lib", "//atomdb/redis_mongodb:redis_mongodb_lib", "//atomdb/remotedb:remotedb_lib", "//commons:commons_lib", diff --git a/src/main/db_loader.cc b/src/main/db_loader.cc index 35366f986..cc69a122b 100644 --- a/src/main/db_loader.cc +++ b/src/main/db_loader.cc @@ -14,7 +14,6 @@ #include "JsonConfigParser.h" #include "MettaParser.h" #include "MettaParserActions.h" -#include "ProtectedAtomDB.h" #include "RedisMongoDB.h" #include "RemoteAtomDB.h" #include "Utils.h" @@ -211,9 +210,6 @@ int main(int argc, char* argv[]) { } else { auto atomdb = AtomDBSingleton::get_instance(); - if (auto protected_db = dynamic_pointer_cast(atomdb)) { - atomdb = protected_db->get_backend(); - } auto db = dynamic_pointer_cast(atomdb); if (db != nullptr) { try { diff --git a/src/tests/cpp/adapterdb_test.cc b/src/tests/cpp/adapterdb_test.cc index 37ce18206..954aeb6d5 100644 --- a/src/tests/cpp/adapterdb_test.cc +++ b/src/tests/cpp/adapterdb_test.cc @@ -251,13 +251,6 @@ TEST_P(AdapterDBTest, ReloadDoesNotThrowAndKeepsBackendUsable) { }); } -TEST_P(AdapterDBTest, IsProtectedDelegatesToBackend) { - auto db = create_current_adapter(); - ASSERT_NE(db, nullptr); - - EXPECT_EQ(db->is_protected(), backend->is_protected()); -} - TEST_P(AdapterDBTest, NeedsSyncIsNotImplemented) { auto db = create_current_adapter(); ASSERT_NE(db, nullptr); diff --git a/src/tests/cpp/protected_atomdb_test.cc b/src/tests/cpp/protected_atomdb_test.cc index 7748099d1..85db3547b 100644 --- a/src/tests/cpp/protected_atomdb_test.cc +++ b/src/tests/cpp/protected_atomdb_test.cc @@ -33,13 +33,6 @@ TEST(ProtectedAtomDBTest, BackendIsNotProtected) { EXPECT_FALSE(backend->is_protected()); } -TEST(ProtectedAtomDBTest, ExposesWrappedBackend) { - auto backend = make_shared("protected_backend_access_"); - ProtectedAtomDB db(backend); - - EXPECT_EQ(db.get_backend().get(), backend.get()); -} - TEST(ProtectedAtomDBTest, DelegatesCapabilityFlagsToBackend) { auto backend = make_shared("protected_flags_"); ProtectedAtomDB db(backend); From f205cad32f89f970fe0ecc3579d5b566e7f053b5 Mon Sep 17 00:00:00 2001 From: marcocapozzoli Date: Mon, 10 Aug 2026 10:05:46 -0300 Subject: [PATCH 06/21] move ProtectedAtomDB --- src/atomdb/AtomDBFactory.cc | 12 ++++++++++-- src/atomdb/BUILD | 17 +++++++++++++++-- src/atomdb/{auth => }/ProtectedAtomDB.cc | 0 src/atomdb/{auth => }/ProtectedAtomDB.h | 0 src/atomdb/auth/BUILD | 24 ------------------------ src/tests/cpp/BUILD | 4 ++-- 6 files changed, 27 insertions(+), 30 deletions(-) rename src/atomdb/{auth => }/ProtectedAtomDB.cc (100%) rename src/atomdb/{auth => }/ProtectedAtomDB.h (100%) delete mode 100644 src/atomdb/auth/BUILD diff --git a/src/atomdb/AtomDBFactory.cc b/src/atomdb/AtomDBFactory.cc index c03b020dc..afa7e7276 100644 --- a/src/atomdb/AtomDBFactory.cc +++ b/src/atomdb/AtomDBFactory.cc @@ -112,6 +112,14 @@ shared_ptr AtomDBFactory::create_composite_atomdb(const JsonConfig& conf } shared_ptr AtomDBFactory::wrap_if_protected(shared_ptr atomdb) { - // AtomDBFactory::wrap_if_protected() is not implemented yet. - return atomdb; + if (!atomdb) { + RAISE_ERROR("AtomDBFactory::wrap_if_protected() received null atomdb"); + } + if (!atomdb->is_protected()) { + return atomdb; + } + if (dynamic_pointer_cast(atomdb)) { + return atomdb; + } + return make_shared(atomdb); } \ No newline at end of file diff --git a/src/atomdb/BUILD b/src/atomdb/BUILD index d091f6914..95015e913 100644 --- a/src/atomdb/BUILD +++ b/src/atomdb/BUILD @@ -11,8 +11,8 @@ cc_library( ":atomdb_factory", ":atomdb_singleton", ":atomdbutils", + ":protected_atomdb", "//atomdb/adapterdb:adapterdb_lib", - "//atomdb/auth:protected_atomdb_lib", "//atomdb/inmemorydb:inmemorydb_lib", "//atomdb/morkdb:morkdb_lib", "//atomdb/redis_mongodb:redis_mongodb_lib", @@ -27,8 +27,8 @@ cc_library( includes = ["."], deps = [ ":atomdb", + ":protected_atomdb", "//atomdb/adapterdb:adapterdb_lib", - "//atomdb/auth:protected_atomdb_lib", "//atomdb/inmemorydb:inmemorydb_lib", "//atomdb/morkdb:morkdb_lib", "//atomdb/redis_mongodb:redis_mongodb_lib", @@ -80,3 +80,16 @@ cc_library( "//commons:commons_lib", ], ) + +cc_library( + name = "protected_atomdb", + srcs = ["ProtectedAtomDB.cc"], + hdrs = ["ProtectedAtomDB.h"], + includes = ["."], + deps = [ + ":atomdb", + ":atomdb_api_types", + "//commons:commons_lib", + "//commons/atoms:atoms_lib", + ], +) diff --git a/src/atomdb/auth/ProtectedAtomDB.cc b/src/atomdb/ProtectedAtomDB.cc similarity index 100% rename from src/atomdb/auth/ProtectedAtomDB.cc rename to src/atomdb/ProtectedAtomDB.cc diff --git a/src/atomdb/auth/ProtectedAtomDB.h b/src/atomdb/ProtectedAtomDB.h similarity index 100% rename from src/atomdb/auth/ProtectedAtomDB.h rename to src/atomdb/ProtectedAtomDB.h diff --git a/src/atomdb/auth/BUILD b/src/atomdb/auth/BUILD deleted file mode 100644 index 38928d78c..000000000 --- a/src/atomdb/auth/BUILD +++ /dev/null @@ -1,24 +0,0 @@ -load("@rules_cc//cc:cc_library.bzl", "cc_library") - -package(default_visibility = ["//visibility:public"]) - -cc_library( - name = "protected_atomdb_lib", - includes = ["."], - deps = [ - ":protected_atomdb", - ], -) - -cc_library( - name = "protected_atomdb", - srcs = ["ProtectedAtomDB.cc"], - hdrs = ["ProtectedAtomDB.h"], - includes = ["."], - deps = [ - "//atomdb", - "//atomdb:atomdb_api_types", - "//commons:commons_lib", - "//commons/atoms:atoms_lib", - ], -) diff --git a/src/tests/cpp/BUILD b/src/tests/cpp/BUILD index 636dba079..566d7802a 100644 --- a/src/tests/cpp/BUILD +++ b/src/tests/cpp/BUILD @@ -860,7 +860,7 @@ cc_test( ], linkstatic = 1, deps = [ - "//atomdb/auth:protected_atomdb_lib", + "//atomdb:protected_atomdb", "//atomdb/inmemorydb:inmemorydb_lib", "//commons/atoms:atoms_lib", "@com_github_google_googletest//:gtest_main", @@ -885,8 +885,8 @@ cc_test( linkstatic = 1, deps = [ "//atomdb:atomdb_factory", + "//atomdb:protected_atomdb", "//atomdb/adapterdb:adapterdb_lib", - "//atomdb/auth:protected_atomdb_lib", "//atomdb/inmemorydb:inmemorydb_lib", "//atomdb/morkdb:morkdb_lib", "//atomdb/remotedb:remotedb_lib", From 1d069b1b0f623fd4efca3e63534c12fd8d8b43ab Mon Sep 17 00:00:00 2001 From: marcocapozzoli Date: Mon, 10 Aug 2026 21:34:20 -0300 Subject: [PATCH 07/21] fix BUILD --- src/atomdb/remotedb/BUILD | 1 + 1 file changed, 1 insertion(+) diff --git a/src/atomdb/remotedb/BUILD b/src/atomdb/remotedb/BUILD index 1f880baca..b556984b1 100644 --- a/src/atomdb/remotedb/BUILD +++ b/src/atomdb/remotedb/BUILD @@ -24,6 +24,7 @@ cc_library( deps = [ "//atomdb", "//atomdb:atomdb_api_types", + "//atomdb/inmemorydb", "//atomdb/inmemorydb:inmemorydb_api_types", "//atomdb/morkdb:morkdb_lib", "//atomdb/redis_mongodb:redis_mongodb_lib", From b4c2a49b0c7b39f329dded1f21a80f0fca39a727 Mon Sep 17 00:00:00 2001 From: marcocapozzoli Date: Mon, 10 Aug 2026 22:07:49 -0300 Subject: [PATCH 08/21] Add RemoteAtomDB::is_protected() --- src/atomdb/remotedb/RemoteAtomDB.cc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/atomdb/remotedb/RemoteAtomDB.cc b/src/atomdb/remotedb/RemoteAtomDB.cc index 53482a6be..11ecd3702 100644 --- a/src/atomdb/remotedb/RemoteAtomDB.cc +++ b/src/atomdb/remotedb/RemoteAtomDB.cc @@ -39,6 +39,15 @@ bool RemoteAtomDB::composite_type_enabled() const { return false; } +bool RemoteAtomDB::is_protected() const { + for (auto& [uid, peer] : remote_db_) { + if (peer->is_protected()) { + return true; + } + } + return false; +} + void RemoteAtomDB::derive_nested_indexing() { // Derive the aggregated nested-indexing capability from the peers. A single global boolean // cannot describe a heterogeneous result set, so mixed configurations are normalized to the From 78a437430115b1f37ab94ed7e52147d82486abbc Mon Sep 17 00:00:00 2001 From: marcocapozzoli Date: Thu, 13 Aug 2026 20:32:47 -0300 Subject: [PATCH 09/21] Change AtomDB::is_protected() to return ProtectionMode and replace string public_key with PublicKey --- src/atomdb/AtomDB.h | 2 +- src/atomdb/AtomDBAPITypes.h | 9 +++ src/atomdb/AtomDBFactory.cc | 6 +- src/atomdb/AtomDBFactory.h | 5 +- src/atomdb/BUILD | 12 +++ src/atomdb/ProtectedAtomDB.cc | 70 +++++++++-------- src/atomdb/ProtectedAtomDB.h | 75 +++++++++++-------- src/atomdb/PublicKey.cc | 44 +++++++++++ src/atomdb/PublicKey.h | 35 +++++++++ src/atomdb/adapterdb/AdapterDB.cc | 2 +- src/atomdb/adapterdb/AdapterDB.h | 2 +- src/atomdb/inmemorydb/InMemoryDB.h | 4 +- src/atomdb/redis_mongodb/RedisMongoDB.cc | 5 +- src/atomdb/redis_mongodb/RedisMongoDB.h | 2 +- src/atomdb/remotedb/RemoteAtomDB.cc | 8 +- src/atomdb/remotedb/RemoteAtomDB.h | 2 +- src/atomdb/remotedb/RemoteAtomDBPeer.cc | 14 +++- src/atomdb/remotedb/RemoteAtomDBPeer.h | 2 +- src/tests/cpp/BUILD | 1 + src/tests/cpp/atomdb_factory_test.cc | 6 +- src/tests/cpp/protected_atomdb_test.cc | 45 ++++++++++- src/tests/cpp/remote_atomdb_test.cc | 18 ++--- src/tests/cpp/test_commons/mocks/MockAtomDB.h | 5 +- 23 files changed, 273 insertions(+), 101 deletions(-) create mode 100644 src/atomdb/PublicKey.cc create mode 100644 src/atomdb/PublicKey.h diff --git a/src/atomdb/AtomDB.h b/src/atomdb/AtomDB.h index 48dfdbb4a..dc89f9af4 100644 --- a/src/atomdb/AtomDB.h +++ b/src/atomdb/AtomDB.h @@ -51,7 +51,7 @@ class AtomDB : public HandleDecoder { virtual bool allow_nested_indexing() = 0; virtual bool composite_type_enabled() const = 0; - virtual bool is_protected() const = 0; + virtual atomdb_api_types::ProtectionMode is_protected() const = 0; virtual shared_ptr get_atom(const string& handle) = 0; // HandleDecoder interface virtual shared_ptr get_node(const string& handle) = 0; diff --git a/src/atomdb/AtomDBAPITypes.h b/src/atomdb/AtomDBAPITypes.h index 4a1d62958..98d3c0dad 100644 --- a/src/atomdb/AtomDBAPITypes.h +++ b/src/atomdb/AtomDBAPITypes.h @@ -67,5 +67,14 @@ class AtomDocument { virtual bool contains(const string& key) = 0; }; +/** + * @brief How an AtomDB participates in protected access. + * + * - UNPROTECTED: no authorization wrapper; open access. + * - PROTECTED: wrap and apply authorization post-processing (filter) after queries. + * - FORWARD: wrap and pass access keys through, but do not post-process locally + */ +enum class ProtectionMode { UNPROTECTED, PROTECTED, FORWARD }; + } // namespace atomdb_api_types } // namespace atomdb diff --git a/src/atomdb/AtomDBFactory.cc b/src/atomdb/AtomDBFactory.cc index fee0ae77f..dd301d788 100644 --- a/src/atomdb/AtomDBFactory.cc +++ b/src/atomdb/AtomDBFactory.cc @@ -115,10 +115,8 @@ shared_ptr AtomDBFactory::wrap_if_protected(shared_ptr atomdb) { if (!atomdb) { RAISE_ERROR("AtomDBFactory::wrap_if_protected() received null atomdb"); } - if (!atomdb->is_protected()) { - return atomdb; - } - if (dynamic_pointer_cast(atomdb)) { + if (atomdb->is_protected() == atomdb_api_types::ProtectionMode::UNPROTECTED || + dynamic_pointer_cast(atomdb)) { return atomdb; } return make_shared(atomdb); diff --git a/src/atomdb/AtomDBFactory.h b/src/atomdb/AtomDBFactory.h index 7f3b663b1..0b352ae5e 100644 --- a/src/atomdb/AtomDBFactory.h +++ b/src/atomdb/AtomDBFactory.h @@ -27,7 +27,8 @@ namespace atomdb { class AtomDBFactory { public: /** - * @brief Creates a AtomDB and wraps it with ProtectedAtomDB when is applyable. + * @brief Creates a AtomDB and wraps it with ProtectedAtomDB when mode is + * PROTECTED or FORWARD. */ static shared_ptr create(const JsonConfig& config, const string& context = ""); @@ -40,7 +41,7 @@ class AtomDBFactory { const string& context = ""); /** - * @brief Wraps an AtomDB with ProtectedAtomDB when protected and not already wrapped. + * @brief Wraps an AtomDB with ProtectedAtomDB when mode is PROTECTED or FORWARD. */ static shared_ptr wrap_if_protected(shared_ptr atomdb); }; diff --git a/src/atomdb/BUILD b/src/atomdb/BUILD index 95015e913..31d8f9172 100644 --- a/src/atomdb/BUILD +++ b/src/atomdb/BUILD @@ -12,6 +12,7 @@ cc_library( ":atomdb_singleton", ":atomdbutils", ":protected_atomdb", + ":public_key", "//atomdb/adapterdb:adapterdb_lib", "//atomdb/inmemorydb:inmemorydb_lib", "//atomdb/morkdb:morkdb_lib", @@ -81,6 +82,16 @@ cc_library( ], ) +cc_library( + name = "public_key", + srcs = ["PublicKey.cc"], + hdrs = ["PublicKey.h"], + includes = ["."], + deps = [ + "//commons:commons_lib", + ], +) + cc_library( name = "protected_atomdb", srcs = ["ProtectedAtomDB.cc"], @@ -89,6 +100,7 @@ cc_library( deps = [ ":atomdb", ":atomdb_api_types", + ":public_key", "//commons:commons_lib", "//commons/atoms:atoms_lib", ], diff --git a/src/atomdb/ProtectedAtomDB.cc b/src/atomdb/ProtectedAtomDB.cc index 1a4af79a9..e06fdd72e 100644 --- a/src/atomdb/ProtectedAtomDB.cc +++ b/src/atomdb/ProtectedAtomDB.cc @@ -19,151 +19,151 @@ ProtectedAtomDB::ProtectedAtomDB(shared_ptr backend) : backend(std::move // -------------------------------------------------------------------------------- // Public methods -shared_ptr ProtectedAtomDB::get_atom(const string& handle, const string& public_key) { +shared_ptr ProtectedAtomDB::get_atom(const string& handle, const PublicKey& public_key) { RAISE_ERROR("ProtectedAtomDB::get_atom(handle, public_key) is not implemented yet"); } -shared_ptr ProtectedAtomDB::get_node(const string& handle, const string& public_key) { +shared_ptr ProtectedAtomDB::get_node(const string& handle, const PublicKey& public_key) { RAISE_ERROR("ProtectedAtomDB::get_node(handle, public_key) is not implemented yet"); } -shared_ptr ProtectedAtomDB::get_link(const string& handle, const string& public_key) { +shared_ptr ProtectedAtomDB::get_link(const string& handle, const PublicKey& public_key) { RAISE_ERROR("ProtectedAtomDB::get_link(handle, public_key) is not implemented yet"); } vector> ProtectedAtomDB::get_matching_atoms(bool is_toplevel, Atom& key, - const string& public_key) { + const PublicKey& public_key) { RAISE_ERROR("ProtectedAtomDB::get_matching_atoms(..., public_key) is not implemented yet"); } shared_ptr ProtectedAtomDB::query_for_pattern(const LinkSchema& link_schema, - const string& public_key) { + const PublicKey& public_key) { RAISE_ERROR("ProtectedAtomDB::query_for_pattern(link_schema, public_key) is not implemented yet"); } -shared_ptr ProtectedAtomDB::query_for_targets(const string& handle, - const string& public_key) { +shared_ptr ProtectedAtomDB::query_for_targets( + const string& handle, const PublicKey& public_key) { RAISE_ERROR("ProtectedAtomDB::query_for_targets(handle, public_key) is not implemented yet"); } shared_ptr ProtectedAtomDB::query_for_incoming_set( - const string& handle, const string& public_key) { + const string& handle, const PublicKey& public_key) { RAISE_ERROR("ProtectedAtomDB::query_for_incoming_set(handle, public_key) is not implemented yet"); } -bool ProtectedAtomDB::atom_exists(const string& handle, const string& public_key) { +bool ProtectedAtomDB::atom_exists(const string& handle, const PublicKey& public_key) { RAISE_ERROR("ProtectedAtomDB::atom_exists(handle, public_key) is not implemented yet"); } -bool ProtectedAtomDB::node_exists(const string& handle, const string& public_key) { +bool ProtectedAtomDB::node_exists(const string& handle, const PublicKey& public_key) { RAISE_ERROR("ProtectedAtomDB::node_exists(handle, public_key) is not implemented yet"); } -bool ProtectedAtomDB::link_exists(const string& handle, const string& public_key) { +bool ProtectedAtomDB::link_exists(const string& handle, const PublicKey& public_key) { RAISE_ERROR("ProtectedAtomDB::link_exists(handle, public_key) is not implemented yet"); } -set ProtectedAtomDB::atoms_exist(const vector& handles, const string& public_key) { +set ProtectedAtomDB::atoms_exist(const vector& handles, const PublicKey& public_key) { RAISE_ERROR("ProtectedAtomDB::atoms_exist(handles, public_key) is not implemented yet"); } -set ProtectedAtomDB::nodes_exist(const vector& handles, const string& public_key) { +set ProtectedAtomDB::nodes_exist(const vector& handles, const PublicKey& public_key) { RAISE_ERROR("ProtectedAtomDB::nodes_exist(handles, public_key) is not implemented yet"); } -set ProtectedAtomDB::links_exist(const vector& handles, const string& public_key) { +set ProtectedAtomDB::links_exist(const vector& handles, const PublicKey& public_key) { RAISE_ERROR("ProtectedAtomDB::links_exist(handles, public_key) is not implemented yet"); } string ProtectedAtomDB::add_atom(const atoms::Atom* atom, - const string& public_key, + const PublicKey& public_key, const atoms::Merger* merger) { RAISE_ERROR("ProtectedAtomDB::add_atom(atom, public_key) is not implemented yet"); } string ProtectedAtomDB::add_node(const atoms::Node* node, - const string& public_key, + const PublicKey& public_key, const atoms::Merger* merger) { RAISE_ERROR("ProtectedAtomDB::add_node(node, public_key) is not implemented yet"); } string ProtectedAtomDB::add_link(const atoms::Link* link, - const string& public_key, + const PublicKey& public_key, const atoms::Merger* merger) { RAISE_ERROR("ProtectedAtomDB::add_link(link, public_key) is not implemented yet"); } vector ProtectedAtomDB::add_atoms(const vector& atom_list, - const string& public_key, + const PublicKey& public_key, bool is_transactional, const atoms::Merger* merger) { RAISE_ERROR("ProtectedAtomDB::add_atoms(atom_list, public_key) is not implemented yet"); } vector ProtectedAtomDB::add_nodes(const vector& nodes, - const string& public_key, + const PublicKey& public_key, bool is_transactional, const atoms::Merger* merger) { RAISE_ERROR("ProtectedAtomDB::add_nodes(nodes, public_key) is not implemented yet"); } vector ProtectedAtomDB::add_links(const vector& links, - const string& public_key, + const PublicKey& public_key, bool is_transactional, const atoms::Merger* merger) { RAISE_ERROR("ProtectedAtomDB::add_links(links, public_key) is not implemented yet"); } bool ProtectedAtomDB::delete_atom(const string& handle, - const string& public_key, + const PublicKey& public_key, bool delete_link_targets) { RAISE_ERROR("ProtectedAtomDB::delete_atom(handle, public_key) is not implemented yet"); } bool ProtectedAtomDB::delete_node(const string& handle, - const string& public_key, + const PublicKey& public_key, bool delete_link_targets) { RAISE_ERROR("ProtectedAtomDB::delete_node(handle, public_key) is not implemented yet"); } bool ProtectedAtomDB::delete_link(const string& handle, - const string& public_key, + const PublicKey& public_key, bool delete_link_targets) { RAISE_ERROR("ProtectedAtomDB::delete_link(handle, public_key) is not implemented yet"); } uint ProtectedAtomDB::delete_atoms(const vector& handles, - const string& public_key, + const PublicKey& public_key, bool delete_link_targets) { RAISE_ERROR("ProtectedAtomDB::delete_atoms(handles, public_key) is not implemented yet"); } uint ProtectedAtomDB::delete_nodes(const vector& handles, - const string& public_key, + const PublicKey& public_key, bool delete_link_targets) { RAISE_ERROR("ProtectedAtomDB::delete_nodes(handles, public_key) is not implemented yet"); } uint ProtectedAtomDB::delete_links(const vector& handles, - const string& public_key, + const PublicKey& public_key, bool delete_link_targets) { RAISE_ERROR("ProtectedAtomDB::delete_links(handles, public_key) is not implemented yet"); } -void ProtectedAtomDB::re_index_patterns(const string& public_key, bool flush_patterns) { +void ProtectedAtomDB::re_index_patterns(const PublicKey& public_key, bool flush_patterns) { RAISE_ERROR("ProtectedAtomDB::re_index_patterns(public_key) is not implemented yet"); } -size_t ProtectedAtomDB::node_count(const string& public_key) const { +size_t ProtectedAtomDB::node_count(const PublicKey& public_key) const { RAISE_ERROR("ProtectedAtomDB::node_count(public_key) is not implemented yet"); } -size_t ProtectedAtomDB::link_count(const string& public_key) const { +size_t ProtectedAtomDB::link_count(const PublicKey& public_key) const { RAISE_ERROR("ProtectedAtomDB::link_count(public_key) is not implemented yet"); } -size_t ProtectedAtomDB::atom_count(const string& public_key) const { +size_t ProtectedAtomDB::atom_count(const PublicKey& public_key) const { RAISE_ERROR("ProtectedAtomDB::atom_count(public_key) is not implemented yet"); } @@ -171,7 +171,13 @@ bool ProtectedAtomDB::allow_nested_indexing() { return this->backend->allow_nest bool ProtectedAtomDB::composite_type_enabled() const { return this->backend->composite_type_enabled(); } -bool ProtectedAtomDB::is_protected() const { return true; } +atomdb_api_types::ProtectionMode ProtectedAtomDB::is_protected() const { + auto backend_mode = this->backend->is_protected(); + if (backend_mode == atomdb_api_types::ProtectionMode::FORWARD) { + return atomdb_api_types::ProtectionMode::FORWARD; + } + return atomdb_api_types::ProtectionMode::PROTECTED; +} // -------------------------------------------------------------------------------- // Public methods (without public_key - reject the call) @@ -292,5 +298,5 @@ size_t ProtectedAtomDB::atom_count() const { raise_public_key_required("atom_cou void ProtectedAtomDB::raise_public_key_required(const string& method_name) { RAISE_ERROR("ProtectedAtomDB::" + method_name + - "() is unavailable without a public_key; use the overload that accepts a public_key"); + "() is unavailable without a PublicKey; use the overload that accepts one"); } diff --git a/src/atomdb/ProtectedAtomDB.h b/src/atomdb/ProtectedAtomDB.h index 456ecb64b..b30461325 100644 --- a/src/atomdb/ProtectedAtomDB.h +++ b/src/atomdb/ProtectedAtomDB.h @@ -6,6 +6,7 @@ #include #include "AtomDB.h" +#include "PublicKey.h" using namespace std; using namespace atoms; @@ -16,9 +17,11 @@ namespace atomdb { * @brief Authorization wrapper around any AtomDB backend for protected databases. * * Data-access methods expose two forms: - * - overloads without public_key: reject the call (protected access requires a key) - * - overloads with public_key: authorize and delegate to the backend + * - overloads without PublicKey: reject the call (protected access requires a key) + * - overloads with PublicKey: authorize and delegate to the backend * + * When the backend reports ProtectionMode::FORWARD, this wrapper forwards the + * access key without applying local authorization post-processing. */ class ProtectedAtomDB : public AtomDB { public: @@ -29,70 +32,72 @@ class ProtectedAtomDB : public AtomDB { bool allow_nested_indexing() override; bool composite_type_enabled() const override; - bool is_protected() const override; + atomdb_api_types::ProtectionMode is_protected() const override; shared_ptr get_atom(const string& handle) override; - shared_ptr get_atom(const string& handle, const string& public_key); + shared_ptr get_atom(const string& handle, const PublicKey& public_key); shared_ptr get_node(const string& handle) override; - shared_ptr get_node(const string& handle, const string& public_key); + shared_ptr get_node(const string& handle, const PublicKey& public_key); shared_ptr get_link(const string& handle) override; - shared_ptr get_link(const string& handle, const string& public_key); + shared_ptr get_link(const string& handle, const PublicKey& public_key); vector> get_matching_atoms(bool is_toplevel, Atom& key) override; - vector> get_matching_atoms(bool is_toplevel, Atom& key, const string& public_key); + vector> get_matching_atoms(bool is_toplevel, + Atom& key, + const PublicKey& public_key); shared_ptr query_for_pattern(const LinkSchema& link_schema) override; shared_ptr query_for_pattern(const LinkSchema& link_schema, - const string& public_key); + const PublicKey& public_key); shared_ptr query_for_targets(const string& handle) override; shared_ptr query_for_targets(const string& handle, - const string& public_key); + const PublicKey& public_key); shared_ptr query_for_incoming_set(const string& handle) override; shared_ptr query_for_incoming_set(const string& handle, - const string& public_key); + const PublicKey& public_key); bool atom_exists(const string& handle) override; - bool atom_exists(const string& handle, const string& public_key); + bool atom_exists(const string& handle, const PublicKey& public_key); bool node_exists(const string& handle) override; - bool node_exists(const string& handle, const string& public_key); + bool node_exists(const string& handle, const PublicKey& public_key); bool link_exists(const string& handle) override; - bool link_exists(const string& handle, const string& public_key); + bool link_exists(const string& handle, const PublicKey& public_key); set atoms_exist(const vector& handles) override; - set atoms_exist(const vector& handles, const string& public_key); + set atoms_exist(const vector& handles, const PublicKey& public_key); set nodes_exist(const vector& handles) override; - set nodes_exist(const vector& handles, const string& public_key); + set nodes_exist(const vector& handles, const PublicKey& public_key); set links_exist(const vector& handles) override; - set links_exist(const vector& handles, const string& public_key); + set links_exist(const vector& handles, const PublicKey& public_key); string add_atom(const atoms::Atom* atom, const atoms::Merger* merger = NULL) override; string add_atom(const atoms::Atom* atom, - const string& public_key, + const PublicKey& public_key, const atoms::Merger* merger = NULL); string add_node(const atoms::Node* node, const atoms::Merger* merger = NULL) override; string add_node(const atoms::Node* node, - const string& public_key, + const PublicKey& public_key, const atoms::Merger* merger = NULL); string add_link(const atoms::Link* link, const atoms::Merger* merger = NULL) override; string add_link(const atoms::Link* link, - const string& public_key, + const PublicKey& public_key, const atoms::Merger* merger = NULL); vector add_atoms(const vector& atom_list, bool is_transactional = false, const atoms::Merger* merger = NULL) override; vector add_atoms(const vector& atom_list, - const string& public_key, + const PublicKey& public_key, bool is_transactional = false, const atoms::Merger* merger = NULL); @@ -100,7 +105,7 @@ class ProtectedAtomDB : public AtomDB { bool is_transactional = false, const atoms::Merger* merger = NULL) override; vector add_nodes(const vector& nodes, - const string& public_key, + const PublicKey& public_key, bool is_transactional = false, const atoms::Merger* merger = NULL); @@ -108,45 +113,51 @@ class ProtectedAtomDB : public AtomDB { bool is_transactional = false, const atoms::Merger* merger = NULL) override; vector add_links(const vector& links, - const string& public_key, + const PublicKey& public_key, bool is_transactional = false, const atoms::Merger* merger = NULL); bool delete_atom(const string& handle, bool delete_link_targets = false) override; - bool delete_atom(const string& handle, const string& public_key, bool delete_link_targets = false); + bool delete_atom(const string& handle, + const PublicKey& public_key, + bool delete_link_targets = false); bool delete_node(const string& handle, bool delete_link_targets = false) override; - bool delete_node(const string& handle, const string& public_key, bool delete_link_targets = false); + bool delete_node(const string& handle, + const PublicKey& public_key, + bool delete_link_targets = false); bool delete_link(const string& handle, bool delete_link_targets = false) override; - bool delete_link(const string& handle, const string& public_key, bool delete_link_targets = false); + bool delete_link(const string& handle, + const PublicKey& public_key, + bool delete_link_targets = false); uint delete_atoms(const vector& handles, bool delete_link_targets = false) override; uint delete_atoms(const vector& handles, - const string& public_key, + const PublicKey& public_key, bool delete_link_targets = false); uint delete_nodes(const vector& handles, bool delete_link_targets = false) override; uint delete_nodes(const vector& handles, - const string& public_key, + const PublicKey& public_key, bool delete_link_targets = false); uint delete_links(const vector& handles, bool delete_link_targets = false) override; uint delete_links(const vector& handles, - const string& public_key, + const PublicKey& public_key, bool delete_link_targets = false); void re_index_patterns(bool flush_patterns = true) override; - void re_index_patterns(const string& public_key, bool flush_patterns = true); + void re_index_patterns(const PublicKey& public_key, bool flush_patterns = true); size_t node_count() const override; - size_t node_count(const string& public_key) const; + size_t node_count(const PublicKey& public_key) const; size_t link_count() const override; - size_t link_count(const string& public_key) const; + size_t link_count(const PublicKey& public_key) const; size_t atom_count() const override; - size_t atom_count(const string& public_key) const; + size_t atom_count(const PublicKey& public_key) const; private: shared_ptr backend; diff --git a/src/atomdb/PublicKey.cc b/src/atomdb/PublicKey.cc new file mode 100644 index 000000000..26a2691a1 --- /dev/null +++ b/src/atomdb/PublicKey.cc @@ -0,0 +1,44 @@ +#include "PublicKey.h" + +#include "Utils.h" + +using namespace atomdb; +using namespace commons; + +PublicKey::PublicKey(const string& key) : single_key(key) { + if (key.empty()) { + RAISE_ERROR("PublicKey: key must not be empty"); + } +} + +PublicKey::PublicKey(const map& keys) : peer_keys(keys) { + if (keys.empty()) { + RAISE_ERROR("PublicKey: keys must not be empty"); + } + for (const auto& [peer_uid, key] : keys) { + if (peer_uid.empty()) { + RAISE_ERROR("PublicKey: peer uid must not be empty"); + } + if (key.empty()) { + RAISE_ERROR("PublicKey: key for peer '" + peer_uid + "' must not be empty"); + } + } +} + +bool PublicKey::is_single_key() const { return !this->single_key.empty(); } + +bool PublicKey::is_peer_map() const { return !this->peer_keys.empty(); } + +const string& PublicKey::key() const { + if (!this->is_single_key()) { + RAISE_ERROR("PublicKey::key() called on a peer-map instance"); + } + return this->single_key; +} + +const map& PublicKey::keys() const { + if (!this->is_peer_map()) { + RAISE_ERROR("PublicKey::keys() called on a single-key instance"); + } + return this->peer_keys; +} diff --git a/src/atomdb/PublicKey.h b/src/atomdb/PublicKey.h new file mode 100644 index 000000000..ee47dc391 --- /dev/null +++ b/src/atomdb/PublicKey.h @@ -0,0 +1,35 @@ +#pragma once + +#include +#include + +using namespace std; + +namespace atomdb { + +/** + * @brief Encapsulates public-key material for protected AtomDB access. + * + * Holds either a single public key or a peer-id -> key map. + * How RemoteAtomDB consumes the map is still TBD; this type only carries the data. + */ +class PublicKey { + public: + explicit PublicKey(const string& key); + explicit PublicKey(const map& keys); + + bool is_single_key() const; + bool is_peer_map() const; + + /** @brief Single key. Raises if this instance holds a peer map. */ + const string& key() const; + + /** @brief Peer-id -> key map. Raises if this instance holds a single key. */ + const map& keys() const; + + private: + string single_key; + map peer_keys; +}; + +} // namespace atomdb diff --git a/src/atomdb/adapterdb/AdapterDB.cc b/src/atomdb/adapterdb/AdapterDB.cc index cb833f397..707b26656 100644 --- a/src/atomdb/adapterdb/AdapterDB.cc +++ b/src/atomdb/adapterdb/AdapterDB.cc @@ -65,7 +65,7 @@ bool AdapterDB::composite_type_enabled() const { return this->atomdb_backend->composite_type_enabled(); } -bool AdapterDB::is_protected() const { +atomdb_api_types::ProtectionMode AdapterDB::is_protected() const { this->ensure_backend_ready(); return this->atomdb_backend->is_protected(); } diff --git a/src/atomdb/adapterdb/AdapterDB.h b/src/atomdb/adapterdb/AdapterDB.h index 756f34133..c5c48bbb6 100644 --- a/src/atomdb/adapterdb/AdapterDB.h +++ b/src/atomdb/adapterdb/AdapterDB.h @@ -62,7 +62,7 @@ class AdapterDB : public AtomDB { */ bool composite_type_enabled() const override; - bool is_protected() const override; + atomdb_api_types::ProtectionMode is_protected() const override; shared_ptr get_atom(const string& handle) override; shared_ptr get_node(const string& handle) override; diff --git a/src/atomdb/inmemorydb/InMemoryDB.h b/src/atomdb/inmemorydb/InMemoryDB.h index 4563605cc..1dcd0faa8 100644 --- a/src/atomdb/inmemorydb/InMemoryDB.h +++ b/src/atomdb/inmemorydb/InMemoryDB.h @@ -23,7 +23,9 @@ class InMemoryDB : public AtomDB { bool allow_nested_indexing() override; bool composite_type_enabled() const override { return false; } - bool is_protected() const override { return false; } + atomdb_api_types::ProtectionMode is_protected() const override { + return atomdb_api_types::ProtectionMode::UNPROTECTED; + } shared_ptr get_atom(const string& handle) override; shared_ptr get_node(const string& handle) override; diff --git a/src/atomdb/redis_mongodb/RedisMongoDB.cc b/src/atomdb/redis_mongodb/RedisMongoDB.cc index 31040379a..80c2a8be3 100644 --- a/src/atomdb/redis_mongodb/RedisMongoDB.cc +++ b/src/atomdb/redis_mongodb/RedisMongoDB.cc @@ -57,7 +57,10 @@ RedisMongoDB::~RedisMongoDB() { bool RedisMongoDB::allow_nested_indexing() { return false; } -bool RedisMongoDB::is_protected() const { return this->protected_flag; } +atomdb_api_types::ProtectionMode RedisMongoDB::is_protected() const { + return this->protected_flag ? atomdb_api_types::ProtectionMode::PROTECTED + : atomdb_api_types::ProtectionMode::UNPROTECTED; +} void RedisMongoDB::redis_setup(const JsonConfig& config) { if (skip_redis_) return; diff --git a/src/atomdb/redis_mongodb/RedisMongoDB.h b/src/atomdb/redis_mongodb/RedisMongoDB.h index efc2dbd1a..53befa8d3 100644 --- a/src/atomdb/redis_mongodb/RedisMongoDB.h +++ b/src/atomdb/redis_mongodb/RedisMongoDB.h @@ -32,7 +32,7 @@ class RedisMongoDB : public AtomDB { bool allow_nested_indexing() override; bool composite_type_enabled() const override { return this->composite_type_enabled_; } - bool is_protected() const override; + atomdb_api_types::ProtectionMode is_protected() const override; static string REDIS_PATTERNS_PREFIX; static string REDIS_OUTGOING_PREFIX; diff --git a/src/atomdb/remotedb/RemoteAtomDB.cc b/src/atomdb/remotedb/RemoteAtomDB.cc index 11ecd3702..6e57e4966 100644 --- a/src/atomdb/remotedb/RemoteAtomDB.cc +++ b/src/atomdb/remotedb/RemoteAtomDB.cc @@ -39,13 +39,13 @@ bool RemoteAtomDB::composite_type_enabled() const { return false; } -bool RemoteAtomDB::is_protected() const { +atomdb_api_types::ProtectionMode RemoteAtomDB::is_protected() const { for (auto& [uid, peer] : remote_db_) { - if (peer->is_protected()) { - return true; + if (peer->is_protected() != atomdb_api_types::ProtectionMode::UNPROTECTED) { + return atomdb_api_types::ProtectionMode::FORWARD; } } - return false; + return atomdb_api_types::ProtectionMode::UNPROTECTED; } void RemoteAtomDB::derive_nested_indexing() { diff --git a/src/atomdb/remotedb/RemoteAtomDB.h b/src/atomdb/remotedb/RemoteAtomDB.h index cb1dbb3e7..53f5d9497 100644 --- a/src/atomdb/remotedb/RemoteAtomDB.h +++ b/src/atomdb/remotedb/RemoteAtomDB.h @@ -27,7 +27,7 @@ class RemoteAtomDB : public AtomDB { bool allow_nested_indexing() override; bool composite_type_enabled() const override; - bool is_protected() const override; + atomdb_api_types::ProtectionMode is_protected() const override; shared_ptr get_atom(const string& handle) override; shared_ptr get_node(const string& handle) override; diff --git a/src/atomdb/remotedb/RemoteAtomDBPeer.cc b/src/atomdb/remotedb/RemoteAtomDBPeer.cc index 497b4cdc2..f706748c0 100644 --- a/src/atomdb/remotedb/RemoteAtomDBPeer.cc +++ b/src/atomdb/remotedb/RemoteAtomDBPeer.cc @@ -40,9 +40,17 @@ bool RemoteAtomDBPeer::composite_type_enabled() const { return local_persistence_ && local_persistence_->composite_type_enabled(); } -bool RemoteAtomDBPeer::is_protected() const { - return (local_persistence_ && local_persistence_->is_protected()) || - (atomdb_ && atomdb_->is_protected()); +atomdb_api_types::ProtectionMode RemoteAtomDBPeer::is_protected() const { + if (local_persistence_) { + auto mode = local_persistence_->is_protected(); + if (mode != atomdb_api_types::ProtectionMode::UNPROTECTED) { + return mode; + } + } + if (atomdb_) { + return atomdb_->is_protected(); + } + return atomdb_api_types::ProtectionMode::UNPROTECTED; } shared_ptr RemoteAtomDBPeer::get_atom(const string& handle) { diff --git a/src/atomdb/remotedb/RemoteAtomDBPeer.h b/src/atomdb/remotedb/RemoteAtomDBPeer.h index 9305329a6..e92c772d1 100644 --- a/src/atomdb/remotedb/RemoteAtomDBPeer.h +++ b/src/atomdb/remotedb/RemoteAtomDBPeer.h @@ -30,7 +30,7 @@ class RemoteAtomDBPeer : public AtomDB, public processor::ThreadMethod { bool allow_nested_indexing() override; bool composite_type_enabled() const override; - bool is_protected() const override; + atomdb_api_types::ProtectionMode is_protected() const override; shared_ptr get_atom(const string& handle) override; shared_ptr get_node(const string& handle) override; diff --git a/src/tests/cpp/BUILD b/src/tests/cpp/BUILD index 566d7802a..cba9298d6 100644 --- a/src/tests/cpp/BUILD +++ b/src/tests/cpp/BUILD @@ -861,6 +861,7 @@ cc_test( linkstatic = 1, deps = [ "//atomdb:protected_atomdb", + "//atomdb:public_key", "//atomdb/inmemorydb:inmemorydb_lib", "//commons/atoms:atoms_lib", "@com_github_google_googletest//:gtest_main", diff --git a/src/tests/cpp/atomdb_factory_test.cc b/src/tests/cpp/atomdb_factory_test.cc index 5d83ee644..c0a98ddf0 100644 --- a/src/tests/cpp/atomdb_factory_test.cc +++ b/src/tests/cpp/atomdb_factory_test.cc @@ -11,8 +11,8 @@ #include "JsonConfig.h" #include "MorkDB.h" #include "Node.h" -#include "RemoteAtomDB.h" #include "ProtectedAtomDB.h" +#include "RemoteAtomDB.h" #include "TestAtomDBJsonConfig.h" #include "Utils.h" #include "expression_hasher.h" @@ -22,6 +22,8 @@ using namespace atoms; using namespace commons; using namespace std; +using atomdb_api_types::ProtectionMode; + namespace { JsonConfig config_with_type(const string& type) { @@ -159,6 +161,6 @@ TEST(AtomDBFactoryTest, CreateAdapterDBRequiresBackendType) { TEST(AtomDBFactoryTest, CreateInMemoryDBIsNotProtected) { auto db = AtomDBFactory::create(config_with_type("inmemorydb"), "factory_unprotected_"); ASSERT_NE(db, nullptr); - EXPECT_FALSE(db->is_protected()); + EXPECT_EQ(db->is_protected(), ProtectionMode::UNPROTECTED); EXPECT_EQ(dynamic_pointer_cast(db), nullptr); } diff --git a/src/tests/cpp/protected_atomdb_test.cc b/src/tests/cpp/protected_atomdb_test.cc index 85db3547b..6f7c01691 100644 --- a/src/tests/cpp/protected_atomdb_test.cc +++ b/src/tests/cpp/protected_atomdb_test.cc @@ -1,5 +1,6 @@ #include +#include #include #include #include @@ -8,11 +9,14 @@ #include "Link.h" #include "Node.h" #include "ProtectedAtomDB.h" +#include "PublicKey.h" using namespace atomdb; using namespace atoms; using namespace std; +using atomdb_api_types::ProtectionMode; + namespace { shared_ptr make_protected_db(const string& context = "protected_atomdb_test_") { @@ -21,16 +25,51 @@ shared_ptr make_protected_db(const string& context = "protected } // namespace +TEST(PublicKeyTest, SingleKey) { + PublicKey key("pk1"); + EXPECT_TRUE(key.is_single_key()); + EXPECT_FALSE(key.is_peer_map()); + EXPECT_EQ(key.key(), "pk1"); + EXPECT_THROW(key.keys(), runtime_error); +} + +TEST(PublicKeyTest, PeerMap) { + PublicKey key(map{{"peer1", "k1"}, {"peer2", "k2"}}); + EXPECT_FALSE(key.is_single_key()); + EXPECT_TRUE(key.is_peer_map()); + EXPECT_EQ(key.keys().size(), 2u); + EXPECT_EQ(key.keys().at("peer1"), "k1"); + EXPECT_THROW(key.key(), runtime_error); +} + +TEST(PublicKeyTest, RejectsEmpty) { + EXPECT_THROW(PublicKey(""), runtime_error); + EXPECT_THROW(PublicKey(map{}), runtime_error); + EXPECT_THROW(PublicKey(map{{"", "k"}}), runtime_error); + EXPECT_THROW(PublicKey(map{{"peer", ""}}), runtime_error); +} + TEST(ProtectedAtomDBTest, RejectsNullBackend) { EXPECT_THROW(ProtectedAtomDB(nullptr), runtime_error); } TEST(ProtectedAtomDBTest, IsProtected) { auto db = make_protected_db(); - EXPECT_TRUE(db->is_protected()); + EXPECT_EQ(db->is_protected(), ProtectionMode::PROTECTED); } TEST(ProtectedAtomDBTest, BackendIsNotProtected) { auto backend = make_shared("protected_backend_"); - EXPECT_FALSE(backend->is_protected()); + EXPECT_EQ(backend->is_protected(), ProtectionMode::UNPROTECTED); +} + +TEST(ProtectedAtomDBTest, ForwardModeWhenBackendIsForward) { + class ForwardInMemoryDB : public InMemoryDB { + public: + explicit ForwardInMemoryDB(const string& context) : InMemoryDB(context) {} + ProtectionMode is_protected() const override { return ProtectionMode::FORWARD; } + }; + + ProtectedAtomDB db(make_shared("protected_forward_")); + EXPECT_EQ(db.is_protected(), ProtectionMode::FORWARD); } TEST(ProtectedAtomDBTest, DelegatesCapabilityFlagsToBackend) { @@ -57,7 +96,7 @@ TEST(ProtectedAtomDBTest, RejectsAccessWithoutPublicKey) { TEST(ProtectedAtomDBTest, PublicKeyOverloadsAreNotImplementedYet) { auto db = make_protected_db("protected_with_key_"); Node node("Symbol", "\"n\""); - const string key = "public_key"; + PublicKey key("public_key"); EXPECT_THROW(db->get_atom("handle", key), runtime_error); EXPECT_THROW(db->atom_exists("handle", key), runtime_error); diff --git a/src/tests/cpp/remote_atomdb_test.cc b/src/tests/cpp/remote_atomdb_test.cc index 7635972f4..b8a13d37c 100644 --- a/src/tests/cpp/remote_atomdb_test.cc +++ b/src/tests/cpp/remote_atomdb_test.cc @@ -549,7 +549,7 @@ class ProtectedInMemoryDB : public InMemoryDB { public: explicit ProtectedInMemoryDB(const string& context) : InMemoryDB(context) {} - bool is_protected() const override { return true; } + ProtectionMode is_protected() const override { return ProtectionMode::PROTECTED; } }; // Builds an Inheritance(x, "mammal") pattern that matches two links in the @@ -696,14 +696,14 @@ TEST(RemoteAtomDBFederationTest, PeerIsProtectedWhenEitherBackendIsProtected) { auto remote = make_shared("prot_none_remote_"); auto local = make_shared("prot_none_local_"); auto peer = make_shared(remote, local, "peer"); - EXPECT_FALSE(peer->is_protected()); + EXPECT_EQ(peer->is_protected(), ProtectionMode::UNPROTECTED); } // Read-only peer (no local persistence) over an unprotected remote. { auto remote = make_shared("prot_readonly_remote_"); auto peer = make_shared(remote, nullptr, "peer"); - EXPECT_FALSE(peer->is_protected()); + EXPECT_EQ(peer->is_protected(), ProtectionMode::UNPROTECTED); } // Protected remote backend. @@ -711,7 +711,7 @@ TEST(RemoteAtomDBFederationTest, PeerIsProtectedWhenEitherBackendIsProtected) { auto remote = make_shared("prot_remote_remote_"); auto local = make_shared("prot_remote_local_"); auto peer = make_shared(remote, local, "peer"); - EXPECT_TRUE(peer->is_protected()); + EXPECT_EQ(peer->is_protected(), ProtectionMode::PROTECTED); } // Protected local persistence. @@ -719,7 +719,7 @@ TEST(RemoteAtomDBFederationTest, PeerIsProtectedWhenEitherBackendIsProtected) { auto remote = make_shared("prot_local_remote_"); auto local = make_shared("prot_local_local_"); auto peer = make_shared(remote, local, "peer"); - EXPECT_TRUE(peer->is_protected()); + EXPECT_EQ(peer->is_protected(), ProtectionMode::PROTECTED); } } @@ -728,7 +728,7 @@ TEST(RemoteAtomDBFederationTest, IsProtectedWhenAnyPeerIsProtected) { { map> peers; auto db = make_shared(peers); - EXPECT_FALSE(db->is_protected()); + EXPECT_EQ(db->is_protected(), ProtectionMode::UNPROTECTED); } // All peers unprotected. @@ -739,10 +739,10 @@ TEST(RemoteAtomDBFederationTest, IsProtectedWhenAnyPeerIsProtected) { peers["peer1"] = make_shared(remote1, nullptr, "peer1"); peers["peer2"] = make_shared(remote2, nullptr, "peer2"); auto db = make_shared(peers); - EXPECT_FALSE(db->is_protected()); + EXPECT_EQ(db->is_protected(), ProtectionMode::UNPROTECTED); } - // A single protected peer protects the whole facade. + // A single protected peer makes the facade FORWARD (no local post-processing). { auto unprotected_remote = make_shared("fed_prot_mixed_remote_"); auto protected_remote = make_shared("fed_prot_mixed_protected_"); @@ -750,7 +750,7 @@ TEST(RemoteAtomDBFederationTest, IsProtectedWhenAnyPeerIsProtected) { peers["unprotected"] = make_shared(unprotected_remote, nullptr, "unprotected"); peers["protected"] = make_shared(protected_remote, nullptr, "protected"); auto db = make_shared(peers); - EXPECT_TRUE(db->is_protected()); + EXPECT_EQ(db->is_protected(), ProtectionMode::FORWARD); } } diff --git a/src/tests/cpp/test_commons/mocks/MockAtomDB.h b/src/tests/cpp/test_commons/mocks/MockAtomDB.h index 1f9dbe958..2fafb9d3e 100644 --- a/src/tests/cpp/test_commons/mocks/MockAtomDB.h +++ b/src/tests/cpp/test_commons/mocks/MockAtomDB.h @@ -5,6 +5,7 @@ using namespace std; using namespace atomdb; +using atomdb_api_types::ProtectionMode; class MockAtomDocument : public atomdb_api_types::AtomDocument { public: @@ -25,7 +26,7 @@ class AtomDBMock : public AtomDB { public: MOCK_METHOD(bool, allow_nested_indexing, (), (override)); MOCK_METHOD(bool, composite_type_enabled, (), (const, override)); - MOCK_METHOD(bool, is_protected, (), (const, override)); + MOCK_METHOD(ProtectionMode, is_protected, (), (const, override)); MOCK_METHOD(shared_ptr, get_atom, (const string& handle), (override)); MOCK_METHOD(shared_ptr, get_node, (const string& handle), (override)); MOCK_METHOD(shared_ptr, get_link, (const string& handle), (override)); @@ -95,7 +96,7 @@ class AtomDBMock : public AtomDB { AtomDBMock() { ON_CALL(*this, composite_type_enabled()).WillByDefault(::testing::Return(true)); - ON_CALL(*this, is_protected()).WillByDefault(::testing::Return(false)); + ON_CALL(*this, is_protected()).WillByDefault(::testing::Return(ProtectionMode::UNPROTECTED)); ON_CALL(*this, get_atom(testing::_)) .WillByDefault(::testing::Return(make_shared("Node", "TestNode"))); ON_CALL(*this, get_node(testing::_)) From 56e647d0d1396e1793b27f86c2d11998097941eb Mon Sep 17 00:00:00 2001 From: marcocapozzoli Date: Fri, 14 Aug 2026 10:24:12 -0300 Subject: [PATCH 10/21] Add new test --- src/tests/cpp/redis_mongodb_test.cc | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/tests/cpp/redis_mongodb_test.cc b/src/tests/cpp/redis_mongodb_test.cc index 1b57ae8dd..d93d2cbf8 100644 --- a/src/tests/cpp/redis_mongodb_test.cc +++ b/src/tests/cpp/redis_mongodb_test.cc @@ -1354,6 +1354,23 @@ TEST_F(RedisMongoDBTest, TransactionalRejectedMergeStillBooksCompositeType) { delete nested; } +TEST_F(RedisMongoDBTest, IsProtectedFollowsPersistedConfig) { + using bsoncxx::builder::basic::kvp; + using bsoncxx::builder::basic::make_document; + + EXPECT_EQ(db->is_protected(), atomdb_api_types::ProtectionMode::UNPROTECTED); + + auto conn = db->get_mongo_pool()->acquire(); + auto collection = + (*conn)[RedisMongoDB::MONGODB_DB_NAME][RedisMongoDB::MONGODB_CONFIG_COLLECTION_NAME]; + collection.insert_one(make_document(kvp("protected", true))); + + auto loaded = AtomDBFactory::create(test_atomdb_json_config(), "test_"); + EXPECT_EQ(loaded->is_protected(), atomdb_api_types::ProtectionMode::PROTECTED); + + collection.delete_many({}); +} + int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); ::testing::AddGlobalTestEnvironment(new RedisMongoDBTestEnvironment()); From e97e41f3c6dc36e572968187698066db61d562df Mon Sep 17 00:00:00 2001 From: marcocapozzoli Date: Mon, 17 Aug 2026 00:27:44 -0300 Subject: [PATCH 11/21] wip - clean --- src/atomdb/BUILD | 10 ------ src/atomdb/ProtectedAtomDB.h | 2 +- src/atomdb/PublicKey.cc | 44 -------------------------- src/atomdb/PublicKey.h | 35 -------------------- src/tests/cpp/protected_atomdb_test.cc | 2 +- 5 files changed, 2 insertions(+), 91 deletions(-) delete mode 100644 src/atomdb/PublicKey.cc delete mode 100644 src/atomdb/PublicKey.h diff --git a/src/atomdb/BUILD b/src/atomdb/BUILD index 31d8f9172..e6b9f5911 100644 --- a/src/atomdb/BUILD +++ b/src/atomdb/BUILD @@ -82,16 +82,6 @@ cc_library( ], ) -cc_library( - name = "public_key", - srcs = ["PublicKey.cc"], - hdrs = ["PublicKey.h"], - includes = ["."], - deps = [ - "//commons:commons_lib", - ], -) - cc_library( name = "protected_atomdb", srcs = ["ProtectedAtomDB.cc"], diff --git a/src/atomdb/ProtectedAtomDB.h b/src/atomdb/ProtectedAtomDB.h index b30461325..bc3534ca1 100644 --- a/src/atomdb/ProtectedAtomDB.h +++ b/src/atomdb/ProtectedAtomDB.h @@ -6,10 +6,10 @@ #include #include "AtomDB.h" -#include "PublicKey.h" using namespace std; using namespace atoms; +using namespace atomdb_api_types; namespace atomdb { diff --git a/src/atomdb/PublicKey.cc b/src/atomdb/PublicKey.cc deleted file mode 100644 index 26a2691a1..000000000 --- a/src/atomdb/PublicKey.cc +++ /dev/null @@ -1,44 +0,0 @@ -#include "PublicKey.h" - -#include "Utils.h" - -using namespace atomdb; -using namespace commons; - -PublicKey::PublicKey(const string& key) : single_key(key) { - if (key.empty()) { - RAISE_ERROR("PublicKey: key must not be empty"); - } -} - -PublicKey::PublicKey(const map& keys) : peer_keys(keys) { - if (keys.empty()) { - RAISE_ERROR("PublicKey: keys must not be empty"); - } - for (const auto& [peer_uid, key] : keys) { - if (peer_uid.empty()) { - RAISE_ERROR("PublicKey: peer uid must not be empty"); - } - if (key.empty()) { - RAISE_ERROR("PublicKey: key for peer '" + peer_uid + "' must not be empty"); - } - } -} - -bool PublicKey::is_single_key() const { return !this->single_key.empty(); } - -bool PublicKey::is_peer_map() const { return !this->peer_keys.empty(); } - -const string& PublicKey::key() const { - if (!this->is_single_key()) { - RAISE_ERROR("PublicKey::key() called on a peer-map instance"); - } - return this->single_key; -} - -const map& PublicKey::keys() const { - if (!this->is_peer_map()) { - RAISE_ERROR("PublicKey::keys() called on a single-key instance"); - } - return this->peer_keys; -} diff --git a/src/atomdb/PublicKey.h b/src/atomdb/PublicKey.h deleted file mode 100644 index ee47dc391..000000000 --- a/src/atomdb/PublicKey.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include -#include - -using namespace std; - -namespace atomdb { - -/** - * @brief Encapsulates public-key material for protected AtomDB access. - * - * Holds either a single public key or a peer-id -> key map. - * How RemoteAtomDB consumes the map is still TBD; this type only carries the data. - */ -class PublicKey { - public: - explicit PublicKey(const string& key); - explicit PublicKey(const map& keys); - - bool is_single_key() const; - bool is_peer_map() const; - - /** @brief Single key. Raises if this instance holds a peer map. */ - const string& key() const; - - /** @brief Peer-id -> key map. Raises if this instance holds a single key. */ - const map& keys() const; - - private: - string single_key; - map peer_keys; -}; - -} // namespace atomdb diff --git a/src/tests/cpp/protected_atomdb_test.cc b/src/tests/cpp/protected_atomdb_test.cc index 6f7c01691..10ff885db 100644 --- a/src/tests/cpp/protected_atomdb_test.cc +++ b/src/tests/cpp/protected_atomdb_test.cc @@ -9,9 +9,9 @@ #include "Link.h" #include "Node.h" #include "ProtectedAtomDB.h" -#include "PublicKey.h" using namespace atomdb; +using namespace atomdb_api_types; using namespace atoms; using namespace std; From a684e7ec5a1b97ef29174aed0e038f781d238846 Mon Sep 17 00:00:00 2001 From: marcocapozzoli Date: Mon, 17 Aug 2026 09:29:12 -0300 Subject: [PATCH 12/21] Align ProtectedAtomDB and tests with the PublicKey DTO, and reject protected local persistence on RemoteAtomDBPeer --- src/atomdb/BUILD | 2 - src/atomdb/ProtectedAtomDB.cc | 63 +++++++++++++------------ src/atomdb/ProtectedAtomDB.h | 61 ++++++++++++------------ src/atomdb/remotedb/RemoteAtomDBPeer.cc | 10 ++-- src/tests/cpp/BUILD | 1 - src/tests/cpp/inmemorydb_test.cc | 4 ++ src/tests/cpp/protected_atomdb_test.cc | 31 ++++++------ src/tests/cpp/redis_mongodb_test.cc | 2 + src/tests/cpp/remote_atomdb_test.cc | 6 +-- 9 files changed, 94 insertions(+), 86 deletions(-) diff --git a/src/atomdb/BUILD b/src/atomdb/BUILD index e6b9f5911..95015e913 100644 --- a/src/atomdb/BUILD +++ b/src/atomdb/BUILD @@ -12,7 +12,6 @@ cc_library( ":atomdb_singleton", ":atomdbutils", ":protected_atomdb", - ":public_key", "//atomdb/adapterdb:adapterdb_lib", "//atomdb/inmemorydb:inmemorydb_lib", "//atomdb/morkdb:morkdb_lib", @@ -90,7 +89,6 @@ cc_library( deps = [ ":atomdb", ":atomdb_api_types", - ":public_key", "//commons:commons_lib", "//commons/atoms:atoms_lib", ], diff --git a/src/atomdb/ProtectedAtomDB.cc b/src/atomdb/ProtectedAtomDB.cc index e06fdd72e..b3f4fd671 100644 --- a/src/atomdb/ProtectedAtomDB.cc +++ b/src/atomdb/ProtectedAtomDB.cc @@ -19,151 +19,151 @@ ProtectedAtomDB::ProtectedAtomDB(shared_ptr backend) : backend(std::move // -------------------------------------------------------------------------------- // Public methods -shared_ptr ProtectedAtomDB::get_atom(const string& handle, const PublicKey& public_key) { +shared_ptr ProtectedAtomDB::get_atom(const string& handle, const atomdb_api_types::PublicKey& public_key) { RAISE_ERROR("ProtectedAtomDB::get_atom(handle, public_key) is not implemented yet"); } -shared_ptr ProtectedAtomDB::get_node(const string& handle, const PublicKey& public_key) { +shared_ptr ProtectedAtomDB::get_node(const string& handle, const atomdb_api_types::PublicKey& public_key) { RAISE_ERROR("ProtectedAtomDB::get_node(handle, public_key) is not implemented yet"); } -shared_ptr ProtectedAtomDB::get_link(const string& handle, const PublicKey& public_key) { +shared_ptr ProtectedAtomDB::get_link(const string& handle, const atomdb_api_types::PublicKey& public_key) { RAISE_ERROR("ProtectedAtomDB::get_link(handle, public_key) is not implemented yet"); } vector> ProtectedAtomDB::get_matching_atoms(bool is_toplevel, Atom& key, - const PublicKey& public_key) { + const atomdb_api_types::PublicKey& public_key) { RAISE_ERROR("ProtectedAtomDB::get_matching_atoms(..., public_key) is not implemented yet"); } shared_ptr ProtectedAtomDB::query_for_pattern(const LinkSchema& link_schema, - const PublicKey& public_key) { + const atomdb_api_types::PublicKey& public_key) { RAISE_ERROR("ProtectedAtomDB::query_for_pattern(link_schema, public_key) is not implemented yet"); } shared_ptr ProtectedAtomDB::query_for_targets( - const string& handle, const PublicKey& public_key) { + const string& handle, const atomdb_api_types::PublicKey& public_key) { RAISE_ERROR("ProtectedAtomDB::query_for_targets(handle, public_key) is not implemented yet"); } shared_ptr ProtectedAtomDB::query_for_incoming_set( - const string& handle, const PublicKey& public_key) { + const string& handle, const atomdb_api_types::PublicKey& public_key) { RAISE_ERROR("ProtectedAtomDB::query_for_incoming_set(handle, public_key) is not implemented yet"); } -bool ProtectedAtomDB::atom_exists(const string& handle, const PublicKey& public_key) { +bool ProtectedAtomDB::atom_exists(const string& handle, const atomdb_api_types::PublicKey& public_key) { RAISE_ERROR("ProtectedAtomDB::atom_exists(handle, public_key) is not implemented yet"); } -bool ProtectedAtomDB::node_exists(const string& handle, const PublicKey& public_key) { +bool ProtectedAtomDB::node_exists(const string& handle, const atomdb_api_types::PublicKey& public_key) { RAISE_ERROR("ProtectedAtomDB::node_exists(handle, public_key) is not implemented yet"); } -bool ProtectedAtomDB::link_exists(const string& handle, const PublicKey& public_key) { +bool ProtectedAtomDB::link_exists(const string& handle, const atomdb_api_types::PublicKey& public_key) { RAISE_ERROR("ProtectedAtomDB::link_exists(handle, public_key) is not implemented yet"); } -set ProtectedAtomDB::atoms_exist(const vector& handles, const PublicKey& public_key) { +set ProtectedAtomDB::atoms_exist(const vector& handles, const atomdb_api_types::PublicKey& public_key) { RAISE_ERROR("ProtectedAtomDB::atoms_exist(handles, public_key) is not implemented yet"); } -set ProtectedAtomDB::nodes_exist(const vector& handles, const PublicKey& public_key) { +set ProtectedAtomDB::nodes_exist(const vector& handles, const atomdb_api_types::PublicKey& public_key) { RAISE_ERROR("ProtectedAtomDB::nodes_exist(handles, public_key) is not implemented yet"); } -set ProtectedAtomDB::links_exist(const vector& handles, const PublicKey& public_key) { +set ProtectedAtomDB::links_exist(const vector& handles, const atomdb_api_types::PublicKey& public_key) { RAISE_ERROR("ProtectedAtomDB::links_exist(handles, public_key) is not implemented yet"); } string ProtectedAtomDB::add_atom(const atoms::Atom* atom, - const PublicKey& public_key, + const atomdb_api_types::PublicKey& public_key, const atoms::Merger* merger) { RAISE_ERROR("ProtectedAtomDB::add_atom(atom, public_key) is not implemented yet"); } string ProtectedAtomDB::add_node(const atoms::Node* node, - const PublicKey& public_key, + const atomdb_api_types::PublicKey& public_key, const atoms::Merger* merger) { RAISE_ERROR("ProtectedAtomDB::add_node(node, public_key) is not implemented yet"); } string ProtectedAtomDB::add_link(const atoms::Link* link, - const PublicKey& public_key, + const atomdb_api_types::PublicKey& public_key, const atoms::Merger* merger) { RAISE_ERROR("ProtectedAtomDB::add_link(link, public_key) is not implemented yet"); } vector ProtectedAtomDB::add_atoms(const vector& atom_list, - const PublicKey& public_key, + const atomdb_api_types::PublicKey& public_key, bool is_transactional, const atoms::Merger* merger) { RAISE_ERROR("ProtectedAtomDB::add_atoms(atom_list, public_key) is not implemented yet"); } vector ProtectedAtomDB::add_nodes(const vector& nodes, - const PublicKey& public_key, + const atomdb_api_types::PublicKey& public_key, bool is_transactional, const atoms::Merger* merger) { RAISE_ERROR("ProtectedAtomDB::add_nodes(nodes, public_key) is not implemented yet"); } vector ProtectedAtomDB::add_links(const vector& links, - const PublicKey& public_key, + const atomdb_api_types::PublicKey& public_key, bool is_transactional, const atoms::Merger* merger) { RAISE_ERROR("ProtectedAtomDB::add_links(links, public_key) is not implemented yet"); } bool ProtectedAtomDB::delete_atom(const string& handle, - const PublicKey& public_key, + const atomdb_api_types::PublicKey& public_key, bool delete_link_targets) { RAISE_ERROR("ProtectedAtomDB::delete_atom(handle, public_key) is not implemented yet"); } bool ProtectedAtomDB::delete_node(const string& handle, - const PublicKey& public_key, + const atomdb_api_types::PublicKey& public_key, bool delete_link_targets) { RAISE_ERROR("ProtectedAtomDB::delete_node(handle, public_key) is not implemented yet"); } bool ProtectedAtomDB::delete_link(const string& handle, - const PublicKey& public_key, + const atomdb_api_types::PublicKey& public_key, bool delete_link_targets) { RAISE_ERROR("ProtectedAtomDB::delete_link(handle, public_key) is not implemented yet"); } uint ProtectedAtomDB::delete_atoms(const vector& handles, - const PublicKey& public_key, + const atomdb_api_types::PublicKey& public_key, bool delete_link_targets) { RAISE_ERROR("ProtectedAtomDB::delete_atoms(handles, public_key) is not implemented yet"); } uint ProtectedAtomDB::delete_nodes(const vector& handles, - const PublicKey& public_key, + const atomdb_api_types::PublicKey& public_key, bool delete_link_targets) { RAISE_ERROR("ProtectedAtomDB::delete_nodes(handles, public_key) is not implemented yet"); } uint ProtectedAtomDB::delete_links(const vector& handles, - const PublicKey& public_key, + const atomdb_api_types::PublicKey& public_key, bool delete_link_targets) { RAISE_ERROR("ProtectedAtomDB::delete_links(handles, public_key) is not implemented yet"); } -void ProtectedAtomDB::re_index_patterns(const PublicKey& public_key, bool flush_patterns) { +void ProtectedAtomDB::re_index_patterns(const atomdb_api_types::PublicKey& public_key, bool flush_patterns) { RAISE_ERROR("ProtectedAtomDB::re_index_patterns(public_key) is not implemented yet"); } -size_t ProtectedAtomDB::node_count(const PublicKey& public_key) const { +size_t ProtectedAtomDB::node_count(const atomdb_api_types::PublicKey& public_key) const { RAISE_ERROR("ProtectedAtomDB::node_count(public_key) is not implemented yet"); } -size_t ProtectedAtomDB::link_count(const PublicKey& public_key) const { +size_t ProtectedAtomDB::link_count(const atomdb_api_types::PublicKey& public_key) const { RAISE_ERROR("ProtectedAtomDB::link_count(public_key) is not implemented yet"); } -size_t ProtectedAtomDB::atom_count(const PublicKey& public_key) const { +size_t ProtectedAtomDB::atom_count(const atomdb_api_types::PublicKey& public_key) const { RAISE_ERROR("ProtectedAtomDB::atom_count(public_key) is not implemented yet"); } @@ -179,6 +179,11 @@ atomdb_api_types::ProtectionMode ProtectedAtomDB::is_protected() const { return atomdb_api_types::ProtectionMode::PROTECTED; } +vector ProtectedAtomDB::get_access_permissions( + const atomdb_api_types::PublicKey& public_key) const { + return this->backend->get_access_permissions(public_key); +} + // -------------------------------------------------------------------------------- // Public methods (without public_key - reject the call) diff --git a/src/atomdb/ProtectedAtomDB.h b/src/atomdb/ProtectedAtomDB.h index bc3534ca1..0c6698e09 100644 --- a/src/atomdb/ProtectedAtomDB.h +++ b/src/atomdb/ProtectedAtomDB.h @@ -9,7 +9,6 @@ using namespace std; using namespace atoms; -using namespace atomdb_api_types; namespace atomdb { @@ -33,71 +32,73 @@ class ProtectedAtomDB : public AtomDB { bool allow_nested_indexing() override; bool composite_type_enabled() const override; atomdb_api_types::ProtectionMode is_protected() const override; + vector get_access_permissions( + const atomdb_api_types::PublicKey& public_key) const override; shared_ptr get_atom(const string& handle) override; - shared_ptr get_atom(const string& handle, const PublicKey& public_key); + shared_ptr get_atom(const string& handle, const atomdb_api_types::PublicKey& public_key); shared_ptr get_node(const string& handle) override; - shared_ptr get_node(const string& handle, const PublicKey& public_key); + shared_ptr get_node(const string& handle, const atomdb_api_types::PublicKey& public_key); shared_ptr get_link(const string& handle) override; - shared_ptr get_link(const string& handle, const PublicKey& public_key); + shared_ptr get_link(const string& handle, const atomdb_api_types::PublicKey& public_key); vector> get_matching_atoms(bool is_toplevel, Atom& key) override; vector> get_matching_atoms(bool is_toplevel, Atom& key, - const PublicKey& public_key); + const atomdb_api_types::PublicKey& public_key); shared_ptr query_for_pattern(const LinkSchema& link_schema) override; shared_ptr query_for_pattern(const LinkSchema& link_schema, - const PublicKey& public_key); + const atomdb_api_types::PublicKey& public_key); shared_ptr query_for_targets(const string& handle) override; shared_ptr query_for_targets(const string& handle, - const PublicKey& public_key); + const atomdb_api_types::PublicKey& public_key); shared_ptr query_for_incoming_set(const string& handle) override; shared_ptr query_for_incoming_set(const string& handle, - const PublicKey& public_key); + const atomdb_api_types::PublicKey& public_key); bool atom_exists(const string& handle) override; - bool atom_exists(const string& handle, const PublicKey& public_key); + bool atom_exists(const string& handle, const atomdb_api_types::PublicKey& public_key); bool node_exists(const string& handle) override; - bool node_exists(const string& handle, const PublicKey& public_key); + bool node_exists(const string& handle, const atomdb_api_types::PublicKey& public_key); bool link_exists(const string& handle) override; - bool link_exists(const string& handle, const PublicKey& public_key); + bool link_exists(const string& handle, const atomdb_api_types::PublicKey& public_key); set atoms_exist(const vector& handles) override; - set atoms_exist(const vector& handles, const PublicKey& public_key); + set atoms_exist(const vector& handles, const atomdb_api_types::PublicKey& public_key); set nodes_exist(const vector& handles) override; - set nodes_exist(const vector& handles, const PublicKey& public_key); + set nodes_exist(const vector& handles, const atomdb_api_types::PublicKey& public_key); set links_exist(const vector& handles) override; - set links_exist(const vector& handles, const PublicKey& public_key); + set links_exist(const vector& handles, const atomdb_api_types::PublicKey& public_key); string add_atom(const atoms::Atom* atom, const atoms::Merger* merger = NULL) override; string add_atom(const atoms::Atom* atom, - const PublicKey& public_key, + const atomdb_api_types::PublicKey& public_key, const atoms::Merger* merger = NULL); string add_node(const atoms::Node* node, const atoms::Merger* merger = NULL) override; string add_node(const atoms::Node* node, - const PublicKey& public_key, + const atomdb_api_types::PublicKey& public_key, const atoms::Merger* merger = NULL); string add_link(const atoms::Link* link, const atoms::Merger* merger = NULL) override; string add_link(const atoms::Link* link, - const PublicKey& public_key, + const atomdb_api_types::PublicKey& public_key, const atoms::Merger* merger = NULL); vector add_atoms(const vector& atom_list, bool is_transactional = false, const atoms::Merger* merger = NULL) override; vector add_atoms(const vector& atom_list, - const PublicKey& public_key, + const atomdb_api_types::PublicKey& public_key, bool is_transactional = false, const atoms::Merger* merger = NULL); @@ -105,7 +106,7 @@ class ProtectedAtomDB : public AtomDB { bool is_transactional = false, const atoms::Merger* merger = NULL) override; vector add_nodes(const vector& nodes, - const PublicKey& public_key, + const atomdb_api_types::PublicKey& public_key, bool is_transactional = false, const atoms::Merger* merger = NULL); @@ -113,51 +114,51 @@ class ProtectedAtomDB : public AtomDB { bool is_transactional = false, const atoms::Merger* merger = NULL) override; vector add_links(const vector& links, - const PublicKey& public_key, + const atomdb_api_types::PublicKey& public_key, bool is_transactional = false, const atoms::Merger* merger = NULL); bool delete_atom(const string& handle, bool delete_link_targets = false) override; bool delete_atom(const string& handle, - const PublicKey& public_key, + const atomdb_api_types::PublicKey& public_key, bool delete_link_targets = false); bool delete_node(const string& handle, bool delete_link_targets = false) override; bool delete_node(const string& handle, - const PublicKey& public_key, + const atomdb_api_types::PublicKey& public_key, bool delete_link_targets = false); bool delete_link(const string& handle, bool delete_link_targets = false) override; bool delete_link(const string& handle, - const PublicKey& public_key, + const atomdb_api_types::PublicKey& public_key, bool delete_link_targets = false); uint delete_atoms(const vector& handles, bool delete_link_targets = false) override; uint delete_atoms(const vector& handles, - const PublicKey& public_key, + const atomdb_api_types::PublicKey& public_key, bool delete_link_targets = false); uint delete_nodes(const vector& handles, bool delete_link_targets = false) override; uint delete_nodes(const vector& handles, - const PublicKey& public_key, + const atomdb_api_types::PublicKey& public_key, bool delete_link_targets = false); uint delete_links(const vector& handles, bool delete_link_targets = false) override; uint delete_links(const vector& handles, - const PublicKey& public_key, + const atomdb_api_types::PublicKey& public_key, bool delete_link_targets = false); void re_index_patterns(bool flush_patterns = true) override; - void re_index_patterns(const PublicKey& public_key, bool flush_patterns = true); + void re_index_patterns(const atomdb_api_types::PublicKey& public_key, bool flush_patterns = true); size_t node_count() const override; - size_t node_count(const PublicKey& public_key) const; + size_t node_count(const atomdb_api_types::PublicKey& public_key) const; size_t link_count() const override; - size_t link_count(const PublicKey& public_key) const; + size_t link_count(const atomdb_api_types::PublicKey& public_key) const; size_t atom_count() const override; - size_t atom_count(const PublicKey& public_key) const; + size_t atom_count(const atomdb_api_types::PublicKey& public_key) const; private: shared_ptr backend; diff --git a/src/atomdb/remotedb/RemoteAtomDBPeer.cc b/src/atomdb/remotedb/RemoteAtomDBPeer.cc index 37e96c991..6d8bdcd00 100644 --- a/src/atomdb/remotedb/RemoteAtomDBPeer.cc +++ b/src/atomdb/remotedb/RemoteAtomDBPeer.cc @@ -61,11 +61,11 @@ void RemoteAtomDBPeer::invalidate_fetched_templates() { } atomdb_api_types::ProtectionMode RemoteAtomDBPeer::is_protected() const { - if (local_persistence_) { - auto mode = local_persistence_->is_protected(); - if (mode != atomdb_api_types::ProtectionMode::UNPROTECTED) { - return mode; - } + if (local_persistence_ && + local_persistence_->is_protected() == atomdb_api_types::ProtectionMode::PROTECTED) { + RAISE_ERROR("[RemoteDB(" + uid_ + + ")] local_persistence is PROTECTED, but RemoteAtomDBPeer does not support protected " + "local persistence"); } if (atomdb_) { return atomdb_->is_protected(); diff --git a/src/tests/cpp/BUILD b/src/tests/cpp/BUILD index 2db125a54..e026ea3e0 100644 --- a/src/tests/cpp/BUILD +++ b/src/tests/cpp/BUILD @@ -861,7 +861,6 @@ cc_test( linkstatic = 1, deps = [ "//atomdb:protected_atomdb", - "//atomdb:public_key", "//atomdb/inmemorydb:inmemorydb_lib", "//commons/atoms:atoms_lib", "@com_github_google_googletest//:gtest_main", diff --git a/src/tests/cpp/inmemorydb_test.cc b/src/tests/cpp/inmemorydb_test.cc index 9ccba7e73..ded345772 100644 --- a/src/tests/cpp/inmemorydb_test.cc +++ b/src/tests/cpp/inmemorydb_test.cc @@ -1105,6 +1105,10 @@ TEST_F(InMemoryDBTest, GetAccessPermissionsReturnsEmpty) { EXPECT_TRUE(permissions.empty()); } +TEST_F(InMemoryDBTest, IsUnprotected) { + EXPECT_EQ(db->is_protected(), ProtectionMode::UNPROTECTED); +} + int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); diff --git a/src/tests/cpp/protected_atomdb_test.cc b/src/tests/cpp/protected_atomdb_test.cc index 10ff885db..bf148e82d 100644 --- a/src/tests/cpp/protected_atomdb_test.cc +++ b/src/tests/cpp/protected_atomdb_test.cc @@ -3,6 +3,7 @@ #include #include #include +#include #include #include "InMemoryDB.h" @@ -27,26 +28,16 @@ shared_ptr make_protected_db(const string& context = "protected TEST(PublicKeyTest, SingleKey) { PublicKey key("pk1"); - EXPECT_TRUE(key.is_single_key()); - EXPECT_FALSE(key.is_peer_map()); - EXPECT_EQ(key.key(), "pk1"); - EXPECT_THROW(key.keys(), runtime_error); + ASSERT_TRUE(holds_alternative(key.value)); + EXPECT_EQ(get(key.value), "pk1"); } TEST(PublicKeyTest, PeerMap) { PublicKey key(map{{"peer1", "k1"}, {"peer2", "k2"}}); - EXPECT_FALSE(key.is_single_key()); - EXPECT_TRUE(key.is_peer_map()); - EXPECT_EQ(key.keys().size(), 2u); - EXPECT_EQ(key.keys().at("peer1"), "k1"); - EXPECT_THROW(key.key(), runtime_error); -} - -TEST(PublicKeyTest, RejectsEmpty) { - EXPECT_THROW(PublicKey(""), runtime_error); - EXPECT_THROW(PublicKey(map{}), runtime_error); - EXPECT_THROW(PublicKey(map{{"", "k"}}), runtime_error); - EXPECT_THROW(PublicKey(map{{"peer", ""}}), runtime_error); + ASSERT_TRUE((holds_alternative>(key.value))); + const auto& keys = get>(key.value); + EXPECT_EQ(keys.size(), 2u); + EXPECT_EQ(keys.at("peer1"), "k1"); } TEST(ProtectedAtomDBTest, RejectsNullBackend) { EXPECT_THROW(ProtectedAtomDB(nullptr), runtime_error); } @@ -80,6 +71,14 @@ TEST(ProtectedAtomDBTest, DelegatesCapabilityFlagsToBackend) { EXPECT_EQ(db.composite_type_enabled(), backend->composite_type_enabled()); } +TEST(ProtectedAtomDBTest, DelegatesAccessPermissionsToBackend) { + auto backend = make_shared("protected_perms_"); + ProtectedAtomDB db(backend); + PublicKey key("any_key"); + + EXPECT_EQ(db.get_access_permissions(key).size(), backend->get_access_permissions(key).size()); +} + TEST(ProtectedAtomDBTest, RejectsAccessWithoutPublicKey) { auto db = make_protected_db("protected_no_key_"); Node node("Symbol", "\"x\""); diff --git a/src/tests/cpp/redis_mongodb_test.cc b/src/tests/cpp/redis_mongodb_test.cc index a94d2a9dc..d3845ca67 100644 --- a/src/tests/cpp/redis_mongodb_test.cc +++ b/src/tests/cpp/redis_mongodb_test.cc @@ -21,6 +21,7 @@ #include "MettaMapping.h" #include "MockAnimalsData.h" #include "Node.h" +#include "ProtectedAtomDB.h" #include "RedisMongoDB.h" #include "TestAtomDBJsonConfig.h" #include "UntypedVariable.h" @@ -1488,6 +1489,7 @@ TEST_F(RedisMongoDBTest, IsProtectedFollowsPersistedConfig) { auto loaded = AtomDBFactory::create(test_atomdb_json_config(), "test_"); EXPECT_EQ(loaded->is_protected(), atomdb_api_types::ProtectionMode::PROTECTED); + EXPECT_NE(dynamic_pointer_cast(loaded), nullptr); collection.delete_many({}); } diff --git a/src/tests/cpp/remote_atomdb_test.cc b/src/tests/cpp/remote_atomdb_test.cc index cf2565725..531126d02 100644 --- a/src/tests/cpp/remote_atomdb_test.cc +++ b/src/tests/cpp/remote_atomdb_test.cc @@ -910,7 +910,7 @@ TEST(RemoteAtomDBFederationTest, CompositeTypeEnabledAggregation) { } } -TEST(RemoteAtomDBFederationTest, PeerIsProtectedWhenEitherBackendIsProtected) { +TEST(RemoteAtomDBFederationTest, PeerIsProtectedFollowsRemoteBackend) { // Neither the remote backend nor the local persistence is protected. { auto remote = make_shared("prot_none_remote_"); @@ -934,12 +934,12 @@ TEST(RemoteAtomDBFederationTest, PeerIsProtectedWhenEitherBackendIsProtected) { EXPECT_EQ(peer->is_protected(), ProtectionMode::PROTECTED); } - // Protected local persistence. + // Protected local persistence is not supported. { auto remote = make_shared("prot_local_remote_"); auto local = make_shared("prot_local_local_"); auto peer = make_shared(remote, local, "peer"); - EXPECT_EQ(peer->is_protected(), ProtectionMode::PROTECTED); + EXPECT_THROW(peer->is_protected(), runtime_error); } } From 14cd6c542381fcc04eb516ba8c268a11a71565f3 Mon Sep 17 00:00:00 2001 From: marcocapozzoli Date: Mon, 17 Aug 2026 09:48:52 -0300 Subject: [PATCH 13/21] Fix tests --- src/atomdb/ProtectedAtomDB.cc | 30 +++++++++++++++----------- src/atomdb/ProtectedAtomDB.h | 21 ++++++++++-------- src/tests/cpp/inmemorydb_test.cc | 4 +--- src/tests/cpp/redis_mongodb_test.cc | 33 +++++++++++++++++++++++------ 4 files changed, 57 insertions(+), 31 deletions(-) diff --git a/src/atomdb/ProtectedAtomDB.cc b/src/atomdb/ProtectedAtomDB.cc index b3f4fd671..9dc5aea49 100644 --- a/src/atomdb/ProtectedAtomDB.cc +++ b/src/atomdb/ProtectedAtomDB.cc @@ -19,26 +19,28 @@ ProtectedAtomDB::ProtectedAtomDB(shared_ptr backend) : backend(std::move // -------------------------------------------------------------------------------- // Public methods -shared_ptr ProtectedAtomDB::get_atom(const string& handle, const atomdb_api_types::PublicKey& public_key) { +shared_ptr ProtectedAtomDB::get_atom(const string& handle, + const atomdb_api_types::PublicKey& public_key) { RAISE_ERROR("ProtectedAtomDB::get_atom(handle, public_key) is not implemented yet"); } -shared_ptr ProtectedAtomDB::get_node(const string& handle, const atomdb_api_types::PublicKey& public_key) { +shared_ptr ProtectedAtomDB::get_node(const string& handle, + const atomdb_api_types::PublicKey& public_key) { RAISE_ERROR("ProtectedAtomDB::get_node(handle, public_key) is not implemented yet"); } -shared_ptr ProtectedAtomDB::get_link(const string& handle, const atomdb_api_types::PublicKey& public_key) { +shared_ptr ProtectedAtomDB::get_link(const string& handle, + const atomdb_api_types::PublicKey& public_key) { RAISE_ERROR("ProtectedAtomDB::get_link(handle, public_key) is not implemented yet"); } -vector> ProtectedAtomDB::get_matching_atoms(bool is_toplevel, - Atom& key, - const atomdb_api_types::PublicKey& public_key) { +vector> ProtectedAtomDB::get_matching_atoms( + bool is_toplevel, Atom& key, const atomdb_api_types::PublicKey& public_key) { RAISE_ERROR("ProtectedAtomDB::get_matching_atoms(..., public_key) is not implemented yet"); } -shared_ptr ProtectedAtomDB::query_for_pattern(const LinkSchema& link_schema, - const atomdb_api_types::PublicKey& public_key) { +shared_ptr ProtectedAtomDB::query_for_pattern( + const LinkSchema& link_schema, const atomdb_api_types::PublicKey& public_key) { RAISE_ERROR("ProtectedAtomDB::query_for_pattern(link_schema, public_key) is not implemented yet"); } @@ -64,15 +66,18 @@ bool ProtectedAtomDB::link_exists(const string& handle, const atomdb_api_types:: RAISE_ERROR("ProtectedAtomDB::link_exists(handle, public_key) is not implemented yet"); } -set ProtectedAtomDB::atoms_exist(const vector& handles, const atomdb_api_types::PublicKey& public_key) { +set ProtectedAtomDB::atoms_exist(const vector& handles, + const atomdb_api_types::PublicKey& public_key) { RAISE_ERROR("ProtectedAtomDB::atoms_exist(handles, public_key) is not implemented yet"); } -set ProtectedAtomDB::nodes_exist(const vector& handles, const atomdb_api_types::PublicKey& public_key) { +set ProtectedAtomDB::nodes_exist(const vector& handles, + const atomdb_api_types::PublicKey& public_key) { RAISE_ERROR("ProtectedAtomDB::nodes_exist(handles, public_key) is not implemented yet"); } -set ProtectedAtomDB::links_exist(const vector& handles, const atomdb_api_types::PublicKey& public_key) { +set ProtectedAtomDB::links_exist(const vector& handles, + const atomdb_api_types::PublicKey& public_key) { RAISE_ERROR("ProtectedAtomDB::links_exist(handles, public_key) is not implemented yet"); } @@ -151,7 +156,8 @@ uint ProtectedAtomDB::delete_links(const vector& handles, RAISE_ERROR("ProtectedAtomDB::delete_links(handles, public_key) is not implemented yet"); } -void ProtectedAtomDB::re_index_patterns(const atomdb_api_types::PublicKey& public_key, bool flush_patterns) { +void ProtectedAtomDB::re_index_patterns(const atomdb_api_types::PublicKey& public_key, + bool flush_patterns) { RAISE_ERROR("ProtectedAtomDB::re_index_patterns(public_key) is not implemented yet"); } diff --git a/src/atomdb/ProtectedAtomDB.h b/src/atomdb/ProtectedAtomDB.h index 0c6698e09..71c15ef96 100644 --- a/src/atomdb/ProtectedAtomDB.h +++ b/src/atomdb/ProtectedAtomDB.h @@ -50,16 +50,16 @@ class ProtectedAtomDB : public AtomDB { const atomdb_api_types::PublicKey& public_key); shared_ptr query_for_pattern(const LinkSchema& link_schema) override; - shared_ptr query_for_pattern(const LinkSchema& link_schema, - const atomdb_api_types::PublicKey& public_key); + shared_ptr query_for_pattern( + const LinkSchema& link_schema, const atomdb_api_types::PublicKey& public_key); shared_ptr query_for_targets(const string& handle) override; - shared_ptr query_for_targets(const string& handle, - const atomdb_api_types::PublicKey& public_key); + shared_ptr query_for_targets( + const string& handle, const atomdb_api_types::PublicKey& public_key); shared_ptr query_for_incoming_set(const string& handle) override; - shared_ptr query_for_incoming_set(const string& handle, - const atomdb_api_types::PublicKey& public_key); + shared_ptr query_for_incoming_set( + const string& handle, const atomdb_api_types::PublicKey& public_key); bool atom_exists(const string& handle) override; bool atom_exists(const string& handle, const atomdb_api_types::PublicKey& public_key); @@ -71,13 +71,16 @@ class ProtectedAtomDB : public AtomDB { bool link_exists(const string& handle, const atomdb_api_types::PublicKey& public_key); set atoms_exist(const vector& handles) override; - set atoms_exist(const vector& handles, const atomdb_api_types::PublicKey& public_key); + set atoms_exist(const vector& handles, + const atomdb_api_types::PublicKey& public_key); set nodes_exist(const vector& handles) override; - set nodes_exist(const vector& handles, const atomdb_api_types::PublicKey& public_key); + set nodes_exist(const vector& handles, + const atomdb_api_types::PublicKey& public_key); set links_exist(const vector& handles) override; - set links_exist(const vector& handles, const atomdb_api_types::PublicKey& public_key); + set links_exist(const vector& handles, + const atomdb_api_types::PublicKey& public_key); string add_atom(const atoms::Atom* atom, const atoms::Merger* merger = NULL) override; string add_atom(const atoms::Atom* atom, diff --git a/src/tests/cpp/inmemorydb_test.cc b/src/tests/cpp/inmemorydb_test.cc index ded345772..f684e2bf8 100644 --- a/src/tests/cpp/inmemorydb_test.cc +++ b/src/tests/cpp/inmemorydb_test.cc @@ -1105,9 +1105,7 @@ TEST_F(InMemoryDBTest, GetAccessPermissionsReturnsEmpty) { EXPECT_TRUE(permissions.empty()); } -TEST_F(InMemoryDBTest, IsUnprotected) { - EXPECT_EQ(db->is_protected(), ProtectionMode::UNPROTECTED); -} +TEST_F(InMemoryDBTest, IsUnprotected) { EXPECT_EQ(db->is_protected(), ProtectionMode::UNPROTECTED); } int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); diff --git a/src/tests/cpp/redis_mongodb_test.cc b/src/tests/cpp/redis_mongodb_test.cc index d3845ca67..6b744fb1b 100644 --- a/src/tests/cpp/redis_mongodb_test.cc +++ b/src/tests/cpp/redis_mongodb_test.cc @@ -21,7 +21,6 @@ #include "MettaMapping.h" #include "MockAnimalsData.h" #include "Node.h" -#include "ProtectedAtomDB.h" #include "RedisMongoDB.h" #include "TestAtomDBJsonConfig.h" #include "UntypedVariable.h" @@ -92,6 +91,12 @@ class LinkSchemaHandle : public LinkSchema { string fixed_handle; }; +class TestRedisMongoDB : public RedisMongoDB { + public: + TestRedisMongoDB(const string& context, const JsonConfig& config) + : RedisMongoDB(context, true, config) {} +}; + TEST_F(RedisMongoDBTest, ConcurrentQueryForPattern) { const int num_threads = 4; vector threads; @@ -1476,20 +1481,34 @@ TEST_F(RedisMongoDBTest, GetAccessPermissionsRejectsInvalidDocument) { collection.delete_many({}); } -TEST_F(RedisMongoDBTest, IsProtectedFollowsPersistedConfig) { +TEST_F(RedisMongoDBTest, IsProtectedWhenPersistedConfigIsTrue) { using bsoncxx::builder::basic::kvp; using bsoncxx::builder::basic::make_document; - EXPECT_EQ(db->is_protected(), atomdb_api_types::ProtectionMode::UNPROTECTED); - auto conn = db->get_mongo_pool()->acquire(); auto collection = (*conn)[RedisMongoDB::MONGODB_DB_NAME][RedisMongoDB::MONGODB_CONFIG_COLLECTION_NAME]; + collection.delete_many({}); collection.insert_one(make_document(kvp("protected", true))); - auto loaded = AtomDBFactory::create(test_atomdb_json_config(), "test_"); - EXPECT_EQ(loaded->is_protected(), atomdb_api_types::ProtectionMode::PROTECTED); - EXPECT_NE(dynamic_pointer_cast(loaded), nullptr); + TestRedisMongoDB loaded("test_", test_atomdb_json_config()); + EXPECT_EQ(loaded.is_protected(), atomdb_api_types::ProtectionMode::PROTECTED); + + collection.delete_many({}); +} + +TEST_F(RedisMongoDBTest, IsProtectedWhenPersistedConfigOmitsField) { + using bsoncxx::builder::basic::kvp; + using bsoncxx::builder::basic::make_document; + + auto conn = db->get_mongo_pool()->acquire(); + auto collection = + (*conn)[RedisMongoDB::MONGODB_DB_NAME][RedisMongoDB::MONGODB_CONFIG_COLLECTION_NAME]; + collection.delete_many({}); + collection.insert_one(make_document(kvp("other", "value"))); + + TestRedisMongoDB loaded("test_", test_atomdb_json_config()); + EXPECT_EQ(loaded.is_protected(), atomdb_api_types::ProtectionMode::UNPROTECTED); collection.delete_many({}); } From 4d87c17fcba42c4a9de0fc98b93d99245ba91512 Mon Sep 17 00:00:00 2001 From: marcocapozzoli Date: Tue, 18 Aug 2026 09:13:48 -0300 Subject: [PATCH 14/21] Change AtomDB::is_protected() to AtomDB::get_protection_mode() --- src/atomdb/AtomDB.h | 2 +- src/atomdb/AtomDBAPITypes.h | 2 +- src/atomdb/AtomDBFactory.cc | 2 +- src/atomdb/ProtectedAtomDB.cc | 20 +++++------------ src/atomdb/ProtectedAtomDB.h | 4 +--- src/atomdb/adapterdb/AdapterDB.cc | 4 ++-- src/atomdb/adapterdb/AdapterDB.h | 2 +- src/atomdb/inmemorydb/InMemoryDB.h | 2 +- src/atomdb/redis_mongodb/RedisMongoDB.cc | 14 ++++++------ src/atomdb/redis_mongodb/RedisMongoDB.h | 6 ++--- src/atomdb/remotedb/RemoteAtomDB.cc | 4 ++-- src/atomdb/remotedb/RemoteAtomDB.h | 2 +- src/atomdb/remotedb/RemoteAtomDBPeer.cc | 6 ++--- src/atomdb/remotedb/RemoteAtomDBPeer.h | 2 +- src/tests/cpp/atomdb_factory_test.cc | 2 +- src/tests/cpp/inmemorydb_test.cc | 4 +++- src/tests/cpp/protected_atomdb_test.cc | 22 ++++--------------- src/tests/cpp/redis_mongodb_test.cc | 4 ++-- src/tests/cpp/remote_atomdb_test.cc | 16 +++++++------- src/tests/cpp/test_commons/mocks/MockAtomDB.h | 3 ++- 20 files changed, 51 insertions(+), 72 deletions(-) diff --git a/src/atomdb/AtomDB.h b/src/atomdb/AtomDB.h index 46e9534b0..f36dd86a4 100644 --- a/src/atomdb/AtomDB.h +++ b/src/atomdb/AtomDB.h @@ -52,7 +52,7 @@ class AtomDB : public HandleDecoder { virtual bool allow_nested_indexing() = 0; virtual bool composite_type_enabled() const = 0; - virtual atomdb_api_types::ProtectionMode is_protected() const = 0; + virtual atomdb_api_types::ProtectionMode get_protection_mode() const = 0; virtual shared_ptr get_atom(const string& handle) = 0; // HandleDecoder interface virtual shared_ptr get_node(const string& handle) = 0; diff --git a/src/atomdb/AtomDBAPITypes.h b/src/atomdb/AtomDBAPITypes.h index 1f0f2f447..b2f040d3a 100644 --- a/src/atomdb/AtomDBAPITypes.h +++ b/src/atomdb/AtomDBAPITypes.h @@ -130,7 +130,7 @@ class AccessPermissionDocument { * - PROTECTED: wrap and apply authorization post-processing (filter) after queries. * - FORWARD: wrap and pass access keys through, but do not post-process locally */ -enum class ProtectionMode { UNPROTECTED, PROTECTED, FORWARD }; +enum class ProtectionMode { UNPROTECTED = 0, FORWARD, PROTECTED }; } // namespace atomdb_api_types } // namespace atomdb diff --git a/src/atomdb/AtomDBFactory.cc b/src/atomdb/AtomDBFactory.cc index 13886e08d..dcb16398c 100644 --- a/src/atomdb/AtomDBFactory.cc +++ b/src/atomdb/AtomDBFactory.cc @@ -107,7 +107,7 @@ shared_ptr AtomDBFactory::wrap_if_protected(shared_ptr atomdb) { if (!atomdb) { RAISE_ERROR("AtomDBFactory::wrap_if_protected() received null atomdb"); } - if (atomdb->is_protected() == atomdb_api_types::ProtectionMode::UNPROTECTED || + if (atomdb->get_protection_mode() == atomdb_api_types::ProtectionMode::UNPROTECTED || dynamic_pointer_cast(atomdb)) { return atomdb; } diff --git a/src/atomdb/ProtectedAtomDB.cc b/src/atomdb/ProtectedAtomDB.cc index 9dc5aea49..5ac645e06 100644 --- a/src/atomdb/ProtectedAtomDB.cc +++ b/src/atomdb/ProtectedAtomDB.cc @@ -9,11 +9,11 @@ using namespace commons; // -------------------------------------------------------------------------------- // Constructors and destructors -ProtectedAtomDB::ProtectedAtomDB(shared_ptr backend) : backend(std::move(backend)) { +ProtectedAtomDB::ProtectedAtomDB(shared_ptr backend) : backend(backend) { if (this->backend == nullptr) { RAISE_ERROR("ProtectedAtomDB requires a non-null backend AtomDB"); } - LOG_INFO("ProtectedAtomDB initialized!"); + LOG_INFO("ProtectedAtomDB initialized"); } // -------------------------------------------------------------------------------- @@ -177,17 +177,8 @@ bool ProtectedAtomDB::allow_nested_indexing() { return this->backend->allow_nest bool ProtectedAtomDB::composite_type_enabled() const { return this->backend->composite_type_enabled(); } -atomdb_api_types::ProtectionMode ProtectedAtomDB::is_protected() const { - auto backend_mode = this->backend->is_protected(); - if (backend_mode == atomdb_api_types::ProtectionMode::FORWARD) { - return atomdb_api_types::ProtectionMode::FORWARD; - } - return atomdb_api_types::ProtectionMode::PROTECTED; -} - -vector ProtectedAtomDB::get_access_permissions( - const atomdb_api_types::PublicKey& public_key) const { - return this->backend->get_access_permissions(public_key); +atomdb_api_types::ProtectionMode ProtectedAtomDB::get_protection_mode() const { + return atomdb_api_types::ProtectionMode::UNPROTECTED; } // -------------------------------------------------------------------------------- @@ -309,5 +300,6 @@ size_t ProtectedAtomDB::atom_count() const { raise_public_key_required("atom_cou void ProtectedAtomDB::raise_public_key_required(const string& method_name) { RAISE_ERROR("ProtectedAtomDB::" + method_name + - "() is unavailable without a PublicKey; use the overload that accepts one"); + "() is unavailable in protected AtomDBs. Use the public API in ProtectedAtomDB passing " + "a PublicKey."); } diff --git a/src/atomdb/ProtectedAtomDB.h b/src/atomdb/ProtectedAtomDB.h index 71c15ef96..67f4489e5 100644 --- a/src/atomdb/ProtectedAtomDB.h +++ b/src/atomdb/ProtectedAtomDB.h @@ -31,9 +31,7 @@ class ProtectedAtomDB : public AtomDB { bool allow_nested_indexing() override; bool composite_type_enabled() const override; - atomdb_api_types::ProtectionMode is_protected() const override; - vector get_access_permissions( - const atomdb_api_types::PublicKey& public_key) const override; + atomdb_api_types::ProtectionMode get_protection_mode() const override; shared_ptr get_atom(const string& handle) override; shared_ptr get_atom(const string& handle, const atomdb_api_types::PublicKey& public_key); diff --git a/src/atomdb/adapterdb/AdapterDB.cc b/src/atomdb/adapterdb/AdapterDB.cc index fc124a323..1ad6aa946 100644 --- a/src/atomdb/adapterdb/AdapterDB.cc +++ b/src/atomdb/adapterdb/AdapterDB.cc @@ -70,9 +70,9 @@ vector AdapterDB::get_access_permiss return this->atomdb_backend->get_access_permissions(public_key); } -atomdb_api_types::ProtectionMode AdapterDB::is_protected() const { +atomdb_api_types::ProtectionMode AdapterDB::get_protection_mode() const { this->ensure_backend_ready(); - return this->atomdb_backend->is_protected(); + return this->atomdb_backend->get_protection_mode(); } shared_ptr AdapterDB::get_atom(const string& handle) { diff --git a/src/atomdb/adapterdb/AdapterDB.h b/src/atomdb/adapterdb/AdapterDB.h index 57d798652..464147a77 100644 --- a/src/atomdb/adapterdb/AdapterDB.h +++ b/src/atomdb/adapterdb/AdapterDB.h @@ -62,7 +62,7 @@ class AdapterDB : public AtomDB { */ bool composite_type_enabled() const override; - atomdb_api_types::ProtectionMode is_protected() const override; + atomdb_api_types::ProtectionMode get_protection_mode() const override; shared_ptr get_atom(const string& handle) override; shared_ptr get_node(const string& handle) override; diff --git a/src/atomdb/inmemorydb/InMemoryDB.h b/src/atomdb/inmemorydb/InMemoryDB.h index 154b1c07a..f269ed1cb 100644 --- a/src/atomdb/inmemorydb/InMemoryDB.h +++ b/src/atomdb/inmemorydb/InMemoryDB.h @@ -41,7 +41,7 @@ class InMemoryDB : public AtomDB { bool allow_nested_indexing() override; bool composite_type_enabled() const override { return false; } - atomdb_api_types::ProtectionMode is_protected() const override { + atomdb_api_types::ProtectionMode get_protection_mode() const override { return atomdb_api_types::ProtectionMode::UNPROTECTED; } diff --git a/src/atomdb/redis_mongodb/RedisMongoDB.cc b/src/atomdb/redis_mongodb/RedisMongoDB.cc index 1aa82579c..af14222cb 100644 --- a/src/atomdb/redis_mongodb/RedisMongoDB.cc +++ b/src/atomdb/redis_mongodb/RedisMongoDB.cc @@ -45,7 +45,7 @@ RedisMongoDB::RedisMongoDB(const string& context, bool skip_redis, const JsonCon skip_redis_(skip_redis), composite_type_enabled_(config.at_path("composite_type_enabled").get_or(true)), cluster_flag(false), - protected_flag(false) { + protection_mode(atomdb_api_types::ProtectionMode::UNPROTECTED) { initialize_statics(context); mongodb_setup(config); load_pattern_index_schema(); @@ -143,9 +143,8 @@ optional RedisMongoDB::load_access_p return atomdb_api_types::AccessPermissionDocument(key, document["full_access"].get(), entries); } -atomdb_api_types::ProtectionMode RedisMongoDB::is_protected() const { - return this->protected_flag ? atomdb_api_types::ProtectionMode::PROTECTED - : atomdb_api_types::ProtectionMode::UNPROTECTED; +atomdb_api_types::ProtectionMode RedisMongoDB::get_protection_mode() const { + return this->protection_mode; } void RedisMongoDB::redis_setup(const JsonConfig& config) { @@ -198,7 +197,7 @@ void RedisMongoDB::mongodb_setup(const JsonConfig& config) { bsoncxx::builder::basic::make_document(bsoncxx::builder::basic::kvp("ping", 1)); mongodb.run_command(ping_cmd.view()); LOG_INFO("Connected to MongoDB at " << address); - load_protected_flag(); + this->load_protection_mode(); } catch (const std::exception& e) { RAISE_ERROR(e.what()); } @@ -1324,12 +1323,13 @@ void RedisMongoDB::add_pattern_index_schema(const string& tokens, this->pattern_index_schema_next_priority++; } -void RedisMongoDB::load_protected_flag() { +void RedisMongoDB::load_protection_mode() { auto conn = this->mongodb_pool->acquire(); auto config_collection = (*conn)[MONGODB_DB_NAME][MONGODB_CONFIG_COLLECTION_NAME]; auto config_doc = config_collection.find_one( bsoncxx::builder::basic::make_document(bsoncxx::builder::basic::kvp("protected", true))); - this->protected_flag = static_cast(config_doc); + this->protection_mode = static_cast( + config_doc ? ProtectionMode::PROTECTED : ProtectionMode::UNPROTECTED); } void RedisMongoDB::load_pattern_index_schema() { diff --git a/src/atomdb/redis_mongodb/RedisMongoDB.h b/src/atomdb/redis_mongodb/RedisMongoDB.h index 7f1b1767b..120a66354 100644 --- a/src/atomdb/redis_mongodb/RedisMongoDB.h +++ b/src/atomdb/redis_mongodb/RedisMongoDB.h @@ -39,7 +39,7 @@ class RedisMongoDB : public AtomDB { */ vector get_access_permissions( const atomdb_api_types::PublicKey& public_key) const override; - atomdb_api_types::ProtectionMode is_protected() const override; + atomdb_api_types::ProtectionMode get_protection_mode() const override; static string REDIS_PATTERNS_PREFIX; static string REDIS_OUTGOING_PREFIX; @@ -165,7 +165,7 @@ class RedisMongoDB : public AtomDB { bool skip_redis_; bool composite_type_enabled_; bool cluster_flag; - bool protected_flag; + atomdb_api_types::ProtectionMode protection_mode; RedisContextPool* redis_pool; mongocxx::pool* mongodb_pool; atomic patterns_next_score{0}; @@ -215,7 +215,7 @@ class RedisMongoDB : public AtomDB { void update_incoming_set(const string& key, const string& value); void load_pattern_index_schema(); - void load_protected_flag(); + void load_protection_mode(); vector match_pattern_index_schema(const Link* link); vector> index_entries_combinations(unsigned int arity); diff --git a/src/atomdb/remotedb/RemoteAtomDB.cc b/src/atomdb/remotedb/RemoteAtomDB.cc index d11787e39..c0470d71c 100644 --- a/src/atomdb/remotedb/RemoteAtomDB.cc +++ b/src/atomdb/remotedb/RemoteAtomDB.cc @@ -31,9 +31,9 @@ RemoteAtomDB::RemoteAtomDB(map> peers) RemoteAtomDB::~RemoteAtomDB() = default; -atomdb_api_types::ProtectionMode RemoteAtomDB::is_protected() const { +atomdb_api_types::ProtectionMode RemoteAtomDB::get_protection_mode() const { for (auto& [uid, peer] : remote_db_) { - if (peer->is_protected() != atomdb_api_types::ProtectionMode::UNPROTECTED) { + if (peer->get_protection_mode() != atomdb_api_types::ProtectionMode::UNPROTECTED) { return atomdb_api_types::ProtectionMode::FORWARD; } } diff --git a/src/atomdb/remotedb/RemoteAtomDB.h b/src/atomdb/remotedb/RemoteAtomDB.h index c546b4252..30bd41d12 100644 --- a/src/atomdb/remotedb/RemoteAtomDB.h +++ b/src/atomdb/remotedb/RemoteAtomDB.h @@ -28,7 +28,7 @@ class RemoteAtomDB : public AtomDB { bool allow_nested_indexing() override; bool composite_type_enabled() const override; - atomdb_api_types::ProtectionMode is_protected() const override; + atomdb_api_types::ProtectionMode get_protection_mode() const override; shared_ptr get_atom(const string& handle) override; shared_ptr get_node(const string& handle) override; diff --git a/src/atomdb/remotedb/RemoteAtomDBPeer.cc b/src/atomdb/remotedb/RemoteAtomDBPeer.cc index ddc250e56..cda11dc89 100644 --- a/src/atomdb/remotedb/RemoteAtomDBPeer.cc +++ b/src/atomdb/remotedb/RemoteAtomDBPeer.cc @@ -60,15 +60,15 @@ void RemoteAtomDBPeer::invalidate_fetched_templates() { fetched_link_templates_.clear(); } -atomdb_api_types::ProtectionMode RemoteAtomDBPeer::is_protected() const { +atomdb_api_types::ProtectionMode RemoteAtomDBPeer::get_protection_mode() const { if (local_persistence_ && - local_persistence_->is_protected() == atomdb_api_types::ProtectionMode::PROTECTED) { + local_persistence_->get_protection_mode() == atomdb_api_types::ProtectionMode::PROTECTED) { RAISE_ERROR("[RemoteDB(" + uid_ + ")] local_persistence is PROTECTED, but RemoteAtomDBPeer does not support protected " "local persistence"); } if (atomdb_) { - return atomdb_->is_protected(); + return atomdb_->get_protection_mode(); } return atomdb_api_types::ProtectionMode::UNPROTECTED; } diff --git a/src/atomdb/remotedb/RemoteAtomDBPeer.h b/src/atomdb/remotedb/RemoteAtomDBPeer.h index f969df0bb..1debda10f 100644 --- a/src/atomdb/remotedb/RemoteAtomDBPeer.h +++ b/src/atomdb/remotedb/RemoteAtomDBPeer.h @@ -43,7 +43,7 @@ class RemoteAtomDBPeer : public AtomDB, public processor::ThreadMethod { bool allow_nested_indexing() override; bool composite_type_enabled() const override; - atomdb_api_types::ProtectionMode is_protected() const override; + atomdb_api_types::ProtectionMode get_protection_mode() const override; shared_ptr get_atom(const string& handle) override; shared_ptr get_node(const string& handle) override; diff --git a/src/tests/cpp/atomdb_factory_test.cc b/src/tests/cpp/atomdb_factory_test.cc index c0a98ddf0..7aa20f346 100644 --- a/src/tests/cpp/atomdb_factory_test.cc +++ b/src/tests/cpp/atomdb_factory_test.cc @@ -161,6 +161,6 @@ TEST(AtomDBFactoryTest, CreateAdapterDBRequiresBackendType) { TEST(AtomDBFactoryTest, CreateInMemoryDBIsNotProtected) { auto db = AtomDBFactory::create(config_with_type("inmemorydb"), "factory_unprotected_"); ASSERT_NE(db, nullptr); - EXPECT_EQ(db->is_protected(), ProtectionMode::UNPROTECTED); + EXPECT_EQ(db->get_protection_mode(), ProtectionMode::UNPROTECTED); EXPECT_EQ(dynamic_pointer_cast(db), nullptr); } diff --git a/src/tests/cpp/inmemorydb_test.cc b/src/tests/cpp/inmemorydb_test.cc index f684e2bf8..c9384c017 100644 --- a/src/tests/cpp/inmemorydb_test.cc +++ b/src/tests/cpp/inmemorydb_test.cc @@ -1105,7 +1105,9 @@ TEST_F(InMemoryDBTest, GetAccessPermissionsReturnsEmpty) { EXPECT_TRUE(permissions.empty()); } -TEST_F(InMemoryDBTest, IsUnprotected) { EXPECT_EQ(db->is_protected(), ProtectionMode::UNPROTECTED); } +TEST_F(InMemoryDBTest, IsUnprotected) { + EXPECT_EQ(db->get_protection_mode(), ProtectionMode::UNPROTECTED); +} int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); diff --git a/src/tests/cpp/protected_atomdb_test.cc b/src/tests/cpp/protected_atomdb_test.cc index bf148e82d..89c2e99c3 100644 --- a/src/tests/cpp/protected_atomdb_test.cc +++ b/src/tests/cpp/protected_atomdb_test.cc @@ -26,41 +26,27 @@ shared_ptr make_protected_db(const string& context = "protected } // namespace -TEST(PublicKeyTest, SingleKey) { - PublicKey key("pk1"); - ASSERT_TRUE(holds_alternative(key.value)); - EXPECT_EQ(get(key.value), "pk1"); -} - -TEST(PublicKeyTest, PeerMap) { - PublicKey key(map{{"peer1", "k1"}, {"peer2", "k2"}}); - ASSERT_TRUE((holds_alternative>(key.value))); - const auto& keys = get>(key.value); - EXPECT_EQ(keys.size(), 2u); - EXPECT_EQ(keys.at("peer1"), "k1"); -} - TEST(ProtectedAtomDBTest, RejectsNullBackend) { EXPECT_THROW(ProtectedAtomDB(nullptr), runtime_error); } TEST(ProtectedAtomDBTest, IsProtected) { auto db = make_protected_db(); - EXPECT_EQ(db->is_protected(), ProtectionMode::PROTECTED); + EXPECT_EQ(db->get_protection_mode(), ProtectionMode::PROTECTED); } TEST(ProtectedAtomDBTest, BackendIsNotProtected) { auto backend = make_shared("protected_backend_"); - EXPECT_EQ(backend->is_protected(), ProtectionMode::UNPROTECTED); + EXPECT_EQ(backend->get_protection_mode(), ProtectionMode::UNPROTECTED); } TEST(ProtectedAtomDBTest, ForwardModeWhenBackendIsForward) { class ForwardInMemoryDB : public InMemoryDB { public: explicit ForwardInMemoryDB(const string& context) : InMemoryDB(context) {} - ProtectionMode is_protected() const override { return ProtectionMode::FORWARD; } + ProtectionMode get_protection_mode() const override { return ProtectionMode::FORWARD; } }; ProtectedAtomDB db(make_shared("protected_forward_")); - EXPECT_EQ(db.is_protected(), ProtectionMode::FORWARD); + EXPECT_EQ(db.get_protection_mode(), ProtectionMode::FORWARD); } TEST(ProtectedAtomDBTest, DelegatesCapabilityFlagsToBackend) { diff --git a/src/tests/cpp/redis_mongodb_test.cc b/src/tests/cpp/redis_mongodb_test.cc index 711458d04..23c7a14cc 100644 --- a/src/tests/cpp/redis_mongodb_test.cc +++ b/src/tests/cpp/redis_mongodb_test.cc @@ -1495,7 +1495,7 @@ TEST_F(RedisMongoDBTest, IsProtectedWhenPersistedConfigIsTrue) { collection.insert_one(make_document(kvp("protected", true))); TestRedisMongoDB loaded("test_", test_atomdb_json_config()); - EXPECT_EQ(loaded.is_protected(), atomdb_api_types::ProtectionMode::PROTECTED); + EXPECT_EQ(loaded.get_protection_mode(), atomdb_api_types::ProtectionMode::PROTECTED); collection.delete_many({}); } @@ -1511,7 +1511,7 @@ TEST_F(RedisMongoDBTest, IsProtectedWhenPersistedConfigOmitsField) { collection.insert_one(make_document(kvp("other", "value"))); TestRedisMongoDB loaded("test_", test_atomdb_json_config()); - EXPECT_EQ(loaded.is_protected(), atomdb_api_types::ProtectionMode::UNPROTECTED); + EXPECT_EQ(loaded.get_protection_mode(), atomdb_api_types::ProtectionMode::UNPROTECTED); collection.delete_many({}); } diff --git a/src/tests/cpp/remote_atomdb_test.cc b/src/tests/cpp/remote_atomdb_test.cc index 08750414d..a73db537b 100644 --- a/src/tests/cpp/remote_atomdb_test.cc +++ b/src/tests/cpp/remote_atomdb_test.cc @@ -748,7 +748,7 @@ class ProtectedInMemoryDB : public InMemoryDB { public: explicit ProtectedInMemoryDB(const string& context) : InMemoryDB(context) {} - ProtectionMode is_protected() const override { return ProtectionMode::PROTECTED; } + ProtectionMode get_protection_mode() const override { return ProtectionMode::PROTECTED; } }; TEST(RemoteAtomDBFederationTest, MetadataAggregationFromNestedPeer) { @@ -859,14 +859,14 @@ TEST(RemoteAtomDBFederationTest, PeerIsProtectedFollowsRemoteBackend) { auto remote = make_shared("prot_none_remote_"); auto local = make_shared("prot_none_local_"); auto peer = make_shared(remote, local, "peer"); - EXPECT_EQ(peer->is_protected(), ProtectionMode::UNPROTECTED); + EXPECT_EQ(peer->get_protection_mode(), ProtectionMode::UNPROTECTED); } // Read-only peer (no local persistence) over an unprotected remote. { auto remote = make_shared("prot_readonly_remote_"); auto peer = make_shared(remote, nullptr, "peer"); - EXPECT_EQ(peer->is_protected(), ProtectionMode::UNPROTECTED); + EXPECT_EQ(peer->get_protection_mode(), ProtectionMode::UNPROTECTED); } // Protected remote backend. @@ -874,7 +874,7 @@ TEST(RemoteAtomDBFederationTest, PeerIsProtectedFollowsRemoteBackend) { auto remote = make_shared("prot_remote_remote_"); auto local = make_shared("prot_remote_local_"); auto peer = make_shared(remote, local, "peer"); - EXPECT_EQ(peer->is_protected(), ProtectionMode::PROTECTED); + EXPECT_EQ(peer->get_protection_mode(), ProtectionMode::PROTECTED); } // Protected local persistence is not supported. @@ -882,7 +882,7 @@ TEST(RemoteAtomDBFederationTest, PeerIsProtectedFollowsRemoteBackend) { auto remote = make_shared("prot_local_remote_"); auto local = make_shared("prot_local_local_"); auto peer = make_shared(remote, local, "peer"); - EXPECT_THROW(peer->is_protected(), runtime_error); + EXPECT_THROW(peer->get_protection_mode(), runtime_error); } } @@ -891,7 +891,7 @@ TEST(RemoteAtomDBFederationTest, IsProtectedWhenAnyPeerIsProtected) { { map> peers; auto db = make_shared(peers); - EXPECT_EQ(db->is_protected(), ProtectionMode::UNPROTECTED); + EXPECT_EQ(db->get_protection_mode(), ProtectionMode::UNPROTECTED); } // All peers unprotected. @@ -902,7 +902,7 @@ TEST(RemoteAtomDBFederationTest, IsProtectedWhenAnyPeerIsProtected) { peers["peer1"] = make_shared(remote1, nullptr, "peer1"); peers["peer2"] = make_shared(remote2, nullptr, "peer2"); auto db = make_shared(peers); - EXPECT_EQ(db->is_protected(), ProtectionMode::UNPROTECTED); + EXPECT_EQ(db->get_protection_mode(), ProtectionMode::UNPROTECTED); } // A single protected peer makes the facade FORWARD (no local post-processing). @@ -913,7 +913,7 @@ TEST(RemoteAtomDBFederationTest, IsProtectedWhenAnyPeerIsProtected) { peers["unprotected"] = make_shared(unprotected_remote, nullptr, "unprotected"); peers["protected"] = make_shared(protected_remote, nullptr, "protected"); auto db = make_shared(peers); - EXPECT_EQ(db->is_protected(), ProtectionMode::FORWARD); + EXPECT_EQ(db->get_protection_mode(), ProtectionMode::FORWARD); } } diff --git a/src/tests/cpp/test_commons/mocks/MockAtomDB.h b/src/tests/cpp/test_commons/mocks/MockAtomDB.h index 5db03b7ac..788790f34 100644 --- a/src/tests/cpp/test_commons/mocks/MockAtomDB.h +++ b/src/tests/cpp/test_commons/mocks/MockAtomDB.h @@ -100,7 +100,8 @@ class AtomDBMock : public AtomDB { AtomDBMock() { ON_CALL(*this, composite_type_enabled()).WillByDefault(::testing::Return(true)); - ON_CALL(*this, is_protected()).WillByDefault(::testing::Return(ProtectionMode::UNPROTECTED)); + ON_CALL(*this, get_protection_mode()) + .WillByDefault(::testing::Return(ProtectionMode::UNPROTECTED)); ON_CALL(*this, get_atom(testing::_)) .WillByDefault(::testing::Return(make_shared("Node", "TestNode"))); ON_CALL(*this, get_node(testing::_)) From 0bf068959f113bdb5cf6a50e4543bcaba81ce97d Mon Sep 17 00:00:00 2001 From: marcocapozzoli Date: Tue, 18 Aug 2026 09:37:35 -0300 Subject: [PATCH 15/21] Fix tests --- src/atomdb/redis_mongodb/RedisMongoDB.cc | 3 +- src/tests/cpp/protected_atomdb_test.cc | 96 +++++++++++++----------- 2 files changed, 55 insertions(+), 44 deletions(-) diff --git a/src/atomdb/redis_mongodb/RedisMongoDB.cc b/src/atomdb/redis_mongodb/RedisMongoDB.cc index af14222cb..4c7b3aef1 100644 --- a/src/atomdb/redis_mongodb/RedisMongoDB.cc +++ b/src/atomdb/redis_mongodb/RedisMongoDB.cc @@ -1329,7 +1329,8 @@ void RedisMongoDB::load_protection_mode() { auto config_doc = config_collection.find_one( bsoncxx::builder::basic::make_document(bsoncxx::builder::basic::kvp("protected", true))); this->protection_mode = static_cast( - config_doc ? ProtectionMode::PROTECTED : ProtectionMode::UNPROTECTED); + config_doc ? atomdb_api_types::ProtectionMode::PROTECTED + : atomdb_api_types::ProtectionMode::UNPROTECTED); } void RedisMongoDB::load_pattern_index_schema() { diff --git a/src/tests/cpp/protected_atomdb_test.cc b/src/tests/cpp/protected_atomdb_test.cc index 89c2e99c3..ee4c1ca00 100644 --- a/src/tests/cpp/protected_atomdb_test.cc +++ b/src/tests/cpp/protected_atomdb_test.cc @@ -1,9 +1,7 @@ #include -#include #include #include -#include #include #include "InMemoryDB.h" @@ -16,76 +14,88 @@ using namespace atomdb_api_types; using namespace atoms; using namespace std; -using atomdb_api_types::ProtectionMode; - namespace { +class ProtectedInMemoryDB : public InMemoryDB { + public: + ProtectedInMemoryDB(const string& context = "") : InMemoryDB(context) {} + + atomdb_api_types::ProtectionMode get_protection_mode() const override { + return atomdb_api_types::ProtectionMode::PROTECTED; + } +}; + shared_ptr make_protected_db(const string& context = "protected_atomdb_test_") { - return make_shared(make_shared(context)); + return make_shared(make_shared(context)); } } // namespace TEST(ProtectedAtomDBTest, RejectsNullBackend) { EXPECT_THROW(ProtectedAtomDB(nullptr), runtime_error); } -TEST(ProtectedAtomDBTest, IsProtected) { - auto db = make_protected_db(); - EXPECT_EQ(db->get_protection_mode(), ProtectionMode::PROTECTED); -} - TEST(ProtectedAtomDBTest, BackendIsNotProtected) { - auto backend = make_shared("protected_backend_"); - EXPECT_EQ(backend->get_protection_mode(), ProtectionMode::UNPROTECTED); + auto backend = make_shared("unprotected_backend_"); + EXPECT_EQ(backend->get_protection_mode(), atomdb_api_types::ProtectionMode::UNPROTECTED); } -TEST(ProtectedAtomDBTest, ForwardModeWhenBackendIsForward) { - class ForwardInMemoryDB : public InMemoryDB { - public: - explicit ForwardInMemoryDB(const string& context) : InMemoryDB(context) {} - ProtectionMode get_protection_mode() const override { return ProtectionMode::FORWARD; } - }; - - ProtectedAtomDB db(make_shared("protected_forward_")); - EXPECT_EQ(db.get_protection_mode(), ProtectionMode::FORWARD); -} - -TEST(ProtectedAtomDBTest, DelegatesCapabilityFlagsToBackend) { - auto backend = make_shared("protected_flags_"); +TEST(ProtectedAtomDBTest, DelegatesToBackend) { + auto backend = make_shared("protected_flags_"); ProtectedAtomDB db(backend); EXPECT_EQ(db.allow_nested_indexing(), backend->allow_nested_indexing()); EXPECT_EQ(db.composite_type_enabled(), backend->composite_type_enabled()); -} -TEST(ProtectedAtomDBTest, DelegatesAccessPermissionsToBackend) { - auto backend = make_shared("protected_perms_"); - ProtectedAtomDB db(backend); PublicKey key("any_key"); EXPECT_EQ(db.get_access_permissions(key).size(), backend->get_access_permissions(key).size()); } -TEST(ProtectedAtomDBTest, RejectsAccessWithoutPublicKey) { - auto db = make_protected_db("protected_no_key_"); - Node node("Symbol", "\"x\""); +TEST(ProtectedAtomDBTest, RejectsOperationsWithoutPublicKey) { + auto db = make_protected_db(); + + Node node("Symbol", "\"node\""); Link link("Expression", {"a", "b"}); + vector handles = {"a", "b"}; + vector atoms = {&node}; EXPECT_THROW(db->get_atom("handle"), runtime_error); + EXPECT_THROW(db->get_node("handle"), runtime_error); + EXPECT_THROW(db->get_link("handle"), runtime_error); + + EXPECT_THROW(db->get_matching_atoms(true, node), runtime_error); + + LinkSchema schema("Expression", 2); + EXPECT_THROW(db->query_for_pattern(schema), runtime_error); + EXPECT_THROW(db->query_for_targets("handle"), runtime_error); + EXPECT_THROW(db->query_for_incoming_set("handle"), runtime_error); + EXPECT_THROW(db->atom_exists("handle"), runtime_error); + EXPECT_THROW(db->node_exists("handle"), runtime_error); + EXPECT_THROW(db->link_exists("handle"), runtime_error); + + EXPECT_THROW(db->atoms_exist(handles), runtime_error); + EXPECT_THROW(db->nodes_exist(handles), runtime_error); + EXPECT_THROW(db->links_exist(handles), runtime_error); + + EXPECT_THROW(db->add_atom(&node), runtime_error); EXPECT_THROW(db->add_node(&node), runtime_error); EXPECT_THROW(db->add_link(&link), runtime_error); + + EXPECT_THROW(db->add_atoms(atoms), runtime_error); + EXPECT_THROW(db->add_nodes({&node}), runtime_error); + EXPECT_THROW(db->add_links({&link}), runtime_error); + EXPECT_THROW(db->delete_atom("handle"), runtime_error); - EXPECT_THROW(db->atom_count(), runtime_error); -} + EXPECT_THROW(db->delete_node("handle"), runtime_error); + EXPECT_THROW(db->delete_link("handle"), runtime_error); + + EXPECT_THROW(db->delete_atoms(handles), runtime_error); + EXPECT_THROW(db->delete_nodes(handles), runtime_error); + EXPECT_THROW(db->delete_links(handles), runtime_error); -TEST(ProtectedAtomDBTest, PublicKeyOverloadsAreNotImplementedYet) { - auto db = make_protected_db("protected_with_key_"); - Node node("Symbol", "\"n\""); - PublicKey key("public_key"); + EXPECT_THROW(db->re_index_patterns(), runtime_error); - EXPECT_THROW(db->get_atom("handle", key), runtime_error); - EXPECT_THROW(db->atom_exists("handle", key), runtime_error); - EXPECT_THROW(db->add_node(&node, key), runtime_error); - EXPECT_THROW(db->delete_atom("handle", key), runtime_error); - EXPECT_THROW(db->atom_count(key), runtime_error); + EXPECT_THROW(db->node_count(), runtime_error); + EXPECT_THROW(db->link_count(), runtime_error); + EXPECT_THROW(db->atom_count(), runtime_error); } From 9e88e42dda12bcec4d7fd4295f4616293f306be9 Mon Sep 17 00:00:00 2001 From: marcocapozzoli Date: Wed, 19 Aug 2026 09:34:57 -0300 Subject: [PATCH 16/21] load_protection_mode() during RedisMongoDB initialization --- src/atomdb/redis_mongodb/RedisMongoDB.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/atomdb/redis_mongodb/RedisMongoDB.cc b/src/atomdb/redis_mongodb/RedisMongoDB.cc index f307b52c4..267290121 100644 --- a/src/atomdb/redis_mongodb/RedisMongoDB.cc +++ b/src/atomdb/redis_mongodb/RedisMongoDB.cc @@ -45,9 +45,10 @@ RedisMongoDB::RedisMongoDB(const string& context, bool skip_redis, const JsonCon skip_redis_(skip_redis), composite_type_enabled_(config.at_path("composite_type_enabled").get_or(true)), cluster_flag(false), - protection_mode(atomdb_api_types::ProtectionMode::UNPROTECTED) { + protection_mode(atomdb_api_types::ProtectionMode::PROTECTED) { initialize_statics(context); mongodb_setup(config); + load_protection_mode(); load_pattern_index_schema(); redis_setup(config); this->patterns_next_score.store(get_next_score(REDIS_PATTERNS_PREFIX + ":next_score")); @@ -198,7 +199,6 @@ void RedisMongoDB::mongodb_setup(const JsonConfig& config) { bsoncxx::builder::basic::make_document(bsoncxx::builder::basic::kvp("ping", 1)); mongodb.run_command(ping_cmd.view()); LOG_INFO("Connected to MongoDB at " << address); - this->load_protection_mode(); } catch (const std::exception& e) { RAISE_ERROR(e.what()); } From 7a62b9db229b2e581329e27b98aa8d6dd44a7b84 Mon Sep 17 00:00:00 2001 From: Marco Capozzoli <55926220+marcocapozzoli@users.noreply.github.com> Date: Thu, 20 Aug 2026 11:34:44 -0300 Subject: [PATCH 17/21] Update src/atomdb/ProtectedAtomDB.cc Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- src/atomdb/ProtectedAtomDB.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/atomdb/ProtectedAtomDB.cc b/src/atomdb/ProtectedAtomDB.cc index 5ac645e06..4bbe1ad5b 100644 --- a/src/atomdb/ProtectedAtomDB.cc +++ b/src/atomdb/ProtectedAtomDB.cc @@ -1,8 +1,10 @@ #include "ProtectedAtomDB.h" +#define LOG_LEVEL INFO_LEVEL #include "Logger.h" #include "Utils.h" +using namespace std; using namespace atomdb; using namespace commons; From 4cdbe6d9a680f1c073d903c7acb3ecfada3884c1 Mon Sep 17 00:00:00 2001 From: marcocapozzoli Date: Thu, 20 Aug 2026 14:21:11 -0300 Subject: [PATCH 18/21] Fix Mock::get_protection_mode() --- src/tests/cpp/test_commons/mocks/MockAtomDB.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests/cpp/test_commons/mocks/MockAtomDB.h b/src/tests/cpp/test_commons/mocks/MockAtomDB.h index 788790f34..0b489c7c2 100644 --- a/src/tests/cpp/test_commons/mocks/MockAtomDB.h +++ b/src/tests/cpp/test_commons/mocks/MockAtomDB.h @@ -30,7 +30,7 @@ class AtomDBMock : public AtomDB { get_access_permissions, (const atomdb_api_types::PublicKey& public_key), (const, override)); - MOCK_METHOD(ProtectionMode, is_protected, (), (const, override)); + MOCK_METHOD(ProtectionMode, get_protection_mode, (), (const, override)); MOCK_METHOD(shared_ptr, get_atom, (const string& handle), (override)); MOCK_METHOD(shared_ptr, get_node, (const string& handle), (override)); MOCK_METHOD(shared_ptr, get_link, (const string& handle), (override)); From 912e5465f5977e8acd33c1a88c4dc8b37c81321a Mon Sep 17 00:00:00 2001 From: marcocapozzoli Date: Thu, 20 Aug 2026 14:30:33 -0300 Subject: [PATCH 19/21] Add TODO --- src/atomdb/AtomDBFactory.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/atomdb/AtomDBFactory.cc b/src/atomdb/AtomDBFactory.cc index dcb16398c..f959e7efd 100644 --- a/src/atomdb/AtomDBFactory.cc +++ b/src/atomdb/AtomDBFactory.cc @@ -111,5 +111,8 @@ shared_ptr AtomDBFactory::wrap_if_protected(shared_ptr atomdb) { dynamic_pointer_cast(atomdb)) { return atomdb; } - return make_shared(atomdb); + // TODO: uncomment this return when the integration with ProtectedDB with authorization is complete + // return make_shared(atomdb); + + return atomdb; } \ No newline at end of file From ad2c8c85720d7a1147a5df110b6e66a8d035b642 Mon Sep 17 00:00:00 2001 From: marcocapozzoli Date: Thu, 20 Aug 2026 21:38:35 -0300 Subject: [PATCH 20/21] Add exception to ProtectedAtomDB; add new check in RedisMongoDB::load_access_permissions(); add validation for local_persistence in the constructor. --- src/atomdb/AtomDBFactory.cc | 2 +- src/atomdb/redis_mongodb/RedisMongoDB.cc | 3 +++ src/atomdb/remotedb/RemoteAtomDBPeer.cc | 14 +++++--------- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/atomdb/AtomDBFactory.cc b/src/atomdb/AtomDBFactory.cc index f959e7efd..1ab20a1b0 100644 --- a/src/atomdb/AtomDBFactory.cc +++ b/src/atomdb/AtomDBFactory.cc @@ -114,5 +114,5 @@ shared_ptr AtomDBFactory::wrap_if_protected(shared_ptr atomdb) { // TODO: uncomment this return when the integration with ProtectedDB with authorization is complete // return make_shared(atomdb); - return atomdb; + RAISE_ERROR("Protected AtomDB support is not available"); } \ No newline at end of file diff --git a/src/atomdb/redis_mongodb/RedisMongoDB.cc b/src/atomdb/redis_mongodb/RedisMongoDB.cc index 267290121..558cb9af3 100644 --- a/src/atomdb/redis_mongodb/RedisMongoDB.cc +++ b/src/atomdb/redis_mongodb/RedisMongoDB.cc @@ -102,6 +102,9 @@ optional RedisMongoDB::load_access_p if (!document.contains("public_key") || !document["public_key"].is_string()) { RAISE_ERROR("AccessPermissionDocument missing required string filed 'public_key'"); } + if (document["public_key"].get() != public_key) { + RAISE_ERROR("AccessPermissionDocument public_key does not match its lookup key"); + } if (!document.contains("full_access") || !document["full_access"].is_boolean()) { RAISE_ERROR("AccessPermissionDocument missing required boolean field 'full_access'"); } diff --git a/src/atomdb/remotedb/RemoteAtomDBPeer.cc b/src/atomdb/remotedb/RemoteAtomDBPeer.cc index cda11dc89..5d1323d90 100644 --- a/src/atomdb/remotedb/RemoteAtomDBPeer.cc +++ b/src/atomdb/remotedb/RemoteAtomDBPeer.cc @@ -29,6 +29,10 @@ RemoteAtomDBPeer::RemoteAtomDBPeer(shared_ptr remote_atomdb, read_cache_(make_shared(uid + "_rc")), atomdb_(remote_atomdb), local_persistence_(local_persistence) { + if (local_persistence_ && + local_persistence_->get_protection_mode() == atomdb_api_types::ProtectionMode::PROTECTED) { + RAISE_ERROR("RemoteAtomDBPeer does not support protected local persistence"); + } start_cleanup_thread(); } @@ -61,15 +65,7 @@ void RemoteAtomDBPeer::invalidate_fetched_templates() { } atomdb_api_types::ProtectionMode RemoteAtomDBPeer::get_protection_mode() const { - if (local_persistence_ && - local_persistence_->get_protection_mode() == atomdb_api_types::ProtectionMode::PROTECTED) { - RAISE_ERROR("[RemoteDB(" + uid_ + - ")] local_persistence is PROTECTED, but RemoteAtomDBPeer does not support protected " - "local persistence"); - } - if (atomdb_) { - return atomdb_->get_protection_mode(); - } + if (atomdb_) return atomdb_->get_protection_mode(); return atomdb_api_types::ProtectionMode::UNPROTECTED; } From e2b69d9bb937bf4bc434bf2a50c385534f5325b3 Mon Sep 17 00:00:00 2001 From: marcocapozzoli Date: Fri, 21 Aug 2026 09:40:26 -0300 Subject: [PATCH 21/21] Fix test --- src/tests/cpp/remote_atomdb_test.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/tests/cpp/remote_atomdb_test.cc b/src/tests/cpp/remote_atomdb_test.cc index a73db537b..5b17783e7 100644 --- a/src/tests/cpp/remote_atomdb_test.cc +++ b/src/tests/cpp/remote_atomdb_test.cc @@ -881,8 +881,7 @@ TEST(RemoteAtomDBFederationTest, PeerIsProtectedFollowsRemoteBackend) { { auto remote = make_shared("prot_local_remote_"); auto local = make_shared("prot_local_local_"); - auto peer = make_shared(remote, local, "peer"); - EXPECT_THROW(peer->get_protection_mode(), runtime_error); + EXPECT_THROW(make_shared(remote, local, "peer"), runtime_error); } }