Extract AuthQuery handling to an interface class
Reviewers: mferencevic, llugovic Reviewed By: mferencevic Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D2623
This commit is contained in:
parent
5fefd9d82f
commit
c7f69fd861
315
src/memgraph.cpp
315
src/memgraph.cpp
@ -15,13 +15,14 @@
|
||||
#else
|
||||
#include "database/single_node/graph_db.hpp"
|
||||
#endif
|
||||
#include "glue/auth.hpp"
|
||||
#include "memgraph_init.hpp"
|
||||
#include "query/exceptions.hpp"
|
||||
#include "query/procedure/module.hpp"
|
||||
#include "telemetry/telemetry.hpp"
|
||||
#include "utils/file.hpp"
|
||||
#include "utils/flag_validation.hpp"
|
||||
|
||||
#include "query/procedure/module.hpp"
|
||||
#include "utils/string.hpp"
|
||||
|
||||
// General purpose flags.
|
||||
DEFINE_string(bolt_address, "0.0.0.0",
|
||||
@ -115,6 +116,312 @@ DEFINE_VALIDATED_string(
|
||||
using ServerT = communication::Server<BoltSession, SessionData>;
|
||||
using communication::ServerContext;
|
||||
|
||||
class AuthQueryHandler final : public query::AuthQueryHandler {
|
||||
auth::Auth *auth_;
|
||||
|
||||
public:
|
||||
explicit AuthQueryHandler(auth::Auth *auth) : auth_(auth) {}
|
||||
|
||||
bool CreateUser(const std::string &username,
|
||||
const std::optional<std::string> &password) override {
|
||||
try {
|
||||
std::lock_guard<std::mutex> lock(auth_->WithLock());
|
||||
return !!auth_->AddUser(username, password);
|
||||
} catch (const auth::AuthException &e) {
|
||||
throw query::QueryRuntimeException(e.what());
|
||||
}
|
||||
}
|
||||
|
||||
bool DropUser(const std::string &username) override {
|
||||
try {
|
||||
std::lock_guard<std::mutex> lock(auth_->WithLock());
|
||||
auto user = auth_->GetUser(username);
|
||||
if (!user) return false;
|
||||
return auth_->RemoveUser(username);
|
||||
} catch (const auth::AuthException &e) {
|
||||
throw query::QueryRuntimeException(e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void SetPassword(const std::string &username,
|
||||
const std::optional<std::string> &password) override {
|
||||
try {
|
||||
std::lock_guard<std::mutex> lock(auth_->WithLock());
|
||||
auto user = auth_->GetUser(username);
|
||||
if (!user) {
|
||||
throw query::QueryRuntimeException("User '{}' doesn't exist.",
|
||||
username);
|
||||
}
|
||||
user->UpdatePassword(password);
|
||||
auth_->SaveUser(*user);
|
||||
} catch (const auth::AuthException &e) {
|
||||
throw query::QueryRuntimeException(e.what());
|
||||
}
|
||||
}
|
||||
|
||||
bool CreateRole(const std::string &rolename) override {
|
||||
try {
|
||||
std::lock_guard<std::mutex> lock(auth_->WithLock());
|
||||
return !!auth_->AddRole(rolename);
|
||||
} catch (const auth::AuthException &e) {
|
||||
throw query::QueryRuntimeException(e.what());
|
||||
}
|
||||
}
|
||||
|
||||
bool DropRole(const std::string &rolename) override {
|
||||
try {
|
||||
std::lock_guard<std::mutex> lock(auth_->WithLock());
|
||||
auto role = auth_->GetRole(rolename);
|
||||
if (!role) return false;
|
||||
return auth_->RemoveRole(rolename);
|
||||
} catch (const auth::AuthException &e) {
|
||||
throw query::QueryRuntimeException(e.what());
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<query::TypedValue> GetUsernames() override {
|
||||
try {
|
||||
std::lock_guard<std::mutex> lock(auth_->WithLock());
|
||||
std::vector<query::TypedValue> usernames;
|
||||
const auto &users = auth_->AllUsers();
|
||||
usernames.reserve(users.size());
|
||||
for (const auto &user : users) {
|
||||
usernames.emplace_back(user.username());
|
||||
}
|
||||
return usernames;
|
||||
} catch (const auth::AuthException &e) {
|
||||
throw query::QueryRuntimeException(e.what());
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<query::TypedValue> GetRolenames() override {
|
||||
try {
|
||||
std::lock_guard<std::mutex> lock(auth_->WithLock());
|
||||
std::vector<query::TypedValue> rolenames;
|
||||
const auto &roles = auth_->AllRoles();
|
||||
rolenames.reserve(roles.size());
|
||||
for (const auto &role : roles) {
|
||||
rolenames.emplace_back(role.rolename());
|
||||
}
|
||||
return rolenames;
|
||||
} catch (const auth::AuthException &e) {
|
||||
throw query::QueryRuntimeException(e.what());
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<std::string> GetRolenameForUser(
|
||||
const std::string &username) override {
|
||||
try {
|
||||
std::lock_guard<std::mutex> lock(auth_->WithLock());
|
||||
auto user = auth_->GetUser(username);
|
||||
if (!user) {
|
||||
throw query::QueryRuntimeException("User '{}' doesn't exist .",
|
||||
username);
|
||||
}
|
||||
if (user->role()) return user->role()->rolename();
|
||||
return std::nullopt;
|
||||
} catch (const auth::AuthException &e) {
|
||||
throw query::QueryRuntimeException(e.what());
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<query::TypedValue> GetUsernamesForRole(
|
||||
const std::string &rolename) override {
|
||||
try {
|
||||
std::lock_guard<std::mutex> lock(auth_->WithLock());
|
||||
auto role = auth_->GetRole(rolename);
|
||||
if (!role) {
|
||||
throw query::QueryRuntimeException("Role '{}' doesn't exist.",
|
||||
rolename);
|
||||
}
|
||||
std::vector<query::TypedValue> usernames;
|
||||
const auto &users = auth_->AllUsersForRole(rolename);
|
||||
usernames.reserve(users.size());
|
||||
for (const auto &user : users) {
|
||||
usernames.emplace_back(user.username());
|
||||
}
|
||||
return usernames;
|
||||
} catch (const auth::AuthException &e) {
|
||||
throw query::QueryRuntimeException(e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void SetRole(const std::string &username,
|
||||
const std::string &rolename) override {
|
||||
try {
|
||||
std::lock_guard<std::mutex> lock(auth_->WithLock());
|
||||
auto user = auth_->GetUser(username);
|
||||
if (!user) {
|
||||
throw query::QueryRuntimeException("User '{}' doesn't exist .",
|
||||
username);
|
||||
}
|
||||
auto role = auth_->GetRole(rolename);
|
||||
if (!role) {
|
||||
throw query::QueryRuntimeException("Role '{}' doesn't exist .",
|
||||
rolename);
|
||||
}
|
||||
if (user->role()) {
|
||||
throw query::QueryRuntimeException(
|
||||
"User '{}' is already a member of role '{}'.", username,
|
||||
user->role()->rolename());
|
||||
}
|
||||
user->SetRole(*role);
|
||||
auth_->SaveUser(*user);
|
||||
} catch (const auth::AuthException &e) {
|
||||
throw query::QueryRuntimeException(e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void ClearRole(const std::string &username) override {
|
||||
try {
|
||||
std::lock_guard<std::mutex> lock(auth_->WithLock());
|
||||
auto user = auth_->GetUser(username);
|
||||
if (!user) {
|
||||
throw query::QueryRuntimeException("User '{}' doesn't exist .",
|
||||
username);
|
||||
}
|
||||
user->ClearRole();
|
||||
auth_->SaveUser(*user);
|
||||
} catch (const auth::AuthException &e) {
|
||||
throw query::QueryRuntimeException(e.what());
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::vector<query::TypedValue>> GetPrivileges(
|
||||
const std::string &user_or_role) override {
|
||||
try {
|
||||
std::lock_guard<std::mutex> lock(auth_->WithLock());
|
||||
std::vector<std::vector<query::TypedValue>> grants;
|
||||
auto user = auth_->GetUser(user_or_role);
|
||||
auto role = auth_->GetRole(user_or_role);
|
||||
if (!user && !role) {
|
||||
throw query::QueryRuntimeException("User or role '{}' doesn't exist.",
|
||||
user_or_role);
|
||||
}
|
||||
if (user) {
|
||||
const auto &permissions = user->GetPermissions();
|
||||
for (const auto &privilege : query::kPrivilegesAll) {
|
||||
auto permission = glue::PrivilegeToPermission(privilege);
|
||||
auto effective = permissions.Has(permission);
|
||||
if (permissions.Has(permission) != auth::PermissionLevel::NEUTRAL) {
|
||||
std::vector<std::string> description;
|
||||
auto user_level = user->permissions().Has(permission);
|
||||
if (user_level == auth::PermissionLevel::GRANT) {
|
||||
description.emplace_back("GRANTED TO USER");
|
||||
} else if (user_level == auth::PermissionLevel::DENY) {
|
||||
description.emplace_back("DENIED TO USER");
|
||||
}
|
||||
if (user->role()) {
|
||||
auto role_level = user->role()->permissions().Has(permission);
|
||||
if (role_level == auth::PermissionLevel::GRANT) {
|
||||
description.emplace_back("GRANTED TO ROLE");
|
||||
} else if (role_level == auth::PermissionLevel::DENY) {
|
||||
description.emplace_back("DENIED TO ROLE");
|
||||
}
|
||||
}
|
||||
grants.push_back(
|
||||
{query::TypedValue(auth::PermissionToString(permission)),
|
||||
query::TypedValue(auth::PermissionLevelToString(effective)),
|
||||
query::TypedValue(utils::Join(description, ", "))});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const auto &permissions = role->permissions();
|
||||
for (const auto &privilege : query::kPrivilegesAll) {
|
||||
auto permission = glue::PrivilegeToPermission(privilege);
|
||||
auto effective = permissions.Has(permission);
|
||||
if (effective != auth::PermissionLevel::NEUTRAL) {
|
||||
std::string description;
|
||||
if (effective == auth::PermissionLevel::GRANT) {
|
||||
description = "GRANTED TO ROLE";
|
||||
} else if (effective == auth::PermissionLevel::DENY) {
|
||||
description = "DENIED TO ROLE";
|
||||
}
|
||||
grants.push_back(
|
||||
{query::TypedValue(auth::PermissionToString(permission)),
|
||||
query::TypedValue(auth::PermissionLevelToString(effective)),
|
||||
query::TypedValue(description)});
|
||||
}
|
||||
}
|
||||
}
|
||||
return grants;
|
||||
} catch (const auth::AuthException &e) {
|
||||
throw query::QueryRuntimeException(e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void GrantPrivilege(
|
||||
const std::string &user_or_role,
|
||||
const std::vector<query::AuthQuery::Privilege> &privileges) override {
|
||||
EditPermissions(user_or_role, privileges,
|
||||
[](auto *permissions, const auto &permission) {
|
||||
// TODO (mferencevic): should we first check that the
|
||||
// privilege is granted/denied/revoked before
|
||||
// unconditionally granting/denying/revoking it?
|
||||
permissions->Grant(permission);
|
||||
});
|
||||
}
|
||||
|
||||
void DenyPrivilege(
|
||||
const std::string &user_or_role,
|
||||
const std::vector<query::AuthQuery::Privilege> &privileges) override {
|
||||
EditPermissions(user_or_role, privileges,
|
||||
[](auto *permissions, const auto &permission) {
|
||||
// TODO (mferencevic): should we first check that the
|
||||
// privilege is granted/denied/revoked before
|
||||
// unconditionally granting/denying/revoking it?
|
||||
permissions->Deny(permission);
|
||||
});
|
||||
}
|
||||
|
||||
void RevokePrivilege(
|
||||
const std::string &user_or_role,
|
||||
const std::vector<query::AuthQuery::Privilege> &privileges) override {
|
||||
EditPermissions(user_or_role, privileges,
|
||||
[](auto *permissions, const auto &permission) {
|
||||
// TODO (mferencevic): should we first check that the
|
||||
// privilege is granted/denied/revoked before
|
||||
// unconditionally granting/denying/revoking it?
|
||||
permissions->Revoke(permission);
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
template <class TEditFun>
|
||||
void EditPermissions(
|
||||
const std::string &user_or_role,
|
||||
const std::vector<query::AuthQuery::Privilege> &privileges,
|
||||
const TEditFun &edit_fun) {
|
||||
try {
|
||||
std::lock_guard<std::mutex> lock(auth_->WithLock());
|
||||
std::vector<auth::Permission> permissions;
|
||||
permissions.reserve(privileges.size());
|
||||
for (const auto &privilege : privileges) {
|
||||
permissions.push_back(glue::PrivilegeToPermission(privilege));
|
||||
}
|
||||
auto user = auth_->GetUser(user_or_role);
|
||||
auto role = auth_->GetRole(user_or_role);
|
||||
if (!user && !role) {
|
||||
throw query::QueryRuntimeException("User or role '{}' doesn't exist.",
|
||||
user_or_role);
|
||||
}
|
||||
if (user) {
|
||||
for (const auto &permission : permissions) {
|
||||
edit_fun(&user->permissions(), permission);
|
||||
}
|
||||
auth_->SaveUser(*user);
|
||||
} else {
|
||||
for (const auto &permission : permissions) {
|
||||
edit_fun(&role->permissions(), permission);
|
||||
}
|
||||
auth_->SaveRole(*role);
|
||||
}
|
||||
} catch (const auth::AuthException &e) {
|
||||
throw query::QueryRuntimeException(e.what());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void SingleNodeMain() {
|
||||
// All enterprise features should be constructed before the main database
|
||||
// storage. This will cause them to be destructed *after* the main database
|
||||
@ -201,8 +508,8 @@ void SingleNodeMain() {
|
||||
}
|
||||
}
|
||||
// Register modules END
|
||||
|
||||
interpreter_context.auth = &auth;
|
||||
AuthQueryHandler auth_handler(&auth);
|
||||
interpreter_context.auth = &auth_handler;
|
||||
|
||||
ServerContext context;
|
||||
std::string service_name = "Bolt";
|
||||
|
@ -4,8 +4,6 @@
|
||||
|
||||
#include <glog/logging.h>
|
||||
|
||||
#include "auth/auth.hpp"
|
||||
#include "glue/auth.hpp"
|
||||
#include "glue/communication.hpp"
|
||||
#ifndef MG_SINGLE_NODE_HA
|
||||
#include "query/dump.hpp"
|
||||
@ -164,7 +162,7 @@ TypedValue EvaluateOptionalExpression(Expression *expression,
|
||||
return expression ? expression->Accept(*eval) : TypedValue();
|
||||
}
|
||||
|
||||
Callback HandleAuthQuery(AuthQuery *auth_query, auth::Auth *auth,
|
||||
Callback HandleAuthQuery(AuthQuery *auth_query, AuthQueryHandler *auth,
|
||||
const Parameters ¶meters,
|
||||
DbAccessor *db_accessor) {
|
||||
// Empty frame for evaluation of password expression. This is OK since
|
||||
@ -183,7 +181,6 @@ Callback HandleAuthQuery(AuthQuery *auth_query, auth::Auth *auth,
|
||||
ExpressionEvaluator evaluator(&frame, symbol_table, evaluation_context,
|
||||
db_accessor, storage::View::OLD);
|
||||
|
||||
AuthQuery::Action action = auth_query->action_;
|
||||
std::string username = auth_query->user_;
|
||||
std::string rolename = auth_query->role_;
|
||||
std::string user_or_role = auth_query->user_or_role_;
|
||||
@ -196,173 +193,101 @@ Callback HandleAuthQuery(AuthQuery *auth_query, auth::Auth *auth,
|
||||
case AuthQuery::Action::CREATE_USER:
|
||||
callback.fn = [auth, username, password] {
|
||||
CHECK(password.IsString() || password.IsNull());
|
||||
|
||||
std::lock_guard<std::mutex> lock(auth->WithLock());
|
||||
auto user = auth->AddUser(
|
||||
username,
|
||||
password.IsString()
|
||||
? std::make_optional(std::string(password.ValueString()))
|
||||
: std::nullopt);
|
||||
if (!user) {
|
||||
throw QueryRuntimeException("User or role '{}' already exists.",
|
||||
username);
|
||||
if (!auth->CreateUser(username, password.IsString()
|
||||
? std::make_optional(std::string(
|
||||
password.ValueString()))
|
||||
: std::nullopt)) {
|
||||
throw QueryRuntimeException("User '{}' already exists.", username);
|
||||
}
|
||||
return std::vector<std::vector<TypedValue>>();
|
||||
};
|
||||
return callback;
|
||||
case AuthQuery::Action::DROP_USER:
|
||||
callback.fn = [auth, username] {
|
||||
std::lock_guard<std::mutex> lock(auth->WithLock());
|
||||
auto user = auth->GetUser(username);
|
||||
if (!user) {
|
||||
if (!auth->DropUser(username)) {
|
||||
throw QueryRuntimeException("User '{}' doesn't exist.", username);
|
||||
}
|
||||
if (!auth->RemoveUser(username)) {
|
||||
throw QueryRuntimeException("Couldn't remove user '{}'.", username);
|
||||
}
|
||||
return std::vector<std::vector<TypedValue>>();
|
||||
};
|
||||
return callback;
|
||||
case AuthQuery::Action::SET_PASSWORD:
|
||||
callback.fn = [auth, username, password] {
|
||||
CHECK(password.IsString() || password.IsNull());
|
||||
|
||||
std::lock_guard<std::mutex> lock(auth->WithLock());
|
||||
auto user = auth->GetUser(username);
|
||||
if (!user) {
|
||||
throw QueryRuntimeException("User '{}' doesn't exist.", username);
|
||||
}
|
||||
user->UpdatePassword(
|
||||
auth->SetPassword(
|
||||
username,
|
||||
password.IsString()
|
||||
? std::make_optional(std::string(password.ValueString()))
|
||||
: std::nullopt);
|
||||
auth->SaveUser(*user);
|
||||
return std::vector<std::vector<TypedValue>>();
|
||||
};
|
||||
return callback;
|
||||
case AuthQuery::Action::CREATE_ROLE:
|
||||
callback.fn = [auth, rolename] {
|
||||
std::lock_guard<std::mutex> lock(auth->WithLock());
|
||||
auto role = auth->AddRole(rolename);
|
||||
if (!role) {
|
||||
throw QueryRuntimeException("User or role '{}' already exists.",
|
||||
rolename);
|
||||
if (!auth->CreateRole(rolename)) {
|
||||
throw QueryRuntimeException("Role '{}' already exists.", rolename);
|
||||
}
|
||||
return std::vector<std::vector<TypedValue>>();
|
||||
};
|
||||
return callback;
|
||||
case AuthQuery::Action::DROP_ROLE:
|
||||
callback.fn = [auth, rolename] {
|
||||
std::lock_guard<std::mutex> lock(auth->WithLock());
|
||||
auto role = auth->GetRole(rolename);
|
||||
if (!role) {
|
||||
if (!auth->DropRole(rolename)) {
|
||||
throw QueryRuntimeException("Role '{}' doesn't exist.", rolename);
|
||||
}
|
||||
if (!auth->RemoveRole(rolename)) {
|
||||
throw QueryRuntimeException("Couldn't remove role '{}'.", rolename);
|
||||
}
|
||||
return std::vector<std::vector<TypedValue>>();
|
||||
};
|
||||
return callback;
|
||||
case AuthQuery::Action::SHOW_USERS:
|
||||
callback.header = {"user"};
|
||||
callback.fn = [auth] {
|
||||
std::lock_guard<std::mutex> lock(auth->WithLock());
|
||||
std::vector<std::vector<TypedValue>> users;
|
||||
for (const auto &user : auth->AllUsers()) {
|
||||
users.push_back({TypedValue(user.username())});
|
||||
std::vector<std::vector<TypedValue>> rows;
|
||||
auto usernames = auth->GetUsernames();
|
||||
rows.reserve(usernames.size());
|
||||
for (auto &&username : usernames) {
|
||||
rows.emplace_back(std::vector<TypedValue>{username});
|
||||
}
|
||||
return users;
|
||||
return rows;
|
||||
};
|
||||
return callback;
|
||||
case AuthQuery::Action::SHOW_ROLES:
|
||||
callback.header = {"role"};
|
||||
callback.fn = [auth] {
|
||||
std::lock_guard<std::mutex> lock(auth->WithLock());
|
||||
std::vector<std::vector<TypedValue>> roles;
|
||||
for (const auto &role : auth->AllRoles()) {
|
||||
roles.push_back({TypedValue(role.rolename())});
|
||||
std::vector<std::vector<TypedValue>> rows;
|
||||
auto rolenames = auth->GetRolenames();
|
||||
rows.reserve(rolenames.size());
|
||||
for (auto &&rolename : rolenames) {
|
||||
rows.emplace_back(std::vector<TypedValue>{rolename});
|
||||
}
|
||||
return roles;
|
||||
return rows;
|
||||
};
|
||||
return callback;
|
||||
case AuthQuery::Action::SET_ROLE:
|
||||
callback.fn = [auth, username, rolename] {
|
||||
std::lock_guard<std::mutex> lock(auth->WithLock());
|
||||
auto user = auth->GetUser(username);
|
||||
if (!user) {
|
||||
throw QueryRuntimeException("User '{}' doesn't exist .", username);
|
||||
}
|
||||
auto role = auth->GetRole(rolename);
|
||||
if (!role) {
|
||||
throw QueryRuntimeException("Role '{}' doesn't exist .", rolename);
|
||||
}
|
||||
if (user->role()) {
|
||||
throw QueryRuntimeException(
|
||||
"User '{}' is already a member of role '{}'.", username,
|
||||
user->role()->rolename());
|
||||
}
|
||||
user->SetRole(*role);
|
||||
auth->SaveUser(*user);
|
||||
auth->SetRole(username, rolename);
|
||||
return std::vector<std::vector<TypedValue>>();
|
||||
};
|
||||
return callback;
|
||||
case AuthQuery::Action::CLEAR_ROLE:
|
||||
callback.fn = [auth, username] {
|
||||
std::lock_guard<std::mutex> lock(auth->WithLock());
|
||||
auto user = auth->GetUser(username);
|
||||
if (!user) {
|
||||
throw QueryRuntimeException("User '{}' doesn't exist .", username);
|
||||
}
|
||||
user->ClearRole();
|
||||
auth->SaveUser(*user);
|
||||
auth->ClearRole(username);
|
||||
return std::vector<std::vector<TypedValue>>();
|
||||
};
|
||||
return callback;
|
||||
case AuthQuery::Action::GRANT_PRIVILEGE:
|
||||
callback.fn = [auth, user_or_role, privileges] {
|
||||
auth->GrantPrivilege(user_or_role, privileges);
|
||||
return std::vector<std::vector<TypedValue>>();
|
||||
};
|
||||
return callback;
|
||||
case AuthQuery::Action::DENY_PRIVILEGE:
|
||||
callback.fn = [auth, user_or_role, privileges] {
|
||||
auth->DenyPrivilege(user_or_role, privileges);
|
||||
return std::vector<std::vector<TypedValue>>();
|
||||
};
|
||||
return callback;
|
||||
case AuthQuery::Action::REVOKE_PRIVILEGE: {
|
||||
callback.fn = [auth, user_or_role, action, privileges] {
|
||||
std::lock_guard<std::mutex> lock(auth->WithLock());
|
||||
std::vector<auth::Permission> permissions;
|
||||
for (const auto &privilege : privileges) {
|
||||
permissions.push_back(glue::PrivilegeToPermission(privilege));
|
||||
}
|
||||
auto user = auth->GetUser(user_or_role);
|
||||
auto role = auth->GetRole(user_or_role);
|
||||
if (!user && !role) {
|
||||
throw QueryRuntimeException("User or role '{}' doesn't exist.",
|
||||
user_or_role);
|
||||
}
|
||||
if (user) {
|
||||
for (const auto &permission : permissions) {
|
||||
// TODO (mferencevic): should we first check that the privilege
|
||||
// is granted/denied/revoked before unconditionally
|
||||
// granting/denying/revoking it?
|
||||
if (action == AuthQuery::Action::GRANT_PRIVILEGE) {
|
||||
user->permissions().Grant(permission);
|
||||
} else if (action == AuthQuery::Action::DENY_PRIVILEGE) {
|
||||
user->permissions().Deny(permission);
|
||||
} else {
|
||||
user->permissions().Revoke(permission);
|
||||
}
|
||||
}
|
||||
auth->SaveUser(*user);
|
||||
} else {
|
||||
for (const auto &permission : permissions) {
|
||||
// TODO (mferencevic): should we first check that the privilege
|
||||
// is granted/denied/revoked before unconditionally
|
||||
// granting/denying/revoking it?
|
||||
if (action == AuthQuery::Action::GRANT_PRIVILEGE) {
|
||||
role->permissions().Grant(permission);
|
||||
} else if (action == AuthQuery::Action::DENY_PRIVILEGE) {
|
||||
role->permissions().Deny(permission);
|
||||
} else {
|
||||
role->permissions().Revoke(permission);
|
||||
}
|
||||
}
|
||||
auth->SaveRole(*role);
|
||||
}
|
||||
callback.fn = [auth, user_or_role, privileges] {
|
||||
auth->RevokePrivilege(user_or_role, privileges);
|
||||
return std::vector<std::vector<TypedValue>>();
|
||||
};
|
||||
return callback;
|
||||
@ -370,89 +295,27 @@ Callback HandleAuthQuery(AuthQuery *auth_query, auth::Auth *auth,
|
||||
case AuthQuery::Action::SHOW_PRIVILEGES:
|
||||
callback.header = {"privilege", "effective", "description"};
|
||||
callback.fn = [auth, user_or_role] {
|
||||
std::lock_guard<std::mutex> lock(auth->WithLock());
|
||||
std::vector<std::vector<TypedValue>> grants;
|
||||
auto user = auth->GetUser(user_or_role);
|
||||
auto role = auth->GetRole(user_or_role);
|
||||
if (!user && !role) {
|
||||
throw QueryRuntimeException("User or role '{}' doesn't exist.",
|
||||
user_or_role);
|
||||
}
|
||||
if (user) {
|
||||
const auto &permissions = user->GetPermissions();
|
||||
for (const auto &privilege : kPrivilegesAll) {
|
||||
auto permission = glue::PrivilegeToPermission(privilege);
|
||||
auto effective = permissions.Has(permission);
|
||||
if (permissions.Has(permission) != auth::PermissionLevel::NEUTRAL) {
|
||||
std::vector<std::string> description;
|
||||
auto user_level = user->permissions().Has(permission);
|
||||
if (user_level == auth::PermissionLevel::GRANT) {
|
||||
description.push_back("GRANTED TO USER");
|
||||
} else if (user_level == auth::PermissionLevel::DENY) {
|
||||
description.push_back("DENIED TO USER");
|
||||
}
|
||||
if (user->role()) {
|
||||
auto role_level = user->role()->permissions().Has(permission);
|
||||
if (role_level == auth::PermissionLevel::GRANT) {
|
||||
description.push_back("GRANTED TO ROLE");
|
||||
} else if (role_level == auth::PermissionLevel::DENY) {
|
||||
description.push_back("DENIED TO ROLE");
|
||||
}
|
||||
}
|
||||
grants.push_back(
|
||||
{TypedValue(auth::PermissionToString(permission)),
|
||||
TypedValue(auth::PermissionLevelToString(effective)),
|
||||
TypedValue(utils::Join(description, ", "))});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const auto &permissions = role->permissions();
|
||||
for (const auto &privilege : kPrivilegesAll) {
|
||||
auto permission = glue::PrivilegeToPermission(privilege);
|
||||
auto effective = permissions.Has(permission);
|
||||
if (effective != auth::PermissionLevel::NEUTRAL) {
|
||||
std::string description;
|
||||
if (effective == auth::PermissionLevel::GRANT) {
|
||||
description = "GRANTED TO ROLE";
|
||||
} else if (effective == auth::PermissionLevel::DENY) {
|
||||
description = "DENIED TO ROLE";
|
||||
}
|
||||
grants.push_back(
|
||||
{TypedValue(auth::PermissionToString(permission)),
|
||||
TypedValue(auth::PermissionLevelToString(effective)),
|
||||
TypedValue(description)});
|
||||
}
|
||||
}
|
||||
}
|
||||
return grants;
|
||||
return auth->GetPrivileges(user_or_role);
|
||||
};
|
||||
return callback;
|
||||
case AuthQuery::Action::SHOW_ROLE_FOR_USER:
|
||||
callback.header = {"role"};
|
||||
callback.fn = [auth, username] {
|
||||
std::lock_guard<std::mutex> lock(auth->WithLock());
|
||||
auto user = auth->GetUser(username);
|
||||
if (!user) {
|
||||
throw QueryRuntimeException("User '{}' doesn't exist .", username);
|
||||
}
|
||||
auto maybe_rolename = auth->GetRolenameForUser(username);
|
||||
return std::vector<std::vector<TypedValue>>{std::vector<TypedValue>{
|
||||
TypedValue(user->role() ? user->role()->rolename() : "null")}};
|
||||
TypedValue(maybe_rolename ? *maybe_rolename : "null")}};
|
||||
};
|
||||
return callback;
|
||||
case AuthQuery::Action::SHOW_USERS_FOR_ROLE:
|
||||
callback.header = {"users"};
|
||||
callback.fn = [auth, rolename] {
|
||||
std::lock_guard<std::mutex> lock(auth->WithLock());
|
||||
auto role = auth->GetRole(rolename);
|
||||
if (!role) {
|
||||
throw QueryRuntimeException("Role '{}' doesn't exist.", rolename);
|
||||
std::vector<std::vector<TypedValue>> rows;
|
||||
auto usernames = auth->GetUsernamesForRole(rolename);
|
||||
rows.reserve(usernames.size());
|
||||
for (auto &&username : usernames) {
|
||||
rows.emplace_back(std::vector<TypedValue>{username});
|
||||
}
|
||||
std::vector<std::vector<TypedValue>> users;
|
||||
for (const auto &user : auth->AllUsersForRole(rolename)) {
|
||||
users.emplace_back(
|
||||
std::vector<TypedValue>{TypedValue(user.username())});
|
||||
}
|
||||
return users;
|
||||
return rows;
|
||||
};
|
||||
return callback;
|
||||
default:
|
||||
|
@ -21,14 +21,81 @@
|
||||
DECLARE_bool(query_cost_planner);
|
||||
DECLARE_int32(query_plan_cache_ttl);
|
||||
|
||||
namespace auth {
|
||||
class Auth;
|
||||
} // namespace auth
|
||||
|
||||
namespace query {
|
||||
|
||||
static constexpr size_t kExecutionMemoryBlockSize = 1U * 1024U * 1024U;
|
||||
|
||||
class AuthQueryHandler {
|
||||
public:
|
||||
AuthQueryHandler() = default;
|
||||
virtual ~AuthQueryHandler() = default;
|
||||
|
||||
AuthQueryHandler(const AuthQueryHandler &) = delete;
|
||||
AuthQueryHandler(AuthQueryHandler &&) = delete;
|
||||
AuthQueryHandler &operator=(const AuthQueryHandler &) = delete;
|
||||
AuthQueryHandler &operator=(AuthQueryHandler &&) = delete;
|
||||
|
||||
/// Return false if the user already exists.
|
||||
/// @throw QueryRuntimeException if an error ocurred.
|
||||
virtual bool CreateUser(const std::string &username,
|
||||
const std::optional<std::string> &password) = 0;
|
||||
|
||||
/// Return false if the user does not exist.
|
||||
/// @throw QueryRuntimeException if an error ocurred.
|
||||
virtual bool DropUser(const std::string &username) = 0;
|
||||
|
||||
/// @throw QueryRuntimeException if an error ocurred.
|
||||
virtual void SetPassword(const std::string &username,
|
||||
const std::optional<std::string> &password) = 0;
|
||||
|
||||
/// Return false if the role already exists.
|
||||
/// @throw QueryRuntimeException if an error ocurred.
|
||||
virtual bool CreateRole(const std::string &rolename) = 0;
|
||||
|
||||
/// Return false if the role does not exist.
|
||||
/// @throw QueryRuntimeException if an error ocurred.
|
||||
virtual bool DropRole(const std::string &rolename) = 0;
|
||||
|
||||
/// @throw QueryRuntimeException if an error ocurred.
|
||||
virtual std::vector<TypedValue> GetUsernames() = 0;
|
||||
|
||||
/// @throw QueryRuntimeException if an error ocurred.
|
||||
virtual std::vector<TypedValue> GetRolenames() = 0;
|
||||
|
||||
/// @throw QueryRuntimeException if an error ocurred.
|
||||
virtual std::optional<std::string> GetRolenameForUser(
|
||||
const std::string &username) = 0;
|
||||
|
||||
/// @throw QueryRuntimeException if an error ocurred.
|
||||
virtual std::vector<TypedValue> GetUsernamesForRole(
|
||||
const std::string &rolename) = 0;
|
||||
|
||||
/// @throw QueryRuntimeException if an error ocurred.
|
||||
virtual void SetRole(const std::string &username,
|
||||
const std::string &rolename) = 0;
|
||||
|
||||
/// @throw QueryRuntimeException if an error ocurred.
|
||||
virtual void ClearRole(const std::string &username) = 0;
|
||||
|
||||
virtual std::vector<std::vector<TypedValue>> GetPrivileges(
|
||||
const std::string &user_or_role) = 0;
|
||||
|
||||
/// @throw QueryRuntimeException if an error ocurred.
|
||||
virtual void GrantPrivilege(
|
||||
const std::string &user_or_role,
|
||||
const std::vector<AuthQuery::Privilege> &privileges) = 0;
|
||||
|
||||
/// @throw QueryRuntimeException if an error ocurred.
|
||||
virtual void DenyPrivilege(
|
||||
const std::string &user_or_role,
|
||||
const std::vector<AuthQuery::Privilege> &privileges) = 0;
|
||||
|
||||
/// @throw QueryRuntimeException if an error ocurred.
|
||||
virtual void RevokePrivilege(
|
||||
const std::string &user_or_role,
|
||||
const std::vector<AuthQuery::Privilege> &privileges) = 0;
|
||||
};
|
||||
|
||||
enum class QueryHandlerResult { COMMIT, ABORT, NOTHING };
|
||||
|
||||
/**
|
||||
@ -144,7 +211,7 @@ struct InterpreterContext {
|
||||
// The default execution timeout is 3 minutes.
|
||||
double execution_timeout_sec{180.0};
|
||||
|
||||
auth::Auth *auth{nullptr};
|
||||
AuthQueryHandler *auth{nullptr};
|
||||
|
||||
utils::SkipList<QueryCacheEntry> ast_cache;
|
||||
utils::SkipList<PlanCacheEntry> plan_cache;
|
||||
|
Loading…
Reference in New Issue
Block a user