Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 23 additions & 8 deletions src/atomdb/ProtectedAtomDB.cc
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,7 @@ optional<string> ProtectedAtomDB::authorize_read(const string& handle,

optional<string> ProtectedAtomDB::authorize_read(const shared_ptr<Atom>& atom,
const shared_ptr<Keychain>& keychain) {
if (atom == nullptr) return nullopt;
auto public_key = this->try_get_public_key(keychain);
if (!public_key || !this->can_read(atom, *public_key)) return nullopt;
return public_key;
Expand All @@ -374,17 +375,29 @@ set<string> ProtectedAtomDB::check_handles(shared_ptr<Keychain> keychain,
shared_ptr<atomdb_api_types::HandleSet> ProtectedAtomDB::filter_handle_set(
const optional<string>& public_key,
const function<shared_ptr<atomdb_api_types::HandleSet>()>& query) {
if (!public_key) return make_shared<atomdb_api_types::HandleSetInMemory>();
if (!public_key) {
LOG_DEBUG("[ProtectedAtomDB(" << this->uid_ << ")] filter_handle_set: skipped, no public key");
return make_shared<atomdb_api_types::HandleSetInMemory>();
}

LOG_DEBUG("[ProtectedAtomDB(" << this->uid_ << ")] filter_handle_set: querying backend");
auto original_handle_set = query();

auto authorized_handle_set = make_shared<atomdb_api_types::HandleSetInMemory>();
LOG_DEBUG("[ProtectedAtomDB(" << this->uid_ << ")] filter_handle_set: backend returned "
<< (original_handle_set ? original_handle_set->size() : 0)
<< " handles");

if (original_handle_set == nullptr) {
return authorized_handle_set;
return make_shared<atomdb_api_types::HandleSetInMemory>();
}

if (this->manifest->is_unrestricted(*public_key)) {
LOG_DEBUG("[ProtectedAtomDB(" << this->uid_
<< ")] filter_handle_set: unrestricted, skipping authorization");
return original_handle_set;
}

auto it = original_handle_set->get_iterator();
auto authorized_handle_set = make_shared<atomdb_api_types::HandleSetInMemory>();

while (true) {
char* handle_cstr = it->next();
Expand All @@ -399,16 +412,18 @@ shared_ptr<atomdb_api_types::HandleSet> ProtectedAtomDB::filter_handle_set(
}
}

LOG_DEBUG("ProtectedAtomDB::filter_handle_set() - original handle_set: " +
std::to_string(original_handle_set->size()));
LOG_DEBUG("ProtectedAtomDB::filter_handle_set() - authorized handle_set: " +
std::to_string(authorized_handle_set->size()));
LOG_DEBUG("[ProtectedAtomDB(" << this->uid_
<< ")] filter_handle_set: " << original_handle_set->size() << " -> "
<< authorized_handle_set->size() << " authorized");

return authorized_handle_set;
}

set<string> ProtectedAtomDB::filter_handles(const set<string>& original_handles,
const string& public_key) {
if (this->manifest->is_unrestricted(public_key)) {
return original_handles;
}
set<string> authorized_handles;
for (const auto& handle : original_handles) {
if (this->can_read(handle, public_key)) {
Expand Down
9 changes: 8 additions & 1 deletion src/atomdb/ProtectedAtomDB.h
Original file line number Diff line number Diff line change
Expand Up @@ -205,10 +205,17 @@ class ProtectedAtomDB : public AtomDB, public KeySensitiveAtomDB {

/**
* @brief Returns whether the caller may READ the specified handle.
*
* Unrestricted profiles skip loading the atom and skip Manifest matching;
* existence is still required (atom_exists), so a missing handle is not readable.
* Restricted profiles load the atom here and send it to the Manifest.
*/
inline bool can_read(const string& handle, const string& public_key) {
if (public_key.empty()) return false;
return this->manifest->is_granted(public_key, handle, AuthorizationOperation::READ);
if (this->manifest->is_unrestricted(public_key)) {
return this->backend->atom_exists(handle);
}
return this->can_read(this->backend->get_atom(handle), public_key);
}

/**
Expand Down
7 changes: 7 additions & 0 deletions src/atomdb/auth/AuthorizationManifest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,13 @@ void AuthorizationManifest::count_matching_atoms(size_t& node_count,
profile->count_matching_atoms(node_count, link_count, atom_count, keychain);
}

bool AuthorizationManifest::is_unrestricted(const string& public_key) {
lock_guard<mutex> lock(this->profiles_mutex);
auto it = this->profiles.find(public_key);
if (it == this->profiles.end() || it->second == nullptr) return false;
return it->second->is_unrestricted();
}

// --------------------------------------------------------------------------------
// Private methods

Expand Down
8 changes: 8 additions & 0 deletions src/atomdb/auth/AuthorizationManifest.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,14 @@ class AuthorizationManifest {
size_t& atom_count,
shared_ptr<Keychain> keychain);

/**
* @brief Returns whether public_key's loaded profile grants unrestricted access.
*
* @return true if the profile is cached and has full access; false if it is
* missing, not loaded, or schema-restricted.
*/
bool is_unrestricted(const string& public_key);

private:
shared_ptr<AtomDB> atomdb;
map<string, shared_ptr<AuthorizationProfile>> profiles;
Expand Down
18 changes: 18 additions & 0 deletions src/tests/cpp/authorization_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,24 @@ TEST(AuthorizationManifestTest, IsGranted) {
EXPECT_FALSE(manifest.is_granted("pk", string(32, '0'), AuthorizationOperation::WRITE));
}

TEST(AuthorizationManifestTest, IsUnrestricted) {
auto db = make_shared<AccessPermissionAtomDB>();
db->permissions["full"] = make_document("full", true, {});
db->permissions["restricted"] = make_document("restricted", false, {read_only_inheritance_schema()});
AuthorizationManifest manifest(db);

EXPECT_FALSE(manifest.is_unrestricted("full"));
EXPECT_FALSE(manifest.is_unrestricted("restricted"));
EXPECT_FALSE(manifest.is_unrestricted("unknown"));
EXPECT_FALSE(manifest.is_unrestricted(""));

ASSERT_TRUE(manifest.ensure_profile_loaded("full"));
ASSERT_TRUE(manifest.ensure_profile_loaded("restricted"));
EXPECT_TRUE(manifest.is_unrestricted("full"));
EXPECT_FALSE(manifest.is_unrestricted("restricted"));
EXPECT_FALSE(manifest.is_unrestricted("unknown"));
}

TEST(AuthorizationManifestTest, FullAccessGrantsAllOperations) {
string link_handle;
auto db = db_with_inheritance_link(&link_handle);
Expand Down
Loading