Commit 5da53f35 authored by jan.koester's avatar jan.koester
Browse files

deb



Co-authored-by: default avatarCopilot <copilot@github.com>
parent f2c69b91
Loading
Loading
Loading
Loading
Loading
+111 −0
Original line number Diff line number Diff line
@@ -46,6 +46,39 @@
namespace authdb {
namespace client {

// Static GPO cache members
int Client::_GpoCacheTTL = Client::DEFAULT_GPO_CACHE_TTL;
std::mutex Client::_GpoCacheMutex;
std::unordered_map<std::string, std::chrono::steady_clock::time_point> Client::_GpoCache;

void Client::setGpoCacheTTL(int seconds) {
    _GpoCacheTTL = seconds;
}

bool Client::checkGpoCache(const std::string& key) {
    std::lock_guard<std::mutex> lk(_GpoCacheMutex);
    auto it = _GpoCache.find(key);
    if (it == _GpoCache.end()) return false;
    if (std::chrono::steady_clock::now() > it->second) {
        _GpoCache.erase(it);
        return false;
    }
    return true;
}

void Client::cacheGpoResult(const std::string& key) {
    std::lock_guard<std::mutex> lk(_GpoCacheMutex);
    _GpoCache[key] = std::chrono::steady_clock::now()
                    + std::chrono::seconds(_GpoCacheTTL);
    if (_GpoCache.size() > 2000) {
        auto now = std::chrono::steady_clock::now();
        for (auto ci = _GpoCache.begin(); ci != _GpoCache.end(); ) {
            if (now > ci->second) ci = _GpoCache.erase(ci);
            else ++ci;
        }
    }
}

namespace {
    std::vector<char> safe_post(libhttppp::HttpClient& client, libhttppp::HttpRequest& req,
                                const std::vector<char>& body, size_t maxTries) {
@@ -627,6 +660,10 @@ void Client::getAvatar(const ::uuid::uuid& did, const ::uuid::uuid& uid,
}

bool Client::GPOcheck(class ::uuid::uuid gpoid) {
    // Check cache first — avoid network round-trip
    std::string cache_key = std::string(_Con.getSessionID().c_str()) + ":" + gpoid.c_str();
    if (checkGpoCache(cache_key)) return true;

    ensure_http_client(_Con);

    json_object* jresponse = json_object_new_object();
@@ -692,6 +729,7 @@ bool Client::GPOcheck(class ::uuid::uuid gpoid) {
    }
    json_object_put(jrequest);
    json_tokener_free(jtok);
    if (ret) cacheGpoResult(cache_key);
    return ret;
}

@@ -1058,5 +1096,78 @@ std::vector<Client::GroupInfo> Client::listGroups() {
    return groups;
}

// -------- PooledConnection --------

PooledConnection::PooledConnection(ConnectionPool* pool,
                                   std::unique_ptr<ClientConnection> con,
                                   std::unique_ptr<Client> clt)
    : _Pool(pool), _Con(std::move(con)), _Clt(std::move(clt)) {}

PooledConnection::PooledConnection(PooledConnection&& other) noexcept
    : _Pool(other._Pool), _Con(std::move(other._Con)), _Clt(std::move(other._Clt)) {
    other._Pool = nullptr;
}

PooledConnection& PooledConnection::operator=(PooledConnection&& other) noexcept {
    if (this != &other) {
        if (_Pool && _Con && _Clt)
            _Pool->release(std::move(_Con), std::move(_Clt));
        _Pool = other._Pool;
        _Con = std::move(other._Con);
        _Clt = std::move(other._Clt);
        other._Pool = nullptr;
    }
    return *this;
}

PooledConnection::~PooledConnection() {
    if (_Pool && _Con && _Clt)
        _Pool->release(std::move(_Con), std::move(_Clt));
}

ClientConnection& PooledConnection::connection() { return *_Con; }
Client& PooledConnection::client() { return *_Clt; }

void PooledConnection::setSessionID(const uuid::uuid& sid) {
    _Con->setSessionID(sid);
}

// -------- ConnectionPool --------

ConnectionPool::ConnectionPool(const std::string& url,
                               const std::string& client_name,
                               const std::string& client_secret,
                               std::size_t max_idle)
    : _Url(url), _CltName(client_name), _CltSecret(client_secret), _MaxIdle(max_idle) {}

ConnectionPool::~ConnectionPool() = default;

PooledConnection ConnectionPool::acquire() {
    {
        std::lock_guard<std::mutex> lk(_Mutex);
        if (!_Idle.empty()) {
            auto entry = std::move(_Idle.front());
            _Idle.pop_front();
            return PooledConnection(this, std::move(entry.con), std::move(entry.clt));
        }
    }
    // Create new connection
    auto con = std::make_unique<ClientConnection>();
    con->setUrl(_Url);
    con->setClientName(_CltName);
    con->setClientSecret(_CltSecret);
    auto clt = std::make_unique<Client>(*con);
    return PooledConnection(this, std::move(con), std::move(clt));
}

void ConnectionPool::release(std::unique_ptr<ClientConnection> con,
                             std::unique_ptr<Client> clt) {
    std::lock_guard<std::mutex> lk(_Mutex);
    if (_Idle.size() < _MaxIdle) {
        _Idle.push_back({std::move(con), std::move(clt)});
    }
    // else: drop — exceeds max idle, connection closes
}

} // namespace client
} // namespace authdb
+80 −0
Original line number Diff line number Diff line
@@ -28,6 +28,10 @@
#include <string>
#include <memory>
#include <vector>
#include <mutex>
#include <deque>
#include <unordered_map>
#include <chrono>
#include <uuidp.h>

#ifndef Windows
@@ -129,9 +133,85 @@ namespace authdb {
            };

