2024-01-29 20:52:32 +08:00
|
|
|
// Copyright 2024 Memgraph Ltd.
|
2021-10-03 18:07:04 +08:00
|
|
|
//
|
|
|
|
// 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
|
|
|
|
// this file except in compliance with the License. You may obtain a copy of the License at https://memgraph.com/legal.
|
|
|
|
//
|
|
|
|
//
|
|
|
|
|
2018-07-27 16:54:20 +08:00
|
|
|
#include "auth/auth.hpp"
|
|
|
|
|
2019-09-10 20:13:31 +08:00
|
|
|
#include <iostream>
|
2019-02-27 22:22:54 +08:00
|
|
|
#include <utility>
|
|
|
|
|
|
|
|
#include <fmt/format.h>
|
|
|
|
|
2018-08-14 17:34:00 +08:00
|
|
|
#include "auth/exceptions.hpp"
|
2022-11-04 22:23:43 +08:00
|
|
|
#include "license/license.hpp"
|
2019-02-27 22:22:54 +08:00
|
|
|
#include "utils/flag_validation.hpp"
|
2021-10-07 20:51:30 +08:00
|
|
|
#include "utils/message.hpp"
|
2021-09-30 01:14:39 +08:00
|
|
|
#include "utils/settings.hpp"
|
2019-02-22 20:20:54 +08:00
|
|
|
#include "utils/string.hpp"
|
2018-07-27 16:54:20 +08:00
|
|
|
|
2021-02-18 22:32:43 +08:00
|
|
|
DEFINE_VALIDATED_string(auth_module_executable, "", "Absolute path to the auth module executable that should be used.",
|
|
|
|
{
|
|
|
|
if (value.empty()) return true;
|
|
|
|
// Check the file status, following symlinks.
|
|
|
|
auto status = std::filesystem::status(value);
|
|
|
|
if (!std::filesystem::is_regular_file(status)) {
|
|
|
|
std::cerr << "The auth module path doesn't exist or isn't a file!" << std::endl;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
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.");
|
2019-09-10 20:13:31 +08:00
|
|
|
DEFINE_VALIDATED_int32(auth_module_timeout_ms, 10000,
|
|
|
|
"Timeout (in milliseconds) used when waiting for a "
|
|
|
|
"response from the auth module.",
|
|
|
|
FLAG_IN_RANGE(100, 1800000));
|
|
|
|
|
2022-03-09 22:53:33 +08:00
|
|
|
namespace memgraph::auth {
|
2018-07-27 16:54:20 +08:00
|
|
|
const std::string kUserPrefix = "user:";
|
|
|
|
const std::string kRolePrefix = "role:";
|
|
|
|
const std::string kLinkPrefix = "link:";
|
|
|
|
|
|
|
|
/**
|
|
|
|
* All data stored in the `Auth` storage is stored in an underlying
|
2020-01-24 17:45:05 +08:00
|
|
|
* `kvstore::KVStore`. Because we are using a key-value store to store the data,
|
2018-07-27 16:54:20 +08:00
|
|
|
* the data has to be encoded. The encoding used is as follows:
|
|
|
|
*
|
|
|
|
* User: key="user:<username>", value="<json_encoded_members_of_user>"
|
|
|
|
* Role: key="role:<rolename>", value="<json_endoded_members_of_role>"
|
|
|
|
*
|
|
|
|
* The User->Role relationship isn't stored in the `User` encoded data because
|
|
|
|
* we want to be able to delete/modify a Role and have it automatically be
|
|
|
|
* removed/modified in all linked users. Because of that we store the links to
|
|
|
|
* the role as a foreign-key like mapping in the KVStore. It is saved as
|
|
|
|
* follows:
|
|
|
|
*
|
|
|
|
* key="link:<username>", value="<rolename>"
|
|
|
|
*/
|
|
|
|
|
2024-01-29 20:52:32 +08:00
|
|
|
Auth::Auth(std::string storage_directory, Config config)
|
|
|
|
: storage_(std::move(storage_directory)), module_(FLAGS_auth_module_executable), config_{std::move(config)} {}
|
2018-07-27 16:54:20 +08:00
|
|
|
|
2021-02-18 22:32:43 +08:00
|
|
|
std::optional<User> Auth::Authenticate(const std::string &username, const std::string &password) {
|
2019-09-10 20:13:31 +08:00
|
|
|
if (module_.IsUsed()) {
|
2022-11-04 22:23:43 +08:00
|
|
|
const auto license_check_result = license::global_license_checker.IsEnterpriseValid(utils::global_settings);
|
2021-09-30 01:14:39 +08:00
|
|
|
if (license_check_result.HasError()) {
|
2022-11-04 22:23:43 +08:00
|
|
|
spdlog::warn(license::LicenseCheckErrorToString(license_check_result.GetError(), "authentication modules"));
|
2021-09-30 01:14:39 +08:00
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
|
2019-09-10 20:13:31 +08:00
|
|
|
nlohmann::json params = nlohmann::json::object();
|
|
|
|
params["username"] = username;
|
|
|
|
params["password"] = password;
|
|
|
|
|
|
|
|
auto ret = module_.Call(params, FLAGS_auth_module_timeout_ms);
|
|
|
|
|
|
|
|
// Verify response integrity.
|
2021-02-18 22:32:43 +08:00
|
|
|
if (!ret.is_object() || ret.find("authenticated") == ret.end() || ret.find("role") == ret.end()) {
|
2019-09-10 20:13:31 +08:00
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
const auto &ret_authenticated = ret.at("authenticated");
|
|
|
|
const auto &ret_role = ret.at("role");
|
|
|
|
if (!ret_authenticated.is_boolean() || !ret_role.is_string()) {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
auto is_authenticated = ret_authenticated.get<bool>();
|
|
|
|
const auto &rolename = ret_role.get<std::string>();
|
|
|
|
|
|
|
|
// Authenticate the user.
|
|
|
|
if (!is_authenticated) return std::nullopt;
|
|
|
|
|
|
|
|
// 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) {
|
2021-10-07 20:51:30 +08:00
|
|
|
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"));
|
2019-09-10 20:13:31 +08:00
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
} else {
|
2021-10-07 20:51:30 +08:00
|
|
|
spdlog::warn(utils::MessageWithLink(
|
|
|
|
"Couldn't authenticate user '{}' using the auth module because the user doesn't exist.", username,
|
|
|
|
"https://memgr.ph/auth"));
|
2019-09-10 20:13:31 +08:00
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
} else {
|
2024-01-29 20:52:32 +08:00
|
|
|
UpdatePassword(*user, password);
|
2019-09-10 20:13:31 +08:00
|
|
|
}
|
|
|
|
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) {
|
2021-01-21 22:47:56 +08:00
|
|
|
spdlog::warn(
|
2021-10-07 20:51:30 +08:00
|
|
|
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"));
|
2019-09-10 20:13:31 +08:00
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
SaveRole(*role);
|
|
|
|
} else {
|
2021-10-07 20:51:30 +08:00
|
|
|
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"));
|
2019-09-10 20:13:31 +08:00
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
user->SetRole(*role);
|
|
|
|
} else {
|
|
|
|
user->ClearRole();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
SaveUser(*user);
|
|
|
|
return user;
|
2019-02-27 22:22:54 +08:00
|
|
|
} else {
|
|
|
|
auto user = GetUser(username);
|
2021-01-21 22:47:56 +08:00
|
|
|
if (!user) {
|
2021-10-07 20:51:30 +08:00
|
|
|
spdlog::warn(utils::MessageWithLink("Couldn't authenticate user '{}' because the user doesn't exist.", username,
|
|
|
|
"https://memgr.ph/auth"));
|
2021-01-21 22:47:56 +08:00
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
if (!user->CheckPassword(password)) {
|
2021-10-07 20:51:30 +08:00
|
|
|
spdlog::warn(utils::MessageWithLink("Couldn't authenticate user '{}' because the password is not correct.",
|
|
|
|
username, "https://memgr.ph/auth"));
|
2021-01-21 22:47:56 +08:00
|
|
|
return std::nullopt;
|
|
|
|
}
|
2019-02-27 22:22:54 +08:00
|
|
|
return user;
|
|
|
|
}
|
2018-07-27 16:54:20 +08:00
|
|
|
}
|
|
|
|
|
2021-07-22 22:22:08 +08:00
|
|
|
std::optional<User> Auth::GetUser(const std::string &username_orig) const {
|
2019-02-22 20:20:54 +08:00
|
|
|
auto username = utils::ToLowerCase(username_orig);
|
2018-07-27 16:54:20 +08:00
|
|
|
auto existing_user = storage_.Get(kUserPrefix + username);
|
2019-04-23 17:00:49 +08:00
|
|
|
if (!existing_user) return std::nullopt;
|
2018-07-27 16:54:20 +08:00
|
|
|
|
|
|
|
nlohmann::json data;
|
|
|
|
try {
|
|
|
|
data = nlohmann::json::parse(*existing_user);
|
|
|
|
} catch (const nlohmann::json::parse_error &e) {
|
2018-08-14 17:34:00 +08:00
|
|
|
throw AuthException("Couldn't load user data!");
|
2018-07-27 16:54:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
auto user = User::Deserialize(data);
|
|
|
|
auto link = storage_.Get(kLinkPrefix + username);
|
|
|
|
|
|
|
|
if (link) {
|
|
|
|
auto role = GetRole(*link);
|
|
|
|
if (role) {
|
|
|
|
user.SetRole(*role);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return user;
|
|
|
|
}
|
|
|
|
|
2018-08-14 17:34:00 +08:00
|
|
|
void Auth::SaveUser(const User &user) {
|
|
|
|
bool success = false;
|
2021-07-22 22:22:08 +08:00
|
|
|
if (const auto *role = user.role(); role != nullptr) {
|
|
|
|
success = storage_.PutMultiple(
|
|
|
|
{{kUserPrefix + user.username(), user.Serialize().dump()}, {kLinkPrefix + user.username(), role->rolename()}});
|
2018-07-27 16:54:20 +08:00
|
|
|
} else {
|
2021-02-18 22:32:43 +08:00
|
|
|
success = storage_.PutAndDeleteMultiple({{kUserPrefix + user.username(), user.Serialize().dump()}},
|
|
|
|
{kLinkPrefix + user.username()});
|
2018-08-14 17:34:00 +08:00
|
|
|
}
|
|
|
|
if (!success) {
|
|
|
|
throw AuthException("Couldn't save user '{}'!", user.username());
|
2018-07-27 16:54:20 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-29 20:52:32 +08:00
|
|
|
void Auth::UpdatePassword(auth::User &user, const std::optional<std::string> &password) {
|
|
|
|
// Check if null
|
|
|
|
if (!password) {
|
|
|
|
if (!config_.password_permit_null) {
|
|
|
|
throw AuthException("Null passwords aren't permitted!");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Check if compliant with our filter
|
|
|
|
if (config_.custom_password_regex) {
|
|
|
|
if (const auto license_check_result = license::global_license_checker.IsEnterpriseValid(utils::global_settings);
|
|
|
|
license_check_result.HasError()) {
|
|
|
|
throw AuthException(
|
|
|
|
"Custom password regex is a Memgraph Enterprise feature. Please set the config "
|
|
|
|
"(\"--auth-password-strength-regex\") to its default value (\"{}\") or remove the flag.\n{}",
|
|
|
|
glue::kDefaultPasswordRegex,
|
|
|
|
license::LicenseCheckErrorToString(license_check_result.GetError(), "password regex"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!std::regex_match(*password, config_.password_regex)) {
|
|
|
|
throw AuthException(
|
|
|
|
"The user password doesn't conform to the required strength! Regex: "
|
|
|
|
"\"{}\"",
|
|
|
|
config_.password_regex_str);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// All checks passed; update
|
|
|
|
user.UpdatePassword(password);
|
|
|
|
}
|
|
|
|
|
2021-02-18 22:32:43 +08:00
|
|
|
std::optional<User> Auth::AddUser(const std::string &username, const std::optional<std::string> &password) {
|
2024-01-29 20:52:32 +08:00
|
|
|
if (!NameRegexMatch(username)) {
|
|
|
|
throw AuthException("Invalid user name.");
|
|
|
|
}
|
2018-07-27 16:54:20 +08:00
|
|
|
auto existing_user = GetUser(username);
|
2019-04-23 17:00:49 +08:00
|
|
|
if (existing_user) return std::nullopt;
|
2018-08-14 17:34:00 +08:00
|
|
|
auto existing_role = GetRole(username);
|
2019-04-23 17:00:49 +08:00
|
|
|
if (existing_role) return std::nullopt;
|
2018-07-27 16:54:20 +08:00
|
|
|
auto new_user = User(username);
|
2024-01-29 20:52:32 +08:00
|
|
|
UpdatePassword(new_user, password);
|
2018-08-14 17:34:00 +08:00
|
|
|
SaveUser(new_user);
|
2018-07-27 16:54:20 +08:00
|
|
|
return new_user;
|
|
|
|
}
|
|
|
|
|
2019-02-22 20:20:54 +08:00
|
|
|
bool Auth::RemoveUser(const std::string &username_orig) {
|
|
|
|
auto username = utils::ToLowerCase(username_orig);
|
2018-07-27 16:54:20 +08:00
|
|
|
if (!storage_.Get(kUserPrefix + username)) return false;
|
2021-02-18 22:32:43 +08:00
|
|
|
std::vector<std::string> keys({kLinkPrefix + username, kUserPrefix + username});
|
2018-08-14 17:34:00 +08:00
|
|
|
if (!storage_.DeleteMultiple(keys)) {
|
|
|
|
throw AuthException("Couldn't remove user '{}'!", username);
|
|
|
|
}
|
|
|
|
return true;
|
2018-07-27 16:54:20 +08:00
|
|
|
}
|
|
|
|
|
2021-07-22 22:22:08 +08:00
|
|
|
std::vector<auth::User> Auth::AllUsers() const {
|
2018-08-14 17:34:00 +08:00
|
|
|
std::vector<auth::User> ret;
|
2021-02-18 22:32:43 +08:00
|
|
|
for (auto it = storage_.begin(kUserPrefix); it != storage_.end(kUserPrefix); ++it) {
|
2019-02-22 20:20:54 +08:00
|
|
|
auto username = it->first.substr(kUserPrefix.size());
|
|
|
|
if (username != utils::ToLowerCase(username)) continue;
|
|
|
|
auto user = GetUser(username);
|
2018-08-14 17:34:00 +08:00
|
|
|
if (user) {
|
[E129-MG <-T0982-MG] implement edge type filtering (#489)
* GRANT, REVOKE, DENY and access_checker DONE
* Added AccessChecker to ExecutionContext
* grammar expanded; (#462)
* current
* T0954 mg expand user and role to hold permissions on labels (#465)
* added FineGrainedAccessPermissions class to model
* expanded user and role with fine grained access permissions
* fixed grammar
* [E129 < T0953-MG] GRANT, DENY, REVOKE added in interpreter and mainVisitor (#464)
* GRANT, DENY, REVOKE added in interpreter and mainVisitor
* Commented labelPermissons
* remove labelsPermission adding
* Fixed
* Removed extra lambda
* fixed
* [E129<-T0955-MG] Expand ExecutionContext with label related information (#467)
* added
* Added FineGrainedAccessChecker to Context
* fixed
* Added filtering
* testing
* Added edge filtering to storage, need to add filtering in simple Expand in operator.cpp
* Removed storage changes
* MATCH filtering working
* EdgeTypeFiltering working, just need to test everything again
* Removed FineGrainedAccessChecker
* Removed Expand Path
* Fix
* Tested FineGrainedAccessHandler, need to test AuthChecker
* Added integration test for lba
* Fixed merge conflicts
* PR fix
* fixed
* PR fix
* Fix test
* removed .vscode, .cache, .githooks
* githooks
* added tests
* fixed build
* Changed ast.lcp and User pointer to value in context.hpp
* Fixed test
* Remove denies on grant all
* AuthChecker
* Pr fix, auth_checker still not fixed
* Create mg-glue and extract UserBasedAuthChecker from AuthChecker
* Build fixed, need to fix test
* e2e tests
* e2e test working
* Added unit test, e2e and FineGrainedChecker
* Mege E129, auth_checker tests
* Fixed test
* e2e fix
Co-authored-by: Boris Taševski <36607228+BorisTasevski@users.noreply.github.com>
Co-authored-by: josipmrden <josip.mrden@external-basf.com>
Co-authored-by: János Benjamin Antal <benjamin.antal@memgraph.io>
2022-08-16 21:57:23 +08:00
|
|
|
ret.push_back(std::move(*user));
|
2018-07-27 16:54:20 +08:00
|
|
|
}
|
|
|
|
}
|
2018-08-14 17:34:00 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2021-07-22 22:22:08 +08:00
|
|
|
bool Auth::HasUsers() const { return storage_.begin(kUserPrefix) != storage_.end(kUserPrefix); }
|
2018-07-27 16:54:20 +08:00
|
|
|
|
2021-07-22 22:22:08 +08:00
|
|
|
std::optional<Role> Auth::GetRole(const std::string &rolename_orig) const {
|
2019-02-22 20:20:54 +08:00
|
|
|
auto rolename = utils::ToLowerCase(rolename_orig);
|
2018-07-27 16:54:20 +08:00
|
|
|
auto existing_role = storage_.Get(kRolePrefix + rolename);
|
2019-04-23 17:00:49 +08:00
|
|
|
if (!existing_role) return std::nullopt;
|
2018-07-27 16:54:20 +08:00
|
|
|
|
|
|
|
nlohmann::json data;
|
|
|
|
try {
|
|
|
|
data = nlohmann::json::parse(*existing_role);
|
|
|
|
} catch (const nlohmann::json::parse_error &e) {
|
2018-08-14 17:34:00 +08:00
|
|
|
throw AuthException("Couldn't load role data!");
|
2018-07-27 16:54:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return Role::Deserialize(data);
|
|
|
|
}
|
|
|
|
|
2018-08-14 17:34:00 +08:00
|
|
|
void Auth::SaveRole(const Role &role) {
|
|
|
|
if (!storage_.Put(kRolePrefix + role.rolename(), role.Serialize().dump())) {
|
|
|
|
throw AuthException("Couldn't save role '{}'!", role.rolename());
|
|
|
|
}
|
2018-07-27 16:54:20 +08:00
|
|
|
}
|
|
|
|
|
2019-04-23 17:00:49 +08:00
|
|
|
std::optional<Role> Auth::AddRole(const std::string &rolename) {
|
2024-01-29 20:52:32 +08:00
|
|
|
if (!NameRegexMatch(rolename)) {
|
|
|
|
throw AuthException("Invalid role name.");
|
|
|
|
}
|
2024-01-23 19:06:10 +08:00
|
|
|
if (auto existing_role = GetRole(rolename)) return std::nullopt;
|
|
|
|
if (auto existing_user = GetUser(rolename)) return std::nullopt;
|
2018-07-27 16:54:20 +08:00
|
|
|
auto new_role = Role(rolename);
|
2018-08-14 17:34:00 +08:00
|
|
|
SaveRole(new_role);
|
2018-07-27 16:54:20 +08:00
|
|
|
return new_role;
|
|
|
|
}
|
|
|
|
|
2019-02-22 20:20:54 +08:00
|
|
|
bool Auth::RemoveRole(const std::string &rolename_orig) {
|
|
|
|
auto rolename = utils::ToLowerCase(rolename_orig);
|
2018-07-27 16:54:20 +08:00
|
|
|
if (!storage_.Get(kRolePrefix + rolename)) return false;
|
2018-08-14 17:34:00 +08:00
|
|
|
std::vector<std::string> keys;
|
2021-02-18 22:32:43 +08:00
|
|
|
for (auto it = storage_.begin(kLinkPrefix); it != storage_.end(kLinkPrefix); ++it) {
|
2019-02-22 20:20:54 +08:00
|
|
|
if (utils::ToLowerCase(it->second) == rolename) {
|
2018-08-14 17:34:00 +08:00
|
|
|
keys.push_back(it->first);
|
2018-07-27 16:54:20 +08:00
|
|
|
}
|
|
|
|
}
|
2018-08-14 17:34:00 +08:00
|
|
|
keys.push_back(kRolePrefix + rolename);
|
|
|
|
if (!storage_.DeleteMultiple(keys)) {
|
|
|
|
throw AuthException("Couldn't remove role '{}'!", rolename);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-07-22 22:22:08 +08:00
|
|
|
std::vector<auth::Role> Auth::AllRoles() const {
|
2018-08-14 17:34:00 +08:00
|
|
|
std::vector<auth::Role> ret;
|
2021-02-18 22:32:43 +08:00
|
|
|
for (auto it = storage_.begin(kRolePrefix); it != storage_.end(kRolePrefix); ++it) {
|
2018-08-14 17:34:00 +08:00
|
|
|
auto rolename = it->first.substr(kRolePrefix.size());
|
2019-02-22 20:20:54 +08:00
|
|
|
if (rolename != utils::ToLowerCase(rolename)) continue;
|
2024-01-23 19:06:10 +08:00
|
|
|
if (auto role = GetRole(rolename)) {
|
2018-08-14 17:34:00 +08:00
|
|
|
ret.push_back(*role);
|
|
|
|
} else {
|
|
|
|
throw AuthException("Couldn't load role '{}'!", rolename);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2021-07-22 22:22:08 +08:00
|
|
|
std::vector<auth::User> Auth::AllUsersForRole(const std::string &rolename_orig) const {
|
2024-01-23 19:06:10 +08:00
|
|
|
const auto rolename = utils::ToLowerCase(rolename_orig);
|
2018-08-14 17:34:00 +08:00
|
|
|
std::vector<auth::User> ret;
|
2021-02-18 22:32:43 +08:00
|
|
|
for (auto it = storage_.begin(kLinkPrefix); it != storage_.end(kLinkPrefix); ++it) {
|
2018-08-14 17:34:00 +08:00
|
|
|
auto username = it->first.substr(kLinkPrefix.size());
|
2019-02-22 20:20:54 +08:00
|
|
|
if (username != utils::ToLowerCase(username)) continue;
|
|
|
|
if (it->second != utils::ToLowerCase(it->second)) continue;
|
2018-08-14 17:34:00 +08:00
|
|
|
if (it->second == rolename) {
|
2024-01-23 19:06:10 +08:00
|
|
|
if (auto user = GetUser(username)) {
|
[E129-MG <-T0982-MG] implement edge type filtering (#489)
* GRANT, REVOKE, DENY and access_checker DONE
* Added AccessChecker to ExecutionContext
* grammar expanded; (#462)
* current
* T0954 mg expand user and role to hold permissions on labels (#465)
* added FineGrainedAccessPermissions class to model
* expanded user and role with fine grained access permissions
* fixed grammar
* [E129 < T0953-MG] GRANT, DENY, REVOKE added in interpreter and mainVisitor (#464)
* GRANT, DENY, REVOKE added in interpreter and mainVisitor
* Commented labelPermissons
* remove labelsPermission adding
* Fixed
* Removed extra lambda
* fixed
* [E129<-T0955-MG] Expand ExecutionContext with label related information (#467)
* added
* Added FineGrainedAccessChecker to Context
* fixed
* Added filtering
* testing
* Added edge filtering to storage, need to add filtering in simple Expand in operator.cpp
* Removed storage changes
* MATCH filtering working
* EdgeTypeFiltering working, just need to test everything again
* Removed FineGrainedAccessChecker
* Removed Expand Path
* Fix
* Tested FineGrainedAccessHandler, need to test AuthChecker
* Added integration test for lba
* Fixed merge conflicts
* PR fix
* fixed
* PR fix
* Fix test
* removed .vscode, .cache, .githooks
* githooks
* added tests
* fixed build
* Changed ast.lcp and User pointer to value in context.hpp
* Fixed test
* Remove denies on grant all
* AuthChecker
* Pr fix, auth_checker still not fixed
* Create mg-glue and extract UserBasedAuthChecker from AuthChecker
* Build fixed, need to fix test
* e2e tests
* e2e test working
* Added unit test, e2e and FineGrainedChecker
* Mege E129, auth_checker tests
* Fixed test
* e2e fix
Co-authored-by: Boris Taševski <36607228+BorisTasevski@users.noreply.github.com>
Co-authored-by: josipmrden <josip.mrden@external-basf.com>
Co-authored-by: János Benjamin Antal <benjamin.antal@memgraph.io>
2022-08-16 21:57:23 +08:00
|
|
|
ret.push_back(std::move(*user));
|
2018-08-14 17:34:00 +08:00
|
|
|
} else {
|
|
|
|
throw AuthException("Couldn't load user '{}'!", username);
|
|
|
|
}
|
|
|
|
}
|
2018-07-27 16:54:20 +08:00
|
|
|
}
|
2018-08-14 17:34:00 +08:00
|
|
|
return ret;
|
2018-07-27 16:54:20 +08:00
|
|
|
}
|
|
|
|
|
2023-08-02 00:49:11 +08:00
|
|
|
#ifdef MG_ENTERPRISE
|
|
|
|
bool Auth::GrantDatabaseToUser(const std::string &db, const std::string &name) {
|
2024-01-23 19:06:10 +08:00
|
|
|
if (auto user = GetUser(name)) {
|
2023-08-02 00:49:11 +08:00
|
|
|
if (db == kAllDatabases) {
|
|
|
|
user->db_access().GrantAll();
|
|
|
|
} else {
|
|
|
|
user->db_access().Add(db);
|
|
|
|
}
|
|
|
|
SaveUser(*user);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Auth::RevokeDatabaseFromUser(const std::string &db, const std::string &name) {
|
2024-01-23 19:06:10 +08:00
|
|
|
if (auto user = GetUser(name)) {
|
2023-08-02 00:49:11 +08:00
|
|
|
if (db == kAllDatabases) {
|
|
|
|
user->db_access().DenyAll();
|
|
|
|
} else {
|
|
|
|
user->db_access().Remove(db);
|
|
|
|
}
|
|
|
|
SaveUser(*user);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Auth::DeleteDatabase(const std::string &db) {
|
|
|
|
for (auto it = storage_.begin(kUserPrefix); it != storage_.end(kUserPrefix); ++it) {
|
|
|
|
auto username = it->first.substr(kUserPrefix.size());
|
2024-01-23 19:06:10 +08:00
|
|
|
if (auto user = GetUser(username)) {
|
2023-08-02 00:49:11 +08:00
|
|
|
user->db_access().Delete(db);
|
|
|
|
SaveUser(*user);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-23 19:06:10 +08:00
|
|
|
bool Auth::SetMainDatabase(std::string_view db, const std::string &name) {
|
|
|
|
if (auto user = GetUser(name)) {
|
2023-08-02 00:49:11 +08:00
|
|
|
if (!user->db_access().SetDefault(db)) {
|
|
|
|
throw AuthException("Couldn't set default database '{}' for user '{}'!", db, name);
|
|
|
|
}
|
|
|
|
SaveUser(*user);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2024-01-29 20:52:32 +08:00
|
|
|
bool Auth::NameRegexMatch(const std::string &user_or_role) const {
|
|
|
|
if (config_.custom_name_regex) {
|
|
|
|
if (const auto license_check_result =
|
|
|
|
memgraph::license::global_license_checker.IsEnterpriseValid(memgraph::utils::global_settings);
|
|
|
|
license_check_result.HasError()) {
|
|
|
|
throw memgraph::auth::AuthException(
|
|
|
|
"Custom user/role regex is a Memgraph Enterprise feature. Please set the config "
|
|
|
|
"(\"--auth-user-or-role-name-regex\") to its default value (\"{}\") or remove the flag.\n{}",
|
|
|
|
glue::kDefaultUserRoleRegex,
|
|
|
|
memgraph::license::LicenseCheckErrorToString(license_check_result.GetError(), "user/role regex"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return std::regex_match(user_or_role, config_.name_regex);
|
|
|
|
}
|
|
|
|
|
2022-03-09 22:53:33 +08:00
|
|
|
} // namespace memgraph::auth
|