Fix auth durability (#1644)
* Change auth durability to store hash algorithm * Add Salt to SHA256
This commit is contained in:
parent
78a88737f8
commit
97b1e67d80
@ -13,6 +13,7 @@
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "auth/crypto.hpp"
|
||||
#include "auth/exceptions.hpp"
|
||||
#include "license/license.hpp"
|
||||
#include "utils/flag_validation.hpp"
|
||||
@ -43,6 +44,9 @@ namespace memgraph::auth {
|
||||
const std::string kUserPrefix = "user:";
|
||||
const std::string kRolePrefix = "role:";
|
||||
const std::string kLinkPrefix = "link:";
|
||||
const std::string kVersion = "version";
|
||||
|
||||
static constexpr auto kVersionV1 = "V1";
|
||||
|
||||
/**
|
||||
* All data stored in the `Auth` storage is stored in an underlying
|
||||
@ -61,8 +65,59 @@ const std::string kLinkPrefix = "link:";
|
||||
* key="link:<username>", value="<rolename>"
|
||||
*/
|
||||
|
||||
namespace {
|
||||
void MigrateVersions(kvstore::KVStore &store) {
|
||||
static constexpr auto kPasswordHashV0V1 = "password_hash";
|
||||
auto version_str = store.Get(kVersion);
|
||||
|
||||
if (!version_str) {
|
||||
using namespace std::string_literals;
|
||||
|
||||
// pre versioning, add version to the store
|
||||
auto puts = std::map<std::string, std::string>{{kVersion, kVersionV1}};
|
||||
|
||||
// also add hash kind into durability
|
||||
|
||||
auto it = store.begin(kUserPrefix);
|
||||
auto const e = store.end(kUserPrefix);
|
||||
|
||||
if (it != e) {
|
||||
const auto hash_algo = CurrentHashAlgorithm();
|
||||
spdlog::info("Updating auth durability, assuming previously stored as {}", AsString(hash_algo));
|
||||
|
||||
for (; it != e; ++it) {
|
||||
auto const &[key, value] = *it;
|
||||
try {
|
||||
auto user_data = nlohmann::json::parse(value);
|
||||
|
||||
auto password_hash = user_data[kPasswordHashV0V1];
|
||||
if (!password_hash.is_string()) {
|
||||
throw AuthException("Couldn't load user data!");
|
||||
}
|
||||
// upgrade the password_hash to include the hash algortihm
|
||||
if (password_hash.empty()) {
|
||||
user_data[kPasswordHashV0V1] = nullptr;
|
||||
} else {
|
||||
user_data[kPasswordHashV0V1] = HashedPassword{hash_algo, password_hash};
|
||||
}
|
||||
puts.emplace(key, user_data.dump());
|
||||
} catch (const nlohmann::json::parse_error &e) {
|
||||
throw AuthException("Couldn't load user data!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Perform migration to V1
|
||||
store.PutMultiple(puts);
|
||||
version_str = kVersionV1;
|
||||
}
|
||||
}
|
||||
}; // namespace
|
||||
|
||||
Auth::Auth(std::string storage_directory, Config config)
|
||||
: storage_(std::move(storage_directory)), module_(FLAGS_auth_module_executable), config_{std::move(config)} {}
|
||||
: storage_(std::move(storage_directory)), module_(FLAGS_auth_module_executable), config_{std::move(config)} {
|
||||
MigrateVersions(storage_);
|
||||
}
|
||||
|
||||
std::optional<User> Auth::Authenticate(const std::string &username, const std::string &password) {
|
||||
if (module_.IsUsed()) {
|
||||
@ -153,6 +208,10 @@ std::optional<User> Auth::Authenticate(const std::string &username, const std::s
|
||||
username, "https://memgr.ph/auth"));
|
||||
return std::nullopt;
|
||||
}
|
||||
if (user->UpgradeHash(password)) {
|
||||
SaveUser(*user);
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 Memgraph Ltd.
|
||||
//
|
||||
// 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
|
||||
@ -22,10 +22,14 @@
|
||||
|
||||
namespace {
|
||||
using namespace std::literals;
|
||||
inline constexpr std::array password_encryption_mappings{
|
||||
std::pair{"bcrypt"sv, memgraph::auth::PasswordEncryptionAlgorithm::BCRYPT},
|
||||
std::pair{"sha256"sv, memgraph::auth::PasswordEncryptionAlgorithm::SHA256},
|
||||
std::pair{"sha256-multiple"sv, memgraph::auth::PasswordEncryptionAlgorithm::SHA256_MULTIPLE}};
|
||||
|
||||
constexpr auto kHashAlgo = "hash_algo";
|
||||
constexpr auto kPasswordHash = "password_hash";
|
||||
|
||||
inline constexpr std::array password_hash_mappings{
|
||||
std::pair{"bcrypt"sv, memgraph::auth::PasswordHashAlgorithm::BCRYPT},
|
||||
std::pair{"sha256"sv, memgraph::auth::PasswordHashAlgorithm::SHA256},
|
||||
std::pair{"sha256-multiple"sv, memgraph::auth::PasswordHashAlgorithm::SHA256_MULTIPLE}};
|
||||
|
||||
inline constexpr uint64_t ONE_SHA_ITERATION = 1;
|
||||
inline constexpr uint64_t MULTIPLE_SHA_ITERATIONS = 1024;
|
||||
@ -35,7 +39,7 @@ inline constexpr uint64_t MULTIPLE_SHA_ITERATIONS = 1024;
|
||||
DEFINE_VALIDATED_string(password_encryption_algorithm, "bcrypt",
|
||||
"The password encryption algorithm used for authentication.", {
|
||||
if (const auto result =
|
||||
memgraph::utils::IsValidEnumValueString(value, password_encryption_mappings);
|
||||
memgraph::utils::IsValidEnumValueString(value, password_hash_mappings);
|
||||
result.HasError()) {
|
||||
const auto error = result.GetError();
|
||||
switch (error) {
|
||||
@ -45,7 +49,7 @@ DEFINE_VALIDATED_string(password_encryption_algorithm, "bcrypt",
|
||||
}
|
||||
case memgraph::utils::ValidationError::InvalidValue: {
|
||||
std::cout << "Invalid value for password encryption algorithm. Allowed values: "
|
||||
<< memgraph::utils::GetAllowedEnumValuesString(password_encryption_mappings)
|
||||
<< memgraph::utils::GetAllowedEnumValuesString(password_hash_mappings)
|
||||
<< std::endl;
|
||||
break;
|
||||
}
|
||||
@ -58,7 +62,7 @@ DEFINE_VALIDATED_string(password_encryption_algorithm, "bcrypt",
|
||||
|
||||
namespace memgraph::auth {
|
||||
namespace BCrypt {
|
||||
std::string EncryptPassword(const std::string &password) {
|
||||
std::string HashPassword(const std::string &password) {
|
||||
char salt[BCRYPT_HASHSIZE];
|
||||
char hash[BCRYPT_HASHSIZE];
|
||||
|
||||
@ -86,16 +90,30 @@ bool VerifyPassword(const std::string &password, const std::string &hash) {
|
||||
} // namespace BCrypt
|
||||
|
||||
namespace SHA {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr auto SHA_LENGTH = 64U;
|
||||
constexpr auto SALT_SIZE = 16U;
|
||||
constexpr auto SALT_SIZE_DURABLE = SALT_SIZE * 2;
|
||||
|
||||
#if OPENSSL_VERSION_MAJOR >= 3
|
||||
std::string EncryptPasswordOpenSSL3(const std::string &password, const uint64_t number_of_iterations) {
|
||||
std::string HashPasswordOpenSSL3(std::string_view password, const uint64_t number_of_iterations,
|
||||
std::string_view salt) {
|
||||
unsigned char hash[SHA256_DIGEST_LENGTH];
|
||||
|
||||
EVP_MD_CTX *ctx = EVP_MD_CTX_new();
|
||||
EVP_MD *md = EVP_MD_fetch(nullptr, "SHA2-256", nullptr);
|
||||
|
||||
EVP_DigestInit_ex(ctx, md, nullptr);
|
||||
|
||||
if (!salt.empty()) {
|
||||
DMG_ASSERT(salt.size() == SALT_SIZE);
|
||||
EVP_DigestUpdate(ctx, salt.data(), salt.size());
|
||||
}
|
||||
|
||||
for (auto i = 0; i < number_of_iterations; i++) {
|
||||
EVP_DigestUpdate(ctx, password.c_str(), password.size());
|
||||
EVP_DigestUpdate(ctx, password.data(), password.size());
|
||||
}
|
||||
EVP_DigestFinal_ex(ctx, hash, nullptr);
|
||||
|
||||
@ -103,6 +121,11 @@ std::string EncryptPasswordOpenSSL3(const std::string &password, const uint64_t
|
||||
EVP_MD_CTX_free(ctx);
|
||||
|
||||
std::stringstream result_stream;
|
||||
|
||||
for (unsigned char salt_char : salt) {
|
||||
result_stream << std::hex << std::setw(2) << std::setfill('0') << (((unsigned int)salt_char) & 0xFFU);
|
||||
}
|
||||
|
||||
for (auto hash_char : hash) {
|
||||
result_stream << std::hex << std::setw(2) << std::setfill('0') << (int)hash_char;
|
||||
}
|
||||
@ -110,17 +133,27 @@ std::string EncryptPasswordOpenSSL3(const std::string &password, const uint64_t
|
||||
return result_stream.str();
|
||||
}
|
||||
#else
|
||||
std::string EncryptPasswordOpenSSL1_1(const std::string &password, const uint64_t number_of_iterations) {
|
||||
std::string HashPasswordOpenSSL1_1(std::string_view password, const uint64_t number_of_iterations,
|
||||
std::string_view salt) {
|
||||
unsigned char hash[SHA256_DIGEST_LENGTH];
|
||||
|
||||
SHA256_CTX sha256;
|
||||
SHA256_Init(&sha256);
|
||||
|
||||
if (!salt.empty()) {
|
||||
DMG_ASSERT(salt.size() == SALT_SIZE);
|
||||
SHA256_Update(&sha256, salt.data(), salt.size());
|
||||
}
|
||||
|
||||
for (auto i = 0; i < number_of_iterations; i++) {
|
||||
SHA256_Update(&sha256, password.c_str(), password.size());
|
||||
SHA256_Update(&sha256, password.data(), password.size());
|
||||
}
|
||||
SHA256_Final(hash, &sha256);
|
||||
|
||||
std::stringstream ss;
|
||||
for (unsigned char salt_char : salt) {
|
||||
ss << std::hex << std::setw(2) << std::setfill('0') << (((unsigned int)salt_char) & 0xFFU);
|
||||
}
|
||||
for (auto hash_char : hash) {
|
||||
ss << std::hex << std::setw(2) << std::setfill('0') << (int)hash_char;
|
||||
}
|
||||
@ -129,55 +162,144 @@ std::string EncryptPasswordOpenSSL1_1(const std::string &password, const uint64_
|
||||
}
|
||||
#endif
|
||||
|
||||
std::string EncryptPassword(const std::string &password, const uint64_t number_of_iterations) {
|
||||
std::string HashPassword(std::string_view password, const uint64_t number_of_iterations, std::string_view salt) {
|
||||
#if OPENSSL_VERSION_MAJOR >= 3
|
||||
return EncryptPasswordOpenSSL3(password, number_of_iterations);
|
||||
return HashPasswordOpenSSL3(password, number_of_iterations, salt);
|
||||
#else
|
||||
return EncryptPasswordOpenSSL1_1(password, number_of_iterations);
|
||||
return HashPasswordOpenSSL1_1(password, number_of_iterations, salt);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool VerifyPassword(const std::string &password, const std::string &hash, const uint64_t number_of_iterations) {
|
||||
auto password_hash = EncryptPassword(password, number_of_iterations);
|
||||
auto ExtractSalt(std::string_view salt_durable) -> std::array<char, SALT_SIZE> {
|
||||
static_assert(SALT_SIZE_DURABLE % 2 == 0);
|
||||
static_assert(SALT_SIZE_DURABLE / 2 == SALT_SIZE);
|
||||
|
||||
MG_ASSERT(salt_durable.size() == SALT_SIZE_DURABLE);
|
||||
auto const *b = salt_durable.cbegin();
|
||||
auto const *const e = salt_durable.cend();
|
||||
|
||||
auto salt = std::array<char, SALT_SIZE>{};
|
||||
auto *inserter = salt.begin();
|
||||
|
||||
auto const toval = [](char a) -> uint8_t {
|
||||
if ('0' <= a && a <= '9') {
|
||||
return a - '0';
|
||||
}
|
||||
if ('a' <= a && a <= 'f') {
|
||||
return 10 + (a - 'a');
|
||||
}
|
||||
MG_ASSERT(false, "Currupt hash, can't extract salt");
|
||||
__builtin_unreachable();
|
||||
};
|
||||
|
||||
for (; b != e; b += 2, ++inserter) {
|
||||
*inserter = static_cast<char>(static_cast<uint8_t>(toval(b[0]) << 4U) | toval(b[1]));
|
||||
}
|
||||
return salt;
|
||||
}
|
||||
|
||||
bool IsSalted(std::string_view hash) { return hash.size() == SHA_LENGTH + SALT_SIZE_DURABLE; }
|
||||
|
||||
bool VerifyPassword(std::string_view password, std::string_view hash, const uint64_t number_of_iterations) {
|
||||
auto password_hash = std::invoke([&] {
|
||||
if (hash.size() == SHA_LENGTH) [[unlikely]] {
|
||||
// Just SHA256
|
||||
return HashPassword(password, number_of_iterations, {});
|
||||
} else {
|
||||
// SHA256 + SALT
|
||||
MG_ASSERT(IsSalted(hash));
|
||||
auto const salt_durable = std::string_view{hash.data(), SALT_SIZE_DURABLE};
|
||||
std::array<char, SALT_SIZE> salt = ExtractSalt(salt_durable);
|
||||
return HashPassword(password, number_of_iterations, {salt.data(), salt.size()});
|
||||
}
|
||||
});
|
||||
return password_hash == hash;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
} // namespace SHA
|
||||
|
||||
bool VerifyPassword(const std::string &password, const std::string &hash) {
|
||||
const auto password_encryption_algorithm = utils::StringToEnum<PasswordEncryptionAlgorithm>(
|
||||
FLAGS_password_encryption_algorithm, password_encryption_mappings);
|
||||
HashedPassword HashPassword(const std::string &password, std::optional<PasswordHashAlgorithm> override_algo) {
|
||||
auto const hash_algo = override_algo.value_or(CurrentHashAlgorithm());
|
||||
auto password_hash = std::invoke([&] {
|
||||
switch (hash_algo) {
|
||||
case PasswordHashAlgorithm::BCRYPT: {
|
||||
return BCrypt::HashPassword(password);
|
||||
}
|
||||
case PasswordHashAlgorithm::SHA256:
|
||||
case PasswordHashAlgorithm::SHA256_MULTIPLE: {
|
||||
auto gen = std::mt19937(std::random_device{}());
|
||||
auto salt = std::array<char, SHA::SALT_SIZE>{};
|
||||
auto dis = std::uniform_int_distribution<unsigned char>(0, 255);
|
||||
std::generate(salt.begin(), salt.end(), [&]() { return dis(gen); });
|
||||
auto iterations = (hash_algo == PasswordHashAlgorithm::SHA256) ? ONE_SHA_ITERATION : MULTIPLE_SHA_ITERATIONS;
|
||||
return SHA::HashPassword(password, iterations, {salt.data(), salt.size()});
|
||||
}
|
||||
}
|
||||
});
|
||||
return HashedPassword{hash_algo, std::move(password_hash)};
|
||||
};
|
||||
|
||||
if (!password_encryption_algorithm.has_value()) {
|
||||
throw AuthException("Invalid password encryption flag '{}'!", FLAGS_password_encryption_algorithm);
|
||||
namespace {
|
||||
|
||||
auto InternalParseHashAlgorithm(std::string_view algo) -> PasswordHashAlgorithm {
|
||||
auto maybe_parsed = utils::StringToEnum<PasswordHashAlgorithm>(algo, password_hash_mappings);
|
||||
if (!maybe_parsed) {
|
||||
throw AuthException("Invalid password encryption '{}'!", algo);
|
||||
}
|
||||
|
||||
switch (password_encryption_algorithm.value()) {
|
||||
case PasswordEncryptionAlgorithm::BCRYPT:
|
||||
return BCrypt::VerifyPassword(password, hash);
|
||||
case PasswordEncryptionAlgorithm::SHA256:
|
||||
return SHA::VerifyPassword(password, hash, ONE_SHA_ITERATION);
|
||||
case PasswordEncryptionAlgorithm::SHA256_MULTIPLE:
|
||||
return SHA::VerifyPassword(password, hash, MULTIPLE_SHA_ITERATIONS);
|
||||
}
|
||||
|
||||
throw AuthException("Invalid password encryption flag '{}'!", FLAGS_password_encryption_algorithm);
|
||||
return *maybe_parsed;
|
||||
}
|
||||
|
||||
std::string EncryptPassword(const std::string &password) {
|
||||
const auto password_encryption_algorithm = utils::StringToEnum<PasswordEncryptionAlgorithm>(
|
||||
FLAGS_password_encryption_algorithm, password_encryption_mappings);
|
||||
PasswordHashAlgorithm &InternalCurrentHashAlgorithm() {
|
||||
static auto current = PasswordHashAlgorithm::BCRYPT;
|
||||
static std::once_flag flag;
|
||||
std::call_once(flag, [] { current = InternalParseHashAlgorithm(FLAGS_password_encryption_algorithm); });
|
||||
return current;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
if (!password_encryption_algorithm.has_value()) {
|
||||
throw AuthException("Invalid password encryption flag '{}'!", FLAGS_password_encryption_algorithm);
|
||||
auto CurrentHashAlgorithm() -> PasswordHashAlgorithm { return InternalCurrentHashAlgorithm(); }
|
||||
|
||||
void SetHashAlgorithm(std::string_view algo) {
|
||||
auto ¤t = InternalCurrentHashAlgorithm();
|
||||
current = InternalParseHashAlgorithm(algo);
|
||||
}
|
||||
|
||||
auto AsString(PasswordHashAlgorithm hash_algo) -> std::string_view {
|
||||
return *utils::EnumToString<PasswordHashAlgorithm>(hash_algo, password_hash_mappings);
|
||||
}
|
||||
|
||||
bool HashedPassword::VerifyPassword(const std::string &password) {
|
||||
switch (hash_algo) {
|
||||
case PasswordHashAlgorithm::BCRYPT:
|
||||
return BCrypt::VerifyPassword(password, password_hash);
|
||||
case PasswordHashAlgorithm::SHA256:
|
||||
return SHA::VerifyPassword(password, password_hash, ONE_SHA_ITERATION);
|
||||
case PasswordHashAlgorithm::SHA256_MULTIPLE:
|
||||
return SHA::VerifyPassword(password, password_hash, MULTIPLE_SHA_ITERATIONS);
|
||||
}
|
||||
}
|
||||
|
||||
switch (password_encryption_algorithm.value()) {
|
||||
case PasswordEncryptionAlgorithm::BCRYPT:
|
||||
return BCrypt::EncryptPassword(password);
|
||||
case PasswordEncryptionAlgorithm::SHA256:
|
||||
return SHA::EncryptPassword(password, ONE_SHA_ITERATION);
|
||||
case PasswordEncryptionAlgorithm::SHA256_MULTIPLE:
|
||||
return SHA::EncryptPassword(password, MULTIPLE_SHA_ITERATIONS);
|
||||
void to_json(nlohmann::json &j, const HashedPassword &p) {
|
||||
j = nlohmann::json{{kHashAlgo, p.hash_algo}, {kPasswordHash, p.password_hash}};
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, HashedPassword &p) {
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-init-variables)
|
||||
PasswordHashAlgorithm hash_algo;
|
||||
j.at(kHashAlgo).get_to(hash_algo);
|
||||
auto password_hash = j.value(kPasswordHash, std::string());
|
||||
p = HashedPassword{hash_algo, std::move(password_hash)};
|
||||
}
|
||||
|
||||
bool HashedPassword::IsSalted() const {
|
||||
switch (hash_algo) {
|
||||
case PasswordHashAlgorithm::BCRYPT:
|
||||
return true;
|
||||
case PasswordHashAlgorithm::SHA256:
|
||||
case PasswordHashAlgorithm::SHA256_MULTIPLE:
|
||||
return SHA::IsSalted(password_hash);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 Memgraph Ltd.
|
||||
//
|
||||
// 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
|
||||
@ -8,14 +8,45 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <json/json.hpp>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace memgraph::auth {
|
||||
enum class PasswordEncryptionAlgorithm : uint8_t { BCRYPT, SHA256, SHA256_MULTIPLE };
|
||||
/// Need to be stable, auth durability depends on this
|
||||
enum class PasswordHashAlgorithm : uint8_t { BCRYPT = 0, SHA256 = 1, SHA256_MULTIPLE = 2 };
|
||||
|
||||
/// @throw AuthException if unable to encrypt the password.
|
||||
std::string EncryptPassword(const std::string &password);
|
||||
void SetHashAlgorithm(std::string_view algo);
|
||||
|
||||
/// @throw AuthException if unable to verify the password.
|
||||
bool VerifyPassword(const std::string &password, const std::string &hash);
|
||||
auto CurrentHashAlgorithm() -> PasswordHashAlgorithm;
|
||||
|
||||
auto AsString(PasswordHashAlgorithm hash_algo) -> std::string_view;
|
||||
|
||||
struct HashedPassword {
|
||||
HashedPassword() = default;
|
||||
HashedPassword(PasswordHashAlgorithm hash_algo, std::string password_hash)
|
||||
: hash_algo{hash_algo}, password_hash{std::move(password_hash)} {}
|
||||
HashedPassword(HashedPassword const &) = default;
|
||||
HashedPassword(HashedPassword &&) = default;
|
||||
HashedPassword &operator=(HashedPassword const &) = default;
|
||||
HashedPassword &operator=(HashedPassword &&) = default;
|
||||
|
||||
friend bool operator==(HashedPassword const &, HashedPassword const &) = default;
|
||||
|
||||
bool VerifyPassword(const std::string &password);
|
||||
|
||||
bool IsSalted() const;
|
||||
|
||||
auto HashAlgo() const -> PasswordHashAlgorithm { return hash_algo; }
|
||||
|
||||
friend void to_json(nlohmann::json &j, const HashedPassword &p);
|
||||
friend void from_json(const nlohmann::json &j, HashedPassword &p);
|
||||
|
||||
private:
|
||||
PasswordHashAlgorithm hash_algo{PasswordHashAlgorithm::BCRYPT};
|
||||
std::string password_hash{};
|
||||
};
|
||||
|
||||
/// @throw AuthException if unable to hash the password.
|
||||
HashedPassword HashPassword(const std::string &password, std::optional<PasswordHashAlgorithm> override_algo = {});
|
||||
} // namespace memgraph::auth
|
||||
|
@ -25,6 +25,21 @@
|
||||
namespace memgraph::auth {
|
||||
namespace {
|
||||
|
||||
constexpr auto kRoleName = "rolename";
|
||||
constexpr auto kPermissions = "permissions";
|
||||
constexpr auto kGrants = "grants";
|
||||
constexpr auto kDenies = "denies";
|
||||
constexpr auto kUsername = "username";
|
||||
constexpr auto kPasswordHash = "password_hash";
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
constexpr auto kGlobalPermission = "global_permission";
|
||||
constexpr auto kFineGrainedAccessHandler = "fine_grained_access_handler";
|
||||
constexpr auto kAllowAll = "allow_all";
|
||||
constexpr auto kDefault = "default";
|
||||
constexpr auto kDatabases = "databases";
|
||||
#endif
|
||||
|
||||
// Constant list of all available permissions.
|
||||
const std::vector<Permission> kPermissionsAll = {Permission::MATCH,
|
||||
Permission::CREATE,
|
||||
@ -233,8 +248,9 @@ std::vector<Permission> Permissions::GetDenies() const {
|
||||
|
||||
nlohmann::json Permissions::Serialize() const {
|
||||
nlohmann::json data = nlohmann::json::object();
|
||||
data["grants"] = grants_;
|
||||
data["denies"] = denies_;
|
||||
|
||||
data[kGrants] = grants_;
|
||||
data[kDenies] = denies_;
|
||||
return data;
|
||||
}
|
||||
|
||||
@ -242,10 +258,10 @@ Permissions Permissions::Deserialize(const nlohmann::json &data) {
|
||||
if (!data.is_object()) {
|
||||
throw AuthException("Couldn't load permissions data!");
|
||||
}
|
||||
if (!data["grants"].is_number_unsigned() || !data["denies"].is_number_unsigned()) {
|
||||
if (!data[kGrants].is_number_unsigned() || !data[kDenies].is_number_unsigned()) {
|
||||
throw AuthException("Couldn't load permissions data!");
|
||||
}
|
||||
return Permissions{data["grants"], data["denies"]};
|
||||
return Permissions{data[kGrants], data[kDenies]};
|
||||
}
|
||||
|
||||
uint64_t Permissions::grants() const { return grants_; }
|
||||
@ -307,8 +323,8 @@ nlohmann::json FineGrainedAccessPermissions::Serialize() const {
|
||||
return {};
|
||||
}
|
||||
nlohmann::json data = nlohmann::json::object();
|
||||
data["permissions"] = permissions_;
|
||||
data["global_permission"] = global_permission_.has_value() ? global_permission_.value() : -1;
|
||||
data[kPermissions] = permissions_;
|
||||
data[kGlobalPermission] = global_permission_.has_value() ? global_permission_.value() : -1;
|
||||
return data;
|
||||
}
|
||||
|
||||
@ -321,13 +337,13 @@ FineGrainedAccessPermissions FineGrainedAccessPermissions::Deserialize(const nlo
|
||||
}
|
||||
std::optional<uint64_t> global_permission;
|
||||
|
||||
if (data["global_permission"].empty() || data["global_permission"] == -1) {
|
||||
if (data[kGlobalPermission].empty() || data[kGlobalPermission] == -1) {
|
||||
global_permission = std::nullopt;
|
||||
} else {
|
||||
global_permission = data["global_permission"];
|
||||
global_permission = data[kGlobalPermission];
|
||||
}
|
||||
|
||||
return FineGrainedAccessPermissions(data["permissions"], global_permission);
|
||||
return FineGrainedAccessPermissions(data[kPermissions], global_permission);
|
||||
}
|
||||
|
||||
const std::unordered_map<std::string, uint64_t> &FineGrainedAccessPermissions::GetPermissions() const {
|
||||
@ -433,13 +449,13 @@ const FineGrainedAccessPermissions &Role::GetFineGrainedAccessEdgeTypePermission
|
||||
|
||||
nlohmann::json Role::Serialize() const {
|
||||
nlohmann::json data = nlohmann::json::object();
|
||||
data["rolename"] = rolename_;
|
||||
data["permissions"] = permissions_.Serialize();
|
||||
data[kRoleName] = rolename_;
|
||||
data[kPermissions] = permissions_.Serialize();
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
data["fine_grained_access_handler"] = fine_grained_access_handler_.Serialize();
|
||||
data[kFineGrainedAccessHandler] = fine_grained_access_handler_.Serialize();
|
||||
} else {
|
||||
data["fine_grained_access_handler"] = {};
|
||||
data[kFineGrainedAccessHandler] = {};
|
||||
}
|
||||
#endif
|
||||
return data;
|
||||
@ -449,21 +465,21 @@ Role Role::Deserialize(const nlohmann::json &data) {
|
||||
if (!data.is_object()) {
|
||||
throw AuthException("Couldn't load role data!");
|
||||
}
|
||||
if (!data["rolename"].is_string() || !data["permissions"].is_object()) {
|
||||
if (!data[kRoleName].is_string() || !data[kPermissions].is_object()) {
|
||||
throw AuthException("Couldn't load role data!");
|
||||
}
|
||||
auto permissions = Permissions::Deserialize(data["permissions"]);
|
||||
auto permissions = Permissions::Deserialize(data[kPermissions]);
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
FineGrainedAccessHandler fine_grained_access_handler;
|
||||
// We can have an empty fine_grained if the user was created without a valid license
|
||||
if (data["fine_grained_access_handler"].is_object()) {
|
||||
fine_grained_access_handler = FineGrainedAccessHandler::Deserialize(data["fine_grained_access_handler"]);
|
||||
if (data[kFineGrainedAccessHandler].is_object()) {
|
||||
fine_grained_access_handler = FineGrainedAccessHandler::Deserialize(data[kFineGrainedAccessHandler]);
|
||||
}
|
||||
return {data["rolename"], permissions, std::move(fine_grained_access_handler)};
|
||||
return {data[kRoleName], permissions, std::move(fine_grained_access_handler)};
|
||||
}
|
||||
#endif
|
||||
return {data["rolename"], permissions};
|
||||
return {data[kRoleName], permissions};
|
||||
}
|
||||
|
||||
bool operator==(const Role &first, const Role &second) {
|
||||
@ -533,10 +549,10 @@ const std::string &Databases::GetDefault() const {
|
||||
|
||||
nlohmann::json Databases::Serialize() const {
|
||||
nlohmann::json data = nlohmann::json::object();
|
||||
data["grants"] = grants_dbs_;
|
||||
data["denies"] = denies_dbs_;
|
||||
data["allow_all"] = allow_all_;
|
||||
data["default"] = default_db_;
|
||||
data[kGrants] = grants_dbs_;
|
||||
data[kDenies] = denies_dbs_;
|
||||
data[kAllowAll] = allow_all_;
|
||||
data[kDefault] = default_db_;
|
||||
return data;
|
||||
}
|
||||
|
||||
@ -544,22 +560,22 @@ Databases Databases::Deserialize(const nlohmann::json &data) {
|
||||
if (!data.is_object()) {
|
||||
throw AuthException("Couldn't load database data!");
|
||||
}
|
||||
if (!data["grants"].is_structured() || !data["denies"].is_structured() || !data["allow_all"].is_boolean() ||
|
||||
!data["default"].is_string()) {
|
||||
if (!data[kGrants].is_structured() || !data[kDenies].is_structured() || !data[kAllowAll].is_boolean() ||
|
||||
!data[kDefault].is_string()) {
|
||||
throw AuthException("Couldn't load database data!");
|
||||
}
|
||||
return {data["allow_all"], data["grants"], data["denies"], data["default"]};
|
||||
return {data[kAllowAll], data[kGrants], data[kDenies], data[kDefault]};
|
||||
}
|
||||
#endif
|
||||
|
||||
User::User() = default;
|
||||
|
||||
User::User(const std::string &username) : username_(utils::ToLowerCase(username)) {}
|
||||
User::User(const std::string &username, std::string password_hash, const Permissions &permissions)
|
||||
User::User(const std::string &username, std::optional<HashedPassword> password_hash, const Permissions &permissions)
|
||||
: username_(utils::ToLowerCase(username)), password_hash_(std::move(password_hash)), permissions_(permissions) {}
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
User::User(const std::string &username, std::string password_hash, const Permissions &permissions,
|
||||
User::User(const std::string &username, std::optional<HashedPassword> password_hash, const Permissions &permissions,
|
||||
FineGrainedAccessHandler fine_grained_access_handler, Databases db_access)
|
||||
: username_(utils::ToLowerCase(username)),
|
||||
password_hash_(std::move(password_hash)),
|
||||
@ -569,16 +585,16 @@ User::User(const std::string &username, std::string password_hash, const Permiss
|
||||
#endif
|
||||
|
||||
bool User::CheckPassword(const std::string &password) {
|
||||
if (password_hash_.empty()) return true;
|
||||
return VerifyPassword(password, password_hash_);
|
||||
return password_hash_ ? password_hash_->VerifyPassword(password) : true;
|
||||
}
|
||||
|
||||
void User::UpdatePassword(const std::optional<std::string> &password) {
|
||||
void User::UpdatePassword(const std::optional<std::string> &password,
|
||||
std::optional<PasswordHashAlgorithm> algo_override) {
|
||||
if (!password) {
|
||||
password_hash_ = "";
|
||||
password_hash_.reset();
|
||||
return;
|
||||
}
|
||||
password_hash_ = EncryptPassword(*password);
|
||||
password_hash_ = HashPassword(*password, algo_override);
|
||||
}
|
||||
|
||||
void User::SetRole(const Role &role) { role_.emplace(role); }
|
||||
@ -637,16 +653,20 @@ const Role *User::role() const {
|
||||
|
||||
nlohmann::json User::Serialize() const {
|
||||
nlohmann::json data = nlohmann::json::object();
|
||||
data["username"] = username_;
|
||||
data["password_hash"] = password_hash_;
|
||||
data["permissions"] = permissions_.Serialize();
|
||||
data[kUsername] = username_;
|
||||
if (password_hash_.has_value()) {
|
||||
data[kPasswordHash] = *password_hash_;
|
||||
} else {
|
||||
data[kPasswordHash] = nullptr;
|
||||
}
|
||||
data[kPermissions] = permissions_.Serialize();
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
data["fine_grained_access_handler"] = fine_grained_access_handler_.Serialize();
|
||||
data["databases"] = database_access_.Serialize();
|
||||
data[kFineGrainedAccessHandler] = fine_grained_access_handler_.Serialize();
|
||||
data[kDatabases] = database_access_.Serialize();
|
||||
} else {
|
||||
data["fine_grained_access_handler"] = {};
|
||||
data["databases"] = {};
|
||||
data[kFineGrainedAccessHandler] = {};
|
||||
data[kDatabases] = {};
|
||||
}
|
||||
#endif
|
||||
// The role shouldn't be serialized here, it is stored as a foreign key.
|
||||
@ -657,15 +677,23 @@ User User::Deserialize(const nlohmann::json &data) {
|
||||
if (!data.is_object()) {
|
||||
throw AuthException("Couldn't load user data!");
|
||||
}
|
||||
if (!data["username"].is_string() || !data["password_hash"].is_string() || !data["permissions"].is_object()) {
|
||||
auto password_hash_json = data[kPasswordHash];
|
||||
if (!data[kUsername].is_string() || !(password_hash_json.is_object() || password_hash_json.is_null()) ||
|
||||
!data[kPermissions].is_object()) {
|
||||
throw AuthException("Couldn't load user data!");
|
||||
}
|
||||
auto permissions = Permissions::Deserialize(data["permissions"]);
|
||||
|
||||
std::optional<HashedPassword> password_hash{};
|
||||
if (password_hash_json.is_object()) {
|
||||
password_hash = password_hash_json.get<HashedPassword>();
|
||||
}
|
||||
|
||||
auto permissions = Permissions::Deserialize(data[kPermissions]);
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
Databases db_access;
|
||||
if (data["databases"].is_structured()) {
|
||||
db_access = Databases::Deserialize(data["databases"]);
|
||||
if (data[kDatabases].is_structured()) {
|
||||
db_access = Databases::Deserialize(data[kDatabases]);
|
||||
} else {
|
||||
// Back-compatibility
|
||||
spdlog::warn("User without specified database access. Given access to the default database.");
|
||||
@ -674,13 +702,13 @@ User User::Deserialize(const nlohmann::json &data) {
|
||||
}
|
||||
FineGrainedAccessHandler fine_grained_access_handler;
|
||||
// We can have an empty fine_grained if the user was created without a valid license
|
||||
if (data["fine_grained_access_handler"].is_object()) {
|
||||
fine_grained_access_handler = FineGrainedAccessHandler::Deserialize(data["fine_grained_access_handler"]);
|
||||
if (data[kFineGrainedAccessHandler].is_object()) {
|
||||
fine_grained_access_handler = FineGrainedAccessHandler::Deserialize(data[kFineGrainedAccessHandler]);
|
||||
}
|
||||
return {data["username"], data["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), db_access};
|
||||
}
|
||||
#endif
|
||||
return {data["username"], data["password_hash"], permissions};
|
||||
return {data[kUsername], std::move(password_hash), permissions};
|
||||
}
|
||||
|
||||
bool operator==(const User &first, const User &second) {
|
||||
|
@ -15,6 +15,7 @@
|
||||
|
||||
#include <json/json.hpp>
|
||||
#include <utility>
|
||||
#include "crypto.hpp"
|
||||
#include "dbms/constants.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
|
||||
@ -332,9 +333,9 @@ class User final {
|
||||
User();
|
||||
|
||||
explicit User(const std::string &username);
|
||||
User(const std::string &username, std::string password_hash, const Permissions &permissions);
|
||||
User(const std::string &username, std::optional<HashedPassword> password_hash, const Permissions &permissions);
|
||||
#ifdef MG_ENTERPRISE
|
||||
User(const std::string &username, std::string password_hash, const Permissions &permissions,
|
||||
User(const std::string &username, std::optional<HashedPassword> password_hash, const Permissions &permissions,
|
||||
FineGrainedAccessHandler fine_grained_access_handler, Databases db_access = {});
|
||||
#endif
|
||||
User(const User &) = default;
|
||||
@ -346,8 +347,18 @@ class User final {
|
||||
/// @throw AuthException if unable to verify the password.
|
||||
bool CheckPassword(const std::string &password);
|
||||
|
||||
bool UpgradeHash(const std::string password) {
|
||||
if (!password_hash_) return false;
|
||||
if (password_hash_->IsSalted()) return false;
|
||||
|
||||
auto const algo = password_hash_->HashAlgo();
|
||||
UpdatePassword(password, algo);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// @throw AuthException if unable to set the password.
|
||||
void UpdatePassword(const std::optional<std::string> &password = std::nullopt);
|
||||
void UpdatePassword(const std::optional<std::string> &password = {},
|
||||
std::optional<PasswordHashAlgorithm> algo_override = std::nullopt);
|
||||
|
||||
void SetRole(const Role &role);
|
||||
|
||||
@ -382,7 +393,7 @@ class User final {
|
||||
|
||||
private:
|
||||
std::string username_;
|
||||
std::string password_hash_;
|
||||
std::optional<HashedPassword> password_hash_;
|
||||
Permissions permissions_;
|
||||
#ifdef MG_ENTERPRISE
|
||||
FineGrainedAccessHandler fine_grained_access_handler_;
|
||||
|
@ -55,6 +55,18 @@ std::optional<Enum> StringToEnum(const auto &value, const auto &mappings) {
|
||||
return mapping_iter->second;
|
||||
}
|
||||
|
||||
// Tries to convert a enum into string, which would then contain a value if the conversion
|
||||
// has been successful.
|
||||
template <typename Enum>
|
||||
auto EnumToString(const auto &value, const auto &mappings) -> std::optional<std::string_view> {
|
||||
const auto mapping_iter =
|
||||
std::find_if(mappings.begin(), mappings.end(), [&](const auto &mapping) { return mapping.second == value; });
|
||||
if (mapping_iter == mappings.cend()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return mapping_iter->first;
|
||||
}
|
||||
|
||||
template <typename T, typename Enum>
|
||||
requires std::integral<T>
|
||||
inline T EnumToNum(Enum res) {
|
||||
|
@ -666,6 +666,17 @@ TEST(AuthWithoutStorage, UserSerializeDeserialize) {
|
||||
ASSERT_EQ(user, output);
|
||||
}
|
||||
|
||||
TEST(AuthWithoutStorage, UserSerializeDeserializeWithOutPassword) {
|
||||
auto user = User("test");
|
||||
user.permissions().Grant(Permission::MATCH);
|
||||
user.permissions().Deny(Permission::MERGE);
|
||||
|
||||
auto data = user.Serialize();
|
||||
|
||||
auto output = User::Deserialize(data);
|
||||
ASSERT_EQ(user, output);
|
||||
}
|
||||
|
||||
TEST(AuthWithoutStorage, RoleSerializeDeserialize) {
|
||||
auto role = Role("test");
|
||||
role.permissions().Grant(Permission::MATCH);
|
||||
@ -716,8 +727,9 @@ TEST(AuthWithoutStorage, CaseInsensitivity) {
|
||||
{
|
||||
auto perms = Permissions();
|
||||
auto fine_grained_access_handler = FineGrainedAccessHandler();
|
||||
auto user1 = User("test", "pw", perms, fine_grained_access_handler);
|
||||
auto user2 = User("Test", "pw", perms, fine_grained_access_handler);
|
||||
auto passwordHash = HashPassword("pw");
|
||||
auto user1 = User("test", passwordHash, perms, fine_grained_access_handler);
|
||||
auto user2 = User("Test", passwordHash, perms, fine_grained_access_handler);
|
||||
ASSERT_EQ(user1, user2);
|
||||
ASSERT_EQ(user1.username(), user2.username());
|
||||
ASSERT_EQ(user1.username(), "test");
|
||||
@ -920,51 +932,50 @@ TEST_F(AuthWithStorage, CaseInsensitivity) {
|
||||
}
|
||||
|
||||
TEST(AuthWithoutStorage, Crypto) {
|
||||
auto hash = EncryptPassword("hello");
|
||||
ASSERT_TRUE(VerifyPassword("hello", hash));
|
||||
ASSERT_FALSE(VerifyPassword("hello1", hash));
|
||||
auto hash = HashPassword("hello");
|
||||
ASSERT_TRUE(hash.VerifyPassword("hello"));
|
||||
ASSERT_FALSE(hash.VerifyPassword("hello1"));
|
||||
}
|
||||
|
||||
class AuthWithVariousEncryptionAlgorithms : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override { FLAGS_password_encryption_algorithm = "bcrypt"; }
|
||||
void SetUp() override { SetHashAlgorithm("bcrypt"); }
|
||||
};
|
||||
|
||||
TEST_F(AuthWithVariousEncryptionAlgorithms, VerifyPasswordDefault) {
|
||||
auto hash = EncryptPassword("hello");
|
||||
ASSERT_TRUE(VerifyPassword("hello", hash));
|
||||
ASSERT_FALSE(VerifyPassword("hello1", hash));
|
||||
auto hash = HashPassword("hello");
|
||||
ASSERT_TRUE(hash.VerifyPassword("hello"));
|
||||
ASSERT_FALSE(hash.VerifyPassword("hello1"));
|
||||
}
|
||||
|
||||
TEST_F(AuthWithVariousEncryptionAlgorithms, VerifyPasswordSHA256) {
|
||||
FLAGS_password_encryption_algorithm = "sha256";
|
||||
auto hash = EncryptPassword("hello");
|
||||
ASSERT_TRUE(VerifyPassword("hello", hash));
|
||||
ASSERT_FALSE(VerifyPassword("hello1", hash));
|
||||
SetHashAlgorithm("sha256");
|
||||
auto hash = HashPassword("hello");
|
||||
ASSERT_TRUE(hash.VerifyPassword("hello"));
|
||||
ASSERT_FALSE(hash.VerifyPassword("hello1"));
|
||||
}
|
||||
|
||||
TEST_F(AuthWithVariousEncryptionAlgorithms, VerifyPasswordSHA256_1024) {
|
||||
FLAGS_password_encryption_algorithm = "sha256-multiple";
|
||||
auto hash = EncryptPassword("hello");
|
||||
ASSERT_TRUE(VerifyPassword("hello", hash));
|
||||
ASSERT_FALSE(VerifyPassword("hello1", hash));
|
||||
SetHashAlgorithm("sha256-multiple");
|
||||
auto hash = HashPassword("hello");
|
||||
ASSERT_TRUE(hash.VerifyPassword("hello"));
|
||||
ASSERT_FALSE(hash.VerifyPassword("hello1"));
|
||||
}
|
||||
|
||||
TEST_F(AuthWithVariousEncryptionAlgorithms, VerifyPasswordThrow) {
|
||||
FLAGS_password_encryption_algorithm = "abcd";
|
||||
ASSERT_THROW(EncryptPassword("hello"), AuthException);
|
||||
TEST_F(AuthWithVariousEncryptionAlgorithms, SetEncryptionAlgorithmNonsenseThrow) {
|
||||
ASSERT_THROW(SetHashAlgorithm("abcd"), AuthException);
|
||||
}
|
||||
|
||||
TEST_F(AuthWithVariousEncryptionAlgorithms, VerifyPasswordEmptyEncryptionThrow) {
|
||||
FLAGS_password_encryption_algorithm = "";
|
||||
ASSERT_THROW(EncryptPassword("hello"), AuthException);
|
||||
TEST_F(AuthWithVariousEncryptionAlgorithms, SetEncryptionAlgorithmEmptyThrow) {
|
||||
ASSERT_THROW(SetHashAlgorithm(""), AuthException);
|
||||
}
|
||||
|
||||
class AuthWithStorageWithVariousEncryptionAlgorithms : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
memgraph::utils::EnsureDir(test_folder_);
|
||||
FLAGS_password_encryption_algorithm = "bcrypt";
|
||||
SetHashAlgorithm("bcrypt");
|
||||
|
||||
memgraph::license::global_license_checker.EnableTesting();
|
||||
}
|
||||
|
||||
@ -982,25 +993,26 @@ TEST_F(AuthWithStorageWithVariousEncryptionAlgorithms, AddUserDefault) {
|
||||
}
|
||||
|
||||
TEST_F(AuthWithStorageWithVariousEncryptionAlgorithms, AddUserSha256) {
|
||||
FLAGS_password_encryption_algorithm = "sha256";
|
||||
SetHashAlgorithm("sha256");
|
||||
auto user = auth.AddUser("Alice", "alice");
|
||||
ASSERT_TRUE(user);
|
||||
ASSERT_EQ(user->username(), "alice");
|
||||
}
|
||||
|
||||
TEST_F(AuthWithStorageWithVariousEncryptionAlgorithms, AddUserSha256_1024) {
|
||||
FLAGS_password_encryption_algorithm = "sha256-multiple";
|
||||
SetHashAlgorithm("sha256-multiple");
|
||||
auto user = auth.AddUser("Alice", "alice");
|
||||
ASSERT_TRUE(user);
|
||||
ASSERT_EQ(user->username(), "alice");
|
||||
}
|
||||
|
||||
TEST_F(AuthWithStorageWithVariousEncryptionAlgorithms, AddUserThrow) {
|
||||
FLAGS_password_encryption_algorithm = "abcd";
|
||||
ASSERT_THROW(auth.AddUser("Alice", "alice"), AuthException);
|
||||
}
|
||||
|
||||
TEST_F(AuthWithStorageWithVariousEncryptionAlgorithms, AddUserEmptyPasswordEncryptionThrow) {
|
||||
FLAGS_password_encryption_algorithm = "";
|
||||
ASSERT_THROW(auth.AddUser("Alice", "alice"), AuthException);
|
||||
TEST(Serialize, HashedPassword) {
|
||||
for (auto algo :
|
||||
{PasswordHashAlgorithm::BCRYPT, PasswordHashAlgorithm::SHA256, PasswordHashAlgorithm::SHA256_MULTIPLE}) {
|
||||
auto sut = HashPassword("password", algo);
|
||||
nlohmann::json j = sut;
|
||||
auto ret = j.get<HashedPassword>();
|
||||
ASSERT_EQ(sut, ret);
|
||||
ASSERT_TRUE(ret.VerifyPassword("password"));
|
||||
}
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ TEST_F(AuthQueryHandlerFixture, GivenAuthQueryHandlerWhenInitializedHaveNoUserna
|
||||
}
|
||||
|
||||
TEST_F(AuthQueryHandlerFixture, GivenUserWhenNoDeniesOrGrantsThenNothingIsReturned) {
|
||||
memgraph::auth::User user = memgraph::auth::User{user_name, "", perms};
|
||||
memgraph::auth::User user = memgraph::auth::User{user_name, std::nullopt, perms};
|
||||
auth->SaveUser(user);
|
||||
|
||||
{ ASSERT_EQ(auth_handler.GetUsernames().size(), 1); }
|
||||
@ -71,7 +71,7 @@ TEST_F(AuthQueryHandlerFixture, GivenUserWhenNoDeniesOrGrantsThenNothingIsReturn
|
||||
|
||||
TEST_F(AuthQueryHandlerFixture, GivenUserWhenAddedGrantPermissionThenItIsReturned) {
|
||||
perms.Grant(memgraph::auth::Permission::MATCH);
|
||||
memgraph::auth::User user = memgraph::auth::User{user_name, "", perms};
|
||||
memgraph::auth::User user = memgraph::auth::User{user_name, std::nullopt, perms};
|
||||
auth->SaveUser(user);
|
||||
|
||||
auto privileges = auth_handler.GetPrivileges(user_name);
|
||||
@ -92,7 +92,7 @@ TEST_F(AuthQueryHandlerFixture, GivenUserWhenAddedGrantPermissionThenItIsReturne
|
||||
|
||||
TEST_F(AuthQueryHandlerFixture, GivenUserWhenAddedDenyPermissionThenItIsReturned) {
|
||||
perms.Deny(memgraph::auth::Permission::MATCH);
|
||||
memgraph::auth::User user = memgraph::auth::User{user_name, "", perms};
|
||||
memgraph::auth::User user = memgraph::auth::User{user_name, std::nullopt, perms};
|
||||
auth->SaveUser(user);
|
||||
|
||||
auto privileges = auth_handler.GetPrivileges(user_name);
|
||||
@ -114,7 +114,7 @@ TEST_F(AuthQueryHandlerFixture, GivenUserWhenAddedDenyPermissionThenItIsReturned
|
||||
TEST_F(AuthQueryHandlerFixture, GivenUserWhenPrivilegeRevokedThenNothingIsReturned) {
|
||||
perms.Deny(memgraph::auth::Permission::MATCH);
|
||||
perms.Revoke(memgraph::auth::Permission::MATCH);
|
||||
memgraph::auth::User user = memgraph::auth::User{user_name, "", perms};
|
||||
memgraph::auth::User user = memgraph::auth::User{user_name, std::nullopt, perms};
|
||||
auth->SaveUser(user);
|
||||
|
||||
auto privileges = auth_handler.GetPrivileges(user_name);
|
||||
@ -180,7 +180,7 @@ TEST_F(AuthQueryHandlerFixture, GivenRoleWhenPrivilegeRevokedThenNothingIsReturn
|
||||
TEST_F(AuthQueryHandlerFixture, GivenUserWhenGrantedTwoPrivilegesThenBothAreReturned) {
|
||||
perms.Grant(memgraph::auth::Permission::MATCH);
|
||||
perms.Grant(memgraph::auth::Permission::CREATE);
|
||||
memgraph::auth::User user = memgraph::auth::User{user_name, "", perms};
|
||||
memgraph::auth::User user = memgraph::auth::User{user_name, std::nullopt, perms};
|
||||
auth->SaveUser(user);
|
||||
|
||||
auto privileges = auth_handler.GetPrivileges(user_name);
|
||||
@ -191,7 +191,7 @@ TEST_F(AuthQueryHandlerFixture, GivenUserAndRoleWhenOneGrantedAndOtherGrantedThe
|
||||
perms.Grant(memgraph::auth::Permission::MATCH);
|
||||
memgraph::auth::Role role = memgraph::auth::Role{"Mates_role", perms};
|
||||
auth->SaveRole(role);
|
||||
memgraph::auth::User user = memgraph::auth::User{user_name, "", perms};
|
||||
memgraph::auth::User user = memgraph::auth::User{user_name, std::nullopt, perms};
|
||||
user.SetRole(role);
|
||||
auth->SaveUser(user);
|
||||
|
||||
@ -215,7 +215,7 @@ TEST_F(AuthQueryHandlerFixture, GivenUserAndRoleWhenOneDeniedAndOtherDeniedThenB
|
||||
perms.Deny(memgraph::auth::Permission::MATCH);
|
||||
memgraph::auth::Role role = memgraph::auth::Role{"Mates_role", perms};
|
||||
auth->SaveRole(role);
|
||||
memgraph::auth::User user = memgraph::auth::User{user_name, "", perms};
|
||||
memgraph::auth::User user = memgraph::auth::User{user_name, std::nullopt, perms};
|
||||
user.SetRole(role);
|
||||
auth->SaveUser(user);
|
||||
|
||||
@ -245,7 +245,7 @@ TEST_F(AuthQueryHandlerFixture, GivenUserAndRoleWhenOneGrantedAndOtherDeniedThen
|
||||
user_perms.Grant(memgraph::auth::Permission::MATCH);
|
||||
memgraph::auth::User user = memgraph::auth::User{
|
||||
user_name,
|
||||
"",
|
||||
std::nullopt,
|
||||
user_perms,
|
||||
};
|
||||
user.SetRole(role);
|
||||
@ -275,7 +275,7 @@ TEST_F(AuthQueryHandlerFixture, GivenUserAndRoleWhenOneDeniedAndOtherGrantedThen
|
||||
|
||||
memgraph::auth::Permissions user_perms{};
|
||||
user_perms.Deny(memgraph::auth::Permission::MATCH);
|
||||
memgraph::auth::User user = memgraph::auth::User{user_name, "", user_perms};
|
||||
memgraph::auth::User user = memgraph::auth::User{user_name, std::nullopt, user_perms};
|
||||
user.SetRole(role);
|
||||
auth->SaveUser(user);
|
||||
|
||||
@ -305,7 +305,7 @@ TEST_F(AuthQueryHandlerFixture, GivenUserWhenGrantedPrivilegeOnLabelThenIsDispla
|
||||
memgraph::auth::FineGrainedAccessPermissions{},
|
||||
};
|
||||
|
||||
memgraph::auth::User user = memgraph::auth::User{user_name, "", perms, handler};
|
||||
memgraph::auth::User user = memgraph::auth::User{user_name, std::nullopt, perms, handler};
|
||||
auth->SaveUser(user);
|
||||
|
||||
auto privileges = auth_handler.GetPrivileges(user_name);
|
||||
@ -334,7 +334,7 @@ TEST_F(AuthQueryHandlerFixture, GivenUserWhenGrantedMultiplePrivilegesOnLabelThe
|
||||
memgraph::auth::FineGrainedAccessPermissions{},
|
||||
};
|
||||
|
||||
memgraph::auth::User user = memgraph::auth::User{user_name, "", perms, handler};
|
||||
memgraph::auth::User user = memgraph::auth::User{user_name, std::nullopt, perms, handler};
|
||||
auth->SaveUser(user);
|
||||
|
||||
auto privileges = auth_handler.GetPrivileges(user_name);
|
||||
@ -364,7 +364,7 @@ TEST_F(AuthQueryHandlerFixture, GivenUserWhenGrantedAllPrivilegesOnLabelThenTopO
|
||||
memgraph::auth::FineGrainedAccessPermissions{},
|
||||
};
|
||||
|
||||
memgraph::auth::User user = memgraph::auth::User{user_name, "", perms, handler};
|
||||
memgraph::auth::User user = memgraph::auth::User{user_name, std::nullopt, perms, handler};
|
||||
auth->SaveUser(user);
|
||||
|
||||
auto privileges = auth_handler.GetPrivileges(user_name);
|
||||
@ -392,7 +392,7 @@ TEST_F(AuthQueryHandlerFixture, GivenUserWhenGrantedGlobalPrivilegeOnLabelThenIs
|
||||
memgraph::auth::FineGrainedAccessPermissions{},
|
||||
};
|
||||
|
||||
memgraph::auth::User user = memgraph::auth::User{user_name, "", perms, handler};
|
||||
memgraph::auth::User user = memgraph::auth::User{user_name, std::nullopt, perms, handler};
|
||||
auth->SaveUser(user);
|
||||
|
||||
auto privileges = auth_handler.GetPrivileges(user_name);
|
||||
@ -421,7 +421,7 @@ TEST_F(AuthQueryHandlerFixture, GivenUserWhenGrantedGlobalMultiplePrivilegesOnLa
|
||||
memgraph::auth::FineGrainedAccessPermissions{},
|
||||
};
|
||||
|
||||
memgraph::auth::User user = memgraph::auth::User{user_name, "", perms, handler};
|
||||
memgraph::auth::User user = memgraph::auth::User{user_name, std::nullopt, perms, handler};
|
||||
auth->SaveUser(user);
|
||||
|
||||
auto privileges = auth_handler.GetPrivileges(user_name);
|
||||
@ -451,7 +451,7 @@ TEST_F(AuthQueryHandlerFixture, GivenUserWhenGrantedGlobalAllPrivilegesOnLabelTh
|
||||
memgraph::auth::FineGrainedAccessPermissions{},
|
||||
};
|
||||
|
||||
memgraph::auth::User user = memgraph::auth::User{user_name, "", perms, handler};
|
||||
memgraph::auth::User user = memgraph::auth::User{user_name, std::nullopt, perms, handler};
|
||||
auth->SaveUser(user);
|
||||
|
||||
auto privileges = auth_handler.GetPrivileges(user_name);
|
||||
@ -480,7 +480,7 @@ TEST_F(AuthQueryHandlerFixture, GivenUserWhenGrantedPrivilegeOnEdgeTypeThenIsDis
|
||||
memgraph::auth::FineGrainedAccessPermissions{read_permission},
|
||||
};
|
||||
|
||||
memgraph::auth::User user = memgraph::auth::User{user_name, "", perms, handler};
|
||||
memgraph::auth::User user = memgraph::auth::User{user_name, std::nullopt, perms, handler};
|
||||
auth->SaveUser(user);
|
||||
|
||||
auto privileges = auth_handler.GetPrivileges(user_name);
|
||||
@ -509,7 +509,7 @@ TEST_F(AuthQueryHandlerFixture, GivenUserWhenGrantedMultiplePrivilegesOnEdgeType
|
||||
memgraph::auth::FineGrainedAccessPermissions{read_permission},
|
||||
};
|
||||
|
||||
memgraph::auth::User user = memgraph::auth::User{user_name, "", perms, handler};
|
||||
memgraph::auth::User user = memgraph::auth::User{user_name, std::nullopt, perms, handler};
|
||||
auth->SaveUser(user);
|
||||
|
||||
auto privileges = auth_handler.GetPrivileges(user_name);
|
||||
@ -539,7 +539,7 @@ TEST_F(AuthQueryHandlerFixture, GivenUserWhenGrantedAllPrivilegesOnEdgeTypeThenT
|
||||
memgraph::auth::FineGrainedAccessPermissions{read_permission},
|
||||
};
|
||||
|
||||
memgraph::auth::User user = memgraph::auth::User{user_name, "", perms, handler};
|
||||
memgraph::auth::User user = memgraph::auth::User{user_name, std::nullopt, perms, handler};
|
||||
auth->SaveUser(user);
|
||||
|
||||
auto privileges = auth_handler.GetPrivileges(user_name);
|
||||
@ -567,7 +567,7 @@ TEST_F(AuthQueryHandlerFixture, GivenUserWhenGrantedGlobalPrivilegeOnEdgeTypeThe
|
||||
memgraph::auth::FineGrainedAccessPermissions{read_permission},
|
||||
};
|
||||
|
||||
memgraph::auth::User user = memgraph::auth::User{user_name, "", perms, handler};
|
||||
memgraph::auth::User user = memgraph::auth::User{user_name, std::nullopt, perms, handler};
|
||||
auth->SaveUser(user);
|
||||
|
||||
auto privileges = auth_handler.GetPrivileges(user_name);
|
||||
@ -597,7 +597,7 @@ TEST_F(AuthQueryHandlerFixture, GivenUserWhenGrantedGlobalMultiplePrivilegesOnEd
|
||||
|
||||
};
|
||||
|
||||
memgraph::auth::User user = memgraph::auth::User{user_name, "", perms, handler};
|
||||
memgraph::auth::User user = memgraph::auth::User{user_name, std::nullopt, perms, handler};
|
||||
auth->SaveUser(user);
|
||||
|
||||
auto privileges = auth_handler.GetPrivileges(user_name);
|
||||
@ -627,7 +627,7 @@ TEST_F(AuthQueryHandlerFixture, GivenUserWhenGrantedGlobalAllPrivilegesOnEdgeTyp
|
||||
memgraph::auth::FineGrainedAccessPermissions{read_permission},
|
||||
};
|
||||
|
||||
memgraph::auth::User user = memgraph::auth::User{user_name, "", perms, handler};
|
||||
memgraph::auth::User user = memgraph::auth::User{user_name, std::nullopt, perms, handler};
|
||||
auth->SaveUser(user);
|
||||
|
||||
auto privileges = auth_handler.GetPrivileges(user_name);
|
||||
@ -656,7 +656,7 @@ TEST_F(AuthQueryHandlerFixture, GivenUserWhenGrantedAndDeniedOnLabelThenNoPermis
|
||||
memgraph::auth::FineGrainedAccessPermissions{},
|
||||
};
|
||||
|
||||
memgraph::auth::User user = memgraph::auth::User{user_name, "", perms, handler};
|
||||
memgraph::auth::User user = memgraph::auth::User{user_name, std::nullopt, perms, handler};
|
||||
auth->SaveUser(user);
|
||||
|
||||
auto privileges = auth_handler.GetPrivileges(user_name);
|
||||
@ -685,7 +685,7 @@ TEST_F(AuthQueryHandlerFixture, GivenUserWhenGrantedAndDeniedOnEdgeTypeThenNoPer
|
||||
memgraph::auth::FineGrainedAccessPermissions{read_permission},
|
||||
};
|
||||
|
||||
memgraph::auth::User user = memgraph::auth::User{user_name, "", perms, handler};
|
||||
memgraph::auth::User user = memgraph::auth::User{user_name, std::nullopt, perms, handler};
|
||||
auth->SaveUser(user);
|
||||
|
||||
auto privileges = auth_handler.GetPrivileges(user_name);
|
||||
@ -714,7 +714,7 @@ TEST_F(AuthQueryHandlerFixture, GivenUserWhenGrantedReadAndDeniedUpdateThenOneIs
|
||||
memgraph::auth::FineGrainedAccessPermissions{read_permission},
|
||||
};
|
||||
|
||||
memgraph::auth::User user = memgraph::auth::User{user_name, "", perms, handler};
|
||||
memgraph::auth::User user = memgraph::auth::User{user_name, std::nullopt, perms, handler};
|
||||
auth->SaveUser(user);
|
||||
|
||||
auto privileges = auth_handler.GetPrivileges(user_name);
|
||||
|
Loading…
Reference in New Issue
Block a user