191 lines
7.5 KiB
C++
191 lines
7.5 KiB
C++
// Copyright 2024 Memgraph Ltd.
|
|
//
|
|
// Use of this software is governed by the Business Source License
|
|
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
|
// License, and you may not use this file except in compliance with the Business Source License.
|
|
//
|
|
// As of the Change Date specified in that file, in accordance with
|
|
// the Business Source License, use of this software will be governed
|
|
// by the Apache License, Version 2.0, included in the file
|
|
// licenses/APL.txt.
|
|
|
|
#include "auth/replication_handlers.hpp"
|
|
|
|
#include "auth/auth.hpp"
|
|
#include "auth/rpc.hpp"
|
|
#include "license/license.hpp"
|
|
|
|
namespace memgraph::auth {
|
|
|
|
void LogWrongMain(const std::optional<utils::UUID> ¤t_main_uuid, const utils::UUID &main_req_id,
|
|
std::string_view rpc_req) {
|
|
spdlog::error(fmt::format("Received {} with main_id: {} != current_main_uuid: {}", rpc_req, std::string(main_req_id),
|
|
current_main_uuid.has_value() ? std::string(current_main_uuid.value()) : ""));
|
|
}
|
|
|
|
#ifdef MG_ENTERPRISE
|
|
void UpdateAuthDataHandler(memgraph::system::ReplicaHandlerAccessToState &system_state_access,
|
|
const std::optional<utils::UUID> ¤t_main_uuid, auth::SynchedAuth &auth,
|
|
slk::Reader *req_reader, slk::Builder *res_builder) {
|
|
replication::UpdateAuthDataReq req;
|
|
memgraph::slk::Load(&req, req_reader);
|
|
|
|
using memgraph::replication::UpdateAuthDataRes;
|
|
UpdateAuthDataRes res(false);
|
|
|
|
if (!current_main_uuid.has_value() || req.main_uuid != current_main_uuid) [[unlikely]] {
|
|
LogWrongMain(current_main_uuid, req.main_uuid, replication::UpdateAuthDataReq::kType.name);
|
|
memgraph::slk::Save(res, res_builder);
|
|
return;
|
|
}
|
|
|
|
// Note: No need to check epoch, recovery mechanism is done by a full uptodate snapshot
|
|
// of the set of databases. Hence no history exists to maintain regarding epoch change.
|
|
// If MAIN has changed we need to check this new group_timestamp is consistent with
|
|
// what we have so far.
|
|
|
|
if (req.expected_group_timestamp != system_state_access.LastCommitedTS()) {
|
|
spdlog::debug("UpdateAuthDataHandler: bad expected timestamp {},{}", req.expected_group_timestamp,
|
|
system_state_access.LastCommitedTS());
|
|
memgraph::slk::Save(res, res_builder);
|
|
return;
|
|
}
|
|
|
|
try {
|
|
// Update
|
|
if (req.user) auth->SaveUser(*req.user);
|
|
if (req.role) auth->SaveRole(*req.role);
|
|
// Success
|
|
system_state_access.SetLastCommitedTS(req.new_group_timestamp);
|
|
res = UpdateAuthDataRes(true);
|
|
spdlog::debug("UpdateAuthDataHandler: SUCCESS updated LCTS to {}", req.new_group_timestamp);
|
|
} catch (const auth::AuthException & /* not used */) {
|
|
// Failure
|
|
}
|
|
|
|
memgraph::slk::Save(res, res_builder);
|
|
}
|
|
|
|
void DropAuthDataHandler(memgraph::system::ReplicaHandlerAccessToState &system_state_access,
|
|
const std::optional<utils::UUID> ¤t_main_uuid, auth::SynchedAuth &auth,
|
|
slk::Reader *req_reader, slk::Builder *res_builder) {
|
|
replication::DropAuthDataReq req;
|
|
memgraph::slk::Load(&req, req_reader);
|
|
|
|
using memgraph::replication::DropAuthDataRes;
|
|
DropAuthDataRes res(false);
|
|
|
|
if (!current_main_uuid.has_value() || req.main_uuid != current_main_uuid) [[unlikely]] {
|
|
LogWrongMain(current_main_uuid, req.main_uuid, replication::DropAuthDataRes::kType.name);
|
|
memgraph::slk::Save(res, res_builder);
|
|
return;
|
|
}
|
|
|
|
// Note: No need to check epoch, recovery mechanism is done by a full uptodate snapshot
|
|
// of the set of databases. Hence no history exists to maintain regarding epoch change.
|
|
// If MAIN has changed we need to check this new group_timestamp is consistent with
|
|
// what we have so far.
|
|
|
|
if (req.expected_group_timestamp != system_state_access.LastCommitedTS()) {
|
|
spdlog::debug("DropAuthDataHandler: bad expected timestamp {},{}", req.expected_group_timestamp,
|
|
system_state_access.LastCommitedTS());
|
|
memgraph::slk::Save(res, res_builder);
|
|
return;
|
|
}
|
|
|
|
try {
|
|
// Remove
|
|
switch (req.type) {
|
|
case replication::DropAuthDataReq::DataType::USER:
|
|
auth->RemoveUser(req.name);
|
|
break;
|
|
case replication::DropAuthDataReq::DataType::ROLE:
|
|
auth->RemoveRole(req.name);
|
|
break;
|
|
}
|
|
// Success
|
|
system_state_access.SetLastCommitedTS(req.new_group_timestamp);
|
|
res = DropAuthDataRes(true);
|
|
spdlog::debug("DropAuthDataHandler: SUCCESS updated LCTS to {}", req.new_group_timestamp);
|
|
} catch (const auth::AuthException & /* not used */) {
|
|
// Failure
|
|
}
|
|
|
|
memgraph::slk::Save(res, res_builder);
|
|
}
|
|
|
|
bool SystemRecoveryHandler(auth::SynchedAuth &auth, auth::Auth::Config auth_config,
|
|
const std::vector<auth::User> &users, const std::vector<auth::Role> &roles) {
|
|
return auth.WithLock([&](auto &locked_auth) {
|
|
// Update config
|
|
locked_auth.SetConfig(std::move(auth_config));
|
|
// Get all current users
|
|
auto old_users = locked_auth.AllUsernames();
|
|
// Save incoming users
|
|
for (const auto &user : users) {
|
|
// Missing users
|
|
try {
|
|
locked_auth.SaveUser(user);
|
|
} catch (const auth::AuthException &) {
|
|
spdlog::debug("SystemRecoveryHandler: Failed to save user");
|
|
return false;
|
|
}
|
|
const auto it = std::find(old_users.begin(), old_users.end(), user.username());
|
|
if (it != old_users.end()) old_users.erase(it);
|
|
}
|
|
// Delete all the leftover users
|
|
for (const auto &user : old_users) {
|
|
if (!locked_auth.RemoveUser(user)) {
|
|
spdlog::debug("SystemRecoveryHandler: Failed to remove user \"{}\".", user);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Roles are only supported with a license
|
|
if (license::global_license_checker.IsEnterpriseValidFast()) {
|
|
// Get all current roles
|
|
auto old_roles = locked_auth.AllRolenames();
|
|
// Save incoming users
|
|
for (const auto &role : roles) {
|
|
// Missing users
|
|
try {
|
|
locked_auth.SaveRole(role);
|
|
} catch (const auth::AuthException &) {
|
|
spdlog::debug("SystemRecoveryHandler: Failed to save user");
|
|
return false;
|
|
}
|
|
const auto it = std::find(old_roles.begin(), old_roles.end(), role.rolename());
|
|
if (it != old_roles.end()) old_roles.erase(it);
|
|
}
|
|
// Delete all the leftover users
|
|
for (const auto &role : old_roles) {
|
|
if (!locked_auth.RemoveRole(role)) {
|
|
spdlog::debug("SystemRecoveryHandler: Failed to remove user \"{}\".", role);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Success
|
|
return true;
|
|
});
|
|
}
|
|
|
|
void Register(replication::RoleReplicaData const &data, system::ReplicaHandlerAccessToState &system_state_access,
|
|
auth::SynchedAuth &auth) {
|
|
// NOTE: Register even without license as the user could add a license at run-time
|
|
data.server->rpc_server_.Register<replication::UpdateAuthDataRpc>(
|
|
[&data, system_state_access, &auth](auto *req_reader, auto *res_builder) mutable {
|
|
spdlog::debug("Received UpdateAuthDataRpc");
|
|
UpdateAuthDataHandler(system_state_access, data.uuid_, auth, req_reader, res_builder);
|
|
});
|
|
data.server->rpc_server_.Register<replication::DropAuthDataRpc>(
|
|
[&data, system_state_access, &auth](auto *req_reader, auto *res_builder) mutable {
|
|
spdlog::debug("Received DropAuthDataRpc");
|
|
DropAuthDataHandler(system_state_access, data.uuid_, auth, req_reader, res_builder);
|
|
});
|
|
}
|
|
#endif
|
|
|
|
} // namespace memgraph::auth
|