Merge "Refactor leafValueFromValue"
diff --git a/src/netconf-client.cpp b/src/netconf-client.cpp
index 73d8433..f1cdfe2 100644
--- a/src/netconf-client.cpp
+++ b/src/netconf-client.cpp
@@ -6,6 +6,7 @@
  *
 */
 
+#include <cstring>
 #include <libyang/Tree_Data.hpp>
 #include <mutex>
 extern "C" {
@@ -13,6 +14,7 @@
 }
 #include <sstream>
 #include "netconf-client.h"
+#include "UniqueResource.h"
 
 namespace libnetconf {
 
@@ -58,6 +60,13 @@
     nc_reply_free(reinterpret_cast<nc_reply*>(reply));
 }
 
+char *ssh_auth_interactive_cb(const char *auth_name, const char *instruction, const char *prompt, int echo, void *priv)
+{
+    const auto cb = static_cast<const client::KbdInteractiveCb*>(priv);
+    auto res = (*cb)(auth_name, instruction, prompt, echo);
+    return ::strdup(res.c_str());
+}
+
 template <auto fn>
 using deleter_from_fn = std::integral_constant<decltype(fn), fn>;
 
@@ -222,6 +231,27 @@
     return session;
 }
 
+std::unique_ptr<Session> Session::connectKbdInteractive(const std::string& host, const uint16_t port, const std::string& user, const KbdInteractiveCb& callback)
+{
+    impl::ClientInit::instance();
+
+    std::lock_guard lk(impl::clientOptions);
+    auto cb_guard = make_unique_resource([user, &callback]() {
+        nc_client_ssh_set_username(user.c_str());
+        nc_client_ssh_set_auth_pref(NC_SSH_AUTH_INTERACTIVE, 5);
+        nc_client_ssh_set_auth_interactive_clb(impl::ssh_auth_interactive_cb, static_cast<void *>(&const_cast<KbdInteractiveCb&>(callback)));
+    }, []() {
+        nc_client_ssh_set_auth_interactive_clb(nullptr, nullptr);
+        nc_client_ssh_set_username(nullptr);
+    });
+
+    auto session = std::make_unique<Session>(nc_connect_ssh(host.c_str(), port, nullptr));
+    if (!session->m_session) {
+        throw std::runtime_error{"nc_connect_ssh failed"};
+    }
+    return session;
+}
+
 std::unique_ptr<Session> Session::connectSocket(const std::string& path)
 {
     impl::ClientInit::instance();
diff --git a/src/netconf-client.h b/src/netconf-client.h
index bd89e6b..1923e71 100644
--- a/src/netconf-client.h
+++ b/src/netconf-client.h
@@ -1,5 +1,6 @@
 #pragma once
 
+#include <functional>
 #include <libnetconf2/messages_client.h>
 #include <memory>
 #include <optional>
@@ -22,11 +23,14 @@
     ~ReportedError() override;
 };
 
+using KbdInteractiveCb = std::function<std::string(const std::string&, const std::string&, const std::string&, bool)>;
+
 class Session {
 public:
     Session(struct nc_session* session);
     ~Session();
     static std::unique_ptr<Session> connectPubkey(const std::string& host, const uint16_t port, const std::string& user, const std::string& pubPath, const std::string& privPath);
+    static std::unique_ptr<Session> connectKbdInteractive(const std::string& host, const uint16_t port, const std::string& user, const KbdInteractiveCb& callback);
     static std::unique_ptr<Session> connectSocket(const std::string& path);
     std::vector<std::string_view> capabilities() const;
     std::shared_ptr<libyang::Data_Node> getConfig(const NC_DATASTORE datastore,
diff --git a/src/netconf_access.cpp b/src/netconf_access.cpp
index bf01ca0..5b1ad78 100644
--- a/src/netconf_access.cpp
+++ b/src/netconf_access.cpp
@@ -68,6 +68,13 @@
     datastoreInit();
 }
 
+NetconfAccess::NetconfAccess(std::unique_ptr<libnetconf::client::Session>&& session)
+    : m_session(std::move(session))
+    , m_schema(new YangSchema())
+{
+    datastoreInit();
+}
+
 NetconfAccess::NetconfAccess(const std::string& socketPath)
     : m_schema(new YangSchema())
 {
diff --git a/src/netconf_access.hpp b/src/netconf_access.hpp
index ccfffcc..29101ed 100644
--- a/src/netconf_access.hpp
+++ b/src/netconf_access.hpp
@@ -31,6 +31,7 @@
 public:
     NetconfAccess(const std::string& hostname, uint16_t port, const std::string& user, const std::string& pubKey, const std::string& privKey);
     NetconfAccess(const std::string& socketPath);
+    NetconfAccess(std::unique_ptr<libnetconf::client::Session>&& session);
     ~NetconfAccess() override;
     std::map<std::string, leaf_data_> getItems(const std::string& path) override;
     void setLeaf(const std::string& path, leaf_data_ value) override;
diff --git a/src/python_netconf.cpp b/src/python_netconf.cpp
index 83964d6..eaa4349 100644
--- a/src/python_netconf.cpp
+++ b/src/python_netconf.cpp
@@ -5,9 +5,11 @@
  *
 */
 
+#include <pybind11/functional.h>
 #include <pybind11/pybind11.h>
 #include <pybind11/stl.h>
 #include "netconf_access.hpp"
+#include "netconf-client.h"
 
 using namespace std::literals;
 using namespace pybind11::literals;
@@ -58,6 +60,12 @@
 
     pybind11::class_<NetconfAccess>(m, "NetconfAccess")
             .def(pybind11::init<const std::string&>(), "socketPath"_a)
+            .def(pybind11::init(
+                     [](const std::string& host, const uint16_t port, const std::string& user, const libnetconf::client::KbdInteractiveCb interactiveAuth) {
+                        auto session = libnetconf::client::Session::connectKbdInteractive(host, port, user, interactiveAuth);
+                        return std::make_unique<NetconfAccess>(std::move(session));
+                    }),
+                    "server"_a, "port"_a=830, "username"_a, "interactive_auth"_a)
             .def("getItems", &NetconfAccess::getItems, "xpath"_a)
             .def("setLeaf", &NetconfAccess::setLeaf, "xpath"_a, "value"_a)
             .def("commitChanges", &NetconfAccess::commitChanges)