From 17cb59d75ab089b8d55f1ce716f40adde8d16275 Mon Sep 17 00:00:00 2001 From: niko4299 Date: Fri, 22 Jul 2022 11:19:56 +0200 Subject: [PATCH] Added filtering --- src/auth/models.cpp | 133 +++++++++----- src/auth/models.hpp | 50 ++++-- src/memgraph.cpp | 28 +-- src/query/fine_grained_access_checker.hpp | 5 +- src/query/interpreter.cpp | 40 ++--- src/query/interpreter.hpp | 6 +- src/storage/v2/vertex_accessor.cpp | 210 +++++++++++----------- src/storage/v2/vertex_accessor.hpp | 11 +- 8 files changed, 288 insertions(+), 195 deletions(-) diff --git a/src/auth/models.cpp b/src/auth/models.cpp index 0910c92da..7e2cca0e5 100644 --- a/src/auth/models.cpp +++ b/src/auth/models.cpp @@ -101,8 +101,6 @@ std::string PermissionLevelToString(PermissionLevel level) { } } -const std::string ASTERISK = "*"; - Permissions::Permissions(uint64_t grants, uint64_t denies) : grants_(grants & (~denies)), denies_(denies) {} PermissionLevel Permissions::Has(Permission permission) const { @@ -293,30 +291,64 @@ bool operator!=(const FineGrainedAccessPermissions &first, const FineGrainedAcce return !(first == second); } +FineGrainedAccessHandler::FineGrainedAccessHandler(const FineGrainedAccessPermissions &labelPermissions, + const FineGrainedAccessPermissions &edgeTypePermissions) + : label_permissions_(labelPermissions), edge_type_permissions_(edgeTypePermissions) {} + +const FineGrainedAccessPermissions &FineGrainedAccessHandler::label_permissions() const { return label_permissions_; } + +const FineGrainedAccessPermissions &FineGrainedAccessHandler::edge_type_permissions() const { + return edge_type_permissions_; +} + +nlohmann::json FineGrainedAccessHandler::Serialize() const { + nlohmann::json data = nlohmann::json::object(); + data["label_permissions"] = label_permissions_.Serialize(); + data["edge_type_permissions"] = edge_type_permissions_.Serialize(); + return data; +} + +FineGrainedAccessHandler FineGrainedAccessHandler::Deserialize(const nlohmann::json &data) { + if (!data.is_object()) { + throw AuthException("Couldn't load role data!"); + } + if (!data["fine_grained_access_handler"].is_object()) { + throw AuthException("Couldn't load FineGrainedAccessHandler data!"); + } + auto label_permissions = FineGrainedAccessPermissions::Deserialize(data["label_permissions"]); + auto edge_type_permissions = FineGrainedAccessPermissions::Deserialize(data["edge_type_permissions"]); + + return {label_permissions, edge_type_permissions}; +} + +bool operator==(const FineGrainedAccessHandler &first, const FineGrainedAccessHandler &second) { + return first.label_permissions_ == second.label_permissions_ && + first.edge_type_permissions_ == second.edge_type_permissions_; +} + +bool operator!=(const FineGrainedAccessHandler &first, const FineGrainedAccessHandler &second) { + return !(first == second); +} + Role::Role(const std::string &rolename) : rolename_(utils::ToLowerCase(rolename)) {} Role::Role(const std::string &rolename, const Permissions &permissions, - const FineGrainedAccessPermissions &fine_grained_access_permissions) + const FineGrainedAccessHandler &fine_grained_access_handler) : rolename_(utils::ToLowerCase(rolename)), permissions_(permissions), - fine_grained_access_permissions_(fine_grained_access_permissions) {} + fine_grained_access_handler_(fine_grained_access_handler) {} const std::string &Role::rolename() const { return rolename_; } const Permissions &Role::permissions() const { return permissions_; } Permissions &Role::permissions() { return permissions_; } -const FineGrainedAccessPermissions &Role::fine_grained_access_permissions() const { - return fine_grained_access_permissions_; -} -FineGrainedAccessPermissions &Role::fine_grained_access_permissions() { return fine_grained_access_permissions_; } - -const AccessPermissions &Role::edgeTypePermissions() const { return edgeTypePermissions_; } -AccessPermissions &Role::edgeTypePermissions() { return edgeTypePermissions_; } +const FineGrainedAccessHandler &Role::fine_grained_access_handler() const { return fine_grained_access_handler_; } +FineGrainedAccessHandler &Role::fine_grained_access_handler() { return fine_grained_access_handler_; } nlohmann::json Role::Serialize() const { nlohmann::json data = nlohmann::json::object(); data["rolename"] = rolename_; data["permissions"] = permissions_.Serialize(); - data["fine_grained_access_permissions"] = fine_grained_access_permissions_.Serialize(); + data["fine_grained_access_handler"] = fine_grained_access_handler_.Serialize(); return data; } @@ -325,28 +357,27 @@ Role Role::Deserialize(const nlohmann::json &data) { throw AuthException("Couldn't load role data!"); } if (!data["rolename"].is_string() || !data["permissions"].is_object() || - !data["fine_grained_access_permissions"].is_object()) { + !data["fine_grained_access_handler"].is_object()) { throw AuthException("Couldn't load role data!"); } auto permissions = Permissions::Deserialize(data["permissions"]); - auto fine_grained_access_permissions = - FineGrainedAccessPermissions::Deserialize(data["fine_grained_access_permissions"]); - return {data["rolename"], permissions, fine_grained_access_permissions}; + auto fine_grained_access_handler = FineGrainedAccessHandler::Deserialize(data["fine_grained_access_handler"]); + return {data["rolename"], permissions, fine_grained_access_handler}; } bool operator==(const Role &first, const Role &second) { return first.rolename_ == second.rolename_ && first.permissions_ == second.permissions_ && - first.edgeTypePermissions_ == second.edgeTypePermissions_; + first.fine_grained_access_handler_ == second.fine_grained_access_handler_; } User::User(const std::string &username) : username_(utils::ToLowerCase(username)) {} User::User(const std::string &username, const std::string &password_hash, const Permissions &permissions, - const FineGrainedAccessPermissions &fine_grained_access_permissions) + const FineGrainedAccessHandler &fine_grained_access_handler) : username_(utils::ToLowerCase(username)), password_hash_(password_hash), permissions_(permissions), - fine_grained_access_permissions_(fine_grained_access_permissions) {} + fine_grained_access_handler_(fine_grained_access_handler) {} bool User::CheckPassword(const std::string &password) { if (password_hash_.empty()) return true; @@ -395,38 +426,58 @@ Permissions User::GetPermissions() const { return permissions_; } -FineGrainedAccessPermissions User::GetFineGrainedAccessPermissions() const { +FineGrainedAccessPermissions User::GetFineGrainedAccessLabelPermissions() const { if (role_) { std::unordered_set resultGrants; - std::set_union(fine_grained_access_permissions_.grants().begin(), fine_grained_access_permissions_.grants().end(), - role_->fine_grained_access_permissions().grants().begin(), - role_->fine_grained_access_permissions().grants().end(), + std::set_union(fine_grained_access_handler_.label_permissions().grants().begin(), + fine_grained_access_handler_.label_permissions().grants().end(), + role_->fine_grained_access_handler().label_permissions().grants().begin(), + role_->fine_grained_access_handler().label_permissions().grants().end(), std::inserter(resultGrants, resultGrants.begin())); std::unordered_set resultDenies; - std::set_union(fine_grained_access_permissions_.denies().begin(), fine_grained_access_permissions_.denies().end(), - role_->fine_grained_access_permissions().denies().begin(), - role_->fine_grained_access_permissions().denies().end(), + std::set_union(fine_grained_access_handler_.label_permissions().denies().begin(), + fine_grained_access_handler_.label_permissions().denies().end(), + role_->fine_grained_access_handler().label_permissions().denies().begin(), + role_->fine_grained_access_handler().label_permissions().denies().end(), std::inserter(resultDenies, resultDenies.begin())); return {resultGrants, resultDenies}; } - return fine_grained_access_permissions_; + return fine_grained_access_handler_.label_permissions(); +} + +FineGrainedAccessPermissions User::GetFineGrainedAccessEdgeTypePermissions() const { + if (role_) { + std::unordered_set resultGrants; + + std::set_union(fine_grained_access_handler_.edge_type_permissions().grants().begin(), + fine_grained_access_handler_.edge_type_permissions().grants().end(), + role_->fine_grained_access_handler().edge_type_permissions().grants().begin(), + role_->fine_grained_access_handler().edge_type_permissions().grants().end(), + std::inserter(resultGrants, resultGrants.begin())); + + std::unordered_set resultDenies; + + std::set_union(fine_grained_access_handler_.edge_type_permissions().denies().begin(), + fine_grained_access_handler_.edge_type_permissions().denies().end(), + role_->fine_grained_access_handler().edge_type_permissions().denies().begin(), + role_->fine_grained_access_handler().edge_type_permissions().denies().end(), + std::inserter(resultDenies, resultDenies.begin())); + + return {resultGrants, resultDenies}; + } + return fine_grained_access_handler_.edge_type_permissions(); } const std::string &User::username() const { return username_; } const Permissions &User::permissions() const { return permissions_; } Permissions &User::permissions() { return permissions_; } -const FineGrainedAccessPermissions &User::fine_grained_access_permissions() const { - return fine_grained_access_permissions_; -} -FineGrainedAccessPermissions &User::fine_grained_access_permissions() { return fine_grained_access_permissions_; } - -const AccessPermissions &User::edgeTypePermissions() const { return edgeTypePermissions_; } -AccessPermissions &User::edgeTypePermissions() { return edgeTypePermissions_; } +const FineGrainedAccessHandler &User::fine_grained_access_handler() const { return fine_grained_access_handler_; } +FineGrainedAccessHandler &User::fine_grained_access_handler() { return fine_grained_access_handler_; } const Role *User::role() const { if (role_.has_value()) { @@ -440,7 +491,7 @@ nlohmann::json User::Serialize() const { data["username"] = username_; data["password_hash"] = password_hash_; data["permissions"] = permissions_.Serialize(); - data["fine_grained_access_permissions"] = fine_grained_access_permissions_.Serialize(); + data["fine_grained_access_handler"] = fine_grained_access_handler_.Serialize(); // The role shouldn't be serialized here, it is stored as a foreign key. return data; } @@ -449,19 +500,19 @@ 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()) { + if (!data["username"].is_string() || !data["password_hash"].is_string() || !data["permissions"].is_object() || + !data["fine_grained_access_handler"].is_object()) { throw AuthException("Couldn't load user data!"); } auto permissions = Permissions::Deserialize(data["permissions"]); - auto fine_grained_access_permissions = - FineGrainedAccessPermissions::Deserialize(data["fine_grained_access_permissions"]); - return {data["username"], data["password_hash"], permissions, fine_grained_access_permissions}; + auto fine_grained_access_handler = FineGrainedAccessHandler::Deserialize(data["fine_grained_access_handler"]); + return {data["username"], data["password_hash"], permissions, fine_grained_access_handler}; } bool operator==(const User &first, const User &second) { return first.username_ == second.username_ && first.password_hash_ == second.password_hash_ && - first.permissions_ == second.permissions_ && first.edgeTypePermissions_ == second.edgeTypePermissions_ && - first.role_ == second.role_; + first.permissions_ == second.permissions_ && first.role_ == second.role_ && + first.fine_grained_access_handler_ == second.fine_grained_access_handler_; } } // namespace memgraph::auth diff --git a/src/auth/models.hpp b/src/auth/models.hpp index 4079b64ee..6d8c19c3c 100644 --- a/src/auth/models.hpp +++ b/src/auth/models.hpp @@ -120,21 +120,43 @@ bool operator==(const FineGrainedAccessPermissions &first, const FineGrainedAcce bool operator!=(const FineGrainedAccessPermissions &first, const FineGrainedAccessPermissions &second); +class FineGrainedAccessHandler final { + public: + FineGrainedAccessHandler(const FineGrainedAccessPermissions &labelPermissions = {}, + const FineGrainedAccessPermissions &edgeTypePermissions = {}); + + const FineGrainedAccessPermissions &label_permissions() const; + FineGrainedAccessPermissions &label_permissions(); + + const FineGrainedAccessPermissions &edge_type_permissions() const; + FineGrainedAccessPermissions &edge_type_permissions(); + + nlohmann::json Serialize() const; + + /// @throw AuthException if unable to deserialize. + static FineGrainedAccessHandler Deserialize(const nlohmann::json &data); + + friend bool operator==(const FineGrainedAccessHandler &first, const FineGrainedAccessHandler &second); + + private: + FineGrainedAccessPermissions label_permissions_; + FineGrainedAccessPermissions edge_type_permissions_; +}; + +bool operator==(const FineGrainedAccessHandler &first, const FineGrainedAccessHandler &second); + class Role final { public: Role(const std::string &rolename); Role(const std::string &rolename, const Permissions &permissions, - const FineGrainedAccessPermissions &fine_grained_access_permissions); + const FineGrainedAccessHandler &fine_grained_access_handler); const std::string &rolename() const; const Permissions &permissions() const; Permissions &permissions(); - const FineGrainedAccessPermissions &fine_grained_access_permissions() const; - FineGrainedAccessPermissions &fine_grained_access_permissions(); - - const AccessPermissions &edgeTypePermissions() const; - AccessPermissions &edgeTypePermissions(); + const FineGrainedAccessHandler &fine_grained_access_handler() const; + FineGrainedAccessHandler &fine_grained_access_handler(); nlohmann::json Serialize() const; @@ -146,7 +168,7 @@ class Role final { private: std::string rolename_; Permissions permissions_; - FineGrainedAccessPermissions fine_grained_access_permissions_; + FineGrainedAccessHandler fine_grained_access_handler_; }; bool operator==(const Role &first, const Role &second); @@ -157,7 +179,7 @@ class User final { User(const std::string &username); User(const std::string &username, const std::string &password_hash, const Permissions &permissions, - const FineGrainedAccessPermissions &fine_grained_access_permissions); + const FineGrainedAccessHandler &fine_grained_access_handler); /// @throw AuthException if unable to verify the password. bool CheckPassword(const std::string &password); @@ -170,17 +192,15 @@ class User final { void ClearRole(); Permissions GetPermissions() const; - FineGrainedAccessPermissions GetFineGrainedAccessPermissions() const; + FineGrainedAccessPermissions GetFineGrainedAccessLabelPermissions() const; + FineGrainedAccessPermissions GetFineGrainedAccessEdgeTypePermissions() const; const std::string &username() const; const Permissions &permissions() const; Permissions &permissions(); - const FineGrainedAccessPermissions &fine_grained_access_permissions() const; - FineGrainedAccessPermissions &fine_grained_access_permissions(); - - const AccessPermissions &edgeTypePermissions() const; - AccessPermissions &edgeTypePermissions(); + const FineGrainedAccessHandler &fine_grained_access_handler() const; + FineGrainedAccessHandler &fine_grained_access_handler(); const Role *role() const; @@ -195,7 +215,7 @@ class User final { std::string username_; std::string password_hash_; Permissions permissions_; - FineGrainedAccessPermissions fine_grained_access_permissions_; + FineGrainedAccessHandler fine_grained_access_handler_; std::optional role_; }; diff --git a/src/memgraph.cpp b/src/memgraph.cpp index 86354c1f6..463c1f09c 100644 --- a/src/memgraph.cpp +++ b/src/memgraph.cpp @@ -506,7 +506,7 @@ class AuthQueryHandler final : public memgraph::query::AuthQueryHandler { if (first_user) { spdlog::info("{} is first created user. Granting all privileges.", username); - GrantPrivilege(username, memgraph::query::kPrivilegesAll, {"*"}); + GrantPrivilege(username, memgraph::query::kPrivilegesAll, {"*"}, {"*"}); } return user_added; @@ -771,8 +771,8 @@ class AuthQueryHandler final : public memgraph::query::AuthQueryHandler { void GrantPrivilege(const std::string &user_or_role, const std::vector &privileges, - const std::vector &labels) override { - EditPermissions(user_or_role, privileges, labels, [](auto *permissions, const auto &permission) { + const std::vector &labels, const std::vector &edgeTypes) override { + EditPermissions(user_or_role, privileges, labels, edgeTypes, [](auto *permissions, const auto &permission) { // TODO (mferencevic): should we first check that the // privilege is granted/denied/revoked before // unconditionally granting/denying/revoking it? @@ -782,8 +782,8 @@ class AuthQueryHandler final : public memgraph::query::AuthQueryHandler { void DenyPrivilege(const std::string &user_or_role, const std::vector &privileges, - const std::vector &labels) override { - EditPermissions(user_or_role, privileges, labels, [](auto *permissions, const auto &permission) { + const std::vector &labels, const std::vector &edgeTypes) override { + EditPermissions(user_or_role, privileges, labels, edgeTypes, [](auto *permissions, const auto &permission) { // TODO (mferencevic): should we first check that the // privilege is granted/denied/revoked before // unconditionally granting/denying/revoking it? @@ -793,8 +793,8 @@ class AuthQueryHandler final : public memgraph::query::AuthQueryHandler { void RevokePrivilege(const std::string &user_or_role, const std::vector &privileges, - const std::vector &labels) override { - EditPermissions(user_or_role, privileges, labels, [](auto *permissions, const auto &permission) { + const std::vector &labels, const std::vector &edgeTypes) override { + EditPermissions(user_or_role, privileges, labels, edgeTypes, [](auto *permissions, const auto &permission) { // TODO (mferencevic): should we first check that the // privilege is granted/denied/revoked before // unconditionally granting/denying/revoking it? @@ -806,7 +806,8 @@ class AuthQueryHandler final : public memgraph::query::AuthQueryHandler { template void EditPermissions(const std::string &user_or_role, const std::vector &privileges, - const std::vector &labels, const TEditFun &edit_fun) { + const std::vector &labels, const std::vector &edgeTypes, + const TEditFun &edit_fun) { if (!std::regex_match(user_or_role, name_regex_)) { throw memgraph::query::QueryRuntimeException("Invalid user or role name."); } @@ -827,15 +828,22 @@ class AuthQueryHandler final : public memgraph::query::AuthQueryHandler { edit_fun(&user->permissions(), permission); } for (const auto &label : labels) { - edit_fun(&user->fine_grained_access_permissions(), label); + edit_fun(&user->fine_grained_access_handler().label_permissions(), label); } + for (const auto &edgeType : edgeTypes) { + edit_fun(&user->fine_grained_access_handler().edge_type_permissions(), edgeType); + } + locked_auth->SaveUser(*user); } else { for (const auto &permission : permissions) { edit_fun(&role->permissions(), permission); } for (const auto &label : labels) { - edit_fun(&user->fine_grained_access_permissions(), label); + edit_fun(&user->fine_grained_access_handler().edge_type_permissions(), label); + } + for (const auto &edgeType : edgeTypes) { + edit_fun(&role->fine_grained_access_handler().edge_type_permissions(), edgeType); } locked_auth->SaveRole(*role); diff --git a/src/query/fine_grained_access_checker.hpp b/src/query/fine_grained_access_checker.hpp index cd508734a..cb9e85003 100644 --- a/src/query/fine_grained_access_checker.hpp +++ b/src/query/fine_grained_access_checker.hpp @@ -18,7 +18,8 @@ namespace memgraph::query { class FineGrainedAccessChecker { public: - virtual bool IsUserAuthorizedLabels(const std::vector &label, - memgraph::query::DbAccessor *dba) const = 0; + virtual bool IsUserAuthorizedLabels(const std::vector &labels) const = 0; + + virtual bool IsUserAuthorizedEdgeType(const memgraph::storage::EdgeTypeId &edgeType) const = 0; }; } // namespace memgraph::query diff --git a/src/query/interpreter.cpp b/src/query/interpreter.cpp index e0af31ff0..5cb8bec46 100644 --- a/src/query/interpreter.cpp +++ b/src/query/interpreter.cpp @@ -45,6 +45,8 @@ #include "query/stream/common.hpp" #include "query/trigger.hpp" #include "query/typed_value.hpp" +#include "storage/v2/edge.hpp" +#include "storage/v2/id_types.hpp" #include "storage/v2/property_value.hpp" #include "utils/algorithm.hpp" #include "utils/csv_parsing.hpp" @@ -263,19 +265,23 @@ class ReplQueryHandler final : public query::ReplicationQueryHandler { class FineGrainedAccessChecker final : public memgraph::query::FineGrainedAccessChecker { public: - explicit FineGrainedAccessChecker(memgraph::auth::User *user) : user_{user} {} + explicit FineGrainedAccessChecker(memgraph::auth::User *user, DbAccessor *dba) : user_{user}, dba_{dba} {} - bool IsUserAuthorizedLabels(const std::vector &labels, - memgraph::query::DbAccessor *dba) const final { - auto labelPermissions = user_->GetFineGrainedAccessPermissions(); - - return std::any_of(labels.begin(), labels.end(), [&labelPermissions, &dba](const auto label) { - return labelPermissions.Has(dba->LabelToName(label)) == memgraph::auth::PermissionLevel::GRANT; + bool IsUserAuthorizedLabels(const std::vector &labels) const final { + return std::any_of(labels.begin(), labels.end(), [this](const auto label) { + return user_->GetFineGrainedAccessLabelPermissions().Has(dba_->LabelToName(label)) == + memgraph::auth::PermissionLevel::GRANT; }); } + bool IsUserAuthorizedEdgeType(const memgraph::storage::EdgeTypeId &edgeType) const final { + return user_->GetFineGrainedAccessEdgeTypePermissions().Has(dba_->EdgeTypeToName(edgeType)) == + memgraph::auth::PermissionLevel::GRANT; + } + private: memgraph::auth::User *user_; + DbAccessor *dba_; }; /// returns false if the replication role can't be set @@ -316,8 +322,7 @@ Callback HandleAuthQuery(AuthQuery *auth_query, AuthQueryHandler *auth, const Pa if (license_check_result.HasError() && enterprise_only_methods.contains(auth_query->action_)) { throw utils::BasicException( - utils::license::LicenseCheckErrorToString(license_check_result.GetError(), "advanced authentication - features")); + utils::license::LicenseCheckErrorToString(license_check_result.GetError(), "advanced authentication features")); } switch (auth_query->action_) { @@ -407,20 +412,20 @@ Callback HandleAuthQuery(AuthQuery *auth_query, AuthQueryHandler *auth, const Pa }; return callback; case AuthQuery::Action::GRANT_PRIVILEGE: - callback.fn = [auth, user_or_role, privileges, labels] { - auth->GrantPrivilege(user_or_role, privileges, labels); + callback.fn = [auth, user_or_role, privileges, labels, edgeTypes] { + auth->GrantPrivilege(user_or_role, privileges, labels, edgeTypes); return std::vector>(); }; return callback; case AuthQuery::Action::DENY_PRIVILEGE: - callback.fn = [auth, user_or_role, privileges, labels] { - auth->DenyPrivilege(user_or_role, privileges, labels); + callback.fn = [auth, user_or_role, privileges, labels, edgeTypes] { + auth->DenyPrivilege(user_or_role, privileges, labels, edgeTypes); return std::vector>(); }; return callback; case AuthQuery::Action::REVOKE_PRIVILEGE: { - callback.fn = [auth, user_or_role, privileges, labels] { - auth->RevokePrivilege(user_or_role, privileges, labels); + callback.fn = [auth, user_or_role, privileges, labels, edgeTypes] { + auth->RevokePrivilege(user_or_role, privileges, labels, edgeTypes); return std::vector>(); }; return callback; @@ -964,11 +969,7 @@ PullPlan::PullPlan(const std::shared_ptr plan, const Parameters &par #ifdef MG_ENTERPRISE if (username.has_value()) { memgraph::auth::User *user = interpreter_context->auth->GetUser(*username); -<<<<<<< HEAD - ctx_.access_checker = new AccessChecker{user}; -======= ctx_.fine_grained_access_checker = new FineGrainedAccessChecker{user}; ->>>>>>> E129-MG-implement-label-based-authorization } #endif if (interpreter_context->config.execution_timeout_sec > 0) { @@ -1305,7 +1306,6 @@ PreparedQuery PrepareProfileQuery(ParsedQuery parsed_query, bool in_explicit_tra auto optional_username = StringPointerToOptional(username); rw_type_checker.InferRWType(const_cast(cypher_query_plan->plan())); - auto optional_username = StringPointerToOptional(username); return PreparedQuery{{"OPERATOR", "ACTUAL HITS", "RELATIVE TIME", "ABSOLUTE TIME"}, std::move(parsed_query.required_privileges), diff --git a/src/query/interpreter.hpp b/src/query/interpreter.hpp index 155f5688a..9a6ba012f 100644 --- a/src/query/interpreter.hpp +++ b/src/query/interpreter.hpp @@ -104,15 +104,15 @@ class AuthQueryHandler { /// @throw QueryRuntimeException if an error ocurred. virtual void GrantPrivilege(const std::string &user_or_role, const std::vector &privileges, - const std::vector &labels) = 0; + const std::vector &labels, const std::vector &edgeTypes) = 0; /// @throw QueryRuntimeException if an error ocurred. virtual void DenyPrivilege(const std::string &user_or_role, const std::vector &privileges, - const std::vector &labels) = 0; + const std::vector &labels, const std::vector &edgeTypes) = 0; /// @throw QueryRuntimeException if an error ocurred. virtual void RevokePrivilege(const std::string &user_or_role, const std::vector &privileges, - const std::vector &labels) = 0; + const std::vector &labels, const std::vector &edgeTypes) = 0; }; enum class QueryHandlerResult { COMMIT, ABORT, NOTHING }; diff --git a/src/storage/v2/vertex_accessor.cpp b/src/storage/v2/vertex_accessor.cpp index e0bd9e18f..fa3d24964 100644 --- a/src/storage/v2/vertex_accessor.cpp +++ b/src/storage/v2/vertex_accessor.cpp @@ -13,6 +13,8 @@ #include +#include "query/context.hpp" +#include "query/fine_grained_access_checker.hpp" #include "storage/v2/edge_accessor.hpp" #include "storage/v2/id_types.hpp" #include "storage/v2/indices.hpp" @@ -339,8 +341,9 @@ Result> VertexAccessor::Properties(View view return std::move(properties); } -Result> VertexAccessor::InEdges(View view, AccessChecker *access_checker, - const VertexAccessor *destination) const { +Result> VertexAccessor::InEdges( + View view, const query::FineGrainedAccessChecker *fine_grained_access_checker, + const VertexAccessor *destination) const { MG_ASSERT(!destination || destination->transaction_ == transaction_, "Invalid accessor!"); bool exists = true; bool deleted = false; @@ -349,65 +352,68 @@ Result> VertexAccessor::InEdges(View view, AccessCheck { std::lock_guard guard(vertex_->lock); deleted = vertex_->deleted; - if (edge_types.empty() && !destination) { + if (!fine_grained_access_checker && !destination) { in_edges = vertex_->in_edges; } else { for (const auto &item : vertex_->in_edges) { const auto &[edge_type, from_vertex, edge] = item; if (destination && from_vertex != destination->vertex_) continue; - if (!edge_types.empty() && std::find(edge_types.begin(), edge_types.end(), edge_type) == edge_types.end()) + if (!fine_grained_access_checker && fine_grained_access_checker->IsUserAuthorizedEdgeType(edge_type) && + fine_grained_access_checker->IsUserAuthorizedLabels(from_vertex->labels)) continue; in_edges.push_back(item); } } delta = vertex_->delta; } - ApplyDeltasForRead( - transaction_, delta, view, [&exists, &deleted, &in_edges, &edge_types, &destination](const Delta &delta) { - switch (delta.action) { - case Delta::Action::ADD_IN_EDGE: { - if (destination && delta.vertex_edge.vertex != destination->vertex_) break; - if (!edge_types.empty() && - std::find(edge_types.begin(), edge_types.end(), delta.vertex_edge.edge_type) == edge_types.end()) - break; - // Add the edge because we don't see the removal. - std::tuple link{delta.vertex_edge.edge_type, delta.vertex_edge.vertex, - delta.vertex_edge.edge}; - auto it = std::find(in_edges.begin(), in_edges.end(), link); - MG_ASSERT(it == in_edges.end(), "Invalid database state!"); - in_edges.push_back(link); - break; - } - case Delta::Action::REMOVE_IN_EDGE: { - if (destination && delta.vertex_edge.vertex != destination->vertex_) break; - if (!edge_types.empty() && - std::find(edge_types.begin(), edge_types.end(), delta.vertex_edge.edge_type) == edge_types.end()) - break; - // Remove the label because we don't see the addition. - std::tuple link{delta.vertex_edge.edge_type, delta.vertex_edge.vertex, - delta.vertex_edge.edge}; - auto it = std::find(in_edges.begin(), in_edges.end(), link); - MG_ASSERT(it != in_edges.end(), "Invalid database state!"); - std::swap(*it, *in_edges.rbegin()); - in_edges.pop_back(); - break; - } - case Delta::Action::DELETE_OBJECT: { - exists = false; - break; - } - case Delta::Action::RECREATE_OBJECT: { - deleted = false; - break; - } - case Delta::Action::ADD_LABEL: - case Delta::Action::REMOVE_LABEL: - case Delta::Action::SET_PROPERTY: - case Delta::Action::ADD_OUT_EDGE: - case Delta::Action::REMOVE_OUT_EDGE: - break; - } - }); + ApplyDeltasForRead(transaction_, delta, view, + [&exists, &deleted, &in_edges, &fine_grained_access_checker, &destination](const Delta &delta) { + switch (delta.action) { + case Delta::Action::ADD_IN_EDGE: { + if (destination && delta.vertex_edge.vertex != destination->vertex_) break; + if (!fine_grained_access_checker && + fine_grained_access_checker->IsUserAuthorizedEdgeType(delta.vertex_edge.edge_type) && + fine_grained_access_checker->IsUserAuthorizedLabels(delta.vertex_edge.vertex->labels)) + break; + // Add the edge because we don't see the removal. + std::tuple link{ + delta.vertex_edge.edge_type, delta.vertex_edge.vertex, delta.vertex_edge.edge}; + auto it = std::find(in_edges.begin(), in_edges.end(), link); + MG_ASSERT(it == in_edges.end(), "Invalid database state!"); + in_edges.push_back(link); + break; + } + case Delta::Action::REMOVE_IN_EDGE: { + if (destination && delta.vertex_edge.vertex != destination->vertex_) break; + if (!fine_grained_access_checker && + fine_grained_access_checker->IsUserAuthorizedEdgeType(delta.vertex_edge.edge_type) && + fine_grained_access_checker->IsUserAuthorizedLabels(delta.vertex_edge.vertex->labels)) + break; + // Remove the label because we don't see the addition. + std::tuple link{ + delta.vertex_edge.edge_type, delta.vertex_edge.vertex, delta.vertex_edge.edge}; + auto it = std::find(in_edges.begin(), in_edges.end(), link); + MG_ASSERT(it != in_edges.end(), "Invalid database state!"); + std::swap(*it, *in_edges.rbegin()); + in_edges.pop_back(); + break; + } + case Delta::Action::DELETE_OBJECT: { + exists = false; + break; + } + case Delta::Action::RECREATE_OBJECT: { + deleted = false; + break; + } + case Delta::Action::ADD_LABEL: + case Delta::Action::REMOVE_LABEL: + case Delta::Action::SET_PROPERTY: + case Delta::Action::ADD_OUT_EDGE: + case Delta::Action::REMOVE_OUT_EDGE: + break; + } + }); if (!exists) return Error::NONEXISTENT_OBJECT; if (deleted) return Error::DELETED_OBJECT; std::vector ret; @@ -419,8 +425,9 @@ Result> VertexAccessor::InEdges(View view, AccessCheck return std::move(ret); } -Result> VertexAccessor::OutEdges(View view, const std::vector &edge_types, - const VertexAccessor *destination) const { +Result> VertexAccessor::OutEdges( + View view, const query::FineGrainedAccessChecker *fine_grained_access_checker, + const VertexAccessor *destination) const { MG_ASSERT(!destination || destination->transaction_ == transaction_, "Invalid accessor!"); bool exists = true; bool deleted = false; @@ -429,65 +436,68 @@ Result> VertexAccessor::OutEdges(View view, const std: { std::lock_guard guard(vertex_->lock); deleted = vertex_->deleted; - if (edge_types.empty() && !destination) { + if (!fine_grained_access_checker && !destination) { out_edges = vertex_->out_edges; } else { for (const auto &item : vertex_->out_edges) { const auto &[edge_type, to_vertex, edge] = item; if (destination && to_vertex != destination->vertex_) continue; - if (!edge_types.empty() && std::find(edge_types.begin(), edge_types.end(), edge_type) == edge_types.end()) + if (!fine_grained_access_checker && fine_grained_access_checker->IsUserAuthorizedEdgeType(edge_type) && + fine_grained_access_checker->IsUserAuthorizedLabels(to_vertex->labels)) continue; out_edges.push_back(item); } } delta = vertex_->delta; } - ApplyDeltasForRead( - transaction_, delta, view, [&exists, &deleted, &out_edges, &edge_types, &destination](const Delta &delta) { - switch (delta.action) { - case Delta::Action::ADD_OUT_EDGE: { - if (destination && delta.vertex_edge.vertex != destination->vertex_) break; - if (!edge_types.empty() && - std::find(edge_types.begin(), edge_types.end(), delta.vertex_edge.edge_type) == edge_types.end()) - break; - // Add the edge because we don't see the removal. - std::tuple link{delta.vertex_edge.edge_type, delta.vertex_edge.vertex, - delta.vertex_edge.edge}; - auto it = std::find(out_edges.begin(), out_edges.end(), link); - MG_ASSERT(it == out_edges.end(), "Invalid database state!"); - out_edges.push_back(link); - break; - } - case Delta::Action::REMOVE_OUT_EDGE: { - if (destination && delta.vertex_edge.vertex != destination->vertex_) break; - if (!edge_types.empty() && - std::find(edge_types.begin(), edge_types.end(), delta.vertex_edge.edge_type) == edge_types.end()) - break; - // Remove the label because we don't see the addition. - std::tuple link{delta.vertex_edge.edge_type, delta.vertex_edge.vertex, - delta.vertex_edge.edge}; - auto it = std::find(out_edges.begin(), out_edges.end(), link); - MG_ASSERT(it != out_edges.end(), "Invalid database state!"); - std::swap(*it, *out_edges.rbegin()); - out_edges.pop_back(); - break; - } - case Delta::Action::DELETE_OBJECT: { - exists = false; - break; - } - case Delta::Action::RECREATE_OBJECT: { - deleted = false; - break; - } - case Delta::Action::ADD_LABEL: - case Delta::Action::REMOVE_LABEL: - case Delta::Action::SET_PROPERTY: - case Delta::Action::ADD_IN_EDGE: - case Delta::Action::REMOVE_IN_EDGE: - break; - } - }); + ApplyDeltasForRead(transaction_, delta, view, + [&exists, &deleted, &out_edges, &fine_grained_access_checker, &destination](const Delta &delta) { + switch (delta.action) { + case Delta::Action::ADD_OUT_EDGE: { + if (destination && delta.vertex_edge.vertex != destination->vertex_) break; + if (!fine_grained_access_checker && + fine_grained_access_checker->IsUserAuthorizedEdgeType(delta.vertex_edge.edge_type) && + fine_grained_access_checker->IsUserAuthorizedLabels(delta.vertex_edge.vertex->labels)) + break; + // Add the edge because we don't see the removal. + std::tuple link{ + delta.vertex_edge.edge_type, delta.vertex_edge.vertex, delta.vertex_edge.edge}; + auto it = std::find(out_edges.begin(), out_edges.end(), link); + MG_ASSERT(it == out_edges.end(), "Invalid database state!"); + out_edges.push_back(link); + break; + } + case Delta::Action::REMOVE_OUT_EDGE: { + if (destination && delta.vertex_edge.vertex != destination->vertex_) break; + if (!fine_grained_access_checker && + fine_grained_access_checker->IsUserAuthorizedEdgeType(delta.vertex_edge.edge_type) && + fine_grained_access_checker->IsUserAuthorizedLabels(delta.vertex_edge.vertex->labels)) + break; + // Remove the label because we don't see the addition. + std::tuple link{ + delta.vertex_edge.edge_type, delta.vertex_edge.vertex, delta.vertex_edge.edge}; + auto it = std::find(out_edges.begin(), out_edges.end(), link); + MG_ASSERT(it != out_edges.end(), "Invalid database state!"); + std::swap(*it, *out_edges.rbegin()); + out_edges.pop_back(); + break; + } + case Delta::Action::DELETE_OBJECT: { + exists = false; + break; + } + case Delta::Action::RECREATE_OBJECT: { + deleted = false; + break; + } + case Delta::Action::ADD_LABEL: + case Delta::Action::REMOVE_LABEL: + case Delta::Action::SET_PROPERTY: + case Delta::Action::ADD_IN_EDGE: + case Delta::Action::REMOVE_IN_EDGE: + break; + } + }); if (!exists) return Error::NONEXISTENT_OBJECT; if (deleted) return Error::DELETED_OBJECT; std::vector ret; diff --git a/src/storage/v2/vertex_accessor.hpp b/src/storage/v2/vertex_accessor.hpp index 332f151df..c2a945cb7 100644 --- a/src/storage/v2/vertex_accessor.hpp +++ b/src/storage/v2/vertex_accessor.hpp @@ -13,6 +13,7 @@ #include +#include "query/fine_grained_access_checker.hpp" #include "storage/v2/vertex.hpp" #include "storage/v2/config.hpp" @@ -81,14 +82,16 @@ class VertexAccessor final { /// @throw std::bad_alloc /// @throw std::length_error if the resulting vector exceeds /// std::vector::max_size(). - Result> InEdges(View view, AccessChecker *access_checker = nullptr, - const VertexAccessor *destination = nullptr) const; + Result> InEdges( + View view, const query::FineGrainedAccessChecker *fine_grained_access_checker = nullptr, + const VertexAccessor *destination = nullptr) const; /// @throw std::bad_alloc /// @throw std::length_error if the resulting vector exceeds /// std::vector::max_size(). - Result> OutEdges(View view, const std::vector &edge_types = {}, - const VertexAccessor *destination = nullptr) const; + Result> OutEdges( + View view, const query::FineGrainedAccessChecker *fine_grained_access_checker = nullptr, + const VertexAccessor *destination = nullptr) const; Result InDegree(View view) const;