[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:
parent
7478300762
commit
d008a2ad8d
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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_;
|
||||
|
@ -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>();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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(),
|
||||
[¤t_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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
|
455
tests/e2e/fine_grained_access/create_delete_filtering_tests.py
Normal file
455
tests/e2e/fine_grained_access/create_delete_filtering_tests.py
Normal 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"]))
|
529
tests/e2e/fine_grained_access/path_filtering_tests.py
Normal file
529
tests/e2e/fine_grained_access/path_filtering_tests.py
Normal 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"]))
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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);",
|
||||
)
|
||||
|
150
tests/e2e/lba_procedures/read_permission_queries.py
Normal file
150
tests/e2e/lba_procedures/read_permission_queries.py
Normal 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"]))
|
158
tests/e2e/lba_procedures/update_permission_queries.py
Normal file
158
tests/e2e/lba_procedures/update_permission_queries.py
Normal 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"]))
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
110
tests/unit/bfs_fine_grained.cpp
Normal file
110
tests/unit/bfs_fine_grained.cpp
Normal 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
@ -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();
|
||||
|
@ -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"
|
||||
|
Loading…
Reference in New Issue
Block a user