[E129-MG < T1006-MG] Expand C API with LBA checks (#527)
* [T1006-MG < T1017-MG] Add LBA checks to all read procedures in C API (#515) * Initial Impl * NextPermittedEdge introduced * revert moving constructor to cpp * edge from and edge to methods expanded with lba check * minor fix * added check to path expand procedure * Added integration tests for read query procedures * additional check * changed iterator type to reference * comments from pr Co-authored-by: Josip Mrden <josip.mrden@memgraph.io> * [T1006-MG < T1018-MG] Add LBA checks to all update procedures in C API (#516) * Initial Impl * NextPermittedEdge introduced * revert moving constructor to cpp * edge from and edge to methods expanded with lba check * minor fix * extended update methods * added check to path expand procedure * Added integration tests for read query procedures * Added integration tests for update query modules * additional check * changed iterator type to reference * fixed bug in Update property for node; fixed 2 e2e tests * replaced enum Co-authored-by: Josip Mrden <josip.mrden@memgraph.io> * [T1006-MG < T1019-MG] Add LBA checks to all Create and Delete procedures in C API (#517) * Initial Impl * NextPermittedEdge introduced * revert moving constructor to cpp * edge from and edge to methods expanded with lba check * minor fix * extended update methods * initial implementation * added check to path expand procedure * Added integration tests for read query procedures * Added integration tests for update query modules * Added unit tests for creation of vertex, adding and removing vertex label * additional check * changed iterator type to reference * Added unit tests for create edge * Corrected query module in create edge * fixed bug in Update property for node; fixed 2 e2e tests * fixed merge errors * Expanded FineGrainedAuthChecker with HasGlobalPermissionOnVertices and HasGlobalPermissionOnEdges * Removed two wrong checks; Added two global checks * return null added * introduced new mgp_error value * fixed endless loop * replaced enum * intermediate * tests updated * PermissionDeniedError -> AuthorizationError rename * rename in enum permission_denied error -> authorization error * mgp_vertex_remove_label check improved * quotes changed; order of imports fixed * string constant introduced * import fixed * yaml format Co-authored-by: Josip Mrden <josip.mrden@memgraph.io> Co-authored-by: Josip Mrden <josip.mrden@memgraph.io>
This commit is contained in:
parent
35f8978560
commit
c09b175c76
@ -37,12 +37,19 @@ extern "C" {
|
|||||||
/// All functions return an error code that can be used to figure out whether the API call was successful or not. In
|
/// All functions return an error code that can be used to figure out whether the API call was successful or not. In
|
||||||
/// case of failure, the specific error code can be used to identify the reason of the failure.
|
/// case of failure, the specific error code can be used to identify the reason of the failure.
|
||||||
MGP_ENUM_CLASS MGP_NODISCARD mgp_error{
|
MGP_ENUM_CLASS MGP_NODISCARD mgp_error{
|
||||||
MGP_ERROR_NO_ERROR, MGP_ERROR_UNKNOWN_ERROR,
|
MGP_ERROR_NO_ERROR,
|
||||||
MGP_ERROR_UNABLE_TO_ALLOCATE, MGP_ERROR_INSUFFICIENT_BUFFER,
|
MGP_ERROR_UNKNOWN_ERROR,
|
||||||
MGP_ERROR_OUT_OF_RANGE, MGP_ERROR_LOGIC_ERROR,
|
MGP_ERROR_UNABLE_TO_ALLOCATE,
|
||||||
MGP_ERROR_DELETED_OBJECT, MGP_ERROR_INVALID_ARGUMENT,
|
MGP_ERROR_INSUFFICIENT_BUFFER,
|
||||||
MGP_ERROR_KEY_ALREADY_EXISTS, MGP_ERROR_IMMUTABLE_OBJECT,
|
MGP_ERROR_OUT_OF_RANGE,
|
||||||
MGP_ERROR_VALUE_CONVERSION, MGP_ERROR_SERIALIZATION_ERROR,
|
MGP_ERROR_LOGIC_ERROR,
|
||||||
|
MGP_ERROR_DELETED_OBJECT,
|
||||||
|
MGP_ERROR_INVALID_ARGUMENT,
|
||||||
|
MGP_ERROR_KEY_ALREADY_EXISTS,
|
||||||
|
MGP_ERROR_IMMUTABLE_OBJECT,
|
||||||
|
MGP_ERROR_VALUE_CONVERSION,
|
||||||
|
MGP_ERROR_SERIALIZATION_ERROR,
|
||||||
|
MGP_ERROR_AUTHORIZATION_ERROR,
|
||||||
};
|
};
|
||||||
///@}
|
///@}
|
||||||
|
|
||||||
|
@ -134,6 +134,15 @@ class SerializationError(_mgp.SerializationError):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class AuthorizationError(_mgp.AuthorizationError):
|
||||||
|
"""
|
||||||
|
Signals that the user doesn't have sufficient permissions to perform
|
||||||
|
procedure call.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class Label:
|
class Label:
|
||||||
"""Label of a `Vertex`."""
|
"""Label of a `Vertex`."""
|
||||||
|
|
||||||
@ -1969,6 +1978,8 @@ def _wrap_exceptions():
|
|||||||
raise ValueConversionError(e)
|
raise ValueConversionError(e)
|
||||||
except _mgp.SerializationError as e:
|
except _mgp.SerializationError as e:
|
||||||
raise SerializationError(e)
|
raise SerializationError(e)
|
||||||
|
except _mgp.AuthorizationError as e:
|
||||||
|
raise AuthorizationError(e)
|
||||||
|
|
||||||
return wrapped_func
|
return wrapped_func
|
||||||
|
|
||||||
|
@ -173,6 +173,10 @@ class SerializationError(Exception):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class AuthorizationError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def type_nullable(elem: Any):
|
def type_nullable(elem: Any):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -28,6 +28,18 @@ bool IsUserAuthorizedLabels(const memgraph::auth::User &user, const memgraph::qu
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsUserAuthorizedGloballyLabels(const memgraph::auth::User &user,
|
||||||
|
const memgraph::auth::FineGrainedPermission fine_grained_permission) {
|
||||||
|
return user.GetFineGrainedAccessLabelPermissions().Has(memgraph::auth::kAsterisk, fine_grained_permission) ==
|
||||||
|
memgraph::auth::PermissionLevel::GRANT;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsUserAuthorizedGloballyEdges(const memgraph::auth::User &user,
|
||||||
|
const memgraph::auth::FineGrainedPermission fine_grained_permission) {
|
||||||
|
return user.GetFineGrainedAccessEdgeTypePermissions().Has(memgraph::auth::kAsterisk, fine_grained_permission) ==
|
||||||
|
memgraph::auth::PermissionLevel::GRANT;
|
||||||
|
}
|
||||||
|
|
||||||
bool IsUserAuthorizedEdgeType(const memgraph::auth::User &user, const memgraph::query::DbAccessor &dba,
|
bool IsUserAuthorizedEdgeType(const memgraph::auth::User &user, const memgraph::query::DbAccessor &dba,
|
||||||
const memgraph::storage::EdgeTypeId &edgeType,
|
const memgraph::storage::EdgeTypeId &edgeType,
|
||||||
const memgraph::query::AuthQuery::FineGrainedPrivilege fine_grained_permission) {
|
const memgraph::query::AuthQuery::FineGrainedPrivilege fine_grained_permission) {
|
||||||
@ -125,4 +137,13 @@ bool FineGrainedAuthChecker::Accept(
|
|||||||
return IsUserAuthorizedEdgeType(user_, dba, edge_type, fine_grained_permission);
|
return IsUserAuthorizedEdgeType(user_, dba, edge_type, fine_grained_permission);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool FineGrainedAuthChecker::HasGlobalPermissionOnVertices(
|
||||||
|
const memgraph::query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) const {
|
||||||
|
return IsUserAuthorizedGloballyLabels(user_, FineGrainedPrivilegeToFineGrainedPermission(fine_grained_privilege));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FineGrainedAuthChecker::HasGlobalPermissionOnEdges(
|
||||||
|
const memgraph::query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) const {
|
||||||
|
return IsUserAuthorizedGloballyEdges(user_, FineGrainedPrivilegeToFineGrainedPermission(fine_grained_privilege));
|
||||||
|
};
|
||||||
} // namespace memgraph::glue
|
} // namespace memgraph::glue
|
||||||
|
@ -12,7 +12,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "auth/auth.hpp"
|
#include "auth/auth.hpp"
|
||||||
#include "auth/models.hpp"
|
|
||||||
#include "glue/auth.hpp"
|
#include "glue/auth.hpp"
|
||||||
#include "query/auth_checker.hpp"
|
#include "query/auth_checker.hpp"
|
||||||
#include "query/db_accessor.hpp"
|
#include "query/db_accessor.hpp"
|
||||||
@ -54,6 +53,12 @@ class FineGrainedAuthChecker : public query::FineGrainedAuthChecker {
|
|||||||
bool Accept(const memgraph::query::DbAccessor &dba, const memgraph::storage::EdgeTypeId &edge_type,
|
bool Accept(const memgraph::query::DbAccessor &dba, const memgraph::storage::EdgeTypeId &edge_type,
|
||||||
query::AuthQuery::FineGrainedPrivilege fine_grained_permission) const override;
|
query::AuthQuery::FineGrainedPrivilege fine_grained_permission) const override;
|
||||||
|
|
||||||
|
bool HasGlobalPermissionOnVertices(
|
||||||
|
memgraph::query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) const override;
|
||||||
|
|
||||||
|
bool HasGlobalPermissionOnEdges(
|
||||||
|
memgraph::query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
auth::User user_;
|
auth::User user_;
|
||||||
};
|
};
|
||||||
|
@ -48,6 +48,12 @@ class FineGrainedAuthChecker {
|
|||||||
[[nodiscard]] virtual bool Accept(const memgraph::query::DbAccessor &dba,
|
[[nodiscard]] virtual bool Accept(const memgraph::query::DbAccessor &dba,
|
||||||
const memgraph::storage::EdgeTypeId &edge_type,
|
const memgraph::storage::EdgeTypeId &edge_type,
|
||||||
query::AuthQuery::FineGrainedPrivilege fine_grained_permission) const = 0;
|
query::AuthQuery::FineGrainedPrivilege fine_grained_permission) const = 0;
|
||||||
|
|
||||||
|
[[nodiscard]] virtual bool HasGlobalPermissionOnVertices(
|
||||||
|
memgraph::query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) const = 0;
|
||||||
|
|
||||||
|
[[nodiscard]] virtual bool HasGlobalPermissionOnEdges(
|
||||||
|
memgraph::query::AuthQuery::FineGrainedPrivilege fine_grained_privilege) const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class AllowEverythingFineGrainedAuthChecker final : public query::FineGrainedAuthChecker {
|
class AllowEverythingFineGrainedAuthChecker final : public query::FineGrainedAuthChecker {
|
||||||
@ -71,6 +77,16 @@ class AllowEverythingFineGrainedAuthChecker final : public query::FineGrainedAut
|
|||||||
const query::AuthQuery::FineGrainedPrivilege fine_grained_permission) const override {
|
const query::AuthQuery::FineGrainedPrivilege fine_grained_permission) const override {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool HasGlobalPermissionOnVertices(
|
||||||
|
const memgraph::query::AuthQuery::FineGrainedPrivilege /*fine_grained_privilege*/) const override {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HasGlobalPermissionOnEdges(
|
||||||
|
const memgraph::query::AuthQuery::FineGrainedPrivilege /*fine_grained_privilege*/) const override {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}; // namespace memgraph::query
|
}; // namespace memgraph::query
|
||||||
|
|
||||||
class AllowEverythingAuthChecker final : public query::AuthChecker {
|
class AllowEverythingAuthChecker final : public query::AuthChecker {
|
||||||
|
@ -120,6 +120,10 @@ struct SerializationException : public memgraph::utils::BasicException {
|
|||||||
using memgraph::utils::BasicException::BasicException;
|
using memgraph::utils::BasicException::BasicException;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct AuthorizationException : public memgraph::utils::BasicException {
|
||||||
|
using memgraph::utils::BasicException::BasicException;
|
||||||
|
};
|
||||||
|
|
||||||
template <typename TFunc, typename TReturn>
|
template <typename TFunc, typename TReturn>
|
||||||
concept ReturnsType = std::same_as<std::invoke_result_t<TFunc>, TReturn>;
|
concept ReturnsType = std::same_as<std::invoke_result_t<TFunc>, TReturn>;
|
||||||
|
|
||||||
@ -160,6 +164,9 @@ template <typename TFunc, typename... Args>
|
|||||||
} catch (const SerializationException &se) {
|
} catch (const SerializationException &se) {
|
||||||
spdlog::error("Serialization error during mg API call: {}", se.what());
|
spdlog::error("Serialization error during mg API call: {}", se.what());
|
||||||
return mgp_error::MGP_ERROR_SERIALIZATION_ERROR;
|
return mgp_error::MGP_ERROR_SERIALIZATION_ERROR;
|
||||||
|
} catch (const AuthorizationException &ae) {
|
||||||
|
spdlog::error("Authorization error during mg API call: {}", ae.what());
|
||||||
|
return mgp_error::MGP_ERROR_AUTHORIZATION_ERROR;
|
||||||
} catch (const std::bad_alloc &bae) {
|
} catch (const std::bad_alloc &bae) {
|
||||||
spdlog::error("Memory allocation error during mg API call: {}", bae.what());
|
spdlog::error("Memory allocation error during mg API call: {}", bae.what());
|
||||||
return mgp_error::MGP_ERROR_UNABLE_TO_ALLOCATE;
|
return mgp_error::MGP_ERROR_UNABLE_TO_ALLOCATE;
|
||||||
@ -1066,6 +1073,7 @@ mgp_error mgp_path_expand(mgp_path *path, mgp_edge *edge) {
|
|||||||
// the given edge.
|
// the given edge.
|
||||||
auto *src_vertex = &path->vertices.back();
|
auto *src_vertex = &path->vertices.back();
|
||||||
mgp_vertex *dst_vertex{nullptr};
|
mgp_vertex *dst_vertex{nullptr};
|
||||||
|
|
||||||
if (edge->to == *src_vertex) {
|
if (edge->to == *src_vertex) {
|
||||||
dst_vertex = &edge->from;
|
dst_vertex = &edge->from;
|
||||||
} else if (edge->from == *src_vertex) {
|
} else if (edge->from == *src_vertex) {
|
||||||
@ -1579,9 +1587,16 @@ memgraph::storage::PropertyValue ToPropertyValue(const mgp_value &value) {
|
|||||||
|
|
||||||
mgp_error mgp_vertex_set_property(struct mgp_vertex *v, const char *property_name, mgp_value *property_value) {
|
mgp_error mgp_vertex_set_property(struct mgp_vertex *v, const char *property_name, mgp_value *property_value) {
|
||||||
return WrapExceptions([=] {
|
return WrapExceptions([=] {
|
||||||
|
if (v->graph->ctx && v->graph->ctx->auth_checker &&
|
||||||
|
!v->graph->ctx->auth_checker->Accept(*v->graph->ctx->db_accessor, v->impl, v->graph->view,
|
||||||
|
memgraph::query::AuthQuery::FineGrainedPrivilege::UPDATE)) {
|
||||||
|
throw AuthorizationException{"Insufficient permissions for setting a property on vertex!"};
|
||||||
|
}
|
||||||
|
|
||||||
if (!MgpVertexIsMutable(*v)) {
|
if (!MgpVertexIsMutable(*v)) {
|
||||||
throw ImmutableObjectException{"Cannot set a property on an immutable vertex!"};
|
throw ImmutableObjectException{"Cannot set a property on an immutable vertex!"};
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto prop_key = v->graph->impl->NameToProperty(property_name);
|
const auto prop_key = v->graph->impl->NameToProperty(property_name);
|
||||||
const auto result = v->impl.SetProperty(prop_key, ToPropertyValue(*property_value));
|
const auto result = v->impl.SetProperty(prop_key, ToPropertyValue(*property_value));
|
||||||
if (result.HasError()) {
|
if (result.HasError()) {
|
||||||
@ -1619,6 +1634,14 @@ mgp_error mgp_vertex_set_property(struct mgp_vertex *v, const char *property_nam
|
|||||||
|
|
||||||
mgp_error mgp_vertex_add_label(struct mgp_vertex *v, mgp_label label) {
|
mgp_error mgp_vertex_add_label(struct mgp_vertex *v, mgp_label label) {
|
||||||
return WrapExceptions([=] {
|
return WrapExceptions([=] {
|
||||||
|
if (v->graph->ctx && v->graph->ctx->auth_checker &&
|
||||||
|
!(v->graph->ctx->auth_checker->Accept(*v->graph->ctx->db_accessor, v->impl, v->graph->view,
|
||||||
|
memgraph::query::AuthQuery::FineGrainedPrivilege::UPDATE) &&
|
||||||
|
v->graph->ctx->auth_checker->Accept(*v->graph->ctx->db_accessor, {v->graph->impl->NameToLabel(label.name)},
|
||||||
|
memgraph::query::AuthQuery::FineGrainedPrivilege::CREATE_DELETE))) {
|
||||||
|
throw AuthorizationException{"Insufficient permissions for adding a label to vertex!"};
|
||||||
|
}
|
||||||
|
|
||||||
if (!MgpVertexIsMutable(*v)) {
|
if (!MgpVertexIsMutable(*v)) {
|
||||||
throw ImmutableObjectException{"Cannot add a label to an immutable vertex!"};
|
throw ImmutableObjectException{"Cannot add a label to an immutable vertex!"};
|
||||||
}
|
}
|
||||||
@ -1651,6 +1674,14 @@ mgp_error mgp_vertex_add_label(struct mgp_vertex *v, mgp_label label) {
|
|||||||
|
|
||||||
mgp_error mgp_vertex_remove_label(struct mgp_vertex *v, mgp_label label) {
|
mgp_error mgp_vertex_remove_label(struct mgp_vertex *v, mgp_label label) {
|
||||||
return WrapExceptions([=] {
|
return WrapExceptions([=] {
|
||||||
|
if (v->graph->ctx && v->graph->ctx->auth_checker &&
|
||||||
|
!(v->graph->ctx->auth_checker->Accept(*v->graph->ctx->db_accessor, v->impl, v->graph->view,
|
||||||
|
memgraph::query::AuthQuery::FineGrainedPrivilege::UPDATE) &&
|
||||||
|
v->graph->ctx->auth_checker->Accept(*v->graph->ctx->db_accessor, {v->graph->impl->NameToLabel(label.name)},
|
||||||
|
memgraph::query::AuthQuery::FineGrainedPrivilege::CREATE_DELETE))) {
|
||||||
|
throw AuthorizationException{"Insufficient permissions for removing a label from vertex!"};
|
||||||
|
}
|
||||||
|
|
||||||
if (!MgpVertexIsMutable(*v)) {
|
if (!MgpVertexIsMutable(*v)) {
|
||||||
throw ImmutableObjectException{"Cannot remove a label from an immutable vertex!"};
|
throw ImmutableObjectException{"Cannot remove a label from an immutable vertex!"};
|
||||||
}
|
}
|
||||||
@ -1828,6 +1859,32 @@ mgp_error mgp_vertex_iter_properties(mgp_vertex *v, mgp_memory *memory, mgp_prop
|
|||||||
|
|
||||||
void mgp_edges_iterator_destroy(mgp_edges_iterator *it) { DeleteRawMgpObject(it); }
|
void mgp_edges_iterator_destroy(mgp_edges_iterator *it) { DeleteRawMgpObject(it); }
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
void NextPermittedEdge(mgp_edges_iterator &it, const bool for_in) {
|
||||||
|
if (!it.source_vertex.graph->ctx || !it.source_vertex.graph->ctx->auth_checker) return;
|
||||||
|
|
||||||
|
auto &impl_it = for_in ? it.in_it : it.out_it;
|
||||||
|
const auto end = for_in ? it.in->end() : it.out->end();
|
||||||
|
|
||||||
|
if (impl_it) {
|
||||||
|
const auto *auth_checker = it.source_vertex.graph->ctx->auth_checker.get();
|
||||||
|
const auto db_accessor = *it.source_vertex.graph->ctx->db_accessor;
|
||||||
|
const auto view = it.source_vertex.graph->view;
|
||||||
|
while (*impl_it != end) {
|
||||||
|
if (auth_checker->Accept(db_accessor, **impl_it, memgraph::query::AuthQuery::FineGrainedPrivilege::READ)) {
|
||||||
|
const auto &check_vertex = it.source_vertex.impl == (*impl_it)->From() ? (*impl_it)->To() : (*impl_it)->From();
|
||||||
|
if (auth_checker->Accept(db_accessor, check_vertex, view,
|
||||||
|
memgraph::query::AuthQuery::FineGrainedPrivilege::READ)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
++*impl_it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace
|
||||||
|
|
||||||
mgp_error mgp_vertex_iter_in_edges(mgp_vertex *v, mgp_memory *memory, mgp_edges_iterator **result) {
|
mgp_error mgp_vertex_iter_in_edges(mgp_vertex *v, mgp_memory *memory, mgp_edges_iterator **result) {
|
||||||
return WrapExceptions(
|
return WrapExceptions(
|
||||||
[v, memory] {
|
[v, memory] {
|
||||||
@ -1851,6 +1908,9 @@ mgp_error mgp_vertex_iter_in_edges(mgp_vertex *v, mgp_memory *memory, mgp_edges_
|
|||||||
}
|
}
|
||||||
it->in.emplace(std::move(*maybe_edges));
|
it->in.emplace(std::move(*maybe_edges));
|
||||||
it->in_it.emplace(it->in->begin());
|
it->in_it.emplace(it->in->begin());
|
||||||
|
|
||||||
|
NextPermittedEdge(*it, true);
|
||||||
|
|
||||||
if (*it->in_it != it->in->end()) {
|
if (*it->in_it != it->in->end()) {
|
||||||
it->current_e.emplace(**it->in_it, v->graph, it->GetMemoryResource());
|
it->current_e.emplace(**it->in_it, v->graph, it->GetMemoryResource());
|
||||||
}
|
}
|
||||||
@ -1883,6 +1943,9 @@ mgp_error mgp_vertex_iter_out_edges(mgp_vertex *v, mgp_memory *memory, mgp_edges
|
|||||||
}
|
}
|
||||||
it->out.emplace(std::move(*maybe_edges));
|
it->out.emplace(std::move(*maybe_edges));
|
||||||
it->out_it.emplace(it->out->begin());
|
it->out_it.emplace(it->out->begin());
|
||||||
|
|
||||||
|
NextPermittedEdge(*it, false);
|
||||||
|
|
||||||
if (*it->out_it != it->out->end()) {
|
if (*it->out_it != it->out->end()) {
|
||||||
it->current_e.emplace(**it->out_it, v->graph, it->GetMemoryResource());
|
it->current_e.emplace(**it->out_it, v->graph, it->GetMemoryResource());
|
||||||
}
|
}
|
||||||
@ -1911,24 +1974,35 @@ mgp_error mgp_edges_iterator_next(mgp_edges_iterator *it, mgp_edge **result) {
|
|||||||
return WrapExceptions(
|
return WrapExceptions(
|
||||||
[it] {
|
[it] {
|
||||||
MG_ASSERT(it->in || it->out);
|
MG_ASSERT(it->in || it->out);
|
||||||
auto next = [&](auto *impl_it, const auto &end) -> mgp_edge * {
|
auto next = [it](const bool for_in) -> mgp_edge * {
|
||||||
|
auto &impl_it = for_in ? it->in_it : it->out_it;
|
||||||
|
const auto end = for_in ? it->in->end() : it->out->end();
|
||||||
if (*impl_it == end) {
|
if (*impl_it == end) {
|
||||||
MG_ASSERT(!it->current_e,
|
MG_ASSERT(!it->current_e,
|
||||||
"Iteration is already done, so it->current_e "
|
"Iteration is already done, so it->current_e "
|
||||||
"should have been set to std::nullopt");
|
"should have been set to std::nullopt");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
if (++(*impl_it) == end) {
|
|
||||||
|
++*impl_it;
|
||||||
|
|
||||||
|
NextPermittedEdge(*it, for_in);
|
||||||
|
|
||||||
|
if (*impl_it == end) {
|
||||||
it->current_e = std::nullopt;
|
it->current_e = std::nullopt;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
it->current_e.emplace(**impl_it, it->source_vertex.graph, it->GetMemoryResource());
|
it->current_e.emplace(**impl_it, it->source_vertex.graph, it->GetMemoryResource());
|
||||||
return &*it->current_e;
|
return &*it->current_e;
|
||||||
};
|
};
|
||||||
if (it->in_it) {
|
if (it->in_it) {
|
||||||
return next(&*it->in_it, it->in->end());
|
auto *result = next(true);
|
||||||
|
if (result != nullptr) {
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
return next(&*it->out_it, it->out->end());
|
}
|
||||||
|
return next(false);
|
||||||
},
|
},
|
||||||
result);
|
result);
|
||||||
}
|
}
|
||||||
@ -2002,6 +2076,12 @@ mgp_error mgp_edge_get_property(mgp_edge *e, const char *name, mgp_memory *memor
|
|||||||
|
|
||||||
mgp_error mgp_edge_set_property(struct mgp_edge *e, const char *property_name, mgp_value *property_value) {
|
mgp_error mgp_edge_set_property(struct mgp_edge *e, const char *property_name, mgp_value *property_value) {
|
||||||
return WrapExceptions([=] {
|
return WrapExceptions([=] {
|
||||||
|
if (e->from.graph->ctx && e->from.graph->ctx->auth_checker &&
|
||||||
|
!e->from.graph->ctx->auth_checker->Accept(*e->from.graph->ctx->db_accessor, e->impl,
|
||||||
|
memgraph::query::AuthQuery::FineGrainedPrivilege::UPDATE)) {
|
||||||
|
throw AuthorizationException{"Insufficient permissions for setting a property on edge!"};
|
||||||
|
}
|
||||||
|
|
||||||
if (!MgpEdgeIsMutable(*e)) {
|
if (!MgpEdgeIsMutable(*e)) {
|
||||||
throw ImmutableObjectException{"Cannot set a property on an immutable edge!"};
|
throw ImmutableObjectException{"Cannot set a property on an immutable edge!"};
|
||||||
}
|
}
|
||||||
@ -2057,7 +2137,8 @@ mgp_error mgp_edge_iter_properties(mgp_edge *e, mgp_memory *memory, mgp_properti
|
|||||||
throw DeletedObjectException{"Cannot get the properties of a deleted edge!"};
|
throw DeletedObjectException{"Cannot get the properties of a deleted edge!"};
|
||||||
case memgraph::storage::Error::NONEXISTENT_OBJECT:
|
case memgraph::storage::Error::NONEXISTENT_OBJECT:
|
||||||
LOG_FATAL(
|
LOG_FATAL(
|
||||||
"Query modules shouldn't have access to nonexistent objects when getting the properties of an edge.");
|
"Query modules shouldn't have access to nonexistent objects when getting the properties of an "
|
||||||
|
"edge.");
|
||||||
case memgraph::storage::Error::PROPERTIES_DISABLED:
|
case memgraph::storage::Error::PROPERTIES_DISABLED:
|
||||||
case memgraph::storage::Error::VERTEX_HAS_EDGES:
|
case memgraph::storage::Error::VERTEX_HAS_EDGES:
|
||||||
case memgraph::storage::Error::SERIALIZATION_ERROR:
|
case memgraph::storage::Error::SERIALIZATION_ERROR:
|
||||||
@ -2088,7 +2169,13 @@ mgp_error mgp_graph_is_mutable(mgp_graph *graph, int *result) {
|
|||||||
|
|
||||||
mgp_error mgp_graph_create_vertex(struct mgp_graph *graph, mgp_memory *memory, mgp_vertex **result) {
|
mgp_error mgp_graph_create_vertex(struct mgp_graph *graph, mgp_memory *memory, mgp_vertex **result) {
|
||||||
return WrapExceptions(
|
return WrapExceptions(
|
||||||
[=] {
|
[=]() -> mgp_vertex * {
|
||||||
|
if (graph->ctx && graph->ctx->auth_checker &&
|
||||||
|
!graph->ctx->auth_checker->HasGlobalPermissionOnVertices(
|
||||||
|
memgraph::query::AuthQuery::FineGrainedPrivilege::CREATE_DELETE)) {
|
||||||
|
throw AuthorizationException{"Insufficient permissions for creating vertices!"};
|
||||||
|
}
|
||||||
|
|
||||||
if (!MgpGraphIsMutable(*graph)) {
|
if (!MgpGraphIsMutable(*graph)) {
|
||||||
throw ImmutableObjectException{"Cannot create a vertex in an immutable graph!"};
|
throw ImmutableObjectException{"Cannot create a vertex in an immutable graph!"};
|
||||||
}
|
}
|
||||||
@ -2107,6 +2194,12 @@ mgp_error mgp_graph_create_vertex(struct mgp_graph *graph, mgp_memory *memory, m
|
|||||||
|
|
||||||
mgp_error mgp_graph_delete_vertex(struct mgp_graph *graph, mgp_vertex *vertex) {
|
mgp_error mgp_graph_delete_vertex(struct mgp_graph *graph, mgp_vertex *vertex) {
|
||||||
return WrapExceptions([=] {
|
return WrapExceptions([=] {
|
||||||
|
if (graph->ctx && graph->ctx->auth_checker &&
|
||||||
|
!graph->ctx->auth_checker->Accept(*graph->ctx->db_accessor, vertex->impl, graph->view,
|
||||||
|
memgraph::query::AuthQuery::FineGrainedPrivilege::CREATE_DELETE)) {
|
||||||
|
throw AuthorizationException{"Insufficient permissions for deleting a vertex!"};
|
||||||
|
}
|
||||||
|
|
||||||
if (!MgpGraphIsMutable(*graph)) {
|
if (!MgpGraphIsMutable(*graph)) {
|
||||||
throw ImmutableObjectException{"Cannot remove a vertex from an immutable graph!"};
|
throw ImmutableObjectException{"Cannot remove a vertex from an immutable graph!"};
|
||||||
}
|
}
|
||||||
@ -2142,6 +2235,12 @@ mgp_error mgp_graph_delete_vertex(struct mgp_graph *graph, mgp_vertex *vertex) {
|
|||||||
|
|
||||||
mgp_error mgp_graph_detach_delete_vertex(struct mgp_graph *graph, mgp_vertex *vertex) {
|
mgp_error mgp_graph_detach_delete_vertex(struct mgp_graph *graph, mgp_vertex *vertex) {
|
||||||
return WrapExceptions([=] {
|
return WrapExceptions([=] {
|
||||||
|
if (graph->ctx && graph->ctx->auth_checker &&
|
||||||
|
!graph->ctx->auth_checker->Accept(*graph->ctx->db_accessor, vertex->impl, graph->view,
|
||||||
|
memgraph::query::AuthQuery::FineGrainedPrivilege::CREATE_DELETE)) {
|
||||||
|
throw AuthorizationException{"Insufficient permissions for deleting a vertex!"};
|
||||||
|
}
|
||||||
|
|
||||||
if (!MgpGraphIsMutable(*graph)) {
|
if (!MgpGraphIsMutable(*graph)) {
|
||||||
throw ImmutableObjectException{"Cannot remove a vertex from an immutable graph!"};
|
throw ImmutableObjectException{"Cannot remove a vertex from an immutable graph!"};
|
||||||
}
|
}
|
||||||
@ -2188,7 +2287,13 @@ mgp_error mgp_graph_detach_delete_vertex(struct mgp_graph *graph, mgp_vertex *ve
|
|||||||
mgp_error mgp_graph_create_edge(mgp_graph *graph, mgp_vertex *from, mgp_vertex *to, mgp_edge_type type,
|
mgp_error mgp_graph_create_edge(mgp_graph *graph, mgp_vertex *from, mgp_vertex *to, mgp_edge_type type,
|
||||||
mgp_memory *memory, mgp_edge **result) {
|
mgp_memory *memory, mgp_edge **result) {
|
||||||
return WrapExceptions(
|
return WrapExceptions(
|
||||||
[=] {
|
[=]() -> mgp_edge * {
|
||||||
|
if (graph->ctx && graph->ctx->auth_checker &&
|
||||||
|
!graph->ctx->auth_checker->Accept(*graph->ctx->db_accessor, from->graph->impl->NameToEdgeType(type.name),
|
||||||
|
memgraph::query::AuthQuery::FineGrainedPrivilege::CREATE_DELETE)) {
|
||||||
|
throw AuthorizationException{"Insufficient permissions for creating edges!"};
|
||||||
|
}
|
||||||
|
|
||||||
if (!MgpGraphIsMutable(*graph)) {
|
if (!MgpGraphIsMutable(*graph)) {
|
||||||
throw ImmutableObjectException{"Cannot create an edge in an immutable graph!"};
|
throw ImmutableObjectException{"Cannot create an edge in an immutable graph!"};
|
||||||
}
|
}
|
||||||
@ -2221,6 +2326,11 @@ mgp_error mgp_graph_create_edge(mgp_graph *graph, mgp_vertex *from, mgp_vertex *
|
|||||||
|
|
||||||
mgp_error mgp_graph_delete_edge(struct mgp_graph *graph, mgp_edge *edge) {
|
mgp_error mgp_graph_delete_edge(struct mgp_graph *graph, mgp_edge *edge) {
|
||||||
return WrapExceptions([=] {
|
return WrapExceptions([=] {
|
||||||
|
if (graph->ctx && graph->ctx->auth_checker &&
|
||||||
|
!graph->ctx->auth_checker->Accept(*graph->ctx->db_accessor, edge->impl,
|
||||||
|
memgraph::query::AuthQuery::FineGrainedPrivilege::CREATE_DELETE)) {
|
||||||
|
throw AuthorizationException{"Insufficient permissions for deleting an edge!"};
|
||||||
|
}
|
||||||
if (!MgpGraphIsMutable(*graph)) {
|
if (!MgpGraphIsMutable(*graph)) {
|
||||||
throw ImmutableObjectException{"Cannot remove an edge from an immutable graph!"};
|
throw ImmutableObjectException{"Cannot remove an edge from an immutable graph!"};
|
||||||
}
|
}
|
||||||
@ -2253,7 +2363,7 @@ mgp_error mgp_graph_delete_edge(struct mgp_graph *graph, mgp_edge *edge) {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
void NextPermitted(mgp_vertices_iterator &it) {
|
void NextPermitted(mgp_vertices_iterator &it) {
|
||||||
if (!it.graph->ctx->auth_checker) {
|
if (!it.graph->ctx || !it.graph->ctx->auth_checker) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,6 +51,7 @@ PyObject *gMgpKeyAlreadyExistsError{nullptr}; // NOLINT(cppcoreguidelines-avo
|
|||||||
PyObject *gMgpImmutableObjectError{nullptr}; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
PyObject *gMgpImmutableObjectError{nullptr}; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||||
PyObject *gMgpValueConversionError{nullptr}; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
PyObject *gMgpValueConversionError{nullptr}; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||||
PyObject *gMgpSerializationError{nullptr}; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
PyObject *gMgpSerializationError{nullptr}; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||||
|
PyObject *gMgpAuthorizationError{nullptr}; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||||
|
|
||||||
// Returns true if an exception is raised
|
// Returns true if an exception is raised
|
||||||
bool RaiseExceptionFromErrorCode(const mgp_error error) {
|
bool RaiseExceptionFromErrorCode(const mgp_error error) {
|
||||||
@ -101,6 +102,10 @@ bool RaiseExceptionFromErrorCode(const mgp_error error) {
|
|||||||
PyErr_SetString(gMgpSerializationError, "Operation cannot be serialized.");
|
PyErr_SetString(gMgpSerializationError, "Operation cannot be serialized.");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
case mgp_error::MGP_ERROR_AUTHORIZATION_ERROR: {
|
||||||
|
PyErr_SetString(gMgpAuthorizationError, "Authorization Error. Permission Denied.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2192,6 +2197,7 @@ PyObject *PyInitMgpModule() {
|
|||||||
PyMgpError{"_mgp.ImmutableObjectError", gMgpImmutableObjectError, PyExc_RuntimeError, nullptr},
|
PyMgpError{"_mgp.ImmutableObjectError", gMgpImmutableObjectError, PyExc_RuntimeError, nullptr},
|
||||||
PyMgpError{"_mgp.ValueConversionError", gMgpValueConversionError, PyExc_RuntimeError, nullptr},
|
PyMgpError{"_mgp.ValueConversionError", gMgpValueConversionError, PyExc_RuntimeError, nullptr},
|
||||||
PyMgpError{"_mgp.SerializationError", gMgpSerializationError, PyExc_RuntimeError, nullptr},
|
PyMgpError{"_mgp.SerializationError", gMgpSerializationError, PyExc_RuntimeError, nullptr},
|
||||||
|
PyMgpError{"_mgp.AuthorizationError", gMgpAuthorizationError, PyExc_RuntimeError, nullptr},
|
||||||
};
|
};
|
||||||
Py_INCREF(Py_None);
|
Py_INCREF(Py_None);
|
||||||
|
|
||||||
|
@ -3,10 +3,11 @@ function(copy_lba_procedures_e2e_python_files FILE_NAME)
|
|||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
copy_lba_procedures_e2e_python_files(common.py)
|
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(show_privileges.py)
|
||||||
|
copy_lba_procedures_e2e_python_files(read_query_modules.py)
|
||||||
|
copy_lba_procedures_e2e_python_files(update_query_modules.py)
|
||||||
|
copy_lba_procedures_e2e_python_files(create_delete_query_modules.py)
|
||||||
copy_lba_procedures_e2e_python_files(read_permission_queries.py)
|
copy_lba_procedures_e2e_python_files(read_permission_queries.py)
|
||||||
copy_lba_procedures_e2e_python_files(update_permission_queries.py)
|
copy_lba_procedures_e2e_python_files(update_permission_queries.py)
|
||||||
|
|
||||||
|
|
||||||
add_subdirectory(procedures)
|
add_subdirectory(procedures)
|
||||||
|
@ -24,7 +24,7 @@ def connect(**kwargs) -> mgclient.Connection:
|
|||||||
return connection
|
return connection
|
||||||
|
|
||||||
|
|
||||||
def reset_permissions(admin_cursor: mgclient.Cursor, create_index: bool):
|
def reset_permissions(admin_cursor: mgclient.Cursor, create_index: bool = False):
|
||||||
execute_and_fetch_all(admin_cursor, "REVOKE LABELS * FROM user;")
|
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, "REVOKE EDGE_TYPES * FROM user;")
|
||||||
execute_and_fetch_all(admin_cursor, "MATCH(n) DETACH DELETE n;")
|
execute_and_fetch_all(admin_cursor, "MATCH(n) DETACH DELETE n;")
|
||||||
@ -32,6 +32,9 @@ def reset_permissions(admin_cursor: mgclient.Cursor, create_index: bool):
|
|||||||
execute_and_fetch_all(admin_cursor, "DROP INDEX ON :read_label;")
|
execute_and_fetch_all(admin_cursor, "DROP INDEX ON :read_label;")
|
||||||
|
|
||||||
execute_and_fetch_all(admin_cursor, "CREATE (n:read_label {prop: 5});")
|
execute_and_fetch_all(admin_cursor, "CREATE (n:read_label {prop: 5});")
|
||||||
|
execute_and_fetch_all(
|
||||||
|
admin_cursor, "CREATE (n:read_label_1 {prop: 5})-[r:read_edge_type]->(m:read_label_2 {prop: 5});"
|
||||||
|
)
|
||||||
|
|
||||||
if create_index:
|
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;")
|
||||||
@ -41,11 +44,26 @@ def reset_permissions(admin_cursor: mgclient.Cursor, create_index: bool):
|
|||||||
def reset_update_permissions(admin_cursor: mgclient.Cursor):
|
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 LABELS * FROM user;")
|
||||||
execute_and_fetch_all(admin_cursor, "REVOKE EDGE_TYPES * 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, "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 {prop: 1});")
|
||||||
execute_and_fetch_all(
|
execute_and_fetch_all(
|
||||||
admin_cursor,
|
admin_cursor,
|
||||||
"CREATE (n:update_label_1)-[r:update_edge_type]->(m:update_label_2);",
|
"CREATE (n:update_label_1)-[r:update_edge_type {prop: 1}]->(m:update_label_2);",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def reset_create_delete_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, "GRANT READ ON LABELS * TO user;")
|
||||||
|
execute_and_fetch_all(admin_cursor, "GRANT READ ON EDGE_TYPES * TO user;")
|
||||||
|
|
||||||
|
execute_and_fetch_all(admin_cursor, "MATCH (n) DETACH DELETE n;")
|
||||||
|
|
||||||
|
execute_and_fetch_all(admin_cursor, "CREATE (n:create_delete_label);")
|
||||||
|
execute_and_fetch_all(
|
||||||
|
admin_cursor,
|
||||||
|
"CREATE (n:create_delete_label_1)-[r:create_delete_edge_type]->(m:create_delete_label_2);",
|
||||||
)
|
)
|
||||||
|
299
tests/e2e/lba_procedures/create_delete_query_modules.py
Normal file
299
tests/e2e/lba_procedures/create_delete_query_modules.py
Normal file
@ -0,0 +1,299 @@
|
|||||||
|
# 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 common import (
|
||||||
|
connect,
|
||||||
|
execute_and_fetch_all,
|
||||||
|
mgclient,
|
||||||
|
reset_create_delete_permissions,
|
||||||
|
)
|
||||||
|
|
||||||
|
AUTHORIZATION_ERROR_IDENTIFIER = "AuthorizationError"
|
||||||
|
|
||||||
|
create_vertex_query = "CALL create_delete.create_vertex() YIELD created_node RETURN labels(created_node);"
|
||||||
|
remove_label_vertex_query = "CALL create_delete.remove_label('create_delete_label') YIELD node RETURN labels(node);"
|
||||||
|
set_label_vertex_query = "CALL create_delete.set_label('new_create_delete_label') YIELD node RETURN labels(node);"
|
||||||
|
create_edge_query = "MATCH (n:create_delete_label_1), (m:create_delete_label_2) CALL create_delete.create_edge(n, m) YIELD nr_of_edges RETURN nr_of_edges;"
|
||||||
|
delete_edge_query = "CALL create_delete.delete_edge() YIELD * RETURN *;"
|
||||||
|
|
||||||
|
|
||||||
|
def test_can_not_create_vertex_when_given_nothing():
|
||||||
|
admin_cursor = connect(username="admin", password="test").cursor()
|
||||||
|
reset_create_delete_permissions(admin_cursor)
|
||||||
|
|
||||||
|
test_cursor = connect(username="user", password="test").cursor()
|
||||||
|
|
||||||
|
with pytest.raises(mgclient.DatabaseError, match=AUTHORIZATION_ERROR_IDENTIFIER):
|
||||||
|
execute_and_fetch_all(test_cursor, create_vertex_query)
|
||||||
|
|
||||||
|
|
||||||
|
def test_can_create_vertex_when_given_global_create_delete():
|
||||||
|
admin_cursor = connect(username="admin", password="test").cursor()
|
||||||
|
reset_create_delete_permissions(admin_cursor)
|
||||||
|
|
||||||
|
execute_and_fetch_all(admin_cursor, "GRANT CREATE_DELETE ON LABELS * TO user;")
|
||||||
|
|
||||||
|
test_cursor = connect(username="user", password="test").cursor()
|
||||||
|
|
||||||
|
result = execute_and_fetch_all(test_cursor, create_vertex_query)
|
||||||
|
|
||||||
|
len(result[0][0]) == 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_can_not_create_vertex_when_given_global_read():
|
||||||
|
admin_cursor = connect(username="admin", password="test").cursor()
|
||||||
|
reset_create_delete_permissions(admin_cursor)
|
||||||
|
|
||||||
|
execute_and_fetch_all(admin_cursor, "GRANT READ ON LABELS * TO user;")
|
||||||
|
|
||||||
|
test_cursor = connect(username="user", password="test").cursor()
|
||||||
|
|
||||||
|
with pytest.raises(mgclient.DatabaseError, match=AUTHORIZATION_ERROR_IDENTIFIER):
|
||||||
|
execute_and_fetch_all(test_cursor, create_vertex_query)
|
||||||
|
|
||||||
|
|
||||||
|
def test_can_not_create_vertex_when_given_global_update():
|
||||||
|
admin_cursor = connect(username="admin", password="test").cursor()
|
||||||
|
reset_create_delete_permissions(admin_cursor)
|
||||||
|
|
||||||
|
execute_and_fetch_all(admin_cursor, "GRANT UPDATE ON LABELS :create_delete_label TO user;")
|
||||||
|
|
||||||
|
test_cursor = connect(username="user", password="test").cursor()
|
||||||
|
|
||||||
|
with pytest.raises(mgclient.DatabaseError, match=AUTHORIZATION_ERROR_IDENTIFIER):
|
||||||
|
execute_and_fetch_all(test_cursor, create_vertex_query)
|
||||||
|
|
||||||
|
|
||||||
|
def test_can_add_vertex_label_when_given_create_delete():
|
||||||
|
admin_cursor = connect(username="admin", password="test").cursor()
|
||||||
|
reset_create_delete_permissions(admin_cursor)
|
||||||
|
|
||||||
|
execute_and_fetch_all(
|
||||||
|
admin_cursor,
|
||||||
|
"GRANT CREATE_DELETE ON LABELS :new_create_delete_label, UPDATE ON LABELS :create_delete_label TO user;",
|
||||||
|
)
|
||||||
|
|
||||||
|
test_cursor = connect(username="user", password="test").cursor()
|
||||||
|
result = execute_and_fetch_all(test_cursor, set_label_vertex_query)
|
||||||
|
|
||||||
|
assert "create_delete_label" in result[0][0]
|
||||||
|
assert "new_create_delete_label" in result[0][0]
|
||||||
|
|
||||||
|
|
||||||
|
def test_can_not_add_vertex_label_when_given_update():
|
||||||
|
admin_cursor = connect(username="admin", password="test").cursor()
|
||||||
|
reset_create_delete_permissions(admin_cursor)
|
||||||
|
|
||||||
|
execute_and_fetch_all(
|
||||||
|
admin_cursor, "GRANT UPDATE ON LABELS :new_create_delete_label, :create_delete_label TO user;"
|
||||||
|
)
|
||||||
|
|
||||||
|
test_cursor = connect(username="user", password="test").cursor()
|
||||||
|
with pytest.raises(mgclient.DatabaseError, match=AUTHORIZATION_ERROR_IDENTIFIER):
|
||||||
|
execute_and_fetch_all(test_cursor, set_label_vertex_query)
|
||||||
|
|
||||||
|
|
||||||
|
def test_can_not_add_vertex_label_when_given_read():
|
||||||
|
admin_cursor = connect(username="admin", password="test").cursor()
|
||||||
|
reset_create_delete_permissions(admin_cursor)
|
||||||
|
|
||||||
|
execute_and_fetch_all(
|
||||||
|
admin_cursor, "GRANT READ ON LABELS :new_create_delete_label, UPDATE ON LABELS :create_delete_label TO user;"
|
||||||
|
)
|
||||||
|
|
||||||
|
test_cursor = connect(username="user", password="test").cursor()
|
||||||
|
with pytest.raises(mgclient.DatabaseError, match=AUTHORIZATION_ERROR_IDENTIFIER):
|
||||||
|
execute_and_fetch_all(test_cursor, set_label_vertex_query)
|
||||||
|
|
||||||
|
|
||||||
|
def test_can_remove_vertex_label_when_given_create_delete():
|
||||||
|
admin_cursor = connect(username="admin", password="test").cursor()
|
||||||
|
reset_create_delete_permissions(admin_cursor)
|
||||||
|
|
||||||
|
execute_and_fetch_all(admin_cursor, "GRANT CREATE_DELETE ON LABELS :create_delete_label TO user;")
|
||||||
|
|
||||||
|
test_cursor = connect(username="user", password="test").cursor()
|
||||||
|
result = execute_and_fetch_all(test_cursor, remove_label_vertex_query)
|
||||||
|
|
||||||
|
assert result[0][0] != ":create_delete_label"
|
||||||
|
|
||||||
|
|
||||||
|
def test_can_remove_vertex_label_when_given_global_create_delete():
|
||||||
|
admin_cursor = connect(username="admin", password="test").cursor()
|
||||||
|
reset_create_delete_permissions(admin_cursor)
|
||||||
|
|
||||||
|
execute_and_fetch_all(admin_cursor, "GRANT CREATE_DELETE ON LABELS * TO user;")
|
||||||
|
|
||||||
|
test_cursor = connect(username="user", password="test").cursor()
|
||||||
|
result = execute_and_fetch_all(test_cursor, remove_label_vertex_query)
|
||||||
|
|
||||||
|
assert result[0][0] != ":create_delete_label"
|
||||||
|
|
||||||
|
|
||||||
|
def test_can_not_remove_vertex_label_when_given_update():
|
||||||
|
admin_cursor = connect(username="admin", password="test").cursor()
|
||||||
|
reset_create_delete_permissions(admin_cursor)
|
||||||
|
|
||||||
|
execute_and_fetch_all(admin_cursor, "GRANT UPDATE ON LABELS :create_delete_label TO user;")
|
||||||
|
|
||||||
|
test_cursor = connect(username="user", password="test").cursor()
|
||||||
|
|
||||||
|
with pytest.raises(mgclient.DatabaseError, match=AUTHORIZATION_ERROR_IDENTIFIER):
|
||||||
|
execute_and_fetch_all(test_cursor, remove_label_vertex_query)
|
||||||
|
|
||||||
|
|
||||||
|
def test_can_not_remove_vertex_label_when_given_global_update():
|
||||||
|
admin_cursor = connect(username="admin", password="test").cursor()
|
||||||
|
reset_create_delete_permissions(admin_cursor)
|
||||||
|
|
||||||
|
execute_and_fetch_all(admin_cursor, "GRANT UPDATE ON LABELS * TO user;")
|
||||||
|
|
||||||
|
test_cursor = connect(username="user", password="test").cursor()
|
||||||
|
|
||||||
|
with pytest.raises(mgclient.DatabaseError, match=AUTHORIZATION_ERROR_IDENTIFIER):
|
||||||
|
execute_and_fetch_all(test_cursor, remove_label_vertex_query)
|
||||||
|
|
||||||
|
|
||||||
|
def test_can_not_remove_vertex_label_when_given_read():
|
||||||
|
admin_cursor = connect(username="admin", password="test").cursor()
|
||||||
|
reset_create_delete_permissions(admin_cursor)
|
||||||
|
|
||||||
|
execute_and_fetch_all(admin_cursor, "GRANT READ ON LABELS :create_delete_label TO user;")
|
||||||
|
|
||||||
|
test_cursor = connect(username="user", password="test").cursor()
|
||||||
|
|
||||||
|
with pytest.raises(mgclient.DatabaseError, match=AUTHORIZATION_ERROR_IDENTIFIER):
|
||||||
|
execute_and_fetch_all(test_cursor, remove_label_vertex_query)
|
||||||
|
|
||||||
|
|
||||||
|
def test_can_not_remove_vertex_label_when_given_global_read():
|
||||||
|
admin_cursor = connect(username="admin", password="test").cursor()
|
||||||
|
reset_create_delete_permissions(admin_cursor)
|
||||||
|
|
||||||
|
execute_and_fetch_all(admin_cursor, "GRANT READ ON LABELS * TO user;")
|
||||||
|
|
||||||
|
test_cursor = connect(username="user", password="test").cursor()
|
||||||
|
|
||||||
|
with pytest.raises(mgclient.DatabaseError, match=AUTHORIZATION_ERROR_IDENTIFIER):
|
||||||
|
execute_and_fetch_all(test_cursor, remove_label_vertex_query)
|
||||||
|
|
||||||
|
|
||||||
|
def test_can_not_create_edge_when_given_nothing():
|
||||||
|
admin_cursor = connect(username="admin", password="test").cursor()
|
||||||
|
reset_create_delete_permissions(admin_cursor)
|
||||||
|
|
||||||
|
test_cursor = connect(username="user", password="test").cursor()
|
||||||
|
|
||||||
|
with pytest.raises(mgclient.DatabaseError, match=AUTHORIZATION_ERROR_IDENTIFIER):
|
||||||
|
execute_and_fetch_all(test_cursor, create_edge_query)
|
||||||
|
|
||||||
|
|
||||||
|
def test_can_not_create_edge_when_given_read():
|
||||||
|
admin_cursor = connect(username="admin", password="test").cursor()
|
||||||
|
reset_create_delete_permissions(admin_cursor)
|
||||||
|
|
||||||
|
execute_and_fetch_all(admin_cursor, "GRANT READ ON EDGE_TYPES :new_create_delete_edge_type TO user")
|
||||||
|
|
||||||
|
test_cursor = connect(username="user", password="test").cursor()
|
||||||
|
|
||||||
|
with pytest.raises(mgclient.DatabaseError, match=AUTHORIZATION_ERROR_IDENTIFIER):
|
||||||
|
execute_and_fetch_all(test_cursor, create_edge_query)
|
||||||
|
|
||||||
|
|
||||||
|
def test_can_not_create_edge_when_given_update():
|
||||||
|
admin_cursor = connect(username="admin", password="test").cursor()
|
||||||
|
reset_create_delete_permissions(admin_cursor)
|
||||||
|
|
||||||
|
execute_and_fetch_all(admin_cursor, "GRANT UPDATE ON EDGE_TYPES :new_create_delete_edge_type TO user")
|
||||||
|
|
||||||
|
test_cursor = connect(username="user", password="test").cursor()
|
||||||
|
|
||||||
|
with pytest.raises(mgclient.DatabaseError, match=AUTHORIZATION_ERROR_IDENTIFIER):
|
||||||
|
execute_and_fetch_all(test_cursor, create_edge_query)
|
||||||
|
|
||||||
|
|
||||||
|
def test_can_create_edge_when_given_create_delete():
|
||||||
|
admin_cursor = connect(username="admin", password="test").cursor()
|
||||||
|
reset_create_delete_permissions(admin_cursor)
|
||||||
|
|
||||||
|
execute_and_fetch_all(
|
||||||
|
admin_cursor,
|
||||||
|
"GRANT CREATE_DELETE ON EDGE_TYPES :new_create_delete_edge_type TO user",
|
||||||
|
)
|
||||||
|
|
||||||
|
test_cursor = connect(username="user", password="test").cursor()
|
||||||
|
|
||||||
|
no_of_edges = execute_and_fetch_all(test_cursor, create_edge_query)
|
||||||
|
|
||||||
|
assert no_of_edges[0][0] == 2
|
||||||
|
|
||||||
|
|
||||||
|
def test_can_not_delete_edge_when_given_nothing():
|
||||||
|
admin_cursor = connect(username="admin", password="test").cursor()
|
||||||
|
reset_create_delete_permissions(admin_cursor)
|
||||||
|
|
||||||
|
test_cursor = connect(username="user", password="test").cursor()
|
||||||
|
|
||||||
|
with pytest.raises(mgclient.DatabaseError, match=AUTHORIZATION_ERROR_IDENTIFIER):
|
||||||
|
execute_and_fetch_all(test_cursor, delete_edge_query)
|
||||||
|
|
||||||
|
|
||||||
|
def test_can_not_delete_edge_when_given_read():
|
||||||
|
admin_cursor = connect(username="admin", password="test").cursor()
|
||||||
|
reset_create_delete_permissions(admin_cursor)
|
||||||
|
|
||||||
|
execute_and_fetch_all(
|
||||||
|
admin_cursor,
|
||||||
|
"GRANT READ ON EDGE_TYPES :create_delete_edge_type TO user",
|
||||||
|
)
|
||||||
|
|
||||||
|
test_cursor = connect(username="user", password="test").cursor()
|
||||||
|
|
||||||
|
with pytest.raises(mgclient.DatabaseError, match=AUTHORIZATION_ERROR_IDENTIFIER):
|
||||||
|
execute_and_fetch_all(test_cursor, delete_edge_query)
|
||||||
|
|
||||||
|
|
||||||
|
def test_can_not_delete_edge_when_given_update():
|
||||||
|
admin_cursor = connect(username="admin", password="test").cursor()
|
||||||
|
reset_create_delete_permissions(admin_cursor)
|
||||||
|
|
||||||
|
execute_and_fetch_all(
|
||||||
|
admin_cursor,
|
||||||
|
"GRANT UPDATE ON EDGE_TYPES :create_delete_edge_type TO user",
|
||||||
|
)
|
||||||
|
|
||||||
|
test_cursor = connect(username="user", password="test").cursor()
|
||||||
|
|
||||||
|
with pytest.raises(mgclient.DatabaseError, match=AUTHORIZATION_ERROR_IDENTIFIER):
|
||||||
|
execute_and_fetch_all(test_cursor, delete_edge_query)
|
||||||
|
|
||||||
|
|
||||||
|
def test_can_delete_edge_when_given_create_delete():
|
||||||
|
admin_cursor = connect(username="admin", password="test").cursor()
|
||||||
|
reset_create_delete_permissions(admin_cursor)
|
||||||
|
|
||||||
|
execute_and_fetch_all(
|
||||||
|
admin_cursor,
|
||||||
|
"GRANT CREATE_DELETE ON EDGE_TYPES :create_delete_edge_type TO user",
|
||||||
|
)
|
||||||
|
|
||||||
|
test_cursor = connect(username="user", password="test").cursor()
|
||||||
|
|
||||||
|
no_of_edges = execute_and_fetch_all(test_cursor, delete_edge_query)
|
||||||
|
|
||||||
|
assert no_of_edges[0][0] == 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(pytest.main([__file__, "-rA"]))
|
@ -1,30 +0,0 @@
|
|||||||
# 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
|
|
||||||
|
|
||||||
|
|
||||||
def test_lba_procedures_vertices_iterator_count_only_permitted_vertices():
|
|
||||||
cursor = connect(username="Josip", password="").cursor()
|
|
||||||
result = execute_and_fetch_all(cursor, "CALL read.number_of_visible_nodes() YIELD nr_of_nodes RETURN nr_of_nodes ;")
|
|
||||||
|
|
||||||
assert result[0][0] == 10
|
|
||||||
|
|
||||||
cursor = connect(username="Boris", password="").cursor()
|
|
||||||
result = execute_and_fetch_all(cursor, "CALL read.number_of_visible_nodes() YIELD nr_of_nodes RETURN nr_of_nodes ;")
|
|
||||||
|
|
||||||
assert result[0][0] == 6
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
sys.exit(pytest.main([__file__, "-rA"]))
|
|
@ -1 +1,3 @@
|
|||||||
copy_lba_procedures_e2e_python_files(read.py)
|
copy_lba_procedures_e2e_python_files(read.py)
|
||||||
|
copy_lba_procedures_e2e_python_files(update.py)
|
||||||
|
copy_lba_procedures_e2e_python_files(create_delete.py)
|
||||||
|
63
tests/e2e/lba_procedures/procedures/create_delete.py
Normal file
63
tests/e2e/lba_procedures/procedures/create_delete.py
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
# Copyright 2021 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 mgp
|
||||||
|
|
||||||
|
|
||||||
|
@mgp.write_proc
|
||||||
|
def create_vertex(ctx: mgp.ProcCtx) -> mgp.Record(created_node=mgp.Vertex):
|
||||||
|
vertex = ctx.graph.create_vertex()
|
||||||
|
return mgp.Record(created_node=vertex)
|
||||||
|
|
||||||
|
|
||||||
|
@mgp.write_proc
|
||||||
|
def remove_label(ctx: mgp.ProcCtx, label: str) -> mgp.Record(node=mgp.Vertex):
|
||||||
|
for vertex in ctx.graph.vertices:
|
||||||
|
if "create_delete_label" in vertex.labels:
|
||||||
|
break
|
||||||
|
|
||||||
|
vertex.remove_label(label)
|
||||||
|
return mgp.Record(node=vertex)
|
||||||
|
|
||||||
|
|
||||||
|
@mgp.write_proc
|
||||||
|
def set_label(ctx: mgp.ProcCtx, new_label: str) -> mgp.Record(node=mgp.Vertex):
|
||||||
|
for vertex in ctx.graph.vertices:
|
||||||
|
if "create_delete_label" in vertex.labels:
|
||||||
|
break
|
||||||
|
|
||||||
|
vertex.add_label(new_label)
|
||||||
|
return mgp.Record(node=vertex)
|
||||||
|
|
||||||
|
|
||||||
|
@mgp.write_proc
|
||||||
|
def create_edge(ctx: mgp.ProcCtx, v1: mgp.Vertex, v2: mgp.Vertex) -> mgp.Record(nr_of_edges=int):
|
||||||
|
ctx.graph.create_edge(v1, v2, mgp.EdgeType("new_create_delete_edge_type"))
|
||||||
|
|
||||||
|
count = 0
|
||||||
|
for vertex in ctx.graph.vertices:
|
||||||
|
for _ in vertex.out_edges:
|
||||||
|
count += 1
|
||||||
|
|
||||||
|
return mgp.Record(nr_of_edges=count)
|
||||||
|
|
||||||
|
|
||||||
|
@mgp.write_proc
|
||||||
|
def delete_edge(ctx: mgp.ProcCtx) -> mgp.Record(edge_count=int):
|
||||||
|
count = 0
|
||||||
|
for vertex in ctx.graph.vertices:
|
||||||
|
for edge in vertex.out_edges:
|
||||||
|
if edge.type.name == "create_delete_edge_type":
|
||||||
|
ctx.graph.delete_edge(edge)
|
||||||
|
else:
|
||||||
|
count += 1
|
||||||
|
|
||||||
|
return mgp.Record(edge_count=count)
|
@ -15,3 +15,13 @@ import mgp
|
|||||||
@mgp.read_proc
|
@mgp.read_proc
|
||||||
def number_of_visible_nodes(ctx: mgp.ProcCtx) -> mgp.Record(nr_of_nodes=int):
|
def number_of_visible_nodes(ctx: mgp.ProcCtx) -> mgp.Record(nr_of_nodes=int):
|
||||||
return mgp.Record(nr_of_nodes=len(mgp.Vertices(ctx.graph._graph)))
|
return mgp.Record(nr_of_nodes=len(mgp.Vertices(ctx.graph._graph)))
|
||||||
|
|
||||||
|
|
||||||
|
@mgp.read_proc
|
||||||
|
def number_of_visible_edges(ctx: mgp.ProcCtx) -> mgp.Record(nr_of_edges=int):
|
||||||
|
count = 0
|
||||||
|
for vertex in ctx.graph.vertices:
|
||||||
|
for _ in vertex.out_edges:
|
||||||
|
count += 1
|
||||||
|
|
||||||
|
return mgp.Record(nr_of_edges=count)
|
||||||
|
21
tests/e2e/lba_procedures/procedures/update.py
Normal file
21
tests/e2e/lba_procedures/procedures/update.py
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# Copyright 2021 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 mgp
|
||||||
|
|
||||||
|
|
||||||
|
@mgp.write_proc
|
||||||
|
def set_property(ctx: mgp.ProcCtx, object: mgp.Any) -> mgp.Record():
|
||||||
|
try:
|
||||||
|
object.properties.set("prop", 2)
|
||||||
|
except mgp.AuthorizationError:
|
||||||
|
pass
|
||||||
|
return mgp.Record()
|
@ -19,10 +19,10 @@ from common import connect, execute_and_fetch_all, reset_permissions
|
|||||||
match_query = "MATCH (n) RETURN n;"
|
match_query = "MATCH (n) RETURN n;"
|
||||||
match_by_id_query = "MATCH (n) WHERE ID(n) >= 0 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_query = "MATCH (n) RETURN n;"
|
||||||
match_by_label_property_range_query = "MATCH (n:read_label) WHERE n.prop < 7 RETURN n;"
|
match_by_label_property_range_query = "MATCH (n) WHERE n.prop < 7 RETURN n;"
|
||||||
match_by_label_property_value_query = "MATCH (n:read_label {prop: 5}) RETURN n;"
|
match_by_label_property_value_query = "MATCH (n {prop: 5}) RETURN n;"
|
||||||
match_by_label_property_query = "MATCH (n:read_label) WHERE n.prop IS NOT NULL RETURN n;"
|
match_by_label_property_query = "MATCH (n) WHERE n.prop IS NOT NULL RETURN n;"
|
||||||
|
|
||||||
|
|
||||||
read_node_without_index_operation_cases = [
|
read_node_without_index_operation_cases = [
|
||||||
@ -34,6 +34,7 @@ read_node_without_index_operation_cases = [
|
|||||||
["GRANT CREATE_DELETE ON LABELS * TO user;"],
|
["GRANT CREATE_DELETE ON LABELS * TO user;"],
|
||||||
]
|
]
|
||||||
|
|
||||||
|
read_node_without_index_operation_cases_expected_size = [1, 3, 1, 3, 1, 3]
|
||||||
|
|
||||||
read_node_with_index_operation_cases = [
|
read_node_with_index_operation_cases = [
|
||||||
["GRANT READ ON LABELS :read_label TO user;"],
|
["GRANT READ ON LABELS :read_label TO user;"],
|
||||||
@ -44,6 +45,7 @@ read_node_with_index_operation_cases = [
|
|||||||
["GRANT CREATE_DELETE ON LABELS * TO user;"],
|
["GRANT CREATE_DELETE ON LABELS * TO user;"],
|
||||||
]
|
]
|
||||||
|
|
||||||
|
read_node_with_index_operation_cases_expected_sizes = [1, 3, 1, 3, 1, 3]
|
||||||
|
|
||||||
not_read_node_without_index_operation_cases = [
|
not_read_node_without_index_operation_cases = [
|
||||||
[],
|
[],
|
||||||
@ -67,6 +69,7 @@ not_read_node_without_index_operation_cases = [
|
|||||||
],
|
],
|
||||||
]
|
]
|
||||||
|
|
||||||
|
not_read_node_without_index_operation_cases_expected_sizes = [0, 0, 0, 0, 2, 0, 2]
|
||||||
|
|
||||||
not_read_node_with_index_operation_cases = [
|
not_read_node_with_index_operation_cases = [
|
||||||
[],
|
[],
|
||||||
@ -90,6 +93,8 @@ not_read_node_with_index_operation_cases = [
|
|||||||
],
|
],
|
||||||
]
|
]
|
||||||
|
|
||||||
|
not_read_node_with_index_operation_cases_expexted_sizes = [0, 0, 0, 0, 2, 0, 2]
|
||||||
|
|
||||||
|
|
||||||
def get_admin_cursor():
|
def get_admin_cursor():
|
||||||
return connect(username="admin", password="test").cursor()
|
return connect(username="admin", password="test").cursor()
|
||||||
@ -100,7 +105,7 @@ def get_user_cursor():
|
|||||||
|
|
||||||
|
|
||||||
def execute_read_node_assertion(
|
def execute_read_node_assertion(
|
||||||
operation_case: List[str], queries: List[str], create_index: bool, can_read: bool
|
operation_case: List[str], queries: List[str], create_index: bool, expected_size: int
|
||||||
) -> None:
|
) -> None:
|
||||||
admin_cursor = get_admin_cursor()
|
admin_cursor = get_admin_cursor()
|
||||||
user_cursor = get_user_cursor()
|
user_cursor = get_user_cursor()
|
||||||
@ -110,10 +115,9 @@ def execute_read_node_assertion(
|
|||||||
for operation in operation_case:
|
for operation in operation_case:
|
||||||
execute_and_fetch_all(admin_cursor, operation)
|
execute_and_fetch_all(admin_cursor, operation)
|
||||||
|
|
||||||
read_size = 1 if can_read else 0
|
|
||||||
for mq in queries:
|
for mq in queries:
|
||||||
results = execute_and_fetch_all(user_cursor, mq)
|
results = execute_and_fetch_all(user_cursor, mq)
|
||||||
assert len(results) == read_size
|
assert len(results) == expected_size
|
||||||
|
|
||||||
|
|
||||||
def test_can_read_node_when_authorized():
|
def test_can_read_node_when_authorized():
|
||||||
@ -125,10 +129,14 @@ def test_can_read_node_when_authorized():
|
|||||||
match_by_label_property_value_query,
|
match_by_label_property_value_query,
|
||||||
]
|
]
|
||||||
|
|
||||||
for operation_case in read_node_without_index_operation_cases:
|
for expected_size, operation_case in zip(
|
||||||
execute_read_node_assertion(operation_case, match_queries_without_index, False, True)
|
read_node_without_index_operation_cases_expected_size, read_node_without_index_operation_cases
|
||||||
for operation_case in read_node_with_index_operation_cases:
|
):
|
||||||
execute_read_node_assertion(operation_case, match_queries_with_index, True, True)
|
execute_read_node_assertion(operation_case, match_queries_without_index, False, expected_size)
|
||||||
|
for expected_size, operation_case in zip(
|
||||||
|
read_node_with_index_operation_cases_expected_sizes, read_node_with_index_operation_cases
|
||||||
|
):
|
||||||
|
execute_read_node_assertion(operation_case, match_queries_with_index, True, expected_size)
|
||||||
|
|
||||||
|
|
||||||
def test_can_not_read_node_when_authorized():
|
def test_can_not_read_node_when_authorized():
|
||||||
@ -140,10 +148,14 @@ def test_can_not_read_node_when_authorized():
|
|||||||
match_by_label_property_value_query,
|
match_by_label_property_value_query,
|
||||||
]
|
]
|
||||||
|
|
||||||
for operation_case in not_read_node_without_index_operation_cases:
|
for expected_size, operation_case in zip(
|
||||||
execute_read_node_assertion(operation_case, match_queries_without_index, False, False)
|
not_read_node_without_index_operation_cases_expected_sizes, not_read_node_without_index_operation_cases
|
||||||
for operation_case in not_read_node_with_index_operation_cases:
|
):
|
||||||
execute_read_node_assertion(operation_case, match_queries_with_index, True, False)
|
execute_read_node_assertion(operation_case, match_queries_without_index, False, expected_size)
|
||||||
|
for expected_size, operation_case in zip(
|
||||||
|
not_read_node_with_index_operation_cases_expexted_sizes, not_read_node_with_index_operation_cases
|
||||||
|
):
|
||||||
|
execute_read_node_assertion(operation_case, match_queries_with_index, True, expected_size)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
225
tests/e2e/lba_procedures/read_query_modules.py
Normal file
225
tests/e2e/lba_procedures/read_query_modules.py
Normal file
@ -0,0 +1,225 @@
|
|||||||
|
# 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_permissions
|
||||||
|
|
||||||
|
get_number_of_vertices_query = "CALL read.number_of_visible_nodes() YIELD nr_of_nodes RETURN nr_of_nodes;"
|
||||||
|
get_number_of_edges_query = "CALL read.number_of_visible_edges() YIELD nr_of_edges RETURN nr_of_edges;"
|
||||||
|
|
||||||
|
|
||||||
|
def test_can_read_vertex_through_c_api_when_given_grant_on_label():
|
||||||
|
admin_cursor = connect(username="admin", password="test").cursor()
|
||||||
|
reset_permissions(admin_cursor)
|
||||||
|
|
||||||
|
execute_and_fetch_all(admin_cursor, "GRANT READ ON LABELS :read_label TO user;")
|
||||||
|
|
||||||
|
test_cursor = connect(username="user", password="test").cursor()
|
||||||
|
result = execute_and_fetch_all(test_cursor, get_number_of_vertices_query)
|
||||||
|
|
||||||
|
assert result[0][0] == 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_can_read_vertex_through_c_api_when_given_update_grant_on_label():
|
||||||
|
admin_cursor = connect(username="admin", password="test").cursor()
|
||||||
|
reset_permissions(admin_cursor)
|
||||||
|
|
||||||
|
execute_and_fetch_all(admin_cursor, "GRANT UPDATE ON LABELS :read_label TO user;")
|
||||||
|
|
||||||
|
test_cursor = connect(username="user", password="test").cursor()
|
||||||
|
result = execute_and_fetch_all(test_cursor, get_number_of_vertices_query)
|
||||||
|
|
||||||
|
assert result[0][0] == 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_can_read_vertex_through_c_api_when_given_create_delete_grant_on_label():
|
||||||
|
admin_cursor = connect(username="admin", password="test").cursor()
|
||||||
|
reset_permissions(admin_cursor)
|
||||||
|
|
||||||
|
execute_and_fetch_all(admin_cursor, "GRANT CREATE_DELETE ON LABELS :read_label TO user;")
|
||||||
|
|
||||||
|
test_cursor = connect(username="user", password="test").cursor()
|
||||||
|
result = execute_and_fetch_all(test_cursor, get_number_of_vertices_query)
|
||||||
|
|
||||||
|
assert result[0][0] == 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_can_not_read_vertex_through_c_api_when_given_nothing():
|
||||||
|
admin_cursor = connect(username="admin", password="test").cursor()
|
||||||
|
reset_permissions(admin_cursor)
|
||||||
|
|
||||||
|
test_cursor = connect(username="user", password="test").cursor()
|
||||||
|
result = execute_and_fetch_all(test_cursor, get_number_of_vertices_query)
|
||||||
|
|
||||||
|
assert result[0][0] == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_can_not_read_vertex_through_c_api_when_given_deny_on_label():
|
||||||
|
admin_cursor = connect(username="admin", password="test").cursor()
|
||||||
|
reset_permissions(admin_cursor)
|
||||||
|
|
||||||
|
execute_and_fetch_all(admin_cursor, "DENY READ ON LABELS :read_label TO user;")
|
||||||
|
|
||||||
|
test_cursor = connect(username="user", password="test").cursor()
|
||||||
|
result = execute_and_fetch_all(test_cursor, get_number_of_vertices_query)
|
||||||
|
|
||||||
|
assert result[0][0] == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_can_read_partial_vertices_through_c_api_when_given_global_read_but_deny_on_label():
|
||||||
|
admin_cursor = connect(username="admin", password="test").cursor()
|
||||||
|
reset_permissions(admin_cursor)
|
||||||
|
|
||||||
|
execute_and_fetch_all(admin_cursor, "DENY READ ON LABELS :read_label TO user;")
|
||||||
|
execute_and_fetch_all(admin_cursor, "GRANT READ ON LABELS * TO user;")
|
||||||
|
|
||||||
|
test_cursor = connect(username="user", password="test").cursor()
|
||||||
|
result = execute_and_fetch_all(test_cursor, get_number_of_vertices_query)
|
||||||
|
|
||||||
|
assert result[0][0] == 2
|
||||||
|
|
||||||
|
|
||||||
|
def test_can_read_partial_vertices_through_c_api_when_given_global_update_but_deny_on_label():
|
||||||
|
admin_cursor = connect(username="admin", password="test").cursor()
|
||||||
|
reset_permissions(admin_cursor)
|
||||||
|
|
||||||
|
execute_and_fetch_all(admin_cursor, "DENY READ ON LABELS :read_label TO user;")
|
||||||
|
execute_and_fetch_all(admin_cursor, "GRANT UPDATE ON LABELS * TO user;")
|
||||||
|
|
||||||
|
test_cursor = connect(username="user", password="test").cursor()
|
||||||
|
result = execute_and_fetch_all(test_cursor, get_number_of_vertices_query)
|
||||||
|
|
||||||
|
assert result[0][0] == 2
|
||||||
|
|
||||||
|
|
||||||
|
def test_can_read_partial_vertices_through_c_api_when_given_global_create_delete_but_deny_on_label():
|
||||||
|
admin_cursor = connect(username="admin", password="test").cursor()
|
||||||
|
reset_permissions(admin_cursor)
|
||||||
|
|
||||||
|
execute_and_fetch_all(admin_cursor, "DENY READ ON LABELS :read_label TO user;")
|
||||||
|
execute_and_fetch_all(admin_cursor, "GRANT CREATE_DELETE ON LABELS * TO user;")
|
||||||
|
|
||||||
|
test_cursor = connect(username="user", password="test").cursor()
|
||||||
|
result = execute_and_fetch_all(test_cursor, get_number_of_vertices_query)
|
||||||
|
|
||||||
|
assert result[0][0] == 2
|
||||||
|
|
||||||
|
|
||||||
|
def test_can_read_edge_through_c_api_when_given_grant_on_edge_type():
|
||||||
|
admin_cursor = connect(username="admin", password="test").cursor()
|
||||||
|
reset_permissions(admin_cursor)
|
||||||
|
|
||||||
|
execute_and_fetch_all(admin_cursor, "GRANT READ ON LABELS :read_label_1, :read_label_2 TO user;")
|
||||||
|
execute_and_fetch_all(admin_cursor, "GRANT READ ON EDGE_TYPES :read_edge_type TO user;")
|
||||||
|
|
||||||
|
test_cursor = connect(username="user", password="test").cursor()
|
||||||
|
result = execute_and_fetch_all(test_cursor, get_number_of_edges_query)
|
||||||
|
|
||||||
|
assert result[0][0] == 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_can_not_read_edge_through_c_api_when_given_deny_on_edge_type():
|
||||||
|
admin_cursor = connect(username="admin", password="test").cursor()
|
||||||
|
reset_permissions(admin_cursor)
|
||||||
|
|
||||||
|
execute_and_fetch_all(admin_cursor, "GRANT READ ON LABELS :read_label_1, :read_label_2 TO user;")
|
||||||
|
execute_and_fetch_all(admin_cursor, "DENY READ ON EDGE_TYPES :read_edge_type TO user;")
|
||||||
|
|
||||||
|
test_cursor = connect(username="user", password="test").cursor()
|
||||||
|
result = execute_and_fetch_all(test_cursor, get_number_of_edges_query)
|
||||||
|
|
||||||
|
assert result[0][0] == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_can_read_edge_through_c_api_when_given_grant_on_edge_type():
|
||||||
|
admin_cursor = connect(username="admin", password="test").cursor()
|
||||||
|
reset_permissions(admin_cursor)
|
||||||
|
|
||||||
|
execute_and_fetch_all(admin_cursor, "GRANT READ ON LABELS :read_label_1, :read_label_2 TO user;")
|
||||||
|
execute_and_fetch_all(admin_cursor, "GRANT READ ON EDGE_TYPES :read_edge_type TO user;")
|
||||||
|
|
||||||
|
test_cursor = connect(username="user", password="test").cursor()
|
||||||
|
result = execute_and_fetch_all(test_cursor, get_number_of_edges_query)
|
||||||
|
|
||||||
|
assert result[0][0] == 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_can_read_edge_through_c_api_when_given_update_on_edge_type():
|
||||||
|
admin_cursor = connect(username="admin", password="test").cursor()
|
||||||
|
reset_permissions(admin_cursor)
|
||||||
|
|
||||||
|
execute_and_fetch_all(admin_cursor, "GRANT READ ON LABELS :read_label_1, :read_label_2 TO user;")
|
||||||
|
execute_and_fetch_all(admin_cursor, "GRANT UPDATE ON EDGE_TYPES :read_edge_type TO user;")
|
||||||
|
|
||||||
|
test_cursor = connect(username="user", password="test").cursor()
|
||||||
|
result = execute_and_fetch_all(test_cursor, get_number_of_edges_query)
|
||||||
|
|
||||||
|
assert result[0][0] == 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_can_read_edge_through_c_api_when_given_create_delete_on_edge_type():
|
||||||
|
admin_cursor = connect(username="admin", password="test").cursor()
|
||||||
|
reset_permissions(admin_cursor)
|
||||||
|
|
||||||
|
execute_and_fetch_all(admin_cursor, "GRANT READ ON LABELS :read_label_1, :read_label_2 TO user;")
|
||||||
|
execute_and_fetch_all(admin_cursor, "GRANT CREATE_DELETE ON EDGE_TYPES :read_edge_type TO user;")
|
||||||
|
|
||||||
|
test_cursor = connect(username="user", password="test").cursor()
|
||||||
|
result = execute_and_fetch_all(test_cursor, get_number_of_edges_query)
|
||||||
|
|
||||||
|
assert result[0][0] == 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_can_not_read_edge_through_c_api_when_given_read_global_but_deny_on_edge_type():
|
||||||
|
admin_cursor = connect(username="admin", password="test").cursor()
|
||||||
|
reset_permissions(admin_cursor)
|
||||||
|
|
||||||
|
execute_and_fetch_all(admin_cursor, "GRANT READ ON LABELS :read_label_1, :read_label_2 TO user;")
|
||||||
|
execute_and_fetch_all(admin_cursor, "DENY READ ON EDGE_TYPES :read_edge_type TO user;")
|
||||||
|
execute_and_fetch_all(admin_cursor, "GRANT READ ON EDGE_TYPES * TO user;")
|
||||||
|
|
||||||
|
test_cursor = connect(username="user", password="test").cursor()
|
||||||
|
result = execute_and_fetch_all(test_cursor, get_number_of_edges_query)
|
||||||
|
|
||||||
|
assert result[0][0] == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_can_not_read_edge_through_c_api_when_given_update_global_but_deny_on_edge_type():
|
||||||
|
admin_cursor = connect(username="admin", password="test").cursor()
|
||||||
|
reset_permissions(admin_cursor)
|
||||||
|
|
||||||
|
execute_and_fetch_all(admin_cursor, "GRANT READ ON LABELS :read_label_1, :read_label_2 TO user;")
|
||||||
|
execute_and_fetch_all(admin_cursor, "DENY READ ON EDGE_TYPES :read_edge_type TO user;")
|
||||||
|
execute_and_fetch_all(admin_cursor, "GRANT UPDATE ON EDGE_TYPES * TO user;")
|
||||||
|
|
||||||
|
test_cursor = connect(username="user", password="test").cursor()
|
||||||
|
result = execute_and_fetch_all(test_cursor, get_number_of_edges_query)
|
||||||
|
|
||||||
|
assert result[0][0] == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_can_not_read_edge_through_c_api_when_given_create_delete_global_but_deny_on_edge_type():
|
||||||
|
admin_cursor = connect(username="admin", password="test").cursor()
|
||||||
|
reset_permissions(admin_cursor)
|
||||||
|
|
||||||
|
execute_and_fetch_all(admin_cursor, "GRANT READ ON LABELS :read_label_1, :read_label_2 TO user;")
|
||||||
|
execute_and_fetch_all(admin_cursor, "DENY READ ON EDGE_TYPES :read_edge_type TO user;")
|
||||||
|
execute_and_fetch_all(admin_cursor, "GRANT CREATE_DELETE ON EDGE_TYPES * TO user;")
|
||||||
|
|
||||||
|
test_cursor = connect(username="user", password="test").cursor()
|
||||||
|
result = execute_and_fetch_all(test_cursor, get_number_of_edges_query)
|
||||||
|
|
||||||
|
assert result[0][0] == 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(pytest.main([__file__, "-rA"]))
|
184
tests/e2e/lba_procedures/update_query_modules.py
Normal file
184
tests/e2e/lba_procedures/update_query_modules.py
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
# 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 common import (
|
||||||
|
connect,
|
||||||
|
execute_and_fetch_all,
|
||||||
|
reset_update_permissions,
|
||||||
|
)
|
||||||
|
|
||||||
|
set_vertex_property_query = "MATCH (n:update_label) CALL update.set_property(n) YIELD * RETURN n.prop;"
|
||||||
|
set_edge_property_query = "MATCH (n:update_label_1)-[r:update_edge_type]->(m:update_label_2) CALL update.set_property(r) YIELD * RETURN r.prop;"
|
||||||
|
|
||||||
|
|
||||||
|
def test_can_not_update_vertex_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()
|
||||||
|
result = execute_and_fetch_all(test_cursor, set_vertex_property_query)
|
||||||
|
|
||||||
|
assert result[0][0] == 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_can_update_vertex_when_given_update_grant_on_label():
|
||||||
|
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()
|
||||||
|
result = execute_and_fetch_all(test_cursor, set_vertex_property_query)
|
||||||
|
|
||||||
|
assert result[0][0] == 2
|
||||||
|
|
||||||
|
|
||||||
|
def test_can_update_vertex_when_given_create_delete_grant_on_label():
|
||||||
|
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()
|
||||||
|
result = execute_and_fetch_all(test_cursor, set_vertex_property_query)
|
||||||
|
|
||||||
|
assert result[0][0] == 2
|
||||||
|
|
||||||
|
|
||||||
|
def test_can_update_vertex_when_given_update_global_grant_on_label():
|
||||||
|
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()
|
||||||
|
result = execute_and_fetch_all(test_cursor, set_vertex_property_query)
|
||||||
|
|
||||||
|
assert result[0][0] == 2
|
||||||
|
|
||||||
|
|
||||||
|
def test_can_update_vertex_when_given_create_delete_global_grant_on_label():
|
||||||
|
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()
|
||||||
|
result = execute_and_fetch_all(test_cursor, set_vertex_property_query)
|
||||||
|
|
||||||
|
assert result[0][0] == 2
|
||||||
|
|
||||||
|
|
||||||
|
def test_can_not_update_vertex_when_denied_update_and_granted_global_update_on_label():
|
||||||
|
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;")
|
||||||
|
execute_and_fetch_all(admin_cursor, "GRANT UPDATE ON LABELS * TO user;")
|
||||||
|
|
||||||
|
test_cursor = connect(username="user", password="test").cursor()
|
||||||
|
result = execute_and_fetch_all(test_cursor, set_vertex_property_query)
|
||||||
|
|
||||||
|
assert result[0][0] == 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_can_not_update_vertex_when_denied_update_and_granted_global_create_delete_on_label():
|
||||||
|
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;")
|
||||||
|
execute_and_fetch_all(admin_cursor, "GRANT CREATE_DELETE ON LABELS * TO user;")
|
||||||
|
|
||||||
|
test_cursor = connect(username="user", password="test").cursor()
|
||||||
|
result = execute_and_fetch_all(test_cursor, set_vertex_property_query)
|
||||||
|
|
||||||
|
assert result[0][0] == 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_can_update_edge_when_given_update_grant_on_edge_type():
|
||||||
|
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_1 TO user;")
|
||||||
|
execute_and_fetch_all(admin_cursor, "GRANT READ ON LABELS :update_label_2 TO user;")
|
||||||
|
execute_and_fetch_all(admin_cursor, "GRANT UPDATE ON EDGE_TYPES :update_edge_type TO user;")
|
||||||
|
|
||||||
|
test_cursor = connect(username="user", password="test").cursor()
|
||||||
|
result = execute_and_fetch_all(test_cursor, set_edge_property_query)
|
||||||
|
|
||||||
|
assert result[0][0] == 2
|
||||||
|
|
||||||
|
|
||||||
|
def test_can_not_update_edge_when_given_read_grant_on_edge_type():
|
||||||
|
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_1 TO user;")
|
||||||
|
execute_and_fetch_all(admin_cursor, "GRANT READ ON LABELS :update_label_2 TO user;")
|
||||||
|
execute_and_fetch_all(admin_cursor, "GRANT READ ON EDGE_TYPES :update_edge_type TO user;")
|
||||||
|
|
||||||
|
test_cursor = connect(username="user", password="test").cursor()
|
||||||
|
result = execute_and_fetch_all(test_cursor, set_edge_property_query)
|
||||||
|
|
||||||
|
assert result[0][0] == 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_can_update_edge_when_given_create_delete_grant_on_edge_type():
|
||||||
|
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_1 TO user;")
|
||||||
|
execute_and_fetch_all(admin_cursor, "GRANT READ ON LABELS :update_label_2 TO user;")
|
||||||
|
execute_and_fetch_all(admin_cursor, "GRANT CREATE_DELETE ON EDGE_TYPES :update_edge_type TO user;")
|
||||||
|
|
||||||
|
test_cursor = connect(username="user", password="test").cursor()
|
||||||
|
result = execute_and_fetch_all(test_cursor, set_edge_property_query)
|
||||||
|
|
||||||
|
assert result[0][0] == 2
|
||||||
|
|
||||||
|
|
||||||
|
def test_can_not_update_edge_when_denied_update_edge_type_but_granted_global_update_on_edge_type():
|
||||||
|
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_1 TO user;")
|
||||||
|
execute_and_fetch_all(admin_cursor, "GRANT READ ON LABELS :update_label_2 TO user;")
|
||||||
|
execute_and_fetch_all(admin_cursor, "DENY UPDATE ON EDGE_TYPES :update_edge_type TO user;")
|
||||||
|
execute_and_fetch_all(admin_cursor, "DENY UPDATE ON EDGE_TYPES * TO user;")
|
||||||
|
|
||||||
|
test_cursor = connect(username="user", password="test").cursor()
|
||||||
|
result = execute_and_fetch_all(test_cursor, set_edge_property_query)
|
||||||
|
|
||||||
|
assert result[0][0] == 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_can_not_update_edge_when_denied_update_edge_type_but_granted_global_create_delete_on_edge_type():
|
||||||
|
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_1 TO user;")
|
||||||
|
execute_and_fetch_all(admin_cursor, "GRANT READ ON LABELS :update_label_2 TO user;")
|
||||||
|
execute_and_fetch_all(admin_cursor, "DENY UPDATE ON EDGE_TYPES :update_edge_type TO user;")
|
||||||
|
execute_and_fetch_all(admin_cursor, "DENY CREATE_DELETE ON EDGE_TYPES * TO user;")
|
||||||
|
|
||||||
|
test_cursor = connect(username="user", password="test").cursor()
|
||||||
|
result = execute_and_fetch_all(test_cursor, set_edge_property_query)
|
||||||
|
|
||||||
|
assert result[0][0] == 1
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(pytest.main([__file__, "-rA"]))
|
@ -1,22 +1,25 @@
|
|||||||
template_cluster: &template_cluster
|
read_query_modules_cluster: &read_query_modules_cluster
|
||||||
cluster:
|
cluster:
|
||||||
main:
|
main:
|
||||||
args: ["--bolt-port", "7687", "--log-level=TRACE"]
|
args: ["--bolt-port", "7687", "--log-level=TRACE"]
|
||||||
log_file: "lba-e2e.log"
|
log_file: "lba-e2e.log"
|
||||||
setup_queries:
|
setup_queries:
|
||||||
- "Create (:Label1 {id: 1}) ;"
|
- "CREATE USER admin IDENTIFIED BY 'test';"
|
||||||
- "Create (:Label1 {id: 2}) ;"
|
- "GRANT ALL PRIVILEGES TO admin"
|
||||||
- "Create (:Label1 {id: 3}) ;"
|
- "CREATE USER user IDENTIFIED BY 'test';"
|
||||||
- "Create (:Label1 {id: 4}) ;"
|
- "GRANT ALL PRIVILEGES TO user"
|
||||||
- "Create (:Label1 {id: 5}) ;"
|
validation_queries: []
|
||||||
- "Create (:Label1 {id: 6}) ;"
|
|
||||||
- "Create (:Label2 {id: 1}) ;"
|
update_query_modules_cluster: &update_query_modules_cluster
|
||||||
- "Create (:Label2 {id: 2}) ;"
|
cluster:
|
||||||
- "Create (:Label2 {id: 3}) ;"
|
main:
|
||||||
- "Create (:Label2 {id: 4}) ;"
|
args: ["--bolt-port", "7687", "--log-level=TRACE"]
|
||||||
- "Create User Josip ;"
|
log_file: "lba-e2e.log"
|
||||||
- "Create User Boris ;"
|
setup_queries:
|
||||||
- "Grant Read On Labels :Label1 to Boris;"
|
- "CREATE USER admin IDENTIFIED BY 'test';"
|
||||||
|
- "GRANT ALL PRIVILEGES TO admin"
|
||||||
|
- "CREATE USER user IDENTIFIED BY 'test';"
|
||||||
|
- "GRANT ALL PRIVILEGES TO user"
|
||||||
validation_queries: []
|
validation_queries: []
|
||||||
|
|
||||||
show_privileges_cluster: &show_privileges_cluster
|
show_privileges_cluster: &show_privileges_cluster
|
||||||
@ -54,8 +57,21 @@ show_privileges_cluster: &show_privileges_cluster
|
|||||||
- "Create User Bruno;"
|
- "Create User Bruno;"
|
||||||
- "Grant Auth to Bruno;"
|
- "Grant Auth to Bruno;"
|
||||||
- "Deny Create_Delete On Labels * to Bruno"
|
- "Deny Create_Delete On Labels * to Bruno"
|
||||||
|
validation_queries: []
|
||||||
|
|
||||||
read_permission_queries: &read_permission_queries
|
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: []
|
||||||
|
|
||||||
|
create_delete_query_modules_cluster: &create_delete_query_modules_cluster
|
||||||
cluster:
|
cluster:
|
||||||
main:
|
main:
|
||||||
args: ["--bolt-port", "7687", "--log-level=TRACE"]
|
args: ["--bolt-port", "7687", "--log-level=TRACE"]
|
||||||
@ -77,15 +93,26 @@ update_permission_queries_cluster: &update_permission_queries_cluster
|
|||||||
- "GRANT ALL PRIVILEGES TO admin;"
|
- "GRANT ALL PRIVILEGES TO admin;"
|
||||||
- "CREATE USER user IDENTIFIED BY 'test'"
|
- "CREATE USER user IDENTIFIED BY 'test'"
|
||||||
- "GRANT ALL PRIVILEGES TO user;"
|
- "GRANT ALL PRIVILEGES TO user;"
|
||||||
|
|
||||||
validation_queries: []
|
validation_queries: []
|
||||||
|
|
||||||
workloads:
|
workloads:
|
||||||
- name: "Label-based auth"
|
- name: "read-query-modules"
|
||||||
binary: "tests/e2e/pytest_runner.sh"
|
binary: "tests/e2e/pytest_runner.sh"
|
||||||
proc: "tests/e2e/lba_procedures/procedures/"
|
proc: "tests/e2e/lba_procedures/procedures/"
|
||||||
args: ["lba_procedures/lba_procedures.py"]
|
args: ["lba_procedures/read_query_modules.py"]
|
||||||
<<: *template_cluster
|
<<: *read_query_modules_cluster
|
||||||
|
|
||||||
|
- name: "update-query-modules"
|
||||||
|
binary: "tests/e2e/pytest_runner.sh"
|
||||||
|
proc: "tests/e2e/lba_procedures/procedures/"
|
||||||
|
args: ["lba_procedures/update_query_modules.py"]
|
||||||
|
<<: *update_query_modules_cluster
|
||||||
|
|
||||||
|
- name: "create-delete-query-modules"
|
||||||
|
binary: "tests/e2e/pytest_runner.sh"
|
||||||
|
proc: "tests/e2e/lba_procedures/procedures/"
|
||||||
|
args: ["lba_procedures/create_delete_query_modules.py"]
|
||||||
|
<<: *create_delete_query_modules_cluster
|
||||||
|
|
||||||
- name: "show-privileges"
|
- name: "show-privileges"
|
||||||
binary: "tests/e2e/pytest_runner.sh"
|
binary: "tests/e2e/pytest_runner.sh"
|
||||||
|
Loading…
Reference in New Issue
Block a user