Add authorization in SetLabels, RemoveLabels, Allshortestpath cursor (#537)
This commit is contained in:
parent
aa02745915
commit
dc8dad9794
@ -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()) {
|
||||
|
@ -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"]))
|
||||
|
@ -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"]))
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user