Skip to content
Snippets Groups Projects
Verified Commit 78f84606 authored by Timm Fitschen's avatar Timm Fitschen
Browse files

REVERT: Remove implementation for role (temporarily)

parent 714d02d2
No related branches found
No related tags found
2 merge requests!42Release 0.2.0,!40F dot in username
Pipeline #25007 passed
Pipeline: caosdb-cppinttest

#25013

    This commit is part of merge request !40. Comments created here will be created in the context of that merge request.
    ...@@ -52,9 +52,7 @@ set(libcaosdb_INCL ...@@ -52,9 +52,7 @@ set(libcaosdb_INCL
    IF(BUILD_ACM) IF(BUILD_ACM)
    list(APPEND libcaosdb_INCL list(APPEND libcaosdb_INCL
    ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/acm/role.h
    ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/acm/user.h ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/acm/user.h
    ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/acm/permission_rule.h
    ) )
    ENDIF() ENDIF()
    ......
    /*
    * This file is a part of the CaosDB Project.
    *
    * Copyright (C) 2022 Timm Fitschen <t.fitschen@indiscale.com>
    * Copyright (C) 2022 IndiScale GmbH <info@indiscale.com>
    *
    * This program is free software: you can redistribute it and/or modify
    * it under the terms of the GNU Affero General Public License as
    * published by the Free Software Foundation, either version 3 of the
    * License, or (at your option) any later version.
    *
    * This program is distributed in the hope that it will be useful,
    * but WITHOUT ANY WARRANTY; without even the implied warranty of
    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    * GNU Affero General Public License for more details.
    *
    * You should have received a copy of the GNU Affero General Public License
    * along with this program. If not, see <https://www.gnu.org/licenses/>.
    *
    */
    /**
    * @brief PermissionRules grant or deny permissions to roles.
    *
    * @file caosdb/acm/permission_rule.h
    * @author Timm Fitchen
    * @date 2022-02-11
    */
    #ifdef BUILD_ACM
    #ifndef CAOSDB_ACM_PERMISSION_RULE_H
    #define CAOSDB_ACM_PERMISSION_RULE_H
    #include <stddef.h> // for size_t
    #include <unordered_set> // for unordered_set
    #include <string> // for string
    namespace caosdb::acm {
    class PermissionRuleImpl;
    class PermissionRule {
    public:
    PermissionRule(std::string permission, bool isGrant, bool isPriority);
    explicit PermissionRule(PermissionRuleImpl *wrapped);
    PermissionRule(const PermissionRule &rule);
    auto operator=(const PermissionRule &rule) -> PermissionRule &;
    ~PermissionRule();
    auto ToString() const -> std::string;
    [[nodiscard]] auto IsGrant() const -> bool;
    auto SetGrant(bool isGrant) -> void;
    [[nodiscard]] auto IsPriority() const -> bool;
    auto SetPriority(bool isPriority) -> void;
    [[nodiscard]] auto GetPermission() const -> const std::string &;
    auto operator==(const PermissionRule &other) const -> bool;
    private:
    PermissionRuleImpl *impl;
    };
    struct HashPermissionRule {
    auto operator()(const PermissionRule &rule) const -> size_t;
    };
    using PermissionRules = std::unordered_set<PermissionRule, HashPermissionRule>;
    } // namespace caosdb::acm
    #endif
    #endif
    /*
    * This file is a part of the CaosDB Project.
    *
    * Copyright (C) 2022 Timm Fitschen <t.fitschen@indiscale.com>
    * Copyright (C) 2022 IndiScale GmbH <info@indiscale.com>
    *
    * This program is free software: you can redistribute it and/or modify
    * it under the terms of the GNU Affero General Public License as
    * published by the Free Software Foundation, either version 3 of the
    * License, or (at your option) any later version.
    *
    * This program is distributed in the hope that it will be useful,
    * but WITHOUT ANY WARRANTY; without even the implied warranty of
    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    * GNU Affero General Public License for more details.
    *
    * You should have received a copy of the GNU Affero General Public License
    * along with this program. If not, see <https://www.gnu.org/licenses/>.
    *
    */
    /**
    * @brief Roles, together with users, and permissions are a fundamental concept
    * of the access controll management of CaosDB.
    *
    * @file caosdb/acm/role.h
    * @author Timm Fitchen
    * @date 2022-02-11
    */
    #ifdef BUILD_ACM
    #ifndef CAOSDB_ACM_ROLES_H
    #define CAOSDB_ACM_ROLES_H
    #include "caosdb/acm/permission_rule.h" // for PermissionRule
    #include <memory> // for unique_ptr
    #include <string> // for string
    namespace caosdb::connection {
    class Connection;
    }
    namespace caosdb::acm {
    class RoleImpl;
    class Role {
    public:
    explicit Role(const std::string &name);
    explicit Role(std::unique_ptr<RoleImpl> wrapped);
    Role(const std::string &name, const std::string &description);
    Role(const Role &role);
    auto operator=(const Role &role) -> Role &;
    ~Role();
    auto ToString() const -> std::string;
    [[nodiscard]] auto GetName() const -> const std::string &;
    auto SetName(const std::string &name) -> void;
    [[nodiscard]] auto GetDescription() const -> const std::string &;
    auto SetDescription(const std::string &description) -> void;
    [[nodiscard]] auto GetPermissionRules() const -> const PermissionRules &;
    // auto SetPermissionRules(PermissionRules rules) -> void;
    auto ClearPermissionRules() -> void;
    auto Grant(const std::string &permission, bool isPriority) -> void;
    auto Deny(const std::string &permission, bool isPriority) -> void;
    auto RevokeGrant(const std::string &permission, bool isPriority) -> void;
    auto RevokeDenial(const std::string &permission, bool isPriority) -> void;
    auto AddPermissionRule(const std::string &permission, bool isGrant, bool isPriority) -> void;
    auto RemovePermissionRule(const std::string &permission, bool isGrant, bool isPriority) -> void;
    friend class caosdb::connection::Connection;
    private:
    std::unique_ptr<RoleImpl> wrapped;
    };
    } // namespace caosdb::acm
    #endif
    #endif
    ...@@ -28,7 +28,6 @@ ...@@ -28,7 +28,6 @@
    * @brief Configuration and setup of the connection. * @brief Configuration and setup of the connection.
    */ */
    #ifdef BUILD_ACM #ifdef BUILD_ACM
    #include "caosdb/acm/role.h" // for Role
    #include "caosdb/acm/user.h" // for User #include "caosdb/acm/user.h" // for User
    #include "caosdb/acm/v1alpha1/main.grpc.pb.h" // for AccessControlMan... #include "caosdb/acm/v1alpha1/main.grpc.pb.h" // for AccessControlMan...
    #endif #endif
    ...@@ -44,11 +43,9 @@ ...@@ -44,11 +43,9 @@
    #include <map> // for map #include <map> // for map
    #include <memory> // for shared_ptr, unique_ptr #include <memory> // for shared_ptr, unique_ptr
    #include <string> // for string, basic_string #include <string> // for string, basic_string
    #include <vector> // for vector
    namespace caosdb::connection { namespace caosdb::connection {
    #ifdef BUILD_ACM #ifdef BUILD_ACM
    using caosdb::acm::Role;
    using caosdb::acm::User; using caosdb::acm::User;
    using caosdb::acm::v1alpha1::AccessControlManagementService; using caosdb::acm::v1alpha1::AccessControlManagementService;
    #endif #endif
    ...@@ -106,17 +103,6 @@ public: ...@@ -106,17 +103,6 @@ public:
    [[nodiscard]] auto CreateTransaction() const -> std::unique_ptr<Transaction>; [[nodiscard]] auto CreateTransaction() const -> std::unique_ptr<Transaction>;
    #ifdef BUILD_ACM #ifdef BUILD_ACM
    /**
    * List all known roles.
    */
    [[nodiscard]] auto ListRoles() const -> std::vector<Role>;
    [[nodiscard]] auto RetrieveSingleRole(const std::string &name) const -> Role;
    auto CreateSingleRole(const Role &role) const -> void;
    auto DeleteSingleRole(const std::string &name) const -> void;
    /** /**
    * Retrieve a single user. * Retrieve a single user.
    */ */
    ......
    ...@@ -41,9 +41,6 @@ set(libcaosdb_SRC ...@@ -41,9 +41,6 @@ set(libcaosdb_SRC
    IF(BUILD_ACM) IF(BUILD_ACM)
    list(APPEND libcaosdb_SRC list(APPEND libcaosdb_SRC
    ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/acm/permission_rule.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/acm/role.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/acm/role_impl.h
    ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/acm/user.cpp ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/acm/user.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/acm/user_impl.h ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/acm/user_impl.h
    ) )
    ......
    /*
    * This file is a part of the CaosDB Project.
    *
    * Copyright (C) 2022 Timm Fitschen <t.fitschen@indiscale.com>
    * Copyright (C) 2022 IndiScale GmbH <info@indiscale.com>
    *
    * This program is free software: you can redistribute it and/or modify
    * it under the terms of the GNU Affero General Public License as
    * published by the Free Software Foundation, either version 3 of the
    * License, or (at your option) any later version.
    *
    * This program is distributed in the hope that it will be useful,
    * but WITHOUT ANY WARRANTY; without even the implied warranty of
    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    * GNU Affero General Public License for more details.
    *
    * You should have received a copy of the GNU Affero General Public License
    * along with this program. If not, see <https://www.gnu.org/licenses/>.
    *
    */
    #include "caosdb/acm/permission_rule.h"
    #include "caosdb/acm/permission_rule_impl.h" // for PermissionRuleImpl
    #include "caosdb/acm/v1alpha1/main.pb.h" // for ListPermissionRulesRequest
    #include "caosdb/protobuf_helper.h" // for ProtoMessageWrapper
    #include <type_traits> // for remove_reference<>::type
    #include <utility> // for move
    namespace caosdb::acm {
    using caosdb::utility::ScalarProtoMessageWrapper;
    using ProtoPermissionRule = caosdb::acm::v1alpha1::PermissionRule;
    PermissionRuleImpl::PermissionRuleImpl() = default;
    PermissionRuleImpl::PermissionRuleImpl(ProtoPermissionRule *rule)
    : ScalarProtoMessageWrapper<ProtoPermissionRule>(rule) {}
    PermissionRuleImpl::PermissionRuleImpl(std::string permission, bool isGrant, bool isPriority) {
    this->wrapped->set_permission(std::move(permission));
    this->wrapped->set_grant(isGrant);
    this->wrapped->set_priority(isPriority);
    }
    PermissionRule::PermissionRule(std::string permission, bool isGrant, bool isPriority)
    : impl(new PermissionRuleImpl(std::move(permission), isGrant, isPriority)) {}
    PermissionRule::PermissionRule(const PermissionRule &rule) : impl(new PermissionRuleImpl()) {
    this->impl->wrapped->CopyFrom(*(rule.impl->wrapped));
    }
    auto PermissionRule::operator=(const PermissionRule &rule) -> PermissionRule & {
    if (this == &rule) {
    return *this;
    }
    this->impl->wrapped->CopyFrom(*(rule.impl->wrapped));
    return *this;
    }
    PermissionRule::~PermissionRule() { delete this->impl; }
    auto PermissionRule::ToString() const -> std::string { return this->impl->ToString(); }
    [[nodiscard]] auto PermissionRule::IsGrant() const -> bool { return this->impl->wrapped->grant(); }
    auto PermissionRule::SetGrant(bool isGrant) -> void { this->impl->wrapped->set_grant(isGrant); }
    [[nodiscard]] auto PermissionRule::IsPriority() const -> bool {
    return this->impl->wrapped->priority();
    }
    auto PermissionRule::SetPriority(bool isPriority) -> void {
    this->impl->wrapped->set_priority(isPriority);
    }
    auto PermissionRule::operator==(const PermissionRule &other) const -> bool {
    if (this == &other) {
    return true;
    }
    return this->GetPermission() == other.GetPermission() &&
    this->IsPriority() == other.IsPriority() && this->IsGrant() == other.IsGrant();
    }
    auto HashPermissionRule::operator()(const PermissionRule &rule) const -> size_t {
    return std::hash<std::string>()(rule.GetPermission());
    }
    auto PermissionRule::GetPermission() const -> const std::string & {
    return this->impl->wrapped->permission();
    }
    } // namespace caosdb::acm
    /*
    * This file is a part of the CaosDB Project.
    *
    * Copyright (C) 2022 Timm Fitschen <t.fitschen@indiscale.com>
    * Copyright (C) 2022 IndiScale GmbH <info@indiscale.com>
    *
    * This program is free software: you can redistribute it and/or modify
    * it under the terms of the GNU Affero General Public License as
    * published by the Free Software Foundation, either version 3 of the
    * License, or (at your option) any later version.
    *
    * This program is distributed in the hope that it will be useful,
    * but WITHOUT ANY WARRANTY; without even the implied warranty of
    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    * GNU Affero General Public License for more details.
    *
    * You should have received a copy of the GNU Affero General Public License
    * along with this program. If not, see <https://www.gnu.org/licenses/>.
    *
    */
    #ifndef CAOSDB_ACM_PERMISSION_RULE_IMPL_H
    #define CAOSDB_ACM_PERMISSION_RULE_IMPL_H
    #include "caosdb/acm/role.h"
    #include "caosdb/acm/v1alpha1/main.pb.h" // for ListRolesRequest
    #include "caosdb/protobuf_helper.h" // for ProtoMessageWrapper
    #include <utility> // for move
    namespace caosdb::acm {
    using caosdb::utility::ScalarProtoMessageWrapper;
    using ProtoPermissionRule = caosdb::acm::v1alpha1::PermissionRule;
    /**
    * PermissionRuleImpl class is designed to hide the implementation which makes direct use
    * of the protobuf objects underneath from the clients of the caosdb library.
    */
    class PermissionRuleImpl : public ScalarProtoMessageWrapper<ProtoPermissionRule> {
    public:
    /**
    * Constructor. Instanciate a role from the server's responces.
    */
    PermissionRuleImpl(ProtoPermissionRule *rule);
    PermissionRuleImpl();
    PermissionRuleImpl(std::string permission, bool isGrant, bool isPriority);
    friend class PermissionRule;
    };
    } // namespace caosdb::acm
    #endif
    /*
    * This file is a part of the CaosDB Project.
    *
    * Copyright (C) 2022 Timm Fitschen <t.fitschen@indiscale.com>
    * Copyright (C) 2022 IndiScale GmbH <info@indiscale.com>
    *
    * This program is free software: you can redistribute it and/or modify
    * it under the terms of the GNU Affero General Public License as
    * published by the Free Software Foundation, either version 3 of the
    * License, or (at your option) any later version.
    *
    * This program is distributed in the hope that it will be useful,
    * but WITHOUT ANY WARRANTY; without even the implied warranty of
    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    * GNU Affero General Public License for more details.
    *
    * You should have received a copy of the GNU Affero General Public License
    * along with this program. If not, see <https://www.gnu.org/licenses/>.
    *
    */
    #include "caosdb/acm/role.h"
    #include "caosdb/acm/permission_rule.h" // for PermissionRule
    #include "caosdb/acm/role_impl.h" // for RoleImpl
    #include "caosdb/acm/v1alpha1/main.pb.h" // for ListRolesRequest
    #include "caosdb/protobuf_helper.h" // for ProtoMessageWrapper
    #include <unordered_set> // for _Node_iterator, operator!=
    #include <utility> // for move
    // IWYU pragma: no_include "net/proto2/public/repeated_field.h"
    namespace caosdb::acm {
    using caosdb::utility::ScalarProtoMessageWrapper;
    using ProtoRole = caosdb::acm::v1alpha1::Role;
    using ProtoRolePermissions = caosdb::acm::v1alpha1::RolePermissions;
    using ProtoRoleCapabilities = caosdb::acm::v1alpha1::RoleCapabilities;
    using ProtoListRoleItem = caosdb::acm::v1alpha1::ListRoleItem;
    RoleImpl::RoleImpl(const std::string &name, const std::string &description) {
    if (!name.empty()) {
    this->wrapped->set_name(name);
    }
    if (!description.empty()) {
    this->wrapped->set_description(description);
    }
    }
    RoleImpl::RoleImpl(ProtoRole *role) : ScalarProtoMessageWrapper<ProtoRole>(role) {}
    RoleImpl::RoleImpl(ProtoListRoleItem &role_item)
    : ScalarProtoMessageWrapper<ProtoRole>(role_item.release_role()) {}
    RoleImpl::RoleImpl(const std::string &name) : RoleImpl(name, "") {}
    [[nodiscard]] auto RoleImpl::GeneratePermissionRulesSet() -> std::unique_ptr<PermissionRules> {
    auto result = std::make_unique<PermissionRules>();
    for (auto const &rule : this->wrapped->permission_rules()) {
    result->emplace(rule.permission(), rule.grant(), rule.priority());
    }
    return result;
    }
    Role::Role(std::unique_ptr<RoleImpl> wrapped) : wrapped(std::move(wrapped)) {}
    Role::Role(const std::string &name, const std::string &description)
    : wrapped(std::make_unique<RoleImpl>(name, description)) {}
    Role::Role(const std::string &name) : Role(name, {""}) {}
    Role::Role(const Role &role) : Role(role.GetName(), role.GetDescription()) {}
    auto Role::operator=(const Role &role) -> Role & {
    if (this == &role) {
    return *this;
    }
    this->wrapped->wrapped->CopyFrom(*(role.wrapped->wrapped));
    return *this;
    }
    Role::~Role() = default;
    auto Role::GetName() const -> const std::string & { return this->wrapped->wrapped->name(); }
    auto Role::SetName(const std::string &name) -> void {
    if (!name.empty()) {
    this->wrapped->wrapped->set_name(name);
    } else {
    this->wrapped->wrapped->clear_name();
    }
    }
    auto Role::GetDescription() const -> const std::string & {
    return this->wrapped->wrapped->description();
    }
    auto Role::SetDescription(const std::string &description) -> void {
    if (!description.empty()) {
    this->wrapped->wrapped->set_description(description);
    } else {
    this->wrapped->wrapped->clear_description();
    }
    }
    auto Role::ToString() const -> std::string { return this->wrapped->ToString(); }
    [[nodiscard]] auto Role::GetPermissionRules() const -> const PermissionRules & {
    if (this->wrapped->rules == nullptr) {
    this->wrapped->rules = this->wrapped->GeneratePermissionRulesSet();
    }
    return *(this->wrapped->rules);
    }
    auto Role::Grant(const std::string &permission, bool isPriority) -> void {
    this->AddPermissionRule(permission, true, isPriority);
    }
    auto Role::RevokeGrant(const std::string &permission, bool isPriority) -> void {
    this->RemovePermissionRule(permission, true, isPriority);
    }
    auto Role::Deny(const std::string &permission, bool isPriority) -> void {
    this->AddPermissionRule(permission, false, isPriority);
    }
    auto Role::RevokeDenial(const std::string &permission, bool isPriority) -> void {
    this->RemovePermissionRule(permission, false, isPriority);
    }
    auto Role::AddPermissionRule(const std::string &permission, bool isGrant, bool isPriority) -> void {
    this->RemovePermissionRule(permission, isGrant, isPriority);
    auto *new_rule = this->wrapped->wrapped->add_permission_rules();
    new_rule->set_permission(permission);
    new_rule->set_priority(isPriority);
    new_rule->set_grant(isGrant);
    }
    auto Role::RemovePermissionRule(const std::string &permission, bool isGrant, bool isPriority)
    -> void {
    this->wrapped->rules.reset();
    auto rule = this->wrapped->wrapped->mutable_permission_rules()->begin();
    while (rule != this->wrapped->wrapped->mutable_permission_rules()->end()) {
    if (rule->grant() == isGrant && rule->priority() == isPriority &&
    rule->permission() == permission) {
    this->wrapped->wrapped->mutable_permission_rules()->erase(rule);
    rule = this->wrapped->wrapped->mutable_permission_rules()->begin();
    } else {
    rule++;
    }
    }
    }
    auto Role::ClearPermissionRules() -> void {
    this->wrapped->rules.reset();
    this->wrapped->wrapped->clear_permission_rules();
    }
    } // namespace caosdb::acm
    /*
    * This file is a part of the CaosDB Project.
    *
    * Copyright (C) 2022 Timm Fitschen <t.fitschen@indiscale.com>
    * Copyright (C) 2022 IndiScale GmbH <info@indiscale.com>
    *
    * This program is free software: you can redistribute it and/or modify
    * it under the terms of the GNU Affero General Public License as
    * published by the Free Software Foundation, either version 3 of the
    * License, or (at your option) any later version.
    *
    * This program is distributed in the hope that it will be useful,
    * but WITHOUT ANY WARRANTY; without even the implied warranty of
    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    * GNU Affero General Public License for more details.
    *
    * You should have received a copy of the GNU Affero General Public License
    * along with this program. If not, see <https://www.gnu.org/licenses/>.
    *
    */
    #include "caosdb/acm/role.h"
    #include "caosdb/acm/v1alpha1/main.pb.h" // for ListRolesRequest
    #include "caosdb/protobuf_helper.h" // for ProtoMessageWrapper
    #include <memory> // for unique_ptr
    #include <utility> // for move
    namespace caosdb::acm {
    using caosdb::utility::ScalarProtoMessageWrapper;
    using ProtoRole = caosdb::acm::v1alpha1::Role;
    using ProtoRolePermissions = caosdb::acm::v1alpha1::RolePermissions;
    using ProtoRoleCapabilities = caosdb::acm::v1alpha1::RoleCapabilities;
    using ProtoListRoleItem = caosdb::acm::v1alpha1::ListRoleItem;
    /**
    * RoleImpl class is designed to hide the implementation which makes direct use
    * of the protobuf objects underneath from the clients of the caosdb library.
    */
    class RoleImpl : public ScalarProtoMessageWrapper<ProtoRole> {
    public:
    /**
    * Constructor. Instanciate a role with the given name.
    */
    explicit RoleImpl(const std::string &name);
    /**
    * Constructor. Instanciate a role with the given name and description.
    */
    RoleImpl(const std::string &name, const std::string &description);
    /**
    * Constructor. Instanciate a role from the server's responces.
    */
    RoleImpl(ProtoRole *role);
    /**
    * Constructor. Instanciate a role from a listRoles RPC.
    */
    RoleImpl(ProtoListRoleItem &role);
    friend class Role;
    friend class caosdb::connection::Connection;
    private:
    std::unique_ptr<PermissionRules> rules;
    [[nodiscard]] auto GeneratePermissionRulesSet() -> std::unique_ptr<PermissionRules>;
    };
    } // namespace caosdb::acm
    ...@@ -21,10 +21,9 @@ ...@@ -21,10 +21,9 @@
    */ */
    #include "caosdb/connection.h" #include "caosdb/connection.h"
    #ifdef BUILD_ACM #ifdef BUILD_ACM
    #include "caosdb/acm/role_impl.h" // for RoleImpl
    #include "caosdb/acm/user_impl.h" // for UserImpl #include "caosdb/acm/user_impl.h" // for UserImpl
    #include "caosdb/acm/v1alpha1/main.grpc.pb.h" // for AccessControlMan... #include "caosdb/acm/v1alpha1/main.grpc.pb.h" // for AccessControlMan...
    #include "caosdb/acm/v1alpha1/main.pb.h" // for ListRolesRequest #include "caosdb/acm/v1alpha1/main.pb.h" // for CreateSingleUser...
    #endif #endif
    #include "caosdb/configuration.h" // for ConnectionConfigur... #include "caosdb/configuration.h" // for ConnectionConfigur...
    #include "caosdb/exceptions.h" // for ConfigurationError #include "caosdb/exceptions.h" // for ConfigurationError
    ...@@ -42,21 +41,12 @@ ...@@ -42,21 +41,12 @@
    namespace caosdb::connection { namespace caosdb::connection {
    #ifdef BUILD_ACM #ifdef BUILD_ACM
    using caosdb::acm::RoleImpl;
    using caosdb::acm::UserImpl; using caosdb::acm::UserImpl;
    using caosdb::acm::v1alpha1::AccessControlManagementService; using caosdb::acm::v1alpha1::AccessControlManagementService;
    using caosdb::acm::v1alpha1::CreateSingleRoleRequest;
    using caosdb::acm::v1alpha1::CreateSingleRoleResponse;
    using caosdb::acm::v1alpha1::CreateSingleUserRequest; using caosdb::acm::v1alpha1::CreateSingleUserRequest;
    using caosdb::acm::v1alpha1::CreateSingleUserResponse; using caosdb::acm::v1alpha1::CreateSingleUserResponse;
    using caosdb::acm::v1alpha1::DeleteSingleRoleRequest;
    using caosdb::acm::v1alpha1::DeleteSingleRoleResponse;
    using caosdb::acm::v1alpha1::DeleteSingleUserRequest; using caosdb::acm::v1alpha1::DeleteSingleUserRequest;
    using caosdb::acm::v1alpha1::DeleteSingleUserResponse; using caosdb::acm::v1alpha1::DeleteSingleUserResponse;
    using caosdb::acm::v1alpha1::ListRolesRequest;
    using caosdb::acm::v1alpha1::ListRolesResponse;
    using caosdb::acm::v1alpha1::RetrieveSingleRoleRequest;
    using caosdb::acm::v1alpha1::RetrieveSingleRoleResponse;
    using caosdb::acm::v1alpha1::RetrieveSingleUserRequest; using caosdb::acm::v1alpha1::RetrieveSingleUserRequest;
    using caosdb::acm::v1alpha1::RetrieveSingleUserResponse; using caosdb::acm::v1alpha1::RetrieveSingleUserResponse;
    #endif #endif
    ...@@ -128,87 +118,6 @@ auto Connection::RetrieveVersionInfo() const -> const VersionInfo & { ...@@ -128,87 +118,6 @@ auto Connection::RetrieveVersionInfo() const -> const VersionInfo & {
    } }
    #ifdef BUILD_ACM #ifdef BUILD_ACM
    [[nodiscard]] auto Connection::RetrieveSingleRole(const std::string &name) const -> Role {
    RetrieveSingleRoleRequest request;
    request.set_name(name);
    RetrieveSingleRoleResponse response;
    grpc::ClientContext context;
    const grpc::Status grpc_status =
    this->access_controll_management_service->RetrieveSingleRole(&context, request, &response);
    auto status = TransactionStatus::SUCCESS();
    if (!grpc_status.ok()) {
    switch (grpc_status.error_code()) {
    case grpc::StatusCode::UNAUTHENTICATED:
    status = TransactionStatus::AUTHENTICATION_ERROR(grpc_status.error_message());
    break;
    case grpc::StatusCode::UNAVAILABLE:
    status = TransactionStatus::CONNECTION_ERROR();
    break;
    default:
    auto error_message = grpc_status.error_message();
    status = TransactionStatus::RPC_ERROR(std::to_string(grpc_status.error_code()) + " - " +
    error_message);
    }
    }
    status.ThrowExceptionIfError();
    auto *role = response.release_role();
    return Role(std::make_unique<RoleImpl>(role));
    }
    auto Connection::DeleteSingleRole(const std::string &name) const -> void {
    DeleteSingleRoleRequest request;
    request.set_name(name);
    DeleteSingleRoleResponse response;
    grpc::ClientContext context;
    const grpc::Status grpc_status =
    this->access_controll_management_service->DeleteSingleRole(&context, request, &response);
    auto status = TransactionStatus::SUCCESS();
    std::vector<Role> result;
    if (!grpc_status.ok()) {
    switch (grpc_status.error_code()) {
    case grpc::StatusCode::UNAUTHENTICATED:
    status = TransactionStatus::AUTHENTICATION_ERROR(grpc_status.error_message());
    break;
    case grpc::StatusCode::UNAVAILABLE:
    status = TransactionStatus::CONNECTION_ERROR();
    break;
    default:
    auto error_message = grpc_status.error_message();
    status = TransactionStatus::RPC_ERROR(std::to_string(grpc_status.error_code()) + " - " +
    error_message);
    }
    }
    status.ThrowExceptionIfError();
    }
    auto Connection::CreateSingleRole(const Role &role) const -> void {
    CreateSingleRoleRequest request;
    request.set_allocated_role(role.wrapped->wrapped);
    CreateSingleRoleResponse response;
    grpc::ClientContext context;
    const grpc::Status grpc_status =
    this->access_controll_management_service->CreateSingleRole(&context, request, &response);
    auto status = TransactionStatus::SUCCESS();
    if (!grpc_status.ok()) {
    switch (grpc_status.error_code()) {
    case grpc::StatusCode::UNAUTHENTICATED:
    status = TransactionStatus::AUTHENTICATION_ERROR(grpc_status.error_message());
    break;
    case grpc::StatusCode::UNAVAILABLE:
    status = TransactionStatus::CONNECTION_ERROR();
    break;
    default:
    auto error_message = grpc_status.error_message();
    status = TransactionStatus::RPC_ERROR(std::to_string(grpc_status.error_code()) + " - " +
    error_message);
    }
    }
    status.ThrowExceptionIfError();
    }
    // TODO(tf) find a way to deal with this: // TODO(tf) find a way to deal with this:
    // NOLINTNEXTLINE(bugprone-easily-swappable-parameters) // NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
    [[nodiscard]] auto Connection::RetrieveSingleUser(const std::string &realm, [[nodiscard]] auto Connection::RetrieveSingleUser(const std::string &realm,
    ...@@ -280,33 +189,6 @@ auto Connection::CreateSingleUser(const User &user) const -> void { ...@@ -280,33 +189,6 @@ auto Connection::CreateSingleUser(const User &user) const -> void {
    this->access_controll_management_service->CreateSingleUser(&context, request, &response); this->access_controll_management_service->CreateSingleUser(&context, request, &response);
    auto status = TransactionStatus::SUCCESS(); auto status = TransactionStatus::SUCCESS();
    std::vector<Role> result;
    if (!grpc_status.ok()) {
    switch (grpc_status.error_code()) {
    case grpc::StatusCode::UNAUTHENTICATED:
    status = TransactionStatus::AUTHENTICATION_ERROR(grpc_status.error_message());
    break;
    case grpc::StatusCode::UNAVAILABLE:
    status = TransactionStatus::CONNECTION_ERROR();
    break;
    default:
    auto error_message = grpc_status.error_message();
    status = TransactionStatus::RPC_ERROR(std::to_string(grpc_status.error_code()) + " - " +
    error_message);
    }
    }
    status.ThrowExceptionIfError();
    }
    [[nodiscard]] auto Connection::ListRoles() const -> std::vector<Role> {
    const ListRolesRequest request;
    ListRolesResponse response;
    grpc::ClientContext context;
    const grpc::Status grpc_status =
    this->access_controll_management_service->ListRoles(&context, request, &response);
    auto status = TransactionStatus::SUCCESS();
    std::vector<Role> result;
    if (!grpc_status.ok()) { if (!grpc_status.ok()) {
    switch (grpc_status.error_code()) { switch (grpc_status.error_code()) {
    case grpc::StatusCode::UNAUTHENTICATED: case grpc::StatusCode::UNAUTHENTICATED:
    ...@@ -322,14 +204,7 @@ auto Connection::CreateSingleUser(const User &user) const -> void { ...@@ -322,14 +204,7 @@ auto Connection::CreateSingleUser(const User &user) const -> void {
    } }
    } }
    status.ThrowExceptionIfError(); status.ThrowExceptionIfError();
    auto *roles = response.mutable_roles();
    for (auto &role_item : *roles) {
    result.emplace_back(std::make_unique<RoleImpl>(role_item));
    }
    return result;
    } }
    #endif #endif
    auto ConnectionManager::mHasConnection(const std::string &name) const -> bool { auto ConnectionManager::mHasConnection(const std::string &name) const -> bool {
    ......
    ...@@ -21,9 +21,6 @@ ...@@ -21,9 +21,6 @@
    */ */
    // A simple caosdb client // A simple caosdb client
    #ifdef BUILD_ACM
    #include "caosdb/acm/role.h" // for Role
    #endif
    #include "caosdb/connection.h" // for Connection, ConnectionManager #include "caosdb/connection.h" // for Connection, ConnectionManager
    #include "caosdb/constants.h" // for LIBCAOSDB_VERSION_MINOR, LIBCAOSDB_V... #include "caosdb/constants.h" // for LIBCAOSDB_VERSION_MINOR, LIBCAOSDB_V...
    #include "caosdb/entity.h" // for Entity #include "caosdb/entity.h" // for Entity
    ...@@ -32,153 +29,59 @@ ...@@ -32,153 +29,59 @@
    #include "caosdb/logging.h" // for CAOSDB_LOG_TRACE #include "caosdb/logging.h" // for CAOSDB_LOG_TRACE
    #include "caosdb/transaction.h" // for Transaction, ResultSet #include "caosdb/transaction.h" // for Transaction, ResultSet
    #include "caosdb/transaction_status.h" // for TransactionSt... #include "caosdb/transaction_status.h" // for TransactionSt...
    #include <boost/program_options.hpp> // for options_description #include <boost/log/core/record.hpp> // for record
    #include <exception> // for exception #include <boost/log/detail/attachable_sstream_buf.hpp> // for basic_ostring...
    #include <iostream> // for operator<<, basic_ostream, basic_ost... #include <boost/log/sources/record_ostream.hpp> // for operator<<
    #include <memory> // for unique_ptr, allocator, __shared_ptr_... #include <boost/preprocessor/seq/limits/enum_256.hpp> // for BOOST_PP_SEQ_...
    #include <string> // for operator<<, char_traits #include <boost/preprocessor/seq/limits/size_256.hpp> // for BOOST_PP_SEQ_...
    #include <vector> // for vector #include <exception> // for exception
    const auto logger_name = "libcaosdb"; #include <iostream> // for operator<<, basic_ostream, basic_ost...
    #include <memory> // for unique_ptr, allocator, __shared_ptr_...
    using boost::program_options::bool_switch; #include <string> // for operator<<, char_traits
    using boost::program_options::command_line_parser;
    using boost::program_options::notify;
    using boost::program_options::options_description;
    using boost::program_options::store;
    using boost::program_options::value;
    using boost::program_options::variables_map;
    #ifdef BUILD_ACM
    using caosdb::acm::Role;
    #endif
    auto print_version(bool print) -> void { const auto logger_name = "libcaosdb";
    if (print) {
    std::cout << "CaosDB C++ client (libcaosdb " << caosdb::LIBCAOSDB_VERSION_MAJOR << "."
    << caosdb::LIBCAOSDB_VERSION_MINOR << "." << caosdb::LIBCAOSDB_VERSION_PATCH << ")\n"
    << "We don't miss the H of caos.\n"
    << std::endl;
    }
    }
    auto test_connection() -> void { auto main() -> int {
    const auto &connection = caosdb::connection::ConnectionManager::GetDefaultConnection();
    connection->RetrieveVersionInfoNoExceptions(); std::cout << "CaosDB C++ client (libcaosdb " << caosdb::LIBCAOSDB_VERSION_MINOR << "."
    // get version info of the server << caosdb::LIBCAOSDB_VERSION_MINOR << "." << caosdb::LIBCAOSDB_VERSION_PATCH << ")\n"
    const auto &v_info = connection->GetVersionInfo(); << "We don't miss the H of caos.\n"
    std::cout << "Server Version: " << v_info->GetMajor() << "." << v_info->GetMinor() << "."
    << v_info->GetPatch() << "-" << v_info->GetPreRelease() << "-" << v_info->GetBuild()
    << std::endl; << std::endl;
    }
    auto retrieve_entity_by_id(const std::string &id) -> void {
    std::cout << "Retrieve entity " << id << std::endl;
    const auto &connection = caosdb::connection::ConnectionManager::GetDefaultConnection();
    auto transaction(connection->CreateTransaction());
    transaction->RetrieveById(id);
    transaction->ExecuteAsynchronously();
    auto t_stat = transaction->WaitForIt();
    CAOSDB_LOG_INFO(logger_name) << "status: " << t_stat.GetCode() << " // "
    << t_stat.GetDescription();
    const auto &result_set = transaction->GetResultSet();
    for (const auto &entity : result_set) {
    std::cout << entity.ToString() << std::endl;
    }
    }
    auto execute_query(const std::string &query) -> void {
    std::cout << "Execute query:\n" << query << std::endl;
    const auto &connection = caosdb::connection::ConnectionManager::GetDefaultConnection();
    auto q_transaction(connection->CreateTransaction());
    q_transaction->Query(query);
    q_transaction->ExecuteAsynchronously();
    auto t_stat = q_transaction->WaitForIt();
    CAOSDB_LOG_INFO(logger_name) << "status: " << t_stat.GetCode() << " // "
    << t_stat.GetDescription();
    const auto &result_set_2 = q_transaction->GetResultSet();
    for (const auto &entity : result_set_2) {
    std::cout << entity.ToString() << std::endl;
    }
    }
    #ifdef BUILD_ACM
    auto list_roles() -> void {
    std::cout << "Known roles:" << std::endl;
    const auto &connection = caosdb::connection::ConnectionManager::GetDefaultConnection();
    auto roles = connection->ListRoles();
    for (const auto &role : roles) {
    std::cout << role.ToString() << std::endl;
    }
    }
    auto retrieve_role(const std::string &name) {
    const auto &connection = caosdb::connection::ConnectionManager::GetDefaultConnection();
    auto role = connection->RetrieveSingleRole(name);
    std::cout << role.ToString() << std::endl;
    }
    auto create_role(const std::string &name) {
    const auto &connection = caosdb::connection::ConnectionManager::GetDefaultConnection();
    Role role(name);
    connection->CreateSingleRole(role);
    std::cout << "OK" << std::endl;
    }
    auto delete_role(const std::string &name) {
    const auto &connection = caosdb::connection::ConnectionManager::GetDefaultConnection();
    connection->DeleteSingleRole(name);
    std::cout << "OK" << std::endl;
    }
    #endif
    auto main(int argc, const char *argv[]) -> int {
    try { try {
    command_line_parser parser(argc, argv); const auto &connection = caosdb::connection::ConnectionManager::GetDefaultConnection();
    options_description desc{"Options"};
    desc.add_options()("help,h", "Show help.")("version,V", bool_switch()->notifier(print_version),
    "Print version of libcaosdb.")(
    "test-connection,t", "Test the connection to the caosdb server.")(
    "retrieve", value<std::string>()->notifier(retrieve_entity_by_id),
    "Retrieve an entity by id and print its JSON representation.")(
    "execute-query", value<std::string>()->notifier(execute_query),
    "Execute a query and print the results")
    #ifdef BUILD_ACM
    ("list-roles", "List all known roles")(
    "retrieve-role", value<std::string>()->notifier(retrieve_role), "Retrieve a role by name")(
    "create-role", value<std::string>()->notifier(create_role), "Create a new role")(
    "delete-role", value<std::string>()->notifier(delete_role), "Create a new role")
    #endif
    ;
    parser.options(desc); connection->RetrieveVersionInfoNoExceptions();
    // get version info of the server
    // TODO(tf) add positional parameters const auto &v_info = connection->GetVersionInfo();
    // positional_options_description pos_desc; std::cout << "Server Version: " << v_info->GetMajor() << "." << v_info->GetMinor() << "."
    // pos_desc.add("phone", 1); << v_info->GetPatch() << "-" << v_info->GetPreRelease() << "-" << v_info->GetBuild()
    // parser.positional(pos_desc); << std::endl;
    variables_map vm; // retrieve an entity
    store(parser.run(), vm); auto transaction(connection->CreateTransaction());
    notify(vm); transaction->RetrieveById("21");
    transaction->ExecuteAsynchronously();
    auto t_stat = transaction->WaitForIt();
    CAOSDB_LOG_INFO(logger_name) << "status: " << t_stat.GetCode() << " // "
    << t_stat.GetDescription();
    const auto &result_set = transaction->GetResultSet();
    for (const auto &entity : result_set) {
    std::cout << entity.ToString() << std::endl;
    }
    if (vm.count("help") != 0U) { // execute a query
    std::cout << desc << std::endl; std::string query("FIND Property \"Prop *\"");
    } else if (vm["version"].as<bool>()) { std::cout << "Trying to execute a query:\n" << query << std::endl;
    } else if (vm.count("test-connection") != 0U) { auto q_transaction(connection->CreateTransaction());
    test_connection(); q_transaction->Query(query);
    #ifdef BUILD_ACM q_transaction->ExecuteAsynchronously();
    } else if (vm.count("list-roles") != 0U) { t_stat = q_transaction->WaitForIt();
    list_roles(); CAOSDB_LOG_INFO(logger_name) << "status: " << t_stat.GetCode() << " // "
    } else if (vm.count("retrieve-role") != 0U) { << t_stat.GetDescription();
    } else if (vm.count("retrieve") != 0U) { const auto &result_set_2 = q_transaction->GetResultSet();
    } else if (vm.count("create-role") != 0U) { for (const auto &entity : result_set_2) {
    } else if (vm.count("delete-role") != 0U) { std::cout << entity.ToString() << std::endl;
    } else if (vm.count("execute-query") != 0U) {
    #endif
    } else {
    print_version(true);
    test_connection();
    } }
    return 0; return 0;
    ...@@ -189,7 +92,7 @@ auto main(int argc, const char *argv[]) -> int { ...@@ -189,7 +92,7 @@ auto main(int argc, const char *argv[]) -> int {
    std::cout << "Exception: " << exc.what() << std::endl; std::cout << "Exception: " << exc.what() << std::endl;
    return 1; return 1;
    } catch (...) { } catch (...) {
    std::cout << "Unknown error." << std::endl; std::cout << "Some other exception." << std::endl;
    return 2; return 2;
    } }
    } }
    ...@@ -37,7 +37,6 @@ set(test_cases ...@@ -37,7 +37,6 @@ set(test_cases
    IF(BUILD_ACM) IF(BUILD_ACM)
    list(APPEND test_cases list(APPEND test_cases
    test_role
    test_user test_user
    ) )
    ENDIF() ENDIF()
    ......
    ...@@ -117,13 +117,6 @@ TEST_F(test_connection, test_retrieve_single_user) { ...@@ -117,13 +117,6 @@ TEST_F(test_connection, test_retrieve_single_user) {
    "The attempt to execute this transaction was not successful because the " "The attempt to execute this transaction was not successful because the "
    "connection to the server could not be established."); "connection to the server could not be established.");
    } }
    TEST_F(test_connection, test_list_roles) {
    auto connection = ConnectionManager::GetDefaultConnection();
    EXPECT_THROW_MESSAGE(auto results = connection->ListRoles(), caosdb::exceptions::ConnectionError,
    "The attempt to execute this transaction was not successful because the "
    "connection to the server could not be established.");
    }
    #endif #endif
    } // namespace caosdb::connection } // namespace caosdb::connection
    /*
    *
    * This file is a part of the CaosDB Project.
    *
    * Copyright (C) 2022 Timm Fitschen <t.fitschen@indiscale.com>
    * Copyright (C) 2022 IndiScale GmbH <info@indiscale.com>
    *
    * This program is free software: you can redistribute it and/or modify
    * it under the terms of the GNU Affero General Public License as
    * published by the Free Software Foundation, either version 3 of the
    * License, or (at your option) any later version.
    *
    * This program is distributed in the hope that it will be useful,
    * but WITHOUT ANY WARRANTY; without even the implied warranty of
    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    * GNU Affero General Public License for more details.
    *
    * You should have received a copy of the GNU Affero General Public License
    * along with this program. If not, see <https://www.gnu.org/licenses/>.
    *
    */
    #include "caosdb/acm/role.h" // for Role
    #include "caosdb/acm/permission_rule.h" // for PermissionRules, PermissionRule
    #include <gtest/gtest-message.h> // for Message
    #include <gtest/gtest-test-part.h> // for TestPartResult, SuiteApiRe...
    #include <gtest/gtest_pred_impl.h> // for Test, EXPECT_EQ, TEST
    #include <string> // for allocator, string
    #include <unordered_set> // for _Node_const_iterator, operat...
    namespace caosdb::acm {
    TEST(test_role, role_name) {
    auto role = Role("role1");
    EXPECT_EQ(role.GetName(), "role1");
    role.SetName("role2");
    EXPECT_EQ(role.GetName(), "role2");
    }
    TEST(test_role, role_description) {
    auto role1 = Role("role1", "description1");
    EXPECT_EQ(role1.GetName(), "role1");
    EXPECT_EQ(role1.GetDescription(), "description1");
    role1.SetName("role3");
    EXPECT_EQ(role1.GetDescription(), "description1");
    auto role2 = Role("role2", "description1");
    EXPECT_EQ(role2.GetDescription(), "description1");
    EXPECT_EQ(role2.GetName(), "role2");
    role2.SetDescription("description2");
    EXPECT_EQ(role2.GetDescription(), "description2");
    EXPECT_EQ(role2.GetName(), "role2");
    }
    TEST(test_role, grant_revoke) {
    auto role1 = Role("role1", "description1");
    EXPECT_TRUE(role1.GetPermissionRules().empty());
    EXPECT_EQ(role1.ToString(), "{\n \"name\": \"role1\",\n \"description\": \"description1\"\n}\n");
    role1.Grant("SOME_PERMISSION", false);
    EXPECT_FALSE(role1.GetPermissionRules().empty());
    EXPECT_EQ(role1.ToString(),
    "{\n \"name\": \"role1\",\n \"description\": \"description1\",\n \"permissionRules\": "
    "[\n {\n \"permission\": \"SOME_PERMISSION\",\n \"grant\": true\n }\n ]\n}\n");
    role1.Grant("SOME_OTHER_PERMISSION", true);
    EXPECT_EQ(role1.GetPermissionRules().size(), 2);
    role1.Grant("SOME_OTHER_PERMISSION", true);
    // is a set
    EXPECT_EQ(role1.GetPermissionRules().size(), 2);
    role1.RevokeGrant("SOME_PERMISSION", false);
    EXPECT_FALSE(role1.GetPermissionRules().empty());
    role1.RevokeGrant("NOT_EXISTING_PERMISSION", true);
    // is immutable
    for (const auto &rule : role1.GetPermissionRules()) {
    EXPECT_EQ(rule.GetPermission(), "SOME_OTHER_PERMISSION");
    }
    role1.RevokeGrant("SOME_OTHER_PERMISSION", true);
    EXPECT_TRUE(role1.GetPermissionRules().empty());
    EXPECT_EQ(role1.ToString(), "{\n \"name\": \"role1\",\n \"description\": \"description1\"\n}\n");
    }
    TEST(test_role, deny_revoke) {
    auto role1 = Role("role1", "description1");
    EXPECT_TRUE(role1.GetPermissionRules().empty());
    EXPECT_EQ(role1.ToString(), "{\n \"name\": \"role1\",\n \"description\": \"description1\"\n}\n");
    role1.Deny("SOME_PERMISSION", false);
    EXPECT_FALSE(role1.GetPermissionRules().empty());
    EXPECT_EQ(role1.ToString(),
    "{\n \"name\": \"role1\",\n \"description\": \"description1\",\n \"permissionRules\": "
    "[\n {\n \"permission\": \"SOME_PERMISSION\"\n }\n ]\n}\n");
    role1.Deny("SOME_OTHER_PERMISSION", true);
    EXPECT_EQ(role1.GetPermissionRules().size(), 2);
    role1.Deny("SOME_OTHER_PERMISSION", true);
    // is a set
    EXPECT_EQ(role1.GetPermissionRules().size(), 2);
    role1.RevokeDenial("SOME_PERMISSION", false);
    EXPECT_FALSE(role1.GetPermissionRules().empty());
    role1.RevokeDenial("NOT_EXISTING_PERMISSION", true);
    // is immutable
    for (const auto &rule : role1.GetPermissionRules()) {
    EXPECT_EQ(rule.GetPermission(), "SOME_OTHER_PERMISSION");
    }
    role1.ClearPermissionRules();
    EXPECT_TRUE(role1.GetPermissionRules().empty());
    EXPECT_EQ(role1.ToString(), "{\n \"name\": \"role1\",\n \"description\": \"description1\"\n}\n");
    }
    TEST(test_role, permission_rule) {
    auto rule = PermissionRule("SOME_PERMISSION", true, false);
    EXPECT_EQ(rule.GetPermission(), "SOME_PERMISSION");
    EXPECT_TRUE(rule.IsGrant());
    EXPECT_FALSE(rule.IsPriority());
    EXPECT_EQ(rule.ToString(), "{\n \"permission\": \"SOME_PERMISSION\",\n \"grant\": true\n}\n");
    }
    } // namespace caosdb::acm
    0% Loading or .
    You are about to add 0 people to the discussion. Proceed with caution.
    Please register or to comment