            std::vector<GroupInfo> listGroups();

            // GPO result cache TTL in seconds (default 300 = 5 min)
            static void setGpoCacheTTL(int seconds);
        private:
            ClientConnection &_Con;

            // Shared GPO result cache: "session_id:gpo_id" -> expiry
            static constexpr int DEFAULT_GPO_CACHE_TTL = 300;
            static int _GpoCacheTTL;
            static std::mutex _GpoCacheMutex;
            static std::unordered_map<std::string,
                std::chrono::steady_clock::time_point> _GpoCache;
            static bool checkGpoCache(const std::string& key);
            static void cacheGpoResult(const std::string& key);

            friend class Session;
        };

        // ---- ConnectionPool: reuses TCP connections to authdb ----

        class ConnectionPool;

        class VISIBILITY PooledConnection {
        public:
            PooledConnection(PooledConnection&& other) noexcept;
            PooledConnection& operator=(PooledConnection&& other) noexcept;
            ~PooledConnection();

            PooledConnection(const PooledConnection&) = delete;
            PooledConnection& operator=(const PooledConnection&) = delete;

            // Access the underlying connection and client
            ClientConnection& connection();
            Client& client();

            // Set per-request session ID
            void setSessionID(const uuid::uuid& sid);

        private:
            friend class ConnectionPool;
            PooledConnection(ConnectionPool* pool,
                             std::unique_ptr<ClientConnection> con,
                             std::unique_ptr<Client> clt);

            ConnectionPool* _Pool;
            std::unique_ptr<ClientConnection> _Con;
            std::unique_ptr<Client> _Clt;
        };

        class VISIBILITY ConnectionPool {
        public:
            ConnectionPool(const std::string& url,
                           const std::string& client_name,
                           const std::string& client_secret,
                           std::size_t max_idle = 4);
            ~ConnectionPool();

            ConnectionPool(const ConnectionPool&) = delete;
            ConnectionPool& operator=(const ConnectionPool&) = delete;

            // Acquire a ready-to-use connection (creates new if pool empty)
            PooledConnection acquire();

        private:
            friend class PooledConnection;
            void release(std::unique_ptr<ClientConnection> con,
                         std::unique_ptr<Client> clt);

            std::string _Url;
            std::string _CltName;
            std::string _CltSecret;
            std::size_t _MaxIdle;
            std::mutex  _Mutex;

            struct IdleEntry {
                std::unique_ptr<ClientConnection> con;
                std::unique_ptr<Client> clt;
            };
            std::deque<IdleEntry> _Idle;
        };
    };
};
+9 −0
Original line number Diff line number Diff line
authdb (20260424+7) unstable; urgency=medium

  * Client library: add ConnectionPool class for TCP connection reuse.
    PooledConnection RAII wrapper auto-returns connections to the pool.
  * Client library: add GPO result cache (5 min TTL) in GPOcheck() to
    avoid redundant network round-trips for repeated policy checks.

 -- Jan Koester <jan.koester@tuxist.de>  Thu, 24 Apr 2026 00:00:00 +0200

authdb (20260423+6) unstable; urgency=high

  * Cluster health monitor: replace sentinel store+remove with filesystem