[E129-MG < T1007-MG] Expand Cursors with LBA checks (#524)

* [T1007-MG < T0997-MG] Authorization on paths (#501)

* Added read authorization in paths operators

* [T1007-MG < T1016-MG] Added authorization in create and delete operators (#513)

* Added authorization in RemoveNodeCursor, RemoveExpandCursor, CreateNodeCursor, CreateExpandCursor,MergeCursor

* [T1007-MG < T1014-MG] Add authorization to read operators (#520)

Added label based access control to read operators (ScanAll).

* [T1007-MG < T1015-MG] Add authorization to update operators (SetProperty, SetProperties, RemoveProperty) (#521)

Added label based authorization to update operators

Co-authored-by: niko4299 <51059248+niko4299@users.noreply.github.com>
Co-authored-by: Josip Mrden <josip.mrden@memgraph.io>
This commit is contained in:
Boris Taševski 2022-09-02 17:12:07 +02:00 committed by GitHub
parent 7478300762
commit d008a2ad8d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 4131 additions and 174 deletions

View File

@ -238,7 +238,7 @@ FineGrainedAccessPermissions::FineGrainedAccessPermissions(const std::unordered_
: permissions_(permissions), global_permission_(global_permission) {}
PermissionLevel FineGrainedAccessPermissions::Has(const std::string &permission,
const FineGrainedPermission label_permission) const {
const FineGrainedPermission fine_grained_permission) const {
const auto concrete_permission = std::invoke([&]() -> uint64_t {
if (permissions_.contains(permission)) {
return permissions_.at(permission);
@ -251,16 +251,17 @@ PermissionLevel FineGrainedAccessPermissions::Has(const std::string &permission,
return 0;
});
const auto temp_permission = concrete_permission & label_permission;
const auto temp_permission = concrete_permission & fine_grained_permission;
return temp_permission > 0 ? PermissionLevel::GRANT : PermissionLevel::DENY;
}
void FineGrainedAccessPermissions::Grant(const std::string &permission, const FineGrainedPermission label_permission) {
void FineGrainedAccessPermissions::Grant(const std::string &permission,
const FineGrainedPermission fine_grained_permission) {
if (permission == kAsterisk) {
global_permission_ = CalculateGrant(label_permission);
global_permission_ = CalculateGrant(fine_grained_permission);
} else {
permissions_[permission] |= CalculateGrant(label_permission);
permissions_[permission] |= CalculateGrant(fine_grained_permission);
}
}
@ -273,11 +274,12 @@ void FineGrainedAccessPermissions::Revoke(const std::string &permission) {
}
}
void FineGrainedAccessPermissions::Deny(const std::string &permission, const FineGrainedPermission label_permission) {
void FineGrainedAccessPermissions::Deny(const std::string &permission,
const FineGrainedPermission fine_grained_permission) {
if (permission == kAsterisk) {
global_permission_ = CalculateDeny(label_permission);
global_permission_ = CalculateDeny(fine_grained_permission);
} else {
permissions_[permission] = CalculateDeny(label_permission);
permissions_[permission] = CalculateDeny(fine_grained_permission);
}
}
@ -309,27 +311,26 @@ const std::unordered_map<std::string, uint64_t> &FineGrainedAccessPermissions::G
}
const std::optional<uint64_t> &FineGrainedAccessPermissions::GetGlobalPermission() const { return global_permission_; };
uint64_t FineGrainedAccessPermissions::CalculateGrant(FineGrainedPermission label_permission) {
uint64_t FineGrainedAccessPermissions::CalculateGrant(FineGrainedPermission fine_grained_permission) {
uint64_t shift{1};
uint64_t result{0};
auto uint_label_permission = static_cast<uint64_t>(label_permission);
while (uint_label_permission > 0) {
result |= uint_label_permission;
uint_label_permission >>= shift;
auto uint_fine_grained_permission = static_cast<uint64_t>(fine_grained_permission);
while (uint_fine_grained_permission > 0) {
result |= uint_fine_grained_permission;
uint_fine_grained_permission >>= shift;
}
return result;
}
uint64_t FineGrainedAccessPermissions::CalculateDeny(FineGrainedPermission label_permission) {
uint64_t FineGrainedAccessPermissions::CalculateDeny(FineGrainedPermission fine_grained_permission) {
uint64_t shift{1};
uint64_t result{0};
auto uint_label_permission = static_cast<uint64_t>(label_permission);
auto uint_fine_grained_permission = static_cast<uint64_t>(fine_grained_permission);
while (uint_label_permission <= kLabelPermissionMax) {
result |= uint_label_permission;
uint_label_permission <<= shift;
while (uint_fine_grained_permission <= kLabelPermissionMax) {
result |= uint_fine_grained_permission;
uint_fine_grained_permission <<= shift;
}
return kLabelPermissionAll - result;

View File

@ -134,14 +134,13 @@ class FineGrainedAccessPermissions final {
FineGrainedAccessPermissions(FineGrainedAccessPermissions &&) = default;
FineGrainedAccessPermissions &operator=(FineGrainedAccessPermissions &&) = default;
~FineGrainedAccessPermissions() = default;
PermissionLevel Has(const std::string &permission, FineGrainedPermission fine_grained_permission) const;
PermissionLevel Has(const std::string &permission, FineGrainedPermission label_permission) const;
void Grant(const std::string &permission, FineGrainedPermission label_permission);
void Grant(const std::string &permission, FineGrainedPermission fine_grained_permission);
void Revoke(const std::string &permission);
void Deny(const std::string &permission, FineGrainedPermission label_permission);
void Deny(const std::string &permission, FineGrainedPermission fine_grained_permission);
nlohmann::json Serialize() const;
@ -155,8 +154,8 @@ class FineGrainedAccessPermissions final {
std::unordered_map<std::string, uint64_t> permissions_{};
std::optional<uint64_t> global_permission_;
static uint64_t CalculateGrant(FineGrainedPermission label_permission);
static uint64_t CalculateDeny(FineGrainedPermission label_permission);
static uint64_t CalculateGrant(FineGrainedPermission fine_grained_permission);
static uint64_t CalculateDeny(FineGrainedPermission fine_grained_permission);
};
bool operator==(const FineGrainedAccessPermissions &first, const FineGrainedAccessPermissions &second);

View File

@ -19,19 +19,21 @@
namespace {
bool IsUserAuthorizedLabels(const memgraph::auth::User &user, const memgraph::query::DbAccessor &dba,
const std::vector<memgraph::storage::LabelId> &labels) {
return std::all_of(labels.begin(), labels.end(), [&dba, &user](const auto label) {
return user.GetFineGrainedAccessLabelPermissions().Has(dba.LabelToName(label),
memgraph::auth::FineGrainedPermission::READ) ==
memgraph::auth::PermissionLevel::GRANT;
const std::vector<memgraph::storage::LabelId> &labels,
const memgraph::query::AuthQuery::FineGrainedPrivilege fine_grained_permission) {
return std::all_of(labels.begin(), labels.end(), [&dba, &user, fine_grained_permission](const auto &label) {
return user.GetFineGrainedAccessLabelPermissions().Has(
dba.LabelToName(label), memgraph::glue::FineGrainedPrivilegeToFineGrainedPermission(
fine_grained_permission)) == memgraph::auth::PermissionLevel::GRANT;
});
}
bool IsUserAuthorizedEdgeType(const memgraph::auth::User &user, const memgraph::query::DbAccessor &dba,
const memgraph::storage::EdgeTypeId &edgeType) {
return user.GetFineGrainedAccessEdgeTypePermissions().Has(dba.EdgeTypeToName(edgeType),
memgraph::auth::FineGrainedPermission::READ) ==
memgraph::auth::PermissionLevel::GRANT;
const memgraph::storage::EdgeTypeId &edgeType,
const memgraph::query::AuthQuery::FineGrainedPrivilege fine_grained_permission) {
return user.GetFineGrainedAccessEdgeTypePermissions().Has(
dba.EdgeTypeToName(edgeType), memgraph::glue::FineGrainedPrivilegeToFineGrainedPermission(
fine_grained_permission)) == memgraph::auth::PermissionLevel::GRANT;
}
} // namespace
@ -84,9 +86,10 @@ bool AuthChecker::IsUserAuthorized(const memgraph::auth::User &user,
FineGrainedAuthChecker::FineGrainedAuthChecker(auth::User user) : user_{std::move(user)} {};
bool FineGrainedAuthChecker::Accept(const memgraph::query::DbAccessor &dba,
const memgraph::query::VertexAccessor &vertex,
const memgraph::storage::View &view) const {
bool FineGrainedAuthChecker::Accept(
const memgraph::query::DbAccessor &dba, const memgraph::query::VertexAccessor &vertex,
const memgraph::storage::View view,
const memgraph::query::AuthQuery::FineGrainedPrivilege fine_grained_permission) const {
auto maybe_labels = vertex.Labels(view);
if (maybe_labels.HasError()) {
switch (maybe_labels.GetError()) {
@ -101,12 +104,25 @@ bool FineGrainedAuthChecker::Accept(const memgraph::query::DbAccessor &dba,
}
}
return IsUserAuthorizedLabels(user_, dba, *maybe_labels);
return IsUserAuthorizedLabels(user_, dba, *maybe_labels, fine_grained_permission);
}
bool FineGrainedAuthChecker::Accept(const memgraph::query::DbAccessor &dba,
const memgraph::query::EdgeAccessor &edge) const {
return IsUserAuthorizedEdgeType(user_, dba, edge.EdgeType());
bool FineGrainedAuthChecker::Accept(
const memgraph::query::DbAccessor &dba, const memgraph::query::EdgeAccessor &edge,
const memgraph::query::AuthQuery::FineGrainedPrivilege fine_grained_permission) const {
return IsUserAuthorizedEdgeType(user_, dba, edge.EdgeType(), fine_grained_permission);
}
bool FineGrainedAuthChecker::Accept(
const memgraph::query::DbAccessor &dba, const std::vector<memgraph::storage::LabelId> &labels,
const memgraph::query::AuthQuery::FineGrainedPrivilege fine_grained_permission) const {
return IsUserAuthorizedLabels(user_, dba, labels, fine_grained_permission);
}
bool FineGrainedAuthChecker::Accept(
const memgraph::query::DbAccessor &dba, const memgraph::storage::EdgeTypeId &edge_type,
const memgraph::query::AuthQuery::FineGrainedPrivilege fine_grained_permission) const {
return IsUserAuthorizedEdgeType(user_, dba, edge_type, fine_grained_permission);
}
} // namespace memgraph::glue

View File

@ -42,10 +42,17 @@ class FineGrainedAuthChecker : public query::FineGrainedAuthChecker {
public:
explicit FineGrainedAuthChecker(auth::User user);
virtual bool Accept(const memgraph::query::DbAccessor &dba, const query::VertexAccessor &vertex,
const memgraph::storage::View &view) const override;
bool Accept(const memgraph::query::DbAccessor &dba, const query::VertexAccessor &vertex, memgraph::storage::View view,
query::AuthQuery::FineGrainedPrivilege fine_grained_permission) const override;
virtual bool Accept(const memgraph::query::DbAccessor &dba, const query::EdgeAccessor &edge) const override;
bool Accept(const memgraph::query::DbAccessor &dba, const query::EdgeAccessor &edge,
query::AuthQuery::FineGrainedPrivilege fine_grained_permission) const override;
bool Accept(const memgraph::query::DbAccessor &dba, const std::vector<memgraph::storage::LabelId> &labels,
query::AuthQuery::FineGrainedPrivilege fine_grained_permission) const override;
bool Accept(const memgraph::query::DbAccessor &dba, const memgraph::storage::EdgeTypeId &edge_type,
query::AuthQuery::FineGrainedPrivilege fine_grained_permission) const override;
private:
auth::User user_;

View File

@ -13,6 +13,7 @@
#include "query/db_accessor.hpp"
#include "query/frontend/ast/ast.hpp"
#include "storage/v2/id_types.hpp"
namespace memgraph::query {
@ -34,22 +35,43 @@ class FineGrainedAuthChecker {
virtual ~FineGrainedAuthChecker() = default;
[[nodiscard]] virtual bool Accept(const memgraph::query::DbAccessor &dba, const query::VertexAccessor &vertex,
const memgraph::storage::View &view) const = 0;
memgraph::storage::View view,
query::AuthQuery::FineGrainedPrivilege fine_grained_permission) const = 0;
[[nodiscard]] virtual bool Accept(const memgraph::query::DbAccessor &dba, const query::EdgeAccessor &edge) const = 0;
[[nodiscard]] virtual bool Accept(const memgraph::query::DbAccessor &dba, const query::EdgeAccessor &edge,
query::AuthQuery::FineGrainedPrivilege fine_grained_permission) const = 0;
[[nodiscard]] virtual bool Accept(const memgraph::query::DbAccessor &dba,
const std::vector<memgraph::storage::LabelId> &labels,
query::AuthQuery::FineGrainedPrivilege fine_grained_permission) const = 0;
[[nodiscard]] virtual bool Accept(const memgraph::query::DbAccessor &dba,
const memgraph::storage::EdgeTypeId &edge_type,
query::AuthQuery::FineGrainedPrivilege fine_grained_permission) const = 0;
};
class AllowEverythingUserBasedAuthChecker final : public query::FineGrainedAuthChecker {
class AllowEverythingFineGrainedAuthChecker final : public query::FineGrainedAuthChecker {
public:
bool Accept(const memgraph::query::DbAccessor &dba, const VertexAccessor &vertex,
const memgraph::storage::View &view) const override {
bool Accept(const memgraph::query::DbAccessor &dba, const VertexAccessor &vertex, const memgraph::storage::View view,
const query::AuthQuery::FineGrainedPrivilege fine_grained_permission) const override {
return true;
}
bool Accept(const memgraph::query::DbAccessor &dba, const memgraph::query::EdgeAccessor &edge) const override {
bool Accept(const memgraph::query::DbAccessor &dba, const memgraph::query::EdgeAccessor &edge,
const query::AuthQuery::FineGrainedPrivilege fine_grained_permission) const override {
return true;
}
};
bool Accept(const memgraph::query::DbAccessor &dba, const std::vector<memgraph::storage::LabelId> &labels,
const query::AuthQuery::FineGrainedPrivilege fine_grained_permission) const override {
return true;
}
bool Accept(const memgraph::query::DbAccessor &dba, const memgraph::storage::EdgeTypeId &edge_type,
const query::AuthQuery::FineGrainedPrivilege fine_grained_permission) const override {
return true;
}
}; // namespace memgraph::query
class AllowEverythingAuthChecker final : public query::AuthChecker {
public:
@ -59,7 +81,7 @@ class AllowEverythingAuthChecker final : public query::AuthChecker {
}
std::unique_ptr<FineGrainedAuthChecker> GetFineGrainedAuthChecker(const std::string & /*username*/) const override {
return std::make_unique<AllowEverythingUserBasedAuthChecker>();
return std::make_unique<AllowEverythingFineGrainedAuthChecker>();
}
};

View File

@ -25,7 +25,9 @@
#include <cppitertools/chain.hpp>
#include <cppitertools/imap.hpp>
#include "spdlog/spdlog.h"
#include "query/auth_checker.hpp"
#include "query/context.hpp"
#include "query/db_accessor.hpp"
#include "query/exceptions.hpp"
@ -237,6 +239,13 @@ CreateNode::CreateNodeCursor::CreateNodeCursor(const CreateNode &self, utils::Me
bool CreateNode::CreateNodeCursor::Pull(Frame &frame, ExecutionContext &context) {
SCOPED_PROFILE_OP("CreateNode");
if (context.auth_checker &&
!context.auth_checker->Accept(*context.db_accessor, self_.node_info_.labels,
memgraph::query::AuthQuery::FineGrainedPrivilege::CREATE_DELETE)) {
spdlog::info("Vertex will not be created due to not having enough permission!");
return false;
}
if (input_cursor_->Pull(frame, context)) {
auto created_vertex = CreateLocalVertex(self_.node_info_, &frame, context);
if (context.trigger_context_collector) {
@ -321,6 +330,16 @@ bool CreateExpand::CreateExpandCursor::Pull(Frame &frame, ExecutionContext &cont
if (!input_cursor_->Pull(frame, context)) return false;
const auto fine_grained_permission = self_.existing_node_
? memgraph::query::AuthQuery::FineGrainedPrivilege::UPDATE
: memgraph::query::AuthQuery::FineGrainedPrivilege::CREATE_DELETE;
if (context.auth_checker &&
!(context.auth_checker->Accept(*context.db_accessor, self_.edge_info_.edge_type,
memgraph::query::AuthQuery::FineGrainedPrivilege::CREATE_DELETE) &&
context.auth_checker->Accept(*context.db_accessor, self_.node_info_.labels, fine_grained_permission))) {
spdlog::info("Edge will not be created due to not having enough permission!");
return false;
}
// get the origin vertex
TypedValue &vertex_value = frame[self_.input_symbol_];
ExpectType(self_.input_symbol_, vertex_value, TypedValue::Type::Vertex);
@ -406,11 +425,9 @@ class ScanAllCursor : public Cursor {
vertices_it_.emplace(vertices_.value().begin());
}
#ifdef MG_ENTERPRISE
if (context.auth_checker && !FindNextVertex(context)) {
return false;
}
#endif
frame[output_symbol_] = *vertices_it_.value();
++vertices_it_.value();
@ -419,7 +436,8 @@ class ScanAllCursor : public Cursor {
bool FindNextVertex(const ExecutionContext &context) {
while (vertices_it_.value() != vertices_.value().end()) {
if (context.auth_checker->Accept(*context.db_accessor, *vertices_it_.value(), memgraph::storage::View::OLD)) {
if (context.auth_checker->Accept(*context.db_accessor, *vertices_it_.value(), memgraph::storage::View::OLD,
memgraph::query::AuthQuery::FineGrainedPrivilege::READ)) {
return true;
}
++vertices_it_.value();
@ -701,8 +719,11 @@ bool Expand::ExpandCursor::Pull(Frame &frame, ExecutionContext &context) {
// attempt to get a value from the incoming edges
if (in_edges_ && *in_edges_it_ != in_edges_->end()) {
auto edge = *(*in_edges_it_)++;
if (context.auth_checker && (!context.auth_checker->Accept(*context.db_accessor, edge) ||
!context.auth_checker->Accept(*context.db_accessor, edge.From(), self_.view_))) {
if (context.auth_checker &&
!(context.auth_checker->Accept(*context.db_accessor, edge,
memgraph::query::AuthQuery::FineGrainedPrivilege::READ) &&
context.auth_checker->Accept(*context.db_accessor, edge.From(), self_.view_,
memgraph::query::AuthQuery::FineGrainedPrivilege::READ))) {
continue;
}
@ -718,8 +739,11 @@ bool Expand::ExpandCursor::Pull(Frame &frame, ExecutionContext &context) {
// we should do only one expansion for cycles, and it was
// already done in the block above
if (self_.common_.direction == EdgeAtom::Direction::BOTH && edge.IsCycle()) continue;
if (context.auth_checker && (!context.auth_checker->Accept(*context.db_accessor, edge) ||
!context.auth_checker->Accept(*context.db_accessor, edge.To(), self_.view_))) {
if (context.auth_checker &&
!(context.auth_checker->Accept(*context.db_accessor, edge,
memgraph::query::AuthQuery::FineGrainedPrivilege::READ) &&
context.auth_checker->Accept(*context.db_accessor, edge.To(), self_.view_,
memgraph::query::AuthQuery::FineGrainedPrivilege::READ))) {
continue;
}
@ -1044,17 +1068,24 @@ class ExpandVariableCursor : public Cursor {
// if we are here, we have a valid stack,
// get the edge, increase the relevant iterator
auto current_edge = *edges_it_.back()++;
// Check edge-uniqueness.
bool found_existing =
std::any_of(edges_on_frame.begin(), edges_on_frame.end(),
[&current_edge](const TypedValue &edge) { return current_edge.first == edge.ValueEdge(); });
if (found_existing) continue;
AppendEdge(current_edge.first, &edges_on_frame);
VertexAccessor current_vertex =
current_edge.second == EdgeAtom::Direction::IN ? current_edge.first.From() : current_edge.first.To();
if (context.auth_checker &&
!(context.auth_checker->Accept(*context.db_accessor, current_edge.first,
memgraph::query::AuthQuery::FineGrainedPrivilege::READ) &&
context.auth_checker->Accept(*context.db_accessor, current_vertex, storage::View::OLD,
memgraph::query::AuthQuery::FineGrainedPrivilege::READ))) {
continue;
}
AppendEdge(current_edge.first, &edges_on_frame);
if (!self_.common_.existing_node) {
frame[self_.common_.node_symbol] = current_vertex;
}
@ -1214,6 +1245,14 @@ class STShortestPathCursor : public query::plan::Cursor {
if (self_.common_.direction != EdgeAtom::Direction::IN) {
auto out_edges = UnwrapEdgesResult(vertex.OutEdges(storage::View::OLD, self_.common_.edge_types));
for (const auto &edge : out_edges) {
if (context.auth_checker &&
!(context.auth_checker->Accept(*context.db_accessor, edge,
memgraph::query::AuthQuery::FineGrainedPrivilege::READ) &&
context.auth_checker->Accept(*context.db_accessor, edge.To(), storage::View::OLD,
memgraph::query::AuthQuery::FineGrainedPrivilege::READ))) {
continue;
}
if (ShouldExpand(edge.To(), edge, frame, evaluator) && !Contains(in_edge, edge.To())) {
in_edge.emplace(edge.To(), edge);
if (Contains(out_edge, edge.To())) {
@ -1231,6 +1270,14 @@ class STShortestPathCursor : public query::plan::Cursor {
if (self_.common_.direction != EdgeAtom::Direction::OUT) {
auto in_edges = UnwrapEdgesResult(vertex.InEdges(storage::View::OLD, self_.common_.edge_types));
for (const auto &edge : in_edges) {
if (context.auth_checker &&
!(context.auth_checker->Accept(*context.db_accessor, edge,
memgraph::query::AuthQuery::FineGrainedPrivilege::READ) &&
context.auth_checker->Accept(*context.db_accessor, edge.From(), storage::View::OLD,
memgraph::query::AuthQuery::FineGrainedPrivilege::READ))) {
continue;
}
if (ShouldExpand(edge.From(), edge, frame, evaluator) && !Contains(in_edge, edge.From())) {
in_edge.emplace(edge.From(), edge);
if (Contains(out_edge, edge.From())) {
@ -1262,6 +1309,13 @@ class STShortestPathCursor : public query::plan::Cursor {
if (self_.common_.direction != EdgeAtom::Direction::OUT) {
auto out_edges = UnwrapEdgesResult(vertex.OutEdges(storage::View::OLD, self_.common_.edge_types));
for (const auto &edge : out_edges) {
if (context.auth_checker &&
!(context.auth_checker->Accept(*context.db_accessor, edge,
memgraph::query::AuthQuery::FineGrainedPrivilege::READ) &&
context.auth_checker->Accept(*context.db_accessor, edge.To(), storage::View::OLD,
memgraph::query::AuthQuery::FineGrainedPrivilege::READ))) {
continue;
}
if (ShouldExpand(vertex, edge, frame, evaluator) && !Contains(out_edge, edge.To())) {
out_edge.emplace(edge.To(), edge);
if (Contains(in_edge, edge.To())) {
@ -1279,6 +1333,13 @@ class STShortestPathCursor : public query::plan::Cursor {
if (self_.common_.direction != EdgeAtom::Direction::IN) {
auto in_edges = UnwrapEdgesResult(vertex.InEdges(storage::View::OLD, self_.common_.edge_types));
for (const auto &edge : in_edges) {
if (context.auth_checker &&
!(context.auth_checker->Accept(*context.db_accessor, edge,
memgraph::query::AuthQuery::FineGrainedPrivilege::READ) &&
context.auth_checker->Accept(*context.db_accessor, edge.From(), storage::View::OLD,
memgraph::query::AuthQuery::FineGrainedPrivilege::READ))) {
continue;
}
if (ShouldExpand(vertex, edge, frame, evaluator) && !Contains(out_edge, edge.From())) {
out_edge.emplace(edge.From(), edge);
if (Contains(in_edge, edge.From())) {
@ -1325,10 +1386,17 @@ class SingleSourceShortestPathCursor : public query::plan::Cursor {
// for the given (edge, vertex) pair checks if they satisfy the
// "where" condition. if so, places them in the to_visit_ structure.
auto expand_pair = [this, &evaluator, &frame](EdgeAccessor edge, VertexAccessor vertex) {
auto expand_pair = [this, &evaluator, &frame, &context](EdgeAccessor edge, VertexAccessor vertex) {
// if we already processed the given vertex it doesn't get expanded
if (processed_.find(vertex) != processed_.end()) return;
if (context.auth_checker &&
!(context.auth_checker->Accept(*context.db_accessor, vertex, storage::View::OLD,
memgraph::query::AuthQuery::FineGrainedPrivilege::READ) &&
context.auth_checker->Accept(*context.db_accessor, edge,
memgraph::query::AuthQuery::FineGrainedPrivilege::READ))) {
return;
}
frame[self_.filter_lambda_.inner_edge_symbol] = edge;
frame[self_.filter_lambda_.inner_node_symbol] = vertex;
@ -1481,9 +1549,18 @@ class ExpandWeightedShortestPathCursor : public query::plan::Cursor {
// For the given (edge, vertex, weight, depth) tuple checks if they
// satisfy the "where" condition. if so, places them in the priority
// queue.
auto expand_pair = [this, &evaluator, &frame, &create_state](const EdgeAccessor &edge, const VertexAccessor &vertex,
const TypedValue &total_weight, int64_t depth) {
auto expand_pair = [this, &evaluator, &frame, &create_state, &context](
const EdgeAccessor &edge, const VertexAccessor &vertex, const TypedValue &total_weight,
int64_t depth) {
auto *memory = evaluator.GetMemoryResource();
if (context.auth_checker &&
!(context.auth_checker->Accept(*context.db_accessor, vertex, storage::View::OLD,
memgraph::query::AuthQuery::FineGrainedPrivilege::READ) &&
context.auth_checker->Accept(*context.db_accessor, edge,
memgraph::query::AuthQuery::FineGrainedPrivilege::READ))) {
return;
}
if (self_.filter_lambda_.expression) {
frame[self_.filter_lambda_.inner_edge_symbol] = edge;
frame[self_.filter_lambda_.inner_node_symbol] = vertex;
@ -1949,7 +2026,18 @@ bool Delete::DeleteCursor::Pull(Frame &frame, ExecutionContext &context) {
for (TypedValue &expression_result : expression_results) {
if (MustAbort(context)) throw HintedAbortError();
if (expression_result.type() == TypedValue::Type::Edge) {
auto maybe_value = dba.RemoveEdge(&expression_result.ValueEdge());
auto &ea = expression_result.ValueEdge();
if (context.auth_checker &&
!(context.auth_checker->Accept(*context.db_accessor, ea,
query::AuthQuery::FineGrainedPrivilege::CREATE_DELETE) &&
context.auth_checker->Accept(*context.db_accessor, ea.To(), storage::View::NEW,
query::AuthQuery::FineGrainedPrivilege::UPDATE) &&
context.auth_checker->Accept(*context.db_accessor, ea.From(), storage::View::NEW,
query::AuthQuery::FineGrainedPrivilege::UPDATE))) {
spdlog::info("Edge will not be deleted due to not having enough permission!");
continue;
}
auto maybe_value = dba.RemoveEdge(&ea);
if (maybe_value.HasError()) {
switch (maybe_value.GetError()) {
case storage::Error::SERIALIZATION_ERROR:
@ -1974,6 +2062,12 @@ bool Delete::DeleteCursor::Pull(Frame &frame, ExecutionContext &context) {
switch (expression_result.type()) {
case TypedValue::Type::Vertex: {
auto &va = expression_result.ValueVertex();
if (context.auth_checker &&
!context.auth_checker->Accept(*context.db_accessor, va, storage::View::NEW,
query::AuthQuery::FineGrainedPrivilege::CREATE_DELETE)) {
spdlog::info("Vertex will not be deleted due to not having enough permission!");
break;
}
if (self_.detach_) {
auto res = dba.DetachRemoveVertex(&va);
if (res.HasError()) {
@ -2077,6 +2171,13 @@ bool SetProperty::SetPropertyCursor::Pull(Frame &frame, ExecutionContext &contex
switch (lhs.type()) {
case TypedValue::Type::Vertex: {
if (context.auth_checker &&
!context.auth_checker->Accept(*context.db_accessor, lhs.ValueVertex(), storage::View::NEW,
memgraph::query::AuthQuery::FineGrainedPrivilege::UPDATE)) {
spdlog::info("Vertex property will not be set due to not having enough permission.");
break;
}
auto old_value = PropsSetChecked(&lhs.ValueVertex(), self_.property_, rhs);
context.execution_stats[ExecutionStats::Key::UPDATED_PROPERTIES] += 1;
if (context.trigger_context_collector) {
@ -2087,6 +2188,13 @@ bool SetProperty::SetPropertyCursor::Pull(Frame &frame, ExecutionContext &contex
break;
}
case TypedValue::Type::Edge: {
if (context.auth_checker &&
!context.auth_checker->Accept(*context.db_accessor, lhs.ValueEdge(),
memgraph::query::AuthQuery::FineGrainedPrivilege::UPDATE)) {
spdlog::info("Edge property will not be set due to not having enough permission.");
break;
}
auto old_value = PropsSetChecked(&lhs.ValueEdge(), self_.property_, rhs);
context.execution_stats[ExecutionStats::Key::UPDATED_PROPERTIES] += 1;
if (context.trigger_context_collector) {
@ -2279,9 +2387,23 @@ bool SetProperties::SetPropertiesCursor::Pull(Frame &frame, ExecutionContext &co
switch (lhs.type()) {
case TypedValue::Type::Vertex:
if (context.auth_checker &&
!context.auth_checker->Accept(*context.db_accessor, lhs.ValueVertex(), storage::View::NEW,
memgraph::query::AuthQuery::FineGrainedPrivilege::UPDATE)) {
spdlog::info("Vertex properties will not be set due to not having enough permission.");
break;
}
SetPropertiesOnRecord(&lhs.ValueVertex(), rhs, self_.op_, &context);
break;
case TypedValue::Type::Edge:
if (context.auth_checker &&
!context.auth_checker->Accept(*context.db_accessor, lhs.ValueEdge(),
memgraph::query::AuthQuery::FineGrainedPrivilege::UPDATE)) {
spdlog::info("Edge properties will not be set due to not having enough permission!");
break;
}
SetPropertiesOnRecord(&lhs.ValueEdge(), rhs, self_.op_, &context);
break;
case TypedValue::Type::Null:
@ -2408,9 +2530,23 @@ bool RemoveProperty::RemovePropertyCursor::Pull(Frame &frame, ExecutionContext &
switch (lhs.type()) {
case TypedValue::Type::Vertex:
if (context.auth_checker &&
!context.auth_checker->Accept(*context.db_accessor, lhs.ValueVertex(), storage::View::NEW,
memgraph::query::AuthQuery::FineGrainedPrivilege::UPDATE)) {
spdlog::info("Vertex property will not be removed due to not having enough permission.");
break;
}
remove_prop(&lhs.ValueVertex());
break;
case TypedValue::Type::Edge:
if (context.auth_checker &&
!context.auth_checker->Accept(*context.db_accessor, lhs.ValueEdge(),
memgraph::query::AuthQuery::FineGrainedPrivilege::UPDATE)) {
spdlog::info("Edge property will not be removed due to not having enough permission.");
break;
}
remove_prop(&lhs.ValueEdge());
break;
case TypedValue::Type::Null:
@ -3203,9 +3339,7 @@ bool Merge::MergeCursor::Pull(Frame &frame, ExecutionContext &context) {
if (pull_input_) {
// if we have just now pulled from the input
// and failed to pull from merge_match, we should create
__attribute__((unused)) bool merge_create_pull_result = merge_create_cursor_->Pull(frame, context);
DMG_ASSERT(merge_create_pull_result, "MergeCreate must never fail");
return true;
return merge_create_cursor_->Pull(frame, context);
}
// We have exhausted merge_match_cursor_ after 1 or more successful
// Pulls. Attempt next input_cursor_ pull

View File

@ -24,6 +24,7 @@
#include "mg_procedure.h"
#include "module.hpp"
#include "query/frontend/ast/ast.hpp"
#include "query/procedure/cypher_types.hpp"
#include "query/procedure/mg_procedure_helpers.hpp"
#include "query/stream/common.hpp"
@ -2257,7 +2258,8 @@ void NextPermitted(mgp_vertices_iterator &it) {
}
while (it.current_it != it.vertices.end()) {
if (it.graph->ctx->auth_checker->Accept(*it.graph->ctx->db_accessor, *it.current_it, it.graph->view)) {
if (it.graph->ctx->auth_checker->Accept(*it.graph->ctx->db_accessor, *it.current_it, it.graph->view,
memgraph::query::AuthQuery::FineGrainedPrivilege::READ)) {
break;
}

View File

@ -3,4 +3,6 @@ function(copy_fine_grained_access_e2e_python_files FILE_NAME)
endfunction()
copy_fine_grained_access_e2e_python_files(common.py)
copy_fine_grained_access_e2e_python_files(create_delete_filtering_tests.py)
copy_fine_grained_access_e2e_python_files(edge_type_filtering_tests.py)
copy_fine_grained_access_e2e_python_files(path_filtering_tests.py)

View File

@ -8,9 +8,18 @@
# the Business Source License, use of this software will be governed
# by the Apache License, Version 2.0, included in the file
# licenses/APL.txt.
import mgclient
def reset_and_prepare(admin_cursor):
execute_and_fetch_all(admin_cursor, "REVOKE LABELS * FROM user;")
execute_and_fetch_all(admin_cursor, "REVOKE EDGE_TYPES * FROM user;")
execute_and_fetch_all(admin_cursor, "MATCH(n) DETACH DELETE n;")
execute_and_fetch_all(admin_cursor, "CREATE (n:test_delete {name: 'test1'});")
execute_and_fetch_all(admin_cursor, "CREATE (n:test_delete_1)-[r:edge_type_delete]->(m:test_delete_2);")
def execute_and_fetch_all(cursor, query):
cursor.execute(query)
return cursor.fetchall()

View File

@ -0,0 +1,455 @@
# Copyright 2022 Memgraph Ltd.
#
# Use of this software is governed by the Business Source License
# included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
# License, and you may not use this file except in compliance with the Business Source License.
#
# As of the Change Date specified in that file, in accordance with
# the Business Source License, use of this software will be governed
# by the Apache License, Version 2.0, included in the file
# licenses/APL.txt.
import common
import sys
import pytest
def test_create_node_all_labels_granted():
admin_connection = common.connect(username="admin", password="test")
user_connnection = common.connect(username="user", password="test")
common.reset_and_prepare(admin_connection.cursor())
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS * TO user;")
results = common.execute_and_fetch_all(user_connnection.cursor(), "CREATE (n:label1) RETURN n;")
assert len(results) == 1
def test_create_node_all_labels_denied():
admin_connection = common.connect(username="admin", password="test")
user_connnection = common.connect(username="user", password="test")
common.reset_and_prepare(admin_connection.cursor())
common.execute_and_fetch_all(admin_connection.cursor(), "DENY CREATE_DELETE ON LABELS * TO user;")
results = common.execute_and_fetch_all(user_connnection.cursor(), "CREATE (n:label1) RETURN n;")
assert len(results) == 0
def test_create_node_specific_label_granted():
admin_connection = common.connect(username="admin", password="test")
user_connnection = common.connect(username="user", password="test")
common.reset_and_prepare(admin_connection.cursor())
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS :label1 TO user;")
results = common.execute_and_fetch_all(user_connnection.cursor(), "CREATE (n:label1) RETURN n;")
assert len(results) == 1
def test_create_node_specific_label_denied():
admin_connection = common.connect(username="admin", password="test")
user_connnection = common.connect(username="user", password="test")
common.reset_and_prepare(admin_connection.cursor())
common.execute_and_fetch_all(admin_connection.cursor(), "DENY CREATE_DELETE ON LABELS :label1 TO user;")
results = common.execute_and_fetch_all(user_connnection.cursor(), "CREATE (n:label1) RETURN n;")
assert len(results) == 0
def test_delete_node_all_labels_granted():
admin_connection = common.connect(username="admin", password="test")
user_connnection = common.connect(username="user", password="test")
common.reset_and_prepare(admin_connection.cursor())
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS * TO user;")
common.execute_and_fetch_all(user_connnection.cursor(), "MATCH (n:test_delete) DELETE n;")
results = common.execute_and_fetch_all(user_connnection.cursor(), "MATCH (n:test_delete) RETURN n;")
assert len(results) == 0
def test_delete_node_all_labels_denied():
admin_connection = common.connect(username="admin", password="test")
user_connnection = common.connect(username="user", password="test")
common.reset_and_prepare(admin_connection.cursor())
common.execute_and_fetch_all(admin_connection.cursor(), "DENY CREATE_DELETE ON LABELS * TO user;")
common.execute_and_fetch_all(user_connnection.cursor(), "MATCH (n:test_delete) DELETE n")
results = common.execute_and_fetch_all(admin_connection.cursor(), "MATCH (n:test_delete) RETURN n;")
assert len(results) == 1
def test_delete_node_specific_label_granted():
admin_connection = common.connect(username="admin", password="test")
user_connnection = common.connect(username="user", password="test")
common.reset_and_prepare(admin_connection.cursor())
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS :test_delete TO user;")
results = common.execute_and_fetch_all(user_connnection.cursor(), "MATCH (n:test_delete) DELETE n;")
results = common.execute_and_fetch_all(admin_connection.cursor(), "MATCH (n:test_delete) RETURN n;")
assert len(results) == 0
def test_delete_node_specific_label_denied():
admin_connection = common.connect(username="admin", password="test")
user_connnection = common.connect(username="user", password="test")
common.reset_and_prepare(admin_connection.cursor())
common.execute_and_fetch_all(admin_connection.cursor(), "DENY CREATE_DELETE ON LABELS :test_delete TO user;")
common.execute_and_fetch_all(user_connnection.cursor(), "MATCH (n:test_delete) DELETE n;")
results = common.execute_and_fetch_all(admin_connection.cursor(), "MATCH (n:test_delete) RETURN n;")
assert len(results) == 1
def test_create_edge_all_labels_all_edge_types_granted():
admin_connection = common.connect(username="admin", password="test")
user_connnection = common.connect(username="user", password="test")
common.reset_and_prepare(admin_connection.cursor())
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS * TO user;")
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON EDGE_TYPES * TO user;")
results = common.execute_and_fetch_all(
user_connnection.cursor(), "CREATE (n:label1)-[r:edge_type]->(m:label2) RETURN n,r,m;"
)
assert len(results) == 1
def test_create_edge_all_labels_all_edge_types_denied():
admin_connection = common.connect(username="admin", password="test")
user_connnection = common.connect(username="user", password="test")
common.reset_and_prepare(admin_connection.cursor())
common.execute_and_fetch_all(admin_connection.cursor(), "DENY CREATE_DELETE ON LABELS * TO user;")
common.execute_and_fetch_all(admin_connection.cursor(), "DENY CREATE_DELETE ON EDGE_TYPES * TO user;")
results = common.execute_and_fetch_all(
user_connnection.cursor(), "CREATE (n:label1)-[r:edge_type]->(m:label2) RETURN n,r,m;"
)
assert len(results) == 0
def test_create_edge_all_labels_denied_all_edge_types_granted():
admin_connection = common.connect(username="admin", password="test")
user_connnection = common.connect(username="user", password="test")
common.reset_and_prepare(admin_connection.cursor())
common.execute_and_fetch_all(admin_connection.cursor(), "DENY CREATE_DELETE ON LABELS * TO user;")
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON EDGE_TYPES * TO user;")
results = common.execute_and_fetch_all(
user_connnection.cursor(), "CREATE (n:label1)-[r:edge_type]->(m:label2) RETURN n,r,m;"
)
assert len(results) == 0
def test_create_edge_all_labels_granted_all_edge_types_denied():
admin_connection = common.connect(username="admin", password="test")
user_connnection = common.connect(username="user", password="test")
common.reset_and_prepare(admin_connection.cursor())
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS * TO user;")
common.execute_and_fetch_all(admin_connection.cursor(), "DENY CREATE_DELETE ON EDGE_TYPES * TO user;")
results = common.execute_and_fetch_all(
user_connnection.cursor(), "CREATE (n:label1)-[r:edge_type]->(m:label2) RETURN n,r,m;"
)
assert len(results) == 0
def test_create_edge_all_labels_granted_specific_edge_types_denied():
admin_connection = common.connect(username="admin", password="test")
user_connnection = common.connect(username="user", password="test")
common.reset_and_prepare(admin_connection.cursor())
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS * TO user;")
common.execute_and_fetch_all(admin_connection.cursor(), "DENY CREATE_DELETE ON EDGE_TYPES :edge_type TO user;")
results = common.execute_and_fetch_all(
user_connnection.cursor(), "CREATE (n:label1)-[r:edge_type]->(m:label2) RETURN n,r,m;"
)
assert len(results) == 0
def test_create_edge_first_node_label_granted():
admin_connection = common.connect(username="admin", password="test")
user_connnection = common.connect(username="user", password="test")
common.reset_and_prepare(admin_connection.cursor())
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS :label1 TO user;")
common.execute_and_fetch_all(admin_connection.cursor(), "DENY CREATE_DELETE ON LABELS :label2 TO user;")
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON EDGE_TYPES :edge_type TO user;")
results = common.execute_and_fetch_all(
user_connnection.cursor(), "CREATE (n:label1)-[r:edge_type]->(m:label2) RETURN n,r,m;"
)
assert len(results) == 0
def test_create_edge_second_node_label_granted():
admin_connection = common.connect(username="admin", password="test")
user_connnection = common.connect(username="user", password="test")
common.reset_and_prepare(admin_connection.cursor())
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS :label2 TO user;")
common.execute_and_fetch_all(admin_connection.cursor(), "DENY CREATE_DELETE ON LABELS :label1 TO user;")
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON EDGE_TYPES :edge_type TO user;")
results = common.execute_and_fetch_all(
user_connnection.cursor(), "CREATE (n:label1)-[r:edge_type]->(m:label2) RETURN n,r,m;"
)
assert len(results) == 0
def test_delete_edge_all_labels_denied_all_edge_types_granted():
admin_connection = common.connect(username="admin", password="test")
user_connnection = common.connect(username="user", password="test")
common.reset_and_prepare(admin_connection.cursor())
common.execute_and_fetch_all(admin_connection.cursor(), "DENY UPDATE ON LABELS * TO user;")
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON EDGE_TYPES * TO user;")
common.execute_and_fetch_all(
user_connnection.cursor(), "MATCH (n:test_delete_1)-[r:edge_type_delete]->(m:test_delete_2) DELETE r"
)
results = common.execute_and_fetch_all(
admin_connection.cursor(), "MATCH (n:test_delete_1)-[r:edge_type_delete]->(m:test_delete_2) RETURN n,r,m;"
)
assert len(results) == 1
def test_delete_edge_all_labels_granted_all_edge_types_denied():
admin_connection = common.connect(username="admin", password="test")
user_connnection = common.connect(username="user", password="test")
common.reset_and_prepare(admin_connection.cursor())
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS * TO user;")
common.execute_and_fetch_all(admin_connection.cursor(), "DENY CREATE_DELETE ON EDGE_TYPES * TO user;")
common.execute_and_fetch_all(
user_connnection.cursor(), "MATCH (n:test_delete_1)-[r:edge_type_delete]->(m:test_delete_2) DELETE r"
)
results = common.execute_and_fetch_all(
admin_connection.cursor(), "MATCH (n:test_delete_1)-[r:edge_type_delete]->(m:test_delete_2) RETURN n,r,m;"
)
assert len(results) == 1
def test_delete_edge_all_labels_granted_specific_edge_types_denied():
admin_connection = common.connect(username="admin", password="test")
user_connnection = common.connect(username="user", password="test")
common.reset_and_prepare(admin_connection.cursor())
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS * TO user;")
common.execute_and_fetch_all(
admin_connection.cursor(), "DENY CREATE_DELETE ON EDGE_TYPES :edge_type_delete TO user;"
)
common.execute_and_fetch_all(
user_connnection.cursor(), "MATCH (n:test_delete_1)-[r:edge_type_delete]->(m:test_delete_2) DELETE r"
)
results = common.execute_and_fetch_all(
admin_connection.cursor(), "MATCH (n:test_delete_1)-[r:edge_type_delete]->(m:test_delete_2) RETURN n,r,m;"
)
assert len(results) == 1
def test_delete_edge_first_node_label_granted():
admin_connection = common.connect(username="admin", password="test")
user_connnection = common.connect(username="user", password="test")
common.reset_and_prepare(admin_connection.cursor())
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT UPDATE ON LABELS :test_delete_1 TO user;")
common.execute_and_fetch_all(admin_connection.cursor(), "DENY UPDATE ON LABELS :test_delete_2 TO user;")
common.execute_and_fetch_all(
admin_connection.cursor(), "GRANT CREATE_DELETE ON EDGE_TYPES :edge_type_delete TO user;"
)
common.execute_and_fetch_all(
user_connnection.cursor(), "MATCH (n:test_delete_1)-[r:edge_type_delete]->(m:test_delete_2) DELETE r"
)
results = common.execute_and_fetch_all(
admin_connection.cursor(), "MATCH (n:test_delete_1)-[r:edge_type_delete]->(m:test_delete_2) RETURN n,r,m;"
)
assert len(results) == 1
def test_delete_edge_second_node_label_granted():
admin_connection = common.connect(username="admin", password="test")
user_connnection = common.connect(username="user", password="test")
common.reset_and_prepare(admin_connection.cursor())
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT UPDATE ON LABELS :test_delete_2 TO user;")
common.execute_and_fetch_all(admin_connection.cursor(), "DENY UPDATE ON LABELS :test_delete_1 TO user;")
common.execute_and_fetch_all(
admin_connection.cursor(), "GRANT CREATE_DELETE ON EDGE_TYPES :edge_type_delete TO user;"
)
common.execute_and_fetch_all(
user_connnection.cursor(), "MATCH (n:test_delete_1)-[r:edge_type_delete]->(m:test_delete_2) DELETE r"
)
results = common.execute_and_fetch_all(
admin_connection.cursor(), "MATCH (n:test_delete_1)-[r:edge_type_delete]->(m:test_delete_2) RETURN n,r,m;"
)
assert len(results) == 1
def test_delete_node_with_edge_label_denied():
admin_connection = common.connect(username="admin", password="test")
user_connnection = common.connect(username="user", password="test")
common.reset_and_prepare(admin_connection.cursor())
common.execute_and_fetch_all(admin_connection.cursor(), "DENY CREATE_DELETE ON LABELS :test_delete_1 TO user;")
common.execute_and_fetch_all(user_connnection.cursor(), "MATCH (n) DETACH DELETE n;")
results = common.execute_and_fetch_all(admin_connection.cursor(), "MATCH (n:test_delete_1) RETURN n;")
assert len(results) == 1
def test_delete_node_with_edge_label_granted():
admin_connection = common.connect(username="admin", password="test")
user_connnection = common.connect(username="user", password="test")
common.reset_and_prepare(admin_connection.cursor())
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS :test_delete_1 TO user;")
common.execute_and_fetch_all(user_connnection.cursor(), "MATCH (n) DETACH DELETE n;")
results = common.execute_and_fetch_all(admin_connection.cursor(), "MATCH (n:test_delete_1) RETURN n;")
assert len(results) == 0
def test_merge_node_all_labels_granted():
admin_connection = common.connect(username="admin", password="test")
user_connnection = common.connect(username="user", password="test")
common.reset_and_prepare(admin_connection.cursor())
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS * TO user;")
results = common.execute_and_fetch_all(user_connnection.cursor(), "MERGE (n:label1) RETURN n;")
assert len(results) == 1
def test_merge_node_all_labels_denied():
admin_connection = common.connect(username="admin", password="test")
user_connnection = common.connect(username="user", password="test")
common.reset_and_prepare(admin_connection.cursor())
common.execute_and_fetch_all(admin_connection.cursor(), "DENY CREATE_DELETE ON LABELS * TO user;")
results = common.execute_and_fetch_all(user_connnection.cursor(), "MERGE (n:label1) RETURN n;")
assert len(results) == 0
def test_merge_node_specific_label_granted():
admin_connection = common.connect(username="admin", password="test")
user_connnection = common.connect(username="user", password="test")
common.reset_and_prepare(admin_connection.cursor())
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS :label1 TO user;")
results = common.execute_and_fetch_all(user_connnection.cursor(), "MERGE (n:label1) RETURN n;")
assert len(results) == 1
def test_merge_node_specific_label_denied():
admin_connection = common.connect(username="admin", password="test")
user_connnection = common.connect(username="user", password="test")
common.reset_and_prepare(admin_connection.cursor())
common.execute_and_fetch_all(admin_connection.cursor(), "DENY CREATE_DELETE ON LABELS :label1 TO user;")
results = common.execute_and_fetch_all(user_connnection.cursor(), "MERGE (n:label1) RETURN n;")
assert len(results) == 0
def test_merge_edge_all_labels_all_edge_types_granted():
admin_connection = common.connect(username="admin", password="test")
user_connnection = common.connect(username="user", password="test")
common.reset_and_prepare(admin_connection.cursor())
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS * TO user;")
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON EDGE_TYPES * TO user;")
results = common.execute_and_fetch_all(
user_connnection.cursor(), "MERGE (n:label1)-[r:edge_type]->(m:label2) RETURN n,r,m;"
)
assert len(results) == 1
def test_merge_edge_all_labels_all_edge_types_denied():
admin_connection = common.connect(username="admin", password="test")
user_connnection = common.connect(username="user", password="test")
common.reset_and_prepare(admin_connection.cursor())
common.execute_and_fetch_all(admin_connection.cursor(), "DENY CREATE_DELETE ON LABELS * TO user;")
common.execute_and_fetch_all(admin_connection.cursor(), "DENY CREATE_DELETE ON EDGE_TYPES * TO user;")
results = common.execute_and_fetch_all(
user_connnection.cursor(), "MERGE (n:label1)-[r:edge_type]->(m:label2) RETURN n,r,m;"
)
assert len(results) == 0
def test_merge_edge_all_labels_denied_all_edge_types_granted():
admin_connection = common.connect(username="admin", password="test")
user_connnection = common.connect(username="user", password="test")
common.reset_and_prepare(admin_connection.cursor())
common.execute_and_fetch_all(admin_connection.cursor(), "DENY CREATE_DELETE ON LABELS * TO user;")
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON EDGE_TYPES * TO user;")
results = common.execute_and_fetch_all(
user_connnection.cursor(), "MERGE (n:label1)-[r:edge_type]->(m:label2) RETURN n,r,m;"
)
assert len(results) == 0
def test_merge_edge_all_labels_granted_all_edge_types_denied():
admin_connection = common.connect(username="admin", password="test")
user_connnection = common.connect(username="user", password="test")
common.reset_and_prepare(admin_connection.cursor())
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS * TO user;")
common.execute_and_fetch_all(admin_connection.cursor(), "DENY CREATE_DELETE ON EDGE_TYPES * TO user;")
results = common.execute_and_fetch_all(
user_connnection.cursor(), "MERGE (n:label1)-[r:edge_type]->(m:label2) RETURN n,r,m;"
)
assert len(results) == 0
def test_merge_edge_all_labels_granted_specific_edge_types_denied():
admin_connection = common.connect(username="admin", password="test")
user_connnection = common.connect(username="user", password="test")
common.reset_and_prepare(admin_connection.cursor())
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS * TO user;")
common.execute_and_fetch_all(admin_connection.cursor(), "DENY CREATE_DELETE ON EDGE_TYPES :edge_type TO user;")
results = common.execute_and_fetch_all(
user_connnection.cursor(), "MERGE (n:label1)-[r:edge_type]->(m:label2) RETURN n,r,m;"
)
assert len(results) == 0
def test_merge_edge_first_node_label_granted():
admin_connection = common.connect(username="admin", password="test")
user_connnection = common.connect(username="user", password="test")
common.reset_and_prepare(admin_connection.cursor())
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS :label1 TO user;")
common.execute_and_fetch_all(admin_connection.cursor(), "DENY CREATE_DELETE ON LABELS :label2 TO user;")
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON EDGE_TYPES :edge_type TO user;")
results = common.execute_and_fetch_all(
user_connnection.cursor(), "MERGE (n:label1)-[r:edge_type]->(m:label2) RETURN n,r,m;"
)
assert len(results) == 0
def test_merge_edge_second_node_label_granted():
admin_connection = common.connect(username="admin", password="test")
user_connnection = common.connect(username="user", password="test")
common.reset_and_prepare(admin_connection.cursor())
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON LABELS :label2 TO user;")
common.execute_and_fetch_all(admin_connection.cursor(), "DENY CREATE_DELETE ON LABELS :label1 TO user;")
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT CREATE_DELETE ON EDGE_TYPES :edge_type TO user;")
results = common.execute_and_fetch_all(
user_connnection.cursor(), "MERGE (n:label1)-[r:edge_type]->(m:label2) RETURN n,r,m;"
)
assert len(results) == 0
if __name__ == "__main__":
sys.exit(pytest.main([__file__, "-rA"]))

View File

@ -0,0 +1,529 @@
import common
import sys
import pytest
def test_weighted_shortest_path_all_edge_types_all_labels_granted():
admin_connection = common.connect(username="admin", password="test")
user_connnection = common.connect(username="user", password="test")
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON LABELS * TO user;")
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES * TO user;")
total_paths_results = common.execute_and_fetch_all(
user_connnection.cursor(),
"MATCH p=(n)-[r *wShortest (r, n | r.weight)]->(m) RETURN extract( node in nodes(p) | node.id);",
)
path_result = common.execute_and_fetch_all(
user_connnection.cursor(),
"MATCH p=(n:label0)-[r *wShortest (r, n | r.weight) path_length]->(m:label4) RETURN path_length,nodes(p);",
)
expected_path = [0, 1, 3, 4, 5]
expected_all_paths = [
[0, 1],
[0, 1, 2],
[0, 1, 3],
[0, 1, 3, 4],
[0, 1, 3, 4, 5],
[1, 2],
[1, 3],
[1, 3, 4],
[1, 3, 4, 5],
[2, 1],
[2, 3],
[2, 3, 4],
[2, 3, 4, 5],
[3, 4],
[3, 4, 5],
[4, 3],
[4, 5],
]
assert len(total_paths_results) == 16
assert all(path[0] in expected_all_paths for path in total_paths_results)
assert path_result[0][0] == 20
assert all(node.id in expected_path for node in path_result[0][1])
def test_weighted_shortest_path_all_edge_types_all_labels_denied():
admin_connection = common.connect(username="admin", password="test")
user_connnection = common.connect(username="user", password="test")
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
common.execute_and_fetch_all(admin_connection.cursor(), "DENY READ ON LABELS * TO user;")
common.execute_and_fetch_all(admin_connection.cursor(), "DENY READ ON EDGE_TYPES * TO user;")
results = common.execute_and_fetch_all(
user_connnection.cursor(), "MATCH p=(n)-[r *wShortest (r, n | r.weight)]->(m) RETURN p;"
)
assert len(results) == 0
def test_weighted_shortest_path_denied_start():
admin_connection = common.connect(username="admin", password="test")
user_connnection = common.connect(username="user", password="test")
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
common.execute_and_fetch_all(
admin_connection.cursor(), "GRANT READ ON LABELS :label1, :label2, :label3, :label4 TO user;"
)
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES * TO user;")
common.execute_and_fetch_all(admin_connection.cursor(), "DENY READ ON LABELS :label0 TO user;")
path_length_result = common.execute_and_fetch_all(
user_connnection.cursor(),
"MATCH p=(n:label0)-[r *wShortest (r, n | r.weight) path_length]->(m:label4) RETURN path_length;",
)
assert len(path_length_result) == 0
def test_weighted_shortest_path_denied_destination():
admin_connection = common.connect(username="admin", password="test")
user_connnection = common.connect(username="user", password="test")
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
common.execute_and_fetch_all(
admin_connection.cursor(), "GRANT READ ON LABELS :label0, :label1, :label2, :label3 TO user;"
)
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES * TO user;")
common.execute_and_fetch_all(admin_connection.cursor(), "DENY READ ON LABELS :label4 TO user;")
path_length_result = common.execute_and_fetch_all(
user_connnection.cursor(),
"MATCH p=(n:label0)-[r *wShortest (r, n | r.weight) path_length]->(m:label4) RETURN path_length;",
)
assert len(path_length_result) == 0
def test_weighted_shortest_path_denied_label_1():
admin_connection = common.connect(username="admin", password="test")
user_connnection = common.connect(username="user", password="test")
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
common.execute_and_fetch_all(
admin_connection.cursor(), "GRANT READ ON LABELS :label0, :label2, :label3, :label4 TO user;"
)
common.execute_and_fetch_all(admin_connection.cursor(), "DENY READ ON LABELS :label1 TO user;")
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES * TO user;")
total_paths_results = common.execute_and_fetch_all(
user_connnection.cursor(),
"MATCH p=(n)-[r *wShortest (r, n | r.weight)]->(m) RETURN extract( node in nodes(p) | node.id);",
)
path_result = common.execute_and_fetch_all(
user_connnection.cursor(),
"MATCH p=(n:label0)-[r *wShortest (r, n | r.weight) path_length]->(m:label4) RETURN path_length, nodes(p);",
)
expected_path = [0, 2, 3, 4, 5]
expected_all_paths = [
[0, 2],
[0, 2, 3],
[0, 2, 3, 4],
[0, 2, 3, 4, 5],
[2, 3],
[2, 3, 4],
[2, 3, 4, 5],
[3, 4],
[3, 4, 5],
[4, 3],
[4, 5],
]
assert len(total_paths_results) == 11
assert all(path[0] in expected_all_paths for path in total_paths_results)
assert path_result[0][0] == 30
assert all(node.id in expected_path for node in path_result[0][1])
def test_weighted_shortest_path_denied_edge_type_3():
admin_connection = common.connect(username="admin", password="test")
user_connnection = common.connect(username="user", password="test")
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON LABELS * TO user;")
common.execute_and_fetch_all(
admin_connection.cursor(), "GRANT READ ON EDGE_TYPES :edge_type_1, :edge_type_2, :edge_type_4 TO user;"
)
common.execute_and_fetch_all(admin_connection.cursor(), "DENY READ ON EDGE_TYPES :edge_type_3 TO user;")
path_result = common.execute_and_fetch_all(
user_connnection.cursor(),
"MATCH p=(n:label0)-[r *wShortest (r, n | r.weight) path_length]->(m:label4) RETURN path_length, nodes(p);",
)
total_paths_results = common.execute_and_fetch_all(
user_connnection.cursor(),
"MATCH p=(n)-[r *wShortest (r, n | r.weight)]->(m) RETURN extract( node in nodes(p) | node.id);",
)
expected_path = [0, 1, 2, 3, 5]
expected_all_paths = [
[0, 1],
[0, 1, 2],
[0, 1, 2, 4],
[0, 1, 2, 4, 3],
[0, 1, 2, 4, 5],
[1, 2, 4, 3],
[1, 2],
[1, 2, 4],
[1, 2, 4, 5],
[2, 1],
[2, 4, 3],
[2, 4],
[2, 4, 5],
[3, 4],
[3, 4, 5],
[4, 3],
[4, 5],
]
assert len(total_paths_results) == 16
assert all(path[0] in expected_all_paths for path in total_paths_results)
assert path_result[0][0] == 25
assert all(node.id in expected_path for node in path_result[0][1])
def test_dfs_all_edge_types_all_labels_granted():
admin_connection = common.connect(username="admin", password="test")
user_connnection = common.connect(username="user", password="test")
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON LABELS * TO user;")
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES * TO user;")
source_destination_paths = common.execute_and_fetch_all(
user_connnection.cursor(),
"MATCH path=(n:label0)-[* 1..3]->(m:label4) RETURN extract( node in nodes(path) | node.id);",
)
expected_paths = [[0, 1, 3, 5], [0, 2, 3, 5], [0, 2, 4, 5]]
assert len(source_destination_paths) == 3
assert all(path[0] in expected_paths for path in source_destination_paths)
def test_dfs_all_edge_types_all_labels_denied():
admin_connection = common.connect(username="admin", password="test")
user_connnection = common.connect(username="user", password="test")
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
common.execute_and_fetch_all(admin_connection.cursor(), "DENY READ ON LABELS * TO user;")
common.execute_and_fetch_all(admin_connection.cursor(), "DENY READ ON EDGE_TYPES * TO user;")
total_paths_results = common.execute_and_fetch_all(user_connnection.cursor(), "MATCH p=(n)-[*]->(m) RETURN p;")
assert len(total_paths_results) == 0
def test_dfs_denied_start():
admin_connection = common.connect(username="admin", password="test")
user_connnection = common.connect(username="user", password="test")
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
common.execute_and_fetch_all(
admin_connection.cursor(), "GRANT READ ON LABELS :label1, :label2, :label3, :label4 TO user;"
)
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES * TO user;")
common.execute_and_fetch_all(admin_connection.cursor(), "DENY READ ON LABELS :label0 TO user;")
source_destination_path = common.execute_and_fetch_all(
user_connnection.cursor(), "MATCH p=(n:label0)-[*]->(m:label4) RETURN p;"
)
assert len(source_destination_path) == 0
def test_dfs_denied_destination():
admin_connection = common.connect(username="admin", password="test")
user_connnection = common.connect(username="user", password="test")
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
common.execute_and_fetch_all(
admin_connection.cursor(), "GRANT READ ON LABELS :label0, :label1, :label2, :label3 TO user;"
)
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES * TO user;")
common.execute_and_fetch_all(admin_connection.cursor(), "DENY READ ON LABELS :label4 TO user;")
source_destination_path = common.execute_and_fetch_all(
user_connnection.cursor(), "MATCH p=(n:label0)-[*]->(m:label4) RETURN p;"
)
assert len(source_destination_path) == 0
def test_dfs_denied_label_1():
admin_connection = common.connect(username="admin", password="test")
user_connnection = common.connect(username="user", password="test")
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
common.execute_and_fetch_all(
admin_connection.cursor(), "GRANT READ ON LABELS :label0, :label2, :label3, :label4 TO user;"
)
common.execute_and_fetch_all(admin_connection.cursor(), "DENY READ ON LABELS :label1 TO user;")
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES * TO user;")
source_destination_paths = common.execute_and_fetch_all(
user_connnection.cursor(),
"MATCH p=(n:label0)-[* 1..3]->(m:label4) RETURN extract( node in nodes(p) | node.id);",
)
expected_paths = [[0, 2, 3, 5], [0, 2, 4, 5]]
assert len(source_destination_paths) == 2
assert all(path[0] in expected_paths for path in source_destination_paths)
def test_dfs_denied_edge_type_3():
admin_connection = common.connect(username="admin", password="test")
user_connnection = common.connect(username="user", password="test")
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON LABELS * TO user;")
common.execute_and_fetch_all(
admin_connection.cursor(), "GRANT READ ON EDGE_TYPES :edge_type_1, :edge_type_2, :edge_type_4 TO user;"
)
common.execute_and_fetch_all(admin_connection.cursor(), "DENY READ ON EDGE_TYPES :edge_type_3 TO user;")
source_destination_path = common.execute_and_fetch_all(
user_connnection.cursor(),
"MATCH p=(n:label0)-[r * 1..3]->(m:label4) RETURN extract( node in nodes(p) | node.id);",
)
expected_path = [0, 2, 4, 5]
assert len(source_destination_path) == 1
assert source_destination_path[0][0] == expected_path
def test_bfs_sts_all_edge_types_all_labels_granted():
admin_connection = common.connect(username="admin", password="test")
user_connnection = common.connect(username="user", password="test")
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON LABELS * TO user;")
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES * TO user;")
source_destination_path = common.execute_and_fetch_all(
user_connnection.cursor(),
"MATCH (n), (m) WITH n, m MATCH p=(n:label0)-[r *BFS]->(m:label4) RETURN extract( node in nodes(p) | node.id);",
)
expected_path = [0, 1, 3, 5]
assert len(source_destination_path) == 1
assert source_destination_path[0][0] == expected_path
def test_bfs_sts_all_edge_types_all_labels_denied():
admin_connection = common.connect(username="admin", password="test")
user_connnection = common.connect(username="user", password="test")
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
common.execute_and_fetch_all(admin_connection.cursor(), "DENY READ ON LABELS * TO user;")
common.execute_and_fetch_all(admin_connection.cursor(), "DENY READ ON EDGE_TYPES * TO user;")
total_paths_results = common.execute_and_fetch_all(
user_connnection.cursor(), "MATCH (n), (m) WITH n, m MATCH p=(n)-[r *BFS]->(m) RETURN p;"
)
assert len(total_paths_results) == 0
def test_bfs_sts_denied_start():
admin_connection = common.connect(username="admin", password="test")
user_connnection = common.connect(username="user", password="test")
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
common.execute_and_fetch_all(
admin_connection.cursor(), "GRANT READ ON LABELS :label1, :label2, :label3, :label4 TO user;"
)
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES * TO user;")
common.execute_and_fetch_all(admin_connection.cursor(), "DENY READ ON LABELS :label0 TO user;")
source_destination_path = common.execute_and_fetch_all(
user_connnection.cursor(), "MATCH (n), (m) WITH n, m MATCH p=(n:label0)-[r *BFS]->(m:label4) RETURN p;"
)
assert len(source_destination_path) == 0
def test_bfs_sts_denied_destination():
admin_connection = common.connect(username="admin", password="test")
user_connnection = common.connect(username="user", password="test")
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
common.execute_and_fetch_all(
admin_connection.cursor(), "GRANT READ ON LABELS :label0, :label1, :label2, :label3 TO user;"
)
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES * TO user;")
common.execute_and_fetch_all(admin_connection.cursor(), "DENY READ ON LABELS :label4 TO user;")
source_destination_path = common.execute_and_fetch_all(
user_connnection.cursor(), "MATCH (n), (m) WITH n, m MATCH p=(n:label0)-[r *BFS]->(m:label4) RETURN p;"
)
assert len(source_destination_path) == 0
def test_bfs_sts_denied_label_1():
admin_connection = common.connect(username="admin", password="test")
user_connnection = common.connect(username="user", password="test")
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
common.execute_and_fetch_all(
admin_connection.cursor(), "GRANT READ ON LABELS :label0, :label2, :label3, :label4 TO user;"
)
common.execute_and_fetch_all(admin_connection.cursor(), "DENY READ ON LABELS :label1 TO user;")
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES * TO user;")
source_destination_path = common.execute_and_fetch_all(
user_connnection.cursor(),
"MATCH (n), (m) WITH n, m MATCH p=(n:label0)-[r *BFS]->(m:label4) RETURN extract( node in nodes(p) | node.id);",
)
expected_path = [0, 2, 4, 5]
assert len(source_destination_path) == 1
assert source_destination_path[0][0] == expected_path
def test_bfs_sts_denied_edge_type_3():
admin_connection = common.connect(username="admin", password="test")
user_connnection = common.connect(username="user", password="test")
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON LABELS * TO user;")
common.execute_and_fetch_all(
admin_connection.cursor(), "GRANT READ ON EDGE_TYPES :edge_type_1, :edge_type_2, :edge_type_4 TO user;"
)
common.execute_and_fetch_all(admin_connection.cursor(), "DENY READ ON EDGE_TYPES :edge_type_3 TO user;")
source_destination_path = common.execute_and_fetch_all(
user_connnection.cursor(),
"MATCH (n), (m) WITH n, m MATCH p=(n:label0)-[r *BFS]->(m:label4) RETURN extract( node in nodes(p) | node.id);",
)
expected_path = [0, 2, 4, 5]
assert len(source_destination_path) == 1
assert source_destination_path[0][0] == expected_path
def test_bfs_single_source_all_edge_types_all_labels_granted():
admin_connection = common.connect(username="admin", password="test")
user_connnection = common.connect(username="user", password="test")
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON LABELS * TO user;")
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES * TO user;")
source_destination_path = common.execute_and_fetch_all(
user_connnection.cursor(),
"MATCH p=(n:label0)-[r *BFS]->(m:label4) RETURN extract( node in nodes(p) | node.id);",
)
expected_path = [0, 2, 3, 5]
assert len(source_destination_path) == 1
assert source_destination_path[0][0] == expected_path
def test_bfs_single_source_all_edge_types_all_labels_denied():
admin_connection = common.connect(username="admin", password="test")
user_connnection = common.connect(username="user", password="test")
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
common.execute_and_fetch_all(admin_connection.cursor(), "DENY READ ON LABELS * TO user;")
common.execute_and_fetch_all(admin_connection.cursor(), "DENY READ ON EDGE_TYPES * TO user;")
total_paths_results = common.execute_and_fetch_all(user_connnection.cursor(), "MATCH p=(n)-[r *BFS]->(m) RETURN p;")
assert len(total_paths_results) == 0
def test_bfs_single_source_denied_start():
admin_connection = common.connect(username="admin", password="test")
user_connnection = common.connect(username="user", password="test")
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
common.execute_and_fetch_all(
admin_connection.cursor(), "GRANT READ ON LABELS :label1, :label2, :label3, :label4 TO user;"
)
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES * TO user;")
common.execute_and_fetch_all(admin_connection.cursor(), "DENY READ ON LABELS :label0 TO user;")
source_destination_path = common.execute_and_fetch_all(
user_connnection.cursor(), "MATCH p=(n:label0)-[r *BFS]->(m:label4) RETURN p;"
)
assert len(source_destination_path) == 0
def test_bfs_single_source_denied_destination():
admin_connection = common.connect(username="admin", password="test")
user_connnection = common.connect(username="user", password="test")
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
common.execute_and_fetch_all(
admin_connection.cursor(), "GRANT READ ON LABELS :label0, :label1, :label2, :label3 TO user;"
)
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES * TO user;")
common.execute_and_fetch_all(admin_connection.cursor(), "DENY READ ON LABELS :label4 TO user;")
source_destination_path = common.execute_and_fetch_all(
user_connnection.cursor(), "MATCH p=(n:label0)-[r *BFS]->(m:label4) RETURN p;"
)
assert len(source_destination_path) == 0
def test_bfs_single_source_denied_label_1():
admin_connection = common.connect(username="admin", password="test")
user_connnection = common.connect(username="user", password="test")
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
common.execute_and_fetch_all(
admin_connection.cursor(), "GRANT READ ON LABELS :label0, :label2, :label3, :label4 TO user;"
)
common.execute_and_fetch_all(admin_connection.cursor(), "DENY READ ON LABELS :label1 TO user;")
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON EDGE_TYPES * TO user;")
source_destination_path = common.execute_and_fetch_all(
user_connnection.cursor(),
"MATCH p=(n:label0)-[r *BFS]->(m:label4) RETURN extract( node in nodes(p) | node.id);",
)
expected_path = [0, 2, 3, 5]
assert len(source_destination_path) == 1
assert source_destination_path[0][0] == expected_path
def test_bfs_single_source_denied_edge_type_3():
admin_connection = common.connect(username="admin", password="test")
user_connnection = common.connect(username="user", password="test")
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE LABELS * FROM user;")
common.execute_and_fetch_all(admin_connection.cursor(), "REVOKE EDGE_TYPES * FROM user;")
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON LABELS * TO user;")
common.execute_and_fetch_all(
admin_connection.cursor(), "GRANT READ ON EDGE_TYPES :edge_type_1, :edge_type_2, :edge_type_4 TO user;"
)
common.execute_and_fetch_all(admin_connection.cursor(), "DENY READ ON EDGE_TYPES :edge_type_3 TO user;")
source_destination_path = common.execute_and_fetch_all(
user_connnection.cursor(),
"MATCH p=(n:label0)-[r *BFS]->(m:label4) RETURN extract( node in nodes(p) | node.id);",
)
expected_path = [0, 2, 4, 5]
assert len(source_destination_path) == 1
assert source_destination_path[0][0] == expected_path
if __name__ == "__main__":
sys.exit(pytest.main([__file__, "-rA"]))

View File

@ -1,5 +1,18 @@
bolt_port: &bolt_port "7687"
template_cluster: &template_cluster
create_delete_filtering_cluster: &create_delete_filtering_cluster
cluster:
main:
args: ["--bolt-port", "7687", "--log-level=TRACE"]
log_file: "fine_grained_access.log"
setup_queries:
[
"CREATE USER admin IDENTIFIED BY 'test';",
"CREATE USER user IDENTIFIED BY 'test';",
"GRANT ALL PRIVILEGES TO admin;",
"GRANT ALL PRIVILEGES TO user;",
]
edge_type_filtering_cluster: &edge_type_filtering_cluster
cluster:
main:
args: ["--bolt-port", *bolt_port, "--log-level=TRACE"]
@ -10,6 +23,8 @@ template_cluster: &template_cluster
"CREATE USER user IDENTIFIED BY 'test';",
"GRANT ALL PRIVILEGES TO admin;",
"GRANT ALL PRIVILEGES TO user;",
"GRANT CREATE_DELETE ON LABELS * TO admin;",
"GRANT CREATE_DELETE ON EDGE_TYPES * TO admin;",
"MERGE (l1:label1 {name: 'test1'});",
"MERGE (l2:label2 {name: 'test2'});",
"MATCH (l1:label1),(l2:label2) WHERE l1.name = 'test1' AND l2.name = 'test2' CREATE (l1)-[r:edgeType1]->(l2);",
@ -20,8 +35,40 @@ template_cluster: &template_cluster
]
validation_queries: []
path_filtering_cluster: &path_filtering_cluster
cluster:
main:
args: ["--bolt-port", "7687", "--log-level=TRACE"]
log_file: "fine_grained_access.log"
setup_queries:
[
"CREATE USER admin IDENTIFIED BY 'test';",
"CREATE USER user IDENTIFIED BY 'test';",
"GRANT ALL PRIVILEGES TO admin;",
"GRANT ALL PRIVILEGES TO user;",
"MERGE (a:label0 {id: 0}) MERGE (b:label1 {id: 1}) CREATE (a)-[:edge_type_1 {weight: 6}]->(b);",
"MERGE (a:label0 {id: 0}) MERGE (b:label2 {id: 2}) CREATE (a)-[:edge_type_1 {weight: 14}]->(b);",
"MERGE (a:label1 {id: 1}) MERGE (b:label2 {id: 2}) CREATE (a)-[:edge_type_2 {weight: 1}]->(b);",
"MERGE (a:label2 {id: 2}) MERGE (b:label3 {id: 4}) CREATE (a)-[:edge_type_2 {weight: 10}]->(b);",
"MERGE (a:label1 {id: 1}) MERGE (b:label3 {id: 3}) CREATE (a)-[:edge_type_3 {weight: 5}]->(b);",
"MERGE (a:label2 {id: 2}) MERGE (b:label3 {id: 3}) CREATE (a)-[:edge_type_3 {weight: 7}]->(b);",
"MERGE (a:label3 {id: 3}) MERGE (b:label3 {id: 4}) CREATE (a)-[:edge_type_4 {weight: 1}]->(b);",
"MERGE (a:label3 {id: 4}) MERGE (b:label3 {id: 3}) CREATE (a)-[:edge_type_4 {weight: 1}]->(b);",
"MERGE (a:label3 {id: 3}) MERGE (b:label4 {id: 5}) CREATE (a)-[:edge_type_4 {weight: 14}]->(b);",
"MERGE (a:label3 {id: 4}) MERGE (b:label4 {id: 5}) CREATE (a)-[:edge_type_4 {weight: 8}]->(b);",
]
workloads:
- name: "Fine Grained Access"
- name: "Create delete filtering"
binary: "tests/e2e/pytest_runner.sh"
args: ["fine_grained_access/create_delete_filtering_tests.py"]
<<: *create_delete_filtering_cluster
- name: "EdgeType filtering"
binary: "tests/e2e/pytest_runner.sh"
args: ["fine_grained_access/edge_type_filtering_tests.py"]
<<: *template_cluster
<<: *edge_type_filtering_cluster
- name: "Path filtering"
binary: "tests/e2e/pytest_runner.sh"
args: ["fine_grained_access/path_filtering_tests.py"]
<<: *path_filtering_cluster

View File

@ -5,5 +5,8 @@ endfunction()
copy_lba_procedures_e2e_python_files(common.py)
copy_lba_procedures_e2e_python_files(lba_procedures.py)
copy_lba_procedures_e2e_python_files(show_privileges.py)
copy_lba_procedures_e2e_python_files(read_permission_queries.py)
copy_lba_procedures_e2e_python_files(update_permission_queries.py)
add_subdirectory(procedures)

View File

@ -22,3 +22,30 @@ def connect(**kwargs) -> mgclient.Connection:
connection = mgclient.connect(host="localhost", port=7687, **kwargs)
connection.autocommit = True
return connection
def reset_permissions(admin_cursor: mgclient.Cursor, create_index: bool):
execute_and_fetch_all(admin_cursor, "REVOKE LABELS * FROM user;")
execute_and_fetch_all(admin_cursor, "REVOKE EDGE_TYPES * FROM user;")
execute_and_fetch_all(admin_cursor, "MATCH(n) DETACH DELETE n;")
execute_and_fetch_all(admin_cursor, "DROP INDEX ON :read_label(prop);")
execute_and_fetch_all(admin_cursor, "DROP INDEX ON :read_label;")
execute_and_fetch_all(admin_cursor, "CREATE (n:read_label {prop: 5});")
if create_index:
execute_and_fetch_all(admin_cursor, "CREATE INDEX ON :read_label;")
execute_and_fetch_all(admin_cursor, "CREATE INDEX ON :read_label(prop);")
def reset_update_permissions(admin_cursor: mgclient.Cursor):
execute_and_fetch_all(admin_cursor, "REVOKE LABELS * FROM user;")
execute_and_fetch_all(admin_cursor, "REVOKE EDGE_TYPES * FROM user;")
execute_and_fetch_all(admin_cursor, "MATCH(n) DETACH DELETE n;")
execute_and_fetch_all(admin_cursor, "CREATE (n:update_label {prop: 1});")
execute_and_fetch_all(
admin_cursor,
"CREATE (n:update_label_1)-[r:update_edge_type]->(m:update_label_2);",
)

View File

@ -0,0 +1,150 @@
# Copyright 2022 Memgraph Ltd.
#
# Use of this software is governed by the Business Source License
# included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
# License, and you may not use this file except in compliance with the Business Source License.
#
# As of the Change Date specified in that file, in accordance with
# the Business Source License, use of this software will be governed
# by the Apache License, Version 2.0, included in the file
# licenses/APL.txt.
import pytest
import sys
from typing import List
from common import connect, execute_and_fetch_all, reset_permissions
match_query = "MATCH (n) RETURN n;"
match_by_id_query = "MATCH (n) WHERE ID(n) >= 0 RETURN n;"
match_by_label_query = "MATCH (n:read_label) RETURN n;"
match_by_label_property_range_query = "MATCH (n:read_label) WHERE n.prop < 7 RETURN n;"
match_by_label_property_value_query = "MATCH (n:read_label {prop: 5}) RETURN n;"
match_by_label_property_query = "MATCH (n:read_label) WHERE n.prop IS NOT NULL RETURN n;"
read_node_without_index_operation_cases = [
["GRANT READ ON LABELS :read_label TO user;"],
["GRANT READ ON LABELS * TO user;"],
["GRANT UPDATE ON LABELS :read_label TO user;"],
["GRANT UPDATE ON LABELS * TO user;"],
["GRANT CREATE_DELETE ON LABELS :read_label TO user;"],
["GRANT CREATE_DELETE ON LABELS * TO user;"],
]
read_node_with_index_operation_cases = [
["GRANT READ ON LABELS :read_label TO user;"],
["GRANT READ ON LABELS * TO user;"],
["GRANT UPDATE ON LABELS :read_label TO user;"],
["GRANT UPDATE ON LABELS * TO user;"],
["GRANT CREATE_DELETE ON LABELS :read_label TO user;"],
["GRANT CREATE_DELETE ON LABELS * TO user;"],
]
not_read_node_without_index_operation_cases = [
[],
["DENY READ ON LABELS :read_label TO user;"],
["DENY READ ON LABELS * TO user;"],
[
"GRANT UPDATE ON LABELS :read_label TO user;",
"DENY READ ON LABELS :read_label TO user",
],
[
"GRANT UPDATE ON LABELS * TO user;",
"DENY READ ON LABELS :read_label TO user",
],
[
"GRANT CREATE_DELETE ON LABELS :read_label TO user;",
"DENY READ ON LABELS :read_label TO user",
],
[
"GRANT CREATE_DELETE ON LABELS * TO user;",
"DENY READ ON LABELS :read_label TO user",
],
]
not_read_node_with_index_operation_cases = [
[],
["DENY READ ON LABELS :read_label TO user;"],
["DENY READ ON LABELS * TO user;"],
[
"GRANT UPDATE ON LABELS :read_label TO user;",
"DENY READ ON LABELS :read_label TO user",
],
[
"GRANT UPDATE ON LABELS * TO user;",
"DENY READ ON LABELS :read_label TO user",
],
[
"GRANT CREATE_DELETE ON LABELS :read_label TO user;",
"DENY READ ON LABELS :read_label TO user",
],
[
"GRANT CREATE_DELETE ON LABELS * TO user;",
"DENY READ ON LABELS :read_label TO user",
],
]
def get_admin_cursor():
return connect(username="admin", password="test").cursor()
def get_user_cursor():
return connect(username="user", password="test").cursor()
def execute_read_node_assertion(
operation_case: List[str], queries: List[str], create_index: bool, can_read: bool
) -> None:
admin_cursor = get_admin_cursor()
user_cursor = get_user_cursor()
reset_permissions(admin_cursor, create_index)
for operation in operation_case:
execute_and_fetch_all(admin_cursor, operation)
read_size = 1 if can_read else 0
for mq in queries:
results = execute_and_fetch_all(user_cursor, mq)
assert len(results) == read_size
def test_can_read_node_when_authorized():
match_queries_without_index = [match_query, match_by_id_query]
match_queries_with_index = [
match_by_label_query,
match_by_label_property_query,
match_by_label_property_range_query,
match_by_label_property_value_query,
]
for operation_case in read_node_without_index_operation_cases:
execute_read_node_assertion(operation_case, match_queries_without_index, False, True)
for operation_case in read_node_with_index_operation_cases:
execute_read_node_assertion(operation_case, match_queries_with_index, True, True)
def test_can_not_read_node_when_authorized():
match_queries_without_index = [match_query, match_by_id_query]
match_queries_with_index = [
match_by_label_query,
match_by_label_property_query,
match_by_label_property_range_query,
match_by_label_property_value_query,
]
for operation_case in not_read_node_without_index_operation_cases:
execute_read_node_assertion(operation_case, match_queries_without_index, False, False)
for operation_case in not_read_node_with_index_operation_cases:
execute_read_node_assertion(operation_case, match_queries_with_index, True, False)
if __name__ == "__main__":
sys.exit(pytest.main([__file__, "-rA"]))

View File

@ -0,0 +1,158 @@
# Copyright 2022 Memgraph Ltd.
#
# Use of this software is governed by the Business Source License
# included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
# License, and you may not use this file except in compliance with the Business Source License.
#
# As of the Change Date specified in that file, in accordance with
# the Business Source License, use of this software will be governed
# by the Apache License, Version 2.0, included in the file
# licenses/APL.txt.
import sys
import pytest
from common import connect, execute_and_fetch_all, reset_update_permissions
update_property_query = "MATCH (n:update_label) SET n.prop = 2 RETURN n.prop;"
update_properties_query = "MATCH (n:update_label) SET n = {prop: 2, prop2: 3} RETURN n.prop;"
remove_property_query = "MATCH (n:update_label) REMOVE n.prop RETURN n.prop;"
def test_can_read_node_when_given_update_grant():
admin_cursor = connect(username="admin", password="test").cursor()
reset_update_permissions(admin_cursor)
execute_and_fetch_all(admin_cursor, "GRANT UPDATE ON LABELS :update_label TO user;")
test_cursor = connect(username="user", password="test").cursor()
results = execute_and_fetch_all(test_cursor, "MATCH (n:update_label) RETURN n;")
assert len(results) == 1
def test_can_update_node_when_given_update_grant():
admin_cursor = connect(username="admin", password="test").cursor()
reset_update_permissions(admin_cursor)
execute_and_fetch_all(admin_cursor, "GRANT UPDATE ON LABELS :update_label TO user;")
test_cursor = connect(username="user", password="test").cursor()
update_property_actual = execute_and_fetch_all(test_cursor, update_property_query)
update_properties_actual = execute_and_fetch_all(test_cursor, update_properties_query)
remove_property_actual = execute_and_fetch_all(test_cursor, remove_property_query)
assert update_property_actual[0][0] == 2
assert update_properties_actual[0][0] == 2
assert remove_property_actual[0][0] is None
def test_can_not_update_node_when_given_deny():
admin_cursor = connect(username="admin", password="test").cursor()
reset_update_permissions(admin_cursor)
execute_and_fetch_all(admin_cursor, "DENY UPDATE ON LABELS :update_label TO user;")
test_cursor = connect(username="user", password="test").cursor()
update_property_actual = execute_and_fetch_all(test_cursor, update_property_query)
update_properties_actual = execute_and_fetch_all(test_cursor, update_properties_query)
remove_property_actual = execute_and_fetch_all(test_cursor, remove_property_query)
assert update_property_actual[0][0] == 1
assert update_properties_actual[0][0] == 1
assert remove_property_actual[0][0] == 1
def test_can_not_update_node_when_given_nothing():
admin_cursor = connect(username="admin", password="test").cursor()
reset_update_permissions(admin_cursor)
test_cursor = connect(username="user", password="test").cursor()
update_property_actual = execute_and_fetch_all(test_cursor, update_property_query)
update_properties_actual = execute_and_fetch_all(test_cursor, update_properties_query)
remove_property_actual = execute_and_fetch_all(test_cursor, remove_property_query)
assert len(update_property_actual) == 0
assert len(update_properties_actual) == 0
assert len(remove_property_actual) == 0
def test_can_not_update_node_when_given_read():
admin_cursor = connect(username="admin", password="test").cursor()
reset_update_permissions(admin_cursor)
execute_and_fetch_all(admin_cursor, "GRANT READ ON LABELS :update_label TO user;")
test_cursor = connect(username="user", password="test").cursor()
update_property_actual = execute_and_fetch_all(test_cursor, update_property_query)
update_properties_actual = execute_and_fetch_all(test_cursor, update_properties_query)
remove_property_actual = execute_and_fetch_all(test_cursor, remove_property_query)
assert update_property_actual[0][0] == 1
assert update_properties_actual[0][0] == 1
assert remove_property_actual[0][0] == 1
def test_can_not_update_node_when_given_read_globally():
admin_cursor = connect(username="admin", password="test").cursor()
reset_update_permissions(admin_cursor)
execute_and_fetch_all(admin_cursor, "GRANT READ ON LABELS * TO user;")
test_cursor = connect(username="user", password="test").cursor()
update_property_actual = execute_and_fetch_all(test_cursor, update_property_query)
update_properties_actual = execute_and_fetch_all(test_cursor, update_properties_query)
remove_property_actual = execute_and_fetch_all(test_cursor, remove_property_query)
assert update_property_actual[0][0] == 1
assert update_properties_actual[0][0] == 1
assert remove_property_actual[0][0] == 1
def test_can_update_node_when_given_update_globally():
admin_cursor = connect(username="admin", password="test").cursor()
reset_update_permissions(admin_cursor)
execute_and_fetch_all(admin_cursor, "GRANT UPDATE ON LABELS * TO user;")
test_cursor = connect(username="user", password="test").cursor()
update_property_actual = execute_and_fetch_all(test_cursor, update_property_query)
update_properties_actual = execute_and_fetch_all(test_cursor, update_properties_query)
remove_property_actual = execute_and_fetch_all(test_cursor, remove_property_query)
assert update_property_actual[0][0] == 2
assert update_properties_actual[0][0] == 2
assert remove_property_actual[0][0] is None
def test_can_update_node_when_given_create_delete_globally():
admin_cursor = connect(username="admin", password="test").cursor()
reset_update_permissions(admin_cursor)
execute_and_fetch_all(admin_cursor, "GRANT CREATE_DELETE ON LABELS * TO user;")
test_cursor = connect(username="user", password="test").cursor()
update_property_actual = execute_and_fetch_all(test_cursor, update_property_query)
update_properties_actual = execute_and_fetch_all(test_cursor, update_properties_query)
remove_property_actual = execute_and_fetch_all(test_cursor, remove_property_query)
assert update_property_actual[0][0] == 2
assert update_properties_actual[0][0] == 2
assert remove_property_actual[0][0] is None
def test_can_update_node_when_given_create_delete():
admin_cursor = connect(username="admin", password="test").cursor()
reset_update_permissions(admin_cursor)
execute_and_fetch_all(admin_cursor, "GRANT CREATE_DELETE ON LABELS :update_label TO user;")
test_cursor = connect(username="user", password="test").cursor()
update_property_actual = execute_and_fetch_all(test_cursor, update_property_query)
update_properties_actual = execute_and_fetch_all(test_cursor, update_properties_query)
remove_property_actual = execute_and_fetch_all(test_cursor, remove_property_query)
assert update_property_actual[0][0] == 2
assert update_properties_actual[0][0] == 2
assert remove_property_actual[0][0] is None
if __name__ == "__main__":
sys.exit(pytest.main([__file__, "-rA"]))

View File

@ -3,21 +3,20 @@ template_cluster: &template_cluster
main:
args: ["--bolt-port", "7687", "--log-level=TRACE"]
log_file: "lba-e2e.log"
setup_queries: [
"Create (:Label1 {id: 1}) ;",
"Create (:Label1 {id: 2}) ;",
"Create (:Label1 {id: 3}) ;",
"Create (:Label1 {id: 4}) ;",
"Create (:Label1 {id: 5}) ;",
"Create (:Label1 {id: 6}) ;",
"Create (:Label2 {id: 1}) ;",
"Create (:Label2 {id: 2}) ;",
"Create (:Label2 {id: 3}) ;",
"Create (:Label2 {id: 4}) ;",
"Create User Josip ;",
"Create User Boris ;",
"Grant Read On Labels :Label1 to Boris;",
]
setup_queries:
- "Create (:Label1 {id: 1}) ;"
- "Create (:Label1 {id: 2}) ;"
- "Create (:Label1 {id: 3}) ;"
- "Create (:Label1 {id: 4}) ;"
- "Create (:Label1 {id: 5}) ;"
- "Create (:Label1 {id: 6}) ;"
- "Create (:Label2 {id: 1}) ;"
- "Create (:Label2 {id: 2}) ;"
- "Create (:Label2 {id: 3}) ;"
- "Create (:Label2 {id: 4}) ;"
- "Create User Josip ;"
- "Create User Boris ;"
- "Grant Read On Labels :Label1 to Boris;"
validation_queries: []
show_privileges_cluster: &show_privileges_cluster
@ -25,37 +24,61 @@ show_privileges_cluster: &show_privileges_cluster
main:
args: ["--bolt-port", "7687", "--log-level=TRACE"]
log_file: "lba-e2e.log"
setup_queries: [
"Create User Josip;",
"Grant Read On Labels :Label1 to Josip;",
"Deny Read On Labels :Label2 to Josip;",
"Grant Update On Labels :Label3 to Josip;",
"Deny Update On Labels :Label4 to Josip;",
"Grant Create_Delete On Labels :Label5 to Josip;",
"Deny Create_Delete On Labels :Label6 to Josip;",
"Grant Create_Delete On Labels :Label7 to Josip;",
"Deny Read On Labels :Label7 to Josip;",
setup_queries:
- "Create User Josip;"
- "Grant Read On Labels :Label1 to Josip;"
- "Deny Read On Labels :Label2 to Josip;"
- "Grant Update On Labels :Label3 to Josip;"
- "Deny Update On Labels :Label4 to Josip;"
- "Grant Create_Delete On Labels :Label5 to Josip;"
- "Deny Create_Delete On Labels :Label6 to Josip;"
- "Grant Create_Delete On Labels :Label7 to Josip;"
- "Deny Read On Labels :Label7 to Josip;"
"Create User Boris;",
"Grant Auth to Boris;",
"Grant Read On Labels :Label1 to Boris;",
"Deny Read On Labels :Label2 to Boris;",
"Grant Update On Labels :Label3 to Boris;",
"Deny Update On Labels :Label4 to Boris;",
"Grant Create_Delete On Labels :Label5 to Boris;",
"Deny Create_Delete On Labels :Label6 to Boris;",
"Grant Create_Delete On Labels :Label7 to Boris;",
"Deny Read On Labels :Label7 to Boris;",
- "Create User Boris;"
- "Grant Auth to Boris;"
- "Grant Read On Labels :Label1 to Boris;"
- "Deny Read On Labels :Label2 to Boris;"
- "Grant Update On Labels :Label3 to Boris;"
- "Deny Update On Labels :Label4 to Boris;"
- "Grant Create_Delete On Labels :Label5 to Boris;"
- "Deny Create_Delete On Labels :Label6 to Boris;"
- "Grant Create_Delete On Labels :Label7 to Boris;"
- "Deny Read On Labels :Label7 to Boris;"
"Create User Niko;",
"Grant Auth to Niko;",
"Grant Create_Delete On Labels * to Niko",
"Deny Update On Labels * to Niko",
- "Create User Niko;"
- "Grant Auth to Niko;"
- "Grant Create_Delete On Labels * to Niko"
- "Deny Update On Labels * to Niko"
"Create User Bruno;",
"Grant Auth to Bruno;",
"Deny Create_Delete On Labels * to Bruno"
]
- "Create User Bruno;"
- "Grant Auth to Bruno;"
- "Deny Create_Delete On Labels * to Bruno"
read_permission_queries: &read_permission_queries
cluster:
main:
args: ["--bolt-port", "7687", "--log-level=TRACE"]
log_file: "lba-e2e.log"
setup_queries:
- "CREATE USER admin IDENTIFIED BY 'test';"
- "GRANT ALL PRIVILEGES TO admin;"
- "CREATE USER user IDENTIFIED BY 'test';"
- "GRANT ALL PRIVILEGES TO user;"
validation_queries: []
update_permission_queries_cluster: &update_permission_queries_cluster
cluster:
main:
args: ["--bolt-port", "7687", "--log-level=TRACE"]
log_file: "lba-e2e.log"
setup_queries:
- "CREATE USER admin IDENTIFIED BY 'test';"
- "GRANT ALL PRIVILEGES TO admin;"
- "CREATE USER user IDENTIFIED BY 'test'"
- "GRANT ALL PRIVILEGES TO user;"
validation_queries: []
workloads:
- name: "Label-based auth"
@ -69,3 +92,15 @@ workloads:
proc: "tests/e2e/lba_procedures/procedures/"
args: ["lba_procedures/show_privileges.py"]
<<: *show_privileges_cluster
- name: "read-permission-queries"
binary: "tests/e2e/pytest_runner.sh"
proc: "tests/e2e/lba_procedures/procedures/"
args: ["lba_procedures/read_permission_queries.py"]
<<: *read_permission_queries
- name: "update-permission-queries"
binary: "tests/e2e/pytest_runner.sh"
proc: "tests/e2e/lba_procedures/procedures/"
args: ["lba_procedures/update_permission_queries.py"]
<<: *update_permission_queries_cluster

View File

@ -70,7 +70,10 @@ target_link_libraries(${test_prefix}mgp_trans_c_api mg-query)
# Test mg-query
add_unit_test(bfs_single_node.cpp)
target_link_libraries(${test_prefix}bfs_single_node mg-query)
target_link_libraries(${test_prefix}bfs_single_node mg-query mg-glue)
add_unit_test(bfs_fine_grained.cpp)
target_link_libraries(${test_prefix}bfs_fine_grained mg-query mg-glue)
add_unit_test(cypher_main_visitor.cpp)
target_link_libraries(${test_prefix}cypher_main_visitor mg-query)

View File

@ -46,16 +46,22 @@ class FineGrainedAuthCheckerFixture : public testing::Test {
TEST_F(FineGrainedAuthCheckerFixture, GrantedAllLabels) {
memgraph::auth::User user{"test"};
user.fine_grained_access_handler().label_permissions().Grant("*",
memgraph::auth::FineGrainedPermission::CREATE_DELETE);
user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
memgraph::glue::FineGrainedAuthChecker auth_checker{user};
ASSERT_TRUE(auth_checker.Accept(dba, v1, memgraph::storage::View::NEW));
ASSERT_TRUE(auth_checker.Accept(dba, v1, memgraph::storage::View::OLD));
ASSERT_TRUE(auth_checker.Accept(dba, v2, memgraph::storage::View::NEW));
ASSERT_TRUE(auth_checker.Accept(dba, v2, memgraph::storage::View::OLD));
ASSERT_TRUE(auth_checker.Accept(dba, v3, memgraph::storage::View::NEW));
ASSERT_TRUE(auth_checker.Accept(dba, v3, memgraph::storage::View::OLD));
ASSERT_TRUE(auth_checker.Accept(dba, v1, memgraph::storage::View::NEW,
memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
ASSERT_TRUE(auth_checker.Accept(dba, v1, memgraph::storage::View::OLD,
memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
ASSERT_TRUE(auth_checker.Accept(dba, v2, memgraph::storage::View::NEW,
memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
ASSERT_TRUE(auth_checker.Accept(dba, v2, memgraph::storage::View::OLD,
memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
ASSERT_TRUE(auth_checker.Accept(dba, v3, memgraph::storage::View::NEW,
memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
ASSERT_TRUE(auth_checker.Accept(dba, v3, memgraph::storage::View::OLD,
memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
}
TEST_F(FineGrainedAuthCheckerFixture, GrantedAllEdgeTypes) {
@ -64,10 +70,10 @@ TEST_F(FineGrainedAuthCheckerFixture, GrantedAllEdgeTypes) {
"*", memgraph::auth::FineGrainedPermission::CREATE_DELETE);
memgraph::glue::FineGrainedAuthChecker auth_checker{user};
ASSERT_TRUE(auth_checker.Accept(dba, r1));
ASSERT_TRUE(auth_checker.Accept(dba, r2));
ASSERT_TRUE(auth_checker.Accept(dba, r3));
ASSERT_TRUE(auth_checker.Accept(dba, r4));
ASSERT_TRUE(auth_checker.Accept(dba, r1, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
ASSERT_TRUE(auth_checker.Accept(dba, r2, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
ASSERT_TRUE(auth_checker.Accept(dba, r3, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
ASSERT_TRUE(auth_checker.Accept(dba, r4, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
}
TEST_F(FineGrainedAuthCheckerFixture, DeniedAllLabels) {
@ -75,12 +81,18 @@ TEST_F(FineGrainedAuthCheckerFixture, DeniedAllLabels) {
user.fine_grained_access_handler().label_permissions().Deny("*", memgraph::auth::FineGrainedPermission::READ);
memgraph::glue::FineGrainedAuthChecker auth_checker{user};
ASSERT_FALSE(auth_checker.Accept(dba, v1, memgraph::storage::View::NEW));
ASSERT_FALSE(auth_checker.Accept(dba, v1, memgraph::storage::View::OLD));
ASSERT_FALSE(auth_checker.Accept(dba, v2, memgraph::storage::View::NEW));
ASSERT_FALSE(auth_checker.Accept(dba, v2, memgraph::storage::View::OLD));
ASSERT_FALSE(auth_checker.Accept(dba, v3, memgraph::storage::View::NEW));
ASSERT_FALSE(auth_checker.Accept(dba, v3, memgraph::storage::View::OLD));
ASSERT_FALSE(auth_checker.Accept(dba, v1, memgraph::storage::View::NEW,
memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
ASSERT_FALSE(auth_checker.Accept(dba, v1, memgraph::storage::View::OLD,
memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
ASSERT_FALSE(auth_checker.Accept(dba, v2, memgraph::storage::View::NEW,
memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
ASSERT_FALSE(auth_checker.Accept(dba, v2, memgraph::storage::View::OLD,
memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
ASSERT_FALSE(auth_checker.Accept(dba, v3, memgraph::storage::View::NEW,
memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
ASSERT_FALSE(auth_checker.Accept(dba, v3, memgraph::storage::View::OLD,
memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
}
TEST_F(FineGrainedAuthCheckerFixture, DeniedAllEdgeTypes) {
@ -88,10 +100,10 @@ TEST_F(FineGrainedAuthCheckerFixture, DeniedAllEdgeTypes) {
user.fine_grained_access_handler().edge_type_permissions().Deny("*", memgraph::auth::FineGrainedPermission::READ);
memgraph::glue::FineGrainedAuthChecker auth_checker{user};
ASSERT_FALSE(auth_checker.Accept(dba, r1));
ASSERT_FALSE(auth_checker.Accept(dba, r2));
ASSERT_FALSE(auth_checker.Accept(dba, r3));
ASSERT_FALSE(auth_checker.Accept(dba, r4));
ASSERT_FALSE(auth_checker.Accept(dba, r1, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
ASSERT_FALSE(auth_checker.Accept(dba, r2, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
ASSERT_FALSE(auth_checker.Accept(dba, r3, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
ASSERT_FALSE(auth_checker.Accept(dba, r4, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
}
TEST_F(FineGrainedAuthCheckerFixture, GrantLabel) {
@ -100,8 +112,10 @@ TEST_F(FineGrainedAuthCheckerFixture, GrantLabel) {
memgraph::auth::FineGrainedPermission::CREATE_DELETE);
memgraph::glue::FineGrainedAuthChecker auth_checker{user};
ASSERT_TRUE(auth_checker.Accept(dba, v1, memgraph::storage::View::NEW));
ASSERT_TRUE(auth_checker.Accept(dba, v1, memgraph::storage::View::OLD));
ASSERT_TRUE(auth_checker.Accept(dba, v1, memgraph::storage::View::NEW,
memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
ASSERT_TRUE(auth_checker.Accept(dba, v1, memgraph::storage::View::OLD,
memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
}
TEST_F(FineGrainedAuthCheckerFixture, DenyLabel) {
@ -109,8 +123,10 @@ TEST_F(FineGrainedAuthCheckerFixture, DenyLabel) {
user.fine_grained_access_handler().label_permissions().Deny("l3", memgraph::auth::FineGrainedPermission::READ);
memgraph::glue::FineGrainedAuthChecker auth_checker{user};
ASSERT_FALSE(auth_checker.Accept(dba, v3, memgraph::storage::View::NEW));
ASSERT_FALSE(auth_checker.Accept(dba, v3, memgraph::storage::View::OLD));
ASSERT_FALSE(auth_checker.Accept(dba, v3, memgraph::storage::View::NEW,
memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
ASSERT_FALSE(auth_checker.Accept(dba, v3, memgraph::storage::View::OLD,
memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
}
TEST_F(FineGrainedAuthCheckerFixture, GrantAndDenySpecificLabels) {
@ -122,12 +138,18 @@ TEST_F(FineGrainedAuthCheckerFixture, GrantAndDenySpecificLabels) {
user.fine_grained_access_handler().label_permissions().Deny("l3", memgraph::auth::FineGrainedPermission::READ);
memgraph::glue::FineGrainedAuthChecker auth_checker{user};
ASSERT_TRUE(auth_checker.Accept(dba, v1, memgraph::storage::View::NEW));
ASSERT_TRUE(auth_checker.Accept(dba, v1, memgraph::storage::View::OLD));
ASSERT_TRUE(auth_checker.Accept(dba, v2, memgraph::storage::View::NEW));
ASSERT_TRUE(auth_checker.Accept(dba, v2, memgraph::storage::View::OLD));
ASSERT_FALSE(auth_checker.Accept(dba, v3, memgraph::storage::View::NEW));
ASSERT_FALSE(auth_checker.Accept(dba, v3, memgraph::storage::View::OLD));
ASSERT_TRUE(auth_checker.Accept(dba, v1, memgraph::storage::View::NEW,
memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
ASSERT_TRUE(auth_checker.Accept(dba, v1, memgraph::storage::View::OLD,
memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
ASSERT_TRUE(auth_checker.Accept(dba, v2, memgraph::storage::View::NEW,
memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
ASSERT_TRUE(auth_checker.Accept(dba, v2, memgraph::storage::View::OLD,
memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
ASSERT_FALSE(auth_checker.Accept(dba, v3, memgraph::storage::View::NEW,
memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
ASSERT_FALSE(auth_checker.Accept(dba, v3, memgraph::storage::View::OLD,
memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
}
TEST_F(FineGrainedAuthCheckerFixture, MultipleVertexLabels) {
@ -142,10 +164,14 @@ TEST_F(FineGrainedAuthCheckerFixture, MultipleVertexLabels) {
ASSERT_TRUE(v2.AddLabel(dba.NameToLabel("l1")).HasValue());
dba.AdvanceCommand();
ASSERT_FALSE(auth_checker.Accept(dba, v1, memgraph::storage::View::NEW));
ASSERT_FALSE(auth_checker.Accept(dba, v1, memgraph::storage::View::OLD));
ASSERT_TRUE(auth_checker.Accept(dba, v2, memgraph::storage::View::NEW));
ASSERT_TRUE(auth_checker.Accept(dba, v2, memgraph::storage::View::OLD));
ASSERT_FALSE(auth_checker.Accept(dba, v1, memgraph::storage::View::NEW,
memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
ASSERT_FALSE(auth_checker.Accept(dba, v1, memgraph::storage::View::OLD,
memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
ASSERT_TRUE(auth_checker.Accept(dba, v2, memgraph::storage::View::NEW,
memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
ASSERT_TRUE(auth_checker.Accept(dba, v2, memgraph::storage::View::OLD,
memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
}
TEST_F(FineGrainedAuthCheckerFixture, GrantEdgeType) {
@ -154,7 +180,7 @@ TEST_F(FineGrainedAuthCheckerFixture, GrantEdgeType) {
"edge_type_1", memgraph::auth::FineGrainedPermission::CREATE_DELETE);
memgraph::glue::FineGrainedAuthChecker auth_checker{user};
ASSERT_TRUE(auth_checker.Accept(dba, r1));
ASSERT_TRUE(auth_checker.Accept(dba, r1, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
}
TEST_F(FineGrainedAuthCheckerFixture, DenyEdgeType) {
@ -163,7 +189,7 @@ TEST_F(FineGrainedAuthCheckerFixture, DenyEdgeType) {
memgraph::auth::FineGrainedPermission::READ);
memgraph::glue::FineGrainedAuthChecker auth_checker{user};
ASSERT_FALSE(auth_checker.Accept(dba, r1));
ASSERT_FALSE(auth_checker.Accept(dba, r1, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
}
TEST_F(FineGrainedAuthCheckerFixture, GrantAndDenySpecificEdgeTypes) {
@ -174,8 +200,8 @@ TEST_F(FineGrainedAuthCheckerFixture, GrantAndDenySpecificEdgeTypes) {
memgraph::auth::FineGrainedPermission::READ);
memgraph::glue::FineGrainedAuthChecker auth_checker{user};
ASSERT_TRUE(auth_checker.Accept(dba, r1));
ASSERT_TRUE(auth_checker.Accept(dba, r2));
ASSERT_FALSE(auth_checker.Accept(dba, r3));
ASSERT_FALSE(auth_checker.Accept(dba, r4));
ASSERT_TRUE(auth_checker.Accept(dba, r1, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
ASSERT_TRUE(auth_checker.Accept(dba, r2, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
ASSERT_FALSE(auth_checker.Accept(dba, r3, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
ASSERT_FALSE(auth_checker.Accept(dba, r4, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
}

View File

@ -13,6 +13,8 @@
#include "gtest/gtest.h"
#include "auth/models.hpp"
#include "glue/auth_checker.hpp"
#include "query/context.hpp"
#include "query/frontend/ast/ast.hpp"
#include "query/interpret/frame.hpp"
@ -189,6 +191,15 @@ std::vector<std::vector<memgraph::query::TypedValue>> PullResults(memgraph::quer
enum class FilterLambdaType { NONE, USE_FRAME, USE_FRAME_NULL, USE_CTX, ERROR };
enum class FineGrainedTestType {
ALL_GRANTED,
ALL_DENIED,
EDGE_TYPE_A_DENIED,
EDGE_TYPE_B_DENIED,
LABEL_0_DENIED,
LABEL_3_DENIED
};
// Common interface for single-node and distributed Memgraph.
class Database {
public:
@ -202,7 +213,6 @@ class Database {
virtual std::pair<std::vector<memgraph::query::VertexAccessor>, std::vector<memgraph::query::EdgeAccessor>>
BuildGraph(memgraph::query::DbAccessor *dba, const std::vector<int> &vertex_locations,
const std::vector<std::tuple<int, int, std::string>> &edges) = 0;
virtual ~Database() {}
};
@ -440,3 +450,271 @@ void BfsTest(Database *db, int lower_bound, int upper_bound, memgraph::query::Ed
dba.Abort();
}
void BfsTestWithFineGrainedFiltering(Database *db, int lower_bound, int upper_bound,
memgraph::query::EdgeAtom::Direction direction,
std::vector<std::string> edge_types, bool known_sink,
FineGrainedTestType fine_grained_test_type) {
auto storage_dba = db->Access();
memgraph::query::DbAccessor db_accessor(&storage_dba);
memgraph::query::AstStorage storage;
memgraph::query::ExecutionContext context{&db_accessor};
memgraph::query::Symbol blocked_symbol = context.symbol_table.CreateSymbol("blocked", true);
memgraph::query::Symbol source_symbol = context.symbol_table.CreateSymbol("source", true);
memgraph::query::Symbol sink_symbol = context.symbol_table.CreateSymbol("sink", true);
memgraph::query::Symbol edges_symbol = context.symbol_table.CreateSymbol("edges", true);
memgraph::query::Symbol inner_node_symbol = context.symbol_table.CreateSymbol("inner_node", true);
memgraph::query::Symbol inner_edge_symbol = context.symbol_table.CreateSymbol("inner_edge", true);
std::vector<memgraph::query::VertexAccessor> vertices;
std::vector<memgraph::query::EdgeAccessor> edges;
std::tie(vertices, edges) = db->BuildGraph(&db_accessor, kVertexLocations, kEdges);
db_accessor.AdvanceCommand();
std::shared_ptr<memgraph::query::plan::LogicalOperator> input_operator;
memgraph::query::Expression *filter_expr = nullptr;
input_operator =
std::make_shared<Yield>(nullptr, std::vector<memgraph::query::Symbol>{blocked_symbol},
std::vector<std::vector<memgraph::query::TypedValue>>{{memgraph::query::TypedValue()}});
memgraph::auth::User user{"test"};
std::vector<std::pair<int, int>> edges_in_result;
switch (fine_grained_test_type) {
case FineGrainedTestType::ALL_GRANTED:
user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().edge_type_permissions().Grant("*",
memgraph::auth::FineGrainedPermission::READ);
edges_in_result = GetEdgeList(kEdges, direction, {"a", "b"});
break;
case FineGrainedTestType::ALL_DENIED:
user.fine_grained_access_handler().label_permissions().Deny("*", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().edge_type_permissions().Deny("*", memgraph::auth::FineGrainedPermission::READ);
break;
case FineGrainedTestType::EDGE_TYPE_A_DENIED:
user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().edge_type_permissions().Grant("b",
memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().edge_type_permissions().Deny("a", memgraph::auth::FineGrainedPermission::READ);
edges_in_result = GetEdgeList(kEdges, direction, {"b"});
break;
case FineGrainedTestType::EDGE_TYPE_B_DENIED:
user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().edge_type_permissions().Grant("a",
memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().edge_type_permissions().Deny("b", memgraph::auth::FineGrainedPermission::READ);
edges_in_result = GetEdgeList(kEdges, direction, {"a"});
break;
case FineGrainedTestType::LABEL_0_DENIED:
user.fine_grained_access_handler().edge_type_permissions().Grant("*",
memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Grant("1", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Grant("2", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Grant("3", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Grant("4", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Deny("0", memgraph::auth::FineGrainedPermission::READ);
edges_in_result = GetEdgeList(kEdges, direction, {"a", "b"});
edges_in_result.erase(
std::remove_if(edges_in_result.begin(), edges_in_result.end(), [](const auto &e) { return e.second == 0; }),
edges_in_result.end());
break;
case FineGrainedTestType::LABEL_3_DENIED:
user.fine_grained_access_handler().edge_type_permissions().Grant("*",
memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Grant("0", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Grant("1", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Grant("2", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Grant("4", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Deny("3", memgraph::auth::FineGrainedPermission::READ);
edges_in_result = GetEdgeList(kEdges, direction, {"a", "b"});
edges_in_result.erase(
std::remove_if(edges_in_result.begin(), edges_in_result.end(), [](const auto &e) { return e.second == 3; }),
edges_in_result.end());
break;
}
memgraph::glue::FineGrainedAuthChecker auth_checker{user};
context.auth_checker = std::make_unique<memgraph::glue::FineGrainedAuthChecker>(std::move(auth_checker));
// We run BFS once from each vertex for each blocked entity.
input_operator = YieldVertices(&db_accessor, vertices, source_symbol, input_operator);
// If the sink is known, we run BFS for all posible combinations of source,
// sink and blocked entity.
if (known_sink) {
input_operator = YieldVertices(&db_accessor, vertices, sink_symbol, input_operator);
}
std::vector<memgraph::storage::EdgeTypeId> storage_edge_types;
for (const auto &t : edge_types) {
storage_edge_types.push_back(db_accessor.NameToEdgeType(t));
}
input_operator = db->MakeBfsOperator(
source_symbol, sink_symbol, edges_symbol, direction, storage_edge_types, input_operator, known_sink,
lower_bound == -1 ? nullptr : LITERAL(lower_bound), upper_bound == -1 ? nullptr : LITERAL(upper_bound),
memgraph::query::plan::ExpansionLambda{inner_edge_symbol, inner_node_symbol, filter_expr});
context.evaluation_context.properties = memgraph::query::NamesToProperties(storage.properties_, &db_accessor);
context.evaluation_context.labels = memgraph::query::NamesToLabels(storage.labels_, &db_accessor);
std::vector<std::vector<memgraph::query::TypedValue>> results;
results = PullResults(input_operator.get(), &context,
std::vector<memgraph::query::Symbol>{source_symbol, sink_symbol, edges_symbol, blocked_symbol});
switch (fine_grained_test_type) {
case FineGrainedTestType::ALL_GRANTED:
switch (direction) {
case memgraph::query::EdgeAtom::Direction::IN:
CheckPathsAndExtractDistances(
&db_accessor, edges_in_result,
std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
break;
case memgraph::query::EdgeAtom::Direction::OUT:
CheckPathsAndExtractDistances(
&db_accessor, edges_in_result,
std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
break;
case memgraph::query::EdgeAtom::Direction::BOTH:
CheckPathsAndExtractDistances(
&db_accessor, edges_in_result,
std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
break;
}
break;
case FineGrainedTestType::ALL_DENIED:
switch (direction) {
case memgraph::query::EdgeAtom::Direction::IN:
EXPECT_EQ(results.size(), 0);
break;
case memgraph::query::EdgeAtom::Direction::OUT:
EXPECT_EQ(results.size(), 0);
break;
case memgraph::query::EdgeAtom::Direction::BOTH:
EXPECT_EQ(results.size(), 0);
break;
}
break;
case FineGrainedTestType::EDGE_TYPE_A_DENIED:
switch (direction) {
case memgraph::query::EdgeAtom::Direction::IN:
CheckPathsAndExtractDistances(
&db_accessor, edges_in_result,
std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
break;
case memgraph::query::EdgeAtom::Direction::OUT:
CheckPathsAndExtractDistances(
&db_accessor, edges_in_result,
std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
break;
case memgraph::query::EdgeAtom::Direction::BOTH:
CheckPathsAndExtractDistances(
&db_accessor, edges_in_result,
std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
break;
}
break;
case FineGrainedTestType::EDGE_TYPE_B_DENIED:
switch (direction) {
case memgraph::query::EdgeAtom::Direction::IN:
CheckPathsAndExtractDistances(
&db_accessor, edges_in_result,
std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
break;
case memgraph::query::EdgeAtom::Direction::OUT:
CheckPathsAndExtractDistances(
&db_accessor, edges_in_result,
std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
break;
case memgraph::query::EdgeAtom::Direction::BOTH:
CheckPathsAndExtractDistances(
&db_accessor, edges_in_result,
std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
break;
}
break;
case FineGrainedTestType::LABEL_0_DENIED:
switch (direction) {
case memgraph::query::EdgeAtom::Direction::IN:
if (known_sink) {
CheckPathsAndExtractDistances(
&db_accessor, edges_in_result,
std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
} else {
CheckPathsAndExtractDistances(
&db_accessor, edges_in_result,
std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
}
break;
case memgraph::query::EdgeAtom::Direction::OUT:
if (known_sink) {
CheckPathsAndExtractDistances(
&db_accessor, edges_in_result,
std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
} else {
CheckPathsAndExtractDistances(
&db_accessor, edges_in_result,
std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
}
break;
case memgraph::query::EdgeAtom::Direction::BOTH:
if (known_sink) {
CheckPathsAndExtractDistances(
&db_accessor, edges_in_result,
std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
} else {
CheckPathsAndExtractDistances(
&db_accessor, edges_in_result,
std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
}
break;
}
break;
case FineGrainedTestType::LABEL_3_DENIED:
switch (direction) {
case memgraph::query::EdgeAtom::Direction::IN:
if (known_sink) {
CheckPathsAndExtractDistances(
&db_accessor, edges_in_result,
std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
} else {
CheckPathsAndExtractDistances(
&db_accessor, edges_in_result,
std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
}
break;
case memgraph::query::EdgeAtom::Direction::OUT:
if (known_sink) {
CheckPathsAndExtractDistances(
&db_accessor, edges_in_result,
std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
} else {
CheckPathsAndExtractDistances(
&db_accessor, edges_in_result,
std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
}
break;
case memgraph::query::EdgeAtom::Direction::BOTH:
if (known_sink) {
CheckPathsAndExtractDistances(
&db_accessor, edges_in_result,
std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
} else {
CheckPathsAndExtractDistances(
&db_accessor, edges_in_result,
std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
}
break;
}
break;
}
db_accessor.Abort();
}

View File

@ -0,0 +1,110 @@
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
// License, and you may not use this file except in compliance with the Business Source License.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
#include "bfs_common.hpp"
#include <optional>
#include <string>
#include <unordered_map>
#include <gtest/internal/gtest-param-util-generated.h>
#include "auth/models.hpp"
using namespace memgraph::query;
using namespace memgraph::query::plan;
class VertexDb : public Database {
public:
VertexDb() : db_() {}
memgraph::storage::Storage::Accessor Access() override { return db_.Access(); }
std::unique_ptr<LogicalOperator> MakeBfsOperator(Symbol source_sym, Symbol sink_sym, Symbol edge_sym,
EdgeAtom::Direction direction,
const std::vector<memgraph::storage::EdgeTypeId> &edge_types,
const std::shared_ptr<LogicalOperator> &input, bool existing_node,
Expression *lower_bound, Expression *upper_bound,
const ExpansionLambda &filter_lambda) override {
return std::make_unique<ExpandVariable>(input, source_sym, sink_sym, edge_sym, EdgeAtom::Type::BREADTH_FIRST,
direction, edge_types, false, lower_bound, upper_bound, existing_node,
filter_lambda, std::nullopt, std::nullopt);
}
std::pair<std::vector<memgraph::query::VertexAccessor>, std::vector<memgraph::query::EdgeAccessor>> BuildGraph(
memgraph::query::DbAccessor *dba, const std::vector<int> &vertex_locations,
const std::vector<std::tuple<int, int, std::string>> &edges) override {
std::vector<memgraph::query::VertexAccessor> vertex_addr;
std::vector<memgraph::query::EdgeAccessor> edge_addr;
for (size_t id = 0; id < vertex_locations.size(); ++id) {
auto vertex = dba->InsertVertex();
MG_ASSERT(
vertex.SetProperty(dba->NameToProperty("id"), memgraph::storage::PropertyValue(static_cast<int64_t>(id)))
.HasValue());
MG_ASSERT(vertex.AddLabel(dba->NameToLabel(std::to_string(id))).HasValue());
vertex_addr.push_back(vertex);
}
for (auto e : edges) {
int u, v;
std::string type;
std::tie(u, v, type) = e;
auto &from = vertex_addr[u];
auto &to = vertex_addr[v];
auto edge = dba->InsertEdge(&from, &to, dba->NameToEdgeType(type));
MG_ASSERT(edge->SetProperty(dba->NameToProperty("from"), memgraph::storage::PropertyValue(u)).HasValue());
MG_ASSERT(edge->SetProperty(dba->NameToProperty("to"), memgraph::storage::PropertyValue(v)).HasValue());
edge_addr.push_back(*edge);
}
return std::make_pair(vertex_addr, edge_addr);
}
protected:
memgraph::storage::Storage db_;
};
class FineGrainedBfsTest
: public ::testing::TestWithParam<
std::tuple<int, int, EdgeAtom::Direction, std::vector<std::string>, bool, FineGrainedTestType>> {
public:
static void SetUpTestCase() { db_ = std::make_unique<VertexDb>(); }
static void TearDownTestCase() { db_ = nullptr; }
protected:
static std::unique_ptr<VertexDb> db_;
};
TEST_P(FineGrainedBfsTest, All) {
int lower_bound;
int upper_bound;
EdgeAtom::Direction direction;
std::vector<std::string> edge_types;
bool known_sink;
FineGrainedTestType fine_grained_test_type;
std::tie(lower_bound, upper_bound, direction, edge_types, known_sink, fine_grained_test_type) = GetParam();
BfsTestWithFineGrainedFiltering(db_.get(), lower_bound, upper_bound, direction, edge_types, known_sink,
fine_grained_test_type);
}
std::unique_ptr<VertexDb> FineGrainedBfsTest::db_{nullptr};
INSTANTIATE_TEST_CASE_P(
FineGrained, FineGrainedBfsTest,
testing::Combine(testing::Values(3), testing::Values(-1),
testing::Values(EdgeAtom::Direction::OUT, EdgeAtom::Direction::IN, EdgeAtom::Direction::BOTH),
testing::Values(std::vector<std::string>{}), testing::Bool(),
testing::Values(FineGrainedTestType::ALL_GRANTED, FineGrainedTestType::ALL_DENIED,
FineGrainedTestType::EDGE_TYPE_A_DENIED, FineGrainedTestType::EDGE_TYPE_B_DENIED,
FineGrainedTestType::LABEL_0_DENIED, FineGrainedTestType::LABEL_3_DENIED)));

File diff suppressed because it is too large Load Diff

View File

@ -9,6 +9,8 @@
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
#include "query_plan_common.hpp"
#include <iterator>
#include <memory>
#include <optional>
@ -31,8 +33,6 @@
#include "query/context.hpp"
#include "query/exceptions.hpp"
#include "query/plan/operator.hpp"
#include "query_plan_common.hpp"
#include "utils/synchronized.hpp"
using namespace memgraph::query;
@ -56,6 +56,15 @@ class MatchReturnFixture : public testing::Test {
for (const auto &row : CollectProduce(*op, &context)) res.emplace_back(row[0].ValuePath());
return res;
}
int PullCountAuthorized(ScanAllTuple scan_all, memgraph::auth::User user) {
auto output =
NEXPR("n", IDENT("n")->MapTo(scan_all.sym_))->MapTo(symbol_table.CreateSymbol("named_expression_1", true));
auto produce = MakeProduce(scan_all.op_, output);
memgraph::glue::FineGrainedAuthChecker auth_checker{user};
auto context = MakeContextWithFineGrainedChecker(storage, symbol_table, &dba, &auth_checker);
return PullAll(*produce, &context);
}
};
TEST_F(MatchReturnFixture, MatchReturn) {
@ -98,6 +107,92 @@ TEST_F(MatchReturnFixture, MatchReturnPath) {
EXPECT_TRUE(std::is_permutation(expected_paths.begin(), expected_paths.end(), results.begin()));
}
TEST_F(MatchReturnFixture, ScanAllWithAuthChecker) {
std::string labelName = "l1";
const auto label = dba.NameToLabel(labelName);
ASSERT_TRUE(dba.InsertVertex().AddLabel(label).HasValue());
dba.AdvanceCommand();
auto test_hypothesis = [&](memgraph::auth::User user, memgraph::storage::View view, int expected_pull_count) {
auto scan_all = MakeScanAll(storage, symbol_table, "n", nullptr, view);
ASSERT_EQ(expected_pull_count, PullCountAuthorized(scan_all, user));
scan_all = MakeScanAll(storage, symbol_table, "n", nullptr, view);
ASSERT_EQ(expected_pull_count, PullCountAuthorized(scan_all, user));
};
{
auto user = memgraph::auth::User{"grant_global"};
user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
test_hypothesis(user, memgraph::storage::View::OLD, 1);
test_hypothesis(user, memgraph::storage::View::NEW, 1);
}
{
auto user = memgraph::auth::User{"deny_global"};
user.fine_grained_access_handler().label_permissions().Deny("*", memgraph::auth::FineGrainedPermission::READ);
test_hypothesis(user, memgraph::storage::View::OLD, 0);
test_hypothesis(user, memgraph::storage::View::NEW, 0);
}
{
auto user = memgraph::auth::User{"grant_label_read"};
user.fine_grained_access_handler().label_permissions().Grant(labelName,
memgraph::auth::FineGrainedPermission::READ);
test_hypothesis(user, memgraph::storage::View::OLD, 1);
test_hypothesis(user, memgraph::storage::View::NEW, 1);
}
{
auto user = memgraph::auth::User{"deny_label_read"};
user.fine_grained_access_handler().label_permissions().Deny(labelName, memgraph::auth::FineGrainedPermission::READ);
test_hypothesis(user, memgraph::storage::View::OLD, 0);
test_hypothesis(user, memgraph::storage::View::NEW, 0);
}
{
auto user = memgraph::auth::User{"grant_global_deny_label"};
user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Deny(labelName, memgraph::auth::FineGrainedPermission::READ);
test_hypothesis(user, memgraph::storage::View::OLD, 0);
test_hypothesis(user, memgraph::storage::View::NEW, 0);
}
{
auto user = memgraph::auth::User{"deny_global_grant_label"};
user.fine_grained_access_handler().label_permissions().Deny("*", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Grant(labelName,
memgraph::auth::FineGrainedPermission::READ);
test_hypothesis(user, memgraph::storage::View::OLD, 1);
test_hypothesis(user, memgraph::storage::View::NEW, 1);
}
{
auto user = memgraph::auth::User{"global_update_deny_label"};
user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::UPDATE);
user.fine_grained_access_handler().label_permissions().Deny(labelName, memgraph::auth::FineGrainedPermission::READ);
test_hypothesis(user, memgraph::storage::View::OLD, 0);
test_hypothesis(user, memgraph::storage::View::NEW, 0);
}
{
auto user = memgraph::auth::User{"global_create_delete_deny_label"};
user.fine_grained_access_handler().label_permissions().Grant("*",
memgraph::auth::FineGrainedPermission::CREATE_DELETE);
user.fine_grained_access_handler().label_permissions().Deny(labelName, memgraph::auth::FineGrainedPermission::READ);
test_hypothesis(user, memgraph::storage::View::OLD, 0);
test_hypothesis(user, memgraph::storage::View::NEW, 0);
}
}
TEST(QueryPlan, MatchReturnCartesian) {
memgraph::storage::Storage db;
auto storage_dba = db.Access();
@ -510,8 +605,6 @@ class QueryPlanExpandVariable : public testing::Test {
memgraph::query::DbAccessor dba{&storage_dba};
// labels for layers in the double chain
std::vector<memgraph::storage::LabelId> labels;
// for all the edges
memgraph::storage::EdgeTypeId edge_type = dba.NameToEdgeType("edge_type");
AstStorage storage;
SymbolTable symbol_table;
@ -532,7 +625,10 @@ class QueryPlanExpandVariable : public testing::Test {
ASSERT_TRUE(v_to.AddLabel(label).HasValue());
for (size_t v_from_ind = 0; v_from_ind < layer.size(); v_from_ind++) {
auto &v_from = layer[v_from_ind];
auto edge = dba.InsertEdge(&v_from, &v_to, edge_type);
auto edge_type = "edge_type_" + std::to_string(from_layer_ind + 1);
memgraph::storage::EdgeTypeId edge_type_id = dba.NameToEdgeType(edge_type);
auto edge = dba.InsertEdge(&v_from, &v_to, edge_type_id);
ASSERT_TRUE(edge->SetProperty(dba.NameToProperty("p"),
memgraph::storage::PropertyValue(fmt::format(
"V{}{}->V{}{}", from_layer_ind, v_from_ind, from_layer_ind + 1, v_to_ind)))
@ -602,10 +698,16 @@ class QueryPlanExpandVariable : public testing::Test {
/**
* Pulls from the given input and returns the results under the given symbol.
*/
auto GetListResults(std::shared_ptr<LogicalOperator> input_op, Symbol symbol) {
auto GetListResults(std::shared_ptr<LogicalOperator> input_op, Symbol symbol, memgraph::auth::User *user = nullptr) {
Frame frame(symbol_table.max_position());
auto cursor = input_op->MakeCursor(memgraph::utils::NewDeleteResource());
auto context = MakeContext(storage, symbol_table, &dba);
ExecutionContext context;
if (user) {
memgraph::glue::FineGrainedAuthChecker auth_checker{*user};
context = MakeContextWithFineGrainedChecker(storage, symbol_table, &dba, &auth_checker);
} else {
context = MakeContext(storage, symbol_table, &dba);
}
std::vector<memgraph::utils::pmr::vector<TypedValue>> results;
while (cursor->Pull(frame, context)) results.emplace_back(frame[symbol].ValueList());
return results;
@ -614,10 +716,16 @@ class QueryPlanExpandVariable : public testing::Test {
/**
* Pulls from the given input and returns the results under the given symbol.
*/
auto GetPathResults(std::shared_ptr<LogicalOperator> input_op, Symbol symbol) {
auto GetPathResults(std::shared_ptr<LogicalOperator> input_op, Symbol symbol, memgraph::auth::User *user = nullptr) {
Frame frame(symbol_table.max_position());
auto cursor = input_op->MakeCursor(memgraph::utils::NewDeleteResource());
auto context = MakeContext(storage, symbol_table, &dba);
ExecutionContext context;
if (user) {
memgraph::glue::FineGrainedAuthChecker auth_checker{*user};
context = MakeContextWithFineGrainedChecker(storage, symbol_table, &dba, &auth_checker);
} else {
context = MakeContext(storage, symbol_table, &dba);
}
std::vector<Path> results;
while (cursor->Pull(frame, context)) results.emplace_back(frame[symbol].ValuePath());
return results;
@ -629,9 +737,10 @@ class QueryPlanExpandVariable : public testing::Test {
*
* @return a map {edge_list_length -> number_of_results}
*/
auto GetEdgeListSizes(std::shared_ptr<LogicalOperator> input_op, Symbol symbol) {
auto GetEdgeListSizes(std::shared_ptr<LogicalOperator> input_op, Symbol symbol,
memgraph::auth::User *user = nullptr) {
map_int count_per_length;
for (const auto &edge_list : GetListResults(input_op, symbol)) {
for (const auto &edge_list : GetListResults(input_op, symbol, user)) {
auto length = edge_list.size();
auto found = count_per_length.find(length);
if (found == count_per_length.end())
@ -679,6 +788,208 @@ TEST_F(QueryPlanExpandVariable, OneVariableExpansion) {
}
}
TEST_F(QueryPlanExpandVariable, FineGrainedOneVariableExpansion) {
auto test_expand = [&](int layer, EdgeAtom::Direction direction, std::optional<size_t> lower,
std::optional<size_t> upper, bool reverse, memgraph::auth::User &user) {
auto e = Edge("r", direction);
return GetEdgeListSizes(AddMatch<ExpandVariable>(nullptr, "n", layer, direction, {}, lower, upper, e, "m",
memgraph::storage::View::OLD, reverse),
e, &user);
};
// All labels, All edge types granted
{
memgraph::auth::User user{"test"};
user.fine_grained_access_handler().edge_type_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
for (auto reverse : {false, true}) {
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::IN, 0, 0, reverse, user), (map_int{{0, 2}}));
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, 0, 0, reverse, user), (map_int{{0, 2}}));
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::BOTH, 0, 0, reverse, user), (map_int{{0, 2}}));
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::IN, 1, 1, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, 1, 1, reverse, user), (map_int{{1, 4}}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, 1, 1, reverse, user), (map_int{{1, 4}}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, 1, 1, reverse, user), (map_int{{1, 4}}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, 1, 1, reverse, user), (map_int{{1, 8}}));
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, 2, 2, reverse, user), (map_int{{2, 8}}));
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, 2, 3, reverse, user), (map_int{{2, 8}}));
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, 1, 2, reverse, user), (map_int{{1, 4}, {2, 8}}));
// the following tests also check edge-uniqueness (cyphermorphisim)
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::BOTH, 1, 2, reverse, user), (map_int{{1, 4}, {2, 12}}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, 4, 4, reverse, user), (map_int{{4, 24}}));
// default bound values (lower default is 1, upper default is inf)
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, nullopt, 0, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, nullopt, 1, reverse, user), (map_int{{1, 4}}));
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, nullopt, 2, reverse, user), (map_int{{1, 4}, {2, 8}}));
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::BOTH, 7, nullopt, reverse, user), (map_int{{7, 24}, {8, 24}}));
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::BOTH, 8, nullopt, reverse, user), (map_int{{8, 24}}));
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::BOTH, 9, nullopt, reverse, user), (map_int{}));
}
}
// All labels, All edge types denied
{
memgraph::auth::User user{"test"};
user.fine_grained_access_handler().edge_type_permissions().Deny("*", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Deny("*", memgraph::auth::FineGrainedPermission::READ);
for (auto reverse : {false, true}) {
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::IN, 1, nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, 1, nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::BOTH, 1, nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, 1, nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, 1, nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, 1, nullopt, reverse, user), (map_int{}));
}
}
// All labels granted, All edge types denied
{
memgraph::auth::User user{"test"};
user.fine_grained_access_handler().edge_type_permissions().Deny("*", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
for (auto reverse : {false, true}) {
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::IN, 0, nullopt, reverse, user), (map_int{{0, 2}}));
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, 0, nullopt, reverse, user), (map_int{{0, 2}}));
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::BOTH, 0, nullopt, reverse, user), (map_int{{0, 2}}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, 0, nullopt, reverse, user), (map_int{{0, 2}}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, 0, nullopt, reverse, user), (map_int{{0, 2}}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, 0, nullopt, reverse, user), (map_int{{0, 2}}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, 1, nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, 1, nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, 1, nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::IN, 1, nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, 1, nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::BOTH, 1, nullopt, reverse, user), (map_int{}));
}
}
// All labels denied, All edge types granted
{
memgraph::auth::User user{"test"};
user.fine_grained_access_handler().edge_type_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Deny("*", memgraph::auth::FineGrainedPermission::READ);
for (auto reverse : {false, true}) {
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::IN, 1, nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, 1, nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::BOTH, 1, nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, 1, nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, 1, nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, 1, nullopt, reverse, user), (map_int{}));
}
}
// Layer 1 labels denied, All edge types granted
{
memgraph::auth::User user{"test"};
user.fine_grained_access_handler().edge_type_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Grant("0", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Grant("2", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Deny("1", memgraph::auth::FineGrainedPermission::READ);
for (auto reverse : {false, true}) {
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::IN, 0, nullopt, reverse, user), (map_int{{0, 2}}));
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, 0, nullopt, reverse, user), (map_int{{0, 2}}));
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::BOTH, 0, nullopt, reverse, user), (map_int{{0, 2}}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, 0, nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, 0, nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, 0, nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, 2, nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, 2, nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, 2, nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::IN, 1, nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, 1, nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::BOTH, 1, nullopt, reverse, user), (map_int{}));
}
}
// All labels granted, Edge types from layer 0 to layer 1 denied
{
memgraph::auth::User user{"test"};
user.fine_grained_access_handler().edge_type_permissions().Deny("edge_type_1",
memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().edge_type_permissions().Grant("edge_type_2",
memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
for (auto reverse : {false, true}) {
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::IN, 0, nullopt, reverse, user), (map_int{{0, 2}}));
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, 0, nullopt, reverse, user), (map_int{{0, 2}}));
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::BOTH, 0, nullopt, reverse, user), (map_int{{0, 2}}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, 0, nullopt, reverse, user), (map_int{{0, 2}}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, 0, nullopt, reverse, user), (map_int{{1, 4}, {0, 2}}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, 0, nullopt, reverse, user),
(map_int{{4, 4}, {3, 4}, {2, 4}, {1, 4}, {0, 2}}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, 1, nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, 1, nullopt, reverse, user), (map_int{{1, 4}}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, 1, nullopt, reverse, user),
(map_int{{1, 4}, {2, 4}, {3, 4}, {4, 4}}));
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::IN, 1, nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, 1, nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::BOTH, 1, nullopt, reverse, user), (map_int{}));
}
}
// Layer 2 labels denied, All edge types granted
{
memgraph::auth::User user{"test"};
user.fine_grained_access_handler().edge_type_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Grant("0", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Grant("1", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Deny("2", memgraph::auth::FineGrainedPermission::READ);
for (auto reverse : {false, true}) {
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::IN, 0, nullopt, reverse, user), (map_int{{0, 2}}));
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, 0, nullopt, reverse, user), (map_int{{0, 2}, {1, 4}}));
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::BOTH, 0, nullopt, reverse, user),
(map_int{{4, 4}, {3, 4}, {2, 4}, {1, 4}, {0, 2}}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, 0, nullopt, reverse, user), (map_int{{1, 4}, {0, 2}}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, 0, nullopt, reverse, user), (map_int{{0, 2}}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, 0, nullopt, reverse, user),
(map_int{{4, 4}, {3, 4}, {2, 4}, {1, 4}, {0, 2}}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, 1, nullopt, reverse, user), (map_int{{1, 4}}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, 1, nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, 1, nullopt, reverse, user),
(map_int{{1, 4}, {2, 4}, {3, 4}, {4, 4}}));
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::IN, 1, nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, 1, nullopt, reverse, user), (map_int{{1, 4}}));
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::BOTH, 1, nullopt, reverse, user),
(map_int{{4, 4}, {3, 4}, {2, 4}, {1, 4}}));
}
}
// All labels granted, Edge types from layer 1 to layer 2 denied
{
memgraph::auth::User user{"test"};
user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().edge_type_permissions().Grant("edge_type_1",
memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().edge_type_permissions().Deny("edge_type_2",
memgraph::auth::FineGrainedPermission::READ);
for (auto reverse : {false, true}) {
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::IN, 0, nullopt, reverse, user), (map_int{{0, 2}}));
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, 0, nullopt, reverse, user), (map_int{{1, 4}, {0, 2}}));
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::BOTH, 0, nullopt, reverse, user),
(map_int{{4, 4}, {3, 4}, {2, 4}, {1, 4}, {0, 2}}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, 0, nullopt, reverse, user), (map_int{{1, 4}, {0, 2}}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, 0, nullopt, reverse, user), (map_int{{0, 2}}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, 0, nullopt, reverse, user),
(map_int{{4, 4}, {3, 4}, {2, 4}, {1, 4}, {0, 2}}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, 1, nullopt, reverse, user), (map_int{{1, 4}}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, 1, nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, 1, nullopt, reverse, user),
(map_int{{1, 4}, {2, 4}, {3, 4}, {4, 4}}));
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::IN, 1, nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, 1, nullopt, reverse, user), (map_int{{1, 4}}));
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::BOTH, 1, nullopt, reverse, user),
(map_int{{4, 4}, {3, 4}, {2, 4}, {1, 4}}));
}
}
}
TEST_F(QueryPlanExpandVariable, EdgeUniquenessSingleAndVariableExpansion) {
auto test_expand = [&](int layer, EdgeAtom::Direction direction, std::optional<size_t> lower,
std::optional<size_t> upper, bool single_expansion_before, bool add_uniqueness_check) {
@ -738,6 +1049,97 @@ TEST_F(QueryPlanExpandVariable, EdgeUniquenessTwoVariableExpansions) {
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, 2, 2, true), (map_int{{2, 5 * 8}}));
}
TEST_F(QueryPlanExpandVariable, FineGrainedEdgeUniquenessTwoVariableExpansions) {
auto test_expand = [&](int layer, EdgeAtom::Direction direction, std::optional<size_t> lower,
std::optional<size_t> upper, bool add_uniqueness_check, memgraph::auth::User &user) {
auto e1 = Edge("r1", direction);
auto first = AddMatch<ExpandVariable>(nullptr, "n1", layer, direction, {}, lower, upper, e1, "m1",
memgraph::storage::View::OLD);
auto e2 = Edge("r2", direction);
auto last_op = AddMatch<ExpandVariable>(first, "n2", layer, direction, {}, lower, upper, e2, "m2",
memgraph::storage::View::OLD);
if (add_uniqueness_check) {
last_op = std::make_shared<EdgeUniquenessFilter>(last_op, e2, std::vector<Symbol>{e1});
}
return GetEdgeListSizes(last_op, e2, &user);
};
// All labels granted, All edge types granted
{
memgraph::auth::User user{"test"};
user.fine_grained_access_handler().edge_type_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, 2, 2, false, user), (map_int{{2, 8 * 8}}));
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, 2, 2, true, user), (map_int{{2, 5 * 8}}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, 0, 2, true, user), (map_int{{1, 20}, {0, 12}}));
}
// All labels denied, All edge types denied
{
memgraph::auth::User user{"test"};
user.fine_grained_access_handler().edge_type_permissions().Deny("*", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Deny("*", memgraph::auth::FineGrainedPermission::READ);
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, 2, 2, false, user), (map_int{}));
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, 2, 2, true, user), (map_int{}));
}
// Layer 1 label denied, All edge types denied
{
memgraph::auth::User user{"test"};
user.fine_grained_access_handler().edge_type_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Grant("0", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Grant("2", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Deny("1", memgraph::auth::FineGrainedPermission::READ);
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, 0, 2, false, user), (map_int{{0, 4}}));
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, 0, 2, true, user), (map_int{{0, 4}}));
}
// Layer 2 label denied, All edge types denied
{
memgraph::auth::User user{"test"};
user.fine_grained_access_handler().edge_type_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Grant("0", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Grant("1", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Deny("2", memgraph::auth::FineGrainedPermission::READ);
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, 0, 2, false, user), (map_int{{1, 4}, {0, 2}}));
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, 0, 2, true, user), (map_int{{1, 4}, {0, 2}}));
}
// All labels granted, Edge type between layer 0 and layer 1 denied
{
memgraph::auth::User user{"test"};
user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().edge_type_permissions().Deny("edge_type_1",
memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().edge_type_permissions().Grant("edge_type_2",
memgraph::auth::FineGrainedPermission::READ);
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, 0, 2, false, user), (map_int{{0, 4}}));
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, 0, 2, true, user), (map_int{{0, 4}}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, 0, 2, false, user), (map_int{{1, 24}, {0, 12}}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, 0, 2, true, user), (map_int{{1, 20}, {0, 12}}));
}
// All labels granted, Edge type between layer 1 and layer 2 denied
{
memgraph::auth::User user{"test"};
user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().edge_type_permissions().Grant("edge_type_1",
memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().edge_type_permissions().Deny("edge_type_2",
memgraph::auth::FineGrainedPermission::READ);
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, 0, 2, false, user), (map_int{{1, 24}, {0, 12}}));
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, 0, 2, true, user), (map_int{{1, 20}, {0, 12}}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, 0, 2, false, user), (map_int{{0, 4}}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, 0, 2, true, user), (map_int{{0, 4}}));
}
}
TEST_F(QueryPlanExpandVariable, NamedPath) {
auto e = Edge("r", EdgeAtom::Direction::OUT);
auto expand = AddMatch<ExpandVariable>(nullptr, "n", 0, EdgeAtom::Direction::OUT, {}, 2, 2, e, "m",
@ -770,6 +1172,145 @@ TEST_F(QueryPlanExpandVariable, NamedPath) {
EXPECT_TRUE(std::is_permutation(results.begin(), results.end(), expected_paths.begin()));
}
TEST_F(QueryPlanExpandVariable, FineGrainedFilterNamedPath) {
auto e = Edge("r", EdgeAtom::Direction::OUT);
auto expand = AddMatch<ExpandVariable>(nullptr, "n", 0, EdgeAtom::Direction::OUT, {}, 0, 2, e, "m",
memgraph::storage::View::OLD);
auto find_symbol = [this](const std::string &name) {
for (const auto &sym : symbol_table.table())
if (sym.second.name() == name) return sym.second;
throw std::runtime_error("Symbol not found");
};
auto path_symbol = symbol_table.CreateSymbol("path", true, Symbol::Type::PATH);
auto create_path = std::make_shared<ConstructNamedPath>(expand, path_symbol,
std::vector<Symbol>{find_symbol("n"), e, find_symbol("m")});
// All labels and edge types granted
{
memgraph::auth::User user{"test"};
user.fine_grained_access_handler().edge_type_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
auto results = GetPathResults(create_path, path_symbol, &user);
ASSERT_EQ(results.size(), 14);
}
// All labels and edge types denied
{
memgraph::auth::User user{"test"};
user.fine_grained_access_handler().edge_type_permissions().Deny("*", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Deny("*", memgraph::auth::FineGrainedPermission::READ);
auto results = GetPathResults(create_path, path_symbol, &user);
ASSERT_EQ(results.size(), 0);
}
// All labels denied, All edge types granted
{
memgraph::auth::User user{"test"};
user.fine_grained_access_handler().edge_type_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Deny("*", memgraph::auth::FineGrainedPermission::READ);
auto results = GetPathResults(create_path, path_symbol, &user);
ASSERT_EQ(results.size(), 0);
}
// All labels granted, All edge types denied
{
memgraph::auth::User user{"test"};
user.fine_grained_access_handler().edge_type_permissions().Deny("*", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
auto results = GetPathResults(create_path, path_symbol, &user);
ASSERT_EQ(results.size(), 2);
}
// 0 layer label denied
{
memgraph::auth::User user{"test"};
user.fine_grained_access_handler().edge_type_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Deny("0", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Grant("1", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Grant("2", memgraph::auth::FineGrainedPermission::READ);
auto results = GetPathResults(create_path, path_symbol, &user);
ASSERT_EQ(results.size(), 0);
}
// First layer label denied
{
memgraph::auth::User user{"test"};
user.fine_grained_access_handler().edge_type_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Grant("0", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Deny("1", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Grant("2", memgraph::auth::FineGrainedPermission::READ);
auto results = GetPathResults(create_path, path_symbol, &user);
ASSERT_EQ(results.size(), 2);
}
// Second layer label denied
{
memgraph::auth::User user{"test"};
user.fine_grained_access_handler().edge_type_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Grant("0", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Grant("1", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Deny("2", memgraph::auth::FineGrainedPermission::READ);
auto results = GetPathResults(create_path, path_symbol, &user);
ASSERT_EQ(results.size(), 6);
std::vector<memgraph::query::Path> expected_paths;
for (const auto &v : dba.Vertices(memgraph::storage::View::OLD)) {
if (!*v.HasLabel(memgraph::storage::View::OLD, labels[0])) continue;
expected_paths.emplace_back(v);
auto maybe_edges1 = v.OutEdges(memgraph::storage::View::OLD);
for (const auto &e1 : *maybe_edges1) {
expected_paths.emplace_back(v, e1, e1.To());
}
}
EXPECT_TRUE(std::is_permutation(results.begin(), results.end(), expected_paths.begin()));
}
// First layer edge type denied
{
memgraph::auth::User user{"test"};
user.fine_grained_access_handler().edge_type_permissions().Deny("edge_type_1",
memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().edge_type_permissions().Grant("edge_type_2",
memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
auto results = GetPathResults(create_path, path_symbol, &user);
ASSERT_EQ(results.size(), 2);
}
// Second layer edge type denied
{
memgraph::auth::User user{"test"};
user.fine_grained_access_handler().edge_type_permissions().Grant("edge_type_1",
memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().edge_type_permissions().Deny("edge_type_2",
memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
auto results = GetPathResults(create_path, path_symbol, &user);
ASSERT_EQ(results.size(), 6);
std::vector<memgraph::query::Path> expected_paths;
for (const auto &v : dba.Vertices(memgraph::storage::View::OLD)) {
if (!*v.HasLabel(memgraph::storage::View::OLD, labels[0])) continue;
expected_paths.emplace_back(v);
auto maybe_edges1 = v.OutEdges(memgraph::storage::View::OLD);
for (const auto &e1 : *maybe_edges1) {
expected_paths.emplace_back(v, e1, e1.To());
}
}
EXPECT_TRUE(std::is_permutation(results.begin(), results.end(), expected_paths.begin()));
}
}
TEST_F(QueryPlanExpandVariable, ExpandToSameSymbol) {
auto test_expand = [&](int layer, EdgeAtom::Direction direction, std::optional<size_t> lower,
std::optional<size_t> upper, bool reverse) {
@ -960,6 +1501,205 @@ TEST_F(QueryPlanExpandVariable, ExpandToSameSymbol) {
}
}
TEST_F(QueryPlanExpandVariable, FineGrainedExpandToSameSymbol) {
auto test_expand = [&](int layer, EdgeAtom::Direction direction, std::optional<size_t> lower,
std::optional<size_t> upper, bool reverse, memgraph::auth::User &user) {
auto e = Edge("r", direction);
auto node = NODE("n");
auto symbol = symbol_table.CreateSymbol("n", true);
node->identifier_->MapTo(symbol);
auto logical_op = std::make_shared<ScanAll>(nullptr, symbol, memgraph::storage::View::OLD);
auto n_from = ScanAllTuple{node, logical_op, symbol};
auto filter_op = std::make_shared<Filter>(
n_from.op_,
storage.Create<memgraph::query::LabelsTest>(
n_from.node_->identifier_, std::vector<LabelIx>{storage.GetLabelIx(dba.LabelToName(labels[layer]))}));
// convert optional ints to optional expressions
auto convert = [this](std::optional<size_t> bound) {
return bound ? LITERAL(static_cast<int64_t>(bound.value())) : nullptr;
};
return GetEdgeListSizes(std::make_shared<ExpandVariable>(
filter_op, symbol, symbol, e, EdgeAtom::Type::DEPTH_FIRST, direction,
std::vector<memgraph::storage::EdgeTypeId>{}, reverse, convert(lower), convert(upper),
/* existing = */ true,
ExpansionLambda{symbol_table.CreateSymbol("inner_edge", false),
symbol_table.CreateSymbol("inner_node", false), nullptr},
std::nullopt, std::nullopt),
e, &user);
};
// All labels granted, All edge types granted
{
memgraph::auth::User user{"test"};
user.fine_grained_access_handler().edge_type_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
for (auto reverse : {false, true}) {
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::IN, std::nullopt, std::nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, std::nullopt, std::nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::BOTH, std::nullopt, std::nullopt, reverse, user),
(map_int{{4, 12}, {8, 24}}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, std::nullopt, std::nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, std::nullopt, std::nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, std::nullopt, std::nullopt, reverse, user),
(map_int{{4, 24}, {8, 48}}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, 4, std::nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, 4, std::nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, 4, std::nullopt, reverse, user), (map_int{{4, 24}, {8, 48}}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, std::nullopt, 8, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, std::nullopt, 8, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, std::nullopt, 8, reverse, user), (map_int{{4, 24}, {8, 48}}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, 4, 8, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, 4, 8, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, 4, 8, reverse, user), (map_int{{4, 24}, {8, 48}}));
}
}
// All labels denied, All edge types denied
{
memgraph::auth::User user{"test"};
user.fine_grained_access_handler().edge_type_permissions().Deny("*", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Deny("*", memgraph::auth::FineGrainedPermission::READ);
for (auto reverse : {false, true}) {
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::IN, std::nullopt, std::nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, std::nullopt, std::nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::BOTH, std::nullopt, std::nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, std::nullopt, std::nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, std::nullopt, std::nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, std::nullopt, std::nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, 4, std::nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, 4, std::nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, 4, std::nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, std::nullopt, 8, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, std::nullopt, 8, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, std::nullopt, 8, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, 4, 8, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, 4, 8, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, 4, 8, reverse, user), (map_int{}));
}
}
// First layer label denied, all edge types granted
{
memgraph::auth::User user{"test"};
user.fine_grained_access_handler().edge_type_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Grant("0", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Deny("1", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Grant("2", memgraph::auth::FineGrainedPermission::READ);
for (auto reverse : {false, true}) {
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::IN, std::nullopt, std::nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, std::nullopt, std::nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::BOTH, std::nullopt, std::nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, std::nullopt, std::nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, std::nullopt, std::nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, std::nullopt, std::nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, 4, std::nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, 4, std::nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, 4, std::nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, std::nullopt, 8, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, std::nullopt, 8, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, std::nullopt, 8, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, 4, 8, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, 4, 8, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, 4, 8, reverse, user), (map_int{}));
}
}
// Second layer label denied, all edge types granted
{
memgraph::auth::User user{"test"};
user.fine_grained_access_handler().edge_type_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Grant("0", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Grant("1", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Deny("2", memgraph::auth::FineGrainedPermission::READ);
for (auto reverse : {false, true}) {
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::IN, std::nullopt, std::nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, std::nullopt, std::nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::BOTH, std::nullopt, std::nullopt, reverse, user),
(map_int{{4, 4}}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, std::nullopt, std::nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, std::nullopt, std::nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, std::nullopt, std::nullopt, reverse, user),
(map_int{{4, 4}}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, 4, std::nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, 4, std::nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, 4, std::nullopt, reverse, user), (map_int{{4, 4}}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, std::nullopt, 8, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, std::nullopt, 8, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, std::nullopt, 8, reverse, user), (map_int{{4, 4}}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, 4, 8, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, 4, 8, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, 4, 8, reverse, user), (map_int{{4, 4}}));
}
}
// All labels granted, Edge type from layer 0 to layer 1 denied
{
memgraph::auth::User user{"test"};
user.fine_grained_access_handler().edge_type_permissions().Deny("edge_type_1",
memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().edge_type_permissions().Grant("edge_type_2",
memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
for (auto reverse : {false, true}) {
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::IN, std::nullopt, std::nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, std::nullopt, std::nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::BOTH, std::nullopt, std::nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, std::nullopt, std::nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, std::nullopt, std::nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, std::nullopt, std::nullopt, reverse, user),
(map_int{{4, 4}}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, 4, std::nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, 4, std::nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, 4, std::nullopt, reverse, user), (map_int{{4, 4}}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, std::nullopt, 8, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, std::nullopt, 8, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, std::nullopt, 8, reverse, user), (map_int{{4, 4}}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, 4, 8, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, 4, 8, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, 4, 8, reverse, user), (map_int{{4, 4}}));
}
}
// All labels granted, Edge type from layer 1 to layer 2 denied
{
memgraph::auth::User user{"test"};
user.fine_grained_access_handler().edge_type_permissions().Grant("edge_type_1",
memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().edge_type_permissions().Deny("edge_type_2",
memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
for (auto reverse : {false, true}) {
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::IN, std::nullopt, std::nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, std::nullopt, std::nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(0, EdgeAtom::Direction::BOTH, std::nullopt, std::nullopt, reverse, user),
(map_int{{4, 4}}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, std::nullopt, std::nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, std::nullopt, std::nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, std::nullopt, std::nullopt, reverse, user),
(map_int{{4, 4}}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, 4, std::nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, 4, std::nullopt, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, 4, std::nullopt, reverse, user), (map_int{{4, 4}}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, std::nullopt, 8, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, std::nullopt, 8, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, std::nullopt, 8, reverse, user), (map_int{{4, 4}}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, 4, 8, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, 4, 8, reverse, user), (map_int{}));
EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, 4, 8, reverse, user), (map_int{{4, 4}}));
}
}
}
namespace std {
template <>
struct hash<std::pair<int, int>> {
@ -1006,6 +1746,8 @@ class QueryPlanExpandWeightedShortestPath : public testing::Test {
for (int i = 0; i < 5; i++) {
v.push_back(dba.InsertVertex());
ASSERT_TRUE(v.back().SetProperty(prop.second, memgraph::storage::PropertyValue(i)).HasValue());
auto label = fmt::format("l{}", i);
ASSERT_TRUE(v.back().AddLabel(db.NameToLabel(label)).HasValue());
}
auto add_edge = [&](int from, int to, double weight) {
@ -1028,7 +1770,8 @@ class QueryPlanExpandWeightedShortestPath : public testing::Test {
// params returns a vector of pairs. each pair is (vector-of-edges,
// vertex)
auto ExpandWShortest(EdgeAtom::Direction direction, std::optional<int> max_depth, Expression *where,
std::optional<int> node_id = 0, ScanAllTuple *existing_node_input = nullptr) {
std::optional<int> node_id = 0, ScanAllTuple *existing_node_input = nullptr,
memgraph::auth::User *user = nullptr) {
// scan the nodes optionally filtering on property value
auto n = MakeScanAll(storage, symbol_table, "n", existing_node_input ? existing_node_input->op_ : nullptr);
auto last_op = n.op_;
@ -1051,7 +1794,14 @@ class QueryPlanExpandWeightedShortestPath : public testing::Test {
Frame frame(symbol_table.max_position());
auto cursor = last_op->MakeCursor(memgraph::utils::NewDeleteResource());
std::vector<ResultType> results;
auto context = MakeContext(storage, symbol_table, &dba);
memgraph::query::ExecutionContext context;
if (user) {
memgraph::glue::FineGrainedAuthChecker auth_checker{*user};
context = MakeContextWithFineGrainedChecker(storage, symbol_table, &dba, &auth_checker);
} else {
context = MakeContext(storage, symbol_table, &dba);
}
while (cursor->Pull(frame, context)) {
results.push_back(ResultType{std::vector<memgraph::query::EdgeAccessor>(), frame[node_sym].ValueVertex(),
frame[total_weight].ValueDouble()});
@ -1280,6 +2030,106 @@ TEST_F(QueryPlanExpandWeightedShortestPath, NegativeUpperBound) {
EXPECT_THROW(ExpandWShortest(EdgeAtom::Direction::BOTH, -1, LITERAL(true)), QueryRuntimeException);
}
TEST_F(QueryPlanExpandWeightedShortestPath, FineGrainedFiltering) {
// All edge_types and labels allowed
{
memgraph::auth::User user{"test"};
user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().edge_type_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
auto results = ExpandWShortest(EdgeAtom::Direction::BOTH, 1000, LITERAL(true), 0, nullptr, &user);
EXPECT_EQ(results[0].path.size(), 1);
EXPECT_EQ(GetDoubleProp(results[0].path[0]), 3);
EXPECT_EQ(results[0].total_weight, 3);
EXPECT_EQ(results[1].path.size(), 1);
EXPECT_EQ(GetDoubleProp(results[1].path[0]), 5);
EXPECT_EQ(results[1].total_weight, 5);
EXPECT_EQ(results[2].path.size(), 2);
EXPECT_EQ(GetDoubleProp(results[2].path[0]), 3);
EXPECT_EQ(GetDoubleProp(results[2].path[1]), 3);
EXPECT_EQ(results[2].total_weight, 6);
EXPECT_EQ(results[3].path.size(), 3);
EXPECT_EQ(GetDoubleProp(results[3].path[0]), 3);
EXPECT_EQ(GetDoubleProp(results[3].path[1]), 3);
EXPECT_EQ(GetDoubleProp(results[3].path[2]), 3);
EXPECT_EQ(results[3].total_weight, 9);
}
// Denied all labels
{
memgraph::auth::User user{"test"};
user.fine_grained_access_handler().label_permissions().Deny("*", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().edge_type_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
auto results = ExpandWShortest(EdgeAtom::Direction::BOTH, 1000, LITERAL(true), 0, nullptr, &user);
ASSERT_EQ(results.size(), 0);
}
// Denied all edge types
{
memgraph::auth::User user{"test"};
user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().edge_type_permissions().Deny("*", memgraph::auth::FineGrainedPermission::READ);
auto results = ExpandWShortest(EdgeAtom::Direction::BOTH, 1000, LITERAL(true), 0, nullptr, &user);
ASSERT_EQ(results.size(), 0);
}
// Denied first vertex label
{
memgraph::auth::User user{"test"};
user.fine_grained_access_handler().label_permissions().Deny("l0", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().edge_type_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
auto results = ExpandWShortest(EdgeAtom::Direction::BOTH, 1000, LITERAL(true), 0, nullptr, &user);
ASSERT_EQ(results.size(), 0);
}
// Denied vertex label 2
{
memgraph::auth::User user{"test"};
user.fine_grained_access_handler().label_permissions().Grant("l0", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Grant("l1", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Grant("l2", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Grant("l3", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().label_permissions().Grant("l4", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().edge_type_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
auto results = ExpandWShortest(EdgeAtom::Direction::BOTH, 1000, LITERAL(true), 0, nullptr, &user);
ASSERT_EQ(results.size(), 4);
user.fine_grained_access_handler().label_permissions().Deny("l2", memgraph::auth::FineGrainedPermission::READ);
auto filtered_results = ExpandWShortest(EdgeAtom::Direction::BOTH, 1000, LITERAL(true), 0, nullptr, &user);
ASSERT_EQ(filtered_results.size(), 3);
}
// Deny edge type (created vertex 5 and edge vertex 4 to vertex 5)
{
v.push_back(dba.InsertVertex());
ASSERT_TRUE(v.back().SetProperty(prop.second, memgraph::storage::PropertyValue(5)).HasValue());
ASSERT_TRUE(v.back().AddLabel(db.NameToLabel("l5")).HasValue());
dba.AdvanceCommand();
memgraph::storage::EdgeTypeId edge_type_filter = dba.NameToEdgeType("edge_type_filter");
auto edge = dba.InsertEdge(&v[4], &v[5], edge_type_filter);
ASSERT_TRUE(edge->SetProperty(prop.second, memgraph::storage::PropertyValue(1)).HasValue());
e.emplace(std::make_pair(4, 5), *edge);
dba.AdvanceCommand();
memgraph::auth::User user{"test"};
user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().edge_type_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
auto results = ExpandWShortest(EdgeAtom::Direction::BOTH, 1000, LITERAL(true), 0, nullptr, &user);
ASSERT_EQ(results.size(), 5);
user.fine_grained_access_handler().edge_type_permissions().Grant("edge_type",
memgraph::auth::FineGrainedPermission::READ);
user.fine_grained_access_handler().edge_type_permissions().Deny("edge_type_filter",
memgraph::auth::FineGrainedPermission::READ);
auto filtered_results = ExpandWShortest(EdgeAtom::Direction::BOTH, 1000, LITERAL(true), 0, nullptr, &user);
ASSERT_EQ(filtered_results.size(), 4);
}
}
TEST(QueryPlan, ExpandOptional) {
memgraph::storage::Storage db;
auto storage_dba = db.Access();

View File

@ -9,10 +9,10 @@
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
#include <gtest/gtest.h>
#include "query_plan_common.hpp"
#include <gtest/gtest.h>
#include "query/frontend/semantic/symbol_table.hpp"
#include "query/plan/operator.hpp"
#include "storage/v2/storage.hpp"