Add authorization in SetLabels, RemoveLabels, Allshortestpath cursor (#537)

This commit is contained in:
niko4299 2022-09-13 17:14:23 +02:00 committed by GitHub
parent aa02745915
commit dc8dad9794
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 543 additions and 36 deletions

View File

@ -1870,17 +1870,29 @@ class ExpandAllShortestPathsCursor : public query::plan::Cursor {
// Populates the priority queue structure with expansions
// from the given vertex. skips expansions that don't satisfy
// the "where" condition.
auto expand_from_vertex = [this, &expand_vertex](const VertexAccessor &vertex, const TypedValue &weight,
int64_t depth) {
auto expand_from_vertex = [this, &expand_vertex, &context](const VertexAccessor &vertex, const TypedValue &weight,
int64_t depth) {
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->Has(edge.To(), storage::View::OLD,
memgraph::query::AuthQuery::FineGrainedPrivilege::READ) &&
context.auth_checker->Has(edge, memgraph::query::AuthQuery::FineGrainedPrivilege::READ))) {
continue;
}
expand_vertex(edge, EdgeAtom::Direction::OUT, weight, depth);
}
}
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->Has(edge.From(), storage::View::OLD,
memgraph::query::AuthQuery::FineGrainedPrivilege::READ) &&
context.auth_checker->Has(edge, memgraph::query::AuthQuery::FineGrainedPrivilege::READ))) {
continue;
}
expand_vertex(edge, EdgeAtom::Direction::IN, weight, depth);
}
}
@ -2725,6 +2737,11 @@ SetLabels::SetLabelsCursor::SetLabelsCursor(const SetLabels &self, utils::Memory
bool SetLabels::SetLabelsCursor::Pull(Frame &frame, ExecutionContext &context) {
SCOPED_PROFILE_OP("SetLabels");
if (context.auth_checker &&
!context.auth_checker->Has(self_.labels_, memgraph::query::AuthQuery::FineGrainedPrivilege::CREATE_DELETE)) {
throw QueryRuntimeException("Couldn't set label due to not having enough permission!");
}
if (!input_cursor_->Pull(frame, context)) return false;
TypedValue &vertex_value = frame[self_.input_symbol_];
@ -2732,6 +2749,12 @@ bool SetLabels::SetLabelsCursor::Pull(Frame &frame, ExecutionContext &context) {
if (vertex_value.IsNull()) return true;
ExpectType(self_.input_symbol_, vertex_value, TypedValue::Type::Vertex);
auto &vertex = vertex_value.ValueVertex();
if (context.auth_checker && !context.auth_checker->Has(vertex, storage::View::OLD,
memgraph::query::AuthQuery::FineGrainedPrivilege::UPDATE)) {
throw QueryRuntimeException("Couldn't set label due to not having enough permission!");
}
for (auto label : self_.labels_) {
auto maybe_value = vertex.AddLabel(label);
if (maybe_value.HasError()) {
@ -2865,6 +2888,11 @@ RemoveLabels::RemoveLabelsCursor::RemoveLabelsCursor(const RemoveLabels &self, u
bool RemoveLabels::RemoveLabelsCursor::Pull(Frame &frame, ExecutionContext &context) {
SCOPED_PROFILE_OP("RemoveLabels");
if (context.auth_checker &&
!context.auth_checker->Has(self_.labels_, memgraph::query::AuthQuery::FineGrainedPrivilege::CREATE_DELETE)) {
throw QueryRuntimeException("Couldn't remove label due to not having enough permission!");
}
if (!input_cursor_->Pull(frame, context)) return false;
TypedValue &vertex_value = frame[self_.input_symbol_];
@ -2872,6 +2900,11 @@ bool RemoveLabels::RemoveLabelsCursor::Pull(Frame &frame, ExecutionContext &cont
if (vertex_value.IsNull()) return true;
ExpectType(self_.input_symbol_, vertex_value, TypedValue::Type::Vertex);
auto &vertex = vertex_value.ValueVertex();
if (context.auth_checker && !context.auth_checker->Has(vertex, storage::View::OLD,
memgraph::query::AuthQuery::FineGrainedPrivilege::UPDATE)) {
throw QueryRuntimeException("Couldn't remove label due to not having enough permission!");
}
for (auto label : self_.labels_) {
auto maybe_value = vertex.RemoveLabel(label);
if (maybe_value.HasError()) {

View File

@ -470,5 +470,48 @@ def test_merge_edge_second_node_label_granted():
)
def test_set_label_when_label_granted():
admin_connection = common.connect(username="admin", password="test")
user_connection = 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 :update_label_2 TO user;")
common.execute_and_fetch_all(user_connection.cursor(), "MATCH (p:test_delete) SET p:update_label_2;")
def test_set_label_when_label_denied():
admin_connection = common.connect(username="admin", password="test")
user_connection = 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 :update_label_2 TO user;")
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON LABELS :test_delete TO user;")
with pytest.raises(DatabaseError):
common.execute_and_fetch_all(user_connection.cursor(), "MATCH (p:test_delete) SET p:update_label_2;")
def test_remove_label_when_label_granted():
admin_connection = common.connect(username="admin", password="test")
user_connection = 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;")
common.execute_and_fetch_all(user_connection.cursor(), "MATCH (p:test_delete) REMOVE p:test_delete;")
def test_remove_label_when_label_denied():
admin_connection = common.connect(username="admin", password="test")
user_connection = 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 :update_label_2 TO user;")
common.execute_and_fetch_all(admin_connection.cursor(), "GRANT READ ON LABELS :test_delete TO user;")
with pytest.raises(DatabaseError):
common.execute_and_fetch_all(user_connection.cursor(), "MATCH (p:test_delete) REMOVE p:test_delete;")
if __name__ == "__main__":
sys.exit(pytest.main([__file__, "-rA"]))

View File

@ -525,5 +525,193 @@ def test_bfs_single_source_denied_edge_type_3():
assert source_destination_path[0][0] == expected_path
def test_all_shortest_paths_when_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 *allShortest (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 *allShortest (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_all_shortest_paths_when_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 *allShortest (r, n | r.weight)]->(m) RETURN p;"
)
assert len(results) == 0
def test_all_shortest_paths_when_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 *allShortest (r, n | r.weight) path_length]->(m:label4) RETURN path_length;",
)
assert len(path_length_result) == 0
def test_all_shortest_paths_when_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 *allShortest (r, n | r.weight) path_length]->(m:label4) RETURN path_length;",
)
assert len(path_length_result) == 0
def test_all_shortest_paths_when_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 *allShortest (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 *allShortest (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_all_shortest_paths_when_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 *allShortest (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 *allShortest (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])
if __name__ == "__main__":
sys.exit(pytest.main([__file__, "-rA"]))

View File

@ -1242,6 +1242,80 @@ TEST(QueryPlan, SetLabels) {
}
}
TEST(QueryPlan, SetLabelsWithFineGrained) {
auto set_labels = [&](memgraph::auth::User user, memgraph::query::DbAccessor dba,
std::vector<memgraph::storage::LabelId> labels) {
ASSERT_TRUE(dba.InsertVertex().AddLabel(labels[0]).HasValue());
ASSERT_TRUE(dba.InsertVertex().AddLabel(labels[0]).HasValue());
dba.AdvanceCommand();
AstStorage storage;
SymbolTable symbol_table;
auto n = MakeScanAll(storage, symbol_table, "n");
auto label_set =
std::make_shared<plan::SetLabels>(n.op_, n.sym_, std::vector<memgraph::storage::LabelId>{labels[1], labels[2]});
memgraph::glue::FineGrainedAuthChecker auth_checker{user, &dba};
auto context = MakeContextWithFineGrainedChecker(storage, symbol_table, &dba, &auth_checker);
PullAll(*label_set, &context);
};
// All labels granted
{
memgraph::auth::User user{"test"};
user.fine_grained_access_handler().label_permissions().Grant("*",
memgraph::auth::FineGrainedPermission::CREATE_DELETE);
memgraph::storage::Storage db;
auto storage_dba = db.Access();
memgraph::query::DbAccessor dba(&storage_dba);
auto label1 = dba.NameToLabel("label1");
auto label2 = dba.NameToLabel("label2");
auto label3 = dba.NameToLabel("label3");
set_labels(user, dba, std::vector<memgraph::storage::LabelId>{label1, label2, label3});
for (auto vertex : dba.Vertices(memgraph::storage::View::OLD)) {
EXPECT_EQ(3, vertex.Labels(memgraph::storage::View::NEW)->size());
EXPECT_TRUE(*vertex.HasLabel(memgraph::storage::View::NEW, label2));
EXPECT_TRUE(*vertex.HasLabel(memgraph::storage::View::NEW, label3));
}
}
// All labels denied
{
memgraph::auth::User user{"test"};
user.fine_grained_access_handler().label_permissions().Deny("*",
memgraph::auth::FineGrainedPermission::CREATE_DELETE);
memgraph::storage::Storage db;
auto storage_dba = db.Access();
memgraph::query::DbAccessor dba(&storage_dba);
auto label1 = dba.NameToLabel("label1");
auto label2 = dba.NameToLabel("label2");
auto label3 = dba.NameToLabel("label3");
ASSERT_THROW(set_labels(user, dba, std::vector<memgraph::storage::LabelId>{label1, label2, label3}),
QueryRuntimeException);
}
// label2 denied
{
memgraph::auth::User user{"test"};
user.fine_grained_access_handler().label_permissions().Grant("label1",
memgraph::auth::FineGrainedPermission::CREATE_DELETE);
user.fine_grained_access_handler().label_permissions().Deny("label2",
memgraph::auth::FineGrainedPermission::CREATE_DELETE);
user.fine_grained_access_handler().label_permissions().Grant("label3",
memgraph::auth::FineGrainedPermission::CREATE_DELETE);
memgraph::storage::Storage db;
auto storage_dba = db.Access();
memgraph::query::DbAccessor dba(&storage_dba);
auto label1 = dba.NameToLabel("label1");
auto label2 = dba.NameToLabel("label2");
auto label3 = dba.NameToLabel("label3");
ASSERT_THROW(set_labels(user, dba, std::vector<memgraph::storage::LabelId>{label1, label2, label3}),
QueryRuntimeException);
}
}
TEST(QueryPlan, RemoveProperty) {
memgraph::storage::Storage db;
auto storage_dba = db.Access();
@ -1339,6 +1413,86 @@ TEST(QueryPlan, RemoveLabels) {
}
}
TEST(QueryPlan, RemoveLabelsFineGrainedFiltering) {
auto remove_labels = [&](memgraph::auth::User user, memgraph::query::DbAccessor dba,
std::vector<memgraph::storage::LabelId> labels) {
auto v1 = dba.InsertVertex();
ASSERT_TRUE(v1.AddLabel(labels[0]).HasValue());
ASSERT_TRUE(v1.AddLabel(labels[1]).HasValue());
ASSERT_TRUE(v1.AddLabel(labels[2]).HasValue());
auto v2 = dba.InsertVertex();
ASSERT_TRUE(v2.AddLabel(labels[0]).HasValue());
ASSERT_TRUE(v2.AddLabel(labels[2]).HasValue());
dba.AdvanceCommand();
AstStorage storage;
SymbolTable symbol_table;
auto n = MakeScanAll(storage, symbol_table, "n");
auto label_remove = std::make_shared<plan::RemoveLabels>(
n.op_, n.sym_, std::vector<memgraph::storage::LabelId>{labels[0], labels[1]});
memgraph::glue::FineGrainedAuthChecker auth_checker{user, &dba};
auto context = MakeContextWithFineGrainedChecker(storage, symbol_table, &dba, &auth_checker);
PullAll(*label_remove, &context);
};
// All labels granted
{
memgraph::auth::User user{"test"};
user.fine_grained_access_handler().label_permissions().Grant("*",
memgraph::auth::FineGrainedPermission::CREATE_DELETE);
memgraph::storage::Storage db;
auto storage_dba = db.Access();
memgraph::query::DbAccessor dba(&storage_dba);
auto label1 = dba.NameToLabel("label1");
auto label2 = dba.NameToLabel("label2");
auto label3 = dba.NameToLabel("label3");
remove_labels(user, dba, std::vector<memgraph::storage::LabelId>{label1, label2, label3});
for (auto vertex : dba.Vertices(memgraph::storage::View::OLD)) {
EXPECT_EQ(1, vertex.Labels(memgraph::storage::View::NEW)->size());
EXPECT_FALSE(*vertex.HasLabel(memgraph::storage::View::NEW, label2));
EXPECT_TRUE(*vertex.HasLabel(memgraph::storage::View::NEW, label3));
}
}
// All labels denied
{
memgraph::auth::User user{"test"};
user.fine_grained_access_handler().label_permissions().Deny("*",
memgraph::auth::FineGrainedPermission::CREATE_DELETE);
memgraph::storage::Storage db;
auto storage_dba = db.Access();
memgraph::query::DbAccessor dba(&storage_dba);
auto label1 = dba.NameToLabel("label1");
auto label2 = dba.NameToLabel("label2");
auto label3 = dba.NameToLabel("label3");
ASSERT_THROW(remove_labels(user, dba, std::vector<memgraph::storage::LabelId>{label1, label2, label3}),
QueryRuntimeException);
}
// label2 denied
{
memgraph::auth::User user{"test"};
user.fine_grained_access_handler().label_permissions().Grant("label1",
memgraph::auth::FineGrainedPermission::CREATE_DELETE);
user.fine_grained_access_handler().label_permissions().Deny("label2",
memgraph::auth::FineGrainedPermission::CREATE_DELETE);
user.fine_grained_access_handler().label_permissions().Grant("label3",
memgraph::auth::FineGrainedPermission::CREATE_DELETE);
memgraph::storage::Storage db;
auto storage_dba = db.Access();
memgraph::query::DbAccessor dba(&storage_dba);
auto label1 = dba.NameToLabel("label1");
auto label2 = dba.NameToLabel("label2");
auto label3 = dba.NameToLabel("label3");
ASSERT_THROW(remove_labels(user, dba, std::vector<memgraph::storage::LabelId>{label1, label2, label3}),
QueryRuntimeException);
}
}
TEST(QueryPlan, NodeFilterSet) {
memgraph::storage::Storage db;
auto storage_dba = db.Access();

View File

@ -2030,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);
}
}
/** A test fixture for all shortest paths expansion */
class QueryPlanExpandAllShortestPaths : public testing::Test {
public:
@ -2069,6 +2169,8 @@ class QueryPlanExpandAllShortestPaths : 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) {
@ -2091,7 +2193,8 @@ class QueryPlanExpandAllShortestPaths : public testing::Test {
// params returns a vector of pairs. each pair is (vector-of-edges,
// vertex)
auto ExpandAllShortest(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,
const 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_;
@ -2114,7 +2217,13 @@ class QueryPlanExpandAllShortestPaths : 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);
ExecutionContext context;
if (user) {
memgraph::glue::FineGrainedAuthChecker auth_checker{*user, &dba};
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()});
@ -2383,48 +2492,27 @@ TEST_F(QueryPlanExpandAllShortestPaths, MultiEdge) {
EXPECT_EQ(results[5].total_weight, 9);
}
TEST_F(QueryPlanExpandWeightedShortestPath, FineGrainedFiltering) {
TEST_F(QueryPlanExpandAllShortestPaths, BasicWithFineGrainedFiltering) {
// 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);
auto results = ExpandAllShortest(EdgeAtom::Direction::BOTH, 1000, LITERAL(true));
sort(results.begin(), results.end(), compareResultType);
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);
auto results = ExpandAllShortest(EdgeAtom::Direction::BOTH, 1000, LITERAL(true), 0, nullptr, &user);
ASSERT_EQ(results.size(), 0);
}
@ -2433,8 +2521,8 @@ TEST_F(QueryPlanExpandWeightedShortestPath, FineGrainedFiltering) {
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 = ExpandAllShortest(EdgeAtom::Direction::BOTH, 1000, LITERAL(true), 0, nullptr, &user);
auto results = ExpandWShortest(EdgeAtom::Direction::BOTH, 1000, LITERAL(true), 0, nullptr, &user);
ASSERT_EQ(results.size(), 0);
}
@ -2448,11 +2536,11 @@ TEST_F(QueryPlanExpandWeightedShortestPath, FineGrainedFiltering) {
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);
auto results = ExpandAllShortest(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);
auto filtered_results = ExpandAllShortest(EdgeAtom::Direction::BOTH, 1000, LITERAL(true), 0, nullptr, &user);
ASSERT_EQ(filtered_results.size(), 3);
}
@ -2471,14 +2559,15 @@ TEST_F(QueryPlanExpandWeightedShortestPath, FineGrainedFiltering) {
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);
auto results = ExpandAllShortest(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);
auto filtered_results = ExpandAllShortest(EdgeAtom::Direction::BOTH, 1000, LITERAL(true), 0, nullptr, &user);
ASSERT_EQ(filtered_results.size(), 4);
}
}