Files
infix/package/sysrepo-cpp/0017-wrap-sr_nacm_check_operation.patch

134 lines
5.3 KiB
Diff

From ad070266f9347f159874e6b2fa57302385f354e3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Pecka?= <tomas.pecka@cesnet.cz>
Date: Wed, 9 Apr 2025 14:51:31 +0200
Subject: [PATCH 17/20] wrap sr_nacm_check_operation
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Organization: Wires
Sysrepo provides API function for explicit checking if an operation can
be authorized on a node. This might come handy if one decides to do NACM
authorization himself, like we will do in our RESTCONF server.
Change-Id: Ida41514a7f03ab120a331363b7f9ed8b69918d88
Signed-off-by: Mattias Walström <lazzer@gmail.com>
---
include/sysrepo-cpp/Session.hpp | 1 +
src/Session.cpp | 13 ++++++++++
tests/session.cpp | 46 +++++++++++++++++++++++++++++++++
tests/test_module.yang | 4 +++
4 files changed, 64 insertions(+)
diff --git a/include/sysrepo-cpp/Session.hpp b/include/sysrepo-cpp/Session.hpp
index 1c409b8..2112d21 100644
--- a/include/sysrepo-cpp/Session.hpp
+++ b/include/sysrepo-cpp/Session.hpp
@@ -101,6 +101,7 @@ public:
void setNacmUser(const std::string& user);
std::optional<std::string> getNacmUser() const;
+ bool checkNacmOperation(const libyang::DataNode& node) const;
[[nodiscard]] Subscription initNacm(
SubscribeOptions opts = SubscribeOptions::Default,
ExceptionHandler handler = nullptr,
diff --git a/src/Session.cpp b/src/Session.cpp
index 2273c79..ce0dec2 100644
--- a/src/Session.cpp
+++ b/src/Session.cpp
@@ -715,6 +715,19 @@ std::optional<std::string> Session::getNacmUser() const
return username ? std::make_optional<std::string>(username) : std::nullopt;
}
+/**
+ * @brief Checks if operation is allowed for current NACM user. Wraps `sr_nacm_check_operation`.
+ * @return true if the current user is authorized to perform operation on given @p node.
+ *
+ * Details on unsuccessfull authorizations can be retrieved via Session::getErrors.
+ * Note that if the NACM user is not set, `sr_nacm_check_operation` and this function both return true.
+ */
+bool Session::checkNacmOperation(const libyang::DataNode& node) const
+{
+ auto res = sr_nacm_check_operation(m_sess.get(), libyang::getRawNode(node));
+ return res == SR_ERR_OK;
+}
+
/**
* @brief Initializes NACM callbacks.
*
diff --git a/tests/session.cpp b/tests/session.cpp
index 548b371..35dd545 100644
--- a/tests/session.cpp
+++ b/tests/session.cpp
@@ -423,6 +423,52 @@ TEST_CASE("session")
sysrepo::ErrorWithCode);
}
+ DOCTEST_SUBCASE("Session::checkNacmOperation")
+ {
+ auto nacmSub = sess.initNacm();
+
+ // check NACM access for RPCs
+ auto shutdownRPC = sess.getContext().newPath("/test_module:shutdown", std::nullopt);
+ auto denyAllRPC = sess.getContext().newPath("/test_module:deny-all-rpc", std::nullopt);
+
+ // user not set, everything is permitted
+ REQUIRE(sess.checkNacmOperation(shutdownRPC) == true);
+ REQUIRE(sess.checkNacmOperation(denyAllRPC) == true);
+ REQUIRE(sess.getErrors().size() == 0);
+
+ sess.setNacmUser("root");
+ REQUIRE(sess.checkNacmOperation(shutdownRPC) == true);
+ REQUIRE(sess.checkNacmOperation(denyAllRPC) == true);
+ REQUIRE(sess.getErrors().size() == 0);
+
+ sess.setNacmUser("nobody");
+ REQUIRE(sess.checkNacmOperation(shutdownRPC) == true);
+ REQUIRE(sess.checkNacmOperation(denyAllRPC) == false);
+ REQUIRE(sess.getErrors().size() == 1);
+ REQUIRE(sess.getErrors().at(0) == sysrepo::ErrorInfo{
+ .code = sysrepo::ErrorCode::Unauthorized,
+ .errorMessage = "Executing the operation is denied because \"nobody\" NACM authorization failed.",
+ });
+
+ sess.setNacmUser("root"); // 'nobody' is not authorized to write into this subtree
+ sess.switchDatastore(sysrepo::Datastore::Running);
+ sess.setItem("/ietf-netconf-acm:nacm/enable-external-groups", "false");
+ sess.setItem("/ietf-netconf-acm:nacm/groups/group[name='grp']/user-name[.='nobody']", "");
+ sess.setItem("/ietf-netconf-acm:nacm/rule-list[name='rule']/group[.='grp']", "");
+ sess.setItem("/ietf-netconf-acm:nacm/rule-list[name='rule']/rule[name='1']/module-name", "test_module");
+ sess.setItem("/ietf-netconf-acm:nacm/rule-list[name='rule']/rule[name='1']/access-operations", "*");
+ sess.setItem("/ietf-netconf-acm:nacm/rule-list[name='rule']/rule[name='1']/action", "deny");
+ sess.applyChanges();
+
+ sess.setNacmUser("root");
+ REQUIRE(sess.checkNacmOperation(denyAllRPC) == true);
+ REQUIRE(sess.checkNacmOperation(shutdownRPC) == true);
+
+ sess.setNacmUser("nobody");
+ REQUIRE(sess.checkNacmOperation(denyAllRPC) == false);
+ REQUIRE(sess.checkNacmOperation(shutdownRPC) == false);
+ }
+
DOCTEST_SUBCASE("Session::getPendingChanges")
{
REQUIRE(sess.getPendingChanges() == std::nullopt);
diff --git a/tests/test_module.yang b/tests/test_module.yang
index 02c467b..3d6c26d 100644
--- a/tests/test_module.yang
+++ b/tests/test_module.yang
@@ -55,6 +55,10 @@ module test_module {
rpc noop {
}
+ rpc deny-all-rpc {
+ nacm:default-deny-all;
+ }
+
rpc shutdown {
output {
leaf success {
--
2.43.0