Merge branch 'master' into run-ci-in-mgbuilders
This commit is contained in:
commit
f259d821ba
@ -35,16 +35,42 @@ DEFINE_VALIDATED_string(auth_module_executable, "", "Absolute path to the auth m
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
DEFINE_bool(auth_module_create_missing_user, true, "Set to false to disable creation of missing users.");
|
|
||||||
DEFINE_bool(auth_module_create_missing_role, true, "Set to false to disable creation of missing roles.");
|
|
||||||
DEFINE_bool(auth_module_manage_roles, true, "Set to false to disable management of roles through the auth module.");
|
|
||||||
DEFINE_VALIDATED_int32(auth_module_timeout_ms, 10000,
|
DEFINE_VALIDATED_int32(auth_module_timeout_ms, 10000,
|
||||||
"Timeout (in milliseconds) used when waiting for a "
|
"Timeout (in milliseconds) used when waiting for a "
|
||||||
"response from the auth module.",
|
"response from the auth module.",
|
||||||
FLAG_IN_RANGE(100, 1800000));
|
FLAG_IN_RANGE(100, 1800000));
|
||||||
|
|
||||||
|
// DEPRECATED FLAGS
|
||||||
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables, misc-unused-parameters)
|
||||||
|
DEFINE_VALIDATED_HIDDEN_bool(auth_module_create_missing_user, true,
|
||||||
|
"Set to false to disable creation of missing users.", {
|
||||||
|
spdlog::warn(
|
||||||
|
"auth_module_create_missing_user flag is deprecated. It not possible to create "
|
||||||
|
"users through the module anymore.");
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables, misc-unused-parameters)
|
||||||
|
DEFINE_VALIDATED_HIDDEN_bool(auth_module_create_missing_role, true,
|
||||||
|
"Set to false to disable creation of missing roles.", {
|
||||||
|
spdlog::warn(
|
||||||
|
"auth_module_create_missing_role flag is deprecated. It not possible to create "
|
||||||
|
"roles through the module anymore.");
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables, misc-unused-parameters)
|
||||||
|
DEFINE_VALIDATED_HIDDEN_bool(
|
||||||
|
auth_module_manage_roles, true, "Set to false to disable management of roles through the auth module.", {
|
||||||
|
spdlog::warn(
|
||||||
|
"auth_module_manage_roles flag is deprecated. It not possible to create roles through the module anymore.");
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
namespace memgraph::auth {
|
namespace memgraph::auth {
|
||||||
|
|
||||||
|
const Auth::Epoch Auth::kStartEpoch = 1;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
/**
|
/**
|
||||||
@ -192,6 +218,17 @@ void MigrateVersions(kvstore::KVStore &store) {
|
|||||||
version_str = kVersionV1;
|
version_str = kVersionV1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto ParseJson(std::string_view str) {
|
||||||
|
nlohmann::json data;
|
||||||
|
try {
|
||||||
|
data = nlohmann::json::parse(str);
|
||||||
|
} catch (const nlohmann::json::parse_error &e) {
|
||||||
|
throw AuthException("Couldn't load auth data!");
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
}; // namespace
|
}; // namespace
|
||||||
|
|
||||||
Auth::Auth(std::string storage_directory, Config config)
|
Auth::Auth(std::string storage_directory, Config config)
|
||||||
@ -199,8 +236,11 @@ Auth::Auth(std::string storage_directory, Config config)
|
|||||||
MigrateVersions(storage_);
|
MigrateVersions(storage_);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<User> Auth::Authenticate(const std::string &username, const std::string &password) {
|
std::optional<UserOrRole> Auth::Authenticate(const std::string &username, const std::string &password) {
|
||||||
if (module_.IsUsed()) {
|
if (module_.IsUsed()) {
|
||||||
|
/*
|
||||||
|
* MODULE AUTH STORAGE
|
||||||
|
*/
|
||||||
const auto license_check_result = license::global_license_checker.IsEnterpriseValid(utils::global_settings);
|
const auto license_check_result = license::global_license_checker.IsEnterpriseValid(utils::global_settings);
|
||||||
if (license_check_result.HasError()) {
|
if (license_check_result.HasError()) {
|
||||||
spdlog::warn(license::LicenseCheckErrorToString(license_check_result.GetError(), "authentication modules"));
|
spdlog::warn(license::LicenseCheckErrorToString(license_check_result.GetError(), "authentication modules"));
|
||||||
@ -225,108 +265,64 @@ std::optional<User> Auth::Authenticate(const std::string &username, const std::s
|
|||||||
auto is_authenticated = ret_authenticated.get<bool>();
|
auto is_authenticated = ret_authenticated.get<bool>();
|
||||||
const auto &rolename = ret_role.get<std::string>();
|
const auto &rolename = ret_role.get<std::string>();
|
||||||
|
|
||||||
|
// Check if role is present
|
||||||
|
auto role = GetRole(rolename);
|
||||||
|
if (!role) {
|
||||||
|
spdlog::warn(utils::MessageWithLink("Couldn't authenticate user '{}' because the role '{}' doesn't exist.",
|
||||||
|
username, rolename, "https://memgr.ph/auth"));
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
// Authenticate the user.
|
// Authenticate the user.
|
||||||
if (!is_authenticated) return std::nullopt;
|
if (!is_authenticated) return std::nullopt;
|
||||||
|
|
||||||
/**
|
return RoleWUsername{username, std::move(*role)};
|
||||||
* TODO
|
|
||||||
* The auth module should not update auth data.
|
|
||||||
* There is now way to replicate it and we should not be storing sensitive data if we don't have to.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Find or create the user and return it.
|
|
||||||
auto user = GetUser(username);
|
|
||||||
if (!user) {
|
|
||||||
if (FLAGS_auth_module_create_missing_user) {
|
|
||||||
user = AddUser(username, password);
|
|
||||||
if (!user) {
|
|
||||||
spdlog::warn(utils::MessageWithLink(
|
|
||||||
"Couldn't create the missing user '{}' using the auth module because the user already exists as a role.",
|
|
||||||
username, "https://memgr.ph/auth"));
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
spdlog::warn(utils::MessageWithLink(
|
|
||||||
"Couldn't authenticate user '{}' using the auth module because the user doesn't exist.", username,
|
|
||||||
"https://memgr.ph/auth"));
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
UpdatePassword(*user, password);
|
|
||||||
}
|
|
||||||
if (FLAGS_auth_module_manage_roles) {
|
|
||||||
if (!rolename.empty()) {
|
|
||||||
auto role = GetRole(rolename);
|
|
||||||
if (!role) {
|
|
||||||
if (FLAGS_auth_module_create_missing_role) {
|
|
||||||
role = AddRole(rolename);
|
|
||||||
if (!role) {
|
|
||||||
spdlog::warn(
|
|
||||||
utils::MessageWithLink("Couldn't authenticate user '{}' using the auth module because the user's "
|
|
||||||
"role '{}' already exists as a user.",
|
|
||||||
username, rolename, "https://memgr.ph/auth"));
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
SaveRole(*role);
|
|
||||||
} else {
|
|
||||||
spdlog::warn(utils::MessageWithLink(
|
|
||||||
"Couldn't authenticate user '{}' using the auth module because the user's role '{}' doesn't exist.",
|
|
||||||
username, rolename, "https://memgr.ph/auth"));
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
user->SetRole(*role);
|
|
||||||
} else {
|
|
||||||
user->ClearRole();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SaveUser(*user);
|
|
||||||
return user;
|
|
||||||
} else {
|
|
||||||
auto user = GetUser(username);
|
|
||||||
if (!user) {
|
|
||||||
spdlog::warn(utils::MessageWithLink("Couldn't authenticate user '{}' because the user doesn't exist.", username,
|
|
||||||
"https://memgr.ph/auth"));
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
if (!user->CheckPassword(password)) {
|
|
||||||
spdlog::warn(utils::MessageWithLink("Couldn't authenticate user '{}' because the password is not correct.",
|
|
||||||
username, "https://memgr.ph/auth"));
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
if (user->UpgradeHash(password)) {
|
|
||||||
SaveUser(*user);
|
|
||||||
}
|
|
||||||
|
|
||||||
return user;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* LOCAL AUTH STORAGE
|
||||||
|
*/
|
||||||
|
auto user = GetUser(username);
|
||||||
|
if (!user) {
|
||||||
|
spdlog::warn(utils::MessageWithLink("Couldn't authenticate user '{}' because the user doesn't exist.", username,
|
||||||
|
"https://memgr.ph/auth"));
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
if (!user->CheckPassword(password)) {
|
||||||
|
spdlog::warn(utils::MessageWithLink("Couldn't authenticate user '{}' because the password is not correct.",
|
||||||
|
username, "https://memgr.ph/auth"));
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
if (user->UpgradeHash(password)) {
|
||||||
|
SaveUser(*user);
|
||||||
|
}
|
||||||
|
|
||||||
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<User> Auth::GetUser(const std::string &username_orig) const {
|
void Auth::LinkUser(User &user) const {
|
||||||
auto username = utils::ToLowerCase(username_orig);
|
auto link = storage_.Get(kLinkPrefix + user.username());
|
||||||
auto existing_user = storage_.Get(kUserPrefix + username);
|
|
||||||
if (!existing_user) return std::nullopt;
|
|
||||||
|
|
||||||
nlohmann::json data;
|
|
||||||
try {
|
|
||||||
data = nlohmann::json::parse(*existing_user);
|
|
||||||
} catch (const nlohmann::json::parse_error &e) {
|
|
||||||
throw AuthException("Couldn't load user data!");
|
|
||||||
}
|
|
||||||
|
|
||||||
auto user = User::Deserialize(data);
|
|
||||||
auto link = storage_.Get(kLinkPrefix + username);
|
|
||||||
|
|
||||||
if (link) {
|
if (link) {
|
||||||
auto role = GetRole(*link);
|
auto role = GetRole(*link);
|
||||||
if (role) {
|
if (role) {
|
||||||
user.SetRole(*role);
|
user.SetRole(*role);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<User> Auth::GetUser(const std::string &username_orig) const {
|
||||||
|
if (module_.IsUsed()) return std::nullopt; // User's are not supported when using module
|
||||||
|
auto username = utils::ToLowerCase(username_orig);
|
||||||
|
auto existing_user = storage_.Get(kUserPrefix + username);
|
||||||
|
if (!existing_user) return std::nullopt;
|
||||||
|
|
||||||
|
auto user = User::Deserialize(ParseJson(*existing_user));
|
||||||
|
LinkUser(user);
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Auth::SaveUser(const User &user, system::Transaction *system_tx) {
|
void Auth::SaveUser(const User &user, system::Transaction *system_tx) {
|
||||||
|
DisableIfModuleUsed();
|
||||||
bool success = false;
|
bool success = false;
|
||||||
if (const auto *role = user.role(); role != nullptr) {
|
if (const auto *role = user.role(); role != nullptr) {
|
||||||
success = storage_.PutMultiple(
|
success = storage_.PutMultiple(
|
||||||
@ -338,6 +334,10 @@ void Auth::SaveUser(const User &user, system::Transaction *system_tx) {
|
|||||||
if (!success) {
|
if (!success) {
|
||||||
throw AuthException("Couldn't save user '{}'!", user.username());
|
throw AuthException("Couldn't save user '{}'!", user.username());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Durability updated -> new epoch
|
||||||
|
UpdateEpoch();
|
||||||
|
|
||||||
// All changes to the user end up calling this function, so no need to add a delta anywhere else
|
// All changes to the user end up calling this function, so no need to add a delta anywhere else
|
||||||
if (system_tx) {
|
if (system_tx) {
|
||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
@ -347,6 +347,7 @@ void Auth::SaveUser(const User &user, system::Transaction *system_tx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Auth::UpdatePassword(auth::User &user, const std::optional<std::string> &password) {
|
void Auth::UpdatePassword(auth::User &user, const std::optional<std::string> &password) {
|
||||||
|
DisableIfModuleUsed();
|
||||||
// Check if null
|
// Check if null
|
||||||
if (!password) {
|
if (!password) {
|
||||||
if (!config_.password_permit_null) {
|
if (!config_.password_permit_null) {
|
||||||
@ -378,6 +379,7 @@ void Auth::UpdatePassword(auth::User &user, const std::optional<std::string> &pa
|
|||||||
|
|
||||||
std::optional<User> Auth::AddUser(const std::string &username, const std::optional<std::string> &password,
|
std::optional<User> Auth::AddUser(const std::string &username, const std::optional<std::string> &password,
|
||||||
system::Transaction *system_tx) {
|
system::Transaction *system_tx) {
|
||||||
|
DisableIfModuleUsed();
|
||||||
if (!NameRegexMatch(username)) {
|
if (!NameRegexMatch(username)) {
|
||||||
throw AuthException("Invalid user name.");
|
throw AuthException("Invalid user name.");
|
||||||
}
|
}
|
||||||
@ -392,12 +394,17 @@ std::optional<User> Auth::AddUser(const std::string &username, const std::option
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Auth::RemoveUser(const std::string &username_orig, system::Transaction *system_tx) {
|
bool Auth::RemoveUser(const std::string &username_orig, system::Transaction *system_tx) {
|
||||||
|
DisableIfModuleUsed();
|
||||||
auto username = utils::ToLowerCase(username_orig);
|
auto username = utils::ToLowerCase(username_orig);
|
||||||
if (!storage_.Get(kUserPrefix + username)) return false;
|
if (!storage_.Get(kUserPrefix + username)) return false;
|
||||||
std::vector<std::string> keys({kLinkPrefix + username, kUserPrefix + username});
|
std::vector<std::string> keys({kLinkPrefix + username, kUserPrefix + username});
|
||||||
if (!storage_.DeleteMultiple(keys)) {
|
if (!storage_.DeleteMultiple(keys)) {
|
||||||
throw AuthException("Couldn't remove user '{}'!", username);
|
throw AuthException("Couldn't remove user '{}'!", username);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Durability updated -> new epoch
|
||||||
|
UpdateEpoch();
|
||||||
|
|
||||||
// Handling drop user delta
|
// Handling drop user delta
|
||||||
if (system_tx) {
|
if (system_tx) {
|
||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
@ -412,9 +419,12 @@ std::vector<auth::User> Auth::AllUsers() const {
|
|||||||
for (auto it = storage_.begin(kUserPrefix); it != storage_.end(kUserPrefix); ++it) {
|
for (auto it = storage_.begin(kUserPrefix); it != storage_.end(kUserPrefix); ++it) {
|
||||||
auto username = it->first.substr(kUserPrefix.size());
|
auto username = it->first.substr(kUserPrefix.size());
|
||||||
if (username != utils::ToLowerCase(username)) continue;
|
if (username != utils::ToLowerCase(username)) continue;
|
||||||
auto user = GetUser(username);
|
try {
|
||||||
if (user) {
|
User user = auth::User::Deserialize(ParseJson(it->second)); // Will throw on failure
|
||||||
ret.push_back(std::move(*user));
|
LinkUser(user);
|
||||||
|
ret.emplace_back(std::move(user));
|
||||||
|
} catch (AuthException &) {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
@ -425,9 +435,12 @@ std::vector<std::string> Auth::AllUsernames() const {
|
|||||||
for (auto it = storage_.begin(kUserPrefix); it != storage_.end(kUserPrefix); ++it) {
|
for (auto it = storage_.begin(kUserPrefix); it != storage_.end(kUserPrefix); ++it) {
|
||||||
auto username = it->first.substr(kUserPrefix.size());
|
auto username = it->first.substr(kUserPrefix.size());
|
||||||
if (username != utils::ToLowerCase(username)) continue;
|
if (username != utils::ToLowerCase(username)) continue;
|
||||||
auto user = GetUser(username);
|
try {
|
||||||
if (user) {
|
// Check if serialized correctly
|
||||||
ret.push_back(username);
|
memgraph::auth::User::Deserialize(ParseJson(it->second)); // Will throw on failure
|
||||||
|
ret.emplace_back(std::move(username));
|
||||||
|
} catch (AuthException &) {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
@ -435,25 +448,24 @@ std::vector<std::string> Auth::AllUsernames() const {
|
|||||||
|
|
||||||
bool Auth::HasUsers() const { return storage_.begin(kUserPrefix) != storage_.end(kUserPrefix); }
|
bool Auth::HasUsers() const { return storage_.begin(kUserPrefix) != storage_.end(kUserPrefix); }
|
||||||
|
|
||||||
|
bool Auth::AccessControlled() const { return HasUsers() || module_.IsUsed(); }
|
||||||
|
|
||||||
std::optional<Role> Auth::GetRole(const std::string &rolename_orig) const {
|
std::optional<Role> Auth::GetRole(const std::string &rolename_orig) const {
|
||||||
auto rolename = utils::ToLowerCase(rolename_orig);
|
auto rolename = utils::ToLowerCase(rolename_orig);
|
||||||
auto existing_role = storage_.Get(kRolePrefix + rolename);
|
auto existing_role = storage_.Get(kRolePrefix + rolename);
|
||||||
if (!existing_role) return std::nullopt;
|
if (!existing_role) return std::nullopt;
|
||||||
|
|
||||||
nlohmann::json data;
|
return Role::Deserialize(ParseJson(*existing_role));
|
||||||
try {
|
|
||||||
data = nlohmann::json::parse(*existing_role);
|
|
||||||
} catch (const nlohmann::json::parse_error &e) {
|
|
||||||
throw AuthException("Couldn't load role data!");
|
|
||||||
}
|
|
||||||
|
|
||||||
return Role::Deserialize(data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Auth::SaveRole(const Role &role, system::Transaction *system_tx) {
|
void Auth::SaveRole(const Role &role, system::Transaction *system_tx) {
|
||||||
if (!storage_.Put(kRolePrefix + role.rolename(), role.Serialize().dump())) {
|
if (!storage_.Put(kRolePrefix + role.rolename(), role.Serialize().dump())) {
|
||||||
throw AuthException("Couldn't save role '{}'!", role.rolename());
|
throw AuthException("Couldn't save role '{}'!", role.rolename());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Durability updated -> new epoch
|
||||||
|
UpdateEpoch();
|
||||||
|
|
||||||
// All changes to the role end up calling this function, so no need to add a delta anywhere else
|
// All changes to the role end up calling this function, so no need to add a delta anywhere else
|
||||||
if (system_tx) {
|
if (system_tx) {
|
||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
@ -486,6 +498,10 @@ bool Auth::RemoveRole(const std::string &rolename_orig, system::Transaction *sys
|
|||||||
if (!storage_.DeleteMultiple(keys)) {
|
if (!storage_.DeleteMultiple(keys)) {
|
||||||
throw AuthException("Couldn't remove role '{}'!", rolename);
|
throw AuthException("Couldn't remove role '{}'!", rolename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Durability updated -> new epoch
|
||||||
|
UpdateEpoch();
|
||||||
|
|
||||||
// Handling drop role delta
|
// Handling drop role delta
|
||||||
if (system_tx) {
|
if (system_tx) {
|
||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
@ -500,11 +516,8 @@ std::vector<auth::Role> Auth::AllRoles() const {
|
|||||||
for (auto it = storage_.begin(kRolePrefix); it != storage_.end(kRolePrefix); ++it) {
|
for (auto it = storage_.begin(kRolePrefix); it != storage_.end(kRolePrefix); ++it) {
|
||||||
auto rolename = it->first.substr(kRolePrefix.size());
|
auto rolename = it->first.substr(kRolePrefix.size());
|
||||||
if (rolename != utils::ToLowerCase(rolename)) continue;
|
if (rolename != utils::ToLowerCase(rolename)) continue;
|
||||||
if (auto role = GetRole(rolename)) {
|
Role role = memgraph::auth::Role::Deserialize(ParseJson(it->second)); // Will throw on failure
|
||||||
ret.push_back(*role);
|
ret.emplace_back(std::move(role));
|
||||||
} else {
|
|
||||||
throw AuthException("Couldn't load role '{}'!", rolename);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -514,14 +527,19 @@ std::vector<std::string> Auth::AllRolenames() const {
|
|||||||
for (auto it = storage_.begin(kRolePrefix); it != storage_.end(kRolePrefix); ++it) {
|
for (auto it = storage_.begin(kRolePrefix); it != storage_.end(kRolePrefix); ++it) {
|
||||||
auto rolename = it->first.substr(kRolePrefix.size());
|
auto rolename = it->first.substr(kRolePrefix.size());
|
||||||
if (rolename != utils::ToLowerCase(rolename)) continue;
|
if (rolename != utils::ToLowerCase(rolename)) continue;
|
||||||
if (auto role = GetRole(rolename)) {
|
try {
|
||||||
ret.push_back(rolename);
|
// Check that the data is serialized correctly
|
||||||
|
memgraph::auth::Role::Deserialize(ParseJson(it->second));
|
||||||
|
ret.emplace_back(std::move(rolename));
|
||||||
|
} catch (AuthException &) {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<auth::User> Auth::AllUsersForRole(const std::string &rolename_orig) const {
|
std::vector<auth::User> Auth::AllUsersForRole(const std::string &rolename_orig) const {
|
||||||
|
DisableIfModuleUsed();
|
||||||
const auto rolename = utils::ToLowerCase(rolename_orig);
|
const auto rolename = utils::ToLowerCase(rolename_orig);
|
||||||
std::vector<auth::User> ret;
|
std::vector<auth::User> ret;
|
||||||
for (auto it = storage_.begin(kLinkPrefix); it != storage_.end(kLinkPrefix); ++it) {
|
for (auto it = storage_.begin(kLinkPrefix); it != storage_.end(kLinkPrefix); ++it) {
|
||||||
@ -540,51 +558,176 @@ std::vector<auth::User> Auth::AllUsersForRole(const std::string &rolename_orig)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
bool Auth::GrantDatabaseToUser(const std::string &db, const std::string &name, system::Transaction *system_tx) {
|
Auth::Result Auth::GrantDatabase(const std::string &db, const std::string &name, system::Transaction *system_tx) {
|
||||||
if (auto user = GetUser(name)) {
|
using enum Auth::Result;
|
||||||
if (db == kAllDatabases) {
|
if (module_.IsUsed()) {
|
||||||
user->db_access().GrantAll();
|
if (auto role = GetRole(name)) {
|
||||||
} else {
|
GrantDatabase(db, *role, system_tx);
|
||||||
user->db_access().Add(db);
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
SaveUser(*user, system_tx);
|
return NO_ROLE;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
if (auto user = GetUser(name)) {
|
||||||
|
GrantDatabase(db, *user, system_tx);
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
if (auto role = GetRole(name)) {
|
||||||
|
GrantDatabase(db, *role, system_tx);
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
return NO_USER_ROLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Auth::RevokeDatabaseFromUser(const std::string &db, const std::string &name, system::Transaction *system_tx) {
|
void Auth::GrantDatabase(const std::string &db, User &user, system::Transaction *system_tx) {
|
||||||
if (auto user = GetUser(name)) {
|
if (db == kAllDatabases) {
|
||||||
if (db == kAllDatabases) {
|
user.db_access().GrantAll();
|
||||||
user->db_access().DenyAll();
|
} else {
|
||||||
} else {
|
user.db_access().Grant(db);
|
||||||
user->db_access().Remove(db);
|
|
||||||
}
|
|
||||||
SaveUser(*user, system_tx);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
SaveUser(user, system_tx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Auth::GrantDatabase(const std::string &db, Role &role, system::Transaction *system_tx) {
|
||||||
|
if (db == kAllDatabases) {
|
||||||
|
role.db_access().GrantAll();
|
||||||
|
} else {
|
||||||
|
role.db_access().Grant(db);
|
||||||
|
}
|
||||||
|
SaveRole(role, system_tx);
|
||||||
|
}
|
||||||
|
|
||||||
|
Auth::Result Auth::DenyDatabase(const std::string &db, const std::string &name, system::Transaction *system_tx) {
|
||||||
|
using enum Auth::Result;
|
||||||
|
if (module_.IsUsed()) {
|
||||||
|
if (auto role = GetRole(name)) {
|
||||||
|
DenyDatabase(db, *role, system_tx);
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
return NO_ROLE;
|
||||||
|
}
|
||||||
|
if (auto user = GetUser(name)) {
|
||||||
|
DenyDatabase(db, *user, system_tx);
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
if (auto role = GetRole(name)) {
|
||||||
|
DenyDatabase(db, *role, system_tx);
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
return NO_USER_ROLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Auth::DenyDatabase(const std::string &db, User &user, system::Transaction *system_tx) {
|
||||||
|
if (db == kAllDatabases) {
|
||||||
|
user.db_access().DenyAll();
|
||||||
|
} else {
|
||||||
|
user.db_access().Deny(db);
|
||||||
|
}
|
||||||
|
SaveUser(user, system_tx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Auth::DenyDatabase(const std::string &db, Role &role, system::Transaction *system_tx) {
|
||||||
|
if (db == kAllDatabases) {
|
||||||
|
role.db_access().DenyAll();
|
||||||
|
} else {
|
||||||
|
role.db_access().Deny(db);
|
||||||
|
}
|
||||||
|
SaveRole(role, system_tx);
|
||||||
|
}
|
||||||
|
|
||||||
|
Auth::Result Auth::RevokeDatabase(const std::string &db, const std::string &name, system::Transaction *system_tx) {
|
||||||
|
using enum Auth::Result;
|
||||||
|
if (module_.IsUsed()) {
|
||||||
|
if (auto role = GetRole(name)) {
|
||||||
|
RevokeDatabase(db, *role, system_tx);
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
return NO_ROLE;
|
||||||
|
}
|
||||||
|
if (auto user = GetUser(name)) {
|
||||||
|
RevokeDatabase(db, *user, system_tx);
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
if (auto role = GetRole(name)) {
|
||||||
|
RevokeDatabase(db, *role, system_tx);
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
return NO_USER_ROLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Auth::RevokeDatabase(const std::string &db, User &user, system::Transaction *system_tx) {
|
||||||
|
if (db == kAllDatabases) {
|
||||||
|
user.db_access().RevokeAll();
|
||||||
|
} else {
|
||||||
|
user.db_access().Revoke(db);
|
||||||
|
}
|
||||||
|
SaveUser(user, system_tx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Auth::RevokeDatabase(const std::string &db, Role &role, system::Transaction *system_tx) {
|
||||||
|
if (db == kAllDatabases) {
|
||||||
|
role.db_access().RevokeAll();
|
||||||
|
} else {
|
||||||
|
role.db_access().Revoke(db);
|
||||||
|
}
|
||||||
|
SaveRole(role, system_tx);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Auth::DeleteDatabase(const std::string &db, system::Transaction *system_tx) {
|
void Auth::DeleteDatabase(const std::string &db, system::Transaction *system_tx) {
|
||||||
for (auto it = storage_.begin(kUserPrefix); it != storage_.end(kUserPrefix); ++it) {
|
for (auto it = storage_.begin(kUserPrefix); it != storage_.end(kUserPrefix); ++it) {
|
||||||
auto username = it->first.substr(kUserPrefix.size());
|
auto username = it->first.substr(kUserPrefix.size());
|
||||||
if (auto user = GetUser(username)) {
|
try {
|
||||||
user->db_access().Delete(db);
|
User user = auth::User::Deserialize(ParseJson(it->second));
|
||||||
SaveUser(*user, system_tx);
|
LinkUser(user);
|
||||||
|
user.db_access().Revoke(db);
|
||||||
|
SaveUser(user, system_tx);
|
||||||
|
} catch (AuthException &) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (auto it = storage_.begin(kRolePrefix); it != storage_.end(kRolePrefix); ++it) {
|
||||||
|
auto rolename = it->first.substr(kRolePrefix.size());
|
||||||
|
try {
|
||||||
|
auto role = memgraph::auth::Role::Deserialize(ParseJson(it->second));
|
||||||
|
role.db_access().Revoke(db);
|
||||||
|
SaveRole(role, system_tx);
|
||||||
|
} catch (AuthException &) {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Auth::SetMainDatabase(std::string_view db, const std::string &name, system::Transaction *system_tx) {
|
Auth::Result Auth::SetMainDatabase(std::string_view db, const std::string &name, system::Transaction *system_tx) {
|
||||||
if (auto user = GetUser(name)) {
|
using enum Auth::Result;
|
||||||
if (!user->db_access().SetDefault(db)) {
|
if (module_.IsUsed()) {
|
||||||
throw AuthException("Couldn't set default database '{}' for user '{}'!", db, name);
|
if (auto role = GetRole(name)) {
|
||||||
|
SetMainDatabase(db, *role, system_tx);
|
||||||
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
SaveUser(*user, system_tx);
|
return NO_ROLE;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
if (auto user = GetUser(name)) {
|
||||||
|
SetMainDatabase(db, *user, system_tx);
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
if (auto role = GetRole(name)) {
|
||||||
|
SetMainDatabase(db, *role, system_tx);
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
return NO_USER_ROLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Auth::SetMainDatabase(std::string_view db, User &user, system::Transaction *system_tx) {
|
||||||
|
if (!user.db_access().SetMain(db)) {
|
||||||
|
throw AuthException("Couldn't set default database '{}' for '{}'!", db, user.username());
|
||||||
|
}
|
||||||
|
SaveUser(user, system_tx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Auth::SetMainDatabase(std::string_view db, Role &role, system::Transaction *system_tx) {
|
||||||
|
if (!role.db_access().SetMain(db)) {
|
||||||
|
throw AuthException("Couldn't set default database '{}' for '{}'!", db, role.rolename());
|
||||||
|
}
|
||||||
|
SaveRole(role, system_tx);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -29,6 +29,18 @@ using SynchedAuth = memgraph::utils::Synchronized<memgraph::auth::Auth, memgraph
|
|||||||
|
|
||||||
static const constexpr char *const kAllDatabases = "*";
|
static const constexpr char *const kAllDatabases = "*";
|
||||||
|
|
||||||
|
struct RoleWUsername : Role {
|
||||||
|
template <typename... Args>
|
||||||
|
RoleWUsername(std::string_view username, Args &&...args) : Role{std::forward<Args>(args)...}, username_{username} {}
|
||||||
|
|
||||||
|
std::string username() { return username_; }
|
||||||
|
const std::string &username() const { return username_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string username_;
|
||||||
|
};
|
||||||
|
using UserOrRole = std::variant<User, RoleWUsername>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class serves as the main Authentication/Authorization storage.
|
* This class serves as the main Authentication/Authorization storage.
|
||||||
* It provides functions for managing Users, Roles, Permissions and FineGrainedAccessPermissions.
|
* It provides functions for managing Users, Roles, Permissions and FineGrainedAccessPermissions.
|
||||||
@ -61,6 +73,25 @@ class Auth final {
|
|||||||
std::regex password_regex{password_regex_str};
|
std::regex password_regex{password_regex_str};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Epoch {
|
||||||
|
Epoch() : epoch_{0} {}
|
||||||
|
Epoch(unsigned e) : epoch_{e} {}
|
||||||
|
|
||||||
|
Epoch operator++() { return ++epoch_; }
|
||||||
|
bool operator==(const Epoch &rhs) const = default;
|
||||||
|
|
||||||
|
private:
|
||||||
|
unsigned epoch_;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const Epoch kStartEpoch;
|
||||||
|
|
||||||
|
enum class Result {
|
||||||
|
SUCCESS,
|
||||||
|
NO_USER_ROLE,
|
||||||
|
NO_ROLE,
|
||||||
|
};
|
||||||
|
|
||||||
explicit Auth(std::string storage_directory, Config config);
|
explicit Auth(std::string storage_directory, Config config);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -89,7 +120,7 @@ class Auth final {
|
|||||||
* @return a user when the username and password match, nullopt otherwise
|
* @return a user when the username and password match, nullopt otherwise
|
||||||
* @throw AuthException if unable to authenticate for whatever reason.
|
* @throw AuthException if unable to authenticate for whatever reason.
|
||||||
*/
|
*/
|
||||||
std::optional<User> Authenticate(const std::string &username, const std::string &password);
|
std::optional<UserOrRole> Authenticate(const std::string &username, const std::string &password);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a user from the storage.
|
* Gets a user from the storage.
|
||||||
@ -101,6 +132,8 @@ class Auth final {
|
|||||||
*/
|
*/
|
||||||
std::optional<User> GetUser(const std::string &username) const;
|
std::optional<User> GetUser(const std::string &username) const;
|
||||||
|
|
||||||
|
void LinkUser(User &user) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves a user object to the storage.
|
* Saves a user object to the storage.
|
||||||
*
|
*
|
||||||
@ -163,6 +196,13 @@ class Auth final {
|
|||||||
*/
|
*/
|
||||||
bool HasUsers() const;
|
bool HasUsers() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the access is controlled by authentication/authorization.
|
||||||
|
*
|
||||||
|
* @return `true` if auth needs to run
|
||||||
|
*/
|
||||||
|
bool AccessControlled() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a role from the storage.
|
* Gets a role from the storage.
|
||||||
*
|
*
|
||||||
@ -173,6 +213,37 @@ class Auth final {
|
|||||||
*/
|
*/
|
||||||
std::optional<Role> GetRole(const std::string &rolename) const;
|
std::optional<Role> GetRole(const std::string &rolename) const;
|
||||||
|
|
||||||
|
std::optional<UserOrRole> GetUserOrRole(const std::optional<std::string> &username,
|
||||||
|
const std::optional<std::string> &rolename) const {
|
||||||
|
auto expect = [](bool condition, std::string &&msg) {
|
||||||
|
if (!condition) throw AuthException(std::move(msg));
|
||||||
|
};
|
||||||
|
// Special case if we are using a module; we must find the specified role
|
||||||
|
if (module_.IsUsed()) {
|
||||||
|
expect(username && rolename, "When using a module, a role needs to be connected to a username.");
|
||||||
|
const auto role = GetRole(*rolename);
|
||||||
|
expect(role != std::nullopt, "No role named " + *rolename);
|
||||||
|
return UserOrRole(auth::RoleWUsername{*username, *role});
|
||||||
|
}
|
||||||
|
|
||||||
|
// First check if we need to find a role
|
||||||
|
if (username && rolename) {
|
||||||
|
const auto role = GetRole(*rolename);
|
||||||
|
expect(role != std::nullopt, "No role named " + *rolename);
|
||||||
|
return UserOrRole(auth::RoleWUsername{*username, *role});
|
||||||
|
}
|
||||||
|
|
||||||
|
// We are only looking for a user
|
||||||
|
if (username) {
|
||||||
|
const auto user = GetUser(*username);
|
||||||
|
expect(user != std::nullopt, "No user named " + *username);
|
||||||
|
return *user;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No user or role
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves a role object to the storage.
|
* Saves a role object to the storage.
|
||||||
*
|
*
|
||||||
@ -229,16 +300,6 @@ class Auth final {
|
|||||||
std::vector<User> AllUsersForRole(const std::string &rolename) const;
|
std::vector<User> AllUsersForRole(const std::string &rolename) const;
|
||||||
|
|
||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
/**
|
|
||||||
* @brief Revoke access to individual database for a user.
|
|
||||||
*
|
|
||||||
* @param db name of the database to revoke
|
|
||||||
* @param name user's username
|
|
||||||
* @return true on success
|
|
||||||
* @throw AuthException if unable to find or update the user
|
|
||||||
*/
|
|
||||||
bool RevokeDatabaseFromUser(const std::string &db, const std::string &name, system::Transaction *system_tx = nullptr);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Grant access to individual database for a user.
|
* @brief Grant access to individual database for a user.
|
||||||
*
|
*
|
||||||
@ -247,7 +308,33 @@ class Auth final {
|
|||||||
* @return true on success
|
* @return true on success
|
||||||
* @throw AuthException if unable to find or update the user
|
* @throw AuthException if unable to find or update the user
|
||||||
*/
|
*/
|
||||||
bool GrantDatabaseToUser(const std::string &db, const std::string &name, system::Transaction *system_tx = nullptr);
|
Result GrantDatabase(const std::string &db, const std::string &name, system::Transaction *system_tx = nullptr);
|
||||||
|
void GrantDatabase(const std::string &db, User &user, system::Transaction *system_tx = nullptr);
|
||||||
|
void GrantDatabase(const std::string &db, Role &role, system::Transaction *system_tx = nullptr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Revoke access to individual database for a user.
|
||||||
|
*
|
||||||
|
* @param db name of the database to revoke
|
||||||
|
* @param name user's username
|
||||||
|
* @return true on success
|
||||||
|
* @throw AuthException if unable to find or update the user
|
||||||
|
*/
|
||||||
|
Result DenyDatabase(const std::string &db, const std::string &name, system::Transaction *system_tx = nullptr);
|
||||||
|
void DenyDatabase(const std::string &db, User &user, system::Transaction *system_tx = nullptr);
|
||||||
|
void DenyDatabase(const std::string &db, Role &role, system::Transaction *system_tx = nullptr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Revoke access to individual database for a user.
|
||||||
|
*
|
||||||
|
* @param db name of the database to revoke
|
||||||
|
* @param name user's username
|
||||||
|
* @return true on success
|
||||||
|
* @throw AuthException if unable to find or update the user
|
||||||
|
*/
|
||||||
|
Result RevokeDatabase(const std::string &db, const std::string &name, system::Transaction *system_tx = nullptr);
|
||||||
|
void RevokeDatabase(const std::string &db, User &user, system::Transaction *system_tx = nullptr);
|
||||||
|
void RevokeDatabase(const std::string &db, Role &role, system::Transaction *system_tx = nullptr);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Delete a database from all users.
|
* @brief Delete a database from all users.
|
||||||
@ -265,9 +352,17 @@ class Auth final {
|
|||||||
* @return true on success
|
* @return true on success
|
||||||
* @throw AuthException if unable to find or update the user
|
* @throw AuthException if unable to find or update the user
|
||||||
*/
|
*/
|
||||||
bool SetMainDatabase(std::string_view db, const std::string &name, system::Transaction *system_tx = nullptr);
|
Result SetMainDatabase(std::string_view db, const std::string &name, system::Transaction *system_tx = nullptr);
|
||||||
|
void SetMainDatabase(std::string_view db, User &user, system::Transaction *system_tx = nullptr);
|
||||||
|
void SetMainDatabase(std::string_view db, Role &role, system::Transaction *system_tx = nullptr);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
bool UpToDate(Epoch &e) const {
|
||||||
|
bool res = e == epoch_;
|
||||||
|
e = epoch_;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
* @brief
|
* @brief
|
||||||
@ -278,11 +373,18 @@ class Auth final {
|
|||||||
*/
|
*/
|
||||||
bool NameRegexMatch(const std::string &user_or_role) const;
|
bool NameRegexMatch(const std::string &user_or_role) const;
|
||||||
|
|
||||||
|
void UpdateEpoch() { ++epoch_; }
|
||||||
|
|
||||||
|
void DisableIfModuleUsed() const {
|
||||||
|
if (module_.IsUsed()) throw AuthException("Operation not permited when using an authentication module.");
|
||||||
|
}
|
||||||
|
|
||||||
// Even though the `kvstore::KVStore` class is guaranteed to be thread-safe,
|
// Even though the `kvstore::KVStore` class is guaranteed to be thread-safe,
|
||||||
// Auth is not thread-safe because modifying users and roles might require
|
// Auth is not thread-safe because modifying users and roles might require
|
||||||
// more than one operation on the storage.
|
// more than one operation on the storage.
|
||||||
kvstore::KVStore storage_;
|
kvstore::KVStore storage_;
|
||||||
auth::Module module_;
|
auth::Module module_;
|
||||||
Config config_;
|
Config config_;
|
||||||
|
Epoch epoch_{kStartEpoch};
|
||||||
};
|
};
|
||||||
} // namespace memgraph::auth
|
} // namespace memgraph::auth
|
||||||
|
@ -425,10 +425,11 @@ Role::Role(const std::string &rolename, const Permissions &permissions)
|
|||||||
: rolename_(utils::ToLowerCase(rolename)), permissions_(permissions) {}
|
: rolename_(utils::ToLowerCase(rolename)), permissions_(permissions) {}
|
||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
Role::Role(const std::string &rolename, const Permissions &permissions,
|
Role::Role(const std::string &rolename, const Permissions &permissions,
|
||||||
FineGrainedAccessHandler fine_grained_access_handler)
|
FineGrainedAccessHandler fine_grained_access_handler, Databases db_access)
|
||||||
: rolename_(utils::ToLowerCase(rolename)),
|
: rolename_(utils::ToLowerCase(rolename)),
|
||||||
permissions_(permissions),
|
permissions_(permissions),
|
||||||
fine_grained_access_handler_(std::move(fine_grained_access_handler)) {}
|
fine_grained_access_handler_(std::move(fine_grained_access_handler)),
|
||||||
|
db_access_(std::move(db_access)) {}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
const std::string &Role::rolename() const { return rolename_; }
|
const std::string &Role::rolename() const { return rolename_; }
|
||||||
@ -454,8 +455,10 @@ nlohmann::json Role::Serialize() const {
|
|||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||||
data[kFineGrainedAccessHandler] = fine_grained_access_handler_.Serialize();
|
data[kFineGrainedAccessHandler] = fine_grained_access_handler_.Serialize();
|
||||||
|
data[kDatabases] = db_access_.Serialize();
|
||||||
} else {
|
} else {
|
||||||
data[kFineGrainedAccessHandler] = {};
|
data[kFineGrainedAccessHandler] = {};
|
||||||
|
data[kDatabases] = {};
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
return data;
|
return data;
|
||||||
@ -471,12 +474,21 @@ Role Role::Deserialize(const nlohmann::json &data) {
|
|||||||
auto permissions = Permissions::Deserialize(data[kPermissions]);
|
auto permissions = Permissions::Deserialize(data[kPermissions]);
|
||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||||
|
Databases db_access;
|
||||||
|
if (data[kDatabases].is_structured()) {
|
||||||
|
db_access = Databases::Deserialize(data[kDatabases]);
|
||||||
|
} else {
|
||||||
|
// Back-compatibility
|
||||||
|
spdlog::warn("Role without specified database access. Given access to the default database.");
|
||||||
|
db_access.Grant(dbms::kDefaultDB);
|
||||||
|
db_access.SetMain(dbms::kDefaultDB);
|
||||||
|
}
|
||||||
FineGrainedAccessHandler fine_grained_access_handler;
|
FineGrainedAccessHandler fine_grained_access_handler;
|
||||||
// We can have an empty fine_grained if the user was created without a valid license
|
// We can have an empty fine_grained if the user was created without a valid license
|
||||||
if (data[kFineGrainedAccessHandler].is_object()) {
|
if (data[kFineGrainedAccessHandler].is_object()) {
|
||||||
fine_grained_access_handler = FineGrainedAccessHandler::Deserialize(data[kFineGrainedAccessHandler]);
|
fine_grained_access_handler = FineGrainedAccessHandler::Deserialize(data[kFineGrainedAccessHandler]);
|
||||||
}
|
}
|
||||||
return {data[kRoleName], permissions, std::move(fine_grained_access_handler)};
|
return {data[kRoleName], permissions, std::move(fine_grained_access_handler), std::move(db_access)};
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
return {data[kRoleName], permissions};
|
return {data[kRoleName], permissions};
|
||||||
@ -493,7 +505,7 @@ bool operator==(const Role &first, const Role &second) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
void Databases::Add(std::string_view db) {
|
void Databases::Grant(std::string_view db) {
|
||||||
if (allow_all_) {
|
if (allow_all_) {
|
||||||
grants_dbs_.clear();
|
grants_dbs_.clear();
|
||||||
allow_all_ = false;
|
allow_all_ = false;
|
||||||
@ -502,19 +514,19 @@ void Databases::Add(std::string_view db) {
|
|||||||
denies_dbs_.erase(std::string{db}); // TODO: C++23 use transparent key compare
|
denies_dbs_.erase(std::string{db}); // TODO: C++23 use transparent key compare
|
||||||
}
|
}
|
||||||
|
|
||||||
void Databases::Remove(const std::string &db) {
|
void Databases::Deny(const std::string &db) {
|
||||||
denies_dbs_.emplace(db);
|
denies_dbs_.emplace(db);
|
||||||
grants_dbs_.erase(db);
|
grants_dbs_.erase(db);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Databases::Delete(const std::string &db) {
|
void Databases::Revoke(const std::string &db) {
|
||||||
denies_dbs_.erase(db);
|
denies_dbs_.erase(db);
|
||||||
if (!allow_all_) {
|
if (!allow_all_) {
|
||||||
grants_dbs_.erase(db);
|
grants_dbs_.erase(db);
|
||||||
}
|
}
|
||||||
// Reset if default deleted
|
// Reset if default deleted
|
||||||
if (default_db_ == db) {
|
if (main_db_ == db) {
|
||||||
default_db_ = "";
|
main_db_ = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -530,9 +542,16 @@ void Databases::DenyAll() {
|
|||||||
denies_dbs_.clear();
|
denies_dbs_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Databases::SetDefault(std::string_view db) {
|
void Databases::RevokeAll() {
|
||||||
|
allow_all_ = false;
|
||||||
|
grants_dbs_.clear();
|
||||||
|
denies_dbs_.clear();
|
||||||
|
main_db_ = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Databases::SetMain(std::string_view db) {
|
||||||
if (!Contains(db)) return false;
|
if (!Contains(db)) return false;
|
||||||
default_db_ = db;
|
main_db_ = db;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -540,11 +559,11 @@ bool Databases::SetDefault(std::string_view db) {
|
|||||||
return !denies_dbs_.contains(db) && (allow_all_ || grants_dbs_.contains(db));
|
return !denies_dbs_.contains(db) && (allow_all_ || grants_dbs_.contains(db));
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string &Databases::GetDefault() const {
|
const std::string &Databases::GetMain() const {
|
||||||
if (!Contains(default_db_)) {
|
if (!Contains(main_db_)) {
|
||||||
throw AuthException("No access to the set default database \"{}\".", default_db_);
|
throw AuthException("No access to the set default database \"{}\".", main_db_);
|
||||||
}
|
}
|
||||||
return default_db_;
|
return main_db_;
|
||||||
}
|
}
|
||||||
|
|
||||||
nlohmann::json Databases::Serialize() const {
|
nlohmann::json Databases::Serialize() const {
|
||||||
@ -552,7 +571,7 @@ nlohmann::json Databases::Serialize() const {
|
|||||||
data[kGrants] = grants_dbs_;
|
data[kGrants] = grants_dbs_;
|
||||||
data[kDenies] = denies_dbs_;
|
data[kDenies] = denies_dbs_;
|
||||||
data[kAllowAll] = allow_all_;
|
data[kAllowAll] = allow_all_;
|
||||||
data[kDefault] = default_db_;
|
data[kDefault] = main_db_;
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -719,15 +738,16 @@ User User::Deserialize(const nlohmann::json &data) {
|
|||||||
} else {
|
} else {
|
||||||
// Back-compatibility
|
// Back-compatibility
|
||||||
spdlog::warn("User without specified database access. Given access to the default database.");
|
spdlog::warn("User without specified database access. Given access to the default database.");
|
||||||
db_access.Add(dbms::kDefaultDB);
|
db_access.Grant(dbms::kDefaultDB);
|
||||||
db_access.SetDefault(dbms::kDefaultDB);
|
db_access.SetMain(dbms::kDefaultDB);
|
||||||
}
|
}
|
||||||
FineGrainedAccessHandler fine_grained_access_handler;
|
FineGrainedAccessHandler fine_grained_access_handler;
|
||||||
// We can have an empty fine_grained if the user was created without a valid license
|
// We can have an empty fine_grained if the user was created without a valid license
|
||||||
if (data[kFineGrainedAccessHandler].is_object()) {
|
if (data[kFineGrainedAccessHandler].is_object()) {
|
||||||
fine_grained_access_handler = FineGrainedAccessHandler::Deserialize(data[kFineGrainedAccessHandler]);
|
fine_grained_access_handler = FineGrainedAccessHandler::Deserialize(data[kFineGrainedAccessHandler]);
|
||||||
}
|
}
|
||||||
return {data[kUsername], std::move(password_hash), permissions, std::move(fine_grained_access_handler), db_access};
|
return {data[kUsername], std::move(password_hash), permissions, std::move(fine_grained_access_handler),
|
||||||
|
std::move(db_access)};
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
return {data[kUsername], std::move(password_hash), permissions};
|
return {data[kUsername], std::move(password_hash), permissions};
|
||||||
|
@ -205,52 +205,10 @@ class FineGrainedAccessHandler final {
|
|||||||
bool operator==(const FineGrainedAccessHandler &first, const FineGrainedAccessHandler &second);
|
bool operator==(const FineGrainedAccessHandler &first, const FineGrainedAccessHandler &second);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
class Role final {
|
|
||||||
public:
|
|
||||||
Role() = default;
|
|
||||||
|
|
||||||
explicit Role(const std::string &rolename);
|
|
||||||
Role(const std::string &rolename, const Permissions &permissions);
|
|
||||||
#ifdef MG_ENTERPRISE
|
|
||||||
Role(const std::string &rolename, const Permissions &permissions,
|
|
||||||
FineGrainedAccessHandler fine_grained_access_handler);
|
|
||||||
#endif
|
|
||||||
Role(const Role &) = default;
|
|
||||||
Role &operator=(const Role &) = default;
|
|
||||||
Role(Role &&) noexcept = default;
|
|
||||||
Role &operator=(Role &&) noexcept = default;
|
|
||||||
~Role() = default;
|
|
||||||
|
|
||||||
const std::string &rolename() const;
|
|
||||||
const Permissions &permissions() const;
|
|
||||||
Permissions &permissions();
|
|
||||||
#ifdef MG_ENTERPRISE
|
|
||||||
const FineGrainedAccessHandler &fine_grained_access_handler() const;
|
|
||||||
FineGrainedAccessHandler &fine_grained_access_handler();
|
|
||||||
const FineGrainedAccessPermissions &GetFineGrainedAccessLabelPermissions() const;
|
|
||||||
const FineGrainedAccessPermissions &GetFineGrainedAccessEdgeTypePermissions() const;
|
|
||||||
#endif
|
|
||||||
nlohmann::json Serialize() const;
|
|
||||||
|
|
||||||
/// @throw AuthException if unable to deserialize.
|
|
||||||
static Role Deserialize(const nlohmann::json &data);
|
|
||||||
|
|
||||||
friend bool operator==(const Role &first, const Role &second);
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::string rolename_;
|
|
||||||
Permissions permissions_;
|
|
||||||
#ifdef MG_ENTERPRISE
|
|
||||||
FineGrainedAccessHandler fine_grained_access_handler_;
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
bool operator==(const Role &first, const Role &second);
|
|
||||||
|
|
||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
class Databases final {
|
class Databases final {
|
||||||
public:
|
public:
|
||||||
Databases() : grants_dbs_{std::string{dbms::kDefaultDB}}, allow_all_(false), default_db_(dbms::kDefaultDB) {}
|
Databases() : grants_dbs_{std::string{dbms::kDefaultDB}}, allow_all_(false), main_db_(dbms::kDefaultDB) {}
|
||||||
|
|
||||||
Databases(const Databases &) = default;
|
Databases(const Databases &) = default;
|
||||||
Databases &operator=(const Databases &) = default;
|
Databases &operator=(const Databases &) = default;
|
||||||
@ -263,7 +221,7 @@ class Databases final {
|
|||||||
*
|
*
|
||||||
* @param db name of the database to grant access to
|
* @param db name of the database to grant access to
|
||||||
*/
|
*/
|
||||||
void Add(std::string_view db);
|
void Grant(std::string_view db);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Remove database to the list of granted access.
|
* @brief Remove database to the list of granted access.
|
||||||
@ -272,7 +230,7 @@ class Databases final {
|
|||||||
*
|
*
|
||||||
* @param db name of the database to grant access to
|
* @param db name of the database to grant access to
|
||||||
*/
|
*/
|
||||||
void Remove(const std::string &db);
|
void Deny(const std::string &db);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Called when database is dropped. Removes it from granted (if allow_all is false) and denied set.
|
* @brief Called when database is dropped. Removes it from granted (if allow_all is false) and denied set.
|
||||||
@ -280,7 +238,7 @@ class Databases final {
|
|||||||
*
|
*
|
||||||
* @param db name of the database to grant access to
|
* @param db name of the database to grant access to
|
||||||
*/
|
*/
|
||||||
void Delete(const std::string &db);
|
void Revoke(const std::string &db);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Set allow_all_ to true and clears grants and denied sets.
|
* @brief Set allow_all_ to true and clears grants and denied sets.
|
||||||
@ -292,10 +250,15 @@ class Databases final {
|
|||||||
*/
|
*/
|
||||||
void DenyAll();
|
void DenyAll();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set allow_all_ to false and clears grants and denied sets.
|
||||||
|
*/
|
||||||
|
void RevokeAll();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Set the default database.
|
* @brief Set the default database.
|
||||||
*/
|
*/
|
||||||
bool SetDefault(std::string_view db);
|
bool SetMain(std::string_view db);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Checks if access is grated to the database.
|
* @brief Checks if access is grated to the database.
|
||||||
@ -304,11 +267,13 @@ class Databases final {
|
|||||||
* @return true if allow_all and not denied or granted
|
* @return true if allow_all and not denied or granted
|
||||||
*/
|
*/
|
||||||
bool Contains(std::string_view db) const;
|
bool Contains(std::string_view db) const;
|
||||||
|
bool Denies(std::string_view db_name) const { return denies_dbs_.contains(db_name); }
|
||||||
|
bool Grants(std::string_view db_name) const { return allow_all_ || grants_dbs_.contains(db_name); }
|
||||||
|
|
||||||
bool GetAllowAll() const { return allow_all_; }
|
bool GetAllowAll() const { return allow_all_; }
|
||||||
const std::set<std::string, std::less<>> &GetGrants() const { return grants_dbs_; }
|
const std::set<std::string, std::less<>> &GetGrants() const { return grants_dbs_; }
|
||||||
const std::set<std::string, std::less<>> &GetDenies() const { return denies_dbs_; }
|
const std::set<std::string, std::less<>> &GetDenies() const { return denies_dbs_; }
|
||||||
const std::string &GetDefault() const;
|
const std::string &GetMain() const;
|
||||||
|
|
||||||
nlohmann::json Serialize() const;
|
nlohmann::json Serialize() const;
|
||||||
/// @throw AuthException if unable to deserialize.
|
/// @throw AuthException if unable to deserialize.
|
||||||
@ -320,15 +285,69 @@ class Databases final {
|
|||||||
: grants_dbs_(std::move(grant)),
|
: grants_dbs_(std::move(grant)),
|
||||||
denies_dbs_(std::move(deny)),
|
denies_dbs_(std::move(deny)),
|
||||||
allow_all_(allow_all),
|
allow_all_(allow_all),
|
||||||
default_db_(std::move(default_db)) {}
|
main_db_(std::move(default_db)) {}
|
||||||
|
|
||||||
std::set<std::string, std::less<>> grants_dbs_; //!< set of databases with granted access
|
std::set<std::string, std::less<>> grants_dbs_; //!< set of databases with granted access
|
||||||
std::set<std::string, std::less<>> denies_dbs_; //!< set of databases with denied access
|
std::set<std::string, std::less<>> denies_dbs_; //!< set of databases with denied access
|
||||||
bool allow_all_; //!< flag to allow access to everything (denied overrides this)
|
bool allow_all_; //!< flag to allow access to everything (denied overrides this)
|
||||||
std::string default_db_; //!< user's default database
|
std::string main_db_; //!< user's default database
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
class Role {
|
||||||
|
public:
|
||||||
|
Role() = default;
|
||||||
|
|
||||||
|
explicit Role(const std::string &rolename);
|
||||||
|
Role(const std::string &rolename, const Permissions &permissions);
|
||||||
|
#ifdef MG_ENTERPRISE
|
||||||
|
Role(const std::string &rolename, const Permissions &permissions,
|
||||||
|
FineGrainedAccessHandler fine_grained_access_handler, Databases db_access = {});
|
||||||
|
#endif
|
||||||
|
Role(const Role &) = default;
|
||||||
|
Role &operator=(const Role &) = default;
|
||||||
|
Role(Role &&) noexcept = default;
|
||||||
|
Role &operator=(Role &&) noexcept = default;
|
||||||
|
~Role() = default;
|
||||||
|
|
||||||
|
const std::string &rolename() const;
|
||||||
|
const Permissions &permissions() const;
|
||||||
|
Permissions &permissions();
|
||||||
|
Permissions GetPermissions() const { return permissions_; }
|
||||||
|
#ifdef MG_ENTERPRISE
|
||||||
|
const FineGrainedAccessHandler &fine_grained_access_handler() const;
|
||||||
|
FineGrainedAccessHandler &fine_grained_access_handler();
|
||||||
|
const FineGrainedAccessPermissions &GetFineGrainedAccessLabelPermissions() const;
|
||||||
|
const FineGrainedAccessPermissions &GetFineGrainedAccessEdgeTypePermissions() const;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef MG_ENTERPRISE
|
||||||
|
Databases &db_access() { return db_access_; }
|
||||||
|
const Databases &db_access() const { return db_access_; }
|
||||||
|
|
||||||
|
bool DeniesDB(std::string_view db_name) const { return db_access_.Denies(db_name); }
|
||||||
|
bool GrantsDB(std::string_view db_name) const { return db_access_.Grants(db_name); }
|
||||||
|
bool HasAccess(std::string_view db_name) const { return !DeniesDB(db_name) && GrantsDB(db_name); }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
nlohmann::json Serialize() const;
|
||||||
|
|
||||||
|
/// @throw AuthException if unable to deserialize.
|
||||||
|
static Role Deserialize(const nlohmann::json &data);
|
||||||
|
|
||||||
|
friend bool operator==(const Role &first, const Role &second);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string rolename_;
|
||||||
|
Permissions permissions_;
|
||||||
|
#ifdef MG_ENTERPRISE
|
||||||
|
FineGrainedAccessHandler fine_grained_access_handler_;
|
||||||
|
Databases db_access_;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
bool operator==(const Role &first, const Role &second);
|
||||||
|
|
||||||
// TODO (mferencevic): Implement password expiry.
|
// TODO (mferencevic): Implement password expiry.
|
||||||
class User final {
|
class User final {
|
||||||
public:
|
public:
|
||||||
@ -388,6 +407,18 @@ class User final {
|
|||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
Databases &db_access() { return database_access_; }
|
Databases &db_access() { return database_access_; }
|
||||||
const Databases &db_access() const { return database_access_; }
|
const Databases &db_access() const { return database_access_; }
|
||||||
|
|
||||||
|
bool DeniesDB(std::string_view db_name) const {
|
||||||
|
bool denies = database_access_.Denies(db_name);
|
||||||
|
if (role_) denies |= role_->DeniesDB(db_name);
|
||||||
|
return denies;
|
||||||
|
}
|
||||||
|
bool GrantsDB(std::string_view db_name) const {
|
||||||
|
bool grants = database_access_.Grants(db_name);
|
||||||
|
if (role_) grants |= role_->GrantsDB(db_name);
|
||||||
|
return grants;
|
||||||
|
}
|
||||||
|
bool HasAccess(std::string_view db_name) const { return !DeniesDB(db_name) && GrantsDB(db_name); }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
nlohmann::json Serialize() const;
|
nlohmann::json Serialize() const;
|
||||||
@ -403,7 +434,7 @@ class User final {
|
|||||||
Permissions permissions_;
|
Permissions permissions_;
|
||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
FineGrainedAccessHandler fine_grained_access_handler_;
|
FineGrainedAccessHandler fine_grained_access_handler_;
|
||||||
Databases database_access_;
|
Databases database_access_{};
|
||||||
#endif
|
#endif
|
||||||
std::optional<Role> role_;
|
std::optional<Role> role_;
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2022 Memgraph Ltd.
|
// Copyright 2024 Memgraph Ltd.
|
||||||
//
|
//
|
||||||
// Licensed as a Memgraph Enterprise file under the Memgraph Enterprise
|
// Licensed as a Memgraph Enterprise file under the Memgraph Enterprise
|
||||||
// License (the "License"); by using this file, you agree to be bound by the terms of the License, and you may not use
|
// License (the "License"); by using this file, you agree to be bound by the terms of the License, and you may not use
|
||||||
@ -403,7 +403,7 @@ nlohmann::json Module::Call(const nlohmann::json ¶ms, int timeout_millisec)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Module::IsUsed() { return !module_executable_path_.empty(); }
|
bool Module::IsUsed() const { return !module_executable_path_.empty(); }
|
||||||
|
|
||||||
void Module::Shutdown() {
|
void Module::Shutdown() {
|
||||||
if (pid_ == -1) return;
|
if (pid_ == -1) return;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2022 Memgraph Ltd.
|
// Copyright 2024 Memgraph Ltd.
|
||||||
//
|
//
|
||||||
// Licensed as a Memgraph Enterprise file under the Memgraph Enterprise
|
// Licensed as a Memgraph Enterprise file under the Memgraph Enterprise
|
||||||
// License (the "License"); by using this file, you agree to be bound by the terms of the License, and you may not use
|
// License (the "License"); by using this file, you agree to be bound by the terms of the License, and you may not use
|
||||||
@ -49,7 +49,7 @@ class Module final {
|
|||||||
/// specified executable path and can thus be used.
|
/// specified executable path and can thus be used.
|
||||||
///
|
///
|
||||||
/// @return boolean indicating whether the module can be used
|
/// @return boolean indicating whether the module can be used
|
||||||
bool IsUsed();
|
bool IsUsed() const;
|
||||||
|
|
||||||
~Module();
|
~Module();
|
||||||
|
|
||||||
|
@ -18,11 +18,9 @@
|
|||||||
#include "utils/enum.hpp"
|
#include "utils/enum.hpp"
|
||||||
|
|
||||||
namespace memgraph::slk {
|
namespace memgraph::slk {
|
||||||
|
|
||||||
// Serialize code for auth::Role
|
// Serialize code for auth::Role
|
||||||
void Save(const auth::Role &self, memgraph::slk::Builder *builder) {
|
void Save(const auth::Role &self, Builder *builder) { memgraph::slk::Save(self.Serialize().dump(), builder); }
|
||||||
memgraph::slk::Save(self.Serialize().dump(), builder);
|
|
||||||
}
|
|
||||||
namespace {
|
namespace {
|
||||||
auth::Role LoadAuthRole(memgraph::slk::Reader *reader) {
|
auth::Role LoadAuthRole(memgraph::slk::Reader *reader) {
|
||||||
std::string tmp;
|
std::string tmp;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2022 Memgraph Ltd.
|
// Copyright 2024 Memgraph Ltd.
|
||||||
//
|
//
|
||||||
// Use of this software is governed by the Business Source License
|
// 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
|
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||||
@ -12,19 +12,44 @@
|
|||||||
#include "communication/websocket/auth.hpp"
|
#include "communication/websocket/auth.hpp"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include "utils/variant_helpers.hpp"
|
||||||
|
|
||||||
namespace memgraph::communication::websocket {
|
namespace memgraph::communication::websocket {
|
||||||
|
|
||||||
bool SafeAuth::Authenticate(const std::string &username, const std::string &password) const {
|
bool SafeAuth::Authenticate(const std::string &username, const std::string &password) const {
|
||||||
return auth_->Lock()->Authenticate(username, password).has_value();
|
user_or_role_ = auth_->Lock()->Authenticate(username, password);
|
||||||
|
return user_or_role_.has_value();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SafeAuth::HasUserPermission(const std::string &username, const auth::Permission permission) const {
|
bool SafeAuth::HasPermission(const auth::Permission permission) const {
|
||||||
if (const auto user = auth_->ReadLock()->GetUser(username); user) {
|
auto locked_auth = auth_->ReadLock();
|
||||||
return user->GetPermissions().Has(permission) == auth::PermissionLevel::GRANT;
|
// Update if cache invalidated
|
||||||
|
if (!locked_auth->UpToDate(auth_epoch_) && user_or_role_) {
|
||||||
|
bool success = true;
|
||||||
|
std::visit(utils::Overloaded{[&](auth::User &user) {
|
||||||
|
auto tmp = locked_auth->GetUser(user.username());
|
||||||
|
if (!tmp) success = false;
|
||||||
|
user = std::move(*tmp);
|
||||||
|
},
|
||||||
|
[&](auth::Role &role) {
|
||||||
|
auto tmp = locked_auth->GetRole(role.rolename());
|
||||||
|
if (!tmp) success = false;
|
||||||
|
role = std::move(*tmp);
|
||||||
|
}},
|
||||||
|
*user_or_role_);
|
||||||
|
// Missing user/role; delete from cache
|
||||||
|
if (!success) user_or_role_.reset();
|
||||||
}
|
}
|
||||||
|
// Check permissions
|
||||||
|
if (user_or_role_) {
|
||||||
|
return std::visit(utils::Overloaded{[&](auto &user_or_role) {
|
||||||
|
return user_or_role.GetPermissions().Has(permission) == auth::PermissionLevel::GRANT;
|
||||||
|
}},
|
||||||
|
*user_or_role_);
|
||||||
|
}
|
||||||
|
// NOTE: websocket authenticates only if there is a user, so no need to check if access controlled
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SafeAuth::HasAnyUsers() const { return auth_->ReadLock()->HasUsers(); }
|
bool SafeAuth::AccessControlled() const { return auth_->ReadLock()->AccessControlled(); }
|
||||||
} // namespace memgraph::communication::websocket
|
} // namespace memgraph::communication::websocket
|
||||||
|
@ -21,9 +21,9 @@ class AuthenticationInterface {
|
|||||||
public:
|
public:
|
||||||
virtual bool Authenticate(const std::string &username, const std::string &password) const = 0;
|
virtual bool Authenticate(const std::string &username, const std::string &password) const = 0;
|
||||||
|
|
||||||
virtual bool HasUserPermission(const std::string &username, auth::Permission permission) const = 0;
|
virtual bool HasPermission(auth::Permission permission) const = 0;
|
||||||
|
|
||||||
virtual bool HasAnyUsers() const = 0;
|
virtual bool AccessControlled() const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class SafeAuth : public AuthenticationInterface {
|
class SafeAuth : public AuthenticationInterface {
|
||||||
@ -32,11 +32,13 @@ class SafeAuth : public AuthenticationInterface {
|
|||||||
|
|
||||||
bool Authenticate(const std::string &username, const std::string &password) const override;
|
bool Authenticate(const std::string &username, const std::string &password) const override;
|
||||||
|
|
||||||
bool HasUserPermission(const std::string &username, auth::Permission permission) const override;
|
bool HasPermission(auth::Permission permission) const override;
|
||||||
|
|
||||||
bool HasAnyUsers() const override;
|
bool AccessControlled() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
auth::SynchedAuth *auth_;
|
auth::SynchedAuth *auth_;
|
||||||
|
mutable std::optional<auth::UserOrRole> user_or_role_;
|
||||||
|
mutable auth::Auth::Epoch auth_epoch_{};
|
||||||
};
|
};
|
||||||
} // namespace memgraph::communication::websocket
|
} // namespace memgraph::communication::websocket
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2023 Memgraph Ltd.
|
// Copyright 2024 Memgraph Ltd.
|
||||||
//
|
//
|
||||||
// Use of this software is governed by the Business Source License
|
// 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
|
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||||
@ -80,7 +80,7 @@ bool Session::Run() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
authenticated_ = !auth_.HasAnyUsers();
|
authenticated_ = !auth_.AccessControlled();
|
||||||
connected_.store(true, std::memory_order_relaxed);
|
connected_.store(true, std::memory_order_relaxed);
|
||||||
|
|
||||||
// run on the strand
|
// run on the strand
|
||||||
@ -162,7 +162,7 @@ utils::BasicResult<std::string> Session::Authorize(const nlohmann::json &creds)
|
|||||||
return {"Authentication failed!"};
|
return {"Authentication failed!"};
|
||||||
}
|
}
|
||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
if (!auth_.HasUserPermission(creds.at("username").get<std::string>(), auth::Permission::WEBSOCKET)) {
|
if (!auth_.HasPermission(auth::Permission::WEBSOCKET)) {
|
||||||
return {"Authorization failed!"};
|
return {"Authorization failed!"};
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -6,5 +6,6 @@ target_sources(mg-glue PRIVATE auth.cpp
|
|||||||
SessionHL.cpp
|
SessionHL.cpp
|
||||||
ServerT.cpp
|
ServerT.cpp
|
||||||
MonitoringServerT.cpp
|
MonitoringServerT.cpp
|
||||||
run_id.cpp)
|
run_id.cpp
|
||||||
|
query_user.cpp)
|
||||||
target_link_libraries(mg-glue mg-query mg-auth mg-audit mg-flags)
|
target_link_libraries(mg-glue mg-query mg-auth mg-audit mg-flags)
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
#include "auth/auth.hpp"
|
||||||
#include "gflags/gflags.h"
|
#include "gflags/gflags.h"
|
||||||
|
|
||||||
#include "audit/log.hpp"
|
#include "audit/log.hpp"
|
||||||
@ -19,17 +20,22 @@
|
|||||||
#include "glue/SessionHL.hpp"
|
#include "glue/SessionHL.hpp"
|
||||||
#include "glue/auth_checker.hpp"
|
#include "glue/auth_checker.hpp"
|
||||||
#include "glue/communication.hpp"
|
#include "glue/communication.hpp"
|
||||||
|
#include "glue/query_user.hpp"
|
||||||
#include "glue/run_id.hpp"
|
#include "glue/run_id.hpp"
|
||||||
#include "license/license.hpp"
|
#include "license/license.hpp"
|
||||||
|
#include "query/auth_checker.hpp"
|
||||||
#include "query/discard_value_stream.hpp"
|
#include "query/discard_value_stream.hpp"
|
||||||
#include "query/interpreter_context.hpp"
|
#include "query/interpreter_context.hpp"
|
||||||
|
#include "query/query_user.hpp"
|
||||||
#include "utils/event_map.hpp"
|
#include "utils/event_map.hpp"
|
||||||
#include "utils/spin_lock.hpp"
|
#include "utils/spin_lock.hpp"
|
||||||
|
#include "utils/variant_helpers.hpp"
|
||||||
|
|
||||||
namespace memgraph::metrics {
|
namespace memgraph::metrics {
|
||||||
extern const Event ActiveBoltSessions;
|
extern const Event ActiveBoltSessions;
|
||||||
} // namespace memgraph::metrics
|
} // namespace memgraph::metrics
|
||||||
|
|
||||||
|
namespace {
|
||||||
auto ToQueryExtras(const memgraph::communication::bolt::Value &extra) -> memgraph::query::QueryExtras {
|
auto ToQueryExtras(const memgraph::communication::bolt::Value &extra) -> memgraph::query::QueryExtras {
|
||||||
auto const &as_map = extra.ValueMap();
|
auto const &as_map = extra.ValueMap();
|
||||||
|
|
||||||
@ -97,20 +103,24 @@ std::vector<memgraph::communication::bolt::Value> TypedValueResultStreamBase::De
|
|||||||
}
|
}
|
||||||
return decoded_values;
|
return decoded_values;
|
||||||
}
|
}
|
||||||
|
|
||||||
TypedValueResultStreamBase::TypedValueResultStreamBase(memgraph::storage::Storage *storage) : storage_(storage) {}
|
TypedValueResultStreamBase::TypedValueResultStreamBase(memgraph::storage::Storage *storage) : storage_(storage) {}
|
||||||
|
|
||||||
namespace memgraph::glue {
|
|
||||||
|
|
||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
inline static void MultiDatabaseAuth(const std::optional<auth::User> &user, std::string_view db) {
|
void MultiDatabaseAuth(memgraph::query::QueryUserOrRole *user, std::string_view db) {
|
||||||
if (user && !AuthChecker::IsUserAuthorized(*user, {}, std::string(db))) {
|
if (user && !user->IsAuthorized({}, std::string(db), &memgraph::query::session_long_policy)) {
|
||||||
throw memgraph::communication::bolt::ClientError(
|
throw memgraph::communication::bolt::ClientError(
|
||||||
"You are not authorized on the database \"{}\"! Please contact your database administrator.", db);
|
"You are not authorized on the database \"{}\"! Please contact your database administrator.", db);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
} // namespace
|
||||||
|
namespace memgraph::glue {
|
||||||
|
|
||||||
|
#ifdef MG_ENTERPRISE
|
||||||
std::string SessionHL::GetDefaultDB() {
|
std::string SessionHL::GetDefaultDB() {
|
||||||
if (user_.has_value()) {
|
if (user_or_role_) {
|
||||||
return user_->db_access().GetDefault();
|
return user_or_role_->GetDefaultDB();
|
||||||
}
|
}
|
||||||
return std::string{memgraph::dbms::kDefaultDB};
|
return std::string{memgraph::dbms::kDefaultDB};
|
||||||
}
|
}
|
||||||
@ -132,13 +142,18 @@ bool SessionHL::Authenticate(const std::string &username, const std::string &pas
|
|||||||
interpreter_.ResetUser();
|
interpreter_.ResetUser();
|
||||||
{
|
{
|
||||||
auto locked_auth = auth_->Lock();
|
auto locked_auth = auth_->Lock();
|
||||||
if (locked_auth->HasUsers()) {
|
if (locked_auth->AccessControlled()) {
|
||||||
user_ = locked_auth->Authenticate(username, password);
|
const auto user_or_role = locked_auth->Authenticate(username, password);
|
||||||
if (user_.has_value()) {
|
if (user_or_role.has_value()) {
|
||||||
interpreter_.SetUser(user_->username());
|
user_or_role_ = AuthChecker::GenQueryUser(auth_, *user_or_role);
|
||||||
|
interpreter_.SetUser(AuthChecker::GenQueryUser(auth_, *user_or_role));
|
||||||
} else {
|
} else {
|
||||||
res = false;
|
res = false;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// No access control -> give empty user
|
||||||
|
user_or_role_ = AuthChecker::GenQueryUser(auth_, std::nullopt);
|
||||||
|
interpreter_.SetUser(AuthChecker::GenQueryUser(auth_, std::nullopt));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
@ -195,21 +210,17 @@ std::pair<std::vector<std::string>, std::optional<int>> SessionHL::Interpret(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
const std::string *username{nullptr};
|
|
||||||
if (user_) {
|
|
||||||
username = &user_->username();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||||
auto &db = interpreter_.current_db_.db_acc_;
|
auto &db = interpreter_.current_db_.db_acc_;
|
||||||
audit_log_->Record(endpoint_.address().to_string(), user_ ? *username : "", query,
|
const auto username = user_or_role_ ? (user_or_role_->username() ? *user_or_role_->username() : "") : "";
|
||||||
memgraph::storage::PropertyValue(params_pv), db ? db->get()->name() : "no known database");
|
audit_log_->Record(endpoint_.address().to_string(), username, query, memgraph::storage::PropertyValue(params_pv),
|
||||||
|
db ? db->get()->name() : "no known database");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
try {
|
try {
|
||||||
auto result = interpreter_.Prepare(query, params_pv, ToQueryExtras(extra));
|
auto result = interpreter_.Prepare(query, params_pv, ToQueryExtras(extra));
|
||||||
const std::string db_name = result.db ? *result.db : "";
|
const std::string db_name = result.db ? *result.db : "";
|
||||||
if (user_ && !AuthChecker::IsUserAuthorized(*user_, result.privileges, db_name)) {
|
if (user_or_role_ && !user_or_role_->IsAuthorized(result.privileges, db_name, &query::session_long_policy)) {
|
||||||
interpreter_.Abort();
|
interpreter_.Abort();
|
||||||
if (db_name.empty()) {
|
if (db_name.empty()) {
|
||||||
throw memgraph::communication::bolt::ClientError(
|
throw memgraph::communication::bolt::ClientError(
|
||||||
@ -311,7 +322,7 @@ void SessionHL::Configure(const std::map<std::string, memgraph::communication::b
|
|||||||
|
|
||||||
// Check if the underlying database needs to be updated
|
// Check if the underlying database needs to be updated
|
||||||
if (update) {
|
if (update) {
|
||||||
MultiDatabaseAuth(user_, db);
|
MultiDatabaseAuth(user_or_role_.get(), db);
|
||||||
interpreter_.SetCurrentDB(db, in_explicit_db_);
|
interpreter_.SetCurrentDB(db, in_explicit_db_);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -338,7 +349,7 @@ SessionHL::SessionHL(memgraph::query::InterpreterContext *interpreter_context,
|
|||||||
// Metrics update
|
// Metrics update
|
||||||
memgraph::metrics::IncrementCounter(memgraph::metrics::ActiveBoltSessions);
|
memgraph::metrics::IncrementCounter(memgraph::metrics::ActiveBoltSessions);
|
||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
interpreter_.OnChangeCB([&](std::string_view db_name) { MultiDatabaseAuth(user_, db_name); });
|
interpreter_.OnChangeCB([&](std::string_view db_name) { MultiDatabaseAuth(user_or_role_.get(), db_name); });
|
||||||
#endif
|
#endif
|
||||||
interpreter_context_->interpreters.WithLock([this](auto &interpreters) { interpreters.insert(&interpreter_); });
|
interpreter_context_->interpreters.WithLock([this](auto &interpreters) { interpreters.insert(&interpreter_); });
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include "communication/v2/server.hpp"
|
#include "communication/v2/server.hpp"
|
||||||
#include "communication/v2/session.hpp"
|
#include "communication/v2/session.hpp"
|
||||||
#include "dbms/database.hpp"
|
#include "dbms/database.hpp"
|
||||||
|
#include "glue/query_user.hpp"
|
||||||
#include "query/interpreter.hpp"
|
#include "query/interpreter.hpp"
|
||||||
|
|
||||||
namespace memgraph::glue {
|
namespace memgraph::glue {
|
||||||
@ -82,7 +83,7 @@ class SessionHL final : public memgraph::communication::bolt::Session<memgraph::
|
|||||||
|
|
||||||
memgraph::query::InterpreterContext *interpreter_context_;
|
memgraph::query::InterpreterContext *interpreter_context_;
|
||||||
memgraph::query::Interpreter interpreter_;
|
memgraph::query::Interpreter interpreter_;
|
||||||
std::optional<memgraph::auth::User> user_;
|
std::unique_ptr<query::QueryUserOrRole> user_or_role_;
|
||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
memgraph::audit::Log *audit_log_;
|
memgraph::audit::Log *audit_log_;
|
||||||
bool in_explicit_db_{false}; //!< If true, the user has defined the database to use via metadata
|
bool in_explicit_db_{false}; //!< If true, the user has defined the database to use via metadata
|
||||||
|
@ -14,53 +14,74 @@
|
|||||||
#include "auth/auth.hpp"
|
#include "auth/auth.hpp"
|
||||||
#include "auth/models.hpp"
|
#include "auth/models.hpp"
|
||||||
#include "glue/auth.hpp"
|
#include "glue/auth.hpp"
|
||||||
|
#include "glue/query_user.hpp"
|
||||||
#include "license/license.hpp"
|
#include "license/license.hpp"
|
||||||
|
#include "query/auth_checker.hpp"
|
||||||
#include "query/constants.hpp"
|
#include "query/constants.hpp"
|
||||||
#include "query/frontend/ast/ast.hpp"
|
#include "query/frontend/ast/ast.hpp"
|
||||||
|
#include "query/query_user.hpp"
|
||||||
|
#include "utils/logging.hpp"
|
||||||
#include "utils/synchronized.hpp"
|
#include "utils/synchronized.hpp"
|
||||||
|
#include "utils/variant_helpers.hpp"
|
||||||
|
|
||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
namespace {
|
namespace {
|
||||||
bool IsUserAuthorizedLabels(const memgraph::auth::User &user, const memgraph::query::DbAccessor *dba,
|
bool IsAuthorizedLabels(const memgraph::auth::UserOrRole &user_or_role, const memgraph::query::DbAccessor *dba,
|
||||||
const std::vector<memgraph::storage::LabelId> &labels,
|
const std::vector<memgraph::storage::LabelId> &labels,
|
||||||
const memgraph::query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) {
|
const memgraph::query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) {
|
||||||
if (!memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
if (!memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return std::all_of(labels.begin(), labels.end(), [dba, &user, fine_grained_privilege](const auto &label) {
|
return std::all_of(labels.begin(), labels.end(), [dba, &user_or_role, fine_grained_privilege](const auto &label) {
|
||||||
return user.GetFineGrainedAccessLabelPermissions().Has(
|
return std::visit(memgraph::utils::Overloaded{[&](auto &user_or_role) {
|
||||||
dba->LabelToName(label), memgraph::glue::FineGrainedPrivilegeToFineGrainedPermission(
|
return user_or_role.GetFineGrainedAccessLabelPermissions().Has(
|
||||||
fine_grained_privilege)) == memgraph::auth::PermissionLevel::GRANT;
|
dba->LabelToName(label), memgraph::glue::FineGrainedPrivilegeToFineGrainedPermission(
|
||||||
|
fine_grained_privilege)) ==
|
||||||
|
memgraph::auth::PermissionLevel::GRANT;
|
||||||
|
}},
|
||||||
|
user_or_role);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsUserAuthorizedGloballyLabels(const memgraph::auth::User &user,
|
bool IsAuthorizedGloballyLabels(const memgraph::auth::UserOrRole &user_or_role,
|
||||||
const memgraph::auth::FineGrainedPermission fine_grained_permission) {
|
const memgraph::auth::FineGrainedPermission fine_grained_permission) {
|
||||||
if (!memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
if (!memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return user.GetFineGrainedAccessLabelPermissions().Has(memgraph::query::kAsterisk, fine_grained_permission) ==
|
return std::visit(memgraph::utils::Overloaded{[&](auto &user_or_role) {
|
||||||
memgraph::auth::PermissionLevel::GRANT;
|
return user_or_role.GetFineGrainedAccessLabelPermissions().Has(memgraph::query::kAsterisk,
|
||||||
|
fine_grained_permission) ==
|
||||||
|
memgraph::auth::PermissionLevel::GRANT;
|
||||||
|
}},
|
||||||
|
user_or_role);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsUserAuthorizedGloballyEdges(const memgraph::auth::User &user,
|
bool IsAuthorizedGloballyEdges(const memgraph::auth::UserOrRole &user_or_role,
|
||||||
const memgraph::auth::FineGrainedPermission fine_grained_permission) {
|
const memgraph::auth::FineGrainedPermission fine_grained_permission) {
|
||||||
if (!memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
if (!memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return user.GetFineGrainedAccessEdgeTypePermissions().Has(memgraph::query::kAsterisk, fine_grained_permission) ==
|
return std::visit(memgraph::utils::Overloaded{[&](auto &user_or_role) {
|
||||||
memgraph::auth::PermissionLevel::GRANT;
|
return user_or_role.GetFineGrainedAccessEdgeTypePermissions().Has(memgraph::query::kAsterisk,
|
||||||
|
fine_grained_permission) ==
|
||||||
|
memgraph::auth::PermissionLevel::GRANT;
|
||||||
|
}},
|
||||||
|
user_or_role);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsUserAuthorizedEdgeType(const memgraph::auth::User &user, const memgraph::query::DbAccessor *dba,
|
bool IsAuthorizedEdgeType(const memgraph::auth::UserOrRole &user_or_role, const memgraph::query::DbAccessor *dba,
|
||||||
const memgraph::storage::EdgeTypeId &edgeType,
|
const memgraph::storage::EdgeTypeId &edgeType,
|
||||||
const memgraph::query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) {
|
const memgraph::query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) {
|
||||||
if (!memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
if (!memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return user.GetFineGrainedAccessEdgeTypePermissions().Has(
|
return std::visit(memgraph::utils::Overloaded{[&](auto &user_or_role) {
|
||||||
dba->EdgeTypeToName(edgeType), memgraph::glue::FineGrainedPrivilegeToFineGrainedPermission(
|
return user_or_role.GetFineGrainedAccessEdgeTypePermissions().Has(
|
||||||
fine_grained_privilege)) == memgraph::auth::PermissionLevel::GRANT;
|
dba->EdgeTypeToName(edgeType),
|
||||||
|
memgraph::glue::FineGrainedPrivilegeToFineGrainedPermission(fine_grained_privilege)) ==
|
||||||
|
memgraph::auth::PermissionLevel::GRANT;
|
||||||
|
}},
|
||||||
|
user_or_role);
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
#endif
|
#endif
|
||||||
@ -68,47 +89,54 @@ namespace memgraph::glue {
|
|||||||
|
|
||||||
AuthChecker::AuthChecker(memgraph::auth::SynchedAuth *auth) : auth_(auth) {}
|
AuthChecker::AuthChecker(memgraph::auth::SynchedAuth *auth) : auth_(auth) {}
|
||||||
|
|
||||||
bool AuthChecker::IsUserAuthorized(const std::optional<std::string> &username,
|
std::shared_ptr<query::QueryUserOrRole> AuthChecker::GenQueryUser(const std::optional<std::string> &username,
|
||||||
const std::vector<memgraph::query::AuthQuery::Privilege> &privileges,
|
const std::optional<std::string> &rolename) const {
|
||||||
const std::string &db_name) const {
|
const auto user_or_role = auth_->ReadLock()->GetUserOrRole(username, rolename);
|
||||||
std::optional<memgraph::auth::User> maybe_user;
|
if (user_or_role) {
|
||||||
{
|
return std::make_shared<QueryUserOrRole>(auth_, *user_or_role);
|
||||||
auto locked_auth = auth_->ReadLock();
|
|
||||||
if (!locked_auth->HasUsers()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (username.has_value()) {
|
|
||||||
maybe_user = locked_auth->GetUser(*username);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
// No user or role
|
||||||
|
return std::make_shared<QueryUserOrRole>(auth_);
|
||||||
|
}
|
||||||
|
|
||||||
return maybe_user.has_value() && IsUserAuthorized(*maybe_user, privileges, db_name);
|
std::unique_ptr<query::QueryUserOrRole> AuthChecker::GenQueryUser(auth::SynchedAuth *auth,
|
||||||
|
const std::optional<auth::UserOrRole> &user_or_role) {
|
||||||
|
if (user_or_role) {
|
||||||
|
return std::visit(
|
||||||
|
utils::Overloaded{[&](auto &user_or_role) { return std::make_unique<QueryUserOrRole>(auth, user_or_role); }},
|
||||||
|
*user_or_role);
|
||||||
|
}
|
||||||
|
// No user or role
|
||||||
|
return std::make_unique<QueryUserOrRole>(auth);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
std::unique_ptr<memgraph::query::FineGrainedAuthChecker> AuthChecker::GetFineGrainedAuthChecker(
|
std::unique_ptr<memgraph::query::FineGrainedAuthChecker> AuthChecker::GetFineGrainedAuthChecker(
|
||||||
const std::string &username, const memgraph::query::DbAccessor *dba) const {
|
std::shared_ptr<query::QueryUserOrRole> user_or_role, const memgraph::query::DbAccessor *dba) const {
|
||||||
if (!memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
if (!memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
try {
|
if (!user_or_role || !*user_or_role) {
|
||||||
auto user = user_.Lock();
|
throw query::QueryRuntimeException("No user specified for fine grained authorization!");
|
||||||
if (username != user->username()) {
|
|
||||||
auto maybe_user = auth_->ReadLock()->GetUser(username);
|
|
||||||
if (!maybe_user) {
|
|
||||||
throw memgraph::query::QueryRuntimeException("User '{}' doesn't exist .", username);
|
|
||||||
}
|
|
||||||
*user = std::move(*maybe_user);
|
|
||||||
}
|
|
||||||
return std::make_unique<memgraph::glue::FineGrainedAuthChecker>(*user, dba);
|
|
||||||
|
|
||||||
} catch (const memgraph::auth::AuthException &e) {
|
|
||||||
throw memgraph::query::QueryRuntimeException(e.what());
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void AuthChecker::ClearCache() const {
|
// Convert from query user to auth user or role
|
||||||
user_.WithLock([](auto &user) mutable { user = {}; });
|
try {
|
||||||
|
auto glue_user = dynamic_cast<glue::QueryUserOrRole &>(*user_or_role);
|
||||||
|
if (glue_user.user_) {
|
||||||
|
return std::make_unique<glue::FineGrainedAuthChecker>(std::move(*glue_user.user_), dba);
|
||||||
|
}
|
||||||
|
if (glue_user.role_) {
|
||||||
|
return std::make_unique<glue::FineGrainedAuthChecker>(
|
||||||
|
auth::RoleWUsername{*glue_user.username(), std::move(*glue_user.role_)}, dba);
|
||||||
|
}
|
||||||
|
DMG_ASSERT(false, "Glue user has neither user not role");
|
||||||
|
} catch (std::bad_cast &e) {
|
||||||
|
DMG_ASSERT(false, "Using a non-glue user in glue...");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should never get here
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -116,7 +144,7 @@ bool AuthChecker::IsUserAuthorized(const memgraph::auth::User &user,
|
|||||||
const std::vector<memgraph::query::AuthQuery::Privilege> &privileges,
|
const std::vector<memgraph::query::AuthQuery::Privilege> &privileges,
|
||||||
const std::string &db_name) { // NOLINT
|
const std::string &db_name) { // NOLINT
|
||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
if (!db_name.empty() && !user.db_access().Contains(db_name)) {
|
if (!db_name.empty() && !user.HasAccess(db_name)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -127,9 +155,34 @@ bool AuthChecker::IsUserAuthorized(const memgraph::auth::User &user,
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AuthChecker::IsRoleAuthorized(const memgraph::auth::Role &role,
|
||||||
|
const std::vector<memgraph::query::AuthQuery::Privilege> &privileges,
|
||||||
|
const std::string &db_name) { // NOLINT
|
||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
FineGrainedAuthChecker::FineGrainedAuthChecker(auth::User user, const memgraph::query::DbAccessor *dba)
|
if (!db_name.empty() && !role.HasAccess(db_name)) {
|
||||||
: user_{std::move(user)}, dba_(dba){};
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
const auto role_permissions = role.permissions();
|
||||||
|
return std::all_of(privileges.begin(), privileges.end(), [&role_permissions](const auto privilege) {
|
||||||
|
return role_permissions.Has(memgraph::glue::PrivilegeToPermission(privilege)) ==
|
||||||
|
memgraph::auth::PermissionLevel::GRANT;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AuthChecker::IsUserOrRoleAuthorized(const memgraph::auth::UserOrRole &user_or_role,
|
||||||
|
const std::vector<memgraph::query::AuthQuery::Privilege> &privileges,
|
||||||
|
const std::string &db_name) {
|
||||||
|
return std::visit(
|
||||||
|
utils::Overloaded{
|
||||||
|
[&](const auth::User &user) -> bool { return AuthChecker::IsUserAuthorized(user, privileges, db_name); },
|
||||||
|
[&](const auth::Role &role) -> bool { return AuthChecker::IsRoleAuthorized(role, privileges, db_name); }},
|
||||||
|
user_or_role);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef MG_ENTERPRISE
|
||||||
|
FineGrainedAuthChecker::FineGrainedAuthChecker(auth::UserOrRole user_or_role, const memgraph::query::DbAccessor *dba)
|
||||||
|
: user_or_role_{std::move(user_or_role)}, dba_(dba){};
|
||||||
|
|
||||||
bool FineGrainedAuthChecker::Has(const memgraph::query::VertexAccessor &vertex, const memgraph::storage::View view,
|
bool FineGrainedAuthChecker::Has(const memgraph::query::VertexAccessor &vertex, const memgraph::storage::View view,
|
||||||
const memgraph::query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) const {
|
const memgraph::query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) const {
|
||||||
@ -147,22 +200,22 @@ bool FineGrainedAuthChecker::Has(const memgraph::query::VertexAccessor &vertex,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return IsUserAuthorizedLabels(user_, dba_, *maybe_labels, fine_grained_privilege);
|
return IsAuthorizedLabels(user_or_role_, dba_, *maybe_labels, fine_grained_privilege);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FineGrainedAuthChecker::Has(const memgraph::query::EdgeAccessor &edge,
|
bool FineGrainedAuthChecker::Has(const memgraph::query::EdgeAccessor &edge,
|
||||||
const memgraph::query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) const {
|
const memgraph::query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) const {
|
||||||
return IsUserAuthorizedEdgeType(user_, dba_, edge.EdgeType(), fine_grained_privilege);
|
return IsAuthorizedEdgeType(user_or_role_, dba_, edge.EdgeType(), fine_grained_privilege);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FineGrainedAuthChecker::Has(const std::vector<memgraph::storage::LabelId> &labels,
|
bool FineGrainedAuthChecker::Has(const std::vector<memgraph::storage::LabelId> &labels,
|
||||||
const memgraph::query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) const {
|
const memgraph::query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) const {
|
||||||
return IsUserAuthorizedLabels(user_, dba_, labels, fine_grained_privilege);
|
return IsAuthorizedLabels(user_or_role_, dba_, labels, fine_grained_privilege);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FineGrainedAuthChecker::Has(const memgraph::storage::EdgeTypeId &edge_type,
|
bool FineGrainedAuthChecker::Has(const memgraph::storage::EdgeTypeId &edge_type,
|
||||||
const memgraph::query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) const {
|
const memgraph::query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) const {
|
||||||
return IsUserAuthorizedEdgeType(user_, dba_, edge_type, fine_grained_privilege);
|
return IsAuthorizedEdgeType(user_or_role_, dba_, edge_type, fine_grained_privilege);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FineGrainedAuthChecker::HasGlobalPrivilegeOnVertices(
|
bool FineGrainedAuthChecker::HasGlobalPrivilegeOnVertices(
|
||||||
@ -170,7 +223,7 @@ bool FineGrainedAuthChecker::HasGlobalPrivilegeOnVertices(
|
|||||||
if (!memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
if (!memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return IsUserAuthorizedGloballyLabels(user_, FineGrainedPrivilegeToFineGrainedPermission(fine_grained_privilege));
|
return IsAuthorizedGloballyLabels(user_or_role_, FineGrainedPrivilegeToFineGrainedPermission(fine_grained_privilege));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FineGrainedAuthChecker::HasGlobalPrivilegeOnEdges(
|
bool FineGrainedAuthChecker::HasGlobalPrivilegeOnEdges(
|
||||||
@ -178,7 +231,7 @@ bool FineGrainedAuthChecker::HasGlobalPrivilegeOnEdges(
|
|||||||
if (!memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
if (!memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return IsUserAuthorizedGloballyEdges(user_, FineGrainedPrivilegeToFineGrainedPermission(fine_grained_privilege));
|
return IsAuthorizedGloballyEdges(user_or_role_, FineGrainedPrivilegeToFineGrainedPermission(fine_grained_privilege));
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
} // namespace memgraph::glue
|
} // namespace memgraph::glue
|
||||||
|
@ -22,53 +22,59 @@ namespace memgraph::glue {
|
|||||||
|
|
||||||
class AuthChecker : public query::AuthChecker {
|
class AuthChecker : public query::AuthChecker {
|
||||||
public:
|
public:
|
||||||
explicit AuthChecker(memgraph::auth::SynchedAuth *auth);
|
explicit AuthChecker(auth::SynchedAuth *auth);
|
||||||
|
|
||||||
bool IsUserAuthorized(const std::optional<std::string> &username,
|
std::shared_ptr<query::QueryUserOrRole> GenQueryUser(const std::optional<std::string> &username,
|
||||||
const std::vector<query::AuthQuery::Privilege> &privileges,
|
const std::optional<std::string> &rolename) const override;
|
||||||
const std::string &db_name) const override;
|
|
||||||
|
static std::unique_ptr<query::QueryUserOrRole> GenQueryUser(auth::SynchedAuth *auth,
|
||||||
|
const std::optional<auth::UserOrRole> &user_or_role);
|
||||||
|
|
||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
std::unique_ptr<memgraph::query::FineGrainedAuthChecker> GetFineGrainedAuthChecker(
|
std::unique_ptr<query::FineGrainedAuthChecker> GetFineGrainedAuthChecker(std::shared_ptr<query::QueryUserOrRole> user,
|
||||||
const std::string &username, const memgraph::query::DbAccessor *dba) const override;
|
const query::DbAccessor *dba) const override;
|
||||||
|
|
||||||
void ClearCache() const override;
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
[[nodiscard]] static bool IsUserAuthorized(const memgraph::auth::User &user,
|
|
||||||
const std::vector<memgraph::query::AuthQuery::Privilege> &privileges,
|
[[nodiscard]] static bool IsUserAuthorized(const auth::User &user,
|
||||||
|
const std::vector<query::AuthQuery::Privilege> &privileges,
|
||||||
const std::string &db_name = "");
|
const std::string &db_name = "");
|
||||||
|
|
||||||
|
[[nodiscard]] static bool IsRoleAuthorized(const auth::Role &role,
|
||||||
|
const std::vector<query::AuthQuery::Privilege> &privileges,
|
||||||
|
const std::string &db_name = "");
|
||||||
|
|
||||||
|
[[nodiscard]] static bool IsUserOrRoleAuthorized(const auth::UserOrRole &user_or_role,
|
||||||
|
const std::vector<query::AuthQuery::Privilege> &privileges,
|
||||||
|
const std::string &db_name = "");
|
||||||
|
|
||||||
private:
|
private:
|
||||||
memgraph::auth::SynchedAuth *auth_;
|
auth::SynchedAuth *auth_;
|
||||||
mutable memgraph::utils::Synchronized<auth::User, memgraph::utils::SpinLock> user_; // cached user
|
mutable utils::Synchronized<auth::UserOrRole, utils::SpinLock> user_or_role_; // cached user
|
||||||
};
|
};
|
||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
class FineGrainedAuthChecker : public query::FineGrainedAuthChecker {
|
class FineGrainedAuthChecker : public query::FineGrainedAuthChecker {
|
||||||
public:
|
public:
|
||||||
explicit FineGrainedAuthChecker(auth::User user, const memgraph::query::DbAccessor *dba);
|
explicit FineGrainedAuthChecker(auth::UserOrRole user, const query::DbAccessor *dba);
|
||||||
|
|
||||||
bool Has(const query::VertexAccessor &vertex, memgraph::storage::View view,
|
bool Has(const query::VertexAccessor &vertex, storage::View view,
|
||||||
query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) const override;
|
query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) const override;
|
||||||
|
|
||||||
bool Has(const query::EdgeAccessor &edge,
|
bool Has(const query::EdgeAccessor &edge,
|
||||||
query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) const override;
|
query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) const override;
|
||||||
|
|
||||||
bool Has(const std::vector<memgraph::storage::LabelId> &labels,
|
bool Has(const std::vector<storage::LabelId> &labels,
|
||||||
query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) const override;
|
query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) const override;
|
||||||
|
|
||||||
bool Has(const memgraph::storage::EdgeTypeId &edge_type,
|
bool Has(const storage::EdgeTypeId &edge_type,
|
||||||
query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) const override;
|
query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) const override;
|
||||||
|
|
||||||
bool HasGlobalPrivilegeOnVertices(
|
bool HasGlobalPrivilegeOnVertices(query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) const override;
|
||||||
memgraph::query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) const override;
|
|
||||||
|
|
||||||
bool HasGlobalPrivilegeOnEdges(
|
bool HasGlobalPrivilegeOnEdges(query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) const override;
|
||||||
memgraph::query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) const override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
auth::User user_;
|
auth::UserOrRole user_or_role_;
|
||||||
const memgraph::query::DbAccessor *dba_;
|
const query::DbAccessor *dba_;
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
} // namespace memgraph::glue
|
} // namespace memgraph::glue
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
|
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
|
|
||||||
|
#include "auth/auth.hpp"
|
||||||
#include "auth/models.hpp"
|
#include "auth/models.hpp"
|
||||||
#include "dbms/constants.hpp"
|
#include "dbms/constants.hpp"
|
||||||
#include "glue/auth.hpp"
|
#include "glue/auth.hpp"
|
||||||
@ -123,6 +124,29 @@ std::vector<std::vector<memgraph::query::TypedValue>> ShowRolePrivileges(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
|
std::vector<std::vector<memgraph::query::TypedValue>> ShowDatabasePrivileges(
|
||||||
|
const std::optional<memgraph::auth::Role> &role) {
|
||||||
|
if (!memgraph::license::global_license_checker.IsEnterpriseValidFast() || !role) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto &db = role->db_access();
|
||||||
|
const auto &allows = db.GetAllowAll();
|
||||||
|
const auto &grants = db.GetGrants();
|
||||||
|
const auto &denies = db.GetDenies();
|
||||||
|
|
||||||
|
std::vector<memgraph::query::TypedValue> res; // First element is a list of granted databases, second of revoked ones
|
||||||
|
if (allows) {
|
||||||
|
res.emplace_back("*");
|
||||||
|
} else {
|
||||||
|
std::vector<memgraph::query::TypedValue> grants_vec(grants.cbegin(), grants.cend());
|
||||||
|
res.emplace_back(std::move(grants_vec));
|
||||||
|
}
|
||||||
|
std::vector<memgraph::query::TypedValue> denies_vec(denies.cbegin(), denies.cend());
|
||||||
|
res.emplace_back(std::move(denies_vec));
|
||||||
|
return {res};
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<std::vector<memgraph::query::TypedValue>> ShowDatabasePrivileges(
|
std::vector<std::vector<memgraph::query::TypedValue>> ShowDatabasePrivileges(
|
||||||
const std::optional<memgraph::auth::User> &user) {
|
const std::optional<memgraph::auth::User> &user) {
|
||||||
if (!memgraph::license::global_license_checker.IsEnterpriseValidFast() || !user) {
|
if (!memgraph::license::global_license_checker.IsEnterpriseValidFast() || !user) {
|
||||||
@ -130,9 +154,15 @@ std::vector<std::vector<memgraph::query::TypedValue>> ShowDatabasePrivileges(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const auto &db = user->db_access();
|
const auto &db = user->db_access();
|
||||||
const auto &allows = db.GetAllowAll();
|
auto allows = db.GetAllowAll();
|
||||||
const auto &grants = db.GetGrants();
|
auto grants = db.GetGrants();
|
||||||
const auto &denies = db.GetDenies();
|
auto denies = db.GetDenies();
|
||||||
|
if (const auto *role = user->role()) {
|
||||||
|
const auto &role_db = role->db_access();
|
||||||
|
allows |= role_db.GetAllowAll();
|
||||||
|
grants.insert(role_db.GetGrants().begin(), role_db.GetGrants().end());
|
||||||
|
denies.insert(role_db.GetDenies().begin(), role_db.GetDenies().end());
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<memgraph::query::TypedValue> res; // First element is a list of granted databases, second of revoked ones
|
std::vector<memgraph::query::TypedValue> res; // First element is a list of granted databases, second of revoked ones
|
||||||
if (allows) {
|
if (allows) {
|
||||||
@ -287,7 +317,7 @@ bool AuthQueryHandler::CreateUser(const std::string &username, const std::option
|
|||||||
,
|
,
|
||||||
system_tx);
|
system_tx);
|
||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
GrantDatabaseToUser(auth::kAllDatabases, username, system_tx);
|
GrantDatabase(auth::kAllDatabases, username, system_tx);
|
||||||
SetMainDatabase(dbms::kDefaultDB, username, system_tx);
|
SetMainDatabase(dbms::kDefaultDB, username, system_tx);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@ -334,51 +364,97 @@ bool AuthQueryHandler::CreateRole(const std::string &rolename, system::Transacti
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
bool AuthQueryHandler::RevokeDatabaseFromUser(const std::string &db_name, const std::string &username,
|
void AuthQueryHandler::GrantDatabase(const std::string &db_name, const std::string &user_or_role,
|
||||||
system::Transaction *system_tx) {
|
system::Transaction *system_tx) {
|
||||||
try {
|
try {
|
||||||
auto locked_auth = auth_->Lock();
|
auto locked_auth = auth_->Lock();
|
||||||
auto user = locked_auth->GetUser(username);
|
const auto res = locked_auth->GrantDatabase(db_name, user_or_role, system_tx);
|
||||||
if (!user) return false;
|
switch (res) {
|
||||||
return locked_auth->RevokeDatabaseFromUser(db_name, username, system_tx);
|
using enum auth::Auth::Result;
|
||||||
|
case SUCCESS:
|
||||||
|
return;
|
||||||
|
case NO_USER_ROLE:
|
||||||
|
throw query::QueryRuntimeException("No user nor role '{}' found.", user_or_role);
|
||||||
|
case NO_ROLE:
|
||||||
|
throw query::QueryRuntimeException("Using auth module, no role '{}' found.", user_or_role);
|
||||||
|
break;
|
||||||
|
}
|
||||||
} catch (const memgraph::auth::AuthException &e) {
|
} catch (const memgraph::auth::AuthException &e) {
|
||||||
throw memgraph::query::QueryRuntimeException(e.what());
|
throw memgraph::query::QueryRuntimeException(e.what());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AuthQueryHandler::GrantDatabaseToUser(const std::string &db_name, const std::string &username,
|
void AuthQueryHandler::DenyDatabase(const std::string &db_name, const std::string &user_or_role,
|
||||||
system::Transaction *system_tx) {
|
system::Transaction *system_tx) {
|
||||||
try {
|
try {
|
||||||
auto locked_auth = auth_->Lock();
|
auto locked_auth = auth_->Lock();
|
||||||
auto user = locked_auth->GetUser(username);
|
const auto res = locked_auth->DenyDatabase(db_name, user_or_role, system_tx);
|
||||||
if (!user) return false;
|
switch (res) {
|
||||||
return locked_auth->GrantDatabaseToUser(db_name, username, system_tx);
|
using enum auth::Auth::Result;
|
||||||
|
case SUCCESS:
|
||||||
|
return;
|
||||||
|
case NO_USER_ROLE:
|
||||||
|
throw query::QueryRuntimeException("No user nor role '{}' found.", user_or_role);
|
||||||
|
case NO_ROLE:
|
||||||
|
throw query::QueryRuntimeException("Using auth module, no role '{}' found.", user_or_role);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (const memgraph::auth::AuthException &e) {
|
||||||
|
throw memgraph::query::QueryRuntimeException(e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AuthQueryHandler::RevokeDatabase(const std::string &db_name, const std::string &user_or_role,
|
||||||
|
system::Transaction *system_tx) {
|
||||||
|
try {
|
||||||
|
auto locked_auth = auth_->Lock();
|
||||||
|
const auto res = locked_auth->RevokeDatabase(db_name, user_or_role, system_tx);
|
||||||
|
switch (res) {
|
||||||
|
using enum auth::Auth::Result;
|
||||||
|
case SUCCESS:
|
||||||
|
return;
|
||||||
|
case NO_USER_ROLE:
|
||||||
|
throw query::QueryRuntimeException("No user nor role '{}' found.", user_or_role);
|
||||||
|
case NO_ROLE:
|
||||||
|
throw query::QueryRuntimeException("Using auth module, no role '{}' found.", user_or_role);
|
||||||
|
break;
|
||||||
|
}
|
||||||
} catch (const memgraph::auth::AuthException &e) {
|
} catch (const memgraph::auth::AuthException &e) {
|
||||||
throw memgraph::query::QueryRuntimeException(e.what());
|
throw memgraph::query::QueryRuntimeException(e.what());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::vector<memgraph::query::TypedValue>> AuthQueryHandler::GetDatabasePrivileges(
|
std::vector<std::vector<memgraph::query::TypedValue>> AuthQueryHandler::GetDatabasePrivileges(
|
||||||
const std::string &username) {
|
const std::string &user_or_role) {
|
||||||
try {
|
try {
|
||||||
auto locked_auth = auth_->ReadLock();
|
auto locked_auth = auth_->ReadLock();
|
||||||
auto user = locked_auth->GetUser(username);
|
if (auto user = locked_auth->GetUser(user_or_role)) {
|
||||||
if (!user) {
|
return ShowDatabasePrivileges(user);
|
||||||
throw memgraph::query::QueryRuntimeException("User '{}' doesn't exist.", username);
|
|
||||||
}
|
}
|
||||||
return ShowDatabasePrivileges(user);
|
if (auto role = locked_auth->GetRole(user_or_role)) {
|
||||||
|
return ShowDatabasePrivileges(role);
|
||||||
|
}
|
||||||
|
throw memgraph::query::QueryRuntimeException("Neither user nor role '{}' exist.", user_or_role);
|
||||||
} catch (const memgraph::auth::AuthException &e) {
|
} catch (const memgraph::auth::AuthException &e) {
|
||||||
throw memgraph::query::QueryRuntimeException(e.what());
|
throw memgraph::query::QueryRuntimeException(e.what());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AuthQueryHandler::SetMainDatabase(std::string_view db_name, const std::string &username,
|
void AuthQueryHandler::SetMainDatabase(std::string_view db_name, const std::string &user_or_role,
|
||||||
system::Transaction *system_tx) {
|
system::Transaction *system_tx) {
|
||||||
try {
|
try {
|
||||||
auto locked_auth = auth_->Lock();
|
auto locked_auth = auth_->Lock();
|
||||||
auto user = locked_auth->GetUser(username);
|
const auto res = locked_auth->SetMainDatabase(db_name, user_or_role, system_tx);
|
||||||
if (!user) return false;
|
switch (res) {
|
||||||
return locked_auth->SetMainDatabase(db_name, username, system_tx);
|
using enum auth::Auth::Result;
|
||||||
|
case SUCCESS:
|
||||||
|
return;
|
||||||
|
case NO_USER_ROLE:
|
||||||
|
throw query::QueryRuntimeException("No user nor role '{}' found.", user_or_role);
|
||||||
|
case NO_ROLE:
|
||||||
|
throw query::QueryRuntimeException("Using auth module, no role '{}' found.", user_or_role);
|
||||||
|
break;
|
||||||
|
}
|
||||||
} catch (const memgraph::auth::AuthException &e) {
|
} catch (const memgraph::auth::AuthException &e) {
|
||||||
throw memgraph::query::QueryRuntimeException(e.what());
|
throw memgraph::query::QueryRuntimeException(e.what());
|
||||||
}
|
}
|
||||||
|
@ -37,15 +37,19 @@ class AuthQueryHandler final : public memgraph::query::AuthQueryHandler {
|
|||||||
system::Transaction *system_tx) override;
|
system::Transaction *system_tx) override;
|
||||||
|
|
||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
bool RevokeDatabaseFromUser(const std::string &db_name, const std::string &username,
|
void GrantDatabase(const std::string &db_name, const std::string &user_or_role,
|
||||||
system::Transaction *system_tx) override;
|
system::Transaction *system_tx) override;
|
||||||
|
|
||||||
bool GrantDatabaseToUser(const std::string &db_name, const std::string &username,
|
void DenyDatabase(const std::string &db_name, const std::string &user_or_role,
|
||||||
system::Transaction *system_tx) override;
|
system::Transaction *system_tx) override;
|
||||||
|
|
||||||
std::vector<std::vector<memgraph::query::TypedValue>> GetDatabasePrivileges(const std::string &username) override;
|
void RevokeDatabase(const std::string &db_name, const std::string &user_or_role,
|
||||||
|
system::Transaction *system_tx) override;
|
||||||
|
|
||||||
bool SetMainDatabase(std::string_view db_name, const std::string &username, system::Transaction *system_tx) override;
|
std::vector<std::vector<memgraph::query::TypedValue>> GetDatabasePrivileges(const std::string &user_or_role) override;
|
||||||
|
|
||||||
|
void SetMainDatabase(std::string_view db_name, const std::string &user_or_role,
|
||||||
|
system::Transaction *system_tx) override;
|
||||||
|
|
||||||
void DeleteDatabase(std::string_view db_name, system::Transaction *system_tx) override;
|
void DeleteDatabase(std::string_view db_name, system::Transaction *system_tx) override;
|
||||||
#endif
|
#endif
|
||||||
|
41
src/glue/query_user.cpp
Normal file
41
src/glue/query_user.cpp
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
// 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 "glue/query_user.hpp"
|
||||||
|
|
||||||
|
#include "glue/auth_checker.hpp"
|
||||||
|
|
||||||
|
namespace memgraph::glue {
|
||||||
|
|
||||||
|
bool QueryUserOrRole::IsAuthorized(const std::vector<query::AuthQuery::Privilege> &privileges,
|
||||||
|
const std::string &db_name, query::UserPolicy *policy) const {
|
||||||
|
auto locked_auth = auth_->Lock();
|
||||||
|
// Check policy and update if behind (and policy permits it)
|
||||||
|
if (policy->DoUpdate() && !locked_auth->UpToDate(auth_epoch_)) {
|
||||||
|
if (user_) user_ = locked_auth->GetUser(user_->username());
|
||||||
|
if (role_) role_ = locked_auth->GetRole(role_->rolename());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user_) return AuthChecker::IsUserAuthorized(*user_, privileges, db_name);
|
||||||
|
if (role_) return AuthChecker::IsRoleAuthorized(*role_, privileges, db_name);
|
||||||
|
|
||||||
|
return !policy->DoUpdate() || !locked_auth->AccessControlled();
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef MG_ENTERPRISE
|
||||||
|
std::string QueryUserOrRole::GetDefaultDB() const {
|
||||||
|
if (user_) return user_->db_access().GetMain();
|
||||||
|
if (role_) return role_->db_access().GetMain();
|
||||||
|
return std::string{dbms::kDefaultDB};
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // namespace memgraph::glue
|
57
src/glue/query_user.hpp
Normal file
57
src/glue/query_user.hpp
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
#include "auth/auth.hpp"
|
||||||
|
#include "query/query_user.hpp"
|
||||||
|
#include "utils/variant_helpers.hpp"
|
||||||
|
|
||||||
|
namespace memgraph::glue {
|
||||||
|
|
||||||
|
struct QueryUserOrRole : public query::QueryUserOrRole {
|
||||||
|
bool IsAuthorized(const std::vector<query::AuthQuery::Privilege> &privileges, const std::string &db_name,
|
||||||
|
query::UserPolicy *policy) const override;
|
||||||
|
|
||||||
|
#ifdef MG_ENTERPRISE
|
||||||
|
std::string GetDefaultDB() const override;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
explicit QueryUserOrRole(auth::SynchedAuth *auth) : query::QueryUserOrRole{std::nullopt, std::nullopt}, auth_{auth} {}
|
||||||
|
|
||||||
|
QueryUserOrRole(auth::SynchedAuth *auth, auth::UserOrRole user_or_role)
|
||||||
|
: query::QueryUserOrRole{std::visit(
|
||||||
|
utils::Overloaded{[](const auto &user_or_role) { return user_or_role.username(); }},
|
||||||
|
user_or_role),
|
||||||
|
std::visit(utils::Overloaded{[&](const auth::User &) -> std::optional<std::string> {
|
||||||
|
return std::nullopt;
|
||||||
|
},
|
||||||
|
[&](const auth::Role &role) -> std::optional<std::string> {
|
||||||
|
return role.rolename();
|
||||||
|
}},
|
||||||
|
user_or_role)},
|
||||||
|
auth_{auth} {
|
||||||
|
std::visit(utils::Overloaded{[&](auth::User &&user) { user_.emplace(std::move(user)); },
|
||||||
|
[&](auth::Role &&role) { role_.emplace(std::move(role)); }},
|
||||||
|
std::move(user_or_role));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class AuthChecker;
|
||||||
|
auth::SynchedAuth *auth_;
|
||||||
|
mutable std::optional<auth::User> user_{};
|
||||||
|
mutable std::optional<auth::Role> role_{};
|
||||||
|
mutable auth::Auth::Epoch auth_epoch_{auth::Auth::kStartEpoch};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace memgraph::glue
|
@ -27,6 +27,7 @@
|
|||||||
#include "helpers.hpp"
|
#include "helpers.hpp"
|
||||||
#include "license/license_sender.hpp"
|
#include "license/license_sender.hpp"
|
||||||
#include "memory/global_memory_control.hpp"
|
#include "memory/global_memory_control.hpp"
|
||||||
|
#include "query/auth_checker.hpp"
|
||||||
#include "query/auth_query_handler.hpp"
|
#include "query/auth_query_handler.hpp"
|
||||||
#include "query/config.hpp"
|
#include "query/config.hpp"
|
||||||
#include "query/discard_value_stream.hpp"
|
#include "query/discard_value_stream.hpp"
|
||||||
@ -57,8 +58,13 @@ constexpr uint64_t kMgVmMaxMapCount = 262144;
|
|||||||
void InitFromCypherlFile(memgraph::query::InterpreterContext &ctx, memgraph::dbms::DatabaseAccess &db_acc,
|
void InitFromCypherlFile(memgraph::query::InterpreterContext &ctx, memgraph::dbms::DatabaseAccess &db_acc,
|
||||||
std::string cypherl_file_path, memgraph::audit::Log *audit_log = nullptr) {
|
std::string cypherl_file_path, memgraph::audit::Log *audit_log = nullptr) {
|
||||||
memgraph::query::Interpreter interpreter(&ctx, db_acc);
|
memgraph::query::Interpreter interpreter(&ctx, db_acc);
|
||||||
std::ifstream file(cypherl_file_path);
|
// Temporary empty user
|
||||||
|
// TODO: Double check with buda
|
||||||
|
memgraph::query::AllowEverythingAuthChecker tmp_auth_checker;
|
||||||
|
auto tmp_user = tmp_auth_checker.GenQueryUser(std::nullopt, std::nullopt);
|
||||||
|
interpreter.SetUser(tmp_user);
|
||||||
|
|
||||||
|
std::ifstream file(cypherl_file_path);
|
||||||
if (!file.is_open()) {
|
if (!file.is_open()) {
|
||||||
spdlog::trace("Could not find init file {}", cypherl_file_path);
|
spdlog::trace("Could not find init file {}", cypherl_file_path);
|
||||||
return;
|
return;
|
||||||
|
@ -40,6 +40,7 @@ set(mg_query_sources
|
|||||||
db_accessor.cpp
|
db_accessor.cpp
|
||||||
auth_query_handler.cpp
|
auth_query_handler.cpp
|
||||||
interpreter_context.cpp
|
interpreter_context.cpp
|
||||||
|
query_user.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(mg-query STATIC ${mg_query_sources})
|
add_library(mg-query STATIC ${mg_query_sources})
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2023 Memgraph Ltd.
|
// Copyright 2024 Memgraph Ltd.
|
||||||
//
|
//
|
||||||
// Use of this software is governed by the Business Source License
|
// 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
|
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||||
@ -16,7 +16,9 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "dbms/constants.hpp"
|
||||||
#include "query/frontend/ast/ast.hpp"
|
#include "query/frontend/ast/ast.hpp"
|
||||||
|
#include "query/query_user.hpp"
|
||||||
#include "storage/v2/id_types.hpp"
|
#include "storage/v2/id_types.hpp"
|
||||||
|
|
||||||
namespace memgraph::query {
|
namespace memgraph::query {
|
||||||
@ -29,15 +31,12 @@ class AuthChecker {
|
|||||||
public:
|
public:
|
||||||
virtual ~AuthChecker() = default;
|
virtual ~AuthChecker() = default;
|
||||||
|
|
||||||
[[nodiscard]] virtual bool IsUserAuthorized(const std::optional<std::string> &username,
|
virtual std::shared_ptr<QueryUserOrRole> GenQueryUser(const std::optional<std::string> &username,
|
||||||
const std::vector<AuthQuery::Privilege> &privileges,
|
const std::optional<std::string> &rolename) const = 0;
|
||||||
const std::string &db_name) const = 0;
|
|
||||||
|
|
||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
[[nodiscard]] virtual std::unique_ptr<FineGrainedAuthChecker> GetFineGrainedAuthChecker(
|
[[nodiscard]] virtual std::unique_ptr<FineGrainedAuthChecker> GetFineGrainedAuthChecker(
|
||||||
const std::string &username, const DbAccessor *db_accessor) const = 0;
|
std::shared_ptr<QueryUserOrRole> user, const DbAccessor *db_accessor) const = 0;
|
||||||
|
|
||||||
virtual void ClearCache() const = 0;
|
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
@ -98,19 +97,29 @@ class AllowEverythingFineGrainedAuthChecker final : public FineGrainedAuthChecke
|
|||||||
|
|
||||||
class AllowEverythingAuthChecker final : public AuthChecker {
|
class AllowEverythingAuthChecker final : public AuthChecker {
|
||||||
public:
|
public:
|
||||||
bool IsUserAuthorized(const std::optional<std::string> & /*username*/,
|
struct User : query::QueryUserOrRole {
|
||||||
const std::vector<AuthQuery::Privilege> & /*privileges*/,
|
User() : query::QueryUserOrRole{std::nullopt, std::nullopt} {}
|
||||||
const std::string & /*db*/) const override {
|
User(std::string name) : query::QueryUserOrRole{std::move(name), std::nullopt} {}
|
||||||
return true;
|
bool IsAuthorized(const std::vector<AuthQuery::Privilege> & /*privileges*/, const std::string & /*db_name*/,
|
||||||
|
UserPolicy * /*policy*/) const override {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#ifdef MG_ENTERPRISE
|
||||||
|
std::string GetDefaultDB() const override { return std::string{dbms::kDefaultDB}; }
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
std::shared_ptr<query::QueryUserOrRole> GenQueryUser(const std::optional<std::string> &name,
|
||||||
|
const std::optional<std::string> & /*role*/) const override {
|
||||||
|
if (name) return std::make_shared<User>(std::move(*name));
|
||||||
|
return std::make_shared<User>();
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
std::unique_ptr<FineGrainedAuthChecker> GetFineGrainedAuthChecker(const std::string & /*username*/,
|
std::unique_ptr<FineGrainedAuthChecker> GetFineGrainedAuthChecker(std::shared_ptr<QueryUserOrRole> /*user*/,
|
||||||
const DbAccessor * /*dba*/) const override {
|
const DbAccessor * /*dba*/) const override {
|
||||||
return std::make_unique<AllowEverythingFineGrainedAuthChecker>();
|
return std::make_unique<AllowEverythingFineGrainedAuthChecker>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClearCache() const override {}
|
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -46,15 +46,17 @@ class AuthQueryHandler {
|
|||||||
system::Transaction *system_tx) = 0;
|
system::Transaction *system_tx) = 0;
|
||||||
|
|
||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
/// Return true if access revoked successfully
|
|
||||||
/// @throw QueryRuntimeException if an error ocurred.
|
|
||||||
virtual bool RevokeDatabaseFromUser(const std::string &db, const std::string &username,
|
|
||||||
system::Transaction *system_tx) = 0;
|
|
||||||
|
|
||||||
/// Return true if access granted successfully
|
/// Return true if access granted successfully
|
||||||
/// @throw QueryRuntimeException if an error ocurred.
|
/// @throw QueryRuntimeException if an error ocurred.
|
||||||
virtual bool GrantDatabaseToUser(const std::string &db, const std::string &username,
|
virtual void GrantDatabase(const std::string &db, const std::string &username, system::Transaction *system_tx) = 0;
|
||||||
system::Transaction *system_tx) = 0;
|
|
||||||
|
/// Return true if access revoked successfully
|
||||||
|
/// @throw QueryRuntimeException if an error ocurred.
|
||||||
|
virtual void DenyDatabase(const std::string &db, const std::string &username, system::Transaction *system_tx) = 0;
|
||||||
|
|
||||||
|
/// Return true if access revoked successfully
|
||||||
|
/// @throw QueryRuntimeException if an error ocurred.
|
||||||
|
virtual void RevokeDatabase(const std::string &db, const std::string &username, system::Transaction *system_tx) = 0;
|
||||||
|
|
||||||
/// Returns database access rights for the user
|
/// Returns database access rights for the user
|
||||||
/// @throw QueryRuntimeException if an error ocurred.
|
/// @throw QueryRuntimeException if an error ocurred.
|
||||||
@ -62,7 +64,7 @@ class AuthQueryHandler {
|
|||||||
|
|
||||||
/// Return true if main database set successfully
|
/// Return true if main database set successfully
|
||||||
/// @throw QueryRuntimeException if an error ocurred.
|
/// @throw QueryRuntimeException if an error ocurred.
|
||||||
virtual bool SetMainDatabase(std::string_view db, const std::string &username, system::Transaction *system_tx) = 0;
|
virtual void SetMainDatabase(std::string_view db, const std::string &username, system::Transaction *system_tx) = 0;
|
||||||
|
|
||||||
/// Delete database from all users
|
/// Delete database from all users
|
||||||
/// @throw QueryRuntimeException if an error ocurred.
|
/// @throw QueryRuntimeException if an error ocurred.
|
||||||
|
@ -2819,6 +2819,7 @@ class AuthQuery : public memgraph::query::Query {
|
|||||||
SHOW_ROLE_FOR_USER,
|
SHOW_ROLE_FOR_USER,
|
||||||
SHOW_USERS_FOR_ROLE,
|
SHOW_USERS_FOR_ROLE,
|
||||||
GRANT_DATABASE_TO_USER,
|
GRANT_DATABASE_TO_USER,
|
||||||
|
DENY_DATABASE_FROM_USER,
|
||||||
REVOKE_DATABASE_FROM_USER,
|
REVOKE_DATABASE_FROM_USER,
|
||||||
SHOW_DATABASE_PRIVILEGES,
|
SHOW_DATABASE_PRIVILEGES,
|
||||||
SET_MAIN_DATABASE,
|
SET_MAIN_DATABASE,
|
||||||
|
@ -1780,22 +1780,35 @@ antlrcpp::Any CypherMainVisitor::visitShowUsersForRole(MemgraphCypher::ShowUsers
|
|||||||
/**
|
/**
|
||||||
* @return AuthQuery*
|
* @return AuthQuery*
|
||||||
*/
|
*/
|
||||||
antlrcpp::Any CypherMainVisitor::visitGrantDatabaseToUser(MemgraphCypher::GrantDatabaseToUserContext *ctx) {
|
antlrcpp::Any CypherMainVisitor::visitGrantDatabaseToUserOrRole(MemgraphCypher::GrantDatabaseToUserOrRoleContext *ctx) {
|
||||||
auto *auth = storage_->Create<AuthQuery>();
|
auto *auth = storage_->Create<AuthQuery>();
|
||||||
auth->action_ = AuthQuery::Action::GRANT_DATABASE_TO_USER;
|
auth->action_ = AuthQuery::Action::GRANT_DATABASE_TO_USER;
|
||||||
auth->database_ = std::any_cast<std::string>(ctx->wildcardName()->accept(this));
|
auth->database_ = std::any_cast<std::string>(ctx->wildcardName()->accept(this));
|
||||||
auth->user_ = std::any_cast<std::string>(ctx->user->accept(this));
|
auth->user_ = std::any_cast<std::string>(ctx->userOrRole->accept(this));
|
||||||
return auth;
|
return auth;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return AuthQuery*
|
* @return AuthQuery*
|
||||||
*/
|
*/
|
||||||
antlrcpp::Any CypherMainVisitor::visitRevokeDatabaseFromUser(MemgraphCypher::RevokeDatabaseFromUserContext *ctx) {
|
antlrcpp::Any CypherMainVisitor::visitDenyDatabaseFromUserOrRole(
|
||||||
|
MemgraphCypher::DenyDatabaseFromUserOrRoleContext *ctx) {
|
||||||
|
auto *auth = storage_->Create<AuthQuery>();
|
||||||
|
auth->action_ = AuthQuery::Action::DENY_DATABASE_FROM_USER;
|
||||||
|
auth->database_ = std::any_cast<std::string>(ctx->wildcardName()->accept(this));
|
||||||
|
auth->user_ = std::any_cast<std::string>(ctx->userOrRole->accept(this));
|
||||||
|
return auth;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return AuthQuery*
|
||||||
|
*/
|
||||||
|
antlrcpp::Any CypherMainVisitor::visitRevokeDatabaseFromUserOrRole(
|
||||||
|
MemgraphCypher::RevokeDatabaseFromUserOrRoleContext *ctx) {
|
||||||
auto *auth = storage_->Create<AuthQuery>();
|
auto *auth = storage_->Create<AuthQuery>();
|
||||||
auth->action_ = AuthQuery::Action::REVOKE_DATABASE_FROM_USER;
|
auth->action_ = AuthQuery::Action::REVOKE_DATABASE_FROM_USER;
|
||||||
auth->database_ = std::any_cast<std::string>(ctx->wildcardName()->accept(this));
|
auth->database_ = std::any_cast<std::string>(ctx->wildcardName()->accept(this));
|
||||||
auth->user_ = std::any_cast<std::string>(ctx->user->accept(this));
|
auth->user_ = std::any_cast<std::string>(ctx->userOrRole->accept(this));
|
||||||
return auth;
|
return auth;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1805,7 +1818,7 @@ antlrcpp::Any CypherMainVisitor::visitRevokeDatabaseFromUser(MemgraphCypher::Rev
|
|||||||
antlrcpp::Any CypherMainVisitor::visitShowDatabasePrivileges(MemgraphCypher::ShowDatabasePrivilegesContext *ctx) {
|
antlrcpp::Any CypherMainVisitor::visitShowDatabasePrivileges(MemgraphCypher::ShowDatabasePrivilegesContext *ctx) {
|
||||||
auto *auth = storage_->Create<AuthQuery>();
|
auto *auth = storage_->Create<AuthQuery>();
|
||||||
auth->action_ = AuthQuery::Action::SHOW_DATABASE_PRIVILEGES;
|
auth->action_ = AuthQuery::Action::SHOW_DATABASE_PRIVILEGES;
|
||||||
auth->user_ = std::any_cast<std::string>(ctx->user->accept(this));
|
auth->user_ = std::any_cast<std::string>(ctx->userOrRole->accept(this));
|
||||||
return auth;
|
return auth;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1816,7 +1829,7 @@ antlrcpp::Any CypherMainVisitor::visitSetMainDatabase(MemgraphCypher::SetMainDat
|
|||||||
auto *auth = storage_->Create<AuthQuery>();
|
auto *auth = storage_->Create<AuthQuery>();
|
||||||
auth->action_ = AuthQuery::Action::SET_MAIN_DATABASE;
|
auth->action_ = AuthQuery::Action::SET_MAIN_DATABASE;
|
||||||
auth->database_ = std::any_cast<std::string>(ctx->db->accept(this));
|
auth->database_ = std::any_cast<std::string>(ctx->db->accept(this));
|
||||||
auth->user_ = std::any_cast<std::string>(ctx->user->accept(this));
|
auth->user_ = std::any_cast<std::string>(ctx->userOrRole->accept(this));
|
||||||
return auth;
|
return auth;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -605,12 +605,17 @@ class CypherMainVisitor : public antlropencypher::MemgraphCypherBaseVisitor {
|
|||||||
/**
|
/**
|
||||||
* @return AuthQuery*
|
* @return AuthQuery*
|
||||||
*/
|
*/
|
||||||
antlrcpp::Any visitGrantDatabaseToUser(MemgraphCypher::GrantDatabaseToUserContext *ctx) override;
|
antlrcpp::Any visitGrantDatabaseToUserOrRole(MemgraphCypher::GrantDatabaseToUserOrRoleContext *ctx) override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return AuthQuery*
|
* @return AuthQuery*
|
||||||
*/
|
*/
|
||||||
antlrcpp::Any visitRevokeDatabaseFromUser(MemgraphCypher::RevokeDatabaseFromUserContext *ctx) override;
|
antlrcpp::Any visitDenyDatabaseFromUserOrRole(MemgraphCypher::DenyDatabaseFromUserOrRoleContext *ctx) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return AuthQuery*
|
||||||
|
*/
|
||||||
|
antlrcpp::Any visitRevokeDatabaseFromUserOrRole(MemgraphCypher::RevokeDatabaseFromUserOrRoleContext *ctx) override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return AuthQuery*
|
* @return AuthQuery*
|
||||||
|
@ -176,8 +176,9 @@ authQuery : createRole
|
|||||||
| showPrivileges
|
| showPrivileges
|
||||||
| showRoleForUser
|
| showRoleForUser
|
||||||
| showUsersForRole
|
| showUsersForRole
|
||||||
| grantDatabaseToUser
|
| grantDatabaseToUserOrRole
|
||||||
| revokeDatabaseFromUser
|
| denyDatabaseFromUserOrRole
|
||||||
|
| revokeDatabaseFromUserOrRole
|
||||||
| showDatabasePrivileges
|
| showDatabasePrivileges
|
||||||
| setMainDatabase
|
| setMainDatabase
|
||||||
;
|
;
|
||||||
@ -303,13 +304,15 @@ denyPrivilege : DENY ( ALL PRIVILEGES | privileges=privilegesList ) TO userOrRol
|
|||||||
|
|
||||||
revokePrivilege : REVOKE ( ALL PRIVILEGES | privileges=revokePrivilegesList ) FROM userOrRole=userOrRoleName ;
|
revokePrivilege : REVOKE ( ALL PRIVILEGES | privileges=revokePrivilegesList ) FROM userOrRole=userOrRoleName ;
|
||||||
|
|
||||||
grantDatabaseToUser : GRANT DATABASE db=wildcardName TO user=symbolicName ;
|
grantDatabaseToUserOrRole : GRANT DATABASE db=wildcardName TO userOrRole=userOrRoleName ;
|
||||||
|
|
||||||
revokeDatabaseFromUser : REVOKE DATABASE db=wildcardName FROM user=symbolicName ;
|
denyDatabaseFromUserOrRole : DENY DATABASE db=wildcardName FROM userOrRole=userOrRoleName ;
|
||||||
|
|
||||||
showDatabasePrivileges : SHOW DATABASE PRIVILEGES FOR user=symbolicName ;
|
revokeDatabaseFromUserOrRole : REVOKE DATABASE db=wildcardName FROM userOrRole=userOrRoleName ;
|
||||||
|
|
||||||
setMainDatabase : SET MAIN DATABASE db=symbolicName FOR user=symbolicName ;
|
showDatabasePrivileges : SHOW DATABASE PRIVILEGES FOR userOrRole=userOrRoleName ;
|
||||||
|
|
||||||
|
setMainDatabase : SET MAIN DATABASE db=symbolicName FOR userOrRole=userOrRoleName ;
|
||||||
|
|
||||||
privilege : CREATE
|
privilege : CREATE
|
||||||
| DELETE
|
| DELETE
|
||||||
|
@ -68,6 +68,7 @@
|
|||||||
#include "query/plan/profile.hpp"
|
#include "query/plan/profile.hpp"
|
||||||
#include "query/plan/vertex_count_cache.hpp"
|
#include "query/plan/vertex_count_cache.hpp"
|
||||||
#include "query/procedure/module.hpp"
|
#include "query/procedure/module.hpp"
|
||||||
|
#include "query/query_user.hpp"
|
||||||
#include "query/replication_query_handler.hpp"
|
#include "query/replication_query_handler.hpp"
|
||||||
#include "query/stream.hpp"
|
#include "query/stream.hpp"
|
||||||
#include "query/stream/common.hpp"
|
#include "query/stream/common.hpp"
|
||||||
@ -629,6 +630,7 @@ Callback HandleAuthQuery(AuthQuery *auth_query, InterpreterContext *interpreter_
|
|||||||
AuthQuery::Action::SHOW_USERS_FOR_ROLE,
|
AuthQuery::Action::SHOW_USERS_FOR_ROLE,
|
||||||
AuthQuery::Action::SHOW_ROLE_FOR_USER,
|
AuthQuery::Action::SHOW_ROLE_FOR_USER,
|
||||||
AuthQuery::Action::GRANT_DATABASE_TO_USER,
|
AuthQuery::Action::GRANT_DATABASE_TO_USER,
|
||||||
|
AuthQuery::Action::DENY_DATABASE_FROM_USER,
|
||||||
AuthQuery::Action::REVOKE_DATABASE_FROM_USER,
|
AuthQuery::Action::REVOKE_DATABASE_FROM_USER,
|
||||||
AuthQuery::Action::SHOW_DATABASE_PRIVILEGES,
|
AuthQuery::Action::SHOW_DATABASE_PRIVILEGES,
|
||||||
AuthQuery::Action::SET_MAIN_DATABASE};
|
AuthQuery::Action::SET_MAIN_DATABASE};
|
||||||
@ -888,9 +890,31 @@ Callback HandleAuthQuery(AuthQuery *auth_query, InterpreterContext *interpreter_
|
|||||||
if (database != memgraph::auth::kAllDatabases) {
|
if (database != memgraph::auth::kAllDatabases) {
|
||||||
db = db_handler->Get(database); // Will throw if databases doesn't exist and protect it during pull
|
db = db_handler->Get(database); // Will throw if databases doesn't exist and protect it during pull
|
||||||
}
|
}
|
||||||
if (!auth->GrantDatabaseToUser(database, username, &*interpreter->system_transaction_)) {
|
auth->GrantDatabase(database, username, &*interpreter->system_transaction_); // Can throws query exception
|
||||||
throw QueryRuntimeException("Failed to grant database {} to user {}.", database, username);
|
} catch (memgraph::dbms::UnknownDatabaseException &e) {
|
||||||
|
throw QueryRuntimeException(e.what());
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
callback.fn = [] {
|
||||||
|
#endif
|
||||||
|
return std::vector<std::vector<TypedValue>>();
|
||||||
|
};
|
||||||
|
return callback;
|
||||||
|
case AuthQuery::Action::DENY_DATABASE_FROM_USER:
|
||||||
|
forbid_on_replica();
|
||||||
|
#ifdef MG_ENTERPRISE
|
||||||
|
callback.fn = [auth, database, username, db_handler, interpreter = &interpreter] { // NOLINT
|
||||||
|
if (!interpreter->system_transaction_) {
|
||||||
|
throw QueryException("Expected to be in a system transaction");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
std::optional<memgraph::dbms::DatabaseAccess> db =
|
||||||
|
std::nullopt; // Hold pointer to database to protect it until query is done
|
||||||
|
if (database != memgraph::auth::kAllDatabases) {
|
||||||
|
db = db_handler->Get(database); // Will throw if databases doesn't exist and protect it during pull
|
||||||
}
|
}
|
||||||
|
auth->DenyDatabase(database, username, &*interpreter->system_transaction_); // Can throws query exception
|
||||||
} catch (memgraph::dbms::UnknownDatabaseException &e) {
|
} catch (memgraph::dbms::UnknownDatabaseException &e) {
|
||||||
throw QueryRuntimeException(e.what());
|
throw QueryRuntimeException(e.what());
|
||||||
}
|
}
|
||||||
@ -914,9 +938,7 @@ Callback HandleAuthQuery(AuthQuery *auth_query, InterpreterContext *interpreter_
|
|||||||
if (database != memgraph::auth::kAllDatabases) {
|
if (database != memgraph::auth::kAllDatabases) {
|
||||||
db = db_handler->Get(database); // Will throw if databases doesn't exist and protect it during pull
|
db = db_handler->Get(database); // Will throw if databases doesn't exist and protect it during pull
|
||||||
}
|
}
|
||||||
if (!auth->RevokeDatabaseFromUser(database, username, &*interpreter->system_transaction_)) {
|
auth->RevokeDatabase(database, username, &*interpreter->system_transaction_); // Can throws query exception
|
||||||
throw QueryRuntimeException("Failed to revoke database {} from user {}.", database, username);
|
|
||||||
}
|
|
||||||
} catch (memgraph::dbms::UnknownDatabaseException &e) {
|
} catch (memgraph::dbms::UnknownDatabaseException &e) {
|
||||||
throw QueryRuntimeException(e.what());
|
throw QueryRuntimeException(e.what());
|
||||||
}
|
}
|
||||||
@ -949,9 +971,7 @@ Callback HandleAuthQuery(AuthQuery *auth_query, InterpreterContext *interpreter_
|
|||||||
try {
|
try {
|
||||||
const auto db =
|
const auto db =
|
||||||
db_handler->Get(database); // Will throw if databases doesn't exist and protect it during pull
|
db_handler->Get(database); // Will throw if databases doesn't exist and protect it during pull
|
||||||
if (!auth->SetMainDatabase(database, username, &*interpreter->system_transaction_)) {
|
auth->SetMainDatabase(database, username, &*interpreter->system_transaction_); // Can throws query exception
|
||||||
throw QueryRuntimeException("Failed to set main database {} for user {}.", database, username);
|
|
||||||
}
|
|
||||||
} catch (memgraph::dbms::UnknownDatabaseException &e) {
|
} catch (memgraph::dbms::UnknownDatabaseException &e) {
|
||||||
throw QueryRuntimeException(e.what());
|
throw QueryRuntimeException(e.what());
|
||||||
}
|
}
|
||||||
@ -1275,7 +1295,7 @@ std::vector<std::string> EvaluateTopicNames(ExpressionVisitor<TypedValue> &evalu
|
|||||||
Callback::CallbackFunction GetKafkaCreateCallback(StreamQuery *stream_query, ExpressionVisitor<TypedValue> &evaluator,
|
Callback::CallbackFunction GetKafkaCreateCallback(StreamQuery *stream_query, ExpressionVisitor<TypedValue> &evaluator,
|
||||||
memgraph::dbms::DatabaseAccess db_acc,
|
memgraph::dbms::DatabaseAccess db_acc,
|
||||||
InterpreterContext *interpreter_context,
|
InterpreterContext *interpreter_context,
|
||||||
const std::optional<std::string> &username) {
|
std::shared_ptr<QueryUserOrRole> user_or_role) {
|
||||||
static constexpr std::string_view kDefaultConsumerGroup = "mg_consumer";
|
static constexpr std::string_view kDefaultConsumerGroup = "mg_consumer";
|
||||||
std::string consumer_group{stream_query->consumer_group_.empty() ? kDefaultConsumerGroup
|
std::string consumer_group{stream_query->consumer_group_.empty() ? kDefaultConsumerGroup
|
||||||
: stream_query->consumer_group_};
|
: stream_query->consumer_group_};
|
||||||
@ -1302,10 +1322,13 @@ Callback::CallbackFunction GetKafkaCreateCallback(StreamQuery *stream_query, Exp
|
|||||||
|
|
||||||
memgraph::metrics::IncrementCounter(memgraph::metrics::StreamsCreated);
|
memgraph::metrics::IncrementCounter(memgraph::metrics::StreamsCreated);
|
||||||
|
|
||||||
|
// Make a copy of the user and pass it to the subsystem
|
||||||
|
auto owner = interpreter_context->auth_checker->GenQueryUser(user_or_role->username(), user_or_role->rolename());
|
||||||
|
|
||||||
return [db_acc = std::move(db_acc), interpreter_context, stream_name = stream_query->stream_name_,
|
return [db_acc = std::move(db_acc), interpreter_context, stream_name = stream_query->stream_name_,
|
||||||
topic_names = EvaluateTopicNames(evaluator, stream_query->topic_names_),
|
topic_names = EvaluateTopicNames(evaluator, stream_query->topic_names_),
|
||||||
consumer_group = std::move(consumer_group), common_stream_info = std::move(common_stream_info),
|
consumer_group = std::move(consumer_group), common_stream_info = std::move(common_stream_info),
|
||||||
bootstrap_servers = std::move(bootstrap), owner = username,
|
bootstrap_servers = std::move(bootstrap), owner = std::move(owner),
|
||||||
configs = get_config_map(stream_query->configs_, "Configs"),
|
configs = get_config_map(stream_query->configs_, "Configs"),
|
||||||
credentials = get_config_map(stream_query->credentials_, "Credentials"),
|
credentials = get_config_map(stream_query->credentials_, "Credentials"),
|
||||||
default_server = interpreter_context->config.default_kafka_bootstrap_servers]() mutable {
|
default_server = interpreter_context->config.default_kafka_bootstrap_servers]() mutable {
|
||||||
@ -1327,7 +1350,7 @@ Callback::CallbackFunction GetKafkaCreateCallback(StreamQuery *stream_query, Exp
|
|||||||
Callback::CallbackFunction GetPulsarCreateCallback(StreamQuery *stream_query, ExpressionVisitor<TypedValue> &evaluator,
|
Callback::CallbackFunction GetPulsarCreateCallback(StreamQuery *stream_query, ExpressionVisitor<TypedValue> &evaluator,
|
||||||
memgraph::dbms::DatabaseAccess db,
|
memgraph::dbms::DatabaseAccess db,
|
||||||
InterpreterContext *interpreter_context,
|
InterpreterContext *interpreter_context,
|
||||||
const std::optional<std::string> &username) {
|
std::shared_ptr<QueryUserOrRole> user_or_role) {
|
||||||
auto service_url = GetOptionalStringValue(stream_query->service_url_, evaluator);
|
auto service_url = GetOptionalStringValue(stream_query->service_url_, evaluator);
|
||||||
if (service_url && service_url->empty()) {
|
if (service_url && service_url->empty()) {
|
||||||
throw SemanticException("Service URL must not be an empty string!");
|
throw SemanticException("Service URL must not be an empty string!");
|
||||||
@ -1335,9 +1358,13 @@ Callback::CallbackFunction GetPulsarCreateCallback(StreamQuery *stream_query, Ex
|
|||||||
auto common_stream_info = GetCommonStreamInfo(stream_query, evaluator);
|
auto common_stream_info = GetCommonStreamInfo(stream_query, evaluator);
|
||||||
memgraph::metrics::IncrementCounter(memgraph::metrics::StreamsCreated);
|
memgraph::metrics::IncrementCounter(memgraph::metrics::StreamsCreated);
|
||||||
|
|
||||||
|
// Make a copy of the user and pass it to the subsystem
|
||||||
|
auto owner = interpreter_context->auth_checker->GenQueryUser(user_or_role->username(), user_or_role->rolename());
|
||||||
|
|
||||||
return [db = std::move(db), interpreter_context, stream_name = stream_query->stream_name_,
|
return [db = std::move(db), interpreter_context, stream_name = stream_query->stream_name_,
|
||||||
topic_names = EvaluateTopicNames(evaluator, stream_query->topic_names_),
|
topic_names = EvaluateTopicNames(evaluator, stream_query->topic_names_),
|
||||||
common_stream_info = std::move(common_stream_info), service_url = std::move(service_url), owner = username,
|
common_stream_info = std::move(common_stream_info), service_url = std::move(service_url),
|
||||||
|
owner = std::move(owner),
|
||||||
default_service = interpreter_context->config.default_pulsar_service_url]() mutable {
|
default_service = interpreter_context->config.default_pulsar_service_url]() mutable {
|
||||||
std::string url = service_url ? std::move(*service_url) : std::move(default_service);
|
std::string url = service_url ? std::move(*service_url) : std::move(default_service);
|
||||||
db->streams()->Create<query::stream::PulsarStream>(
|
db->streams()->Create<query::stream::PulsarStream>(
|
||||||
@ -1351,7 +1378,7 @@ Callback::CallbackFunction GetPulsarCreateCallback(StreamQuery *stream_query, Ex
|
|||||||
|
|
||||||
Callback HandleStreamQuery(StreamQuery *stream_query, const Parameters ¶meters,
|
Callback HandleStreamQuery(StreamQuery *stream_query, const Parameters ¶meters,
|
||||||
memgraph::dbms::DatabaseAccess &db_acc, InterpreterContext *interpreter_context,
|
memgraph::dbms::DatabaseAccess &db_acc, InterpreterContext *interpreter_context,
|
||||||
const std::optional<std::string> &username, std::vector<Notification> *notifications) {
|
std::shared_ptr<QueryUserOrRole> user_or_role, std::vector<Notification> *notifications) {
|
||||||
// TODO: MemoryResource for EvaluationContext, it should probably be passed as
|
// TODO: MemoryResource for EvaluationContext, it should probably be passed as
|
||||||
// the argument to Callback.
|
// the argument to Callback.
|
||||||
EvaluationContext evaluation_context;
|
EvaluationContext evaluation_context;
|
||||||
@ -1364,10 +1391,12 @@ Callback HandleStreamQuery(StreamQuery *stream_query, const Parameters ¶mete
|
|||||||
case StreamQuery::Action::CREATE_STREAM: {
|
case StreamQuery::Action::CREATE_STREAM: {
|
||||||
switch (stream_query->type_) {
|
switch (stream_query->type_) {
|
||||||
case StreamQuery::Type::KAFKA:
|
case StreamQuery::Type::KAFKA:
|
||||||
callback.fn = GetKafkaCreateCallback(stream_query, evaluator, db_acc, interpreter_context, username);
|
callback.fn =
|
||||||
|
GetKafkaCreateCallback(stream_query, evaluator, db_acc, interpreter_context, std::move(user_or_role));
|
||||||
break;
|
break;
|
||||||
case StreamQuery::Type::PULSAR:
|
case StreamQuery::Type::PULSAR:
|
||||||
callback.fn = GetPulsarCreateCallback(stream_query, evaluator, db_acc, interpreter_context, username);
|
callback.fn =
|
||||||
|
GetPulsarCreateCallback(stream_query, evaluator, db_acc, interpreter_context, std::move(user_or_role));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
notifications->emplace_back(SeverityLevel::INFO, NotificationCode::CREATE_STREAM,
|
notifications->emplace_back(SeverityLevel::INFO, NotificationCode::CREATE_STREAM,
|
||||||
@ -1640,7 +1669,7 @@ struct TxTimeout {
|
|||||||
struct PullPlan {
|
struct PullPlan {
|
||||||
explicit PullPlan(std::shared_ptr<PlanWrapper> plan, const Parameters ¶meters, bool is_profile_query,
|
explicit PullPlan(std::shared_ptr<PlanWrapper> plan, const Parameters ¶meters, bool is_profile_query,
|
||||||
DbAccessor *dba, InterpreterContext *interpreter_context, utils::MemoryResource *execution_memory,
|
DbAccessor *dba, InterpreterContext *interpreter_context, utils::MemoryResource *execution_memory,
|
||||||
std::optional<std::string> username, std::atomic<TransactionStatus> *transaction_status,
|
std::shared_ptr<QueryUserOrRole> user_or_role, std::atomic<TransactionStatus> *transaction_status,
|
||||||
std::shared_ptr<utils::AsyncTimer> tx_timer,
|
std::shared_ptr<utils::AsyncTimer> tx_timer,
|
||||||
TriggerContextCollector *trigger_context_collector = nullptr,
|
TriggerContextCollector *trigger_context_collector = nullptr,
|
||||||
std::optional<size_t> memory_limit = {}, bool use_monotonic_memory = true,
|
std::optional<size_t> memory_limit = {}, bool use_monotonic_memory = true,
|
||||||
@ -1680,7 +1709,7 @@ struct PullPlan {
|
|||||||
|
|
||||||
PullPlan::PullPlan(const std::shared_ptr<PlanWrapper> plan, const Parameters ¶meters, const bool is_profile_query,
|
PullPlan::PullPlan(const std::shared_ptr<PlanWrapper> plan, const Parameters ¶meters, const bool is_profile_query,
|
||||||
DbAccessor *dba, InterpreterContext *interpreter_context, utils::MemoryResource *execution_memory,
|
DbAccessor *dba, InterpreterContext *interpreter_context, utils::MemoryResource *execution_memory,
|
||||||
std::optional<std::string> username, std::atomic<TransactionStatus> *transaction_status,
|
std::shared_ptr<QueryUserOrRole> user_or_role, std::atomic<TransactionStatus> *transaction_status,
|
||||||
std::shared_ptr<utils::AsyncTimer> tx_timer, TriggerContextCollector *trigger_context_collector,
|
std::shared_ptr<utils::AsyncTimer> tx_timer, TriggerContextCollector *trigger_context_collector,
|
||||||
const std::optional<size_t> memory_limit, bool use_monotonic_memory,
|
const std::optional<size_t> memory_limit, bool use_monotonic_memory,
|
||||||
FrameChangeCollector *frame_change_collector)
|
FrameChangeCollector *frame_change_collector)
|
||||||
@ -1696,10 +1725,9 @@ PullPlan::PullPlan(const std::shared_ptr<PlanWrapper> plan, const Parameters &pa
|
|||||||
ctx_.evaluation_context.properties = NamesToProperties(plan->ast_storage().properties_, dba);
|
ctx_.evaluation_context.properties = NamesToProperties(plan->ast_storage().properties_, dba);
|
||||||
ctx_.evaluation_context.labels = NamesToLabels(plan->ast_storage().labels_, dba);
|
ctx_.evaluation_context.labels = NamesToLabels(plan->ast_storage().labels_, dba);
|
||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
if (license::global_license_checker.IsEnterpriseValidFast() && username.has_value() && dba) {
|
if (license::global_license_checker.IsEnterpriseValidFast() && user_or_role && *user_or_role && dba) {
|
||||||
// TODO How can we avoid creating this every time? If we must create it, it would be faster with an auth::User
|
// Create only if an explicit user is defined
|
||||||
// instead of the username
|
auto auth_checker = interpreter_context->auth_checker->GetFineGrainedAuthChecker(std::move(user_or_role), dba);
|
||||||
auto auth_checker = interpreter_context->auth_checker->GetFineGrainedAuthChecker(*username, dba);
|
|
||||||
|
|
||||||
// if the user has global privileges to read, edit and write anything, we don't need to perform authorization
|
// if the user has global privileges to read, edit and write anything, we don't need to perform authorization
|
||||||
// otherwise, we do assign the auth checker to check for label access control
|
// otherwise, we do assign the auth checker to check for label access control
|
||||||
@ -1989,7 +2017,7 @@ bool IsCallBatchedProcedureQuery(const std::vector<memgraph::query::Clause *> &c
|
|||||||
PreparedQuery PrepareCypherQuery(ParsedQuery parsed_query, std::map<std::string, TypedValue> *summary,
|
PreparedQuery PrepareCypherQuery(ParsedQuery parsed_query, std::map<std::string, TypedValue> *summary,
|
||||||
InterpreterContext *interpreter_context, CurrentDB ¤t_db,
|
InterpreterContext *interpreter_context, CurrentDB ¤t_db,
|
||||||
utils::MemoryResource *execution_memory, std::vector<Notification> *notifications,
|
utils::MemoryResource *execution_memory, std::vector<Notification> *notifications,
|
||||||
std::optional<std::string> const &username,
|
std::shared_ptr<QueryUserOrRole> user_or_role,
|
||||||
std::atomic<TransactionStatus> *transaction_status,
|
std::atomic<TransactionStatus> *transaction_status,
|
||||||
std::shared_ptr<utils::AsyncTimer> tx_timer,
|
std::shared_ptr<utils::AsyncTimer> tx_timer,
|
||||||
FrameChangeCollector *frame_change_collector = nullptr) {
|
FrameChangeCollector *frame_change_collector = nullptr) {
|
||||||
@ -2057,8 +2085,8 @@ PreparedQuery PrepareCypherQuery(ParsedQuery parsed_query, std::map<std::string,
|
|||||||
auto *trigger_context_collector =
|
auto *trigger_context_collector =
|
||||||
current_db.trigger_context_collector_ ? &*current_db.trigger_context_collector_ : nullptr;
|
current_db.trigger_context_collector_ ? &*current_db.trigger_context_collector_ : nullptr;
|
||||||
auto pull_plan = std::make_shared<PullPlan>(
|
auto pull_plan = std::make_shared<PullPlan>(
|
||||||
plan, parsed_query.parameters, false, dba, interpreter_context, execution_memory, username, transaction_status,
|
plan, parsed_query.parameters, false, dba, interpreter_context, execution_memory, std::move(user_or_role),
|
||||||
std::move(tx_timer), trigger_context_collector, memory_limit, use_monotonic_memory,
|
transaction_status, std::move(tx_timer), trigger_context_collector, memory_limit, use_monotonic_memory,
|
||||||
frame_change_collector->IsTrackingValues() ? frame_change_collector : nullptr);
|
frame_change_collector->IsTrackingValues() ? frame_change_collector : nullptr);
|
||||||
return PreparedQuery{std::move(header), std::move(parsed_query.required_privileges),
|
return PreparedQuery{std::move(header), std::move(parsed_query.required_privileges),
|
||||||
[pull_plan = std::move(pull_plan), output_symbols = std::move(output_symbols), summary](
|
[pull_plan = std::move(pull_plan), output_symbols = std::move(output_symbols), summary](
|
||||||
@ -2130,7 +2158,8 @@ PreparedQuery PrepareExplainQuery(ParsedQuery parsed_query, std::map<std::string
|
|||||||
PreparedQuery PrepareProfileQuery(ParsedQuery parsed_query, bool in_explicit_transaction,
|
PreparedQuery PrepareProfileQuery(ParsedQuery parsed_query, bool in_explicit_transaction,
|
||||||
std::map<std::string, TypedValue> *summary, std::vector<Notification> *notifications,
|
std::map<std::string, TypedValue> *summary, std::vector<Notification> *notifications,
|
||||||
InterpreterContext *interpreter_context, CurrentDB ¤t_db,
|
InterpreterContext *interpreter_context, CurrentDB ¤t_db,
|
||||||
utils::MemoryResource *execution_memory, std::optional<std::string> const &username,
|
utils::MemoryResource *execution_memory,
|
||||||
|
std::shared_ptr<QueryUserOrRole> user_or_role,
|
||||||
std::atomic<TransactionStatus> *transaction_status,
|
std::atomic<TransactionStatus> *transaction_status,
|
||||||
std::shared_ptr<utils::AsyncTimer> tx_timer,
|
std::shared_ptr<utils::AsyncTimer> tx_timer,
|
||||||
FrameChangeCollector *frame_change_collector) {
|
FrameChangeCollector *frame_change_collector) {
|
||||||
@ -2208,37 +2237,37 @@ PreparedQuery PrepareProfileQuery(ParsedQuery parsed_query, bool in_explicit_tra
|
|||||||
|
|
||||||
rw_type_checker.InferRWType(const_cast<plan::LogicalOperator &>(cypher_query_plan->plan()));
|
rw_type_checker.InferRWType(const_cast<plan::LogicalOperator &>(cypher_query_plan->plan()));
|
||||||
|
|
||||||
return PreparedQuery{{"OPERATOR", "ACTUAL HITS", "RELATIVE TIME", "ABSOLUTE TIME"},
|
return PreparedQuery{
|
||||||
std::move(parsed_query.required_privileges),
|
{"OPERATOR", "ACTUAL HITS", "RELATIVE TIME", "ABSOLUTE TIME"},
|
||||||
[plan = std::move(cypher_query_plan), parameters = std::move(parsed_inner_query.parameters),
|
std::move(parsed_query.required_privileges),
|
||||||
summary, dba, interpreter_context, execution_memory, memory_limit, username,
|
[plan = std::move(cypher_query_plan), parameters = std::move(parsed_inner_query.parameters), summary, dba,
|
||||||
// We want to execute the query we are profiling lazily, so we delay
|
interpreter_context, execution_memory, memory_limit, user_or_role = std::move(user_or_role),
|
||||||
// the construction of the corresponding context.
|
// We want to execute the query we are profiling lazily, so we delay
|
||||||
stats_and_total_time = std::optional<plan::ProfilingStatsWithTotalTime>{},
|
// the construction of the corresponding context.
|
||||||
pull_plan = std::shared_ptr<PullPlanVector>(nullptr), transaction_status, use_monotonic_memory,
|
stats_and_total_time = std::optional<plan::ProfilingStatsWithTotalTime>{},
|
||||||
frame_change_collector, tx_timer = std::move(tx_timer)](
|
pull_plan = std::shared_ptr<PullPlanVector>(nullptr), transaction_status, use_monotonic_memory,
|
||||||
AnyStream *stream, std::optional<int> n) mutable -> std::optional<QueryHandlerResult> {
|
frame_change_collector, tx_timer = std::move(tx_timer)](
|
||||||
// No output symbols are given so that nothing is streamed.
|
AnyStream *stream, std::optional<int> n) mutable -> std::optional<QueryHandlerResult> {
|
||||||
if (!stats_and_total_time) {
|
// No output symbols are given so that nothing is streamed.
|
||||||
stats_and_total_time =
|
if (!stats_and_total_time) {
|
||||||
PullPlan(plan, parameters, true, dba, interpreter_context, execution_memory, username,
|
stats_and_total_time =
|
||||||
transaction_status, std::move(tx_timer), nullptr, memory_limit,
|
PullPlan(plan, parameters, true, dba, interpreter_context, execution_memory, std::move(user_or_role),
|
||||||
use_monotonic_memory,
|
transaction_status, std::move(tx_timer), nullptr, memory_limit, use_monotonic_memory,
|
||||||
frame_change_collector->IsTrackingValues() ? frame_change_collector : nullptr)
|
frame_change_collector->IsTrackingValues() ? frame_change_collector : nullptr)
|
||||||
.Pull(stream, {}, {}, summary);
|
.Pull(stream, {}, {}, summary);
|
||||||
pull_plan = std::make_shared<PullPlanVector>(ProfilingStatsToTable(*stats_and_total_time));
|
pull_plan = std::make_shared<PullPlanVector>(ProfilingStatsToTable(*stats_and_total_time));
|
||||||
}
|
}
|
||||||
|
|
||||||
MG_ASSERT(stats_and_total_time, "Failed to execute the query!");
|
MG_ASSERT(stats_and_total_time, "Failed to execute the query!");
|
||||||
|
|
||||||
if (pull_plan->Pull(stream, n)) {
|
if (pull_plan->Pull(stream, n)) {
|
||||||
summary->insert_or_assign("profile", ProfilingStatsToJson(*stats_and_total_time).dump());
|
summary->insert_or_assign("profile", ProfilingStatsToJson(*stats_and_total_time).dump());
|
||||||
return QueryHandlerResult::ABORT;
|
return QueryHandlerResult::ABORT;
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
},
|
},
|
||||||
rw_type_checker.type};
|
rw_type_checker.type};
|
||||||
}
|
}
|
||||||
|
|
||||||
PreparedQuery PrepareDumpQuery(ParsedQuery parsed_query, CurrentDB ¤t_db) {
|
PreparedQuery PrepareDumpQuery(ParsedQuery parsed_query, CurrentDB ¤t_db) {
|
||||||
@ -2662,26 +2691,22 @@ PreparedQuery PrepareAuthQuery(ParsedQuery parsed_query, bool in_explicit_transa
|
|||||||
|
|
||||||
auto callback = HandleAuthQuery(auth_query, interpreter_context, parsed_query.parameters, interpreter);
|
auto callback = HandleAuthQuery(auth_query, interpreter_context, parsed_query.parameters, interpreter);
|
||||||
|
|
||||||
return PreparedQuery{std::move(callback.header), std::move(parsed_query.required_privileges),
|
return PreparedQuery{
|
||||||
[handler = std::move(callback.fn), pull_plan = std::shared_ptr<PullPlanVector>(nullptr),
|
std::move(callback.header), std::move(parsed_query.required_privileges),
|
||||||
interpreter_context]( // NOLINT
|
[handler = std::move(callback.fn), pull_plan = std::shared_ptr<PullPlanVector>(nullptr)]( // NOLINT
|
||||||
AnyStream *stream, std::optional<int> n) mutable -> std::optional<QueryHandlerResult> {
|
AnyStream *stream, std::optional<int> n) mutable -> std::optional<QueryHandlerResult> {
|
||||||
if (!pull_plan) {
|
if (!pull_plan) {
|
||||||
// Run the specific query
|
// Run the specific query
|
||||||
auto results = handler();
|
auto results = handler();
|
||||||
pull_plan = std::make_shared<PullPlanVector>(std::move(results));
|
pull_plan = std::make_shared<PullPlanVector>(std::move(results));
|
||||||
#ifdef MG_ENTERPRISE
|
}
|
||||||
// Invalidate auth cache after every type of AuthQuery
|
|
||||||
interpreter_context->auth_checker->ClearCache();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pull_plan->Pull(stream, n)) {
|
if (pull_plan->Pull(stream, n)) {
|
||||||
return QueryHandlerResult::COMMIT;
|
return QueryHandlerResult::COMMIT;
|
||||||
}
|
}
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
},
|
},
|
||||||
RWType::NONE};
|
RWType::NONE};
|
||||||
}
|
}
|
||||||
|
|
||||||
PreparedQuery PrepareReplicationQuery(ParsedQuery parsed_query, bool in_explicit_transaction,
|
PreparedQuery PrepareReplicationQuery(ParsedQuery parsed_query, bool in_explicit_transaction,
|
||||||
@ -2885,17 +2910,18 @@ TriggerEventType ToTriggerEventType(const TriggerQuery::EventType event_type) {
|
|||||||
Callback CreateTrigger(TriggerQuery *trigger_query,
|
Callback CreateTrigger(TriggerQuery *trigger_query,
|
||||||
const std::map<std::string, storage::PropertyValue> &user_parameters,
|
const std::map<std::string, storage::PropertyValue> &user_parameters,
|
||||||
TriggerStore *trigger_store, InterpreterContext *interpreter_context, DbAccessor *dba,
|
TriggerStore *trigger_store, InterpreterContext *interpreter_context, DbAccessor *dba,
|
||||||
std::optional<std::string> owner) {
|
std::shared_ptr<QueryUserOrRole> user_or_role) {
|
||||||
|
// Make a copy of the user and pass it to the subsystem
|
||||||
|
auto owner = interpreter_context->auth_checker->GenQueryUser(user_or_role->username(), user_or_role->rolename());
|
||||||
return {{},
|
return {{},
|
||||||
[trigger_name = std::move(trigger_query->trigger_name_),
|
[trigger_name = std::move(trigger_query->trigger_name_),
|
||||||
trigger_statement = std::move(trigger_query->statement_), event_type = trigger_query->event_type_,
|
trigger_statement = std::move(trigger_query->statement_), event_type = trigger_query->event_type_,
|
||||||
before_commit = trigger_query->before_commit_, trigger_store, interpreter_context, dba, user_parameters,
|
before_commit = trigger_query->before_commit_, trigger_store, interpreter_context, dba, user_parameters,
|
||||||
owner = std::move(owner)]() mutable -> std::vector<std::vector<TypedValue>> {
|
owner = std::move(owner)]() mutable -> std::vector<std::vector<TypedValue>> {
|
||||||
trigger_store->AddTrigger(std::move(trigger_name), trigger_statement, user_parameters,
|
trigger_store->AddTrigger(
|
||||||
ToTriggerEventType(event_type),
|
std::move(trigger_name), trigger_statement, user_parameters, ToTriggerEventType(event_type),
|
||||||
before_commit ? TriggerPhase::BEFORE_COMMIT : TriggerPhase::AFTER_COMMIT,
|
before_commit ? TriggerPhase::BEFORE_COMMIT : TriggerPhase::AFTER_COMMIT,
|
||||||
&interpreter_context->ast_cache, dba, interpreter_context->config.query,
|
&interpreter_context->ast_cache, dba, interpreter_context->config.query, std::move(owner));
|
||||||
std::move(owner), interpreter_context->auth_checker);
|
|
||||||
memgraph::metrics::IncrementCounter(memgraph::metrics::TriggersCreated);
|
memgraph::metrics::IncrementCounter(memgraph::metrics::TriggersCreated);
|
||||||
return {};
|
return {};
|
||||||
}};
|
}};
|
||||||
@ -2937,7 +2963,7 @@ PreparedQuery PrepareTriggerQuery(ParsedQuery parsed_query, bool in_explicit_tra
|
|||||||
std::vector<Notification> *notifications, CurrentDB ¤t_db,
|
std::vector<Notification> *notifications, CurrentDB ¤t_db,
|
||||||
InterpreterContext *interpreter_context,
|
InterpreterContext *interpreter_context,
|
||||||
const std::map<std::string, storage::PropertyValue> &user_parameters,
|
const std::map<std::string, storage::PropertyValue> &user_parameters,
|
||||||
std::optional<std::string> const &username) {
|
std::shared_ptr<QueryUserOrRole> user_or_role) {
|
||||||
if (in_explicit_transaction) {
|
if (in_explicit_transaction) {
|
||||||
throw TriggerModificationInMulticommandTxException();
|
throw TriggerModificationInMulticommandTxException();
|
||||||
}
|
}
|
||||||
@ -2951,8 +2977,9 @@ PreparedQuery PrepareTriggerQuery(ParsedQuery parsed_query, bool in_explicit_tra
|
|||||||
MG_ASSERT(trigger_query);
|
MG_ASSERT(trigger_query);
|
||||||
|
|
||||||
std::optional<Notification> trigger_notification;
|
std::optional<Notification> trigger_notification;
|
||||||
|
|
||||||
auto callback = std::invoke([trigger_query, trigger_store, interpreter_context, dba, &user_parameters,
|
auto callback = std::invoke([trigger_query, trigger_store, interpreter_context, dba, &user_parameters,
|
||||||
owner = username, &trigger_notification]() mutable {
|
owner = std::move(user_or_role), &trigger_notification]() mutable {
|
||||||
switch (trigger_query->action_) {
|
switch (trigger_query->action_) {
|
||||||
case TriggerQuery::Action::CREATE_TRIGGER:
|
case TriggerQuery::Action::CREATE_TRIGGER:
|
||||||
trigger_notification.emplace(SeverityLevel::INFO, NotificationCode::CREATE_TRIGGER,
|
trigger_notification.emplace(SeverityLevel::INFO, NotificationCode::CREATE_TRIGGER,
|
||||||
@ -2990,7 +3017,8 @@ PreparedQuery PrepareTriggerQuery(ParsedQuery parsed_query, bool in_explicit_tra
|
|||||||
|
|
||||||
PreparedQuery PrepareStreamQuery(ParsedQuery parsed_query, bool in_explicit_transaction,
|
PreparedQuery PrepareStreamQuery(ParsedQuery parsed_query, bool in_explicit_transaction,
|
||||||
std::vector<Notification> *notifications, CurrentDB ¤t_db,
|
std::vector<Notification> *notifications, CurrentDB ¤t_db,
|
||||||
InterpreterContext *interpreter_context, const std::optional<std::string> &username) {
|
InterpreterContext *interpreter_context,
|
||||||
|
std::shared_ptr<QueryUserOrRole> user_or_role) {
|
||||||
if (in_explicit_transaction) {
|
if (in_explicit_transaction) {
|
||||||
throw StreamQueryInMulticommandTxException();
|
throw StreamQueryInMulticommandTxException();
|
||||||
}
|
}
|
||||||
@ -3000,8 +3028,8 @@ PreparedQuery PrepareStreamQuery(ParsedQuery parsed_query, bool in_explicit_tran
|
|||||||
|
|
||||||
auto *stream_query = utils::Downcast<StreamQuery>(parsed_query.query);
|
auto *stream_query = utils::Downcast<StreamQuery>(parsed_query.query);
|
||||||
MG_ASSERT(stream_query);
|
MG_ASSERT(stream_query);
|
||||||
auto callback =
|
auto callback = HandleStreamQuery(stream_query, parsed_query.parameters, db_acc, interpreter_context,
|
||||||
HandleStreamQuery(stream_query, parsed_query.parameters, db_acc, interpreter_context, username, notifications);
|
std::move(user_or_role), notifications);
|
||||||
|
|
||||||
return PreparedQuery{std::move(callback.header), std::move(parsed_query.required_privileges),
|
return PreparedQuery{std::move(callback.header), std::move(parsed_query.required_privileges),
|
||||||
[callback_fn = std::move(callback.fn), pull_plan = std::shared_ptr<PullPlanVector>{nullptr}](
|
[callback_fn = std::move(callback.fn), pull_plan = std::shared_ptr<PullPlanVector>{nullptr}](
|
||||||
@ -3305,7 +3333,7 @@ PreparedQuery PrepareSettingQuery(ParsedQuery parsed_query, bool in_explicit_tra
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename Func>
|
template <typename Func>
|
||||||
auto ShowTransactions(const std::unordered_set<Interpreter *> &interpreters, const std::optional<std::string> &username,
|
auto ShowTransactions(const std::unordered_set<Interpreter *> &interpreters, QueryUserOrRole *user_or_role,
|
||||||
Func &&privilege_checker) -> std::vector<std::vector<TypedValue>> {
|
Func &&privilege_checker) -> std::vector<std::vector<TypedValue>> {
|
||||||
std::vector<std::vector<TypedValue>> results;
|
std::vector<std::vector<TypedValue>> results;
|
||||||
results.reserve(interpreters.size());
|
results.reserve(interpreters.size());
|
||||||
@ -3325,11 +3353,21 @@ auto ShowTransactions(const std::unordered_set<Interpreter *> &interpreters, con
|
|||||||
static std::string all;
|
static std::string all;
|
||||||
return interpreter->current_db_.db_acc_ ? interpreter->current_db_.db_acc_->get()->name() : all;
|
return interpreter->current_db_.db_acc_ ? interpreter->current_db_.db_acc_->get()->name() : all;
|
||||||
};
|
};
|
||||||
if (transaction_id.has_value() &&
|
|
||||||
(interpreter->username_ == username || privilege_checker(get_interpreter_db_name()))) {
|
auto same_user = [](const auto &lv, const auto &rv) {
|
||||||
|
if (lv.get() == rv) return true;
|
||||||
|
if (lv && rv) return *lv == *rv;
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (transaction_id.has_value() && (same_user(interpreter->user_or_role_, user_or_role) ||
|
||||||
|
privilege_checker(user_or_role, get_interpreter_db_name()))) {
|
||||||
const auto &typed_queries = interpreter->GetQueries();
|
const auto &typed_queries = interpreter->GetQueries();
|
||||||
results.push_back({TypedValue(interpreter->username_.value_or("")),
|
results.push_back(
|
||||||
TypedValue(std::to_string(transaction_id.value())), TypedValue(typed_queries)});
|
{TypedValue(interpreter->user_or_role_
|
||||||
|
? (interpreter->user_or_role_->username() ? *interpreter->user_or_role_->username() : "")
|
||||||
|
: ""),
|
||||||
|
TypedValue(std::to_string(transaction_id.value())), TypedValue(typed_queries)});
|
||||||
// Handle user-defined metadata
|
// Handle user-defined metadata
|
||||||
std::map<std::string, TypedValue> metadata_tv;
|
std::map<std::string, TypedValue> metadata_tv;
|
||||||
if (interpreter->metadata_) {
|
if (interpreter->metadata_) {
|
||||||
@ -3344,17 +3382,19 @@ auto ShowTransactions(const std::unordered_set<Interpreter *> &interpreters, con
|
|||||||
}
|
}
|
||||||
|
|
||||||
Callback HandleTransactionQueueQuery(TransactionQueueQuery *transaction_query,
|
Callback HandleTransactionQueueQuery(TransactionQueueQuery *transaction_query,
|
||||||
const std::optional<std::string> &username, const Parameters ¶meters,
|
std::shared_ptr<QueryUserOrRole> user_or_role, const Parameters ¶meters,
|
||||||
InterpreterContext *interpreter_context) {
|
InterpreterContext *interpreter_context) {
|
||||||
auto privilege_checker = [username, auth_checker = interpreter_context->auth_checker](std::string const &db_name) {
|
auto privilege_checker = [](QueryUserOrRole *user_or_role, std::string const &db_name) {
|
||||||
return auth_checker->IsUserAuthorized(username, {query::AuthQuery::Privilege::TRANSACTION_MANAGEMENT}, db_name);
|
return user_or_role && user_or_role->IsAuthorized({query::AuthQuery::Privilege::TRANSACTION_MANAGEMENT}, db_name,
|
||||||
|
&query::up_to_date_policy);
|
||||||
};
|
};
|
||||||
|
|
||||||
Callback callback;
|
Callback callback;
|
||||||
switch (transaction_query->action_) {
|
switch (transaction_query->action_) {
|
||||||
case TransactionQueueQuery::Action::SHOW_TRANSACTIONS: {
|
case TransactionQueueQuery::Action::SHOW_TRANSACTIONS: {
|
||||||
auto show_transactions = [username, privilege_checker = std::move(privilege_checker)](const auto &interpreters) {
|
auto show_transactions = [user_or_role = std::move(user_or_role),
|
||||||
return ShowTransactions(interpreters, username, privilege_checker);
|
privilege_checker = std::move(privilege_checker)](const auto &interpreters) {
|
||||||
|
return ShowTransactions(interpreters, user_or_role.get(), privilege_checker);
|
||||||
};
|
};
|
||||||
callback.header = {"username", "transaction_id", "query", "metadata"};
|
callback.header = {"username", "transaction_id", "query", "metadata"};
|
||||||
callback.fn = [interpreter_context, show_transactions = std::move(show_transactions)] {
|
callback.fn = [interpreter_context, show_transactions = std::move(show_transactions)] {
|
||||||
@ -3372,9 +3412,10 @@ Callback HandleTransactionQueueQuery(TransactionQueueQuery *transaction_query,
|
|||||||
return std::string(expression->Accept(evaluator).ValueString());
|
return std::string(expression->Accept(evaluator).ValueString());
|
||||||
});
|
});
|
||||||
callback.header = {"transaction_id", "killed"};
|
callback.header = {"transaction_id", "killed"};
|
||||||
callback.fn = [interpreter_context, maybe_kill_transaction_ids = std::move(maybe_kill_transaction_ids), username,
|
callback.fn = [interpreter_context, maybe_kill_transaction_ids = std::move(maybe_kill_transaction_ids),
|
||||||
|
user_or_role = std::move(user_or_role),
|
||||||
privilege_checker = std::move(privilege_checker)]() mutable {
|
privilege_checker = std::move(privilege_checker)]() mutable {
|
||||||
return interpreter_context->TerminateTransactions(std::move(maybe_kill_transaction_ids), username,
|
return interpreter_context->TerminateTransactions(std::move(maybe_kill_transaction_ids), user_or_role.get(),
|
||||||
std::move(privilege_checker));
|
std::move(privilege_checker));
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
@ -3384,12 +3425,12 @@ Callback HandleTransactionQueueQuery(TransactionQueueQuery *transaction_query,
|
|||||||
return callback;
|
return callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
PreparedQuery PrepareTransactionQueueQuery(ParsedQuery parsed_query, const std::optional<std::string> &username,
|
PreparedQuery PrepareTransactionQueueQuery(ParsedQuery parsed_query, std::shared_ptr<QueryUserOrRole> user_or_role,
|
||||||
InterpreterContext *interpreter_context) {
|
InterpreterContext *interpreter_context) {
|
||||||
auto *transaction_queue_query = utils::Downcast<TransactionQueueQuery>(parsed_query.query);
|
auto *transaction_queue_query = utils::Downcast<TransactionQueueQuery>(parsed_query.query);
|
||||||
MG_ASSERT(transaction_queue_query);
|
MG_ASSERT(transaction_queue_query);
|
||||||
auto callback =
|
auto callback = HandleTransactionQueueQuery(transaction_queue_query, std::move(user_or_role), parsed_query.parameters,
|
||||||
HandleTransactionQueueQuery(transaction_queue_query, username, parsed_query.parameters, interpreter_context);
|
interpreter_context);
|
||||||
|
|
||||||
return PreparedQuery{std::move(callback.header), std::move(parsed_query.required_privileges),
|
return PreparedQuery{std::move(callback.header), std::move(parsed_query.required_privileges),
|
||||||
[callback_fn = std::move(callback.fn), pull_plan = std::shared_ptr<PullPlanVector>{nullptr}](
|
[callback_fn = std::move(callback.fn), pull_plan = std::shared_ptr<PullPlanVector>{nullptr}](
|
||||||
@ -4022,7 +4063,7 @@ PreparedQuery PrepareMultiDatabaseQuery(ParsedQuery parsed_query, CurrentDB &cur
|
|||||||
}
|
}
|
||||||
|
|
||||||
PreparedQuery PrepareShowDatabasesQuery(ParsedQuery parsed_query, InterpreterContext *interpreter_context,
|
PreparedQuery PrepareShowDatabasesQuery(ParsedQuery parsed_query, InterpreterContext *interpreter_context,
|
||||||
const std::optional<std::string> &username) {
|
std::shared_ptr<QueryUserOrRole> user_or_role) {
|
||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
if (!license::global_license_checker.IsEnterpriseValidFast()) {
|
if (!license::global_license_checker.IsEnterpriseValidFast()) {
|
||||||
throw QueryException("Trying to use enterprise feature without a valid license.");
|
throw QueryException("Trying to use enterprise feature without a valid license.");
|
||||||
@ -4033,7 +4074,8 @@ PreparedQuery PrepareShowDatabasesQuery(ParsedQuery parsed_query, InterpreterCon
|
|||||||
|
|
||||||
Callback callback;
|
Callback callback;
|
||||||
callback.header = {"Name"};
|
callback.header = {"Name"};
|
||||||
callback.fn = [auth, db_handler, username]() mutable -> std::vector<std::vector<TypedValue>> {
|
callback.fn = [auth, db_handler,
|
||||||
|
user_or_role = std::move(user_or_role)]() mutable -> std::vector<std::vector<TypedValue>> {
|
||||||
std::vector<std::vector<TypedValue>> status;
|
std::vector<std::vector<TypedValue>> status;
|
||||||
auto gen_status = [&]<typename T, typename K>(T all, K denied) {
|
auto gen_status = [&]<typename T, typename K>(T all, K denied) {
|
||||||
Sort(all);
|
Sort(all);
|
||||||
@ -4055,12 +4097,12 @@ PreparedQuery PrepareShowDatabasesQuery(ParsedQuery parsed_query, InterpreterCon
|
|||||||
status.erase(iter, status.end());
|
status.erase(iter, status.end());
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!username) {
|
if (!user_or_role || !*user_or_role) {
|
||||||
// No user, return all
|
// No user, return all
|
||||||
gen_status(db_handler->All(), std::vector<TypedValue>{});
|
gen_status(db_handler->All(), std::vector<TypedValue>{});
|
||||||
} else {
|
} else {
|
||||||
// User has a subset of accessible dbs; this is synched with the SessionContextHandler
|
// User has a subset of accessible dbs; this is synched with the SessionContextHandler
|
||||||
const auto &db_priv = auth->GetDatabasePrivileges(*username);
|
const auto &db_priv = auth->GetDatabasePrivileges(user_or_role->key());
|
||||||
const auto &allowed = db_priv[0][0];
|
const auto &allowed = db_priv[0][0];
|
||||||
const auto &denied = db_priv[0][1].ValueList();
|
const auto &denied = db_priv[0][1].ValueList();
|
||||||
if (allowed.IsString() && allowed.ValueString() == auth::kAllDatabases) {
|
if (allowed.IsString() && allowed.ValueString() == auth::kAllDatabases) {
|
||||||
@ -4128,6 +4170,7 @@ void Interpreter::SetCurrentDB(std::string_view db_name, bool in_explicit_db) {
|
|||||||
Interpreter::PrepareResult Interpreter::Prepare(const std::string &query_string,
|
Interpreter::PrepareResult Interpreter::Prepare(const std::string &query_string,
|
||||||
const std::map<std::string, storage::PropertyValue> ¶ms,
|
const std::map<std::string, storage::PropertyValue> ¶ms,
|
||||||
QueryExtras const &extras) {
|
QueryExtras const &extras) {
|
||||||
|
MG_ASSERT(user_or_role_, "Trying to prepare a query without a query user.");
|
||||||
// Handle transaction control queries.
|
// Handle transaction control queries.
|
||||||
const auto upper_case_query = utils::ToUpperCase(query_string);
|
const auto upper_case_query = utils::ToUpperCase(query_string);
|
||||||
const auto trimmed_query = utils::Trim(upper_case_query);
|
const auto trimmed_query = utils::Trim(upper_case_query);
|
||||||
@ -4270,7 +4313,7 @@ Interpreter::PrepareResult Interpreter::Prepare(const std::string &query_string,
|
|||||||
frame_change_collector_.emplace();
|
frame_change_collector_.emplace();
|
||||||
if (utils::Downcast<CypherQuery>(parsed_query.query)) {
|
if (utils::Downcast<CypherQuery>(parsed_query.query)) {
|
||||||
prepared_query = PrepareCypherQuery(std::move(parsed_query), &query_execution->summary, interpreter_context_,
|
prepared_query = PrepareCypherQuery(std::move(parsed_query), &query_execution->summary, interpreter_context_,
|
||||||
current_db_, memory_resource, &query_execution->notifications, username_,
|
current_db_, memory_resource, &query_execution->notifications, user_or_role_,
|
||||||
&transaction_status_, current_timeout_timer_, &*frame_change_collector_);
|
&transaction_status_, current_timeout_timer_, &*frame_change_collector_);
|
||||||
} else if (utils::Downcast<ExplainQuery>(parsed_query.query)) {
|
} else if (utils::Downcast<ExplainQuery>(parsed_query.query)) {
|
||||||
prepared_query = PrepareExplainQuery(std::move(parsed_query), &query_execution->summary,
|
prepared_query = PrepareExplainQuery(std::move(parsed_query), &query_execution->summary,
|
||||||
@ -4278,7 +4321,7 @@ Interpreter::PrepareResult Interpreter::Prepare(const std::string &query_string,
|
|||||||
} else if (utils::Downcast<ProfileQuery>(parsed_query.query)) {
|
} else if (utils::Downcast<ProfileQuery>(parsed_query.query)) {
|
||||||
prepared_query = PrepareProfileQuery(std::move(parsed_query), in_explicit_transaction_, &query_execution->summary,
|
prepared_query = PrepareProfileQuery(std::move(parsed_query), in_explicit_transaction_, &query_execution->summary,
|
||||||
&query_execution->notifications, interpreter_context_, current_db_,
|
&query_execution->notifications, interpreter_context_, current_db_,
|
||||||
&query_execution->execution_memory_with_exception, username_,
|
&query_execution->execution_memory_with_exception, user_or_role_,
|
||||||
&transaction_status_, current_timeout_timer_, &*frame_change_collector_);
|
&transaction_status_, current_timeout_timer_, &*frame_change_collector_);
|
||||||
} else if (utils::Downcast<DumpQuery>(parsed_query.query)) {
|
} else if (utils::Downcast<DumpQuery>(parsed_query.query)) {
|
||||||
prepared_query = PrepareDumpQuery(std::move(parsed_query), current_db_);
|
prepared_query = PrepareDumpQuery(std::move(parsed_query), current_db_);
|
||||||
@ -4322,11 +4365,11 @@ Interpreter::PrepareResult Interpreter::Prepare(const std::string &query_string,
|
|||||||
} else if (utils::Downcast<TriggerQuery>(parsed_query.query)) {
|
} else if (utils::Downcast<TriggerQuery>(parsed_query.query)) {
|
||||||
prepared_query =
|
prepared_query =
|
||||||
PrepareTriggerQuery(std::move(parsed_query), in_explicit_transaction_, &query_execution->notifications,
|
PrepareTriggerQuery(std::move(parsed_query), in_explicit_transaction_, &query_execution->notifications,
|
||||||
current_db_, interpreter_context_, params, username_);
|
current_db_, interpreter_context_, params, user_or_role_);
|
||||||
} else if (utils::Downcast<StreamQuery>(parsed_query.query)) {
|
} else if (utils::Downcast<StreamQuery>(parsed_query.query)) {
|
||||||
prepared_query =
|
prepared_query =
|
||||||
PrepareStreamQuery(std::move(parsed_query), in_explicit_transaction_, &query_execution->notifications,
|
PrepareStreamQuery(std::move(parsed_query), in_explicit_transaction_, &query_execution->notifications,
|
||||||
current_db_, interpreter_context_, username_);
|
current_db_, interpreter_context_, user_or_role_);
|
||||||
} else if (utils::Downcast<IsolationLevelQuery>(parsed_query.query)) {
|
} else if (utils::Downcast<IsolationLevelQuery>(parsed_query.query)) {
|
||||||
prepared_query = PrepareIsolationLevelQuery(std::move(parsed_query), in_explicit_transaction_, current_db_, this);
|
prepared_query = PrepareIsolationLevelQuery(std::move(parsed_query), in_explicit_transaction_, current_db_, this);
|
||||||
} else if (utils::Downcast<CreateSnapshotQuery>(parsed_query.query)) {
|
} else if (utils::Downcast<CreateSnapshotQuery>(parsed_query.query)) {
|
||||||
@ -4347,7 +4390,7 @@ Interpreter::PrepareResult Interpreter::Prepare(const std::string &query_string,
|
|||||||
if (in_explicit_transaction_) {
|
if (in_explicit_transaction_) {
|
||||||
throw TransactionQueueInMulticommandTxException();
|
throw TransactionQueueInMulticommandTxException();
|
||||||
}
|
}
|
||||||
prepared_query = PrepareTransactionQueueQuery(std::move(parsed_query), username_, interpreter_context_);
|
prepared_query = PrepareTransactionQueueQuery(std::move(parsed_query), user_or_role_, interpreter_context_);
|
||||||
} else if (utils::Downcast<MultiDatabaseQuery>(parsed_query.query)) {
|
} else if (utils::Downcast<MultiDatabaseQuery>(parsed_query.query)) {
|
||||||
if (in_explicit_transaction_) {
|
if (in_explicit_transaction_) {
|
||||||
throw MultiDatabaseQueryInMulticommandTxException();
|
throw MultiDatabaseQueryInMulticommandTxException();
|
||||||
@ -4357,7 +4400,7 @@ Interpreter::PrepareResult Interpreter::Prepare(const std::string &query_string,
|
|||||||
prepared_query =
|
prepared_query =
|
||||||
PrepareMultiDatabaseQuery(std::move(parsed_query), current_db_, interpreter_context_, on_change_, *this);
|
PrepareMultiDatabaseQuery(std::move(parsed_query), current_db_, interpreter_context_, on_change_, *this);
|
||||||
} else if (utils::Downcast<ShowDatabasesQuery>(parsed_query.query)) {
|
} else if (utils::Downcast<ShowDatabasesQuery>(parsed_query.query)) {
|
||||||
prepared_query = PrepareShowDatabasesQuery(std::move(parsed_query), interpreter_context_, username_);
|
prepared_query = PrepareShowDatabasesQuery(std::move(parsed_query), interpreter_context_, user_or_role_);
|
||||||
} else if (utils::Downcast<EdgeImportModeQuery>(parsed_query.query)) {
|
} else if (utils::Downcast<EdgeImportModeQuery>(parsed_query.query)) {
|
||||||
if (in_explicit_transaction_) {
|
if (in_explicit_transaction_) {
|
||||||
throw EdgeImportModeModificationInMulticommandTxException();
|
throw EdgeImportModeModificationInMulticommandTxException();
|
||||||
@ -4433,6 +4476,12 @@ std::vector<TypedValue> Interpreter::GetQueries() {
|
|||||||
|
|
||||||
void Interpreter::Abort() {
|
void Interpreter::Abort() {
|
||||||
bool decrement = true;
|
bool decrement = true;
|
||||||
|
|
||||||
|
// System tx
|
||||||
|
// TODO Implement system transaction scope and the ability to abort
|
||||||
|
system_transaction_.reset();
|
||||||
|
|
||||||
|
// Data tx
|
||||||
auto expected = TransactionStatus::ACTIVE;
|
auto expected = TransactionStatus::ACTIVE;
|
||||||
while (!transaction_status_.compare_exchange_weak(expected, TransactionStatus::STARTED_ROLLBACK)) {
|
while (!transaction_status_.compare_exchange_weak(expected, TransactionStatus::STARTED_ROLLBACK)) {
|
||||||
if (expected == TransactionStatus::TERMINATED || expected == TransactionStatus::IDLE) {
|
if (expected == TransactionStatus::TERMINATED || expected == TransactionStatus::IDLE) {
|
||||||
@ -4484,8 +4533,7 @@ void RunTriggersAfterCommit(dbms::DatabaseAccess db_acc, InterpreterContext *int
|
|||||||
trigger_context.AdaptForAccessor(&db_accessor);
|
trigger_context.AdaptForAccessor(&db_accessor);
|
||||||
try {
|
try {
|
||||||
trigger.Execute(&db_accessor, &execution_memory, flags::run_time::GetExecutionTimeout(),
|
trigger.Execute(&db_accessor, &execution_memory, flags::run_time::GetExecutionTimeout(),
|
||||||
&interpreter_context->is_shutting_down, transaction_status, trigger_context,
|
&interpreter_context->is_shutting_down, transaction_status, trigger_context);
|
||||||
interpreter_context->auth_checker);
|
|
||||||
} catch (const utils::BasicException &exception) {
|
} catch (const utils::BasicException &exception) {
|
||||||
spdlog::warn("Trigger '{}' failed with exception:\n{}", trigger.Name(), exception.what());
|
spdlog::warn("Trigger '{}' failed with exception:\n{}", trigger.Name(), exception.what());
|
||||||
db_accessor.Abort();
|
db_accessor.Abort();
|
||||||
@ -4642,8 +4690,7 @@ void Interpreter::Commit() {
|
|||||||
AdvanceCommand();
|
AdvanceCommand();
|
||||||
try {
|
try {
|
||||||
trigger.Execute(&*current_db_.execution_db_accessor_, &execution_memory, flags::run_time::GetExecutionTimeout(),
|
trigger.Execute(&*current_db_.execution_db_accessor_, &execution_memory, flags::run_time::GetExecutionTimeout(),
|
||||||
&interpreter_context_->is_shutting_down, &transaction_status_, *trigger_context,
|
&interpreter_context_->is_shutting_down, &transaction_status_, *trigger_context);
|
||||||
interpreter_context_->auth_checker);
|
|
||||||
} catch (const utils::BasicException &e) {
|
} catch (const utils::BasicException &e) {
|
||||||
throw utils::BasicException(
|
throw utils::BasicException(
|
||||||
fmt::format("Trigger '{}' caused the transaction to fail.\nException: {}", trigger.Name(), e.what()));
|
fmt::format("Trigger '{}' caused the transaction to fail.\nException: {}", trigger.Name(), e.what()));
|
||||||
@ -4758,7 +4805,7 @@ void Interpreter::SetNextTransactionIsolationLevel(const storage::IsolationLevel
|
|||||||
void Interpreter::SetSessionIsolationLevel(const storage::IsolationLevel isolation_level) {
|
void Interpreter::SetSessionIsolationLevel(const storage::IsolationLevel isolation_level) {
|
||||||
interpreter_isolation_level.emplace(isolation_level);
|
interpreter_isolation_level.emplace(isolation_level);
|
||||||
}
|
}
|
||||||
void Interpreter::ResetUser() { username_.reset(); }
|
void Interpreter::ResetUser() { user_or_role_.reset(); }
|
||||||
void Interpreter::SetUser(std::string_view username) { username_ = username; }
|
void Interpreter::SetUser(std::shared_ptr<QueryUserOrRole> user_or_role) { user_or_role_ = std::move(user_or_role); }
|
||||||
|
|
||||||
} // namespace memgraph::query
|
} // namespace memgraph::query
|
||||||
|
@ -210,7 +210,7 @@ class Interpreter final {
|
|||||||
std::optional<std::string> db;
|
std::optional<std::string> db;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::optional<std::string> username_;
|
std::shared_ptr<QueryUserOrRole> user_or_role_{};
|
||||||
bool in_explicit_transaction_{false};
|
bool in_explicit_transaction_{false};
|
||||||
CurrentDB current_db_;
|
CurrentDB current_db_;
|
||||||
|
|
||||||
@ -300,7 +300,7 @@ class Interpreter final {
|
|||||||
|
|
||||||
void ResetUser();
|
void ResetUser();
|
||||||
|
|
||||||
void SetUser(std::string_view username);
|
void SetUser(std::shared_ptr<QueryUserOrRole> user);
|
||||||
|
|
||||||
std::optional<memgraph::system::Transaction> system_transaction_{};
|
std::optional<memgraph::system::Transaction> system_transaction_{};
|
||||||
|
|
||||||
|
@ -35,13 +35,13 @@ InterpreterContext::InterpreterContext(InterpreterConfig interpreter_config, dbm
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::vector<TypedValue>> InterpreterContext::TerminateTransactions(
|
std::vector<std::vector<TypedValue>> InterpreterContext::TerminateTransactions(
|
||||||
std::vector<std::string> maybe_kill_transaction_ids, const std::optional<std::string> &username,
|
std::vector<std::string> maybe_kill_transaction_ids, QueryUserOrRole *user_or_role,
|
||||||
std::function<bool(std::string const &)> privilege_checker) {
|
std::function<bool(QueryUserOrRole *, std::string const &)> privilege_checker) {
|
||||||
auto not_found_midpoint = maybe_kill_transaction_ids.end();
|
auto not_found_midpoint = maybe_kill_transaction_ids.end();
|
||||||
|
|
||||||
// Multiple simultaneous TERMINATE TRANSACTIONS aren't allowed
|
// Multiple simultaneous TERMINATE TRANSACTIONS aren't allowed
|
||||||
// TERMINATE and SHOW TRANSACTIONS are mutually exclusive
|
// TERMINATE and SHOW TRANSACTIONS are mutually exclusive
|
||||||
interpreters.WithLock([¬_found_midpoint, &maybe_kill_transaction_ids, username,
|
interpreters.WithLock([¬_found_midpoint, &maybe_kill_transaction_ids, user_or_role,
|
||||||
privilege_checker = std::move(privilege_checker)](const auto &interpreters) {
|
privilege_checker = std::move(privilege_checker)](const auto &interpreters) {
|
||||||
for (Interpreter *interpreter : interpreters) {
|
for (Interpreter *interpreter : interpreters) {
|
||||||
TransactionStatus alive_status = TransactionStatus::ACTIVE;
|
TransactionStatus alive_status = TransactionStatus::ACTIVE;
|
||||||
@ -73,7 +73,15 @@ std::vector<std::vector<TypedValue>> InterpreterContext::TerminateTransactions(
|
|||||||
static std::string all;
|
static std::string all;
|
||||||
return interpreter->current_db_.db_acc_ ? interpreter->current_db_.db_acc_->get()->name() : all;
|
return interpreter->current_db_.db_acc_ ? interpreter->current_db_.db_acc_->get()->name() : all;
|
||||||
};
|
};
|
||||||
if (interpreter->username_ == username || privilege_checker(get_interpreter_db_name())) {
|
|
||||||
|
auto same_user = [](const auto &lv, const auto &rv) {
|
||||||
|
if (lv.get() == rv) return true;
|
||||||
|
if (lv && rv) return *lv == *rv;
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (same_user(interpreter->user_or_role_, user_or_role) ||
|
||||||
|
privilege_checker(user_or_role, get_interpreter_db_name())) {
|
||||||
killed = true; // Note: this is used by the above `clean_status` (OnScopeExit)
|
killed = true; // Note: this is used by the above `clean_status` (OnScopeExit)
|
||||||
spdlog::warn("Transaction {} successfully killed", transaction_id);
|
spdlog::warn("Transaction {} successfully killed", transaction_id);
|
||||||
} else {
|
} else {
|
||||||
|
@ -46,6 +46,7 @@ constexpr uint64_t kInterpreterTransactionInitialId = 1ULL << 63U;
|
|||||||
class AuthQueryHandler;
|
class AuthQueryHandler;
|
||||||
class AuthChecker;
|
class AuthChecker;
|
||||||
class Interpreter;
|
class Interpreter;
|
||||||
|
struct QueryUserOrRole;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds data shared between multiple `Interpreter` instances (which might be
|
* Holds data shared between multiple `Interpreter` instances (which might be
|
||||||
@ -95,8 +96,8 @@ struct InterpreterContext {
|
|||||||
void Shutdown() { is_shutting_down.store(true, std::memory_order_release); }
|
void Shutdown() { is_shutting_down.store(true, std::memory_order_release); }
|
||||||
|
|
||||||
std::vector<std::vector<TypedValue>> TerminateTransactions(
|
std::vector<std::vector<TypedValue>> TerminateTransactions(
|
||||||
std::vector<std::string> maybe_kill_transaction_ids, const std::optional<std::string> &username,
|
std::vector<std::string> maybe_kill_transaction_ids, QueryUserOrRole *user_or_role,
|
||||||
std::function<bool(std::string const &)> privilege_checker);
|
std::function<bool(QueryUserOrRole *, std::string const &)> privilege_checker);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace memgraph::query
|
} // namespace memgraph::query
|
||||||
|
21
src/query/query_user.cpp
Normal file
21
src/query/query_user.cpp
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
// 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 "query/query_user.hpp"
|
||||||
|
|
||||||
|
namespace memgraph::query {
|
||||||
|
// The variables below are used to define a user auth policy.
|
||||||
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||||
|
SessionLongPolicy session_long_policy;
|
||||||
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||||
|
UpToDatePolicy up_to_date_policy;
|
||||||
|
|
||||||
|
} // namespace memgraph::query
|
61
src/query/query_user.hpp
Normal file
61
src/query/query_user.hpp
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "query/frontend/ast/ast.hpp"
|
||||||
|
|
||||||
|
namespace memgraph::query {
|
||||||
|
|
||||||
|
class UserPolicy {
|
||||||
|
public:
|
||||||
|
virtual bool DoUpdate() const = 0;
|
||||||
|
};
|
||||||
|
extern struct SessionLongPolicy : UserPolicy {
|
||||||
|
public:
|
||||||
|
bool DoUpdate() const override { return false; }
|
||||||
|
} session_long_policy;
|
||||||
|
extern struct UpToDatePolicy : UserPolicy {
|
||||||
|
public:
|
||||||
|
bool DoUpdate() const override { return true; }
|
||||||
|
} up_to_date_policy;
|
||||||
|
|
||||||
|
struct QueryUserOrRole {
|
||||||
|
QueryUserOrRole(std::optional<std::string> username, std::optional<std::string> rolename)
|
||||||
|
: username_{std::move(username)}, rolename_{std::move(rolename)} {}
|
||||||
|
virtual ~QueryUserOrRole() = default;
|
||||||
|
|
||||||
|
virtual bool IsAuthorized(const std::vector<AuthQuery::Privilege> &privileges, const std::string &db_name,
|
||||||
|
UserPolicy *policy) const = 0;
|
||||||
|
|
||||||
|
#ifdef MG_ENTERPRISE
|
||||||
|
virtual std::string GetDefaultDB() const = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
std::string key() const {
|
||||||
|
// NOTE: Each role has an associated username, that's why we check it with higher priority
|
||||||
|
return rolename_ ? *rolename_ : (username_ ? *username_ : "");
|
||||||
|
}
|
||||||
|
const std::optional<std::string> &username() const { return username_; }
|
||||||
|
const std::optional<std::string> &rolename() const { return rolename_; }
|
||||||
|
|
||||||
|
bool operator==(const QueryUserOrRole &other) const = default;
|
||||||
|
operator bool() const { return username_.has_value(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::optional<std::string> username_;
|
||||||
|
std::optional<std::string> rolename_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace memgraph::query
|
@ -29,6 +29,7 @@
|
|||||||
#include "query/procedure/mg_procedure_helpers.hpp"
|
#include "query/procedure/mg_procedure_helpers.hpp"
|
||||||
#include "query/procedure/mg_procedure_impl.hpp"
|
#include "query/procedure/mg_procedure_impl.hpp"
|
||||||
#include "query/procedure/module.hpp"
|
#include "query/procedure/module.hpp"
|
||||||
|
#include "query/query_user.hpp"
|
||||||
#include "query/stream/sources.hpp"
|
#include "query/stream/sources.hpp"
|
||||||
#include "query/typed_value.hpp"
|
#include "query/typed_value.hpp"
|
||||||
#include "utils/event_counter.hpp"
|
#include "utils/event_counter.hpp"
|
||||||
@ -131,6 +132,7 @@ StreamStatus<TStream> CreateStatus(std::string stream_name, std::string transfor
|
|||||||
const std::string kStreamName{"name"};
|
const std::string kStreamName{"name"};
|
||||||
const std::string kIsRunningKey{"is_running"};
|
const std::string kIsRunningKey{"is_running"};
|
||||||
const std::string kOwner{"owner"};
|
const std::string kOwner{"owner"};
|
||||||
|
const std::string kOwnerRole{"owner_role"};
|
||||||
const std::string kType{"type"};
|
const std::string kType{"type"};
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
@ -142,6 +144,11 @@ void to_json(nlohmann::json &data, StreamStatus<TStream> &&status) {
|
|||||||
|
|
||||||
if (status.owner.has_value()) {
|
if (status.owner.has_value()) {
|
||||||
data[kOwner] = std::move(*status.owner);
|
data[kOwner] = std::move(*status.owner);
|
||||||
|
if (status.owner_role.has_value()) {
|
||||||
|
data[kOwnerRole] = std::move(*status.owner_role);
|
||||||
|
} else {
|
||||||
|
data[kOwnerRole] = nullptr;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
data[kOwner] = nullptr;
|
data[kOwner] = nullptr;
|
||||||
}
|
}
|
||||||
@ -156,6 +163,11 @@ void from_json(const nlohmann::json &data, StreamStatus<TStream> &status) {
|
|||||||
|
|
||||||
if (const auto &owner = data.at(kOwner); !owner.is_null()) {
|
if (const auto &owner = data.at(kOwner); !owner.is_null()) {
|
||||||
status.owner = owner.get<typename decltype(status.owner)::value_type>();
|
status.owner = owner.get<typename decltype(status.owner)::value_type>();
|
||||||
|
if (const auto &owner_role = data.at(kOwnerRole); !owner_role.is_null()) {
|
||||||
|
owner_role.get_to(status.owner_role);
|
||||||
|
} else {
|
||||||
|
status.owner_role = {};
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
status.owner = {};
|
status.owner = {};
|
||||||
}
|
}
|
||||||
@ -449,7 +461,7 @@ void Streams::RegisterPulsarProcedures() {
|
|||||||
|
|
||||||
template <Stream TStream, typename TDbAccess>
|
template <Stream TStream, typename TDbAccess>
|
||||||
void Streams::Create(const std::string &stream_name, typename TStream::StreamInfo info,
|
void Streams::Create(const std::string &stream_name, typename TStream::StreamInfo info,
|
||||||
std::optional<std::string> owner, TDbAccess db_acc, InterpreterContext *ic) {
|
std::shared_ptr<QueryUserOrRole> owner, TDbAccess db_acc, InterpreterContext *ic) {
|
||||||
auto locked_streams = streams_.Lock();
|
auto locked_streams = streams_.Lock();
|
||||||
auto it = CreateConsumer<TStream, TDbAccess>(*locked_streams, stream_name, std::move(info), std::move(owner),
|
auto it = CreateConsumer<TStream, TDbAccess>(*locked_streams, stream_name, std::move(info), std::move(owner),
|
||||||
std::move(db_acc), ic);
|
std::move(db_acc), ic);
|
||||||
@ -469,31 +481,39 @@ void Streams::Create(const std::string &stream_name, typename TStream::StreamInf
|
|||||||
|
|
||||||
template void Streams::Create<KafkaStream, dbms::DatabaseAccess>(const std::string &stream_name,
|
template void Streams::Create<KafkaStream, dbms::DatabaseAccess>(const std::string &stream_name,
|
||||||
KafkaStream::StreamInfo info,
|
KafkaStream::StreamInfo info,
|
||||||
std::optional<std::string> owner,
|
std::shared_ptr<QueryUserOrRole> owner,
|
||||||
dbms::DatabaseAccess db, InterpreterContext *ic);
|
dbms::DatabaseAccess db, InterpreterContext *ic);
|
||||||
template void Streams::Create<PulsarStream, dbms::DatabaseAccess>(const std::string &stream_name,
|
template void Streams::Create<PulsarStream, dbms::DatabaseAccess>(const std::string &stream_name,
|
||||||
PulsarStream::StreamInfo info,
|
PulsarStream::StreamInfo info,
|
||||||
std::optional<std::string> owner,
|
std::shared_ptr<QueryUserOrRole> owner,
|
||||||
dbms::DatabaseAccess db, InterpreterContext *ic);
|
dbms::DatabaseAccess db, InterpreterContext *ic);
|
||||||
|
|
||||||
template <Stream TStream, typename TDbAccess>
|
template <Stream TStream, typename TDbAccess>
|
||||||
Streams::StreamsMap::iterator Streams::CreateConsumer(StreamsMap &map, const std::string &stream_name,
|
Streams::StreamsMap::iterator Streams::CreateConsumer(StreamsMap &map, const std::string &stream_name,
|
||||||
typename TStream::StreamInfo stream_info,
|
typename TStream::StreamInfo stream_info,
|
||||||
std::optional<std::string> owner, TDbAccess db_acc,
|
std::shared_ptr<QueryUserOrRole> owner, TDbAccess db_acc,
|
||||||
InterpreterContext *interpreter_context) {
|
InterpreterContext *interpreter_context) {
|
||||||
if (map.contains(stream_name)) {
|
if (map.contains(stream_name)) {
|
||||||
throw StreamsException{"Stream already exists with name '{}'", stream_name};
|
throw StreamsException{"Stream already exists with name '{}'", stream_name};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto ownername = owner->username();
|
||||||
|
auto rolename = owner->rolename();
|
||||||
|
|
||||||
auto *memory_resource = utils::NewDeleteResource();
|
auto *memory_resource = utils::NewDeleteResource();
|
||||||
|
|
||||||
auto consumer_function = [interpreter_context, memory_resource, stream_name,
|
auto consumer_function = [interpreter_context, memory_resource, stream_name,
|
||||||
transformation_name = stream_info.common_info.transformation_name, owner = owner,
|
transformation_name = stream_info.common_info.transformation_name, owner = std::move(owner),
|
||||||
interpreter = std::make_shared<Interpreter>(interpreter_context, std::move(db_acc)),
|
interpreter = std::make_shared<Interpreter>(interpreter_context, std::move(db_acc)),
|
||||||
result = mgp_result{nullptr, memory_resource},
|
result = mgp_result{nullptr, memory_resource},
|
||||||
total_retries = interpreter_context->config.stream_transaction_conflict_retries,
|
total_retries = interpreter_context->config.stream_transaction_conflict_retries,
|
||||||
retry_interval = interpreter_context->config.stream_transaction_retry_interval](
|
retry_interval = interpreter_context->config.stream_transaction_retry_interval](
|
||||||
const std::vector<typename TStream::Message> &messages) mutable {
|
const std::vector<typename TStream::Message> &messages) mutable {
|
||||||
|
// Set interpreter's user to the stream owner
|
||||||
|
// NOTE: We generate an empty user to avoid generating interpreter's fine grained access control and rely only on
|
||||||
|
// the global auth_checker used in the stream itself
|
||||||
|
// TODO: Fix auth inconsistency
|
||||||
|
interpreter->SetUser(interpreter_context->auth_checker->GenQueryUser(std::nullopt, std::nullopt));
|
||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
interpreter->OnChangeCB([](auto) { return false; }); // Disable database change
|
interpreter->OnChangeCB([](auto) { return false; }); // Disable database change
|
||||||
#endif
|
#endif
|
||||||
@ -523,12 +543,11 @@ Streams::StreamsMap::iterator Streams::CreateConsumer(StreamsMap &map, const std
|
|||||||
spdlog::trace("Processing row in stream '{}'", stream_name);
|
spdlog::trace("Processing row in stream '{}'", stream_name);
|
||||||
auto [query_value, params_value] = ExtractTransformationResult(row.values, transformation_name, stream_name);
|
auto [query_value, params_value] = ExtractTransformationResult(row.values, transformation_name, stream_name);
|
||||||
storage::PropertyValue params_prop{params_value};
|
storage::PropertyValue params_prop{params_value};
|
||||||
|
|
||||||
std::string query{query_value.ValueString()};
|
std::string query{query_value.ValueString()};
|
||||||
spdlog::trace("Executing query '{}' in stream '{}'", query, stream_name);
|
spdlog::trace("Executing query '{}' in stream '{}'", query, stream_name);
|
||||||
auto prepare_result =
|
auto prepare_result =
|
||||||
interpreter->Prepare(query, params_prop.IsNull() ? empty_parameters : params_prop.ValueMap(), {});
|
interpreter->Prepare(query, params_prop.IsNull() ? empty_parameters : params_prop.ValueMap(), {});
|
||||||
if (!interpreter_context->auth_checker->IsUserAuthorized(owner, prepare_result.privileges, "")) {
|
if (!owner->IsAuthorized(prepare_result.privileges, "", &up_to_date_policy)) {
|
||||||
throw StreamsException{
|
throw StreamsException{
|
||||||
"Couldn't execute query '{}' for stream '{}' because the owner is not authorized to execute the "
|
"Couldn't execute query '{}' for stream '{}' because the owner is not authorized to execute the "
|
||||||
"query!",
|
"query!",
|
||||||
@ -553,7 +572,8 @@ Streams::StreamsMap::iterator Streams::CreateConsumer(StreamsMap &map, const std
|
|||||||
};
|
};
|
||||||
|
|
||||||
auto insert_result = map.try_emplace(
|
auto insert_result = map.try_emplace(
|
||||||
stream_name, StreamData<TStream>{std::move(stream_info.common_info.transformation_name), std::move(owner),
|
stream_name, StreamData<TStream>{std::move(stream_info.common_info.transformation_name), std::move(ownername),
|
||||||
|
std::move(rolename),
|
||||||
std::make_unique<SynchronizedStreamSource<TStream>>(
|
std::make_unique<SynchronizedStreamSource<TStream>>(
|
||||||
stream_name, std::move(stream_info), std::move(consumer_function))});
|
stream_name, std::move(stream_info), std::move(consumer_function))});
|
||||||
MG_ASSERT(insert_result.second, "Unexpected error during storing consumer '{}'", stream_name);
|
MG_ASSERT(insert_result.second, "Unexpected error during storing consumer '{}'", stream_name);
|
||||||
@ -575,6 +595,7 @@ void Streams::RestoreStreams(TDbAccess db, InterpreterContext *ic) {
|
|||||||
const auto create_consumer = [&, &stream_name = stream_name]<typename T>(StreamStatus<T> status,
|
const auto create_consumer = [&, &stream_name = stream_name]<typename T>(StreamStatus<T> status,
|
||||||
auto &&stream_json_data) {
|
auto &&stream_json_data) {
|
||||||
try {
|
try {
|
||||||
|
// TODO: Migration
|
||||||
stream_json_data.get_to(status);
|
stream_json_data.get_to(status);
|
||||||
} catch (const nlohmann::json::type_error &exception) {
|
} catch (const nlohmann::json::type_error &exception) {
|
||||||
spdlog::warn(get_failed_message("invalid type conversion", exception.what()));
|
spdlog::warn(get_failed_message("invalid type conversion", exception.what()));
|
||||||
@ -586,8 +607,8 @@ void Streams::RestoreStreams(TDbAccess db, InterpreterContext *ic) {
|
|||||||
MG_ASSERT(status.name == stream_name, "Expected stream name is '{}', but got '{}'", status.name, stream_name);
|
MG_ASSERT(status.name == stream_name, "Expected stream name is '{}', but got '{}'", status.name, stream_name);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
auto it = CreateConsumer<T>(*locked_streams_map, stream_name, std::move(status.info), std::move(status.owner),
|
auto owner = ic->auth_checker->GenQueryUser(status.owner, status.owner_role);
|
||||||
db, ic);
|
auto it = CreateConsumer<T>(*locked_streams_map, stream_name, std::move(status.info), std::move(owner), db, ic);
|
||||||
if (status.is_running) {
|
if (status.is_running) {
|
||||||
std::visit(
|
std::visit(
|
||||||
[&](const auto &stream_data) {
|
[&](const auto &stream_data) {
|
||||||
@ -745,7 +766,7 @@ std::vector<StreamStatus<>> Streams::GetStreamInfo() const {
|
|||||||
auto info = locked_stream_source->Info(stream_data.transformation_name);
|
auto info = locked_stream_source->Info(stream_data.transformation_name);
|
||||||
result.emplace_back(StreamStatus<>{stream_name, StreamType(*locked_stream_source),
|
result.emplace_back(StreamStatus<>{stream_name, StreamType(*locked_stream_source),
|
||||||
locked_stream_source->IsRunning(), std::move(info.common_info),
|
locked_stream_source->IsRunning(), std::move(info.common_info),
|
||||||
stream_data.owner});
|
stream_data.owner, stream_data.owner_role});
|
||||||
},
|
},
|
||||||
stream_data);
|
stream_data);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2023 Memgraph Ltd.
|
// Copyright 2024 Memgraph Ltd.
|
||||||
//
|
//
|
||||||
// Use of this software is governed by the Business Source License
|
// 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
|
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||||
@ -67,6 +67,7 @@ struct StreamStatus {
|
|||||||
bool is_running;
|
bool is_running;
|
||||||
StreamInfoType<T> info;
|
StreamInfoType<T> info;
|
||||||
std::optional<std::string> owner;
|
std::optional<std::string> owner;
|
||||||
|
std::optional<std::string> owner_role;
|
||||||
};
|
};
|
||||||
|
|
||||||
using TransformationResult = std::vector<std::vector<TypedValue>>;
|
using TransformationResult = std::vector<std::vector<TypedValue>>;
|
||||||
@ -100,7 +101,7 @@ class Streams final {
|
|||||||
///
|
///
|
||||||
/// @throws StreamsException if the stream with the same name exists or if the creation of Kafka consumer fails
|
/// @throws StreamsException if the stream with the same name exists or if the creation of Kafka consumer fails
|
||||||
template <Stream TStream, typename TDbAccess>
|
template <Stream TStream, typename TDbAccess>
|
||||||
void Create(const std::string &stream_name, typename TStream::StreamInfo info, std::optional<std::string> owner,
|
void Create(const std::string &stream_name, typename TStream::StreamInfo info, std::shared_ptr<QueryUserOrRole> owner,
|
||||||
TDbAccess db, InterpreterContext *interpreter_context);
|
TDbAccess db, InterpreterContext *interpreter_context);
|
||||||
|
|
||||||
/// Deletes an existing stream and all the data that was persisted.
|
/// Deletes an existing stream and all the data that was persisted.
|
||||||
@ -182,6 +183,7 @@ class Streams final {
|
|||||||
struct StreamData {
|
struct StreamData {
|
||||||
std::string transformation_name;
|
std::string transformation_name;
|
||||||
std::optional<std::string> owner;
|
std::optional<std::string> owner;
|
||||||
|
std::optional<std::string> owner_role;
|
||||||
std::unique_ptr<SynchronizedStreamSource<TStream>> stream_source;
|
std::unique_ptr<SynchronizedStreamSource<TStream>> stream_source;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -191,7 +193,7 @@ class Streams final {
|
|||||||
|
|
||||||
template <Stream TStream, typename TDbAccess>
|
template <Stream TStream, typename TDbAccess>
|
||||||
StreamsMap::iterator CreateConsumer(StreamsMap &map, const std::string &stream_name,
|
StreamsMap::iterator CreateConsumer(StreamsMap &map, const std::string &stream_name,
|
||||||
typename TStream::StreamInfo stream_info, std::optional<std::string> owner,
|
typename TStream::StreamInfo stream_info, std::shared_ptr<QueryUserOrRole> owner,
|
||||||
TDbAccess db, InterpreterContext *interpreter_context);
|
TDbAccess db, InterpreterContext *interpreter_context);
|
||||||
|
|
||||||
template <Stream TStream>
|
template <Stream TStream>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2023 Memgraph Ltd.
|
// Copyright 2024 Memgraph Ltd.
|
||||||
//
|
//
|
||||||
// Use of this software is governed by the Business Source License
|
// 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
|
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||||
@ -11,14 +11,13 @@
|
|||||||
|
|
||||||
#include "query/trigger.hpp"
|
#include "query/trigger.hpp"
|
||||||
|
|
||||||
#include <concepts>
|
|
||||||
|
|
||||||
#include "query/config.hpp"
|
#include "query/config.hpp"
|
||||||
#include "query/context.hpp"
|
#include "query/context.hpp"
|
||||||
#include "query/cypher_query_interpreter.hpp"
|
#include "query/cypher_query_interpreter.hpp"
|
||||||
#include "query/db_accessor.hpp"
|
#include "query/db_accessor.hpp"
|
||||||
#include "query/frontend/ast/ast.hpp"
|
#include "query/frontend/ast/ast.hpp"
|
||||||
#include "query/interpret/frame.hpp"
|
#include "query/interpret/frame.hpp"
|
||||||
|
#include "query/query_user.hpp"
|
||||||
#include "query/serialization/property_value.hpp"
|
#include "query/serialization/property_value.hpp"
|
||||||
#include "query/typed_value.hpp"
|
#include "query/typed_value.hpp"
|
||||||
#include "storage/v2/property_value.hpp"
|
#include "storage/v2/property_value.hpp"
|
||||||
@ -154,20 +153,19 @@ Trigger::Trigger(std::string name, const std::string &query,
|
|||||||
const std::map<std::string, storage::PropertyValue> &user_parameters,
|
const std::map<std::string, storage::PropertyValue> &user_parameters,
|
||||||
const TriggerEventType event_type, utils::SkipList<QueryCacheEntry> *query_cache,
|
const TriggerEventType event_type, utils::SkipList<QueryCacheEntry> *query_cache,
|
||||||
DbAccessor *db_accessor, const InterpreterConfig::Query &query_config,
|
DbAccessor *db_accessor, const InterpreterConfig::Query &query_config,
|
||||||
std::optional<std::string> owner, const query::AuthChecker *auth_checker)
|
std::shared_ptr<QueryUserOrRole> owner)
|
||||||
: name_{std::move(name)},
|
: name_{std::move(name)},
|
||||||
parsed_statements_{ParseQuery(query, user_parameters, query_cache, query_config)},
|
parsed_statements_{ParseQuery(query, user_parameters, query_cache, query_config)},
|
||||||
event_type_{event_type},
|
event_type_{event_type},
|
||||||
owner_{std::move(owner)} {
|
owner_{std::move(owner)} {
|
||||||
// We check immediately if the query is valid by trying to create a plan.
|
// We check immediately if the query is valid by trying to create a plan.
|
||||||
GetPlan(db_accessor, auth_checker);
|
GetPlan(db_accessor);
|
||||||
}
|
}
|
||||||
|
|
||||||
Trigger::TriggerPlan::TriggerPlan(std::unique_ptr<LogicalPlan> logical_plan, std::vector<IdentifierInfo> identifiers)
|
Trigger::TriggerPlan::TriggerPlan(std::unique_ptr<LogicalPlan> logical_plan, std::vector<IdentifierInfo> identifiers)
|
||||||
: cached_plan(std::move(logical_plan)), identifiers(std::move(identifiers)) {}
|
: cached_plan(std::move(logical_plan)), identifiers(std::move(identifiers)) {}
|
||||||
|
|
||||||
std::shared_ptr<Trigger::TriggerPlan> Trigger::GetPlan(DbAccessor *db_accessor,
|
std::shared_ptr<Trigger::TriggerPlan> Trigger::GetPlan(DbAccessor *db_accessor) const {
|
||||||
const query::AuthChecker *auth_checker) const {
|
|
||||||
std::lock_guard plan_guard{plan_lock_};
|
std::lock_guard plan_guard{plan_lock_};
|
||||||
if (!parsed_statements_.is_cacheable || !trigger_plan_) {
|
if (!parsed_statements_.is_cacheable || !trigger_plan_) {
|
||||||
auto identifiers = GetPredefinedIdentifiers(event_type_);
|
auto identifiers = GetPredefinedIdentifiers(event_type_);
|
||||||
@ -187,7 +185,7 @@ std::shared_ptr<Trigger::TriggerPlan> Trigger::GetPlan(DbAccessor *db_accessor,
|
|||||||
|
|
||||||
trigger_plan_ = std::make_shared<TriggerPlan>(std::move(logical_plan), std::move(identifiers));
|
trigger_plan_ = std::make_shared<TriggerPlan>(std::move(logical_plan), std::move(identifiers));
|
||||||
}
|
}
|
||||||
if (!auth_checker->IsUserAuthorized(owner_, parsed_statements_.required_privileges, "")) {
|
if (!owner_->IsAuthorized(parsed_statements_.required_privileges, "", &up_to_date_policy)) {
|
||||||
throw utils::BasicException("The owner of trigger '{}' is not authorized to execute the query!", name_);
|
throw utils::BasicException("The owner of trigger '{}' is not authorized to execute the query!", name_);
|
||||||
}
|
}
|
||||||
return trigger_plan_;
|
return trigger_plan_;
|
||||||
@ -195,14 +193,13 @@ std::shared_ptr<Trigger::TriggerPlan> Trigger::GetPlan(DbAccessor *db_accessor,
|
|||||||
|
|
||||||
void Trigger::Execute(DbAccessor *dba, utils::MonotonicBufferResource *execution_memory,
|
void Trigger::Execute(DbAccessor *dba, utils::MonotonicBufferResource *execution_memory,
|
||||||
const double max_execution_time_sec, std::atomic<bool> *is_shutting_down,
|
const double max_execution_time_sec, std::atomic<bool> *is_shutting_down,
|
||||||
std::atomic<TransactionStatus> *transaction_status, const TriggerContext &context,
|
std::atomic<TransactionStatus> *transaction_status, const TriggerContext &context) const {
|
||||||
const AuthChecker *auth_checker) const {
|
|
||||||
if (!context.ShouldEventTrigger(event_type_)) {
|
if (!context.ShouldEventTrigger(event_type_)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
spdlog::debug("Executing trigger '{}'", name_);
|
spdlog::debug("Executing trigger '{}'", name_);
|
||||||
auto trigger_plan = GetPlan(dba, auth_checker);
|
auto trigger_plan = GetPlan(dba);
|
||||||
MG_ASSERT(trigger_plan, "Invalid trigger plan received");
|
MG_ASSERT(trigger_plan, "Invalid trigger plan received");
|
||||||
auto &[plan, identifiers] = *trigger_plan;
|
auto &[plan, identifiers] = *trigger_plan;
|
||||||
|
|
||||||
@ -308,6 +305,7 @@ void TriggerStore::RestoreTriggers(utils::SkipList<QueryCacheEntry> *query_cache
|
|||||||
}
|
}
|
||||||
const auto user_parameters = serialization::DeserializePropertyValueMap(json_trigger_data["user_parameters"]);
|
const auto user_parameters = serialization::DeserializePropertyValueMap(json_trigger_data["user_parameters"]);
|
||||||
|
|
||||||
|
// TODO: Migration
|
||||||
const auto owner_json = json_trigger_data["owner"];
|
const auto owner_json = json_trigger_data["owner"];
|
||||||
std::optional<std::string> owner{};
|
std::optional<std::string> owner{};
|
||||||
if (owner_json.is_string()) {
|
if (owner_json.is_string()) {
|
||||||
@ -317,10 +315,21 @@ void TriggerStore::RestoreTriggers(utils::SkipList<QueryCacheEntry> *query_cache
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto owner_role_json = json_trigger_data["owner_role"];
|
||||||
|
std::optional<std::string> role{};
|
||||||
|
if (owner_role_json.is_string()) {
|
||||||
|
owner.emplace(owner_role_json.get<std::string>());
|
||||||
|
} else if (!owner_role_json.is_null()) {
|
||||||
|
spdlog::warn(invalid_state_message);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto user = auth_checker->GenQueryUser(owner, role);
|
||||||
|
|
||||||
std::optional<Trigger> trigger;
|
std::optional<Trigger> trigger;
|
||||||
try {
|
try {
|
||||||
trigger.emplace(trigger_name, statement, user_parameters, event_type, query_cache, db_accessor, query_config,
|
trigger.emplace(trigger_name, statement, user_parameters, event_type, query_cache, db_accessor, query_config,
|
||||||
std::move(owner), auth_checker);
|
std::move(user));
|
||||||
} catch (const utils::BasicException &e) {
|
} catch (const utils::BasicException &e) {
|
||||||
spdlog::warn("Failed to create trigger '{}' because: {}", trigger_name, e.what());
|
spdlog::warn("Failed to create trigger '{}' because: {}", trigger_name, e.what());
|
||||||
continue;
|
continue;
|
||||||
@ -338,8 +347,7 @@ void TriggerStore::AddTrigger(std::string name, const std::string &query,
|
|||||||
const std::map<std::string, storage::PropertyValue> &user_parameters,
|
const std::map<std::string, storage::PropertyValue> &user_parameters,
|
||||||
TriggerEventType event_type, TriggerPhase phase,
|
TriggerEventType event_type, TriggerPhase phase,
|
||||||
utils::SkipList<QueryCacheEntry> *query_cache, DbAccessor *db_accessor,
|
utils::SkipList<QueryCacheEntry> *query_cache, DbAccessor *db_accessor,
|
||||||
const InterpreterConfig::Query &query_config, std::optional<std::string> owner,
|
const InterpreterConfig::Query &query_config, std::shared_ptr<QueryUserOrRole> owner) {
|
||||||
const query::AuthChecker *auth_checker) {
|
|
||||||
std::unique_lock store_guard{store_lock_};
|
std::unique_lock store_guard{store_lock_};
|
||||||
if (storage_.Get(name)) {
|
if (storage_.Get(name)) {
|
||||||
throw utils::BasicException("Trigger with the same name already exists.");
|
throw utils::BasicException("Trigger with the same name already exists.");
|
||||||
@ -348,7 +356,7 @@ void TriggerStore::AddTrigger(std::string name, const std::string &query,
|
|||||||
std::optional<Trigger> trigger;
|
std::optional<Trigger> trigger;
|
||||||
try {
|
try {
|
||||||
trigger.emplace(std::move(name), query, user_parameters, event_type, query_cache, db_accessor, query_config,
|
trigger.emplace(std::move(name), query, user_parameters, event_type, query_cache, db_accessor, query_config,
|
||||||
std::move(owner), auth_checker);
|
std::move(owner));
|
||||||
} catch (const utils::BasicException &e) {
|
} catch (const utils::BasicException &e) {
|
||||||
const auto identifiers = GetPredefinedIdentifiers(event_type);
|
const auto identifiers = GetPredefinedIdentifiers(event_type);
|
||||||
std::stringstream identifier_names_stream;
|
std::stringstream identifier_names_stream;
|
||||||
@ -370,10 +378,23 @@ void TriggerStore::AddTrigger(std::string name, const std::string &query,
|
|||||||
data["phase"] = phase;
|
data["phase"] = phase;
|
||||||
data["version"] = kVersion;
|
data["version"] = kVersion;
|
||||||
|
|
||||||
if (const auto &owner_from_trigger = trigger->Owner(); owner_from_trigger.has_value()) {
|
if (const auto &owner_from_trigger = trigger->Owner(); owner_from_trigger && *owner_from_trigger) {
|
||||||
data["owner"] = *owner_from_trigger;
|
const auto &maybe_username = owner_from_trigger->username();
|
||||||
|
if (maybe_username) {
|
||||||
|
data["owner"] = *maybe_username;
|
||||||
|
// Roles need to be associated with a username
|
||||||
|
const auto &maybe_rolename = owner_from_trigger->rolename();
|
||||||
|
if (maybe_rolename) {
|
||||||
|
data["owner_role"] = *maybe_rolename;
|
||||||
|
} else {
|
||||||
|
data["owner_role"] = nullptr;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
data["owner"] = nullptr;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
data["owner"] = nullptr;
|
data["owner"] = nullptr;
|
||||||
|
data["owner_role"] = nullptr;
|
||||||
}
|
}
|
||||||
storage_.Put(trigger->Name(), data.dump());
|
storage_.Put(trigger->Name(), data.dump());
|
||||||
store_guard.unlock();
|
store_guard.unlock();
|
||||||
@ -417,7 +438,9 @@ std::vector<TriggerStore::TriggerInfo> TriggerStore::GetTriggerInfo() const {
|
|||||||
|
|
||||||
const auto add_info = [&](const utils::SkipList<Trigger> &trigger_list, const TriggerPhase phase) {
|
const auto add_info = [&](const utils::SkipList<Trigger> &trigger_list, const TriggerPhase phase) {
|
||||||
for (const auto &trigger : trigger_list.access()) {
|
for (const auto &trigger : trigger_list.access()) {
|
||||||
info.push_back({trigger.Name(), trigger.OriginalStatement(), trigger.EventType(), phase, trigger.Owner()});
|
std::optional<std::string> owner_str{};
|
||||||
|
if (const auto &owner = trigger.Owner(); owner && *owner) owner_str = owner->username();
|
||||||
|
info.push_back({trigger.Name(), trigger.OriginalStatement(), trigger.EventType(), phase, std::move(owner_str)});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2023 Memgraph Ltd.
|
// Copyright 2024 Memgraph Ltd.
|
||||||
//
|
//
|
||||||
// Use of this software is governed by the Business Source License
|
// 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
|
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||||
@ -37,12 +37,11 @@ struct Trigger {
|
|||||||
explicit Trigger(std::string name, const std::string &query,
|
explicit Trigger(std::string name, const std::string &query,
|
||||||
const std::map<std::string, storage::PropertyValue> &user_parameters, TriggerEventType event_type,
|
const std::map<std::string, storage::PropertyValue> &user_parameters, TriggerEventType event_type,
|
||||||
utils::SkipList<QueryCacheEntry> *query_cache, DbAccessor *db_accessor,
|
utils::SkipList<QueryCacheEntry> *query_cache, DbAccessor *db_accessor,
|
||||||
const InterpreterConfig::Query &query_config, std::optional<std::string> owner,
|
const InterpreterConfig::Query &query_config, std::shared_ptr<QueryUserOrRole> owner);
|
||||||
const query::AuthChecker *auth_checker);
|
|
||||||
|
|
||||||
void Execute(DbAccessor *dba, utils::MonotonicBufferResource *execution_memory, double max_execution_time_sec,
|
void Execute(DbAccessor *dba, utils::MonotonicBufferResource *execution_memory, double max_execution_time_sec,
|
||||||
std::atomic<bool> *is_shutting_down, std::atomic<TransactionStatus> *transaction_status,
|
std::atomic<bool> *is_shutting_down, std::atomic<TransactionStatus> *transaction_status,
|
||||||
const TriggerContext &context, const AuthChecker *auth_checker) const;
|
const TriggerContext &context) const;
|
||||||
|
|
||||||
bool operator==(const Trigger &other) const { return name_ == other.name_; }
|
bool operator==(const Trigger &other) const { return name_ == other.name_; }
|
||||||
// NOLINTNEXTLINE (modernize-use-nullptr)
|
// NOLINTNEXTLINE (modernize-use-nullptr)
|
||||||
@ -65,7 +64,7 @@ struct Trigger {
|
|||||||
PlanWrapper cached_plan;
|
PlanWrapper cached_plan;
|
||||||
std::vector<IdentifierInfo> identifiers;
|
std::vector<IdentifierInfo> identifiers;
|
||||||
};
|
};
|
||||||
std::shared_ptr<TriggerPlan> GetPlan(DbAccessor *db_accessor, const query::AuthChecker *auth_checker) const;
|
std::shared_ptr<TriggerPlan> GetPlan(DbAccessor *db_accessor) const;
|
||||||
|
|
||||||
std::string name_;
|
std::string name_;
|
||||||
ParsedQuery parsed_statements_;
|
ParsedQuery parsed_statements_;
|
||||||
@ -74,7 +73,7 @@ struct Trigger {
|
|||||||
|
|
||||||
mutable utils::SpinLock plan_lock_;
|
mutable utils::SpinLock plan_lock_;
|
||||||
mutable std::shared_ptr<TriggerPlan> trigger_plan_;
|
mutable std::shared_ptr<TriggerPlan> trigger_plan_;
|
||||||
std::optional<std::string> owner_;
|
std::shared_ptr<QueryUserOrRole> owner_;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class TriggerPhase : uint8_t { BEFORE_COMMIT, AFTER_COMMIT };
|
enum class TriggerPhase : uint8_t { BEFORE_COMMIT, AFTER_COMMIT };
|
||||||
@ -88,8 +87,7 @@ struct TriggerStore {
|
|||||||
void AddTrigger(std::string name, const std::string &query,
|
void AddTrigger(std::string name, const std::string &query,
|
||||||
const std::map<std::string, storage::PropertyValue> &user_parameters, TriggerEventType event_type,
|
const std::map<std::string, storage::PropertyValue> &user_parameters, TriggerEventType event_type,
|
||||||
TriggerPhase phase, utils::SkipList<QueryCacheEntry> *query_cache, DbAccessor *db_accessor,
|
TriggerPhase phase, utils::SkipList<QueryCacheEntry> *query_cache, DbAccessor *db_accessor,
|
||||||
const InterpreterConfig::Query &query_config, std::optional<std::string> owner,
|
const InterpreterConfig::Query &query_config, std::shared_ptr<QueryUserOrRole> owner);
|
||||||
const query::AuthChecker *auth_checker);
|
|
||||||
|
|
||||||
void DropTrigger(const std::string &name);
|
void DropTrigger(const std::string &name);
|
||||||
|
|
||||||
|
@ -103,7 +103,8 @@ void RecoverReplication(memgraph::replication::ReplicationState &repl_state,
|
|||||||
|
|
||||||
inline std::optional<query::RegisterReplicaError> HandleRegisterReplicaStatus(
|
inline std::optional<query::RegisterReplicaError> HandleRegisterReplicaStatus(
|
||||||
utils::BasicResult<replication::RegisterReplicaError, replication::ReplicationClient *> &instance_client) {
|
utils::BasicResult<replication::RegisterReplicaError, replication::ReplicationClient *> &instance_client) {
|
||||||
if (instance_client.HasError()) switch (instance_client.GetError()) {
|
if (instance_client.HasError()) {
|
||||||
|
switch (instance_client.GetError()) {
|
||||||
case replication::RegisterReplicaError::NOT_MAIN:
|
case replication::RegisterReplicaError::NOT_MAIN:
|
||||||
MG_ASSERT(false, "Only main instance can register a replica!");
|
MG_ASSERT(false, "Only main instance can register a replica!");
|
||||||
return {};
|
return {};
|
||||||
@ -116,6 +117,7 @@ inline std::optional<query::RegisterReplicaError> HandleRegisterReplicaStatus(
|
|||||||
case replication::RegisterReplicaError::SUCCESS:
|
case replication::RegisterReplicaError::SUCCESS:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@ std::filesystem::path data_directory{std::filesystem::temp_directory_path() / "e
|
|||||||
class ExpansionBenchFixture : public benchmark::Fixture {
|
class ExpansionBenchFixture : public benchmark::Fixture {
|
||||||
protected:
|
protected:
|
||||||
std::optional<memgraph::system::System> system;
|
std::optional<memgraph::system::System> system;
|
||||||
|
std::optional<memgraph::query::AllowEverythingAuthChecker> auth_checker;
|
||||||
std::optional<memgraph::query::InterpreterContext> interpreter_context;
|
std::optional<memgraph::query::InterpreterContext> interpreter_context;
|
||||||
std::optional<memgraph::query::Interpreter> interpreter;
|
std::optional<memgraph::query::Interpreter> interpreter;
|
||||||
std::optional<memgraph::utils::Gatekeeper<memgraph::dbms::Database>> db_gk;
|
std::optional<memgraph::utils::Gatekeeper<memgraph::dbms::Database>> db_gk;
|
||||||
@ -43,6 +44,7 @@ class ExpansionBenchFixture : public benchmark::Fixture {
|
|||||||
auto &db_acc = *db_acc_opt;
|
auto &db_acc = *db_acc_opt;
|
||||||
|
|
||||||
system.emplace();
|
system.emplace();
|
||||||
|
auth_checker.emplace();
|
||||||
interpreter_context.emplace(memgraph::query::InterpreterConfig{}, nullptr, &repl_state.value(), *system
|
interpreter_context.emplace(memgraph::query::InterpreterConfig{}, nullptr, &repl_state.value(), *system
|
||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
,
|
,
|
||||||
@ -73,13 +75,15 @@ class ExpansionBenchFixture : public benchmark::Fixture {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interpreter.emplace(&*interpreter_context, std::move(db_acc));
|
interpreter.emplace(&*interpreter_context, std::move(db_acc));
|
||||||
|
interpreter->SetUser(auth_checker->GenQueryUser(std::nullopt, std::nullopt));
|
||||||
}
|
}
|
||||||
|
|
||||||
void TearDown(const benchmark::State &) override {
|
void TearDown(const benchmark::State &) override {
|
||||||
interpreter = std::nullopt;
|
interpreter = std::nullopt;
|
||||||
interpreter_context = std::nullopt;
|
interpreter_context = std::nullopt;
|
||||||
system.reset();
|
|
||||||
db_gk.reset();
|
db_gk.reset();
|
||||||
|
auth_checker.reset();
|
||||||
|
system.reset();
|
||||||
std::filesystem::remove_all(data_directory);
|
std::filesystem::remove_all(data_directory);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -14,14 +14,7 @@
|
|||||||
# If you wish to modify these, update the startup_config_dict and workloads.yaml !
|
# If you wish to modify these, update the startup_config_dict and workloads.yaml !
|
||||||
|
|
||||||
startup_config_dict = {
|
startup_config_dict = {
|
||||||
"auth_module_create_missing_role": ("true", "true", "Set to false to disable creation of missing roles."),
|
|
||||||
"auth_module_create_missing_user": ("true", "true", "Set to false to disable creation of missing users."),
|
|
||||||
"auth_module_executable": ("", "", "Absolute path to the auth module executable that should be used."),
|
"auth_module_executable": ("", "", "Absolute path to the auth module executable that should be used."),
|
||||||
"auth_module_manage_roles": (
|
|
||||||
"true",
|
|
||||||
"true",
|
|
||||||
"Set to false to disable management of roles through the auth module.",
|
|
||||||
),
|
|
||||||
"auth_module_timeout_ms": (
|
"auth_module_timeout_ms": (
|
||||||
"10000",
|
"10000",
|
||||||
"10000",
|
"10000",
|
||||||
|
@ -19,10 +19,10 @@ from mgclient import DatabaseError
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_create_node_all_labels_granted(switch):
|
def test_create_node_all_labels_granted(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.reset_and_prepare(admin_connection.cursor())
|
common.reset_and_prepare(admin_connection.cursor())
|
||||||
common.create_multi_db(admin_connection.cursor(), switch)
|
common.create_multi_db(admin_connection.cursor(), switch)
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS * TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
results = common.execute_and_fetch_all(user_connection.cursor(), "CREATE (n:label1) RETURN n;")
|
results = common.execute_and_fetch_all(user_connection.cursor(), "CREATE (n:label1) RETURN n;")
|
||||||
@ -33,10 +33,10 @@ def test_create_node_all_labels_granted(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_create_node_all_labels_denied(switch):
|
def test_create_node_all_labels_denied(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.reset_and_prepare(admin_connection.cursor())
|
common.reset_and_prepare(admin_connection.cursor())
|
||||||
common.create_multi_db(admin_connection.cursor(), switch)
|
common.create_multi_db(admin_connection.cursor(), switch)
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT UPDATE ON LABELS * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT UPDATE ON LABELS * TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -47,10 +47,10 @@ def test_create_node_all_labels_denied(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_create_node_specific_label_granted(switch):
|
def test_create_node_specific_label_granted(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.reset_and_prepare(admin_connection.cursor())
|
common.reset_and_prepare(admin_connection.cursor())
|
||||||
common.create_multi_db(admin_connection.cursor(), switch)
|
common.create_multi_db(admin_connection.cursor(), switch)
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS :label1 TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS :label1 TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
results = common.execute_and_fetch_all(user_connection.cursor(), "CREATE (n:label1) RETURN n;")
|
results = common.execute_and_fetch_all(user_connection.cursor(), "CREATE (n:label1) RETURN n;")
|
||||||
@ -61,10 +61,10 @@ def test_create_node_specific_label_granted(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_create_node_specific_label_denied(switch):
|
def test_create_node_specific_label_denied(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.reset_and_prepare(admin_connection.cursor())
|
common.reset_and_prepare(admin_connection.cursor())
|
||||||
common.create_multi_db(admin_connection.cursor(), switch)
|
common.create_multi_db(admin_connection.cursor(), switch)
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT UPDATE ON LABELS :label1 TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT UPDATE ON LABELS :label1 TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -75,10 +75,10 @@ def test_create_node_specific_label_denied(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_delete_node_all_labels_granted(switch):
|
def test_delete_node_all_labels_granted(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.reset_and_prepare(admin_connection.cursor())
|
common.reset_and_prepare(admin_connection.cursor())
|
||||||
common.create_multi_db(admin_connection.cursor(), switch)
|
common.create_multi_db(admin_connection.cursor(), switch)
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS * TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
common.execute_and_fetch_all(user_connection.cursor(), "MATCH (n:test_delete) DELETE n;")
|
common.execute_and_fetch_all(user_connection.cursor(), "MATCH (n:test_delete) DELETE n;")
|
||||||
@ -91,10 +91,10 @@ def test_delete_node_all_labels_granted(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_delete_node_all_labels_denied(switch):
|
def test_delete_node_all_labels_denied(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.reset_and_prepare(admin_connection.cursor())
|
common.reset_and_prepare(admin_connection.cursor())
|
||||||
common.create_multi_db(admin_connection.cursor(), switch)
|
common.create_multi_db(admin_connection.cursor(), switch)
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT UPDATE ON LABELS * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT UPDATE ON LABELS * TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -105,10 +105,10 @@ def test_delete_node_all_labels_denied(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_delete_node_specific_label_granted(switch):
|
def test_delete_node_specific_label_granted(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.reset_and_prepare(admin_connection.cursor())
|
common.reset_and_prepare(admin_connection.cursor())
|
||||||
common.create_multi_db(admin_connection.cursor(), switch)
|
common.create_multi_db(admin_connection.cursor(), switch)
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS :test_delete TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS :test_delete TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
results = common.execute_and_fetch_all(user_connection.cursor(), "MATCH (n:test_delete) DELETE n;")
|
results = common.execute_and_fetch_all(user_connection.cursor(), "MATCH (n:test_delete) DELETE n;")
|
||||||
@ -123,10 +123,10 @@ def test_delete_node_specific_label_granted(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_delete_node_specific_label_denied(switch):
|
def test_delete_node_specific_label_denied(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.reset_and_prepare(admin_connection.cursor())
|
common.reset_and_prepare(admin_connection.cursor())
|
||||||
common.create_multi_db(admin_connection.cursor(), switch)
|
common.create_multi_db(admin_connection.cursor(), switch)
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT UPDATE ON LABELS :test_delete TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT UPDATE ON LABELS :test_delete TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -137,11 +137,11 @@ def test_delete_node_specific_label_denied(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_create_edge_all_labels_all_edge_types_granted(switch):
|
def test_create_edge_all_labels_all_edge_types_granted(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.reset_and_prepare(admin_connection.cursor())
|
common.reset_and_prepare(admin_connection.cursor())
|
||||||
common.create_multi_db(admin_connection.cursor(), switch)
|
common.create_multi_db(admin_connection.cursor(), switch)
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS * TO user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON EDGE_TYPES * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON EDGE_TYPES * TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -156,11 +156,11 @@ def test_create_edge_all_labels_all_edge_types_granted(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_create_edge_all_labels_all_edge_types_denied(switch):
|
def test_create_edge_all_labels_all_edge_types_denied(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.reset_and_prepare(admin_connection.cursor())
|
common.reset_and_prepare(admin_connection.cursor())
|
||||||
common.create_multi_db(admin_connection.cursor(), switch)
|
common.create_multi_db(admin_connection.cursor(), switch)
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT UPDATE ON LABELS * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT UPDATE ON LABELS * TO user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT UPDATE ON EDGE_TYPES * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT UPDATE ON EDGE_TYPES * TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -174,11 +174,11 @@ def test_create_edge_all_labels_all_edge_types_denied(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_create_edge_all_labels_denied_all_edge_types_granted(switch):
|
def test_create_edge_all_labels_denied_all_edge_types_granted(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.reset_and_prepare(admin_connection.cursor())
|
common.reset_and_prepare(admin_connection.cursor())
|
||||||
common.create_multi_db(admin_connection.cursor(), switch)
|
common.create_multi_db(admin_connection.cursor(), switch)
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT UPDATE ON LABELS * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT UPDATE ON LABELS * TO user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT UPDATE ON EDGE_TYPES * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT UPDATE ON EDGE_TYPES * TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -192,11 +192,11 @@ def test_create_edge_all_labels_denied_all_edge_types_granted(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_create_edge_all_labels_granted_all_edge_types_denied(switch):
|
def test_create_edge_all_labels_granted_all_edge_types_denied(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.reset_and_prepare(admin_connection.cursor())
|
common.reset_and_prepare(admin_connection.cursor())
|
||||||
common.create_multi_db(admin_connection.cursor(), switch)
|
common.create_multi_db(admin_connection.cursor(), switch)
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS * TO user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT UPDATE ON EDGE_TYPES * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT UPDATE ON EDGE_TYPES * TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -210,7 +210,6 @@ def test_create_edge_all_labels_granted_all_edge_types_denied(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_create_edge_all_labels_granted_specific_edge_types_denied(switch):
|
def test_create_edge_all_labels_granted_specific_edge_types_denied(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.reset_and_prepare(admin_connection.cursor())
|
common.reset_and_prepare(admin_connection.cursor())
|
||||||
common.create_multi_db(admin_connection.cursor(), switch)
|
common.create_multi_db(admin_connection.cursor(), switch)
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS * TO user;")
|
||||||
@ -218,6 +217,7 @@ def test_create_edge_all_labels_granted_specific_edge_types_denied(switch):
|
|||||||
admin_connection.cursor(),
|
admin_connection.cursor(),
|
||||||
"GRANT UPDATE ON EDGE_TYPES :edge_type TO user;",
|
"GRANT UPDATE ON EDGE_TYPES :edge_type TO user;",
|
||||||
)
|
)
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -231,7 +231,6 @@ def test_create_edge_all_labels_granted_specific_edge_types_denied(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_create_edge_first_node_label_granted(switch):
|
def test_create_edge_first_node_label_granted(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.reset_and_prepare(admin_connection.cursor())
|
common.reset_and_prepare(admin_connection.cursor())
|
||||||
common.create_multi_db(admin_connection.cursor(), switch)
|
common.create_multi_db(admin_connection.cursor(), switch)
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS :label1 TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS :label1 TO user;")
|
||||||
@ -240,6 +239,7 @@ def test_create_edge_first_node_label_granted(switch):
|
|||||||
admin_connection.cursor(),
|
admin_connection.cursor(),
|
||||||
"GRANT CREATE_DELETE ON EDGE_TYPES :edge_type TO user;",
|
"GRANT CREATE_DELETE ON EDGE_TYPES :edge_type TO user;",
|
||||||
)
|
)
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -253,7 +253,6 @@ def test_create_edge_first_node_label_granted(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_create_edge_second_node_label_granted(switch):
|
def test_create_edge_second_node_label_granted(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.reset_and_prepare(admin_connection.cursor())
|
common.reset_and_prepare(admin_connection.cursor())
|
||||||
common.create_multi_db(admin_connection.cursor(), switch)
|
common.create_multi_db(admin_connection.cursor(), switch)
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS :label2 TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS :label2 TO user;")
|
||||||
@ -262,6 +261,7 @@ def test_create_edge_second_node_label_granted(switch):
|
|||||||
admin_connection.cursor(),
|
admin_connection.cursor(),
|
||||||
"GRANT CREATE_DELETE ON EDGE_TYPES :edge_type TO user;",
|
"GRANT CREATE_DELETE ON EDGE_TYPES :edge_type TO user;",
|
||||||
)
|
)
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -275,11 +275,11 @@ def test_create_edge_second_node_label_granted(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_delete_edge_all_labels_denied_all_edge_types_granted(switch):
|
def test_delete_edge_all_labels_denied_all_edge_types_granted(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.reset_and_prepare(admin_connection.cursor())
|
common.reset_and_prepare(admin_connection.cursor())
|
||||||
common.create_multi_db(admin_connection.cursor(), switch)
|
common.create_multi_db(admin_connection.cursor(), switch)
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON LABELS * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON LABELS * TO user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON EDGE_TYPES * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON EDGE_TYPES * TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -293,11 +293,11 @@ def test_delete_edge_all_labels_denied_all_edge_types_granted(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_delete_edge_all_labels_granted_all_edge_types_denied(switch):
|
def test_delete_edge_all_labels_granted_all_edge_types_denied(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.reset_and_prepare(admin_connection.cursor())
|
common.reset_and_prepare(admin_connection.cursor())
|
||||||
common.create_multi_db(admin_connection.cursor(), switch)
|
common.create_multi_db(admin_connection.cursor(), switch)
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS * TO user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT UPDATE ON EDGE_TYPES * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT UPDATE ON EDGE_TYPES * TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -311,7 +311,6 @@ def test_delete_edge_all_labels_granted_all_edge_types_denied(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_delete_edge_all_labels_granted_specific_edge_types_denied(switch):
|
def test_delete_edge_all_labels_granted_specific_edge_types_denied(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.reset_and_prepare(admin_connection.cursor())
|
common.reset_and_prepare(admin_connection.cursor())
|
||||||
common.create_multi_db(admin_connection.cursor(), switch)
|
common.create_multi_db(admin_connection.cursor(), switch)
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS * TO user;")
|
||||||
@ -319,6 +318,7 @@ def test_delete_edge_all_labels_granted_specific_edge_types_denied(switch):
|
|||||||
admin_connection.cursor(),
|
admin_connection.cursor(),
|
||||||
"GRANT UPDATE ON EDGE_TYPES :edge_type_delete TO user;",
|
"GRANT UPDATE ON EDGE_TYPES :edge_type_delete TO user;",
|
||||||
)
|
)
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -332,7 +332,6 @@ def test_delete_edge_all_labels_granted_specific_edge_types_denied(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_delete_edge_first_node_label_granted(switch):
|
def test_delete_edge_first_node_label_granted(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.reset_and_prepare(admin_connection.cursor())
|
common.reset_and_prepare(admin_connection.cursor())
|
||||||
common.create_multi_db(admin_connection.cursor(), switch)
|
common.create_multi_db(admin_connection.cursor(), switch)
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT UPDATE ON LABELS :test_delete_1 TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT UPDATE ON LABELS :test_delete_1 TO user;")
|
||||||
@ -341,6 +340,7 @@ def test_delete_edge_first_node_label_granted(switch):
|
|||||||
admin_connection.cursor(),
|
admin_connection.cursor(),
|
||||||
"GRANT CREATE_DELETE ON EDGE_TYPES :edge_type_delete TO user;",
|
"GRANT CREATE_DELETE ON EDGE_TYPES :edge_type_delete TO user;",
|
||||||
)
|
)
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -354,7 +354,6 @@ def test_delete_edge_first_node_label_granted(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_delete_edge_second_node_label_granted(switch):
|
def test_delete_edge_second_node_label_granted(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.reset_and_prepare(admin_connection.cursor())
|
common.reset_and_prepare(admin_connection.cursor())
|
||||||
common.create_multi_db(admin_connection.cursor(), switch)
|
common.create_multi_db(admin_connection.cursor(), switch)
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT UPDATE ON LABELS :test_delete_2 TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT UPDATE ON LABELS :test_delete_2 TO user;")
|
||||||
@ -363,6 +362,7 @@ def test_delete_edge_second_node_label_granted(switch):
|
|||||||
admin_connection.cursor(),
|
admin_connection.cursor(),
|
||||||
"GRANT CREATE_DELETE ON EDGE_TYPES :edge_type_delete TO user;",
|
"GRANT CREATE_DELETE ON EDGE_TYPES :edge_type_delete TO user;",
|
||||||
)
|
)
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -376,13 +376,13 @@ def test_delete_edge_second_node_label_granted(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_delete_node_with_edge_label_denied(switch):
|
def test_delete_node_with_edge_label_denied(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.reset_and_prepare(admin_connection.cursor())
|
common.reset_and_prepare(admin_connection.cursor())
|
||||||
common.create_multi_db(admin_connection.cursor(), switch)
|
common.create_multi_db(admin_connection.cursor(), switch)
|
||||||
common.execute_and_fetch_all(
|
common.execute_and_fetch_all(
|
||||||
admin_connection.cursor(),
|
admin_connection.cursor(),
|
||||||
"GRANT UPDATE ON LABELS :test_delete_1 TO user;",
|
"GRANT UPDATE ON LABELS :test_delete_1 TO user;",
|
||||||
)
|
)
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -393,13 +393,13 @@ def test_delete_node_with_edge_label_denied(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_delete_node_with_edge_label_granted(switch):
|
def test_delete_node_with_edge_label_granted(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.reset_and_prepare(admin_connection.cursor())
|
common.reset_and_prepare(admin_connection.cursor())
|
||||||
common.create_multi_db(admin_connection.cursor(), switch)
|
common.create_multi_db(admin_connection.cursor(), switch)
|
||||||
common.execute_and_fetch_all(
|
common.execute_and_fetch_all(
|
||||||
admin_connection.cursor(),
|
admin_connection.cursor(),
|
||||||
"GRANT CREATE_DELETE ON LABELS :test_delete_1 TO user;",
|
"GRANT CREATE_DELETE ON LABELS :test_delete_1 TO user;",
|
||||||
)
|
)
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -415,10 +415,10 @@ def test_delete_node_with_edge_label_granted(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_merge_node_all_labels_granted(switch):
|
def test_merge_node_all_labels_granted(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.reset_and_prepare(admin_connection.cursor())
|
common.reset_and_prepare(admin_connection.cursor())
|
||||||
common.create_multi_db(admin_connection.cursor(), switch)
|
common.create_multi_db(admin_connection.cursor(), switch)
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS * TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
results = common.execute_and_fetch_all(user_connection.cursor(), "MERGE (n:label1) RETURN n;")
|
results = common.execute_and_fetch_all(user_connection.cursor(), "MERGE (n:label1) RETURN n;")
|
||||||
@ -429,10 +429,10 @@ def test_merge_node_all_labels_granted(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_merge_node_all_labels_denied(switch):
|
def test_merge_node_all_labels_denied(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.reset_and_prepare(admin_connection.cursor())
|
common.reset_and_prepare(admin_connection.cursor())
|
||||||
common.create_multi_db(admin_connection.cursor(), switch)
|
common.create_multi_db(admin_connection.cursor(), switch)
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT UPDATE ON LABELS * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT UPDATE ON LABELS * TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -443,10 +443,10 @@ def test_merge_node_all_labels_denied(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_merge_node_specific_label_granted(switch):
|
def test_merge_node_specific_label_granted(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.reset_and_prepare(admin_connection.cursor())
|
common.reset_and_prepare(admin_connection.cursor())
|
||||||
common.create_multi_db(admin_connection.cursor(), switch)
|
common.create_multi_db(admin_connection.cursor(), switch)
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS :label1 TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS :label1 TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
results = common.execute_and_fetch_all(user_connection.cursor(), "MERGE (n:label1) RETURN n;")
|
results = common.execute_and_fetch_all(user_connection.cursor(), "MERGE (n:label1) RETURN n;")
|
||||||
@ -457,10 +457,10 @@ def test_merge_node_specific_label_granted(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_merge_node_specific_label_denied(switch):
|
def test_merge_node_specific_label_denied(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.reset_and_prepare(admin_connection.cursor())
|
common.reset_and_prepare(admin_connection.cursor())
|
||||||
common.create_multi_db(admin_connection.cursor(), switch)
|
common.create_multi_db(admin_connection.cursor(), switch)
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT UPDATE ON LABELS :label1 TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT UPDATE ON LABELS :label1 TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -471,11 +471,11 @@ def test_merge_node_specific_label_denied(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_merge_edge_all_labels_all_edge_types_granted(switch):
|
def test_merge_edge_all_labels_all_edge_types_granted(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.reset_and_prepare(admin_connection.cursor())
|
common.reset_and_prepare(admin_connection.cursor())
|
||||||
common.create_multi_db(admin_connection.cursor(), switch)
|
common.create_multi_db(admin_connection.cursor(), switch)
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS * TO user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON EDGE_TYPES * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON EDGE_TYPES * TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
results = common.execute_and_fetch_all(
|
results = common.execute_and_fetch_all(
|
||||||
@ -489,11 +489,11 @@ def test_merge_edge_all_labels_all_edge_types_granted(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_merge_edge_all_labels_all_edge_types_denied(switch):
|
def test_merge_edge_all_labels_all_edge_types_denied(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.reset_and_prepare(admin_connection.cursor())
|
common.reset_and_prepare(admin_connection.cursor())
|
||||||
common.create_multi_db(admin_connection.cursor(), switch)
|
common.create_multi_db(admin_connection.cursor(), switch)
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT UPDATE ON LABELS * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT UPDATE ON LABELS * TO user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT UPDATE ON EDGE_TYPES * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT UPDATE ON EDGE_TYPES * TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -507,11 +507,11 @@ def test_merge_edge_all_labels_all_edge_types_denied(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_merge_edge_all_labels_denied_all_edge_types_granted(switch):
|
def test_merge_edge_all_labels_denied_all_edge_types_granted(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.reset_and_prepare(admin_connection.cursor())
|
common.reset_and_prepare(admin_connection.cursor())
|
||||||
common.create_multi_db(admin_connection.cursor(), switch)
|
common.create_multi_db(admin_connection.cursor(), switch)
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT UPDATE ON LABELS * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT UPDATE ON LABELS * TO user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON EDGE_TYPES * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON EDGE_TYPES * TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -525,11 +525,11 @@ def test_merge_edge_all_labels_denied_all_edge_types_granted(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_merge_edge_all_labels_granted_all_edge_types_denied(switch):
|
def test_merge_edge_all_labels_granted_all_edge_types_denied(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.reset_and_prepare(admin_connection.cursor())
|
common.reset_and_prepare(admin_connection.cursor())
|
||||||
common.create_multi_db(admin_connection.cursor(), switch)
|
common.create_multi_db(admin_connection.cursor(), switch)
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS * TO user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT UPDATE ON EDGE_TYPES * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT UPDATE ON EDGE_TYPES * TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -543,7 +543,6 @@ def test_merge_edge_all_labels_granted_all_edge_types_denied(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_merge_edge_all_labels_granted_specific_edge_types_denied(switch):
|
def test_merge_edge_all_labels_granted_specific_edge_types_denied(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.reset_and_prepare(admin_connection.cursor())
|
common.reset_and_prepare(admin_connection.cursor())
|
||||||
common.create_multi_db(admin_connection.cursor(), switch)
|
common.create_multi_db(admin_connection.cursor(), switch)
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS * TO user;")
|
||||||
@ -551,6 +550,7 @@ def test_merge_edge_all_labels_granted_specific_edge_types_denied(switch):
|
|||||||
admin_connection.cursor(),
|
admin_connection.cursor(),
|
||||||
"GRANT UPDATE ON EDGE_TYPES :edge_type TO user;",
|
"GRANT UPDATE ON EDGE_TYPES :edge_type TO user;",
|
||||||
)
|
)
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -564,7 +564,6 @@ def test_merge_edge_all_labels_granted_specific_edge_types_denied(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_merge_edge_first_node_label_granted(switch):
|
def test_merge_edge_first_node_label_granted(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.reset_and_prepare(admin_connection.cursor())
|
common.reset_and_prepare(admin_connection.cursor())
|
||||||
common.create_multi_db(admin_connection.cursor(), switch)
|
common.create_multi_db(admin_connection.cursor(), switch)
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS :label1 TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS :label1 TO user;")
|
||||||
@ -573,6 +572,7 @@ def test_merge_edge_first_node_label_granted(switch):
|
|||||||
admin_connection.cursor(),
|
admin_connection.cursor(),
|
||||||
"GRANT CREATE_DELETE ON EDGE_TYPES :edge_type TO user;",
|
"GRANT CREATE_DELETE ON EDGE_TYPES :edge_type TO user;",
|
||||||
)
|
)
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -586,7 +586,6 @@ def test_merge_edge_first_node_label_granted(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_merge_edge_second_node_label_granted(switch):
|
def test_merge_edge_second_node_label_granted(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.reset_and_prepare(admin_connection.cursor())
|
common.reset_and_prepare(admin_connection.cursor())
|
||||||
common.create_multi_db(admin_connection.cursor(), switch)
|
common.create_multi_db(admin_connection.cursor(), switch)
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS :label2 TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS :label2 TO user;")
|
||||||
@ -595,6 +594,7 @@ def test_merge_edge_second_node_label_granted(switch):
|
|||||||
admin_connection.cursor(),
|
admin_connection.cursor(),
|
||||||
"GRANT CREATE_DELETE ON EDGE_TYPES :edge_type TO user;",
|
"GRANT CREATE_DELETE ON EDGE_TYPES :edge_type TO user;",
|
||||||
)
|
)
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -608,10 +608,10 @@ def test_merge_edge_second_node_label_granted(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_set_label_when_label_granted(switch):
|
def test_set_label_when_label_granted(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.reset_and_prepare(admin_connection.cursor())
|
common.reset_and_prepare(admin_connection.cursor())
|
||||||
common.create_multi_db(admin_connection.cursor(), switch)
|
common.create_multi_db(admin_connection.cursor(), switch)
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS :update_label_2 TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS :update_label_2 TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -621,12 +621,12 @@ def test_set_label_when_label_granted(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_set_label_when_label_denied(switch):
|
def test_set_label_when_label_denied(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
|
|
||||||
common.reset_and_prepare(admin_connection.cursor())
|
common.reset_and_prepare(admin_connection.cursor())
|
||||||
common.create_multi_db(admin_connection.cursor(), switch)
|
common.create_multi_db(admin_connection.cursor(), switch)
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT UPDATE ON LABELS :update_label_2 TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT UPDATE ON LABELS :update_label_2 TO user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON LABELS :test_delete TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON LABELS :test_delete TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -637,11 +637,11 @@ def test_set_label_when_label_denied(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_remove_label_when_label_granted(switch):
|
def test_remove_label_when_label_granted(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
|
|
||||||
common.reset_and_prepare(admin_connection.cursor())
|
common.reset_and_prepare(admin_connection.cursor())
|
||||||
common.create_multi_db(admin_connection.cursor(), switch)
|
common.create_multi_db(admin_connection.cursor(), switch)
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS :test_delete TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS :test_delete TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -651,12 +651,12 @@ def test_remove_label_when_label_granted(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_remove_label_when_label_denied(switch):
|
def test_remove_label_when_label_denied(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
|
|
||||||
common.reset_and_prepare(admin_connection.cursor())
|
common.reset_and_prepare(admin_connection.cursor())
|
||||||
common.create_multi_db(admin_connection.cursor(), switch)
|
common.create_multi_db(admin_connection.cursor(), switch)
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT UPDATE ON LABELS :update_label_2 TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT UPDATE ON LABELS :update_label_2 TO user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON LABELS :test_delete TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON LABELS :test_delete TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -667,12 +667,12 @@ def test_remove_label_when_label_denied(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_merge_nodes_pass_when_having_create_delete(switch):
|
def test_merge_nodes_pass_when_having_create_delete(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
|
|
||||||
common.reset_and_prepare(admin_connection.cursor())
|
common.reset_and_prepare(admin_connection.cursor())
|
||||||
common.create_multi_db(admin_connection.cursor(), switch)
|
common.create_multi_db(admin_connection.cursor(), switch)
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS * TO user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON EDGE_TYPES * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON EDGE_TYPES * TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
|
@ -7,9 +7,9 @@ import pytest
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_all_edge_types_all_labels_granted(switch):
|
def test_all_edge_types_all_labels_granted(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON LABELS * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON LABELS * TO user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES * TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -21,9 +21,9 @@ def test_all_edge_types_all_labels_granted(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_deny_all_edge_types_and_all_labels(switch):
|
def test_deny_all_edge_types_and_all_labels(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON LABELS * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON LABELS * TO user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON EDGE_TYPES * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON EDGE_TYPES * TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -35,9 +35,9 @@ def test_deny_all_edge_types_and_all_labels(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_revoke_all_edge_types_and_all_labels(switch):
|
def test_revoke_all_edge_types_and_all_labels(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -49,10 +49,10 @@ def test_revoke_all_edge_types_and_all_labels(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_deny_edge_type(switch):
|
def test_deny_edge_type(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON LABELS :label1, :label2, :label3 TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON LABELS :label1, :label2, :label3 TO user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES :edgeType2 TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES :edgeType2 TO user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON EDGE_TYPES :edgeType1 TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON EDGE_TYPES :edgeType1 TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -64,10 +64,10 @@ def test_deny_edge_type(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_denied_node_label(switch):
|
def test_denied_node_label(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON LABELS :label1,:label3 TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON LABELS :label1,:label3 TO user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES :edgeType1, :edgeType2 TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES :edgeType1, :edgeType2 TO user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON LABELS :label2 TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON LABELS :label2 TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -79,10 +79,10 @@ def test_denied_node_label(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_denied_one_of_node_label(switch):
|
def test_denied_one_of_node_label(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON LABELS :label1,:label2 TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON LABELS :label1,:label2 TO user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES :edgeType1, :edgeType2 TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES :edgeType1, :edgeType2 TO user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON LABELS :label3 TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON LABELS :label3 TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -94,8 +94,8 @@ def test_denied_one_of_node_label(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_revoke_all_labels(switch):
|
def test_revoke_all_labels(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
results = common.execute_and_fetch_all(user_connection.cursor(), "MATCH (n)-[r]->(m) RETURN n,r,m;")
|
results = common.execute_and_fetch_all(user_connection.cursor(), "MATCH (n)-[r]->(m) RETURN n,r,m;")
|
||||||
@ -106,8 +106,8 @@ def test_revoke_all_labels(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_revoke_all_edge_types(switch):
|
def test_revoke_all_edge_types(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
results = common.execute_and_fetch_all(user_connection.cursor(), "MATCH (n)-[r]->(m) RETURN n,r,m;")
|
results = common.execute_and_fetch_all(user_connection.cursor(), "MATCH (n)-[r]->(m) RETURN n,r,m;")
|
||||||
|
@ -7,11 +7,11 @@ import pytest
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_weighted_shortest_path_all_edge_types_all_labels_granted(switch):
|
def test_weighted_shortest_path_all_edge_types_all_labels_granted(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON LABELS * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON LABELS * TO user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES * TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -54,11 +54,11 @@ def test_weighted_shortest_path_all_edge_types_all_labels_granted(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_weighted_shortest_path_all_edge_types_all_labels_denied(switch):
|
def test_weighted_shortest_path_all_edge_types_all_labels_denied(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON LABELS * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON LABELS * TO user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON EDGE_TYPES * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON EDGE_TYPES * TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -72,7 +72,6 @@ def test_weighted_shortest_path_all_edge_types_all_labels_denied(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_weighted_shortest_path_denied_start(switch):
|
def test_weighted_shortest_path_denied_start(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
||||||
common.execute_and_fetch_all(
|
common.execute_and_fetch_all(
|
||||||
@ -80,6 +79,7 @@ def test_weighted_shortest_path_denied_start(switch):
|
|||||||
)
|
)
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES * TO user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON LABELS :label0 TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON LABELS :label0 TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -94,7 +94,6 @@ def test_weighted_shortest_path_denied_start(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_weighted_shortest_path_denied_destination(switch):
|
def test_weighted_shortest_path_denied_destination(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
||||||
common.execute_and_fetch_all(
|
common.execute_and_fetch_all(
|
||||||
@ -102,6 +101,7 @@ def test_weighted_shortest_path_denied_destination(switch):
|
|||||||
)
|
)
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES * TO user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON LABELS :label4 TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON LABELS :label4 TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -116,7 +116,6 @@ def test_weighted_shortest_path_denied_destination(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_weighted_shortest_path_denied_label_1(switch):
|
def test_weighted_shortest_path_denied_label_1(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
||||||
common.execute_and_fetch_all(
|
common.execute_and_fetch_all(
|
||||||
@ -124,6 +123,7 @@ def test_weighted_shortest_path_denied_label_1(switch):
|
|||||||
)
|
)
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON LABELS :label1 TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON LABELS :label1 TO user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES * TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -162,7 +162,6 @@ def test_weighted_shortest_path_denied_label_1(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_weighted_shortest_path_denied_edge_type_3(switch):
|
def test_weighted_shortest_path_denied_edge_type_3(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON LABELS * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON LABELS * TO user;")
|
||||||
@ -170,6 +169,7 @@ def test_weighted_shortest_path_denied_edge_type_3(switch):
|
|||||||
admin_connection.cursor(), "GRANT READ ON EDGE_TYPES :edge_type_1, :edge_type_2, :edge_type_4 TO user;"
|
admin_connection.cursor(), "GRANT READ ON EDGE_TYPES :edge_type_1, :edge_type_2, :edge_type_4 TO user;"
|
||||||
)
|
)
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON EDGE_TYPES :edge_type_3 TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON EDGE_TYPES :edge_type_3 TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -213,11 +213,11 @@ def test_weighted_shortest_path_denied_edge_type_3(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_dfs_all_edge_types_all_labels_granted(switch):
|
def test_dfs_all_edge_types_all_labels_granted(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON LABELS * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON LABELS * TO user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES * TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -235,11 +235,11 @@ def test_dfs_all_edge_types_all_labels_granted(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_dfs_all_edge_types_all_labels_denied(switch):
|
def test_dfs_all_edge_types_all_labels_denied(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON LABELS * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON LABELS * TO user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON EDGE_TYPES * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON EDGE_TYPES * TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -251,7 +251,6 @@ def test_dfs_all_edge_types_all_labels_denied(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_dfs_denied_start(switch):
|
def test_dfs_denied_start(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
||||||
common.execute_and_fetch_all(
|
common.execute_and_fetch_all(
|
||||||
@ -259,6 +258,7 @@ def test_dfs_denied_start(switch):
|
|||||||
)
|
)
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES * TO user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON LABELS :label0 TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON LABELS :label0 TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -272,7 +272,6 @@ def test_dfs_denied_start(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_dfs_denied_destination(switch):
|
def test_dfs_denied_destination(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
||||||
common.execute_and_fetch_all(
|
common.execute_and_fetch_all(
|
||||||
@ -280,6 +279,7 @@ def test_dfs_denied_destination(switch):
|
|||||||
)
|
)
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES * TO user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON LABELS :label4 TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON LABELS :label4 TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -293,7 +293,6 @@ def test_dfs_denied_destination(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_dfs_denied_label_1(switch):
|
def test_dfs_denied_label_1(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
||||||
common.execute_and_fetch_all(
|
common.execute_and_fetch_all(
|
||||||
@ -301,6 +300,7 @@ def test_dfs_denied_label_1(switch):
|
|||||||
)
|
)
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON LABELS :label1 TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON LABELS :label1 TO user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES * TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -318,7 +318,6 @@ def test_dfs_denied_label_1(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_dfs_denied_edge_type_3(switch):
|
def test_dfs_denied_edge_type_3(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
||||||
|
|
||||||
@ -327,6 +326,7 @@ def test_dfs_denied_edge_type_3(switch):
|
|||||||
admin_connection.cursor(), "GRANT READ ON EDGE_TYPES :edge_type_1, :edge_type_2, :edge_type_4 TO user;"
|
admin_connection.cursor(), "GRANT READ ON EDGE_TYPES :edge_type_1, :edge_type_2, :edge_type_4 TO user;"
|
||||||
)
|
)
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON EDGE_TYPES :edge_type_3 TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON EDGE_TYPES :edge_type_3 TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -344,11 +344,11 @@ def test_dfs_denied_edge_type_3(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_bfs_sts_all_edge_types_all_labels_granted(switch):
|
def test_bfs_sts_all_edge_types_all_labels_granted(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON LABELS * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON LABELS * TO user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES * TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -366,11 +366,11 @@ def test_bfs_sts_all_edge_types_all_labels_granted(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_bfs_sts_all_edge_types_all_labels_denied(switch):
|
def test_bfs_sts_all_edge_types_all_labels_denied(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON LABELS * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON LABELS * TO user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON EDGE_TYPES * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON EDGE_TYPES * TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -384,7 +384,6 @@ def test_bfs_sts_all_edge_types_all_labels_denied(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_bfs_sts_denied_start(switch):
|
def test_bfs_sts_denied_start(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
||||||
common.execute_and_fetch_all(
|
common.execute_and_fetch_all(
|
||||||
@ -392,6 +391,7 @@ def test_bfs_sts_denied_start(switch):
|
|||||||
)
|
)
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES * TO user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON LABELS :label0 TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON LABELS :label0 TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -405,7 +405,6 @@ def test_bfs_sts_denied_start(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_bfs_sts_denied_destination(switch):
|
def test_bfs_sts_denied_destination(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
||||||
common.execute_and_fetch_all(
|
common.execute_and_fetch_all(
|
||||||
@ -413,6 +412,7 @@ def test_bfs_sts_denied_destination(switch):
|
|||||||
)
|
)
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES * TO user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON LABELS :label4 TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON LABELS :label4 TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -426,7 +426,6 @@ def test_bfs_sts_denied_destination(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_bfs_sts_denied_label_1(switch):
|
def test_bfs_sts_denied_label_1(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
||||||
common.execute_and_fetch_all(
|
common.execute_and_fetch_all(
|
||||||
@ -434,6 +433,7 @@ def test_bfs_sts_denied_label_1(switch):
|
|||||||
)
|
)
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON LABELS :label1 TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON LABELS :label1 TO user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES * TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -450,7 +450,6 @@ def test_bfs_sts_denied_label_1(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_bfs_sts_denied_edge_type_3(switch):
|
def test_bfs_sts_denied_edge_type_3(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON LABELS * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON LABELS * TO user;")
|
||||||
@ -458,6 +457,7 @@ def test_bfs_sts_denied_edge_type_3(switch):
|
|||||||
admin_connection.cursor(), "GRANT READ ON EDGE_TYPES :edge_type_1, :edge_type_2, :edge_type_4 TO user;"
|
admin_connection.cursor(), "GRANT READ ON EDGE_TYPES :edge_type_1, :edge_type_2, :edge_type_4 TO user;"
|
||||||
)
|
)
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON EDGE_TYPES :edge_type_3 TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON EDGE_TYPES :edge_type_3 TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -474,11 +474,11 @@ def test_bfs_sts_denied_edge_type_3(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_bfs_single_source_all_edge_types_all_labels_granted(switch):
|
def test_bfs_single_source_all_edge_types_all_labels_granted(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON LABELS * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON LABELS * TO user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES * TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -496,11 +496,11 @@ def test_bfs_single_source_all_edge_types_all_labels_granted(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_bfs_single_source_all_edge_types_all_labels_denied(switch):
|
def test_bfs_single_source_all_edge_types_all_labels_denied(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON LABELS * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON LABELS * TO user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON EDGE_TYPES * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON EDGE_TYPES * TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -512,7 +512,6 @@ def test_bfs_single_source_all_edge_types_all_labels_denied(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_bfs_single_source_denied_start(switch):
|
def test_bfs_single_source_denied_start(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
||||||
common.execute_and_fetch_all(
|
common.execute_and_fetch_all(
|
||||||
@ -520,6 +519,7 @@ def test_bfs_single_source_denied_start(switch):
|
|||||||
)
|
)
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES * TO user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON LABELS :label0 TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON LABELS :label0 TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -533,7 +533,6 @@ def test_bfs_single_source_denied_start(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_bfs_single_source_denied_destination(switch):
|
def test_bfs_single_source_denied_destination(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
||||||
common.execute_and_fetch_all(
|
common.execute_and_fetch_all(
|
||||||
@ -541,6 +540,7 @@ def test_bfs_single_source_denied_destination(switch):
|
|||||||
)
|
)
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES * TO user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON LABELS :label4 TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON LABELS :label4 TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -554,7 +554,6 @@ def test_bfs_single_source_denied_destination(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_bfs_single_source_denied_label_1(switch):
|
def test_bfs_single_source_denied_label_1(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
||||||
common.execute_and_fetch_all(
|
common.execute_and_fetch_all(
|
||||||
@ -562,6 +561,7 @@ def test_bfs_single_source_denied_label_1(switch):
|
|||||||
)
|
)
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON LABELS :label1 TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON LABELS :label1 TO user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES * TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -579,7 +579,6 @@ def test_bfs_single_source_denied_label_1(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_bfs_single_source_denied_edge_type_3(switch):
|
def test_bfs_single_source_denied_edge_type_3(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON LABELS * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON LABELS * TO user;")
|
||||||
@ -587,6 +586,7 @@ def test_bfs_single_source_denied_edge_type_3(switch):
|
|||||||
admin_connection.cursor(), "GRANT READ ON EDGE_TYPES :edge_type_1, :edge_type_2, :edge_type_4 TO user;"
|
admin_connection.cursor(), "GRANT READ ON EDGE_TYPES :edge_type_1, :edge_type_2, :edge_type_4 TO user;"
|
||||||
)
|
)
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON EDGE_TYPES :edge_type_3 TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON EDGE_TYPES :edge_type_3 TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -604,11 +604,11 @@ def test_bfs_single_source_denied_edge_type_3(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_all_shortest_paths_when_all_edge_types_all_labels_granted(switch):
|
def test_all_shortest_paths_when_all_edge_types_all_labels_granted(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON LABELS * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON LABELS * TO user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES * TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -651,11 +651,11 @@ def test_all_shortest_paths_when_all_edge_types_all_labels_granted(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_all_shortest_paths_when_all_edge_types_all_labels_denied(switch):
|
def test_all_shortest_paths_when_all_edge_types_all_labels_denied(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON LABELS * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON LABELS * TO user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON EDGE_TYPES * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON EDGE_TYPES * TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -669,7 +669,6 @@ def test_all_shortest_paths_when_all_edge_types_all_labels_denied(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_all_shortest_paths_when_denied_start(switch):
|
def test_all_shortest_paths_when_denied_start(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
||||||
common.execute_and_fetch_all(
|
common.execute_and_fetch_all(
|
||||||
@ -677,6 +676,7 @@ def test_all_shortest_paths_when_denied_start(switch):
|
|||||||
)
|
)
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES * TO user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON LABELS :label0 TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON LABELS :label0 TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -691,7 +691,6 @@ def test_all_shortest_paths_when_denied_start(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_all_shortest_paths_when_denied_destination(switch):
|
def test_all_shortest_paths_when_denied_destination(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
||||||
common.execute_and_fetch_all(
|
common.execute_and_fetch_all(
|
||||||
@ -699,6 +698,7 @@ def test_all_shortest_paths_when_denied_destination(switch):
|
|||||||
)
|
)
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES * TO user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON LABELS :label4 TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON LABELS :label4 TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -713,7 +713,6 @@ def test_all_shortest_paths_when_denied_destination(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_all_shortest_paths_when_denied_label_1(switch):
|
def test_all_shortest_paths_when_denied_label_1(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
||||||
common.execute_and_fetch_all(
|
common.execute_and_fetch_all(
|
||||||
@ -721,6 +720,7 @@ def test_all_shortest_paths_when_denied_label_1(switch):
|
|||||||
)
|
)
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON LABELS :label1 TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON LABELS :label1 TO user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES * TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
@ -759,7 +759,6 @@ def test_all_shortest_paths_when_denied_label_1(switch):
|
|||||||
@pytest.mark.parametrize("switch", [False, True])
|
@pytest.mark.parametrize("switch", [False, True])
|
||||||
def test_all_shortest_paths_when_denied_edge_type_3(switch):
|
def test_all_shortest_paths_when_denied_edge_type_3(switch):
|
||||||
admin_connection = common.connect(username="admin", password="test")
|
admin_connection = common.connect(username="admin", password="test")
|
||||||
user_connection = common.connect(username="user", password="test")
|
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON LABELS * TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON LABELS * TO user;")
|
||||||
@ -767,6 +766,7 @@ def test_all_shortest_paths_when_denied_edge_type_3(switch):
|
|||||||
admin_connection.cursor(), "GRANT READ ON EDGE_TYPES :edge_type_1, :edge_type_2, :edge_type_4 TO user;"
|
admin_connection.cursor(), "GRANT READ ON EDGE_TYPES :edge_type_1, :edge_type_2, :edge_type_4 TO user;"
|
||||||
)
|
)
|
||||||
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON EDGE_TYPES :edge_type_3 TO user;")
|
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT NOTHING ON EDGE_TYPES :edge_type_3 TO user;")
|
||||||
|
user_connection = common.connect(username="user", password="test")
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
common.switch_db(user_connection.cursor())
|
common.switch_db(user_connection.cursor())
|
||||||
|
@ -84,11 +84,12 @@ show_databases_w_user_setup_queries: &show_databases_w_user_setup_queries
|
|||||||
- "GRANT DATABASE db1 TO user;"
|
- "GRANT DATABASE db1 TO user;"
|
||||||
- "GRANT ALL PRIVILEGES TO user2;"
|
- "GRANT ALL PRIVILEGES TO user2;"
|
||||||
- "GRANT DATABASE db2 TO user2;"
|
- "GRANT DATABASE db2 TO user2;"
|
||||||
|
- "GRANT DATABASE memgraph TO user2;"
|
||||||
- "REVOKE DATABASE memgraph FROM user2;"
|
- "REVOKE DATABASE memgraph FROM user2;"
|
||||||
- "SET MAIN DATABASE db2 FOR user2"
|
- "SET MAIN DATABASE db2 FOR user2"
|
||||||
- "GRANT ALL PRIVILEGES TO user3;"
|
- "GRANT ALL PRIVILEGES TO user3;"
|
||||||
- "GRANT DATABASE * TO user3;"
|
- "GRANT DATABASE * TO user3;"
|
||||||
- "REVOKE DATABASE memgraph FROM user3;"
|
- "DENY DATABASE memgraph FROM user3;"
|
||||||
- "SET MAIN DATABASE db1 FOR user3"
|
- "SET MAIN DATABASE db1 FOR user3"
|
||||||
|
|
||||||
create_delete_filtering_in_memory_cluster: &create_delete_filtering_in_memory_cluster
|
create_delete_filtering_in_memory_cluster: &create_delete_filtering_in_memory_cluster
|
||||||
|
@ -107,18 +107,21 @@ def execute_read_node_assertion(
|
|||||||
operation_case: List[str], queries: List[str], create_index: bool, expected_size: int, switch: bool
|
operation_case: List[str], queries: List[str], create_index: bool, expected_size: int, switch: bool
|
||||||
) -> None:
|
) -> None:
|
||||||
admin_cursor = get_admin_cursor()
|
admin_cursor = get_admin_cursor()
|
||||||
user_cursor = get_user_cursor()
|
|
||||||
|
|
||||||
if switch:
|
if switch:
|
||||||
create_multi_db(admin_cursor)
|
create_multi_db(admin_cursor)
|
||||||
switch_db(admin_cursor)
|
switch_db(admin_cursor)
|
||||||
switch_db(user_cursor)
|
|
||||||
|
|
||||||
reset_permissions(admin_cursor, create_index)
|
reset_permissions(admin_cursor, create_index)
|
||||||
|
|
||||||
for operation in operation_case:
|
for operation in operation_case:
|
||||||
execute_and_fetch_all(admin_cursor, operation)
|
execute_and_fetch_all(admin_cursor, operation)
|
||||||
|
|
||||||
|
# Connect after possible auth changes
|
||||||
|
user_cursor = get_user_cursor()
|
||||||
|
if switch:
|
||||||
|
switch_db(user_cursor)
|
||||||
|
|
||||||
for mq in queries:
|
for mq in queries:
|
||||||
results = execute_and_fetch_all(user_cursor, mq)
|
results = execute_and_fetch_all(user_cursor, mq)
|
||||||
assert len(results) == expected_size
|
assert len(results) == expected_size
|
||||||
|
@ -121,6 +121,7 @@ def only_main_queries(cursor):
|
|||||||
n_exceptions += try_and_count(cursor, f"REVOKE EDGE_TYPES :e FROM user_name")
|
n_exceptions += try_and_count(cursor, f"REVOKE EDGE_TYPES :e FROM user_name")
|
||||||
n_exceptions += try_and_count(cursor, f"GRANT DATABASE memgraph TO user_name;")
|
n_exceptions += try_and_count(cursor, f"GRANT DATABASE memgraph TO user_name;")
|
||||||
n_exceptions += try_and_count(cursor, f"SET MAIN DATABASE memgraph FOR user_name")
|
n_exceptions += try_and_count(cursor, f"SET MAIN DATABASE memgraph FOR user_name")
|
||||||
|
n_exceptions += try_and_count(cursor, f"DENY DATABASE memgraph FROM user_name;")
|
||||||
n_exceptions += try_and_count(cursor, f"REVOKE DATABASE memgraph FROM user_name;")
|
n_exceptions += try_and_count(cursor, f"REVOKE DATABASE memgraph FROM user_name;")
|
||||||
|
|
||||||
return n_exceptions
|
return n_exceptions
|
||||||
@ -198,8 +199,8 @@ def test_auth_queries_on_replica(connection):
|
|||||||
|
|
||||||
# 1/
|
# 1/
|
||||||
assert only_main_queries(cursor_main) == 0
|
assert only_main_queries(cursor_main) == 0
|
||||||
assert only_main_queries(cursor_replica_1) == 17
|
assert only_main_queries(cursor_replica_1) == 18
|
||||||
assert only_main_queries(cursor_replica_2) == 17
|
assert only_main_queries(cursor_replica_2) == 18
|
||||||
assert main_and_repl_queries(cursor_main) == 0
|
assert main_and_repl_queries(cursor_main) == 0
|
||||||
assert main_and_repl_queries(cursor_replica_1) == 0
|
assert main_and_repl_queries(cursor_replica_1) == 0
|
||||||
assert main_and_repl_queries(cursor_replica_2) == 0
|
assert main_and_repl_queries(cursor_replica_2) == 0
|
||||||
@ -383,6 +384,7 @@ def test_manual_roles_recovery(connection):
|
|||||||
"--log-level=TRACE",
|
"--log-level=TRACE",
|
||||||
"--data_directory",
|
"--data_directory",
|
||||||
TEMP_DIR + "/replica1",
|
TEMP_DIR + "/replica1",
|
||||||
|
"--also-log-to-stderr",
|
||||||
],
|
],
|
||||||
"log_file": "replica1.log",
|
"log_file": "replica1.log",
|
||||||
"setup_queries": [
|
"setup_queries": [
|
||||||
@ -818,13 +820,15 @@ def test_auth_replication(connection):
|
|||||||
{("LABEL :l3", "UPDATE", "LABEL PERMISSION GRANTED TO ROLE")},
|
{("LABEL :l3", "UPDATE", "LABEL PERMISSION GRANTED TO ROLE")},
|
||||||
)
|
)
|
||||||
|
|
||||||
# GRANT/REVOKE DATABASE
|
# GRANT/DENY DATABASE
|
||||||
execute_and_fetch_all(cursor_main, "CREATE DATABASE auth_test")
|
execute_and_fetch_all(cursor_main, "CREATE DATABASE auth_test")
|
||||||
execute_and_fetch_all(cursor_main, "CREATE DATABASE auth_test2")
|
execute_and_fetch_all(cursor_main, "CREATE DATABASE auth_test2")
|
||||||
execute_and_fetch_all(cursor_main, "GRANT DATABASE auth_test TO user4")
|
execute_and_fetch_all(cursor_main, "GRANT DATABASE auth_test TO user4")
|
||||||
check(partial(show_database_privileges_func, user="user4"), [(["auth_test", "memgraph"], [])])
|
check(partial(show_database_privileges_func, user="user4"), [(["auth_test", "memgraph"], [])])
|
||||||
execute_and_fetch_all(cursor_main, "REVOKE DATABASE auth_test2 FROM user4")
|
execute_and_fetch_all(cursor_main, "DENY DATABASE auth_test2 FROM user4")
|
||||||
check(partial(show_database_privileges_func, user="user4"), [(["auth_test", "memgraph"], ["auth_test2"])])
|
check(partial(show_database_privileges_func, user="user4"), [(["auth_test", "memgraph"], ["auth_test2"])])
|
||||||
|
execute_and_fetch_all(cursor_main, "REVOKE DATABASE memgraph FROM user4")
|
||||||
|
check(partial(show_database_privileges_func, user="user4"), [(["auth_test"], ["auth_test2"])])
|
||||||
|
|
||||||
# SET MAIN DATABASE
|
# SET MAIN DATABASE
|
||||||
execute_and_fetch_all(cursor_main, "GRANT ALL PRIVILEGES TO user4")
|
execute_and_fetch_all(cursor_main, "GRANT ALL PRIVILEGES TO user4")
|
||||||
|
@ -70,21 +70,26 @@ def test_multitenant_transactions():
|
|||||||
# TODO Add SHOW TRANSACTIONS ON * that should return all transactions
|
# TODO Add SHOW TRANSACTIONS ON * that should return all transactions
|
||||||
|
|
||||||
|
|
||||||
def test_admin_has_one_transaction():
|
def test_admin_has_one_transaction(request):
|
||||||
"""Creates admin and tests that he sees only one transaction."""
|
"""Creates admin and tests that he sees only one transaction."""
|
||||||
# a_cursor is used for creating admin user, simulates main thread
|
# a_cursor is used for creating admin user, simulates main thread
|
||||||
superadmin_cursor = connect().cursor()
|
superadmin_cursor = connect().cursor()
|
||||||
execute_and_fetch_all(superadmin_cursor, "CREATE USER admin")
|
execute_and_fetch_all(superadmin_cursor, "CREATE USER admin")
|
||||||
execute_and_fetch_all(superadmin_cursor, "GRANT TRANSACTION_MANAGEMENT TO admin")
|
execute_and_fetch_all(superadmin_cursor, "GRANT TRANSACTION_MANAGEMENT TO admin")
|
||||||
execute_and_fetch_all(superadmin_cursor, "GRANT DATABASE * TO admin")
|
execute_and_fetch_all(superadmin_cursor, "GRANT DATABASE * TO admin")
|
||||||
|
|
||||||
|
def on_exit():
|
||||||
|
execute_and_fetch_all(superadmin_cursor, "DROP USER admin")
|
||||||
|
|
||||||
|
request.addfinalizer(on_exit)
|
||||||
|
|
||||||
admin_cursor = connect(username="admin", password="").cursor()
|
admin_cursor = connect(username="admin", password="").cursor()
|
||||||
process = multiprocessing.Process(target=show_transactions_test, args=(admin_cursor, 1))
|
process = multiprocessing.Process(target=show_transactions_test, args=(admin_cursor, 1))
|
||||||
process.start()
|
process.start()
|
||||||
process.join()
|
process.join()
|
||||||
execute_and_fetch_all(superadmin_cursor, "DROP USER admin")
|
|
||||||
|
|
||||||
|
|
||||||
def test_user_can_see_its_transaction():
|
def test_user_can_see_its_transaction(request):
|
||||||
"""Tests that user without privileges can see its own transaction"""
|
"""Tests that user without privileges can see its own transaction"""
|
||||||
superadmin_cursor = connect().cursor()
|
superadmin_cursor = connect().cursor()
|
||||||
execute_and_fetch_all(superadmin_cursor, "CREATE USER admin")
|
execute_and_fetch_all(superadmin_cursor, "CREATE USER admin")
|
||||||
@ -92,20 +97,31 @@ def test_user_can_see_its_transaction():
|
|||||||
execute_and_fetch_all(superadmin_cursor, "GRANT DATABASE * TO admin")
|
execute_and_fetch_all(superadmin_cursor, "GRANT DATABASE * TO admin")
|
||||||
execute_and_fetch_all(superadmin_cursor, "CREATE USER user")
|
execute_and_fetch_all(superadmin_cursor, "CREATE USER user")
|
||||||
execute_and_fetch_all(superadmin_cursor, "REVOKE ALL PRIVILEGES FROM user")
|
execute_and_fetch_all(superadmin_cursor, "REVOKE ALL PRIVILEGES FROM user")
|
||||||
|
|
||||||
|
def on_exit():
|
||||||
|
execute_and_fetch_all(superadmin_cursor, "DROP USER admin")
|
||||||
|
execute_and_fetch_all(superadmin_cursor, "DROP USER user")
|
||||||
|
|
||||||
|
request.addfinalizer(on_exit)
|
||||||
|
|
||||||
user_cursor = connect(username="user", password="").cursor()
|
user_cursor = connect(username="user", password="").cursor()
|
||||||
process = multiprocessing.Process(target=show_transactions_test, args=(user_cursor, 1))
|
process = multiprocessing.Process(target=show_transactions_test, args=(user_cursor, 1))
|
||||||
process.start()
|
process.start()
|
||||||
process.join()
|
process.join()
|
||||||
admin_cursor = connect(username="admin", password="").cursor()
|
admin_cursor = connect(username="admin", password="").cursor()
|
||||||
execute_and_fetch_all(admin_cursor, "DROP USER user")
|
|
||||||
execute_and_fetch_all(admin_cursor, "DROP USER admin")
|
|
||||||
|
|
||||||
|
|
||||||
def test_explicit_transaction_output():
|
def test_explicit_transaction_output(request):
|
||||||
superadmin_cursor = connect().cursor()
|
superadmin_cursor = connect().cursor()
|
||||||
execute_and_fetch_all(superadmin_cursor, "CREATE USER admin")
|
execute_and_fetch_all(superadmin_cursor, "CREATE USER admin")
|
||||||
execute_and_fetch_all(superadmin_cursor, "GRANT TRANSACTION_MANAGEMENT TO admin")
|
execute_and_fetch_all(superadmin_cursor, "GRANT TRANSACTION_MANAGEMENT TO admin")
|
||||||
execute_and_fetch_all(superadmin_cursor, "GRANT DATABASE * TO admin")
|
execute_and_fetch_all(superadmin_cursor, "GRANT DATABASE * TO admin")
|
||||||
|
|
||||||
|
def on_exit():
|
||||||
|
execute_and_fetch_all(superadmin_cursor, "DROP USER admin")
|
||||||
|
|
||||||
|
request.addfinalizer(on_exit)
|
||||||
|
|
||||||
admin_connection = connect(username="admin", password="")
|
admin_connection = connect(username="admin", password="")
|
||||||
admin_cursor = admin_connection.cursor()
|
admin_cursor = admin_connection.cursor()
|
||||||
# Admin starts running explicit transaction
|
# Admin starts running explicit transaction
|
||||||
@ -123,10 +139,9 @@ def test_explicit_transaction_output():
|
|||||||
assert show_results[1 - executing_index][2] == ["CREATE (n:Person {id_: 1})", "CREATE (n:Person {id_: 2})"]
|
assert show_results[1 - executing_index][2] == ["CREATE (n:Person {id_: 1})", "CREATE (n:Person {id_: 2})"]
|
||||||
|
|
||||||
execute_and_fetch_all(superadmin_cursor, "ROLLBACK")
|
execute_and_fetch_all(superadmin_cursor, "ROLLBACK")
|
||||||
execute_and_fetch_all(superadmin_cursor, "DROP USER admin")
|
|
||||||
|
|
||||||
|
|
||||||
def test_superadmin_cannot_see_admin_can_see_admin():
|
def test_superadmin_cannot_see_admin_can_see_admin(request):
|
||||||
"""Tests that superadmin cannot see the transaction created by admin but two admins can see and kill each other's transactions."""
|
"""Tests that superadmin cannot see the transaction created by admin but two admins can see and kill each other's transactions."""
|
||||||
superadmin_cursor = connect().cursor()
|
superadmin_cursor = connect().cursor()
|
||||||
execute_and_fetch_all(superadmin_cursor, "CREATE USER admin1")
|
execute_and_fetch_all(superadmin_cursor, "CREATE USER admin1")
|
||||||
@ -135,6 +150,13 @@ def test_superadmin_cannot_see_admin_can_see_admin():
|
|||||||
execute_and_fetch_all(superadmin_cursor, "CREATE USER admin2")
|
execute_and_fetch_all(superadmin_cursor, "CREATE USER admin2")
|
||||||
execute_and_fetch_all(superadmin_cursor, "GRANT TRANSACTION_MANAGEMENT TO admin2")
|
execute_and_fetch_all(superadmin_cursor, "GRANT TRANSACTION_MANAGEMENT TO admin2")
|
||||||
execute_and_fetch_all(superadmin_cursor, "GRANT DATABASE * TO admin2")
|
execute_and_fetch_all(superadmin_cursor, "GRANT DATABASE * TO admin2")
|
||||||
|
|
||||||
|
def on_exit():
|
||||||
|
execute_and_fetch_all(superadmin_cursor, "DROP USER admin1")
|
||||||
|
execute_and_fetch_all(superadmin_cursor, "DROP USER admin2")
|
||||||
|
|
||||||
|
request.addfinalizer(on_exit)
|
||||||
|
|
||||||
# Admin starts running infinite query
|
# Admin starts running infinite query
|
||||||
admin_connection_1 = connect(username="admin1", password="")
|
admin_connection_1 = connect(username="admin1", password="")
|
||||||
admin_cursor_1 = admin_connection_1.cursor()
|
admin_cursor_1 = admin_connection_1.cursor()
|
||||||
@ -160,19 +182,23 @@ def test_superadmin_cannot_see_admin_can_see_admin():
|
|||||||
# Kill transaction
|
# Kill transaction
|
||||||
long_transaction_id = show_results[1 - executing_index][1]
|
long_transaction_id = show_results[1 - executing_index][1]
|
||||||
execute_and_fetch_all(admin_cursor_2, f"TERMINATE TRANSACTIONS '{long_transaction_id}'")
|
execute_and_fetch_all(admin_cursor_2, f"TERMINATE TRANSACTIONS '{long_transaction_id}'")
|
||||||
execute_and_fetch_all(superadmin_cursor, "DROP USER admin1")
|
|
||||||
execute_and_fetch_all(superadmin_cursor, "DROP USER admin2")
|
|
||||||
admin_connection_1.close()
|
admin_connection_1.close()
|
||||||
admin_connection_2.close()
|
admin_connection_2.close()
|
||||||
|
|
||||||
|
|
||||||
def test_admin_sees_superadmin():
|
def test_admin_sees_superadmin(request):
|
||||||
"""Tests that admin created by superadmin can see the superadmin's transaction."""
|
"""Tests that admin created by superadmin can see the superadmin's transaction."""
|
||||||
superadmin_connection = connect()
|
superadmin_connection = connect()
|
||||||
superadmin_cursor = superadmin_connection.cursor()
|
superadmin_cursor = superadmin_connection.cursor()
|
||||||
execute_and_fetch_all(superadmin_cursor, "CREATE USER admin")
|
execute_and_fetch_all(superadmin_cursor, "CREATE USER admin")
|
||||||
execute_and_fetch_all(superadmin_cursor, "GRANT TRANSACTION_MANAGEMENT TO admin")
|
execute_and_fetch_all(superadmin_cursor, "GRANT TRANSACTION_MANAGEMENT TO admin")
|
||||||
execute_and_fetch_all(superadmin_cursor, "GRANT DATABASE * TO admin")
|
execute_and_fetch_all(superadmin_cursor, "GRANT DATABASE * TO admin")
|
||||||
|
|
||||||
|
def on_exit():
|
||||||
|
execute_and_fetch_all(admin_cursor, "DROP USER admin")
|
||||||
|
|
||||||
|
request.addfinalizer(on_exit)
|
||||||
|
|
||||||
# Admin starts running infinite query
|
# Admin starts running infinite query
|
||||||
process = multiprocessing.Process(
|
process = multiprocessing.Process(
|
||||||
target=process_function, args=(superadmin_cursor, ["CALL infinite_query.long_query() YIELD my_id RETURN my_id"])
|
target=process_function, args=(superadmin_cursor, ["CALL infinite_query.long_query() YIELD my_id RETURN my_id"])
|
||||||
@ -194,17 +220,23 @@ def test_admin_sees_superadmin():
|
|||||||
# Kill transaction
|
# Kill transaction
|
||||||
long_transaction_id = show_results[1 - executing_index][1]
|
long_transaction_id = show_results[1 - executing_index][1]
|
||||||
execute_and_fetch_all(admin_cursor, f"TERMINATE TRANSACTIONS '{long_transaction_id}'")
|
execute_and_fetch_all(admin_cursor, f"TERMINATE TRANSACTIONS '{long_transaction_id}'")
|
||||||
execute_and_fetch_all(admin_cursor, "DROP USER admin")
|
|
||||||
superadmin_connection.close()
|
superadmin_connection.close()
|
||||||
|
|
||||||
|
|
||||||
def test_admin_can_see_user_transaction():
|
def test_admin_can_see_user_transaction(request):
|
||||||
"""Tests that admin can see user's transaction and kill it."""
|
"""Tests that admin can see user's transaction and kill it."""
|
||||||
superadmin_cursor = connect().cursor()
|
superadmin_cursor = connect().cursor()
|
||||||
execute_and_fetch_all(superadmin_cursor, "CREATE USER admin")
|
execute_and_fetch_all(superadmin_cursor, "CREATE USER admin")
|
||||||
execute_and_fetch_all(superadmin_cursor, "GRANT TRANSACTION_MANAGEMENT TO admin")
|
execute_and_fetch_all(superadmin_cursor, "GRANT TRANSACTION_MANAGEMENT TO admin")
|
||||||
execute_and_fetch_all(superadmin_cursor, "GRANT DATABASE * TO admin")
|
execute_and_fetch_all(superadmin_cursor, "GRANT DATABASE * TO admin")
|
||||||
execute_and_fetch_all(superadmin_cursor, "CREATE USER user")
|
execute_and_fetch_all(superadmin_cursor, "CREATE USER user")
|
||||||
|
|
||||||
|
def on_exit():
|
||||||
|
execute_and_fetch_all(superadmin_cursor, "DROP USER admin")
|
||||||
|
execute_and_fetch_all(superadmin_cursor, "DROP USER user")
|
||||||
|
|
||||||
|
request.addfinalizer(on_exit)
|
||||||
|
|
||||||
# Admin starts running infinite query
|
# Admin starts running infinite query
|
||||||
admin_connection = connect(username="admin", password="")
|
admin_connection = connect(username="admin", password="")
|
||||||
admin_cursor = admin_connection.cursor()
|
admin_cursor = admin_connection.cursor()
|
||||||
@ -229,13 +261,11 @@ def test_admin_can_see_user_transaction():
|
|||||||
# Kill transaction
|
# Kill transaction
|
||||||
long_transaction_id = show_results[1 - executing_index][1]
|
long_transaction_id = show_results[1 - executing_index][1]
|
||||||
execute_and_fetch_all(admin_cursor, f"TERMINATE TRANSACTIONS '{long_transaction_id}'")
|
execute_and_fetch_all(admin_cursor, f"TERMINATE TRANSACTIONS '{long_transaction_id}'")
|
||||||
execute_and_fetch_all(superadmin_cursor, "DROP USER admin")
|
|
||||||
execute_and_fetch_all(superadmin_cursor, "DROP USER user")
|
|
||||||
admin_connection.close()
|
admin_connection.close()
|
||||||
user_connection.close()
|
user_connection.close()
|
||||||
|
|
||||||
|
|
||||||
def test_user_cannot_see_admin_transaction():
|
def test_user_cannot_see_admin_transaction(request):
|
||||||
"""User cannot see admin's transaction but other admin can and he can kill it."""
|
"""User cannot see admin's transaction but other admin can and he can kill it."""
|
||||||
# Superadmin creates two admins and one user
|
# Superadmin creates two admins and one user
|
||||||
superadmin_cursor = connect().cursor()
|
superadmin_cursor = connect().cursor()
|
||||||
@ -246,6 +276,14 @@ def test_user_cannot_see_admin_transaction():
|
|||||||
execute_and_fetch_all(superadmin_cursor, "GRANT TRANSACTION_MANAGEMENT TO admin2")
|
execute_and_fetch_all(superadmin_cursor, "GRANT TRANSACTION_MANAGEMENT TO admin2")
|
||||||
execute_and_fetch_all(superadmin_cursor, "GRANT DATABASE * TO admin2")
|
execute_and_fetch_all(superadmin_cursor, "GRANT DATABASE * TO admin2")
|
||||||
execute_and_fetch_all(superadmin_cursor, "CREATE USER user")
|
execute_and_fetch_all(superadmin_cursor, "CREATE USER user")
|
||||||
|
|
||||||
|
def on_exit():
|
||||||
|
execute_and_fetch_all(superadmin_cursor, "DROP USER admin1")
|
||||||
|
execute_and_fetch_all(superadmin_cursor, "DROP USER admin2")
|
||||||
|
execute_and_fetch_all(superadmin_cursor, "DROP USER user")
|
||||||
|
|
||||||
|
request.addfinalizer(on_exit)
|
||||||
|
|
||||||
admin_connection_1 = connect(username="admin1", password="")
|
admin_connection_1 = connect(username="admin1", password="")
|
||||||
admin_cursor_1 = admin_connection_1.cursor()
|
admin_cursor_1 = admin_connection_1.cursor()
|
||||||
admin_connection_2 = connect(username="admin2", password="")
|
admin_connection_2 = connect(username="admin2", password="")
|
||||||
@ -274,9 +312,6 @@ def test_user_cannot_see_admin_transaction():
|
|||||||
# Kill transaction
|
# Kill transaction
|
||||||
long_transaction_id = show_results[1 - executing_index][1]
|
long_transaction_id = show_results[1 - executing_index][1]
|
||||||
execute_and_fetch_all(admin_cursor_2, f"TERMINATE TRANSACTIONS '{long_transaction_id}'")
|
execute_and_fetch_all(admin_cursor_2, f"TERMINATE TRANSACTIONS '{long_transaction_id}'")
|
||||||
execute_and_fetch_all(superadmin_cursor, "DROP USER admin1")
|
|
||||||
execute_and_fetch_all(superadmin_cursor, "DROP USER admin2")
|
|
||||||
execute_and_fetch_all(superadmin_cursor, "DROP USER user")
|
|
||||||
admin_connection_1.close()
|
admin_connection_1.close()
|
||||||
admin_connection_2.close()
|
admin_connection_2.close()
|
||||||
user_connection.close()
|
user_connection.close()
|
||||||
@ -300,12 +335,18 @@ def test_killing_multiple_non_existing_transactions():
|
|||||||
assert results[i][1] == False # not killed
|
assert results[i][1] == False # not killed
|
||||||
|
|
||||||
|
|
||||||
def test_admin_killing_multiple_non_existing_transactions():
|
def test_admin_killing_multiple_non_existing_transactions(request):
|
||||||
# Starting, superadmin admin
|
# Starting, superadmin admin
|
||||||
superadmin_cursor = connect().cursor()
|
superadmin_cursor = connect().cursor()
|
||||||
execute_and_fetch_all(superadmin_cursor, "CREATE USER admin")
|
execute_and_fetch_all(superadmin_cursor, "CREATE USER admin")
|
||||||
execute_and_fetch_all(superadmin_cursor, "GRANT TRANSACTION_MANAGEMENT TO admin")
|
execute_and_fetch_all(superadmin_cursor, "GRANT TRANSACTION_MANAGEMENT TO admin")
|
||||||
execute_and_fetch_all(superadmin_cursor, "GRANT DATABASE * TO admin")
|
execute_and_fetch_all(superadmin_cursor, "GRANT DATABASE * TO admin")
|
||||||
|
|
||||||
|
def on_exit():
|
||||||
|
execute_and_fetch_all(admin_cursor, "DROP USER admin")
|
||||||
|
|
||||||
|
request.addfinalizer(on_exit)
|
||||||
|
|
||||||
# Connect with admin
|
# Connect with admin
|
||||||
admin_cursor = connect(username="admin", password="").cursor()
|
admin_cursor = connect(username="admin", password="").cursor()
|
||||||
transactions_id = ["'1'", "'2'", "'3'"]
|
transactions_id = ["'1'", "'2'", "'3'"]
|
||||||
@ -314,7 +355,6 @@ def test_admin_killing_multiple_non_existing_transactions():
|
|||||||
for i in range(len(results)):
|
for i in range(len(results)):
|
||||||
assert results[i][0] == eval(transactions_id[i]) # transaction id
|
assert results[i][0] == eval(transactions_id[i]) # transaction id
|
||||||
assert results[i][1] == False # not killed
|
assert results[i][1] == False # not killed
|
||||||
execute_and_fetch_all(admin_cursor, "DROP USER admin")
|
|
||||||
|
|
||||||
|
|
||||||
def test_user_killing_some_transactions():
|
def test_user_killing_some_transactions():
|
||||||
|
@ -193,12 +193,12 @@ def execute_test(memgraph_binary, tester_binary, checker_binary):
|
|||||||
"GRANT DATABASE db2 TO user",
|
"GRANT DATABASE db2 TO user",
|
||||||
"CREATE USER useR2 IDENTIFIED BY 'user'",
|
"CREATE USER useR2 IDENTIFIED BY 'user'",
|
||||||
"GRANT DATABASE db2 TO user2",
|
"GRANT DATABASE db2 TO user2",
|
||||||
"REVOKE DATABASE memgraph FROM user2",
|
"DENY DATABASE memgraph FROM user2",
|
||||||
"SET MAIN DATABASE db2 FOR user2",
|
"SET MAIN DATABASE db2 FOR user2",
|
||||||
"CREATE USER user3 IDENTIFIED BY 'user'",
|
"CREATE USER user3 IDENTIFIED BY 'user'",
|
||||||
"GRANT ALL PRIVILEGES TO user3",
|
"GRANT ALL PRIVILEGES TO user3",
|
||||||
"GRANT DATABASE * TO user3",
|
"GRANT DATABASE * TO user3",
|
||||||
"REVOKE DATABASE memgraph FROM user3",
|
"DENY DATABASE memgraph FROM user3",
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -139,7 +139,7 @@ class Memgraph:
|
|||||||
def initialize_test(memgraph, tester_binary, **kwargs):
|
def initialize_test(memgraph, tester_binary, **kwargs):
|
||||||
memgraph.start(module_executable="")
|
memgraph.start(module_executable="")
|
||||||
|
|
||||||
execute_tester(tester_binary, ["CREATE USER root", "GRANT ALL PRIVILEGES TO root"])
|
execute_tester(tester_binary, ["CREATE ROLE root_role", "GRANT ALL PRIVILEGES TO root_role"])
|
||||||
check_login = kwargs.pop("check_login", True)
|
check_login = kwargs.pop("check_login", True)
|
||||||
memgraph.restart(**kwargs)
|
memgraph.restart(**kwargs)
|
||||||
if check_login:
|
if check_login:
|
||||||
@ -149,20 +149,24 @@ def initialize_test(memgraph, tester_binary, **kwargs):
|
|||||||
# Tests
|
# Tests
|
||||||
|
|
||||||
|
|
||||||
def test_basic(memgraph, tester_binary):
|
def test_module_ux(memgraph, tester_binary):
|
||||||
initialize_test(memgraph, tester_binary)
|
initialize_test(memgraph, tester_binary)
|
||||||
execute_tester(tester_binary, [], "alice")
|
execute_tester(tester_binary, ["CREATE USER user1"], "root", query_should_fail=True)
|
||||||
execute_tester(tester_binary, ["GRANT MATCH TO alice"], "root")
|
execute_tester(tester_binary, ["CREATE ROLE role1"], "root", query_should_fail=False)
|
||||||
execute_tester(tester_binary, ["MATCH (n) RETURN n"], "alice")
|
execute_tester(tester_binary, ["DROP USER user1"], "root", query_should_fail=True)
|
||||||
|
execute_tester(tester_binary, ["DROP ROLE role1"], "root", query_should_fail=False)
|
||||||
|
execute_tester(tester_binary, ["SET ROLE FOR user1 TO role1"], "root", query_should_fail=True)
|
||||||
|
execute_tester(tester_binary, ["CLEAR ROLE FOR user1"], "root", query_should_fail=True)
|
||||||
memgraph.stop()
|
memgraph.stop()
|
||||||
|
|
||||||
|
|
||||||
def test_only_existing_users(memgraph, tester_binary):
|
def test_user_auth(memgraph, tester_binary):
|
||||||
initialize_test(memgraph, tester_binary, create_missing_user=False)
|
initialize_test(memgraph, tester_binary)
|
||||||
execute_tester(tester_binary, [], "alice", auth_should_fail=True)
|
execute_tester(tester_binary, [], "alice", auth_should_fail=True)
|
||||||
execute_tester(tester_binary, ["CREATE USER alice"], "root")
|
execute_tester(tester_binary, ["CREATE ROLE moderator"], "root")
|
||||||
execute_tester(tester_binary, [], "alice")
|
execute_tester(tester_binary, [], "alice")
|
||||||
execute_tester(tester_binary, ["GRANT MATCH TO alice"], "root")
|
execute_tester(tester_binary, ["MATCH (n) RETURN n"], "alice", query_should_fail=True)
|
||||||
|
execute_tester(tester_binary, ["GRANT MATCH TO moderator"], "root")
|
||||||
execute_tester(tester_binary, ["MATCH (n) RETURN n"], "alice")
|
execute_tester(tester_binary, ["MATCH (n) RETURN n"], "alice")
|
||||||
memgraph.stop()
|
memgraph.stop()
|
||||||
|
|
||||||
@ -170,77 +174,50 @@ def test_only_existing_users(memgraph, tester_binary):
|
|||||||
def test_role_mapping(memgraph, tester_binary):
|
def test_role_mapping(memgraph, tester_binary):
|
||||||
initialize_test(memgraph, tester_binary)
|
initialize_test(memgraph, tester_binary)
|
||||||
|
|
||||||
execute_tester(tester_binary, [], "alice")
|
execute_tester(tester_binary, [], "alice", auth_should_fail=True)
|
||||||
|
execute_tester(tester_binary, [], "bob", auth_should_fail=True)
|
||||||
|
execute_tester(tester_binary, [], "carol", auth_should_fail=True)
|
||||||
|
execute_tester(tester_binary, ["CREATE ROLE moderator"], "root")
|
||||||
|
execute_tester(tester_binary, ["CREATE ROLE admin"], "root")
|
||||||
|
execute_tester(tester_binary, [], "alice", auth_should_fail=False)
|
||||||
|
execute_tester(tester_binary, [], "bob", auth_should_fail=True)
|
||||||
|
execute_tester(tester_binary, [], "carol", auth_should_fail=False)
|
||||||
|
|
||||||
execute_tester(tester_binary, ["MATCH (n) RETURN n"], "alice", query_should_fail=True)
|
execute_tester(tester_binary, ["MATCH (n) RETURN n"], "alice", query_should_fail=True)
|
||||||
|
execute_tester(tester_binary, ["MATCH (n) RETURN n"], "carol", query_should_fail=True)
|
||||||
execute_tester(tester_binary, ["GRANT MATCH TO moderator"], "root")
|
execute_tester(tester_binary, ["GRANT MATCH TO moderator"], "root")
|
||||||
execute_tester(tester_binary, ["MATCH (n) RETURN n"], "alice")
|
execute_tester(tester_binary, ["MATCH (n) RETURN n"], "alice", query_should_fail=False)
|
||||||
|
execute_tester(tester_binary, ["MATCH (n) RETURN n"], "carol", query_should_fail=True)
|
||||||
|
execute_tester(tester_binary, ["GRANT MATCH TO admin"], "root")
|
||||||
|
execute_tester(tester_binary, ["MATCH (n) RETURN n"], "alice", query_should_fail=False)
|
||||||
|
execute_tester(tester_binary, ["MATCH (n) RETURN n"], "carol", query_should_fail=False)
|
||||||
|
|
||||||
execute_tester(tester_binary, [], "bob")
|
execute_tester(tester_binary, ["CREATE (n) RETURN n"], "alice", query_should_fail=True)
|
||||||
execute_tester(tester_binary, ["MATCH (n) RETURN n"], "bob", query_should_fail=True)
|
|
||||||
|
|
||||||
execute_tester(tester_binary, [], "carol")
|
|
||||||
execute_tester(tester_binary, ["CREATE (n) RETURN n"], "carol", query_should_fail=True)
|
execute_tester(tester_binary, ["CREATE (n) RETURN n"], "carol", query_should_fail=True)
|
||||||
execute_tester(tester_binary, ["GRANT CREATE TO admin"], "root")
|
execute_tester(tester_binary, ["GRANT CREATE TO admin"], "root")
|
||||||
execute_tester(tester_binary, ["CREATE (n) RETURN n"], "carol")
|
execute_tester(tester_binary, ["CREATE (n) RETURN n"], "alice", query_should_fail=True)
|
||||||
execute_tester(tester_binary, ["CREATE (n) RETURN n"], "dave")
|
execute_tester(tester_binary, ["CREATE (n) RETURN n"], "carol", query_should_fail=False)
|
||||||
|
|
||||||
memgraph.stop()
|
memgraph.stop()
|
||||||
|
|
||||||
|
|
||||||
|
def test_instance_restart(memgraph, tester_binary):
|
||||||
|
initialize_test(memgraph, tester_binary)
|
||||||
|
execute_tester(tester_binary, ["CREATE ROLE moderator"], "root")
|
||||||
|
execute_tester(tester_binary, ["GRANT MATCH TO moderator"], "root")
|
||||||
|
execute_tester(tester_binary, ["MATCH (n) RETURN n"], "alice")
|
||||||
|
memgraph.restart()
|
||||||
|
execute_tester(tester_binary, ["MATCH (n) RETURN n"], "alice")
|
||||||
|
memgraph.stop()
|
||||||
|
|
||||||
|
|
||||||
def test_role_removal(memgraph, tester_binary):
|
def test_role_removal(memgraph, tester_binary):
|
||||||
initialize_test(memgraph, tester_binary)
|
initialize_test(memgraph, tester_binary)
|
||||||
execute_tester(tester_binary, [], "alice")
|
execute_tester(tester_binary, ["CREATE ROLE moderator"], "root")
|
||||||
execute_tester(tester_binary, ["MATCH (n) RETURN n"], "alice", query_should_fail=True)
|
|
||||||
execute_tester(tester_binary, ["GRANT MATCH TO moderator"], "root")
|
execute_tester(tester_binary, ["GRANT MATCH TO moderator"], "root")
|
||||||
execute_tester(tester_binary, ["MATCH (n) RETURN n"], "alice")
|
execute_tester(tester_binary, ["MATCH (n) RETURN n"], "alice")
|
||||||
memgraph.restart(manage_roles=False)
|
execute_tester(tester_binary, ["DROP ROLE moderator"], "root")
|
||||||
execute_tester(tester_binary, ["MATCH (n) RETURN n"], "alice")
|
|
||||||
execute_tester(tester_binary, ["CLEAR ROLE FOR alice"], "root")
|
|
||||||
execute_tester(tester_binary, ["MATCH (n) RETURN n"], "alice", query_should_fail=True)
|
|
||||||
memgraph.stop()
|
|
||||||
|
|
||||||
|
|
||||||
def test_only_existing_roles(memgraph, tester_binary):
|
|
||||||
initialize_test(memgraph, tester_binary, create_missing_role=False)
|
|
||||||
execute_tester(tester_binary, [], "bob")
|
|
||||||
execute_tester(tester_binary, [], "alice", auth_should_fail=True)
|
execute_tester(tester_binary, [], "alice", auth_should_fail=True)
|
||||||
execute_tester(tester_binary, ["CREATE ROLE moderator"], "root")
|
|
||||||
execute_tester(tester_binary, [], "alice")
|
|
||||||
memgraph.stop()
|
|
||||||
|
|
||||||
|
|
||||||
def test_role_is_user(memgraph, tester_binary):
|
|
||||||
initialize_test(memgraph, tester_binary)
|
|
||||||
execute_tester(tester_binary, [], "admin")
|
|
||||||
execute_tester(tester_binary, [], "carol", auth_should_fail=True)
|
|
||||||
memgraph.stop()
|
|
||||||
|
|
||||||
|
|
||||||
def test_user_is_role(memgraph, tester_binary):
|
|
||||||
initialize_test(memgraph, tester_binary)
|
|
||||||
execute_tester(tester_binary, [], "carol")
|
|
||||||
execute_tester(tester_binary, [], "admin", auth_should_fail=True)
|
|
||||||
memgraph.stop()
|
|
||||||
|
|
||||||
|
|
||||||
def test_user_permissions_persistancy(memgraph, tester_binary):
|
|
||||||
initialize_test(memgraph, tester_binary)
|
|
||||||
execute_tester(tester_binary, ["CREATE USER alice", "GRANT MATCH TO alice"], "root")
|
|
||||||
execute_tester(tester_binary, ["MATCH (n) RETURN n"], "alice")
|
|
||||||
memgraph.stop()
|
|
||||||
|
|
||||||
|
|
||||||
def test_role_permissions_persistancy(memgraph, tester_binary):
|
|
||||||
initialize_test(memgraph, tester_binary)
|
|
||||||
execute_tester(tester_binary, ["CREATE ROLE moderator", "GRANT MATCH TO moderator"], "root")
|
|
||||||
execute_tester(tester_binary, ["MATCH (n) RETURN n"], "alice")
|
|
||||||
memgraph.stop()
|
|
||||||
|
|
||||||
|
|
||||||
def test_only_authentication(memgraph, tester_binary):
|
|
||||||
initialize_test(memgraph, tester_binary, manage_roles=False)
|
|
||||||
execute_tester(tester_binary, ["CREATE ROLE moderator", "GRANT MATCH TO moderator"], "root")
|
|
||||||
execute_tester(tester_binary, ["MATCH (n) RETURN n"], "alice", query_should_fail=True)
|
|
||||||
memgraph.stop()
|
memgraph.stop()
|
||||||
|
|
||||||
|
|
||||||
@ -258,36 +235,36 @@ def test_wrong_suffix(memgraph, tester_binary):
|
|||||||
|
|
||||||
def test_suffix_with_spaces(memgraph, tester_binary):
|
def test_suffix_with_spaces(memgraph, tester_binary):
|
||||||
initialize_test(memgraph, tester_binary, suffix=", ou= people, dc = memgraph, dc = com")
|
initialize_test(memgraph, tester_binary, suffix=", ou= people, dc = memgraph, dc = com")
|
||||||
execute_tester(tester_binary, ["CREATE USER alice", "GRANT MATCH TO alice"], "root")
|
|
||||||
execute_tester(tester_binary, ["MATCH (n) RETURN n"], "alice")
|
|
||||||
memgraph.stop()
|
|
||||||
|
|
||||||
|
|
||||||
def test_role_mapping_wrong_root_dn(memgraph, tester_binary):
|
|
||||||
initialize_test(memgraph, tester_binary, root_dn="ou=invalid,dc=memgraph,dc=com")
|
|
||||||
execute_tester(tester_binary, ["CREATE ROLE moderator", "GRANT MATCH TO moderator"], "root")
|
execute_tester(tester_binary, ["CREATE ROLE moderator", "GRANT MATCH TO moderator"], "root")
|
||||||
execute_tester(tester_binary, ["MATCH (n) RETURN n"], "alice", query_should_fail=True)
|
|
||||||
memgraph.restart()
|
|
||||||
execute_tester(tester_binary, ["MATCH (n) RETURN n"], "alice")
|
execute_tester(tester_binary, ["MATCH (n) RETURN n"], "alice")
|
||||||
memgraph.stop()
|
memgraph.stop()
|
||||||
|
|
||||||
|
|
||||||
def test_role_mapping_wrong_root_objectclass(memgraph, tester_binary):
|
# def test_role_mapping_wrong_root_dn(memgraph, tester_binary):
|
||||||
initialize_test(memgraph, tester_binary, root_objectclass="person")
|
# initialize_test(memgraph, tester_binary, root_dn="ou=invalid,dc=memgraph,dc=com")
|
||||||
execute_tester(tester_binary, ["CREATE ROLE moderator", "GRANT MATCH TO moderator"], "root")
|
# execute_tester(tester_binary, ["CREATE ROLE moderator", "GRANT MATCH TO moderator"], "root")
|
||||||
execute_tester(tester_binary, ["MATCH (n) RETURN n"], "alice", query_should_fail=True)
|
# execute_tester(tester_binary, ["MATCH (n) RETURN n"], "alice", query_should_fail=True)
|
||||||
memgraph.restart()
|
# memgraph.restart()
|
||||||
execute_tester(tester_binary, ["MATCH (n) RETURN n"], "alice")
|
# execute_tester(tester_binary, ["MATCH (n) RETURN n"], "alice")
|
||||||
memgraph.stop()
|
# memgraph.stop()
|
||||||
|
|
||||||
|
|
||||||
def test_role_mapping_wrong_user_attribute(memgraph, tester_binary):
|
# def test_role_mapping_wrong_root_objectclass(memgraph, tester_binary):
|
||||||
initialize_test(memgraph, tester_binary, user_attribute="cn")
|
# initialize_test(memgraph, tester_binary, root_objectclass="person")
|
||||||
execute_tester(tester_binary, ["CREATE ROLE moderator", "GRANT MATCH TO moderator"], "root")
|
# execute_tester(tester_binary, ["CREATE ROLE moderator", "GRANT MATCH TO moderator"], "root")
|
||||||
execute_tester(tester_binary, ["MATCH (n) RETURN n"], "alice", query_should_fail=True)
|
# execute_tester(tester_binary, ["MATCH (n) RETURN n"], "alice", query_should_fail=True)
|
||||||
memgraph.restart()
|
# memgraph.restart()
|
||||||
execute_tester(tester_binary, ["MATCH (n) RETURN n"], "alice")
|
# execute_tester(tester_binary, ["MATCH (n) RETURN n"], "alice")
|
||||||
memgraph.stop()
|
# memgraph.stop()
|
||||||
|
|
||||||
|
|
||||||
|
# def test_role_mapping_wrong_user_attribute(memgraph, tester_binary):
|
||||||
|
# initialize_test(memgraph, tester_binary, user_attribute="cn")
|
||||||
|
# execute_tester(tester_binary, ["CREATE ROLE moderator", "GRANT MATCH TO moderator"], "root")
|
||||||
|
# execute_tester(tester_binary, ["MATCH (n) RETURN n"], "alice", query_should_fail=True)
|
||||||
|
# memgraph.restart()
|
||||||
|
# execute_tester(tester_binary, ["MATCH (n) RETURN n"], "alice")
|
||||||
|
# memgraph.stop()
|
||||||
|
|
||||||
|
|
||||||
def test_wrong_password(memgraph, tester_binary):
|
def test_wrong_password(memgraph, tester_binary):
|
||||||
@ -297,31 +274,9 @@ def test_wrong_password(memgraph, tester_binary):
|
|||||||
memgraph.stop()
|
memgraph.stop()
|
||||||
|
|
||||||
|
|
||||||
def test_password_persistancy(memgraph, tester_binary):
|
|
||||||
initialize_test(memgraph, tester_binary, check_login=False)
|
|
||||||
memgraph.restart(module_executable="")
|
|
||||||
execute_tester(tester_binary, ["SHOW USERS"], "root", password="sudo")
|
|
||||||
execute_tester(tester_binary, ["SHOW USERS"], "root", password="root")
|
|
||||||
memgraph.restart()
|
|
||||||
execute_tester(tester_binary, [], "root", password="sudo", auth_should_fail=True)
|
|
||||||
execute_tester(tester_binary, ["SHOW USERS"], "root", password="root")
|
|
||||||
memgraph.restart(module_executable="")
|
|
||||||
execute_tester(tester_binary, [], "root", password="sudo", auth_should_fail=True)
|
|
||||||
execute_tester(tester_binary, ["SHOW USERS"], "root", password="root")
|
|
||||||
memgraph.stop()
|
|
||||||
|
|
||||||
|
|
||||||
def test_user_multiple_roles(memgraph, tester_binary):
|
def test_user_multiple_roles(memgraph, tester_binary):
|
||||||
initialize_test(memgraph, tester_binary, check_login=False)
|
initialize_test(memgraph, tester_binary)
|
||||||
memgraph.restart()
|
execute_tester(tester_binary, ["MATCH (n) RETURN n"], "eve", auth_should_fail=True)
|
||||||
execute_tester(tester_binary, ["MATCH (n) RETURN n"], "eve", query_should_fail=True)
|
|
||||||
execute_tester(tester_binary, ["GRANT MATCH TO moderator"], "root", query_should_fail=True)
|
|
||||||
memgraph.restart(manage_roles=False)
|
|
||||||
execute_tester(tester_binary, ["MATCH (n) RETURN n"], "eve", query_should_fail=True)
|
|
||||||
execute_tester(tester_binary, ["GRANT MATCH TO moderator"], "root", query_should_fail=True)
|
|
||||||
memgraph.restart(manage_roles=False, root_dn="")
|
|
||||||
execute_tester(tester_binary, ["MATCH (n) RETURN n"], "eve", query_should_fail=True)
|
|
||||||
execute_tester(tester_binary, ["GRANT MATCH TO moderator"], "root", query_should_fail=True)
|
|
||||||
memgraph.stop()
|
memgraph.stop()
|
||||||
|
|
||||||
|
|
||||||
|
@ -84,6 +84,13 @@ objectclass: organizationalUnit
|
|||||||
objectclass: top
|
objectclass: top
|
||||||
ou: roles
|
ou: roles
|
||||||
|
|
||||||
|
# Role root
|
||||||
|
dn: cn=root_role,ou=roles,dc=memgraph,dc=com
|
||||||
|
cn: root_role
|
||||||
|
member: cn=root,ou=people,dc=memgraph,dc=com
|
||||||
|
objectclass: groupOfNames
|
||||||
|
objectclass: top
|
||||||
|
|
||||||
# Role moderator
|
# Role moderator
|
||||||
dn: cn=moderator,ou=roles,dc=memgraph,dc=com
|
dn: cn=moderator,ou=roles,dc=memgraph,dc=com
|
||||||
cn: moderator
|
cn: moderator
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2022 Memgraph Ltd.
|
// Copyright 2024 Memgraph Ltd.
|
||||||
//
|
//
|
||||||
// Use of this software is governed by the Business Source License
|
// 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
|
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||||
@ -48,6 +48,7 @@ int main(int argc, char **argv) {
|
|||||||
}
|
}
|
||||||
if (FLAGS_auth_should_fail) {
|
if (FLAGS_auth_should_fail) {
|
||||||
MG_ASSERT(!what.empty(), "The authentication should have failed!");
|
MG_ASSERT(!what.empty(), "The authentication should have failed!");
|
||||||
|
return 0; // Auth failed, nothing left to do
|
||||||
} else {
|
} else {
|
||||||
MG_ASSERT(what.empty(),
|
MG_ASSERT(what.empty(),
|
||||||
"The authentication should have succeeded, but "
|
"The authentication should have succeeded, but "
|
||||||
|
@ -50,6 +50,8 @@ int main(int argc, char *argv[]) {
|
|||||||
memgraph::query::Interpreter interpreter{&interpreter_context, db_acc};
|
memgraph::query::Interpreter interpreter{&interpreter_context, db_acc};
|
||||||
|
|
||||||
ResultStreamFaker stream(db_acc->storage());
|
ResultStreamFaker stream(db_acc->storage());
|
||||||
|
memgraph::query::AllowEverythingAuthChecker auth_checker;
|
||||||
|
interpreter.SetUser(auth_checker.GenQueryUser(std::nullopt, std::nullopt));
|
||||||
auto [header, _1, qid, _2] = interpreter.Prepare(argv[1], {}, {});
|
auto [header, _1, qid, _2] = interpreter.Prepare(argv[1], {}, {});
|
||||||
stream.Header(header);
|
stream.Header(header);
|
||||||
auto summary = interpreter.PullAll(&stream);
|
auto summary = interpreter.PullAll(&stream);
|
||||||
|
@ -280,6 +280,8 @@ TEST_F(AuthWithStorage, RoleManipulations) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
const auto all = auth->AllUsernames();
|
||||||
|
for (const auto &user : all) std::cout << user << std::endl;
|
||||||
auto users = auth->AllUsers();
|
auto users = auth->AllUsers();
|
||||||
std::sort(users.begin(), users.end(), [](const User &a, const User &b) { return a.username() < b.username(); });
|
std::sort(users.begin(), users.end(), [](const User &a, const User &b) { return a.username() < b.username(); });
|
||||||
ASSERT_EQ(users.size(), 2);
|
ASSERT_EQ(users.size(), 2);
|
||||||
@ -774,14 +776,16 @@ TEST_F(AuthWithStorage, CaseInsensitivity) {
|
|||||||
|
|
||||||
// Authenticate
|
// Authenticate
|
||||||
{
|
{
|
||||||
auto user = auth->Authenticate("alice", "alice");
|
auto user_or_role = auth->Authenticate("alice", "alice");
|
||||||
ASSERT_TRUE(user);
|
ASSERT_TRUE(user_or_role);
|
||||||
ASSERT_EQ(user->username(), "alice");
|
const auto &user = std::get<memgraph::auth::User>(*user_or_role);
|
||||||
|
ASSERT_EQ(user.username(), "alice");
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto user = auth->Authenticate("alICe", "alice");
|
auto user_or_role = auth->Authenticate("alICe", "alice");
|
||||||
ASSERT_TRUE(user);
|
ASSERT_TRUE(user_or_role);
|
||||||
ASSERT_EQ(user->username(), "alice");
|
const auto &user = std::get<memgraph::auth::User>(*user_or_role);
|
||||||
|
ASSERT_EQ(user.username(), "alice");
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUser
|
// GetUser
|
||||||
@ -809,6 +813,8 @@ TEST_F(AuthWithStorage, CaseInsensitivity) {
|
|||||||
|
|
||||||
// AllUsers
|
// AllUsers
|
||||||
{
|
{
|
||||||
|
const auto all = auth->AllUsernames();
|
||||||
|
for (const auto &user : all) std::cout << user << std::endl;
|
||||||
auto users = auth->AllUsers();
|
auto users = auth->AllUsers();
|
||||||
ASSERT_EQ(users.size(), 2);
|
ASSERT_EQ(users.size(), 2);
|
||||||
std::sort(users.begin(), users.end(), [](const auto &a, const auto &b) { return a.username() < b.username(); });
|
std::sort(users.begin(), users.end(), [](const auto &a, const auto &b) { return a.username() < b.username(); });
|
||||||
|
@ -12,11 +12,14 @@
|
|||||||
#include <gmock/gmock.h>
|
#include <gmock/gmock.h>
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include "auth/exceptions.hpp"
|
||||||
#include "auth/models.hpp"
|
#include "auth/models.hpp"
|
||||||
#include "disk_test_utils.hpp"
|
#include "disk_test_utils.hpp"
|
||||||
#include "glue/auth_checker.hpp"
|
#include "glue/auth_checker.hpp"
|
||||||
|
|
||||||
#include "license/license.hpp"
|
#include "license/license.hpp"
|
||||||
|
#include "query/frontend/ast/ast.hpp"
|
||||||
|
#include "query/query_user.hpp"
|
||||||
#include "query_plan_common.hpp"
|
#include "query_plan_common.hpp"
|
||||||
#include "storage/v2/config.hpp"
|
#include "storage/v2/config.hpp"
|
||||||
#include "storage/v2/disk/storage.hpp"
|
#include "storage/v2/disk/storage.hpp"
|
||||||
@ -225,4 +228,123 @@ TYPED_TEST(FineGrainedAuthCheckerFixture, GrantAndDenySpecificEdgeTypes) {
|
|||||||
ASSERT_FALSE(auth_checker.Has(this->r3, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
|
ASSERT_FALSE(auth_checker.Has(this->r3, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
|
||||||
ASSERT_FALSE(auth_checker.Has(this->r4, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
|
ASSERT_FALSE(auth_checker.Has(this->r4, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(AuthChecker, Generate) {
|
||||||
|
std::filesystem::path auth_dir{std::filesystem::temp_directory_path() / "MG_auth_checker"};
|
||||||
|
memgraph::utils::OnScopeExit clean([&]() {
|
||||||
|
if (std::filesystem::exists(auth_dir)) {
|
||||||
|
std::filesystem::remove_all(auth_dir);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
memgraph::auth::SynchedAuth auth(auth_dir, memgraph::auth::Auth::Config{/* default config */});
|
||||||
|
memgraph::glue::AuthChecker auth_checker(&auth);
|
||||||
|
|
||||||
|
auto empty_user = auth_checker.GenQueryUser(std::nullopt, std::nullopt);
|
||||||
|
ASSERT_THROW(auth_checker.GenQueryUser("does_not_exist", std::nullopt), memgraph::auth::AuthException);
|
||||||
|
|
||||||
|
EXPECT_FALSE(empty_user && *empty_user);
|
||||||
|
// Still empty auth, so the above should have su permissions
|
||||||
|
using enum memgraph::query::AuthQuery::Privilege;
|
||||||
|
EXPECT_TRUE(empty_user->IsAuthorized({AUTH, REMOVE, REPLICATION}, "", &memgraph::query::session_long_policy));
|
||||||
|
EXPECT_TRUE(empty_user->IsAuthorized({FREE_MEMORY, WEBSOCKET, MULTI_DATABASE_EDIT}, "memgraph",
|
||||||
|
&memgraph::query::session_long_policy));
|
||||||
|
EXPECT_TRUE(
|
||||||
|
empty_user->IsAuthorized({TRIGGER, DURABILITY, STORAGE_MODE}, "some_db", &memgraph::query::session_long_policy));
|
||||||
|
|
||||||
|
// Add user
|
||||||
|
auth->AddUser("new_user");
|
||||||
|
|
||||||
|
// ~Empty user should now fail~
|
||||||
|
// NOTE: Cache invalidation has been disabled, so this will pass; change if it is ever turned on
|
||||||
|
EXPECT_TRUE(empty_user->IsAuthorized({AUTH, REMOVE, REPLICATION}, "", &memgraph::query::session_long_policy));
|
||||||
|
EXPECT_TRUE(empty_user->IsAuthorized({FREE_MEMORY, WEBSOCKET, MULTI_DATABASE_EDIT}, "memgraph",
|
||||||
|
&memgraph::query::session_long_policy));
|
||||||
|
EXPECT_TRUE(
|
||||||
|
empty_user->IsAuthorized({TRIGGER, DURABILITY, STORAGE_MODE}, "some_db", &memgraph::query::session_long_policy));
|
||||||
|
|
||||||
|
// Add role and new user
|
||||||
|
auto new_role = *auth->AddRole("new_role");
|
||||||
|
auto new_user2 = *auth->AddUser("new_user2");
|
||||||
|
auto role = auth_checker.GenQueryUser("anyuser", "new_role");
|
||||||
|
auto user2 = auth_checker.GenQueryUser("new_user2", std::nullopt);
|
||||||
|
|
||||||
|
// Should be permission-less by default
|
||||||
|
EXPECT_FALSE(role->IsAuthorized({AUTH}, "memgraph", &memgraph::query::session_long_policy));
|
||||||
|
EXPECT_FALSE(role->IsAuthorized({FREE_MEMORY}, "memgraph", &memgraph::query::session_long_policy));
|
||||||
|
EXPECT_FALSE(role->IsAuthorized({TRIGGER}, "memgraph", &memgraph::query::session_long_policy));
|
||||||
|
EXPECT_FALSE(user2->IsAuthorized({AUTH}, "memgraph", &memgraph::query::session_long_policy));
|
||||||
|
EXPECT_FALSE(user2->IsAuthorized({FREE_MEMORY}, "memgraph", &memgraph::query::session_long_policy));
|
||||||
|
EXPECT_FALSE(user2->IsAuthorized({TRIGGER}, "memgraph", &memgraph::query::session_long_policy));
|
||||||
|
|
||||||
|
// Update permissions and recheck
|
||||||
|
new_user2.permissions().Grant(memgraph::auth::Permission::AUTH);
|
||||||
|
new_role.permissions().Grant(memgraph::auth::Permission::TRIGGER);
|
||||||
|
auth->SaveUser(new_user2);
|
||||||
|
auth->SaveRole(new_role);
|
||||||
|
role = auth_checker.GenQueryUser("no check", "new_role");
|
||||||
|
user2 = auth_checker.GenQueryUser("new_user2", std::nullopt);
|
||||||
|
EXPECT_FALSE(role->IsAuthorized({AUTH}, "memgraph", &memgraph::query::session_long_policy));
|
||||||
|
EXPECT_FALSE(role->IsAuthorized({FREE_MEMORY}, "memgraph", &memgraph::query::session_long_policy));
|
||||||
|
EXPECT_TRUE(role->IsAuthorized({TRIGGER}, "memgraph", &memgraph::query::session_long_policy));
|
||||||
|
EXPECT_TRUE(user2->IsAuthorized({AUTH}, "memgraph", &memgraph::query::session_long_policy));
|
||||||
|
EXPECT_FALSE(user2->IsAuthorized({FREE_MEMORY}, "memgraph", &memgraph::query::session_long_policy));
|
||||||
|
EXPECT_FALSE(user2->IsAuthorized({TRIGGER}, "memgraph", &memgraph::query::session_long_policy));
|
||||||
|
|
||||||
|
// Connect role and recheck
|
||||||
|
new_user2.SetRole(new_role);
|
||||||
|
auth->SaveUser(new_user2);
|
||||||
|
user2 = auth_checker.GenQueryUser("new_user2", std::nullopt);
|
||||||
|
EXPECT_TRUE(user2->IsAuthorized({AUTH}, "memgraph", &memgraph::query::session_long_policy));
|
||||||
|
EXPECT_FALSE(user2->IsAuthorized({FREE_MEMORY}, "memgraph", &memgraph::query::session_long_policy));
|
||||||
|
EXPECT_TRUE(user2->IsAuthorized({TRIGGER}, "memgraph", &memgraph::query::session_long_policy));
|
||||||
|
|
||||||
|
// Add database and recheck
|
||||||
|
EXPECT_FALSE(user2->IsAuthorized({AUTH}, "non_default", &memgraph::query::session_long_policy));
|
||||||
|
EXPECT_FALSE(user2->IsAuthorized({AUTH}, "another", &memgraph::query::session_long_policy));
|
||||||
|
EXPECT_TRUE(user2->IsAuthorized({AUTH}, "memgraph", &memgraph::query::session_long_policy));
|
||||||
|
EXPECT_FALSE(role->IsAuthorized({TRIGGER}, "non_default", &memgraph::query::session_long_policy));
|
||||||
|
EXPECT_FALSE(role->IsAuthorized({TRIGGER}, "another", &memgraph::query::session_long_policy));
|
||||||
|
EXPECT_TRUE(role->IsAuthorized({TRIGGER}, "memgraph", &memgraph::query::session_long_policy));
|
||||||
|
new_user2.db_access().Grant("another");
|
||||||
|
new_role.db_access().Grant("non_default");
|
||||||
|
auth->SaveUser(new_user2);
|
||||||
|
auth->SaveRole(new_role);
|
||||||
|
// Session policy test
|
||||||
|
// Session long policy
|
||||||
|
EXPECT_FALSE(user2->IsAuthorized({AUTH}, "non_default", &memgraph::query::session_long_policy));
|
||||||
|
EXPECT_FALSE(user2->IsAuthorized({AUTH}, "another", &memgraph::query::session_long_policy));
|
||||||
|
EXPECT_TRUE(user2->IsAuthorized({AUTH}, "memgraph", &memgraph::query::session_long_policy));
|
||||||
|
EXPECT_FALSE(role->IsAuthorized({TRIGGER}, "non_default", &memgraph::query::session_long_policy));
|
||||||
|
EXPECT_FALSE(role->IsAuthorized({TRIGGER}, "another", &memgraph::query::session_long_policy));
|
||||||
|
EXPECT_TRUE(role->IsAuthorized({TRIGGER}, "memgraph", &memgraph::query::session_long_policy));
|
||||||
|
// Up to date policy
|
||||||
|
EXPECT_TRUE(user2->IsAuthorized({AUTH}, "non_default", &memgraph::query::up_to_date_policy));
|
||||||
|
EXPECT_TRUE(user2->IsAuthorized({AUTH}, "another", &memgraph::query::up_to_date_policy));
|
||||||
|
EXPECT_TRUE(user2->IsAuthorized({AUTH}, "memgraph", &memgraph::query::up_to_date_policy));
|
||||||
|
EXPECT_TRUE(role->IsAuthorized({TRIGGER}, "non_default", &memgraph::query::up_to_date_policy));
|
||||||
|
EXPECT_FALSE(role->IsAuthorized({TRIGGER}, "another", &memgraph::query::up_to_date_policy));
|
||||||
|
EXPECT_TRUE(role->IsAuthorized({TRIGGER}, "memgraph", &memgraph::query::up_to_date_policy));
|
||||||
|
|
||||||
|
new_user2.db_access().Deny("memgraph");
|
||||||
|
new_role.db_access().Deny("non_default");
|
||||||
|
auth->SaveUser(new_user2);
|
||||||
|
auth->SaveRole(new_role);
|
||||||
|
EXPECT_FALSE(user2->IsAuthorized({AUTH}, "non_default", &memgraph::query::up_to_date_policy));
|
||||||
|
EXPECT_TRUE(user2->IsAuthorized({AUTH}, "another", &memgraph::query::up_to_date_policy));
|
||||||
|
EXPECT_FALSE(user2->IsAuthorized({AUTH}, "memgraph", &memgraph::query::up_to_date_policy));
|
||||||
|
EXPECT_FALSE(role->IsAuthorized({TRIGGER}, "non_default", &memgraph::query::up_to_date_policy));
|
||||||
|
EXPECT_FALSE(role->IsAuthorized({TRIGGER}, "another", &memgraph::query::up_to_date_policy));
|
||||||
|
EXPECT_TRUE(role->IsAuthorized({TRIGGER}, "memgraph", &memgraph::query::up_to_date_policy));
|
||||||
|
|
||||||
|
new_user2.db_access().Revoke("memgraph");
|
||||||
|
new_role.db_access().Revoke("non_default");
|
||||||
|
auth->SaveUser(new_user2);
|
||||||
|
auth->SaveRole(new_role);
|
||||||
|
EXPECT_FALSE(user2->IsAuthorized({AUTH}, "non_default", &memgraph::query::up_to_date_policy));
|
||||||
|
EXPECT_TRUE(user2->IsAuthorized({AUTH}, "another", &memgraph::query::up_to_date_policy));
|
||||||
|
EXPECT_TRUE(user2->IsAuthorized({AUTH}, "memgraph", &memgraph::query::up_to_date_policy));
|
||||||
|
EXPECT_FALSE(role->IsAuthorized({TRIGGER}, "non_default", &memgraph::query::up_to_date_policy));
|
||||||
|
EXPECT_FALSE(role->IsAuthorized({TRIGGER}, "another", &memgraph::query::up_to_date_policy));
|
||||||
|
EXPECT_TRUE(role->IsAuthorized({TRIGGER}, "memgraph", &memgraph::query::up_to_date_policy));
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2023 Memgraph Ltd.
|
// Copyright 2024 Memgraph Ltd.
|
||||||
//
|
//
|
||||||
// Use of this software is governed by the Business Source License
|
// 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
|
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||||
@ -18,6 +18,7 @@ struct InterpreterFaker {
|
|||||||
: interpreter_context(interpreter_context), interpreter(interpreter_context, db) {
|
: interpreter_context(interpreter_context), interpreter(interpreter_context, db) {
|
||||||
interpreter_context->auth_checker = &auth_checker;
|
interpreter_context->auth_checker = &auth_checker;
|
||||||
interpreter_context->interpreters.WithLock([this](auto &interpreters) { interpreters.insert(&interpreter); });
|
interpreter_context->interpreters.WithLock([this](auto &interpreters) { interpreters.insert(&interpreter); });
|
||||||
|
interpreter.SetUser(auth_checker.GenQueryUser(std::nullopt, std::nullopt));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Prepare(const std::string &query, const std::map<std::string, memgraph::storage::PropertyValue> ¶ms = {}) {
|
auto Prepare(const std::string &query, const std::map<std::string, memgraph::storage::PropertyValue> ¶ms = {}) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2022 Memgraph Ltd.
|
// Copyright 2024 Memgraph Ltd.
|
||||||
//
|
//
|
||||||
// Use of this software is governed by the Business Source License
|
// 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
|
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||||
@ -44,11 +44,9 @@ struct MockAuth : public memgraph::communication::websocket::AuthenticationInter
|
|||||||
return authentication;
|
return authentication;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HasUserPermission(const std::string & /*username*/, memgraph::auth::Permission /*permission*/) const override {
|
bool HasPermission(memgraph::auth::Permission /*permission*/) const override { return authorization; }
|
||||||
return authorization;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HasAnyUsers() const override { return has_any_users; }
|
bool AccessControlled() const override { return has_any_users; }
|
||||||
|
|
||||||
bool authentication{true};
|
bool authentication{true};
|
||||||
bool authorization{true};
|
bool authorization{true};
|
||||||
|
@ -21,6 +21,8 @@
|
|||||||
#include "communication/result_stream_faker.hpp"
|
#include "communication/result_stream_faker.hpp"
|
||||||
#include "dbms/database.hpp"
|
#include "dbms/database.hpp"
|
||||||
#include "disk_test_utils.hpp"
|
#include "disk_test_utils.hpp"
|
||||||
|
#include "glue/auth_checker.hpp"
|
||||||
|
#include "query/auth_checker.hpp"
|
||||||
#include "query/config.hpp"
|
#include "query/config.hpp"
|
||||||
#include "query/dump.hpp"
|
#include "query/dump.hpp"
|
||||||
#include "query/interpreter.hpp"
|
#include "query/interpreter.hpp"
|
||||||
@ -216,6 +218,8 @@ DatabaseState GetState(memgraph::storage::Storage *db) {
|
|||||||
auto Execute(memgraph::query::InterpreterContext *context, memgraph::dbms::DatabaseAccess db,
|
auto Execute(memgraph::query::InterpreterContext *context, memgraph::dbms::DatabaseAccess db,
|
||||||
const std::string &query) {
|
const std::string &query) {
|
||||||
memgraph::query::Interpreter interpreter(context, db);
|
memgraph::query::Interpreter interpreter(context, db);
|
||||||
|
memgraph::query::AllowEverythingAuthChecker auth_checker;
|
||||||
|
interpreter.SetUser(auth_checker.GenQueryUser(std::nullopt, std::nullopt));
|
||||||
ResultStreamFaker stream(db->storage());
|
ResultStreamFaker stream(db->storage());
|
||||||
|
|
||||||
auto [header, _1, qid, _2] = interpreter.Prepare(query, {}, {});
|
auto [header, _1, qid, _2] = interpreter.Prepare(query, {}, {});
|
||||||
@ -915,7 +919,10 @@ TYPED_TEST(DumpTest, ExecuteDumpDatabase) {
|
|||||||
class StatefulInterpreter {
|
class StatefulInterpreter {
|
||||||
public:
|
public:
|
||||||
explicit StatefulInterpreter(memgraph::query::InterpreterContext *context, memgraph::dbms::DatabaseAccess db)
|
explicit StatefulInterpreter(memgraph::query::InterpreterContext *context, memgraph::dbms::DatabaseAccess db)
|
||||||
: context_(context), interpreter_(context_, db) {}
|
: context_(context), interpreter_(context_, db) {
|
||||||
|
memgraph::query::AllowEverythingAuthChecker auth_checker;
|
||||||
|
interpreter_.SetUser(auth_checker.GenQueryUser(std::nullopt, std::nullopt));
|
||||||
|
}
|
||||||
|
|
||||||
auto Execute(const std::string &query) {
|
auto Execute(const std::string &query) {
|
||||||
ResultStreamFaker stream(interpreter_.current_db_.db_acc_->get()->storage());
|
ResultStreamFaker stream(interpreter_.current_db_.db_acc_->get()->storage());
|
||||||
@ -1138,7 +1145,7 @@ TYPED_TEST(DumpTest, DumpDatabaseWithTriggers) {
|
|||||||
memgraph::query::DbAccessor dba(acc.get());
|
memgraph::query::DbAccessor dba(acc.get());
|
||||||
const std::map<std::string, memgraph::storage::PropertyValue> props;
|
const std::map<std::string, memgraph::storage::PropertyValue> props;
|
||||||
trigger_store->AddTrigger(trigger_name, trigger_statement, props, trigger_event_type, trigger_phase, &ast_cache,
|
trigger_store->AddTrigger(trigger_name, trigger_statement, props, trigger_event_type, trigger_phase, &ast_cache,
|
||||||
&dba, query_config, std::nullopt, &auth_checker);
|
&dba, query_config, auth_checker.GenQueryUser(std::nullopt, std::nullopt));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
ResultStreamFaker stream(this->db->storage());
|
ResultStreamFaker stream(this->db->storage());
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
#include "communication/result_stream_faker.hpp"
|
#include "communication/result_stream_faker.hpp"
|
||||||
|
#include "query/auth_checker.hpp"
|
||||||
#include "query/interpreter.hpp"
|
#include "query/interpreter.hpp"
|
||||||
#include "query/interpreter_context.hpp"
|
#include "query/interpreter_context.hpp"
|
||||||
#include "query/stream/streams.hpp"
|
#include "query/stream/streams.hpp"
|
||||||
@ -36,6 +37,7 @@ class QueryExecution : public testing::Test {
|
|||||||
const std::string testSuite = "query_plan_edge_cases";
|
const std::string testSuite = "query_plan_edge_cases";
|
||||||
std::optional<memgraph::dbms::DatabaseAccess> db_acc_;
|
std::optional<memgraph::dbms::DatabaseAccess> db_acc_;
|
||||||
std::optional<memgraph::query::InterpreterContext> interpreter_context_;
|
std::optional<memgraph::query::InterpreterContext> interpreter_context_;
|
||||||
|
std::optional<memgraph::query::AllowEverythingAuthChecker> auth_checker_;
|
||||||
std::optional<memgraph::query::Interpreter> interpreter_;
|
std::optional<memgraph::query::Interpreter> interpreter_;
|
||||||
|
|
||||||
std::filesystem::path data_directory{std::filesystem::temp_directory_path() / "MG_tests_unit_query_plan_edge_cases"};
|
std::filesystem::path data_directory{std::filesystem::temp_directory_path() / "MG_tests_unit_query_plan_edge_cases"};
|
||||||
@ -73,11 +75,14 @@ class QueryExecution : public testing::Test {
|
|||||||
nullptr
|
nullptr
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
|
auth_checker_.emplace();
|
||||||
interpreter_.emplace(&*interpreter_context_, *db_acc_);
|
interpreter_.emplace(&*interpreter_context_, *db_acc_);
|
||||||
|
interpreter_->SetUser(auth_checker_->GenQueryUser(std::nullopt, std::nullopt));
|
||||||
}
|
}
|
||||||
|
|
||||||
void TearDown() override {
|
void TearDown() override {
|
||||||
interpreter_ = std::nullopt;
|
interpreter_ = std::nullopt;
|
||||||
|
auth_checker_.reset();
|
||||||
interpreter_context_ = std::nullopt;
|
interpreter_context_ = std::nullopt;
|
||||||
system_state.reset();
|
system_state.reset();
|
||||||
db_acc_.reset();
|
db_acc_.reset();
|
||||||
|
@ -20,9 +20,11 @@
|
|||||||
#include "integrations/constants.hpp"
|
#include "integrations/constants.hpp"
|
||||||
#include "integrations/kafka/exceptions.hpp"
|
#include "integrations/kafka/exceptions.hpp"
|
||||||
#include "kafka_mock.hpp"
|
#include "kafka_mock.hpp"
|
||||||
|
#include "query/auth_checker.hpp"
|
||||||
#include "query/config.hpp"
|
#include "query/config.hpp"
|
||||||
#include "query/interpreter.hpp"
|
#include "query/interpreter.hpp"
|
||||||
#include "query/interpreter_context.hpp"
|
#include "query/interpreter_context.hpp"
|
||||||
|
#include "query/query_user.hpp"
|
||||||
#include "query/stream/streams.hpp"
|
#include "query/stream/streams.hpp"
|
||||||
#include "storage/v2/config.hpp"
|
#include "storage/v2/config.hpp"
|
||||||
#include "storage/v2/disk/storage.hpp"
|
#include "storage/v2/disk/storage.hpp"
|
||||||
@ -35,11 +37,23 @@ using StreamStatus = memgraph::query::stream::StreamStatus<memgraph::query::stre
|
|||||||
namespace {
|
namespace {
|
||||||
const static std::string kTopicName{"TrialTopic"};
|
const static std::string kTopicName{"TrialTopic"};
|
||||||
|
|
||||||
|
struct FakeUser : memgraph::query::QueryUserOrRole {
|
||||||
|
FakeUser() : memgraph::query::QueryUserOrRole{std::nullopt, std::nullopt} {}
|
||||||
|
|
||||||
|
bool IsAuthorized(const std::vector<memgraph::query::AuthQuery::Privilege> &privileges, const std::string &db_name,
|
||||||
|
memgraph::query::UserPolicy *policy) const {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#ifdef MG_ENTERPRISE
|
||||||
|
std::string GetDefaultDB() const { return "memgraph"; }
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
struct StreamCheckData {
|
struct StreamCheckData {
|
||||||
std::string name;
|
std::string name;
|
||||||
StreamInfo info;
|
StreamInfo info;
|
||||||
bool is_running;
|
bool is_running;
|
||||||
std::optional<std::string> owner;
|
std::shared_ptr<memgraph::query::QueryUserOrRole> owner;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::string GetDefaultStreamName() {
|
std::string GetDefaultStreamName() {
|
||||||
@ -105,13 +119,16 @@ class StreamsTestFixture : public ::testing::Test {
|
|||||||
}() // iile
|
}() // iile
|
||||||
};
|
};
|
||||||
memgraph::system::System system_state;
|
memgraph::system::System system_state;
|
||||||
memgraph::query::InterpreterContext interpreter_context_{memgraph::query::InterpreterConfig{}, nullptr, &repl_state,
|
memgraph::query::AllowEverythingAuthChecker auth_checker;
|
||||||
system_state
|
memgraph::query::InterpreterContext interpreter_context_{memgraph::query::InterpreterConfig{},
|
||||||
|
nullptr,
|
||||||
|
&repl_state,
|
||||||
|
system_state,
|
||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
,
|
nullptr,
|
||||||
nullptr
|
|
||||||
#endif
|
#endif
|
||||||
};
|
nullptr,
|
||||||
|
&auth_checker};
|
||||||
std::filesystem::path streams_data_directory_{data_directory_ / "separate-dir-for-test"};
|
std::filesystem::path streams_data_directory_{data_directory_ / "separate-dir-for-test"};
|
||||||
std::optional<StreamsTest> proxyStreams_;
|
std::optional<StreamsTest> proxyStreams_;
|
||||||
|
|
||||||
@ -173,7 +190,7 @@ class StreamsTestFixture : public ::testing::Test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StreamCheckData CreateDefaultStreamCheckData() {
|
StreamCheckData CreateDefaultStreamCheckData() {
|
||||||
return {GetDefaultStreamName(), CreateDefaultStreamInfo(), false, std::nullopt};
|
return {GetDefaultStreamName(), CreateDefaultStreamInfo(), false, std::make_unique<FakeUser>()};
|
||||||
}
|
}
|
||||||
|
|
||||||
void Clear() {
|
void Clear() {
|
||||||
@ -215,11 +232,11 @@ TYPED_TEST(StreamsTestFixture, CreateAlreadyExisting) {
|
|||||||
auto stream_info = this->CreateDefaultStreamInfo();
|
auto stream_info = this->CreateDefaultStreamInfo();
|
||||||
auto stream_name = GetDefaultStreamName();
|
auto stream_name = GetDefaultStreamName();
|
||||||
this->proxyStreams_->streams_->template Create<memgraph::query::stream::KafkaStream>(
|
this->proxyStreams_->streams_->template Create<memgraph::query::stream::KafkaStream>(
|
||||||
stream_name, stream_info, std::nullopt, this->db_, &this->interpreter_context_);
|
stream_name, stream_info, std::make_unique<FakeUser>(), this->db_, &this->interpreter_context_);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this->proxyStreams_->streams_->template Create<memgraph::query::stream::KafkaStream>(
|
this->proxyStreams_->streams_->template Create<memgraph::query::stream::KafkaStream>(
|
||||||
stream_name, stream_info, std::nullopt, this->db_, &this->interpreter_context_);
|
stream_name, stream_info, std::make_unique<FakeUser>(), this->db_, &this->interpreter_context_);
|
||||||
FAIL() << "Creating already existing stream should throw\n";
|
FAIL() << "Creating already existing stream should throw\n";
|
||||||
} catch (memgraph::query::stream::StreamsException &exception) {
|
} catch (memgraph::query::stream::StreamsException &exception) {
|
||||||
EXPECT_EQ(exception.what(), fmt::format("Stream already exists with name '{}'", stream_name));
|
EXPECT_EQ(exception.what(), fmt::format("Stream already exists with name '{}'", stream_name));
|
||||||
@ -231,7 +248,7 @@ TYPED_TEST(StreamsTestFixture, DropNotExistingStream) {
|
|||||||
const auto stream_name = GetDefaultStreamName();
|
const auto stream_name = GetDefaultStreamName();
|
||||||
const std::string not_existing_stream_name{"ThisDoesn'tExists"};
|
const std::string not_existing_stream_name{"ThisDoesn'tExists"};
|
||||||
this->proxyStreams_->streams_->template Create<memgraph::query::stream::KafkaStream>(
|
this->proxyStreams_->streams_->template Create<memgraph::query::stream::KafkaStream>(
|
||||||
stream_name, stream_info, std::nullopt, this->db_, &this->interpreter_context_);
|
stream_name, stream_info, std::make_unique<FakeUser>(), this->db_, &this->interpreter_context_);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this->proxyStreams_->streams_->Drop(not_existing_stream_name);
|
this->proxyStreams_->streams_->Drop(not_existing_stream_name);
|
||||||
@ -262,7 +279,7 @@ TYPED_TEST(StreamsTestFixture, RestoreStreams) {
|
|||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
stream_info.common_info.batch_interval = std::chrono::milliseconds((i + 1) * 10);
|
stream_info.common_info.batch_interval = std::chrono::milliseconds((i + 1) * 10);
|
||||||
stream_info.common_info.batch_size = 1000 + i;
|
stream_info.common_info.batch_size = 1000 + i;
|
||||||
stream_check_data.owner = std::string{"owner"} + iteration_postfix;
|
stream_check_data.owner = std::make_unique<FakeUser>();
|
||||||
|
|
||||||
// These are just random numbers to make the CONFIGS and CREDENTIALS map vary between consumers:
|
// These are just random numbers to make the CONFIGS and CREDENTIALS map vary between consumers:
|
||||||
// - 0 means no config, no credential
|
// - 0 means no config, no credential
|
||||||
@ -280,7 +297,7 @@ TYPED_TEST(StreamsTestFixture, RestoreStreams) {
|
|||||||
this->mock_cluster_.CreateTopic(stream_info.topics[0]);
|
this->mock_cluster_.CreateTopic(stream_info.topics[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
stream_check_datas[3].owner = {};
|
stream_check_datas[3].owner = std::make_unique<FakeUser>();
|
||||||
|
|
||||||
const auto check_restore_logic = [&stream_check_datas, this]() {
|
const auto check_restore_logic = [&stream_check_datas, this]() {
|
||||||
// Reset the Streams object to trigger reloading
|
// Reset the Streams object to trigger reloading
|
||||||
@ -336,7 +353,7 @@ TYPED_TEST(StreamsTestFixture, CheckWithTimeout) {
|
|||||||
const auto stream_info = this->CreateDefaultStreamInfo();
|
const auto stream_info = this->CreateDefaultStreamInfo();
|
||||||
const auto stream_name = GetDefaultStreamName();
|
const auto stream_name = GetDefaultStreamName();
|
||||||
this->proxyStreams_->streams_->template Create<memgraph::query::stream::KafkaStream>(
|
this->proxyStreams_->streams_->template Create<memgraph::query::stream::KafkaStream>(
|
||||||
stream_name, stream_info, std::nullopt, this->db_, &this->interpreter_context_);
|
stream_name, stream_info, std::make_unique<FakeUser>(), this->db_, &this->interpreter_context_);
|
||||||
|
|
||||||
std::chrono::milliseconds timeout{3000};
|
std::chrono::milliseconds timeout{3000};
|
||||||
|
|
||||||
@ -360,9 +377,10 @@ TYPED_TEST(StreamsTestFixture, CheckInvalidConfig) {
|
|||||||
EXPECT_TRUE(message.find(kInvalidConfigName) != std::string::npos) << message;
|
EXPECT_TRUE(message.find(kInvalidConfigName) != std::string::npos) << message;
|
||||||
EXPECT_TRUE(message.find(kConfigValue) != std::string::npos) << message;
|
EXPECT_TRUE(message.find(kConfigValue) != std::string::npos) << message;
|
||||||
};
|
};
|
||||||
EXPECT_THROW_WITH_MSG(this->proxyStreams_->streams_->template Create<memgraph::query::stream::KafkaStream>(
|
EXPECT_THROW_WITH_MSG(
|
||||||
stream_name, stream_info, std::nullopt, this->db_, &this->interpreter_context_),
|
this->proxyStreams_->streams_->template Create<memgraph::query::stream::KafkaStream>(
|
||||||
memgraph::integrations::kafka::SettingCustomConfigFailed, checker);
|
stream_name, stream_info, std::make_unique<FakeUser>(), this->db_, &this->interpreter_context_),
|
||||||
|
memgraph::integrations::kafka::SettingCustomConfigFailed, checker);
|
||||||
}
|
}
|
||||||
|
|
||||||
TYPED_TEST(StreamsTestFixture, CheckInvalidCredentials) {
|
TYPED_TEST(StreamsTestFixture, CheckInvalidCredentials) {
|
||||||
@ -376,7 +394,8 @@ TYPED_TEST(StreamsTestFixture, CheckInvalidCredentials) {
|
|||||||
EXPECT_TRUE(message.find(memgraph::integrations::kReducted) != std::string::npos) << message;
|
EXPECT_TRUE(message.find(memgraph::integrations::kReducted) != std::string::npos) << message;
|
||||||
EXPECT_TRUE(message.find(kCredentialValue) == std::string::npos) << message;
|
EXPECT_TRUE(message.find(kCredentialValue) == std::string::npos) << message;
|
||||||
};
|
};
|
||||||
EXPECT_THROW_WITH_MSG(this->proxyStreams_->streams_->template Create<memgraph::query::stream::KafkaStream>(
|
EXPECT_THROW_WITH_MSG(
|
||||||
stream_name, stream_info, std::nullopt, this->db_, &this->interpreter_context_),
|
this->proxyStreams_->streams_->template Create<memgraph::query::stream::KafkaStream>(
|
||||||
memgraph::integrations::kafka::SettingCustomConfigFailed, checker);
|
stream_name, stream_info, std::make_unique<FakeUser>(), this->db_, &this->interpreter_context_),
|
||||||
|
memgraph::integrations::kafka::SettingCustomConfigFailed, checker);
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#include "query/db_accessor.hpp"
|
#include "query/db_accessor.hpp"
|
||||||
#include "query/frontend/ast/ast.hpp"
|
#include "query/frontend/ast/ast.hpp"
|
||||||
#include "query/interpreter.hpp"
|
#include "query/interpreter.hpp"
|
||||||
|
#include "query/query_user.hpp"
|
||||||
#include "query/trigger.hpp"
|
#include "query/trigger.hpp"
|
||||||
#include "query/typed_value.hpp"
|
#include "query/typed_value.hpp"
|
||||||
#include "storage/v2/config.hpp"
|
#include "storage/v2/config.hpp"
|
||||||
@ -42,16 +43,27 @@ const std::unordered_set<memgraph::query::TriggerEventType> kAllEventTypes{
|
|||||||
|
|
||||||
class MockAuthChecker : public memgraph::query::AuthChecker {
|
class MockAuthChecker : public memgraph::query::AuthChecker {
|
||||||
public:
|
public:
|
||||||
MOCK_CONST_METHOD3(IsUserAuthorized,
|
MOCK_CONST_METHOD2(GenQueryUser,
|
||||||
bool(const std::optional<std::string> &username,
|
std::shared_ptr<memgraph::query::QueryUserOrRole>(const std::optional<std::string> &username,
|
||||||
const std::vector<memgraph::query::AuthQuery::Privilege> &privileges, const std::string &db));
|
const std::optional<std::string> &rolename));
|
||||||
#ifdef MG_ENTERPRISE
|
#ifdef MG_ENTERPRISE
|
||||||
MOCK_CONST_METHOD2(GetFineGrainedAuthChecker,
|
MOCK_CONST_METHOD2(GetFineGrainedAuthChecker, std::unique_ptr<memgraph::query::FineGrainedAuthChecker>(
|
||||||
std::unique_ptr<memgraph::query::FineGrainedAuthChecker>(
|
std::shared_ptr<memgraph::query::QueryUserOrRole> user,
|
||||||
const std::string &username, const memgraph::query::DbAccessor *db_accessor));
|
const memgraph::query::DbAccessor *db_accessor));
|
||||||
MOCK_CONST_METHOD0(ClearCache, void());
|
MOCK_CONST_METHOD0(ClearCache, void());
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class MockQueryUser : public memgraph::query::QueryUserOrRole {
|
||||||
|
public:
|
||||||
|
MockQueryUser(std::optional<std::string> name) : memgraph::query::QueryUserOrRole(std::move(name), std::nullopt) {}
|
||||||
|
MOCK_CONST_METHOD3(IsAuthorized, bool(const std::vector<memgraph::query::AuthQuery::Privilege> &privileges,
|
||||||
|
const std::string &db_name, memgraph::query::UserPolicy *policy));
|
||||||
|
|
||||||
|
#ifdef MG_ENTERPRISE
|
||||||
|
MOCK_CONST_METHOD0(GetDefaultDB, std::string());
|
||||||
|
#endif
|
||||||
|
};
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
const std::string testSuite = "query_trigger";
|
const std::string testSuite = "query_trigger";
|
||||||
@ -966,12 +978,12 @@ TYPED_TEST(TriggerStoreTest, Restore) {
|
|||||||
trigger_name_before, trigger_statement,
|
trigger_name_before, trigger_statement,
|
||||||
std::map<std::string, memgraph::storage::PropertyValue>{{"parameter", memgraph::storage::PropertyValue{1}}},
|
std::map<std::string, memgraph::storage::PropertyValue>{{"parameter", memgraph::storage::PropertyValue{1}}},
|
||||||
event_type, memgraph::query::TriggerPhase::BEFORE_COMMIT, &this->ast_cache, &*this->dba,
|
event_type, memgraph::query::TriggerPhase::BEFORE_COMMIT, &this->ast_cache, &*this->dba,
|
||||||
memgraph::query::InterpreterConfig::Query{}, std::nullopt, &this->auth_checker);
|
memgraph::query::InterpreterConfig::Query{}, this->auth_checker.GenQueryUser(std::nullopt, std::nullopt));
|
||||||
store->AddTrigger(
|
store->AddTrigger(
|
||||||
trigger_name_after, trigger_statement,
|
trigger_name_after, trigger_statement,
|
||||||
std::map<std::string, memgraph::storage::PropertyValue>{{"parameter", memgraph::storage::PropertyValue{"value"}}},
|
std::map<std::string, memgraph::storage::PropertyValue>{{"parameter", memgraph::storage::PropertyValue{"value"}}},
|
||||||
event_type, memgraph::query::TriggerPhase::AFTER_COMMIT, &this->ast_cache, &*this->dba,
|
event_type, memgraph::query::TriggerPhase::AFTER_COMMIT, &this->ast_cache, &*this->dba,
|
||||||
memgraph::query::InterpreterConfig::Query{}, {owner}, &this->auth_checker);
|
memgraph::query::InterpreterConfig::Query{}, this->auth_checker.GenQueryUser(owner, std::nullopt));
|
||||||
|
|
||||||
const auto check_triggers = [&] {
|
const auto check_triggers = [&] {
|
||||||
ASSERT_EQ(store->GetTriggerInfo().size(), 2);
|
ASSERT_EQ(store->GetTriggerInfo().size(), 2);
|
||||||
@ -981,9 +993,9 @@ TYPED_TEST(TriggerStoreTest, Restore) {
|
|||||||
ASSERT_EQ(trigger.OriginalStatement(), trigger_statement);
|
ASSERT_EQ(trigger.OriginalStatement(), trigger_statement);
|
||||||
ASSERT_EQ(trigger.EventType(), event_type);
|
ASSERT_EQ(trigger.EventType(), event_type);
|
||||||
if (owner != nullptr) {
|
if (owner != nullptr) {
|
||||||
ASSERT_EQ(*trigger.Owner(), *owner);
|
ASSERT_EQ(trigger.Owner()->username(), *owner);
|
||||||
} else {
|
} else {
|
||||||
ASSERT_FALSE(trigger.Owner().has_value());
|
ASSERT_FALSE(trigger.Owner()->username());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1022,32 +1034,38 @@ TYPED_TEST(TriggerStoreTest, AddTrigger) {
|
|||||||
// Invalid query in statements
|
// Invalid query in statements
|
||||||
ASSERT_THROW(store.AddTrigger("trigger", "RETUR 1", {}, memgraph::query::TriggerEventType::VERTEX_CREATE,
|
ASSERT_THROW(store.AddTrigger("trigger", "RETUR 1", {}, memgraph::query::TriggerEventType::VERTEX_CREATE,
|
||||||
memgraph::query::TriggerPhase::BEFORE_COMMIT, &this->ast_cache, &*this->dba,
|
memgraph::query::TriggerPhase::BEFORE_COMMIT, &this->ast_cache, &*this->dba,
|
||||||
memgraph::query::InterpreterConfig::Query{}, std::nullopt, &this->auth_checker),
|
memgraph::query::InterpreterConfig::Query{},
|
||||||
|
this->auth_checker.GenQueryUser(std::nullopt, std::nullopt)),
|
||||||
memgraph::utils::BasicException);
|
memgraph::utils::BasicException);
|
||||||
ASSERT_THROW(store.AddTrigger("trigger", "RETURN createdEdges", {}, memgraph::query::TriggerEventType::VERTEX_CREATE,
|
ASSERT_THROW(store.AddTrigger("trigger", "RETURN createdEdges", {}, memgraph::query::TriggerEventType::VERTEX_CREATE,
|
||||||
memgraph::query::TriggerPhase::BEFORE_COMMIT, &this->ast_cache, &*this->dba,
|
memgraph::query::TriggerPhase::BEFORE_COMMIT, &this->ast_cache, &*this->dba,
|
||||||
memgraph::query::InterpreterConfig::Query{}, std::nullopt, &this->auth_checker),
|
memgraph::query::InterpreterConfig::Query{},
|
||||||
|
this->auth_checker.GenQueryUser(std::nullopt, std::nullopt)),
|
||||||
memgraph::utils::BasicException);
|
memgraph::utils::BasicException);
|
||||||
|
|
||||||
ASSERT_THROW(store.AddTrigger("trigger", "RETURN $parameter", {}, memgraph::query::TriggerEventType::VERTEX_CREATE,
|
ASSERT_THROW(store.AddTrigger("trigger", "RETURN $parameter", {}, memgraph::query::TriggerEventType::VERTEX_CREATE,
|
||||||
memgraph::query::TriggerPhase::BEFORE_COMMIT, &this->ast_cache, &*this->dba,
|
memgraph::query::TriggerPhase::BEFORE_COMMIT, &this->ast_cache, &*this->dba,
|
||||||
memgraph::query::InterpreterConfig::Query{}, std::nullopt, &this->auth_checker),
|
memgraph::query::InterpreterConfig::Query{},
|
||||||
|
this->auth_checker.GenQueryUser(std::nullopt, std::nullopt)),
|
||||||
memgraph::utils::BasicException);
|
memgraph::utils::BasicException);
|
||||||
|
|
||||||
ASSERT_NO_THROW(store.AddTrigger(
|
ASSERT_NO_THROW(store.AddTrigger(
|
||||||
"trigger", "RETURN $parameter",
|
"trigger", "RETURN $parameter",
|
||||||
std::map<std::string, memgraph::storage::PropertyValue>{{"parameter", memgraph::storage::PropertyValue{1}}},
|
std::map<std::string, memgraph::storage::PropertyValue>{{"parameter", memgraph::storage::PropertyValue{1}}},
|
||||||
memgraph::query::TriggerEventType::VERTEX_CREATE, memgraph::query::TriggerPhase::BEFORE_COMMIT, &this->ast_cache,
|
memgraph::query::TriggerEventType::VERTEX_CREATE, memgraph::query::TriggerPhase::BEFORE_COMMIT, &this->ast_cache,
|
||||||
&*this->dba, memgraph::query::InterpreterConfig::Query{}, std::nullopt, &this->auth_checker));
|
&*this->dba, memgraph::query::InterpreterConfig::Query{},
|
||||||
|
this->auth_checker.GenQueryUser(std::nullopt, std::nullopt)));
|
||||||
|
|
||||||
// Inserting with the same name
|
// Inserting with the same name
|
||||||
ASSERT_THROW(store.AddTrigger("trigger", "RETURN 1", {}, memgraph::query::TriggerEventType::VERTEX_CREATE,
|
ASSERT_THROW(store.AddTrigger("trigger", "RETURN 1", {}, memgraph::query::TriggerEventType::VERTEX_CREATE,
|
||||||
memgraph::query::TriggerPhase::BEFORE_COMMIT, &this->ast_cache, &*this->dba,
|
memgraph::query::TriggerPhase::BEFORE_COMMIT, &this->ast_cache, &*this->dba,
|
||||||
memgraph::query::InterpreterConfig::Query{}, std::nullopt, &this->auth_checker),
|
memgraph::query::InterpreterConfig::Query{},
|
||||||
|
this->auth_checker.GenQueryUser(std::nullopt, std::nullopt)),
|
||||||
memgraph::utils::BasicException);
|
memgraph::utils::BasicException);
|
||||||
ASSERT_THROW(store.AddTrigger("trigger", "RETURN 1", {}, memgraph::query::TriggerEventType::VERTEX_CREATE,
|
ASSERT_THROW(store.AddTrigger("trigger", "RETURN 1", {}, memgraph::query::TriggerEventType::VERTEX_CREATE,
|
||||||
memgraph::query::TriggerPhase::AFTER_COMMIT, &this->ast_cache, &*this->dba,
|
memgraph::query::TriggerPhase::AFTER_COMMIT, &this->ast_cache, &*this->dba,
|
||||||
memgraph::query::InterpreterConfig::Query{}, std::nullopt, &this->auth_checker),
|
memgraph::query::InterpreterConfig::Query{},
|
||||||
|
this->auth_checker.GenQueryUser(std::nullopt, std::nullopt)),
|
||||||
memgraph::utils::BasicException);
|
memgraph::utils::BasicException);
|
||||||
|
|
||||||
ASSERT_EQ(store.GetTriggerInfo().size(), 1);
|
ASSERT_EQ(store.GetTriggerInfo().size(), 1);
|
||||||
@ -1063,7 +1081,8 @@ TYPED_TEST(TriggerStoreTest, DropTrigger) {
|
|||||||
const auto *trigger_name = "trigger";
|
const auto *trigger_name = "trigger";
|
||||||
store.AddTrigger(trigger_name, "RETURN 1", {}, memgraph::query::TriggerEventType::VERTEX_CREATE,
|
store.AddTrigger(trigger_name, "RETURN 1", {}, memgraph::query::TriggerEventType::VERTEX_CREATE,
|
||||||
memgraph::query::TriggerPhase::BEFORE_COMMIT, &this->ast_cache, &*this->dba,
|
memgraph::query::TriggerPhase::BEFORE_COMMIT, &this->ast_cache, &*this->dba,
|
||||||
memgraph::query::InterpreterConfig::Query{}, std::nullopt, &this->auth_checker);
|
memgraph::query::InterpreterConfig::Query{},
|
||||||
|
this->auth_checker.GenQueryUser(std::nullopt, std::nullopt));
|
||||||
|
|
||||||
ASSERT_THROW(store.DropTrigger("Unknown"), memgraph::utils::BasicException);
|
ASSERT_THROW(store.DropTrigger("Unknown"), memgraph::utils::BasicException);
|
||||||
ASSERT_NO_THROW(store.DropTrigger(trigger_name));
|
ASSERT_NO_THROW(store.DropTrigger(trigger_name));
|
||||||
@ -1076,7 +1095,8 @@ TYPED_TEST(TriggerStoreTest, TriggerInfo) {
|
|||||||
std::vector<memgraph::query::TriggerStore::TriggerInfo> expected_info;
|
std::vector<memgraph::query::TriggerStore::TriggerInfo> expected_info;
|
||||||
store.AddTrigger("trigger", "RETURN 1", {}, memgraph::query::TriggerEventType::VERTEX_CREATE,
|
store.AddTrigger("trigger", "RETURN 1", {}, memgraph::query::TriggerEventType::VERTEX_CREATE,
|
||||||
memgraph::query::TriggerPhase::BEFORE_COMMIT, &this->ast_cache, &*this->dba,
|
memgraph::query::TriggerPhase::BEFORE_COMMIT, &this->ast_cache, &*this->dba,
|
||||||
memgraph::query::InterpreterConfig::Query{}, std::nullopt, &this->auth_checker);
|
memgraph::query::InterpreterConfig::Query{},
|
||||||
|
this->auth_checker.GenQueryUser(std::nullopt, std::nullopt));
|
||||||
expected_info.push_back({"trigger",
|
expected_info.push_back({"trigger",
|
||||||
"RETURN 1",
|
"RETURN 1",
|
||||||
memgraph::query::TriggerEventType::VERTEX_CREATE,
|
memgraph::query::TriggerEventType::VERTEX_CREATE,
|
||||||
@ -1099,7 +1119,8 @@ TYPED_TEST(TriggerStoreTest, TriggerInfo) {
|
|||||||
|
|
||||||
store.AddTrigger("edge_update_trigger", "RETURN 1", {}, memgraph::query::TriggerEventType::EDGE_UPDATE,
|
store.AddTrigger("edge_update_trigger", "RETURN 1", {}, memgraph::query::TriggerEventType::EDGE_UPDATE,
|
||||||
memgraph::query::TriggerPhase::AFTER_COMMIT, &this->ast_cache, &*this->dba,
|
memgraph::query::TriggerPhase::AFTER_COMMIT, &this->ast_cache, &*this->dba,
|
||||||
memgraph::query::InterpreterConfig::Query{}, std::nullopt, &this->auth_checker);
|
memgraph::query::InterpreterConfig::Query{},
|
||||||
|
this->auth_checker.GenQueryUser(std::nullopt, std::nullopt));
|
||||||
expected_info.push_back({"edge_update_trigger",
|
expected_info.push_back({"edge_update_trigger",
|
||||||
"RETURN 1",
|
"RETURN 1",
|
||||||
memgraph::query::TriggerEventType::EDGE_UPDATE,
|
memgraph::query::TriggerEventType::EDGE_UPDATE,
|
||||||
@ -1216,7 +1237,8 @@ TYPED_TEST(TriggerStoreTest, AnyTriggerAllKeywords) {
|
|||||||
SCOPED_TRACE(keyword);
|
SCOPED_TRACE(keyword);
|
||||||
EXPECT_NO_THROW(store.AddTrigger(trigger_name, fmt::format("RETURN {}", keyword), {}, event_type,
|
EXPECT_NO_THROW(store.AddTrigger(trigger_name, fmt::format("RETURN {}", keyword), {}, event_type,
|
||||||
memgraph::query::TriggerPhase::BEFORE_COMMIT, &this->ast_cache, &*this->dba,
|
memgraph::query::TriggerPhase::BEFORE_COMMIT, &this->ast_cache, &*this->dba,
|
||||||
memgraph::query::InterpreterConfig::Query{}, std::nullopt, &this->auth_checker));
|
memgraph::query::InterpreterConfig::Query{},
|
||||||
|
this->auth_checker.GenQueryUser(std::nullopt, std::nullopt)));
|
||||||
store.DropTrigger(trigger_name);
|
store.DropTrigger(trigger_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1228,45 +1250,50 @@ TYPED_TEST(TriggerStoreTest, AuthCheckerUsage) {
|
|||||||
using ::testing::ElementsAre;
|
using ::testing::ElementsAre;
|
||||||
using ::testing::Return;
|
using ::testing::Return;
|
||||||
std::optional<memgraph::query::TriggerStore> store{this->testing_directory};
|
std::optional<memgraph::query::TriggerStore> store{this->testing_directory};
|
||||||
const std::optional<std::string> owner{"testing_owner"};
|
|
||||||
MockAuthChecker mock_checker;
|
MockAuthChecker mock_checker;
|
||||||
|
const std::optional<std::string> owner{"mock_user"};
|
||||||
|
MockQueryUser mock_user(owner);
|
||||||
|
std::shared_ptr<memgraph::query::QueryUserOrRole> mock_user_ptr(
|
||||||
|
&mock_user, [](memgraph::query::QueryUserOrRole *) { /* do nothing */ });
|
||||||
|
MockQueryUser mock_userless(std::nullopt);
|
||||||
|
std::shared_ptr<memgraph::query::QueryUserOrRole> mock_userless_ptr(
|
||||||
|
&mock_userless, [](memgraph::query::QueryUserOrRole *) { /* do nothing */ });
|
||||||
|
|
||||||
::testing::InSequence s;
|
::testing::InSequence s;
|
||||||
|
|
||||||
EXPECT_CALL(mock_checker, IsUserAuthorized(std::optional<std::string>{}, ElementsAre(Privilege::CREATE), ""))
|
// TODO Userless
|
||||||
.Times(1)
|
EXPECT_CALL(mock_user, IsAuthorized(ElementsAre(Privilege::CREATE), "", &memgraph::query::up_to_date_policy))
|
||||||
.WillOnce(Return(true));
|
.WillOnce(Return(true));
|
||||||
EXPECT_CALL(mock_checker, IsUserAuthorized(owner, ElementsAre(Privilege::CREATE), ""))
|
|
||||||
.Times(1)
|
|
||||||
.WillOnce(Return(true));
|
|
||||||
|
|
||||||
ASSERT_NO_THROW(store->AddTrigger("successfull_trigger_1", "CREATE (n:VERTEX) RETURN n", {},
|
ASSERT_NO_THROW(store->AddTrigger("successfull_trigger_1", "CREATE (n:VERTEX) RETURN n", {},
|
||||||
memgraph::query::TriggerEventType::EDGE_UPDATE,
|
memgraph::query::TriggerEventType::EDGE_UPDATE,
|
||||||
memgraph::query::TriggerPhase::AFTER_COMMIT, &this->ast_cache, &*this->dba,
|
memgraph::query::TriggerPhase::AFTER_COMMIT, &this->ast_cache, &*this->dba,
|
||||||
memgraph::query::InterpreterConfig::Query{}, std::nullopt, &mock_checker));
|
memgraph::query::InterpreterConfig::Query{}, mock_user_ptr));
|
||||||
|
|
||||||
|
EXPECT_CALL(mock_userless, IsAuthorized(ElementsAre(Privilege::CREATE), "", &memgraph::query::up_to_date_policy))
|
||||||
|
.WillOnce(Return(true));
|
||||||
ASSERT_NO_THROW(store->AddTrigger("successfull_trigger_2", "CREATE (n:VERTEX) RETURN n", {},
|
ASSERT_NO_THROW(store->AddTrigger("successfull_trigger_2", "CREATE (n:VERTEX) RETURN n", {},
|
||||||
memgraph::query::TriggerEventType::EDGE_UPDATE,
|
memgraph::query::TriggerEventType::EDGE_UPDATE,
|
||||||
memgraph::query::TriggerPhase::AFTER_COMMIT, &this->ast_cache, &*this->dba,
|
memgraph::query::TriggerPhase::AFTER_COMMIT, &this->ast_cache, &*this->dba,
|
||||||
memgraph::query::InterpreterConfig::Query{}, owner, &mock_checker));
|
memgraph::query::InterpreterConfig::Query{}, mock_userless_ptr));
|
||||||
|
|
||||||
EXPECT_CALL(mock_checker, IsUserAuthorized(std::optional<std::string>{}, ElementsAre(Privilege::MATCH), ""))
|
EXPECT_CALL(mock_user, IsAuthorized(ElementsAre(Privilege::MATCH), "", &memgraph::query::up_to_date_policy))
|
||||||
.Times(1)
|
|
||||||
.WillOnce(Return(false));
|
.WillOnce(Return(false));
|
||||||
|
ASSERT_THROW(
|
||||||
|
store->AddTrigger("unprivileged_trigger", "MATCH (n:VERTEX) RETURN n", {},
|
||||||
|
memgraph::query::TriggerEventType::EDGE_UPDATE, memgraph::query::TriggerPhase::AFTER_COMMIT,
|
||||||
|
&this->ast_cache, &*this->dba, memgraph::query::InterpreterConfig::Query{}, mock_user_ptr);
|
||||||
|
, memgraph::utils::BasicException);
|
||||||
|
|
||||||
ASSERT_THROW(store->AddTrigger("unprivileged_trigger", "MATCH (n:VERTEX) RETURN n", {},
|
// Restore
|
||||||
memgraph::query::TriggerEventType::EDGE_UPDATE,
|
|
||||||
memgraph::query::TriggerPhase::AFTER_COMMIT, &this->ast_cache, &*this->dba,
|
|
||||||
memgraph::query::InterpreterConfig::Query{}, std::nullopt, &mock_checker);
|
|
||||||
, memgraph::utils::BasicException);
|
|
||||||
|
|
||||||
store.emplace(this->testing_directory);
|
store.emplace(this->testing_directory);
|
||||||
EXPECT_CALL(mock_checker, IsUserAuthorized(std::optional<std::string>{}, ElementsAre(Privilege::CREATE), ""))
|
|
||||||
.Times(1)
|
std::optional<std::string> nopt{};
|
||||||
.WillOnce(Return(false));
|
EXPECT_CALL(mock_checker, GenQueryUser(owner, nopt)).WillOnce(Return(mock_user_ptr));
|
||||||
EXPECT_CALL(mock_checker, IsUserAuthorized(owner, ElementsAre(Privilege::CREATE), ""))
|
EXPECT_CALL(mock_user, IsAuthorized(ElementsAre(Privilege::CREATE), "", &memgraph::query::up_to_date_policy))
|
||||||
.Times(1)
|
|
||||||
.WillOnce(Return(true));
|
.WillOnce(Return(true));
|
||||||
|
EXPECT_CALL(mock_checker, GenQueryUser(nopt, nopt)).WillOnce(Return(mock_userless_ptr));
|
||||||
|
EXPECT_CALL(mock_userless, IsAuthorized(ElementsAre(Privilege::CREATE), "", &memgraph::query::up_to_date_policy))
|
||||||
|
.WillOnce(Return(false));
|
||||||
|
|
||||||
ASSERT_NO_THROW(store->RestoreTriggers(&this->ast_cache, &*this->dba, memgraph::query::InterpreterConfig::Query{},
|
ASSERT_NO_THROW(store->RestoreTriggers(&this->ast_cache, &*this->dba, memgraph::query::InterpreterConfig::Query{},
|
||||||
&mock_checker));
|
&mock_checker));
|
||||||
|
Loading…
Reference in New Issue
Block a user