From dc96556c846196b3152306bb6f47c94b10918071 Mon Sep 17 00:00:00 2001 From: joshusooyaa <106642518+joshusooyaa@users.noreply.github.com> Date: Wed, 30 Jul 2025 14:32:49 -0700 Subject: [PATCH 01/57] Begin auth-db module --- src/modules/auth/auth-db/AuthDBFwd.hpp | 15 +------ src/modules/auth/auth-db/AuthDBInstance.hpp | 43 ------------------ src/modules/auth/auth-db/AuthDBModule.cpp | 49 +++------------------ src/modules/auth/auth-db/AuthDBModule.hpp | 40 ++++------------- 4 files changed, 15 insertions(+), 132 deletions(-) diff --git a/src/modules/auth/auth-db/AuthDBFwd.hpp b/src/modules/auth/auth-db/AuthDBFwd.hpp index 0489bb32c..22273480a 100644 --- a/src/modules/auth/auth-db/AuthDBFwd.hpp +++ b/src/modules/auth/auth-db/AuthDBFwd.hpp @@ -15,17 +15,4 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . -*/ - -#pragma once - -namespace Leosac -{ -namespace Module -{ -namespace Auth -{ -using AuthDBInstanceId = unsigned long long; -} -} -} +*/ \ No newline at end of file diff --git a/src/modules/auth/auth-db/AuthDBInstance.hpp b/src/modules/auth/auth-db/AuthDBInstance.hpp index c646e63b9..c2c8b23e0 100644 --- a/src/modules/auth/auth-db/AuthDBInstance.hpp +++ b/src/modules/auth/auth-db/AuthDBInstance.hpp @@ -17,46 +17,3 @@ along with this program. If not, see . */ -#pragma once - -#include "core/auth/AuthFwd.hpp" -#include "modules/auth/auth-db/AuthDBFwd.hpp" - -namespace Leosac -{ -namespace Module -{ -namespace Auth -{ -/** - * An instance of authentication handler that use the database - * to verify credentials and permissions. - */ -#pragma db object optimistic -class AuthDBInstance -{ - private: -#pragma db id auto - AuthDBInstanceId id_; - - /** - * Target door. - */ - ::Leosac::Auth::DoorLPtr door_; - -#pragma db version - size_t odb_version_; - - - friend odb::access; - - // AUTH_SOURCE device. - // todo: Hardware service and related infrastructure improvements. -}; -} -} -} - -#ifdef ODB_COMPILER -#include "core/auth/Door.hpp" -#endif diff --git a/src/modules/auth/auth-db/AuthDBModule.cpp b/src/modules/auth/auth-db/AuthDBModule.cpp index 26377afce..a02d243bc 100644 --- a/src/modules/auth/auth-db/AuthDBModule.cpp +++ b/src/modules/auth/auth-db/AuthDBModule.cpp @@ -20,57 +20,18 @@ #include "modules/auth/auth-db/AuthDBModule.hpp" #include "core/CoreUtils.hpp" #include "core/kernel.hpp" -#include using namespace Leosac; using namespace Leosac::Module::Auth; -AuthDBModule::AuthDBModule(zmqpp::context &ctx, zmqpp::socket *pipe, - const boost::property_tree::ptree &cfg, - CoreUtilsPtr utils) +AuthDBModule::AuthDBModule(zmqpp::context& ctx, zmqpp::socket *pipe, + const boost::property_tree::ptree &cfg, CoreUtilsPtr utils) : AsioModule(ctx, pipe, cfg, utils) { - process_config(); - - /* for (auto authenticator : authenticators_) - { - reactor_.add(authenticator->bus_sub(), - std::bind(&AuthDBInstance::handle_bus_msg, authenticator)); - }*/ + // TODO: Implement } AuthDBModule::~AuthDBModule() { -} - -void AuthDBModule::process_config() -{ - setup_database(); -} - -void AuthDBModule::on_service_event(const service_event::Event &) -{ -} - -void AuthDBModule::setup_database() -{ - using namespace odb; - using namespace odb::core; - auto db = utils_->database(); - schema_version v = db->schema_version("module_auth-db"); - schema_version cv(schema_catalog::current_version(*db, "module_auth-db")); - if (v == 0) - { - transaction t(db->begin()); - schema_catalog::create_schema(*db, "module_auth-db"); - t.commit(); - } - else if (v < cv) - { - INFO("AuthDB Module performing database migration. Going from version " - << v << " to version " << cv); - transaction t(db->begin()); - schema_catalog::migrate(*db, cv, "module_auth-db"); - t.commit(); - } -} + // TODO: Implement +} \ No newline at end of file diff --git a/src/modules/auth/auth-db/AuthDBModule.hpp b/src/modules/auth/auth-db/AuthDBModule.hpp index 895907888..7f66ea28b 100644 --- a/src/modules/auth/auth-db/AuthDBModule.hpp +++ b/src/modules/auth/auth-db/AuthDBModule.hpp @@ -19,46 +19,24 @@ #pragma once -#include "modules/AsioModule.hpp" -#include -#include -#include +#include "AsioModule.hpp" +#include namespace Leosac { namespace Module { -namespace Auth +nameSpace Auth { -class AuthDBInstance; -/** -* This implements a authentication module that uses Leosac database -* to validate access. -*/ class AuthDBModule : public AsioModule { - public: - AuthDBModule(zmqpp::context &ctx, zmqpp::socket *pipe, - const boost::property_tree::ptree &cfg, CoreUtilsPtr utils); - - AuthDBModule(const AuthDBModule &) = delete; - - ~AuthDBModule(); - - protected: - void on_service_event(const service_event::Event &event) override; - - private: - void process_config(); - - void setup_database(); - - /** - * Authenticator instance. - */ - // std::vector authenticators_; -}; + public: + AuthDBModule(zmqpp::context& ctx, zmqpp::socket *pipe, + const boost::property_tree::ptree &cfg, CoreUtilsPtr utils); + + ~AuthDBModule(); } } } +} \ No newline at end of file From ea1cedd9d164b34144ad2a512912a20b8ea72847 Mon Sep 17 00:00:00 2001 From: joshusooyaa <106642518+joshusooyaa@users.noreply.github.com> Date: Wed, 30 Jul 2025 16:26:27 -0700 Subject: [PATCH 02/57] Set up constructor for AuthDBInstance --- src/modules/auth/auth-db/AuthDBFwd.hpp | 17 ++++- src/modules/auth/auth-db/AuthDBInstance.cpp | 33 ++++++++++ src/modules/auth/auth-db/AuthDBInstance.hpp | 69 +++++++++++++++++++++ 3 files changed, 118 insertions(+), 1 deletion(-) diff --git a/src/modules/auth/auth-db/AuthDBFwd.hpp b/src/modules/auth/auth-db/AuthDBFwd.hpp index 22273480a..12e6f0f88 100644 --- a/src/modules/auth/auth-db/AuthDBFwd.hpp +++ b/src/modules/auth/auth-db/AuthDBFwd.hpp @@ -15,4 +15,19 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . -*/ \ No newline at end of file +*/ + +#pragma once + +#include + +namespace Leosac +{ +namespace Module +{ +namespace Auth +{ + class AuthDBInstance; +} +} +} \ No newline at end of file diff --git a/src/modules/auth/auth-db/AuthDBInstance.cpp b/src/modules/auth/auth-db/AuthDBInstance.cpp index 40bb648d8..85b086cc1 100644 --- a/src/modules/auth/auth-db/AuthDBInstance.cpp +++ b/src/modules/auth/auth-db/AuthDBInstance.cpp @@ -18,3 +18,36 @@ */ #include "AuthDBInstance.hpp" +#include "core/CoreUtils.hpp" +#include "tools/db/DBService.hpp" +#include "tools/log.hpp" +#include +#include + +using namespace Leosac::Module::Auth; + +AuthDBInstance::AuthDBInstance(zmqpp::context &ctx, + const std::string &auth_ctx_name, + const std::list &auth_sources_names, + const std::string &auth_target_name, + CoreUtilsPtr core_utils) + : db_service_(std::make_shared(core_utils->database())) + , bus_push_(ctx, zmqpp::socket_type::push) + , bus_sub_(ctx, zmqpp::socket_type::sub) + , name_(auth_ctx_name) + , target_name_(auth_target_name) + , core_utils_(core_utils) +{ + bus_push_.connect("inproc://zmq-bus-pull"); + bus_sub_.connect("inproc://zmq-bus-pub"); + bus_sub_.subscribe("KERNEL"); + + for (const auto &auth_source : auth_sources_names) { + bus_sub_.subscribe("S_" + auth_source); + } + + INFO("AuthDB Instance (" << auth_ctx_name << ") and is subscribed to " + << boost::algorithm::join(auth_sources_names, ", ")); +} + +AuthDBInstance::~AuthDBInstance(){} \ No newline at end of file diff --git a/src/modules/auth/auth-db/AuthDBInstance.hpp b/src/modules/auth/auth-db/AuthDBInstance.hpp index c2c8b23e0..bfae7bd13 100644 --- a/src/modules/auth/auth-db/AuthDBInstance.hpp +++ b/src/modules/auth/auth-db/AuthDBInstance.hpp @@ -17,3 +17,72 @@ along with this program. If not, see . */ +#pragma once + +#include "LeosacFwd.hpp" +#include "tools/db/DBService.hpp" +#include +#include +#include +#include +#include + +namespace Leosac +{ +namespace Module +{ +namespace Auth +{ +class AuthDBInstance : public std::enable_shared_from_this +{ + public: + /** + * Create an Authenticator instance that watches readers and writes authentication messages to the bus + * @param ctx the ZeroMQ context + * @param auth_ctx_name name of the instance in the auth_db module config + * @param auth_source_names names of the authentication sources for the instance in the auth_db module config + * @param auth_target_name name of the target attached to the instance + * @param core_utils core utilities + */ + AuthDBInstance(zmqpp::context &ctx, const std::string &auth_ctx_name, + const std::list &auth_sources_names, + const std::string &auth_target_name, + CoreUtilsPtr core_utils); + + ~AuthDBInstance(); + + private: + /** + * Database service to query + */ + std::shared_ptr db_service_; + + /** + * Socket to write authentication messages to the bus + */ + zmqpp::socket bus_push_; + + /** + * Socket to read authentication messages from the bus + */ + zmqpp::socket bus_sub_; + + /** + * Name of the auth context instance + */ + std::string name_; + + /** + * Name of the target to authenticate against + */ + std::string target_name_; + + + CoreUtilsPtr core_utils_; + + std::mutex mutex_; +}; + +} +} +} \ No newline at end of file From 51d77c8321270e0a23f92fd3a27d717ddef98763 Mon Sep 17 00:00:00 2001 From: joshusooyaa <106642518+joshusooyaa@users.noreply.github.com> Date: Wed, 30 Jul 2025 16:27:18 -0700 Subject: [PATCH 03/57] Fix log message --- src/modules/auth/auth-db/AuthDBInstance.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/auth/auth-db/AuthDBInstance.cpp b/src/modules/auth/auth-db/AuthDBInstance.cpp index 85b086cc1..5d54a3934 100644 --- a/src/modules/auth/auth-db/AuthDBInstance.cpp +++ b/src/modules/auth/auth-db/AuthDBInstance.cpp @@ -46,7 +46,7 @@ AuthDBInstance::AuthDBInstance(zmqpp::context &ctx, bus_sub_.subscribe("S_" + auth_source); } - INFO("AuthDB Instance (" << auth_ctx_name << ") and is subscribed to " + INFO("AuthDB Instance (" << auth_ctx_name << ") created and is subscribed to " << boost::algorithm::join(auth_sources_names, ", ")); } From 5a249c65eefadeee5ae3ca23bbf013bc37d5ba66 Mon Sep 17 00:00:00 2001 From: joshusooyaa <106642518+joshusooyaa@users.noreply.github.com> Date: Wed, 30 Jul 2025 16:28:52 -0700 Subject: [PATCH 04/57] Update cmake list --- src/modules/auth/auth-db/CMakeLists.txt | 59 +++++++++---------------- 1 file changed, 22 insertions(+), 37 deletions(-) diff --git a/src/modules/auth/auth-db/CMakeLists.txt b/src/modules/auth/auth-db/CMakeLists.txt index 4a6b03e76..53b532d5d 100644 --- a/src/modules/auth/auth-db/CMakeLists.txt +++ b/src/modules/auth/auth-db/CMakeLists.txt @@ -3,55 +3,40 @@ set(AUTH-DB_BIN auth-db) set(AUTH-DB_SRCS init.cpp AuthDBModule.cpp - ) - -# Database support -set(OdbCMake_ODB_HEADERS - ${CMAKE_CURRENT_SOURCE_DIR}/AuthDBInstance.hpp - ) - -set(LEOSAC_ODB_INCLUDE_DIRS - ${CMAKE_SOURCE_DIR}/deps/zmqpp/src/ - ${CMAKE_SOURCE_DIR}/src - ${CMAKE_SOURCE_DIR}/deps/flagset - ${CMAKE_SOURCE_DIR}/deps/date/include - ${ZEROMQ_INCLUDE_DIR} - ${CMAKE_SOURCE_DIR}/src/db - ) - -set(OdbCMake_SOURCES "" AuthDBInstance.cpp AuthDBInstance.hpp) -odb_compile(OdbCMake_SOURCES - FILES ${OdbCMake_ODB_HEADERS} - MULTI_DATABASE dynamic - DB pgsql sqlite - STANDARD "c++14" - GENERATE_QUERY GENERATE_SESSION GENERATE_SCHEMA - SCHEMA_FORMAT embedded - SCHEMA_NAME module_auth-db - PROFILE "boost/date-time" "boost/optional" "leosac-profile" - INCLUDE_PREFIX modules/auth/auth-db - INCLUDE ${LEOSAC_ODB_INCLUDE_DIRS} - DEFAULT_POINTER "std::shared_ptr" - PGSQL_SERVER_VERSION "9.1" - ODB_EPILOGUE "#include \"core/audit/AuditEntry.hpp\"" - OUTPUT_DIR ${CMAKE_BINARY_DIR}/odb_gen/modules/auth/auth-db + AuthDBInstance.cpp ) add_library(${AUTH-DB_BIN} SHARED ${AUTH-DB_SRCS} - ${OdbCMake_SOURCES} ) set_target_properties(${AUTH-DB_BIN} PROPERTIES COMPILE_FLAGS "${MODULE_COMPILE_FLAGS}" ) -target_include_directories(${AUTH-DB_BIN} PUBLIC +find_library(LEOSAC_DB_LIB leosac_db PATHS /home/pi/leosac/build NO_DEFAULT_PATH) +find_library(LEOSAC_LIB_LIB leosac_lib PATHS /home/pi/leosac/build NO_DEFAULT_PATH) + +target_link_libraries(${AUTH-DB_BIN} + ${Boost_LIBRARIES} + ${LEOSAC_DB_LIB} + ${LEOSAC_LIB_LIB} + ) + +target_include_directories(${AUTH-DB_BIN} + PUBLIC ${CMAKE_SOURCE_DIR}/src + ${CMAKE_SOURCE_DIR}/src/modules + ${CMAKE_SOURCE_DIR}/deps/zmqpp/src + ${CMAKE_SOURCE_DIR}/deps/flagset + ${CMAKE_SOURCE_DIR}/deps/date/include + ${CMAKE_BINARY_DIR}/odb_gen + ${CMAKE_BINARY_DIR} + ${ZEROMQ_INCLUDE_DIR} ${ODB_INCLUDE_DIRS} ${ODB_COMPILE_OUTPUT_DIR} ) -target_link_libraries(${AUTH-DB_BIN} websock-api) - -install(TARGETS ${AUTH-DB_BIN} DESTINATION ${LEOSAC_MODULE_INSTALL_DIR}) +install(TARGETS ${AUTH-DB_BIN} + LIBRARY DESTINATION lib/leosac/modules + ) \ No newline at end of file From e9435292e38a6df6051066f5f030855477d8f948 Mon Sep 17 00:00:00 2001 From: joshusooyaa <106642518+joshusooyaa@users.noreply.github.com> Date: Wed, 30 Jul 2025 16:38:48 -0700 Subject: [PATCH 05/57] Fix namespace typo --- src/modules/auth/auth-db/AuthDBInstance.hpp | 1 - src/modules/auth/auth-db/AuthDBModule.hpp | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/modules/auth/auth-db/AuthDBInstance.hpp b/src/modules/auth/auth-db/AuthDBInstance.hpp index bfae7bd13..17785242f 100644 --- a/src/modules/auth/auth-db/AuthDBInstance.hpp +++ b/src/modules/auth/auth-db/AuthDBInstance.hpp @@ -82,7 +82,6 @@ class AuthDBInstance : public std::enable_shared_from_this std::mutex mutex_; }; - } } } \ No newline at end of file diff --git a/src/modules/auth/auth-db/AuthDBModule.hpp b/src/modules/auth/auth-db/AuthDBModule.hpp index 7f66ea28b..1c72e05e1 100644 --- a/src/modules/auth/auth-db/AuthDBModule.hpp +++ b/src/modules/auth/auth-db/AuthDBModule.hpp @@ -26,7 +26,7 @@ namespace Leosac { namespace Module { -nameSpace Auth +namespace Auth { class AuthDBModule : public AsioModule From 3df4cbc8082f71030478dfc606881e71bbdfe52a Mon Sep 17 00:00:00 2001 From: joshusooyaa <106642518+joshusooyaa@users.noreply.github.com> Date: Wed, 30 Jul 2025 16:42:04 -0700 Subject: [PATCH 06/57] Add missing virtual function --- src/modules/auth/auth-db/AuthDBModule.hpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/modules/auth/auth-db/AuthDBModule.hpp b/src/modules/auth/auth-db/AuthDBModule.hpp index 1c72e05e1..998996ffb 100644 --- a/src/modules/auth/auth-db/AuthDBModule.hpp +++ b/src/modules/auth/auth-db/AuthDBModule.hpp @@ -36,7 +36,10 @@ class AuthDBModule : public AsioModule const boost::property_tree::ptree &cfg, CoreUtilsPtr utils); ~AuthDBModule(); -} + + protected: + void on_service_event(const service_event::Event &event) override; +}; } } } \ No newline at end of file From c98c9e66c798b5cfce8abd0f264ee9955c3849ed Mon Sep 17 00:00:00 2001 From: joshusooyaa <106642518+joshusooyaa@users.noreply.github.com> Date: Wed, 30 Jul 2025 16:46:44 -0700 Subject: [PATCH 07/57] Implement on_service_event --- src/modules/auth/auth-db/AuthDBModule.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/modules/auth/auth-db/AuthDBModule.cpp b/src/modules/auth/auth-db/AuthDBModule.cpp index a02d243bc..b2eabb771 100644 --- a/src/modules/auth/auth-db/AuthDBModule.cpp +++ b/src/modules/auth/auth-db/AuthDBModule.cpp @@ -34,4 +34,6 @@ AuthDBModule::AuthDBModule(zmqpp::context& ctx, zmqpp::socket *pipe, AuthDBModule::~AuthDBModule() { // TODO: Implement -} \ No newline at end of file +} + +void AuthDBModule::on_service_event(const service_event::Event &event) {} \ No newline at end of file From 81d009f10fd07db2831f45fdbb27b9bfc8304e90 Mon Sep 17 00:00:00 2001 From: joshusooyaa <106642518+joshusooyaa@users.noreply.github.com> Date: Wed, 30 Jul 2025 17:02:51 -0700 Subject: [PATCH 08/57] Finish AuthDBModule declarations --- src/modules/auth/auth-db/AuthDBFwd.hpp | 4 +++- src/modules/auth/auth-db/AuthDBModule.hpp | 24 +++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/modules/auth/auth-db/AuthDBFwd.hpp b/src/modules/auth/auth-db/AuthDBFwd.hpp index 12e6f0f88..5330f7d0c 100644 --- a/src/modules/auth/auth-db/AuthDBFwd.hpp +++ b/src/modules/auth/auth-db/AuthDBFwd.hpp @@ -27,7 +27,9 @@ namespace Module { namespace Auth { - class AuthDBInstance; +class AuthDBInstance; +using AuthDBInstancePtr = std::shared_ptr; +using AuthDBInstanceId = unsigned long long; } } } \ No newline at end of file diff --git a/src/modules/auth/auth-db/AuthDBModule.hpp b/src/modules/auth/auth-db/AuthDBModule.hpp index 998996ffb..3d6deeba9 100644 --- a/src/modules/auth/auth-db/AuthDBModule.hpp +++ b/src/modules/auth/auth-db/AuthDBModule.hpp @@ -19,8 +19,11 @@ #pragma once +#include "AuthDBFwd.hpp" #include "AsioModule.hpp" +#include "core/CoreUtils.hpp" #include +#include namespace Leosac { @@ -39,6 +42,27 @@ class AuthDBModule : public AsioModule protected: void on_service_event(const service_event::Event &event) override; + + private: + /* + * Process the module configuration from the kernel + */ + void process_config(); + + /* + * Add tables to the database if they don't exist + */ + void setup_tables(); + + /* + * Setup authenticator instances + */ + void setup_authenticators(); + + /* + * List of the created authenticator instances + */ + std::list authenticators_; }; } } From ed068c4f82b09af273de5f4fe287a55de9302c11 Mon Sep 17 00:00:00 2001 From: joshusooyaa <106642518+joshusooyaa@users.noreply.github.com> Date: Wed, 30 Jul 2025 17:42:20 -0700 Subject: [PATCH 09/57] Add config setup for AuthDB --- src/modules/auth/auth-db/AuthDBModule.cpp | 42 ++++++++++++++++++++--- src/modules/auth/auth-db/AuthDBModule.hpp | 20 ++++++----- 2 files changed, 49 insertions(+), 13 deletions(-) diff --git a/src/modules/auth/auth-db/AuthDBModule.cpp b/src/modules/auth/auth-db/AuthDBModule.cpp index b2eabb771..dbf313e8e 100644 --- a/src/modules/auth/auth-db/AuthDBModule.cpp +++ b/src/modules/auth/auth-db/AuthDBModule.cpp @@ -20,6 +20,8 @@ #include "modules/auth/auth-db/AuthDBModule.hpp" #include "core/CoreUtils.hpp" #include "core/kernel.hpp" +#include "modules/auth/auth-db/AuthDBInstance.hpp" +#include using namespace Leosac; using namespace Leosac::Module::Auth; @@ -28,12 +30,42 @@ AuthDBModule::AuthDBModule(zmqpp::context& ctx, zmqpp::socket *pipe, const boost::property_tree::ptree &cfg, CoreUtilsPtr utils) : AsioModule(ctx, pipe, cfg, utils) { - // TODO: Implement + process_config(); } -AuthDBModule::~AuthDBModule() -{ - // TODO: Implement +AuthDBModule::~AuthDBModule() {} + +void AuthDBModule::on_service_event(const service_event::Event &event) {} + +void AuthDBModule::process_config() { + boost::property_tree::ptree auth_db_cfg = config_.get_child("module_config"); + + for (const auto &instance_node : auth_db_cfg.get_child("instances")) { + boost::property_tree::ptree auth_instance_cfg = instance_node.second; + std::string auth_ctx_name = auth_instance_cfg.get_child("name").data(); + std::string auth_target_name = auth_instance_cfg.get("target", ""); + std::list auth_sources_names; + + for (const auto &instance_subnode : auth_instance_cfg) { + if (instance_subnode.first == "auth_source") { + auth_sources_names.push_back(instance_subnode.second.data()); + } + } + + if (!auth_target_name.empty()) { + auth_target_name = utils_->kernel().config_manager().instance_name() + '.' + auth_target_name; + } + + INFO("Config processed for AuthDB instance: " << auth_ctx_name); + INFO(" - Target: " << auth_target_name); + INFO(" - Sources: " << boost::algorithm::join(auth_sources_names, ", ")); + } } -void AuthDBModule::on_service_event(const service_event::Event &event) {} \ No newline at end of file +void AuthDBModule::setup_tables() { + // TODO: Re-implement this back in the future +} + +void AuthDBModule::setup_authenticators() { + // TODO: Implement +} \ No newline at end of file diff --git a/src/modules/auth/auth-db/AuthDBModule.hpp b/src/modules/auth/auth-db/AuthDBModule.hpp index 3d6deeba9..d39bb1295 100644 --- a/src/modules/auth/auth-db/AuthDBModule.hpp +++ b/src/modules/auth/auth-db/AuthDBModule.hpp @@ -32,36 +32,40 @@ namespace Module namespace Auth { +/** + * Module that provides authentication using database-stored credentials + */ class AuthDBModule : public AsioModule { public: AuthDBModule(zmqpp::context& ctx, zmqpp::socket *pipe, const boost::property_tree::ptree &cfg, CoreUtilsPtr utils); + AuthDBModule(const AuthDBModule &) = delete; ~AuthDBModule(); protected: void on_service_event(const service_event::Event &event) override; private: - /* + /** * Process the module configuration from the kernel - */ + */ void process_config(); - /* + /** * Add tables to the database if they don't exist - */ + */ void setup_tables(); - /* + /** * Setup authenticator instances - */ + */ void setup_authenticators(); - /* + /** * List of the created authenticator instances - */ + */ std::list authenticators_; }; } From bc9977354b9e7abe9c63d5134409f608e8a5d7cd Mon Sep 17 00:00:00 2001 From: joshusooyaa <106642518+joshusooyaa@users.noreply.github.com> Date: Wed, 30 Jul 2025 17:46:57 -0700 Subject: [PATCH 10/57] Fix log for config setup --- src/modules/auth/auth-db/AuthDBModule.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/modules/auth/auth-db/AuthDBModule.cpp b/src/modules/auth/auth-db/AuthDBModule.cpp index dbf313e8e..6662e1a3d 100644 --- a/src/modules/auth/auth-db/AuthDBModule.cpp +++ b/src/modules/auth/auth-db/AuthDBModule.cpp @@ -58,7 +58,10 @@ void AuthDBModule::process_config() { INFO("Config processed for AuthDB instance: " << auth_ctx_name); INFO(" - Target: " << auth_target_name); - INFO(" - Sources: " << boost::algorithm::join(auth_sources_names, ", ")); + INFO(" - Sources (" << auth_sources_names.size() << "): "); + for (const auto& source : auth_sources_names) { + INFO(" * " << source); + } } } From 26fb7d90d22cee6325885ce4f18e4ee6f137d500 Mon Sep 17 00:00:00 2001 From: Josh Sawyer <106642518+joshusooyaa@users.noreply.github.com> Date: Fri, 1 Aug 2025 12:13:24 -0700 Subject: [PATCH 11/57] Refactor --- src/modules/auth/auth-db/AuthDBModule.cpp | 11 +++++++++-- src/modules/auth/auth-db/AuthDBModule.hpp | 4 +++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/modules/auth/auth-db/AuthDBModule.cpp b/src/modules/auth/auth-db/AuthDBModule.cpp index 6662e1a3d..863f2a3ea 100644 --- a/src/modules/auth/auth-db/AuthDBModule.cpp +++ b/src/modules/auth/auth-db/AuthDBModule.cpp @@ -62,6 +62,8 @@ void AuthDBModule::process_config() { for (const auto& source : auth_sources_names) { INFO(" * " << source); } + + setup_authenticators(auth_ctx_name, auth_sources_names, auth_target_name); } } @@ -69,6 +71,11 @@ void AuthDBModule::setup_tables() { // TODO: Re-implement this back in the future } -void AuthDBModule::setup_authenticators() { - // TODO: Implement +void AuthDBModule::setup_authenticators(const std::string &auth_ctx_name, + const std::list &auth_sources_names, + const std::string &auth_target_name) +{ + authenticators_.push_back(AuthDBInstancePtr( + new AuthDBInstance(ctx_, auth_ctx_name, auth_sources_names, + auth_target_name, utils_))); } \ No newline at end of file diff --git a/src/modules/auth/auth-db/AuthDBModule.hpp b/src/modules/auth/auth-db/AuthDBModule.hpp index d39bb1295..8e1ea556e 100644 --- a/src/modules/auth/auth-db/AuthDBModule.hpp +++ b/src/modules/auth/auth-db/AuthDBModule.hpp @@ -61,7 +61,9 @@ class AuthDBModule : public AsioModule /** * Setup authenticator instances */ - void setup_authenticators(); + void setup_authenticators(const std::string &auth_ctx_name, + const std::list &auth_sources_names, + const std::string &auth_target_name); /** * List of the created authenticator instances From 020946f5f288188a589fdf8b3a9a1015adce364f Mon Sep 17 00:00:00 2001 From: Josh Sawyer <106642518+joshusooyaa@users.noreply.github.com> Date: Fri, 1 Aug 2025 14:52:20 -0700 Subject: [PATCH 12/57] Add AuthResult struct --- src/modules/auth/auth-db/AuthDBInstance.hpp | 28 +++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/modules/auth/auth-db/AuthDBInstance.hpp b/src/modules/auth/auth-db/AuthDBInstance.hpp index 17785242f..98dc564dc 100644 --- a/src/modules/auth/auth-db/AuthDBInstance.hpp +++ b/src/modules/auth/auth-db/AuthDBInstance.hpp @@ -33,6 +33,34 @@ namespace Module { namespace Auth { + +struct AuthResult +{ + AuthResult(bool s, ::Leosac::Auth::IAccessProfilePtr p, + ::Leosac::Auth::UserPtr u) + : success(s) + , profile(p) + , user(u) + {} + + /** + * Access granted or denied + */ + bool success; + + /** + * Profile used to grant or deny access. May be null if no profiles + * corresponding to the auth source were found. + */ + ::Leosac::Auth::IAccessProfilePtr profile; + + /** + * A user object representing the user who made the authentication + * attempt. + */ + ::Leosac::Auth::UserPtr user; +}; + class AuthDBInstance : public std::enable_shared_from_this { public: From e26839feb3792c3249ae1a0150f2daec64141445 Mon Sep 17 00:00:00 2001 From: Josh Sawyer <106642518+joshusooyaa@users.noreply.github.com> Date: Mon, 4 Aug 2025 02:35:46 -0700 Subject: [PATCH 13/57] Start handle_bus_msg implementation --- src/modules/auth/auth-db/AuthDBInstance.cpp | 49 ++++++++++++++++++++- src/modules/auth/auth-db/AuthDBInstance.hpp | 35 ++++++++++++++- 2 files changed, 82 insertions(+), 2 deletions(-) diff --git a/src/modules/auth/auth-db/AuthDBInstance.cpp b/src/modules/auth/auth-db/AuthDBInstance.cpp index 5d54a3934..1e2aeb1ee 100644 --- a/src/modules/auth/auth-db/AuthDBInstance.cpp +++ b/src/modules/auth/auth-db/AuthDBInstance.cpp @@ -21,6 +21,7 @@ #include "core/CoreUtils.hpp" #include "tools/db/DBService.hpp" #include "tools/log.hpp" +#include "tools/Colorize.hpp" #include #include @@ -50,4 +51,50 @@ AuthDBInstance::AuthDBInstance(zmqpp::context &ctx, << boost::algorithm::join(auth_sources_names, ", ")); } -AuthDBInstance::~AuthDBInstance(){} \ No newline at end of file +AuthDBInstance::~AuthDBInstance() { + INFO("AuthDBInstance (" << name_ << ") destroyed"); +} + +void AuthDBInstance::handle_bus_msg() { + zmqpp::message msg; + zmqpp::message auth_result_msg; + + bus_sub_.receive(msg); + if(handle_kernel_msg(msg)) { + return; + } + + format_auth_result_msg(auth_result_msg); + auto auth_result = handle_auth_msg(msg); + + update_auth_result_msg(auth_result, auth_result_msg); + bus_push_.send(auth_result_msg); +} + +bool AuthDBInstance::handle_kernel_msg(zmqpp::message &msg) { + // TODO: Implement this + return false; +} + +AuthResult AuthDBInstance::handle_auth_msg(zmqpp::message &msg) { + // TODO: Implement this + return AuthResult(false, nullptr, nullptr); +} + +void AuthDBInstance::format_auth_result_msg(zmqpp::message &msg) { + msg << ("S_" + name_); +} + +std::string AuthDBInstance::format_user_name(const AuthResult &auth_result) { + using namespace Colorize; + if (auth_result.user) { + return Colorize::green(auth_result.user->username()); + } else { + return Colorize::red("UNKNOWN_USER"); + } +} + +void AuthDBInstance::update_auth_result_msg(const AuthResult &auth_result, zmqpp::message &msg) { + // TODO: Implement this + INFO("TODO"); +} \ No newline at end of file diff --git a/src/modules/auth/auth-db/AuthDBInstance.hpp b/src/modules/auth/auth-db/AuthDBInstance.hpp index 98dc564dc..e44d84a41 100644 --- a/src/modules/auth/auth-db/AuthDBInstance.hpp +++ b/src/modules/auth/auth-db/AuthDBInstance.hpp @@ -78,8 +78,41 @@ class AuthDBInstance : public std::enable_shared_from_this CoreUtilsPtr core_utils); ~AuthDBInstance(); - + + AuthDBInstance(const AuthDBInstance &) = delete; + AuthDBInstance &operator=(const AuthDBInstance &) = delete; + + /** + * Handle a message from the bus + */ + void handle_bus_msg(); + private: + /** + * Handle a message from the kernel + */ + bool handle_kernel_msg(zmqpp::message &msg); + + /** + * Handle a message from an auth source + */ + AuthResult handle_auth_msg(zmqpp::message &msg); + + /** + * Format auth result message + */ + void format_auth_result_msg(zmqpp::message &msg); + + /** + * Format user name for logging + */ + std::string format_user_name(const AuthResult &auth_result); + + /** + * Append full auth result info to auth result message + */ + void update_auth_result_msg(const AuthResult &auth_result, zmqpp::message &msg); + /** * Database service to query */ From 3c3ad1e0e1893cc120298cf47615dfeb892f96fa Mon Sep 17 00:00:00 2001 From: Josh Sawyer <106642518+joshusooyaa@users.noreply.github.com> Date: Mon, 4 Aug 2025 02:46:14 -0700 Subject: [PATCH 14/57] Implement update_and_log_auth_result_msg --- src/modules/auth/auth-db/AuthDBInstance.cpp | 22 +++++++++++++++++---- src/modules/auth/auth-db/AuthDBInstance.hpp | 4 ++-- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/modules/auth/auth-db/AuthDBInstance.cpp b/src/modules/auth/auth-db/AuthDBInstance.cpp index 1e2aeb1ee..9acb7ab55 100644 --- a/src/modules/auth/auth-db/AuthDBInstance.cpp +++ b/src/modules/auth/auth-db/AuthDBInstance.cpp @@ -22,10 +22,12 @@ #include "tools/db/DBService.hpp" #include "tools/log.hpp" #include "tools/Colorize.hpp" +#include "core/auth/Auth.hpp" #include #include using namespace Leosac::Module::Auth; +using namespace Leosac::Auth; AuthDBInstance::AuthDBInstance(zmqpp::context &ctx, const std::string &auth_ctx_name, @@ -67,7 +69,7 @@ void AuthDBInstance::handle_bus_msg() { format_auth_result_msg(auth_result_msg); auto auth_result = handle_auth_msg(msg); - update_auth_result_msg(auth_result, auth_result_msg); + update_and_log_auth_result_msg(auth_result, auth_result_msg); bus_push_.send(auth_result_msg); } @@ -94,7 +96,19 @@ std::string AuthDBInstance::format_user_name(const AuthResult &auth_result) { } } -void AuthDBInstance::update_auth_result_msg(const AuthResult &auth_result, zmqpp::message &msg) { - // TODO: Implement this - INFO("TODO"); +void AuthDBInstance::update_and_log_auth_result_msg(const AuthResult &auth_result, zmqpp::message &auth_result_msg) { + using namespace Colorize; + std::string user = format_user_name(auth_result); + + if (auth_result.success) { + auth_result_msg << Leosac::Auth::AccessStatus::GRANTED; + INFO(Colorize::bold(name_) + << " " << Colorize::green("GRANTED") << " access to target " + << Colorize::underline(target_name_) << " for user " << user); + } else { + auth_result_msg << Leosac::Auth::AccessStatus::DENIED; + INFO(Colorize::bold(name_) + << " " << Colorize::red("DENIED") << " access to target " + << Colorize::underline(target_name_) << " for user " << user); + } } \ No newline at end of file diff --git a/src/modules/auth/auth-db/AuthDBInstance.hpp b/src/modules/auth/auth-db/AuthDBInstance.hpp index e44d84a41..b12a7b6af 100644 --- a/src/modules/auth/auth-db/AuthDBInstance.hpp +++ b/src/modules/auth/auth-db/AuthDBInstance.hpp @@ -109,9 +109,9 @@ class AuthDBInstance : public std::enable_shared_from_this std::string format_user_name(const AuthResult &auth_result); /** - * Append full auth result info to auth result message + * Append access result to auth result and log event to console */ - void update_auth_result_msg(const AuthResult &auth_result, zmqpp::message &msg); + void update_and_log_auth_result_msg(const AuthResult &auth_result, zmqpp::message &auth_result_msg); /** * Database service to query From c84e2ffaa18a7ef95c5ac380bfb82bc537408bbf Mon Sep 17 00:00:00 2001 From: Josh Sawyer <106642518+joshusooyaa@users.noreply.github.com> Date: Mon, 4 Aug 2025 02:53:46 -0700 Subject: [PATCH 15/57] Implement handle_kernel_msg --- src/modules/auth/auth-db/AuthDBInstance.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/modules/auth/auth-db/AuthDBInstance.cpp b/src/modules/auth/auth-db/AuthDBInstance.cpp index 9acb7ab55..ef211dc58 100644 --- a/src/modules/auth/auth-db/AuthDBInstance.cpp +++ b/src/modules/auth/auth-db/AuthDBInstance.cpp @@ -74,7 +74,18 @@ void AuthDBInstance::handle_bus_msg() { } bool AuthDBInstance::handle_kernel_msg(zmqpp::message &msg) { - // TODO: Implement this + auto msg_copy = msg.copy(); + std::string tmp; + msg_copy >> tmp; + + if (tmp == "KERNEL") { + msg_copy >> tmp; + if (tmp == "SIGHUP") { + INFO("AuthDBInstance received SIGHUP"); + // TODO: Implement reload authdb config + } + return true; + } return false; } From 85164a88a50e39d1304ebec671bbb7f231759b46 Mon Sep 17 00:00:00 2001 From: Josh Sawyer <106642518+joshusooyaa@users.noreply.github.com> Date: Mon, 4 Aug 2025 03:06:31 -0700 Subject: [PATCH 16/57] Begin handle_auth implementation --- src/modules/auth/auth-db/AuthDBInstance.cpp | 16 +++++++++++++--- src/modules/auth/auth-db/AuthDBInstance.hpp | 3 ++- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/modules/auth/auth-db/AuthDBInstance.cpp b/src/modules/auth/auth-db/AuthDBInstance.cpp index ef211dc58..a754a8b75 100644 --- a/src/modules/auth/auth-db/AuthDBInstance.cpp +++ b/src/modules/auth/auth-db/AuthDBInstance.cpp @@ -23,6 +23,7 @@ #include "tools/log.hpp" #include "tools/Colorize.hpp" #include "core/auth/Auth.hpp" +#include "exception/ExceptionsTools.hpp" #include #include @@ -89,9 +90,18 @@ bool AuthDBInstance::handle_kernel_msg(zmqpp::message &msg) { return false; } -AuthResult AuthDBInstance::handle_auth_msg(zmqpp::message &msg) { - // TODO: Implement this - return AuthResult(false, nullptr, nullptr); +AuthResult AuthDBInstance::handle_auth(zmqpp::message &msg) { + AuthResult auth_result(false, nullptr, nullptr); + + try { + std::lock_guard guard(mutex_); + + } catch (std::exception &e) { + WARN("Error while handling auth request: " << e.what()); + log_exception(e); + } + + return auth_result; } void AuthDBInstance::format_auth_result_msg(zmqpp::message &msg) { diff --git a/src/modules/auth/auth-db/AuthDBInstance.hpp b/src/modules/auth/auth-db/AuthDBInstance.hpp index b12a7b6af..ced438f76 100644 --- a/src/modules/auth/auth-db/AuthDBInstance.hpp +++ b/src/modules/auth/auth-db/AuthDBInstance.hpp @@ -95,8 +95,9 @@ class AuthDBInstance : public std::enable_shared_from_this /** * Handle a message from an auth source + * Check if access is granted or denied by checking source against the database */ - AuthResult handle_auth_msg(zmqpp::message &msg); + AuthResult handle_auth(zmqpp::message &msg); /** * Format auth result message From 0d14b2e77845560776cdd4a470305b357342c269 Mon Sep 17 00:00:00 2001 From: Josh Sawyer <106642518+joshusooyaa@users.noreply.github.com> Date: Mon, 4 Aug 2025 03:12:59 -0700 Subject: [PATCH 17/57] Update handle_auth to pass by pointer --- src/modules/auth/auth-db/AuthDBInstance.cpp | 4 ++-- src/modules/auth/auth-db/AuthDBInstance.hpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/modules/auth/auth-db/AuthDBInstance.cpp b/src/modules/auth/auth-db/AuthDBInstance.cpp index a754a8b75..d638e4960 100644 --- a/src/modules/auth/auth-db/AuthDBInstance.cpp +++ b/src/modules/auth/auth-db/AuthDBInstance.cpp @@ -68,7 +68,7 @@ void AuthDBInstance::handle_bus_msg() { } format_auth_result_msg(auth_result_msg); - auto auth_result = handle_auth_msg(msg); + auto auth_result = handle_auth(&msg); update_and_log_auth_result_msg(auth_result, auth_result_msg); bus_push_.send(auth_result_msg); @@ -90,7 +90,7 @@ bool AuthDBInstance::handle_kernel_msg(zmqpp::message &msg) { return false; } -AuthResult AuthDBInstance::handle_auth(zmqpp::message &msg) { +AuthResult AuthDBInstance::handle_auth(zmqpp::message *msg) noexcept { AuthResult auth_result(false, nullptr, nullptr); try { diff --git a/src/modules/auth/auth-db/AuthDBInstance.hpp b/src/modules/auth/auth-db/AuthDBInstance.hpp index ced438f76..4b61579e1 100644 --- a/src/modules/auth/auth-db/AuthDBInstance.hpp +++ b/src/modules/auth/auth-db/AuthDBInstance.hpp @@ -97,7 +97,7 @@ class AuthDBInstance : public std::enable_shared_from_this * Handle a message from an auth source * Check if access is granted or denied by checking source against the database */ - AuthResult handle_auth(zmqpp::message &msg); + AuthResult handle_auth(zmqpp::message *msg) noexcept; /** * Format auth result message From 218b2e90797ea243a1e749966898c6f224ef0e54 Mon Sep 17 00:00:00 2001 From: Josh Sawyer <106642518+joshusooyaa@users.noreply.github.com> Date: Mon, 4 Aug 2025 03:52:27 -0700 Subject: [PATCH 18/57] Set up fetch credentials from database structure Checks credentials passed in message, and if they are RFIDCard credentials, check to see if they exist in the database. Implementation for fetching from the database TODO. --- src/modules/auth/auth-db/AuthDBInstance.cpp | 33 +++++++++++++++++++++ src/modules/auth/auth-db/AuthDBInstance.hpp | 10 +++++++ 2 files changed, 43 insertions(+) diff --git a/src/modules/auth/auth-db/AuthDBInstance.cpp b/src/modules/auth/auth-db/AuthDBInstance.cpp index d638e4960..ed54432d6 100644 --- a/src/modules/auth/auth-db/AuthDBInstance.cpp +++ b/src/modules/auth/auth-db/AuthDBInstance.cpp @@ -23,6 +23,7 @@ #include "tools/log.hpp" #include "tools/Colorize.hpp" #include "core/auth/Auth.hpp" +#include "core/auth/AuthSourceBuilder.hpp" #include "exception/ExceptionsTools.hpp" #include #include @@ -95,6 +96,15 @@ AuthResult AuthDBInstance::handle_auth(zmqpp::message *msg) noexcept { try { std::lock_guard guard(mutex_); + + Cred::ICredentialPtr db_credentials = find_db_credentials(msg); + if (!db_credentials) { + return auth_result; + } + + Cred::ICredentialPtr auth_source = db_credentials; + + //TODO: Finish this } catch (std::exception &e) { WARN("Error while handling auth request: " << e.what()); @@ -104,6 +114,29 @@ AuthResult AuthDBInstance::handle_auth(zmqpp::message *msg) noexcept { return auth_result; } +Cred::ICredentialPtr AuthDBInstance::find_db_credentials(zmqpp::message *msg) { + AuthSourceBuilder builder; + Cred::ICredentialPtr auth_source = builder.create(msg); + Cred::ICredentialPtr db_credentials = nullptr; + + if (auto rfid_card = std::dynamic_pointer_cast(auth_source)) { + db_credentials = find_credentials_by_card_id(rfid_card->card_id(), rfid_card->nb_bits()); + } else if (auto pin_code = std::dynamic_pointer_cast(auth_source)) { + INFO("Pin code auth source not supported yet"); + } else if (auto card_pin = std::dynamic_pointer_cast(auth_source)) { + INFO("Card pin auth source not supported yet"); + } else { + WARN("Unknown credential type"); + } + + return db_credentials; +} + +Cred::ICredentialPtr AuthDBInstance::find_credentials_by_card_id(const std::string &card_id, const int nb_bits) const { + // TODO: Implement this + return nullptr; +} + void AuthDBInstance::format_auth_result_msg(zmqpp::message &msg) { msg << ("S_" + name_); } diff --git a/src/modules/auth/auth-db/AuthDBInstance.hpp b/src/modules/auth/auth-db/AuthDBInstance.hpp index 4b61579e1..765d42c46 100644 --- a/src/modules/auth/auth-db/AuthDBInstance.hpp +++ b/src/modules/auth/auth-db/AuthDBInstance.hpp @@ -99,6 +99,16 @@ class AuthDBInstance : public std::enable_shared_from_this */ AuthResult handle_auth(zmqpp::message *msg) noexcept; + /** + * Fetch credentials passed in message from database if they exist + */ + Cred::ICredentialPtr find_db_credentials(zmqpp::message *msg); + + /** + * Find credentials in database by card id and number of bits + */ + Cred::ICredentialPtr find_credentials_by_card_id(const std::string &card_id, const int nb_bits) const; + /** * Format auth result message */ From 648f0375f4173137481cbf57cf16301b18d6328c Mon Sep 17 00:00:00 2001 From: Josh Sawyer <106642518+joshusooyaa@users.noreply.github.com> Date: Mon, 4 Aug 2025 05:08:14 -0700 Subject: [PATCH 19/57] Implement get_user Gets the user from the database that owns the credentials --- src/modules/auth/auth-db/AuthDBInstance.cpp | 30 ++++++++++++++++++--- src/modules/auth/auth-db/AuthDBInstance.hpp | 5 ++++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/src/modules/auth/auth-db/AuthDBInstance.cpp b/src/modules/auth/auth-db/AuthDBInstance.cpp index ed54432d6..a1ca49751 100644 --- a/src/modules/auth/auth-db/AuthDBInstance.cpp +++ b/src/modules/auth/auth-db/AuthDBInstance.cpp @@ -27,6 +27,7 @@ #include "exception/ExceptionsTools.hpp" #include #include +#include using namespace Leosac::Module::Auth; using namespace Leosac::Auth; @@ -97,12 +98,12 @@ AuthResult AuthDBInstance::handle_auth(zmqpp::message *msg) noexcept { try { std::lock_guard guard(mutex_); - Cred::ICredentialPtr db_credentials = find_db_credentials(msg); - if (!db_credentials) { + Cred::ICredentialPtr credentials = find_db_credentials(msg); + if (!credentials) { return auth_result; } - Cred::ICredentialPtr auth_source = db_credentials; + ::Leosac::Auth::UserPtr user = get_user(credentials); //TODO: Finish this @@ -137,6 +138,29 @@ Cred::ICredentialPtr AuthDBInstance::find_credentials_by_card_id(const std::stri return nullptr; } +::Leosac::Auth::UserPtr AuthDBInstance::get_user(const Cred::ICredentialPtr *credentials) { + using namespace odb; + using namespace odb::core; + ::Leosac::Auth::UserPtr user = nullptr; + + auto db = core_utils_->database(); + odb::transaction t(db->begin()); + + if (auto owner_lazy = credentials->owner()) { + if (auto owner = owner_lazy.load()) { + user = owner; + } else { + INFO("User does not exist in database"); + } + } else { + INFO("These credentials do not have an owner"); + } + + t.commit(); + + return user; +} + void AuthDBInstance::format_auth_result_msg(zmqpp::message &msg) { msg << ("S_" + name_); } diff --git a/src/modules/auth/auth-db/AuthDBInstance.hpp b/src/modules/auth/auth-db/AuthDBInstance.hpp index 765d42c46..1a49137fc 100644 --- a/src/modules/auth/auth-db/AuthDBInstance.hpp +++ b/src/modules/auth/auth-db/AuthDBInstance.hpp @@ -109,6 +109,11 @@ class AuthDBInstance : public std::enable_shared_from_this */ Cred::ICredentialPtr find_credentials_by_card_id(const std::string &card_id, const int nb_bits) const; + /** + * Get the user from the db associated with the credentials + */ + ::Leosac::Auth::UserPtr get_user(const Cred::ICredentialPtr *credentials); + /** * Format auth result message */ From 0dd4bf273ba8aa33ffeb062ca6f1445f4be656ce Mon Sep 17 00:00:00 2001 From: joshusooyaa <106642518+joshusooyaa@users.noreply.github.com> Date: Mon, 4 Aug 2025 14:37:52 -0700 Subject: [PATCH 20/57] Fix compilation errors --- src/modules/auth/auth-db/AuthDBInstance.cpp | 15 ++++++++++++--- src/modules/auth/auth-db/AuthDBInstance.hpp | 7 ++++++- src/modules/auth/auth-db/AuthDBModule.cpp | 6 ++++++ 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/modules/auth/auth-db/AuthDBInstance.cpp b/src/modules/auth/auth-db/AuthDBInstance.cpp index a1ca49751..2666fea17 100644 --- a/src/modules/auth/auth-db/AuthDBInstance.cpp +++ b/src/modules/auth/auth-db/AuthDBInstance.cpp @@ -23,7 +23,15 @@ #include "tools/log.hpp" #include "tools/Colorize.hpp" #include "core/auth/Auth.hpp" +#include "core/auth/User.hpp" +#include "core/auth/User_odb.h" #include "core/auth/AuthSourceBuilder.hpp" +#include "core/credentials/ICredential.hpp" +#include "core/credentials/RFIDCard.hpp" +#include "core/credentials/PinCode.hpp" +#include "core/credentials/RFIDCardPin.hpp" +#include "core/credentials/serializers/PolymorphicCredentialSerializer.hpp" +#include "core/SecurityContext.hpp" #include "exception/ExceptionsTools.hpp" #include #include @@ -31,6 +39,7 @@ using namespace Leosac::Module::Auth; using namespace Leosac::Auth; +using namespace Leosac::Cred; AuthDBInstance::AuthDBInstance(zmqpp::context &ctx, const std::string &auth_ctx_name, @@ -115,7 +124,7 @@ AuthResult AuthDBInstance::handle_auth(zmqpp::message *msg) noexcept { return auth_result; } -Cred::ICredentialPtr AuthDBInstance::find_db_credentials(zmqpp::message *msg) { +ICredentialPtr AuthDBInstance::find_db_credentials(zmqpp::message *msg) { AuthSourceBuilder builder; Cred::ICredentialPtr auth_source = builder.create(msg); Cred::ICredentialPtr db_credentials = nullptr; @@ -133,12 +142,12 @@ Cred::ICredentialPtr AuthDBInstance::find_db_credentials(zmqpp::message *msg) { return db_credentials; } -Cred::ICredentialPtr AuthDBInstance::find_credentials_by_card_id(const std::string &card_id, const int nb_bits) const { +ICredentialPtr AuthDBInstance::find_credentials_by_card_id(const std::string &card_id, const int nb_bits) const { // TODO: Implement this return nullptr; } -::Leosac::Auth::UserPtr AuthDBInstance::get_user(const Cred::ICredentialPtr *credentials) { +::Leosac::Auth::UserPtr AuthDBInstance::get_user(Cred::ICredentialPtr &credentials) { using namespace odb; using namespace odb::core; ::Leosac::Auth::UserPtr user = nullptr; diff --git a/src/modules/auth/auth-db/AuthDBInstance.hpp b/src/modules/auth/auth-db/AuthDBInstance.hpp index 1a49137fc..582cb7b12 100644 --- a/src/modules/auth/auth-db/AuthDBInstance.hpp +++ b/src/modules/auth/auth-db/AuthDBInstance.hpp @@ -87,6 +87,11 @@ class AuthDBInstance : public std::enable_shared_from_this */ void handle_bus_msg(); + /** + * Get the bus subscription socket + */ + zmqpp::socket& bus_sub() { return bus_sub_; } + private: /** * Handle a message from the kernel @@ -112,7 +117,7 @@ class AuthDBInstance : public std::enable_shared_from_this /** * Get the user from the db associated with the credentials */ - ::Leosac::Auth::UserPtr get_user(const Cred::ICredentialPtr *credentials); + ::Leosac::Auth::UserPtr get_user(Cred::ICredentialPtr &credentials); /** * Format auth result message diff --git a/src/modules/auth/auth-db/AuthDBModule.cpp b/src/modules/auth/auth-db/AuthDBModule.cpp index 863f2a3ea..b1125ee65 100644 --- a/src/modules/auth/auth-db/AuthDBModule.cpp +++ b/src/modules/auth/auth-db/AuthDBModule.cpp @@ -31,6 +31,12 @@ AuthDBModule::AuthDBModule(zmqpp::context& ctx, zmqpp::socket *pipe, : AsioModule(ctx, pipe, cfg, utils) { process_config(); + + for (auto authenticator : authenticators_) + { + reactor_.add(authenticator->bus_sub(), + std::bind(&AuthDBInstance::handle_bus_msg, authenticator)); + } } AuthDBModule::~AuthDBModule() {} From 426ddd4d789218eaedc05ae3d7b20ceb0d8da7ba Mon Sep 17 00:00:00 2001 From: joshusooyaa <106642518+joshusooyaa@users.noreply.github.com> Date: Mon, 4 Aug 2025 15:27:52 -0700 Subject: [PATCH 21/57] Implement find_credentials_by_card_id --- src/modules/auth/auth-db/AuthDBInstance.cpp | 54 +++++++++++++++++++-- src/modules/auth/auth-db/AuthDBInstance.hpp | 7 ++- 2 files changed, 55 insertions(+), 6 deletions(-) diff --git a/src/modules/auth/auth-db/AuthDBInstance.cpp b/src/modules/auth/auth-db/AuthDBInstance.cpp index 2666fea17..9450e55e7 100644 --- a/src/modules/auth/auth-db/AuthDBInstance.cpp +++ b/src/modules/auth/auth-db/AuthDBInstance.cpp @@ -31,11 +31,13 @@ #include "core/credentials/PinCode.hpp" #include "core/credentials/RFIDCardPin.hpp" #include "core/credentials/serializers/PolymorphicCredentialSerializer.hpp" +#include "core/credentials/RFIDCard_odb.h" #include "core/SecurityContext.hpp" #include "exception/ExceptionsTools.hpp" #include #include #include +#include using namespace Leosac::Module::Auth; using namespace Leosac::Auth; @@ -107,12 +109,12 @@ AuthResult AuthDBInstance::handle_auth(zmqpp::message *msg) noexcept { try { std::lock_guard guard(mutex_); - Cred::ICredentialPtr credentials = find_db_credentials(msg); + Cred::ICredentialPtr credentials = get_db_credentials(msg); if (!credentials) { return auth_result; } - ::Leosac::Auth::UserPtr user = get_user(credentials); + log_credentials(credentials); // Temporary //TODO: Finish this @@ -124,7 +126,7 @@ AuthResult AuthDBInstance::handle_auth(zmqpp::message *msg) noexcept { return auth_result; } -ICredentialPtr AuthDBInstance::find_db_credentials(zmqpp::message *msg) { +ICredentialPtr AuthDBInstance::get_db_credentials(zmqpp::message *msg) { AuthSourceBuilder builder; Cred::ICredentialPtr auth_source = builder.create(msg); Cred::ICredentialPtr db_credentials = nullptr; @@ -143,8 +145,36 @@ ICredentialPtr AuthDBInstance::find_db_credentials(zmqpp::message *msg) { } ICredentialPtr AuthDBInstance::find_credentials_by_card_id(const std::string &card_id, const int nb_bits) const { - // TODO: Implement this - return nullptr; + INFO("Searching for credentials by card id: " << card_id << " with " << nb_bits << " bits"); + + try { + using namespace odb; + using namespace odb::core; + using Query = odb::query; + + auto db = core_utils_->database(); + odb::transaction t(db->begin()); + + Query q(Query::card_id == card_id && Query::nb_bits == nb_bits); + auto result = db->query(q); + + for (const auto &card : result) { + if (card.validity().is_valid()) { + t.commit(); + return std::make_shared(card); + } else { + INFO("RFIDCard is not enabled (validity check failed)."); + } + } + + t.commit(); + return nullptr; + + } catch (const std::exception &e) { + WARN("Error finding credentials by card id: " << e.what()); + log_exception(e); + return nullptr; + } } ::Leosac::Auth::UserPtr AuthDBInstance::get_user(Cred::ICredentialPtr &credentials) { @@ -198,4 +228,18 @@ void AuthDBInstance::update_and_log_auth_result_msg(const AuthResult &auth_resul << " " << Colorize::red("DENIED") << " access to target " << Colorize::underline(target_name_) << " for user " << user); } +} + +void AuthDBInstance::log_credentials(Cred::ICredentialPtr &credentials) { + using namespace odb; + using namespace odb::core; + auto db = core_utils_->database(); + odb::transaction t(db->begin()); + + std::string cred_serialized; + cred_serialized = PolymorphicCredentialJSONStringSerializer::serialize( + *credentials, SystemSecurityContext::instance()); + INFO("Using Credential: " << cred_serialized); + + t.commit(); } \ No newline at end of file diff --git a/src/modules/auth/auth-db/AuthDBInstance.hpp b/src/modules/auth/auth-db/AuthDBInstance.hpp index 582cb7b12..e4e91da33 100644 --- a/src/modules/auth/auth-db/AuthDBInstance.hpp +++ b/src/modules/auth/auth-db/AuthDBInstance.hpp @@ -107,7 +107,7 @@ class AuthDBInstance : public std::enable_shared_from_this /** * Fetch credentials passed in message from database if they exist */ - Cred::ICredentialPtr find_db_credentials(zmqpp::message *msg); + Cred::ICredentialPtr get_db_credentials(zmqpp::message *msg); /** * Find credentials in database by card id and number of bits @@ -134,6 +134,11 @@ class AuthDBInstance : public std::enable_shared_from_this */ void update_and_log_auth_result_msg(const AuthResult &auth_result, zmqpp::message &auth_result_msg); + /** + * Log credentials to console + */ + void log_credentials(Cred::ICredentialPtr &credentials); + /** * Database service to query */ From 8ded91b16219420d4b67b17e9d738329fb14a678 Mon Sep 17 00:00:00 2001 From: joshusooyaa <106642518+joshusooyaa@users.noreply.github.com> Date: Mon, 4 Aug 2025 16:23:29 -0700 Subject: [PATCH 22/57] Build profile for authentication --- cmake/UseODB.cmake | 4 +- src/modules/auth/auth-db/AuthDBInstance.cpp | 51 ++++++++++++++++++++- src/modules/auth/auth-db/AuthDBInstance.hpp | 14 +++++- 3 files changed, 65 insertions(+), 4 deletions(-) diff --git a/cmake/UseODB.cmake b/cmake/UseODB.cmake index ebef8c110..288975b12 100644 --- a/cmake/UseODB.cmake +++ b/cmake/UseODB.cmake @@ -175,8 +175,8 @@ function(odb_compile outvar) list(APPEND ODB_ARGS "-I${dir}") endforeach() - file(REMOVE_RECURSE "${ODB_COMPILE_OUTPUT_DIR}") - file(MAKE_DIRECTORY "${ODB_COMPILE_OUTPUT_DIR}") + # file(REMOVE_RECURSE "${ODB_COMPILE_OUTPUT_DIR}") # Temporary for debugging and fast make + # file(MAKE_DIRECTORY "${ODB_COMPILE_OUTPUT_DIR}") # Temporary for debugging and fast make foreach(input ${PARAM_FILES}) get_filename_component(fname "${input}" NAME_WE) diff --git a/src/modules/auth/auth-db/AuthDBInstance.cpp b/src/modules/auth/auth-db/AuthDBInstance.cpp index 9450e55e7..24f5d430d 100644 --- a/src/modules/auth/auth-db/AuthDBInstance.cpp +++ b/src/modules/auth/auth-db/AuthDBInstance.cpp @@ -23,9 +23,11 @@ #include "tools/log.hpp" #include "tools/Colorize.hpp" #include "core/auth/Auth.hpp" +#include "core/auth/AuthFwd.hpp" #include "core/auth/User.hpp" #include "core/auth/User_odb.h" #include "core/auth/AuthSourceBuilder.hpp" +#include "core/auth/SimpleAccessProfile.hpp" #include "core/credentials/ICredential.hpp" #include "core/credentials/RFIDCard.hpp" #include "core/credentials/PinCode.hpp" @@ -42,6 +44,7 @@ using namespace Leosac::Module::Auth; using namespace Leosac::Auth; using namespace Leosac::Cred; +using namespace ::Leosac::Auth; AuthDBInstance::AuthDBInstance(zmqpp::context &ctx, const std::string &auth_ctx_name, @@ -116,6 +119,9 @@ AuthResult AuthDBInstance::handle_auth(zmqpp::message *msg) noexcept { log_credentials(credentials); // Temporary + UserPtr user = get_user(credentials); + IAccessProfilePtr profile = build_profile(user, credentials); + //TODO: Finish this } catch (std::exception &e) { @@ -200,6 +206,49 @@ ::Leosac::Auth::UserPtr AuthDBInstance::get_user(Cred::ICredentialPtr &credentia return user; } +::Leosac::Auth::IAccessProfilePtr AuthDBInstance::build_profile(UserPtr &user, ICredentialPtr &credentials) { + try { + std::vector profiles; + + using namespace odb; + using namespace odb::core; + auto db = core_utils_->database(); + odb::transaction t(db->begin()); + + auto mappings = db->query(); + + for (const auto &schedule_map : mappings) { + if ((user && schedule_map.has_user(user->id())) || + (credentials && schedule_map.has_cred(credentials->id()))) { + create_profile_from_schedule_mapping(schedule_map, profiles); + } + } + + t.commit(); + + if (profiles.empty()) { + return nullptr; + } + + return profiles.at(0); // Temporary + + } catch (const std::exception &e) { + WARN("AuthDBInstance::build_profile - Error building profile: " << e.what()); + return nullptr; + } +} + +void AuthDBInstance::create_profile_from_schedule_mapping(const Tools::ScheduleMapping &mapping, + std::vector &profiles) { + auto profile = std::make_shared(); + auto schedule = mapping.schedule().load(); + if (schedule) + { + profile->addAccessSchedule(nullptr, schedule); + profiles.push_back(profile); + } +} + void AuthDBInstance::format_auth_result_msg(zmqpp::message &msg) { msg << ("S_" + name_); } @@ -239,7 +288,7 @@ void AuthDBInstance::log_credentials(Cred::ICredentialPtr &credentials) { std::string cred_serialized; cred_serialized = PolymorphicCredentialJSONStringSerializer::serialize( *credentials, SystemSecurityContext::instance()); - INFO("Using Credential: " << cred_serialized); + INFO("Using Credentials: " << cred_serialized); t.commit(); } \ No newline at end of file diff --git a/src/modules/auth/auth-db/AuthDBInstance.hpp b/src/modules/auth/auth-db/AuthDBInstance.hpp index e4e91da33..7d3740b14 100644 --- a/src/modules/auth/auth-db/AuthDBInstance.hpp +++ b/src/modules/auth/auth-db/AuthDBInstance.hpp @@ -117,7 +117,13 @@ class AuthDBInstance : public std::enable_shared_from_this /** * Get the user from the db associated with the credentials */ - ::Leosac::Auth::UserPtr get_user(Cred::ICredentialPtr &credentials); + ::Leosac::Auth::UserPtr get_user(Cred::ICredentialPtr &credentials); + + /** + * Build access profile for the user + */ + ::Leosac::Auth::IAccessProfilePtr build_profile(::Leosac::Auth::UserPtr &user, + Cred::ICredentialPtr &credentials); /** * Format auth result message @@ -139,6 +145,12 @@ class AuthDBInstance : public std::enable_shared_from_this */ void log_credentials(Cred::ICredentialPtr &credentials); + /** + * Create a profile from a schedule mapping and add it to the list of profiles + */ + void create_profile_from_schedule_mapping(const Tools::ScheduleMapping &mapping, + std::vector<::Leosac::Auth::IAccessProfilePtr> &profiles); + /** * Database service to query */ From a9f398912091b6cac65d050e0d6bd29ebaf7e99a Mon Sep 17 00:00:00 2001 From: joshusooyaa <106642518+joshusooyaa@users.noreply.github.com> Date: Mon, 4 Aug 2025 16:57:46 -0700 Subject: [PATCH 23/57] Implement is_access_granted --- src/modules/auth/auth-db/AuthDBInstance.cpp | 16 ++++++++++++++++ src/modules/auth/auth-db/AuthDBInstance.hpp | 5 +++++ 2 files changed, 21 insertions(+) diff --git a/src/modules/auth/auth-db/AuthDBInstance.cpp b/src/modules/auth/auth-db/AuthDBInstance.cpp index 24f5d430d..4ec0169da 100644 --- a/src/modules/auth/auth-db/AuthDBInstance.cpp +++ b/src/modules/auth/auth-db/AuthDBInstance.cpp @@ -122,6 +122,11 @@ AuthResult AuthDBInstance::handle_auth(zmqpp::message *msg) noexcept { UserPtr user = get_user(credentials); IAccessProfilePtr profile = build_profile(user, credentials); + if (profile) { + bool access_granted = is_access_granted(profile); + auth_result = AuthResult(access_granted, profile, user); + } + //TODO: Finish this } catch (std::exception &e) { @@ -249,6 +254,17 @@ void AuthDBInstance::create_profile_from_schedule_mapping(const Tools::ScheduleM } } +bool AuthDBInstance::is_access_granted(IAccessProfilePtr &profile) { + auto now = std::chrono::system_clock::now(); + + if (target_name_.empty()) { + return profile->isAccessGranted(now, nullptr); + } else { + AuthTargetPtr target(new AuthTarget(target_name_)); + return profile->isAccessGranted(now, target); + } +} + void AuthDBInstance::format_auth_result_msg(zmqpp::message &msg) { msg << ("S_" + name_); } diff --git a/src/modules/auth/auth-db/AuthDBInstance.hpp b/src/modules/auth/auth-db/AuthDBInstance.hpp index 7d3740b14..8eda8f8f9 100644 --- a/src/modules/auth/auth-db/AuthDBInstance.hpp +++ b/src/modules/auth/auth-db/AuthDBInstance.hpp @@ -124,6 +124,11 @@ class AuthDBInstance : public std::enable_shared_from_this */ ::Leosac::Auth::IAccessProfilePtr build_profile(::Leosac::Auth::UserPtr &user, Cred::ICredentialPtr &credentials); + + /** + * Checks if the profile has access to the target + */ + bool is_access_granted(::Leosac::Auth::IAccessProfilePtr &profile); /** * Format auth result message From 8c086af92e08be709e8d2df65c07e28d4c2a6b0a Mon Sep 17 00:00:00 2001 From: joshusooyaa <106642518+joshusooyaa@users.noreply.github.com> Date: Mon, 4 Aug 2025 17:15:38 -0700 Subject: [PATCH 24/57] Add audit logging on auth event --- src/modules/auth/auth-db/AuthDBInstance.cpp | 25 ++++++++++++++++++++- src/modules/auth/auth-db/AuthDBInstance.hpp | 5 +++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/modules/auth/auth-db/AuthDBInstance.cpp b/src/modules/auth/auth-db/AuthDBInstance.cpp index 4ec0169da..f9780b872 100644 --- a/src/modules/auth/auth-db/AuthDBInstance.cpp +++ b/src/modules/auth/auth-db/AuthDBInstance.cpp @@ -35,6 +35,8 @@ #include "core/credentials/serializers/PolymorphicCredentialSerializer.hpp" #include "core/credentials/RFIDCard_odb.h" #include "core/SecurityContext.hpp" +#include "core/audit/AuthEvent.hpp" +#include "core/audit/AuditFactory.hpp" #include "exception/ExceptionsTools.hpp" #include #include @@ -127,7 +129,7 @@ AuthResult AuthDBInstance::handle_auth(zmqpp::message *msg) noexcept { auth_result = AuthResult(access_granted, profile, user); } - //TODO: Finish this + log_auth_event(auth_result, credentials); } catch (std::exception &e) { WARN("Error while handling auth request: " << e.what()); @@ -265,6 +267,27 @@ bool AuthDBInstance::is_access_granted(IAccessProfilePtr &profile) { } } +void AuthDBInstance::log_auth_event(const AuthResult &auth_result, Cred::ICredentialPtr &credentials) { + try { + if (!target_name_.empty()) { + using namespace odb; + using namespace odb::core; + auto db = core_utils_->database(); + odb::transaction t(db->begin()); + + auto audit = Audit::Factory::AuthEvent(db, credentials, target_name_); + audit->event_mask(auth_result.success ? Audit::EventType::AUTH_GRANTED : Audit::EventType::AUTH_DENIED); + audit->finalize(); + + t.commit(); + } else { + INFO("No target name provided, skipping Audit AuthEvent creation"); + } + } catch (const std::exception &e) { + WARN("Failed to create AuthEvent: " << e.what()); + } +} + void AuthDBInstance::format_auth_result_msg(zmqpp::message &msg) { msg << ("S_" + name_); } diff --git a/src/modules/auth/auth-db/AuthDBInstance.hpp b/src/modules/auth/auth-db/AuthDBInstance.hpp index 8eda8f8f9..e76b94bbb 100644 --- a/src/modules/auth/auth-db/AuthDBInstance.hpp +++ b/src/modules/auth/auth-db/AuthDBInstance.hpp @@ -130,6 +130,11 @@ class AuthDBInstance : public std::enable_shared_from_this */ bool is_access_granted(::Leosac::Auth::IAccessProfilePtr &profile); + /** + * Log auth event + */ + void log_auth_event(const AuthResult &auth_result, Cred::ICredentialPtr &credentials); + /** * Format auth result message */ From d54d4d95406b2ecbc5d30790a7b899cb457767b2 Mon Sep 17 00:00:00 2001 From: joshusooyaa <106642518+joshusooyaa@users.noreply.github.com> Date: Tue, 5 Aug 2025 17:02:11 -0700 Subject: [PATCH 25/57] Add state to lbgipod message handling Allows for doorman module to have door schedules - doorman tries to check door states during start up (if schedules are set). FGPIO::isOn() called with "STATE" command. LIBGPIOD did not have this message handled. This fix now has libgpiod handle the "STATE" message and sends the state back as "ON" or "OFF". --- src/modules/libgpiod/LibgpiodPin.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/modules/libgpiod/LibgpiodPin.cpp b/src/modules/libgpiod/LibgpiodPin.cpp index a9e910f61..c60469e86 100644 --- a/src/modules/libgpiod/LibgpiodPin.cpp +++ b/src/modules/libgpiod/LibgpiodPin.cpp @@ -111,6 +111,11 @@ void LibgpiodPin::handle_message() ok = turn_off(); else if (frame1 == "TOGGLE") ok = toggle(); + else if (frame1 == "STATE") { + bool state = read_value(); + sock_.send(state ? "ON" : "OFF"); + return; + } sock_.send(ok ? "OK" : "KO"); // publish new state. From 3a2eb9cdde9e28a96dff9832a5867603bbd1b53b Mon Sep 17 00:00:00 2001 From: joshusooyaa <106642518+joshusooyaa@users.noreply.github.com> Date: Mon, 11 Aug 2025 15:07:50 -0700 Subject: [PATCH 26/57] Add noise filtering in authentication Sometimes kickback will occur due to the nature of inductors. Solenoid strikers send kicback through the lines when they close. Additionally, wiegand lines can be very sensitive to noise. As a result, noise can come into the D0 and D1 lines and result in false negatives. This commit adds config variables to allow the user to ignore a certain amount of bits coming in that they might consider noise to have the software ignore it as an access attempt. --- src/modules/auth/auth-db/AuthDBInstance.cpp | 61 +++++++++++++++------ src/modules/auth/auth-db/AuthDBInstance.hpp | 51 +++++++++++++++-- src/modules/auth/auth-db/AuthDBModule.cpp | 9 ++- src/modules/auth/auth-db/AuthDBModule.hpp | 11 ++++ 4 files changed, 111 insertions(+), 21 deletions(-) diff --git a/src/modules/auth/auth-db/AuthDBInstance.cpp b/src/modules/auth/auth-db/AuthDBInstance.cpp index f9780b872..70dc67a03 100644 --- a/src/modules/auth/auth-db/AuthDBInstance.cpp +++ b/src/modules/auth/auth-db/AuthDBInstance.cpp @@ -52,13 +52,17 @@ AuthDBInstance::AuthDBInstance(zmqpp::context &ctx, const std::string &auth_ctx_name, const std::list &auth_sources_names, const std::string &auth_target_name, - CoreUtilsPtr core_utils) + CoreUtilsPtr core_utils, + const int bits_low_threshold, + const int bits_high_threshold) : db_service_(std::make_shared(core_utils->database())) , bus_push_(ctx, zmqpp::socket_type::push) , bus_sub_(ctx, zmqpp::socket_type::sub) , name_(auth_ctx_name) , target_name_(auth_target_name) , core_utils_(core_utils) + , bits_low_threshold_(bits_low_threshold) + , bits_high_threshold_(bits_high_threshold) { bus_push_.connect("inproc://zmq-bus-pull"); bus_sub_.connect("inproc://zmq-bus-pub"); @@ -87,9 +91,11 @@ void AuthDBInstance::handle_bus_msg() { format_auth_result_msg(auth_result_msg); auto auth_result = handle_auth(&msg); - - update_and_log_auth_result_msg(auth_result, auth_result_msg); - bus_push_.send(auth_result_msg); + + if (!auth_result.ignore) { + update_and_log_auth_result_msg(auth_result, auth_result_msg); + bus_push_.send(auth_result_msg); + } } bool AuthDBInstance::handle_kernel_msg(zmqpp::message &msg) { @@ -101,7 +107,7 @@ bool AuthDBInstance::handle_kernel_msg(zmqpp::message &msg) { msg_copy >> tmp; if (tmp == "SIGHUP") { INFO("AuthDBInstance received SIGHUP"); - // TODO: Implement reload authdb config + // TODO: Implement reload authdb config? } return true; } @@ -109,12 +115,18 @@ bool AuthDBInstance::handle_kernel_msg(zmqpp::message &msg) { } AuthResult AuthDBInstance::handle_auth(zmqpp::message *msg) noexcept { - AuthResult auth_result(false, nullptr, nullptr); + AuthResult auth_result(false, false, nullptr, nullptr); try { std::lock_guard guard(mutex_); - Cred::ICredentialPtr credentials = get_db_credentials(msg); + CredResult cred_result = get_db_credentials(msg); + if (cred_result.ignore) { + auth_result.ignore = true; + return auth_result; + } + + Cred::ICredentialPtr credentials = cred_result.db_credentials; if (!credentials) { return auth_result; } @@ -126,7 +138,7 @@ AuthResult AuthDBInstance::handle_auth(zmqpp::message *msg) noexcept { if (profile) { bool access_granted = is_access_granted(profile); - auth_result = AuthResult(access_granted, profile, user); + auth_result = AuthResult(access_granted, false, profile, user); } log_auth_event(auth_result, credentials); @@ -139,13 +151,13 @@ AuthResult AuthDBInstance::handle_auth(zmqpp::message *msg) noexcept { return auth_result; } -ICredentialPtr AuthDBInstance::get_db_credentials(zmqpp::message *msg) { +CredResult AuthDBInstance::get_db_credentials(zmqpp::message *msg) { AuthSourceBuilder builder; Cred::ICredentialPtr auth_source = builder.create(msg); - Cred::ICredentialPtr db_credentials = nullptr; + CredResult cred_result(false, nullptr); if (auto rfid_card = std::dynamic_pointer_cast(auth_source)) { - db_credentials = find_credentials_by_card_id(rfid_card->card_id(), rfid_card->nb_bits()); + cred_result = find_credentials_by_card_id(rfid_card->card_id(), rfid_card->nb_bits()); } else if (auto pin_code = std::dynamic_pointer_cast(auth_source)) { INFO("Pin code auth source not supported yet"); } else if (auto card_pin = std::dynamic_pointer_cast(auth_source)) { @@ -154,12 +166,17 @@ ICredentialPtr AuthDBInstance::get_db_credentials(zmqpp::message *msg) { WARN("Unknown credential type"); } - return db_credentials; + return cred_result; } -ICredentialPtr AuthDBInstance::find_credentials_by_card_id(const std::string &card_id, const int nb_bits) const { +CredResult AuthDBInstance::find_credentials_by_card_id(const std::string &card_id, const int nb_bits) const { INFO("Searching for credentials by card id: " << card_id << " with " << nb_bits << " bits"); + if (is_noise(nb_bits)) { + INFO("Number of bits, " << nb_bits << ", is configured as noise, ignoring."); + return CredResult(true, nullptr); + } + try { using namespace odb; using namespace odb::core; @@ -174,19 +191,19 @@ ICredentialPtr AuthDBInstance::find_credentials_by_card_id(const std::string &ca for (const auto &card : result) { if (card.validity().is_valid()) { t.commit(); - return std::make_shared(card); + return CredResult(false, std::make_shared(card)); } else { INFO("RFIDCard is not enabled (validity check failed)."); } } t.commit(); - return nullptr; + return CredResult(false, nullptr); } catch (const std::exception &e) { WARN("Error finding credentials by card id: " << e.what()); log_exception(e); - return nullptr; + return CredResult(false, nullptr); } } @@ -330,4 +347,16 @@ void AuthDBInstance::log_credentials(Cred::ICredentialPtr &credentials) { INFO("Using Credentials: " << cred_serialized); t.commit(); +} + +bool AuthDBInstance::is_noise(const int nb_bits) const { + if (bits_low_threshold_ != -1 && nb_bits < bits_low_threshold_) { + return true; + } + + if (bits_high_threshold_ != -1 && nb_bits > bits_high_threshold_) { + return true; + } + + return false; } \ No newline at end of file diff --git a/src/modules/auth/auth-db/AuthDBInstance.hpp b/src/modules/auth/auth-db/AuthDBInstance.hpp index e76b94bbb..32f572086 100644 --- a/src/modules/auth/auth-db/AuthDBInstance.hpp +++ b/src/modules/auth/auth-db/AuthDBInstance.hpp @@ -34,11 +34,31 @@ namespace Module namespace Auth { +struct CredResult +{ + CredResult(bool i, Cred::ICredentialPtr c) + : ignore(i) + , db_credentials(c) + {} + + /** + * If true, the auth request should be ignored + * Used for kickback and other unwanted noise on the bus + */ + bool ignore; + + /** + * Credentials found in the database + */ + Cred::ICredentialPtr db_credentials; +}; + struct AuthResult { - AuthResult(bool s, ::Leosac::Auth::IAccessProfilePtr p, + AuthResult(bool s, bool i, ::Leosac::Auth::IAccessProfilePtr p, ::Leosac::Auth::UserPtr u) : success(s) + , ignore(i) , profile(p) , user(u) {} @@ -48,6 +68,12 @@ struct AuthResult */ bool success; + /** + * If true, the auth request should be ignored + * Used for kickback and other unwanted noise on the bus + */ + bool ignore; + /** * Profile used to grant or deny access. May be null if no profiles * corresponding to the auth source were found. @@ -75,7 +101,9 @@ class AuthDBInstance : public std::enable_shared_from_this AuthDBInstance(zmqpp::context &ctx, const std::string &auth_ctx_name, const std::list &auth_sources_names, const std::string &auth_target_name, - CoreUtilsPtr core_utils); + CoreUtilsPtr core_utils, + const int bits_low_threshold, + const int bits_high_threshold); ~AuthDBInstance(); @@ -107,12 +135,12 @@ class AuthDBInstance : public std::enable_shared_from_this /** * Fetch credentials passed in message from database if they exist */ - Cred::ICredentialPtr get_db_credentials(zmqpp::message *msg); + CredResult get_db_credentials(zmqpp::message *msg); /** * Find credentials in database by card id and number of bits */ - Cred::ICredentialPtr find_credentials_by_card_id(const std::string &card_id, const int nb_bits) const; + CredResult find_credentials_by_card_id(const std::string &card_id, const int nb_bits) const; /** * Get the user from the db associated with the credentials @@ -160,6 +188,11 @@ class AuthDBInstance : public std::enable_shared_from_this */ void create_profile_from_schedule_mapping(const Tools::ScheduleMapping &mapping, std::vector<::Leosac::Auth::IAccessProfilePtr> &profiles); + + /** + * Check if the number of bits received is considered noise + */ + bool is_noise(const int nb_bits) const; /** * Database service to query @@ -190,6 +223,16 @@ class AuthDBInstance : public std::enable_shared_from_this CoreUtilsPtr core_utils_; std::mutex mutex_; + + /** + * Ignore <= this number of bits from bus + */ + int bits_low_threshold_; + + /** + * Ignore >= this number of bits from bus + */ + int bits_high_threshold_; }; } } diff --git a/src/modules/auth/auth-db/AuthDBModule.cpp b/src/modules/auth/auth-db/AuthDBModule.cpp index b1125ee65..7d62cef1f 100644 --- a/src/modules/auth/auth-db/AuthDBModule.cpp +++ b/src/modules/auth/auth-db/AuthDBModule.cpp @@ -45,6 +45,12 @@ void AuthDBModule::on_service_event(const service_event::Event &event) {} void AuthDBModule::process_config() { boost::property_tree::ptree auth_db_cfg = config_.get_child("module_config"); + + bits_low_threshold_ = auth_db_cfg.get("bits_low_threshold", -1); + bits_high_threshold_ = auth_db_cfg.get("bits_high_threshold", 127); + + INFO("Configured bits_low_threshold: " << bits_low_threshold_); + INFO("Configured bits_high_threshold: " << bits_high_threshold_); for (const auto &instance_node : auth_db_cfg.get_child("instances")) { boost::property_tree::ptree auth_instance_cfg = instance_node.second; @@ -83,5 +89,6 @@ void AuthDBModule::setup_authenticators(const std::string &auth_ctx_name, { authenticators_.push_back(AuthDBInstancePtr( new AuthDBInstance(ctx_, auth_ctx_name, auth_sources_names, - auth_target_name, utils_))); + auth_target_name, utils_, bits_low_threshold_, + bits_high_threshold_))); } \ No newline at end of file diff --git a/src/modules/auth/auth-db/AuthDBModule.hpp b/src/modules/auth/auth-db/AuthDBModule.hpp index 8e1ea556e..d8bbeb616 100644 --- a/src/modules/auth/auth-db/AuthDBModule.hpp +++ b/src/modules/auth/auth-db/AuthDBModule.hpp @@ -43,6 +43,17 @@ class AuthDBModule : public AsioModule AuthDBModule(const AuthDBModule &) = delete; ~AuthDBModule(); + + + /** + * Ignore less than this number of bits from bus + */ + int bits_low_threshold_; + + /** + * Ignore bits higher than this number of bits from bus + */ + int bits_high_threshold_; protected: void on_service_event(const service_event::Event &event) override; From c93dd64293fd4dd51710b4f49108e6e601db00b7 Mon Sep 17 00:00:00 2001 From: joshusooyaa <106642518+joshusooyaa@users.noreply.github.com> Date: Mon, 11 Aug 2025 15:34:14 -0700 Subject: [PATCH 27/57] Update auth-db module readme --- src/modules/auth/auth-db/auth-db.md | 147 +++++++++++++++++++++++++++- 1 file changed, 146 insertions(+), 1 deletion(-) diff --git a/src/modules/auth/auth-db/auth-db.md b/src/modules/auth/auth-db/auth-db.md index 1032c5a6a..8aa13be70 100644 --- a/src/modules/auth/auth-db/auth-db.md +++ b/src/modules/auth/auth-db/auth-db.md @@ -17,4 +17,149 @@ grant or deny access. Granting or denying access means sending a message to the application. This module doesn't open a door or perform action on its own. -@note Obviously this module requires that Leosac run with a database enabled. +@note This module requires that Leosac run with a database enabled. + +@note This module is suitable for systems with a large number of users/cards/groups as all data is stored in the database. + +Configuration Options {#mod_auth_db_user_config} +=================================================== + +Options | Options | Options | Options | Description | Mandatory +-----------|-----------|-----------------|--------------|-------------------------------------------------------------------|----------- +module_config | | | | Module configuration section | YES +---> | bits_low_threshold | | | Minimum number of bits to accept (ignores shorter as noise) | NO (default: -1) +---> | bits_high_threshold | | | Maximum number of bits to accept (ignores longer as noise) | NO (default: 127) +---> | instances | | | List of each link between reader and door(s) for authentication | YES +---> | ---> | instance | | Name of one particular auth context instance. Links with doorman module. Each instance under `instances` requires `` | YES +---> | ---> | ---> | name | Name of one particular auth context instance. Links with doorman module | YES +---> | ---> | ---> | auth_source | Which device (auth source) we listen to. Can appear multiple times. | YES +---> | ---> | ---> | target | Name of the target (door) that we are authenticating against | NO + +Notes: + + If the `target` is not present, the module assumes the default target, and will ignore target-specific permissions. + + The `bits_low_threshold` and `bits_high_threshold` help filter out noise from the bus. Values ≤ low_threshold or ≥ high_threshold are ignored. This is to help with systems that have inductive load (most door strikes are). D0 and D1 wires are very sensitive to any type of noise. Note that from a hardware perspective, you should have a diode across your door strike if it's an inductive load, since the kickback could potentially damage the GPIO pins. + + You can enter multiple `auth_source` devices. The module instance will listen to all of them. + + The `target` field is prefixed by the instance name and a dot when checking for permission in the database. This should match the GPIO out name in the `LIBGPIOD` module. + +Noise Filtering {#mod_auth_db_noise_filtering} +============================================== + +The AuthDB module includes intelligent noise filtering to prevent false authentication attempts from electrical interference or corrupted data on the bus. + +### Bit Threshold Configuration + +- **`bits_low_threshold`**: Any credential with fewer bits than this value will be ignored as noise +- **`bits_high_threshold`**: Any credential with more bits than this value will be ignored as corruption + +### Default Values + +- **Low threshold**: -1 (disabled by default) +- **High threshold**: 127 (maximum reasonable bit length) + +### Example Configuration + +```xml + + 8 + 64 + + +``` + +This configuration would: +- Ignore any credentials with ≤8 bits (likely noise) +- Ignore any credentials with ≥64 bits (likely noise) +- Only process credentials with 9-63 bits + +If for example, you only wanted to monitor attempts with 27 bit wiegand data, you could set the following: + +```xml + + 26 + 28 + + +``` + +This configuration would: +- Ignore any attempts that aren't 27 bits +- Even if there are valid attempts made with cards that aren't 27 bits, it would ignore it. + +Users {#mod_auth_db_user} +====================== + +Users are stored in the Leosac database and are a core component of the authentication system. +Users map to groups, credentials, schedules, etc. + +Users can hold additional properties like their name, email address, or the status of their account (`enabled`, or `disabled`). + +Credentials {#mod_auth_db_cred} +================================ + +Credentials are stored in the database and map to users. The module supports multiple credential types: + +### Supported Credential Types + +1. **RFIDCard**: Standard RFID card credentials +2. **PinCode**: PIN code authentication (not yet implemented) +3. **RFIDCardPin**: Combination of RFID card and PIN code (not yet implemented) + +Audit Logging {#mod_auth_db_audit} +=================================== + +The module provides comprehensive audit logging for all authentication attempts: + +### Logged Events + +- **AUTH_GRANTED**: Successful authentication attempts +- **AUTH_DENIED**: Failed authentication attempts +- **Credential usage**: Details about which credentials were used +- **Target information**: Which door/area was accessed +- **User information**: Who attempted the access + +### Audit Data + +Each audit event includes: +- Timestamp of the attempt +- Credential used +- Target accessed +- User attempting access +- Result (granted/denied) +- Access profile used + +Example Configuration {#mod_auth_db_example} +================================ + +This is the module configuration in the main config file: + +```xml + + AUTH-MANAGER-DB + libauth-db.so + 41 + + 8 + 64 + + + AUTH_CONTEXT_1 + MY_WIEGAND_1 + MY_WIEGAND_2 + MAIN_ENTRANCE + + + AUTH_CONTEXT_2 + BACK_DOOR_READER + BACK_ENTRANCE + + + + +``` + +This configuration: +- Sets noise filtering thresholds (8-64 bits) +- Creates two authentication contexts +- Each context listens to different readers +- Each context targets different doors + From 8709cf2c177a52c9b236dacd3f890f2c5bf4f9d0 Mon Sep 17 00:00:00 2001 From: joshusooyaa <106642518+joshusooyaa@users.noreply.github.com> Date: Mon, 11 Aug 2025 16:23:11 -0700 Subject: [PATCH 28/57] Remove uncessary method No longer needed since authdb tables are set up in leosac_db creation --- src/modules/auth/auth-db/AuthDBModule.cpp | 4 ---- src/modules/auth/auth-db/AuthDBModule.hpp | 5 ----- 2 files changed, 9 deletions(-) diff --git a/src/modules/auth/auth-db/AuthDBModule.cpp b/src/modules/auth/auth-db/AuthDBModule.cpp index 7d62cef1f..6ce26b501 100644 --- a/src/modules/auth/auth-db/AuthDBModule.cpp +++ b/src/modules/auth/auth-db/AuthDBModule.cpp @@ -79,10 +79,6 @@ void AuthDBModule::process_config() { } } -void AuthDBModule::setup_tables() { - // TODO: Re-implement this back in the future -} - void AuthDBModule::setup_authenticators(const std::string &auth_ctx_name, const std::list &auth_sources_names, const std::string &auth_target_name) diff --git a/src/modules/auth/auth-db/AuthDBModule.hpp b/src/modules/auth/auth-db/AuthDBModule.hpp index d8bbeb616..3ddbbf9f7 100644 --- a/src/modules/auth/auth-db/AuthDBModule.hpp +++ b/src/modules/auth/auth-db/AuthDBModule.hpp @@ -64,11 +64,6 @@ class AuthDBModule : public AsioModule */ void process_config(); - /** - * Add tables to the database if they don't exist - */ - void setup_tables(); - /** * Setup authenticator instances */ From 1f23e6da06e0f5292436578c178026862421af80 Mon Sep 17 00:00:00 2001 From: joshusooyaa <106642518+joshusooyaa@users.noreply.github.com> Date: Tue, 12 Aug 2025 14:41:27 -0700 Subject: [PATCH 29/57] Add targets to authentication mapping No longer allows access to all doors --- src/modules/auth/auth-db/AuthDBInstance.cpp | 33 +++++++++++++++++++-- src/modules/auth/auth-db/AuthDBInstance.hpp | 7 +++++ 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/modules/auth/auth-db/AuthDBInstance.cpp b/src/modules/auth/auth-db/AuthDBInstance.cpp index 70dc67a03..151c5da01 100644 --- a/src/modules/auth/auth-db/AuthDBInstance.cpp +++ b/src/modules/auth/auth-db/AuthDBInstance.cpp @@ -266,10 +266,37 @@ void AuthDBInstance::create_profile_from_schedule_mapping(const Tools::ScheduleM std::vector &profiles) { auto profile = std::make_shared(); auto schedule = mapping.schedule().load(); - if (schedule) - { + + if (!schedule) { + return; + } + + add_doors_to_profile(mapping, profile, schedule); + + profiles.push_back(profile); +} + +void AuthDBInstance::add_doors_to_profile(const Tools::ScheduleMapping &mapping, + SimpleAccessProfilePtr &profile, + const Leosac::Tools::IScheduleCPtr& schedule) { + for (const auto &door : mapping.doors()) { + if (auto lazy_door = door.load()) { + std::string alias = lazy_door->alias(); + if (alias.empty()) { + continue; + } + + profile->addAccessSchedule(AuthTargetPtr(new AuthTarget(alias)), schedule); + + static const std::string ns_prefix = "my_leosac."; + if (alias.rfind(ns_prefix, 0) != 0) { + profile->addAccessSchedule(AuthTargetPtr(new AuthTarget(ns_prefix + alias)), schedule); + } + } + } + + if (mapping.doors().empty()) { profile->addAccessSchedule(nullptr, schedule); - profiles.push_back(profile); } } diff --git a/src/modules/auth/auth-db/AuthDBInstance.hpp b/src/modules/auth/auth-db/AuthDBInstance.hpp index 32f572086..4520690a9 100644 --- a/src/modules/auth/auth-db/AuthDBInstance.hpp +++ b/src/modules/auth/auth-db/AuthDBInstance.hpp @@ -189,6 +189,13 @@ class AuthDBInstance : public std::enable_shared_from_this void create_profile_from_schedule_mapping(const Tools::ScheduleMapping &mapping, std::vector<::Leosac::Auth::IAccessProfilePtr> &profiles); + /** + * Add targets to the profile from a schedule mapping + */ + void add_doors_to_profile(const Tools::ScheduleMapping &mapping, + Leosac::Auth::SimpleAccessProfilePtr &profile, + const ::Leosac::Tools::IScheduleCPtr& schedule); + /** * Check if the number of bits received is considered noise */ From b8396dc9829151d700d5c9c4693e06749cfb8fe3 Mon Sep 17 00:00:00 2001 From: joshusooyaa <106642518+joshusooyaa@users.noreply.github.com> Date: Tue, 12 Aug 2025 15:44:49 -0700 Subject: [PATCH 30/57] Check users and groups for access --- src/modules/auth/auth-db/AuthDBInstance.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/auth/auth-db/AuthDBInstance.cpp b/src/modules/auth/auth-db/AuthDBInstance.cpp index 151c5da01..13b8efe89 100644 --- a/src/modules/auth/auth-db/AuthDBInstance.cpp +++ b/src/modules/auth/auth-db/AuthDBInstance.cpp @@ -242,7 +242,7 @@ ::Leosac::Auth::IAccessProfilePtr AuthDBInstance::build_profile(UserPtr &user, I auto mappings = db->query(); for (const auto &schedule_map : mappings) { - if ((user && schedule_map.has_user(user->id())) || + if ((user && schedule_map.has_user_indirect(user)) || (credentials && schedule_map.has_cred(credentials->id()))) { create_profile_from_schedule_mapping(schedule_map, profiles); } From 4c97fbf52af66ad7f4fe34e9102b62ec48f8e033 Mon Sep 17 00:00:00 2001 From: joshusooyaa <106642518+joshusooyaa@users.noreply.github.com> Date: Tue, 12 Aug 2025 17:06:56 -0700 Subject: [PATCH 31/57] Fix EventType values for proper FlagSet indexing Replaced invalid bitmask hex values with sequential indicies. The bitmask hex values were all defaulting to 0. --- src/core/audit/AuditFwd.hpp | 133 ++++++++++++++++-------------------- 1 file changed, 59 insertions(+), 74 deletions(-) diff --git a/src/core/audit/AuditFwd.hpp b/src/core/audit/AuditFwd.hpp index 38fe79867..03f0dde03 100644 --- a/src/core/audit/AuditFwd.hpp +++ b/src/core/audit/AuditFwd.hpp @@ -124,80 +124,65 @@ using ZoneEventPtr = std::shared_ptr; enum class EventType { - /** - * Admin & System Event Types - */ - AL_SYSTEM = 0x1000, - /** - * User Event Types - */ - AL_USER = 0x2000, - - WSAPI_CALL = 0x00 & AL_SYSTEM, - - USER_CREATED = 0x10 & AL_SYSTEM, - USER_DELETED = 0x11 & AL_SYSTEM, - USER_EDITED = 0x12 & AL_SYSTEM, - USER_PASSWORD_CHANGED = 0x13 & AL_SYSTEM, - /** - * An attempt to change the password failed. - */ - USER_PASSWORD_CHANGE_FAILURE = 0x14 & AL_SYSTEM, - - GROUP_CREATED = 0x20 & AL_SYSTEM, - GROUP_UPDATED = 0x21 & AL_SYSTEM, - GROUP_DELETED = 0x22 & AL_SYSTEM, - /** - * Someone joined the group. - */ - GROUP_MEMBERSHIP_JOINED = 0x23 & AL_SYSTEM, - /** - * Someone left the group. - */ - GROUP_MEMBERSHIP_LEFT = 0x24 & AL_SYSTEM, - - CREDENTIAL_DELETED = 0x30 & AL_SYSTEM, - CREDENTIAL_CREATED = 0x31 & AL_SYSTEM, - CREDENTIAL_UPDATED = 0x32 & AL_SYSTEM, - - SCHEDULE_CREATED = 0x40 & AL_SYSTEM, - SCHEDULE_DELETED = 0x41 & AL_SYSTEM, - SCHEDULE_UPDATED = 0x42 & AL_SYSTEM, - - DOOR_CREATED = 0x50 & AL_SYSTEM, - DOOR_UPDATED = 0x51 & AL_SYSTEM, - DOOR_DELETED = 0x52 & AL_SYSTEM, - /** - * This event is linked to door. It is generated - * by the Schedule CRUD manager. It's used to let a door - * know that it may have been removed from a mapping. - * - * Fixme: Currently this event is triggered for every door in a mapping - * everytime a schedule is updated. - */ - MAPPING_MAY_HAVE_CHANGED = 0x53 & AL_SYSTEM, - - ACCESS_POINT_CREATED = 0x60 & AL_SYSTEM, - ACCESS_POINT_UPDATED = 0x61 & AL_SYSTEM, - ACCESS_POINT_DELETED = 0x62 & AL_SYSTEM, - - ZONE_CREATED = 0x70 & AL_SYSTEM, - ZONE_UPDATED = 0x71 & AL_SYSTEM, - ZONE_DELETED = 0x72 & AL_SYSTEM, - - UPDATE_CREATED = 0x80 & AL_SYSTEM, - UPDATE_ACKED = 0x81 & AL_SYSTEM, - UPDATE_CANCELLED = 0x81 & AL_SYSTEM, - - DOOR_OPENED = 0x10 & AL_USER, - DOOR_OPENED_MANUALLY = 0x11 & AL_USER, - DOOR_FORCED = 0x12 & AL_USER, - DOOR_FORCED_END = 0x13 & AL_USER, - - AUTH_GRANTED = 0x20 & AL_USER, - AUTH_DENIED = 0x21 & AL_USER, - - LAST__ = 0xffff + // 0..63 reserved to keep masks within 64-bit numeric serialization + + // 0..15 System/Admin + WSAPI_CALL = 0, + USER_CREATED = 1, + USER_DELETED = 2, + USER_EDITED = 3, + USER_PASSWORD_CHANGED = 4, + USER_PASSWORD_CHANGE_FAILURE = 5, + + // 16..23 Groups + GROUP_CREATED = 16, + GROUP_UPDATED = 17, + GROUP_DELETED = 18, + GROUP_MEMBERSHIP_JOINED = 19, + GROUP_MEMBERSHIP_LEFT = 20, + + // 24..27 Credentials + CREDENTIAL_CREATED = 24, + CREDENTIAL_UPDATED = 25, + CREDENTIAL_DELETED = 26, + + // 28..30 Schedules + SCHEDULE_CREATED = 28, + SCHEDULE_UPDATED = 29, + SCHEDULE_DELETED = 30, + + // 31..35 Doors + DOOR_CREATED = 31, + DOOR_UPDATED = 32, + DOOR_DELETED = 33, + MAPPING_MAY_HAVE_CHANGED = 34, + + // 36..38 Access points + ACCESS_POINT_CREATED = 36, + ACCESS_POINT_UPDATED = 37, + ACCESS_POINT_DELETED = 38, + + // 39..41 Zones + ZONE_CREATED = 39, + ZONE_UPDATED = 40, + ZONE_DELETED = 41, + + // 42..44 Updates + UPDATE_CREATED = 42, + UPDATE_ACKED = 43, + UPDATE_CANCELLED = 44, + + // 45..48 Door usage + DOOR_OPENED = 45, + DOOR_OPENED_MANUALLY = 46, + DOOR_FORCED = 47, + DOOR_FORCED_END = 48, + + // 49..50 Auth outcomes + AUTH_GRANTED = 49, + AUTH_DENIED = 50, + + LAST__ = 64 }; using EventMask = FlagSet; From ede14e94e0c6bc374f09b89d92b8738d23fef65a Mon Sep 17 00:00:00 2001 From: joshusooyaa <106642518+joshusooyaa@users.noreply.github.com> Date: Wed, 13 Aug 2025 14:43:29 -0700 Subject: [PATCH 32/57] Remove required AuditEntry parent Parent is allowed to be null for authevents since it can be a top level event. --- src/core/audit/AuditFactory.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/core/audit/AuditFactory.cpp b/src/core/audit/AuditFactory.cpp index 425052a26..0bd0e3ba2 100644 --- a/src/core/audit/AuditFactory.cpp +++ b/src/core/audit/AuditFactory.cpp @@ -143,8 +143,6 @@ IAuthEventPtr Factory::AuthEvent(const DBPtr &database, Cred::ICredentialPtr cre ASSERT_LOG(database, "Database cannot be null."); ASSERT_LOG(credential, "Credential must be non null."); ASSERT_LOG(!door.empty(), "Door must be set."); - ASSERT_LOG(parent, "Parent must be non null."); - ASSERT_LOG(parent->id(), "Parent must be already persisted."); AuditEntryPtr parent_odb; if (parent && parent->id()) From 7bf08d840d1a5cf2ac57aad67c0f00f532679fae Mon Sep 17 00:00:00 2001 From: joshusooyaa <106642518+joshusooyaa@users.noreply.github.com> Date: Tue, 19 Aug 2025 10:42:23 -0700 Subject: [PATCH 33/57] Add clear_schedules Used as a helper to clear schedules so doorman can constantly check if each door has updated schedules. --- src/core/auth/AuthTarget.cpp | 5 +++++ src/core/auth/AuthTarget.hpp | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/src/core/auth/AuthTarget.cpp b/src/core/auth/AuthTarget.cpp index 249857eff..1e92f35d4 100644 --- a/src/core/auth/AuthTarget.cpp +++ b/src/core/auth/AuthTarget.cpp @@ -49,6 +49,11 @@ void AuthTarget::add_always_close_sched(Leosac::Tools::IScheduleCPtr const &sche always_close_.push_back(sched); } +void AuthTarget::clear_schedules() { + always_open_.clear(); + always_close_.clear(); +} + Leosac::Hardware::FGPIO *AuthTarget::gpio() const { return gpio_.get(); diff --git a/src/core/auth/AuthTarget.hpp b/src/core/auth/AuthTarget.hpp index 803505ab9..c9c602e29 100644 --- a/src/core/auth/AuthTarget.hpp +++ b/src/core/auth/AuthTarget.hpp @@ -57,6 +57,11 @@ class AuthTarget */ bool is_always_closed(const std::chrono::system_clock::time_point &tp) const; + /** + * Clear schedules (helper for refreshing schedules) + */ + void clear_schedules(); + /* * Reset the door the its expected default state at the current time. */ From e5deec6f8fe83159dc0e28d0b63025a731a8f768 Mon Sep 17 00:00:00 2001 From: joshusooyaa <106642518+joshusooyaa@users.noreply.github.com> Date: Tue, 19 Aug 2025 15:25:33 -0700 Subject: [PATCH 34/57] Begin db logic for door schedules --- src/modules/doorman/DoormanModule.cpp | 22 +++++++++++++++++++++- src/modules/doorman/DoormanModule.hpp | 14 ++++++++++++-- src/modules/doorman/doorman.md | 1 + 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/src/modules/doorman/DoormanModule.cpp b/src/modules/doorman/DoormanModule.cpp index 28c2af8b2..b26dd2d5a 100644 --- a/src/modules/doorman/DoormanModule.cpp +++ b/src/modules/doorman/DoormanModule.cpp @@ -24,6 +24,8 @@ #include "core/kernel.hpp" #include "hardware/facades/FAlarm.hpp" #include "tools/log.hpp" +#include "tools/service/ServiceRegistry.hpp" +#include "tools/db/DBService.hpp" using namespace Leosac::Module::Doorman; using namespace Leosac::Auth; @@ -32,6 +34,9 @@ DoormanModule::DoormanModule(zmqpp::context &ctx, zmqpp::socket *pipe, const boost::property_tree::ptree &cfg, CoreUtilsPtr utils) : BaseModule(ctx, pipe, cfg, utils) + , use_db_schedules_(false) + , db_service_(nullptr) + { try { @@ -60,6 +65,9 @@ void DoormanModule::process_config() { boost::property_tree::ptree module_config = config_.get_child("module_config"); + use_db_schedules_ = module_config.get("use_db_schedules", false); + set_db_service(); + auto doors_cfg = module_config.get_child_optional("doors"); if (doors_cfg) process_doors_config(*doors_cfg); @@ -110,6 +118,18 @@ void DoormanModule::process_config() } } +void DoormanModule::set_db_service() { + if (use_db_schedules_) { + db_service_ = utils_->service_registry().get_service(); + if (!db_service_) { + WARN("Database schedules requested but DBService not available. Falling back to config schedules."); + use_db_schedules_ = false; + } else { + INFO("Using database schedules for doorman module"); + } + } +} + void DoormanModule::run() { while (is_running_) @@ -136,7 +156,7 @@ void DoormanModule::process_doors_config( door->gpio( std::unique_ptr(new Hardware::FGPIO(ctx_, gpio))); - if (open_schedule) + if (!use_db_schedules_ && open_schedule) { Tools::XmlScheduleLoader xml_sched; xml_sched.load(*open_schedule); diff --git a/src/modules/doorman/DoormanModule.hpp b/src/modules/doorman/DoormanModule.hpp index 0fc923a69..6fc363f12 100644 --- a/src/modules/doorman/DoormanModule.hpp +++ b/src/modules/doorman/DoormanModule.hpp @@ -23,6 +23,7 @@ #include "hardware/facades/FGPIO.hpp" #include "modules/BaseModule.hpp" #include "tools/XmlScheduleLoader.hpp" +#include "tools/db/db_fwd.hpp" #include #include #include @@ -66,8 +67,6 @@ class DoormanModule : public BaseModule const std::vector &doors() const; private: - void update(); - /** * Processing the configuration tree, spawning AuthFileInstance object as * described in the @@ -77,6 +76,13 @@ class DoormanModule : public BaseModule void process_doors_config(const boost::property_tree::ptree &t); + /** + * Sets the db_service_ member variable if use_db_schedules_ is true. + */ + void set_db_service(); + + void update(); + /** * Authenticator instances. */ @@ -86,6 +92,10 @@ class DoormanModule : public BaseModule * Doors, to manage the always-on or always off stuff. */ std::vector doors_; + + bool use_db_schedules_; + + DBServicePtr db_service_; }; } } diff --git a/src/modules/doorman/doorman.md b/src/modules/doorman/doorman.md index 02b5f7895..7d42318b1 100644 --- a/src/modules/doorman/doorman.md +++ b/src/modules/doorman/doorman.md @@ -26,6 +26,7 @@ Configuration Options {#mod_doorman_user_config} Options | Options | Options | Options | Options | Description | Mandatory -----------|-----------|-----------------|--------------|-------------|-------------------------------------------------------------------|----------- +use_db_schedules | | | | | When set to true, use database schedules instead of config schedules | NO (default: false) instances | | | | | Multiples (unrelated) doorman instance are allowed | YES ---> | instance | | | | Configuration information for 1 instance | YES ---> | ---> | name | | | Name of this doorman instance | YES From e78964f1db82447ceb09930557dbadc1f2c056ef Mon Sep 17 00:00:00 2001 From: joshusooyaa <106642518+joshusooyaa@users.noreply.github.com> Date: Tue, 19 Aug 2025 15:47:43 -0700 Subject: [PATCH 35/57] Fetch doors to configure schedule for --- src/modules/doorman/CMakeLists.txt | 15 +++++++++ src/modules/doorman/DoormanModule.cpp | 45 +++++++++++++++++++++++++++ src/modules/doorman/DoormanModule.hpp | 12 +++++++ 3 files changed, 72 insertions(+) diff --git a/src/modules/doorman/CMakeLists.txt b/src/modules/doorman/CMakeLists.txt index 828da04a0..764dbf076 100644 --- a/src/modules/doorman/CMakeLists.txt +++ b/src/modules/doorman/CMakeLists.txt @@ -13,4 +13,19 @@ set_target_properties(${DOORMAN_BIN} PROPERTIES COMPILE_FLAGS "${MODULE_COMPILE_FLAGS}" ) +target_include_directories(${DOORMAN_BIN} + PUBLIC + ${CMAKE_SOURCE_DIR}/src + ${CMAKE_SOURCE_DIR}/src/modules + ${CMAKE_SOURCE_DIR}/deps/zmqpp/src + ${CMAKE_SOURCE_DIR}/deps/flagset + ${CMAKE_SOURCE_DIR}/deps/date/include + ${CMAKE_BINARY_DIR}/odb_gen + ${CMAKE_BINARY_DIR} + ${ZEROMQ_INCLUDE_DIR} + ${ODB_INCLUDE_DIRS} + ${ODB_COMPILE_OUTPUT_DIR} + ) + + install(TARGETS ${DOORMAN_BIN} DESTINATION ${LEOSAC_MODULE_INSTALL_DIR}) diff --git a/src/modules/doorman/DoormanModule.cpp b/src/modules/doorman/DoormanModule.cpp index b26dd2d5a..4ba25999b 100644 --- a/src/modules/doorman/DoormanModule.cpp +++ b/src/modules/doorman/DoormanModule.cpp @@ -26,6 +26,10 @@ #include "tools/log.hpp" #include "tools/service/ServiceRegistry.hpp" #include "tools/db/DBService.hpp" +#include "tools/Schedule.hpp" +#include "tools/ScheduleMapping.hpp" +#include "core/auth/Door.hpp" +#include "core/auth/Door_odb.h" using namespace Leosac::Module::Doorman; using namespace Leosac::Auth; @@ -71,6 +75,8 @@ void DoormanModule::process_config() auto doors_cfg = module_config.get_child_optional("doors"); if (doors_cfg) process_doors_config(*doors_cfg); + if (use_db_schedules_) + process_db_schedules(); for (const auto &node : module_config.get_child("instances")) { @@ -204,6 +210,45 @@ void DoormanModule::process_doors_config( } } +void DoormanModule::process_db_schedules() { + try { + auto db = db_service_->db(); + odb::transaction t(db->begin()); + odb::result schedules = db->query(); + std::map> door_open_timeframes; + + clear_door_schedules(); + + for (const auto &schedule : schedules) { + for (const auto &mapping : schedule.mapping()) { + if (is_door_schedule(mapping)) { + for (const auto &lazy_door : mapping->doors()) { + // Continue here + } + } + } + } + } catch (const std::exception &e) { + ERROR("Failed to process database schedules: " << e.what()); + use_db_schedules_ = false; + } +} + +void DoormanModule::clear_door_schedules() { + for (auto &door : doors_) + door->clear_schedules(); +} + +bool DoormanModule::is_door_schedule(const Tools::ScheduleMappingPtr &mapping) { + bool has_doors = !mapping->doors().empty(); + bool has_users = !mapping->users().empty(); + bool has_groups = !mapping->groups().empty(); + bool has_credentials = !mapping->credentials().empty(); + bool has_zones = !mapping->zones().empty(); + + return (has_doors || has_zones) && !has_users && !has_groups && !has_credentials; +} + void DoormanModule::update() { auto now = std::chrono::system_clock::now(); diff --git a/src/modules/doorman/DoormanModule.hpp b/src/modules/doorman/DoormanModule.hpp index 6fc363f12..85c730a22 100644 --- a/src/modules/doorman/DoormanModule.hpp +++ b/src/modules/doorman/DoormanModule.hpp @@ -76,11 +76,23 @@ class DoormanModule : public BaseModule void process_doors_config(const boost::property_tree::ptree &t); + void process_db_schedules(); + /** * Sets the db_service_ member variable if use_db_schedules_ is true. */ void set_db_service(); + /** + * Clears always on and off schedules from all doors. + */ + void clear_door_schedules(); + + /** + * Checks if a schedule mapping contains only doors + */ + bool is_door_schedule(const Tools::ScheduleMappingPtr &mapping); + void update(); /** From d7d9c9b2e5802486f2f090924176b05b7bfd7612 Mon Sep 17 00:00:00 2001 From: joshusooyaa <106642518+joshusooyaa@users.noreply.github.com> Date: Tue, 19 Aug 2025 16:01:31 -0700 Subject: [PATCH 36/57] Add open door schedules from db to doors --- src/modules/doorman/DoormanModule.cpp | 31 ++++++++++++++++++++------- src/modules/doorman/DoormanModule.hpp | 6 ++++++ 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/modules/doorman/DoormanModule.cpp b/src/modules/doorman/DoormanModule.cpp index 4ba25999b..6764a3e43 100644 --- a/src/modules/doorman/DoormanModule.cpp +++ b/src/modules/doorman/DoormanModule.cpp @@ -219,18 +219,33 @@ void DoormanModule::process_db_schedules() { clear_door_schedules(); - for (const auto &schedule : schedules) { - for (const auto &mapping : schedule.mapping()) { - if (is_door_schedule(mapping)) { - for (const auto &lazy_door : mapping->doors()) { - // Continue here + add_open_door_schedules(schedules); + + t.commit(); + } catch (const std::exception &e) { + ERROR("Failed to process database schedules: " << e.what()); + use_db_schedules_ = false; + } +} + +void DoormanModule::add_open_door_schedules(odb::result &schedules) { + for (const auto &schedule : schedules) { + for (const auto &mapping : schedule.mapping()) { + if (is_door_schedule(mapping)) { + for (const auto &lazy_door : mapping->doors()) { + auto door_ptr = lazy_door.load(); + if (door_ptr) { + std::string door_name = door_ptr->alias(); + for (auto &door : doors_) { + if (door->name() == door_name) { + auto schedule_copy = std::make_shared(schedule); + door->add_always_open_sched(schedule_copy); + } + } } } } } - } catch (const std::exception &e) { - ERROR("Failed to process database schedules: " << e.what()); - use_db_schedules_ = false; } } diff --git a/src/modules/doorman/DoormanModule.hpp b/src/modules/doorman/DoormanModule.hpp index 85c730a22..17133a942 100644 --- a/src/modules/doorman/DoormanModule.hpp +++ b/src/modules/doorman/DoormanModule.hpp @@ -24,6 +24,7 @@ #include "modules/BaseModule.hpp" #include "tools/XmlScheduleLoader.hpp" #include "tools/db/db_fwd.hpp" +#include "tools/Schedule.hpp" #include #include #include @@ -78,6 +79,11 @@ class DoormanModule : public BaseModule void process_db_schedules(); + /** + * Adds open schedules to the doors from the database. + */ + void add_open_door_schedules(odb::result &schedules); + /** * Sets the db_service_ member variable if use_db_schedules_ is true. */ From 9e9fa20cf0921319fbcfb2f127e8f07ba7c7b5e6 Mon Sep 17 00:00:00 2001 From: joshusooyaa <106642518+joshusooyaa@users.noreply.github.com> Date: Tue, 19 Aug 2025 17:12:45 -0700 Subject: [PATCH 37/57] Begin logic for adding close door schedules --- src/modules/doorman/DoormanModule.cpp | 30 +++++++++++++++++++++++++-- src/modules/doorman/DoormanModule.hpp | 12 ++++++++++- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/src/modules/doorman/DoormanModule.cpp b/src/modules/doorman/DoormanModule.cpp index 6764a3e43..bc932d182 100644 --- a/src/modules/doorman/DoormanModule.cpp +++ b/src/modules/doorman/DoormanModule.cpp @@ -219,7 +219,8 @@ void DoormanModule::process_db_schedules() { clear_door_schedules(); - add_open_door_schedules(schedules); + add_open_door_schedules(schedules, door_open_timeframes); + add_close_door_schedules(schedules, door_open_timeframes); t.commit(); } catch (const std::exception &e) { @@ -228,7 +229,9 @@ void DoormanModule::process_db_schedules() { } } -void DoormanModule::add_open_door_schedules(odb::result &schedules) { +void DoormanModule::add_open_door_schedules(odb::result &schedules, + std::map> &door_open_timeframes) +{ for (const auto &schedule : schedules) { for (const auto &mapping : schedule.mapping()) { if (is_door_schedule(mapping)) { @@ -240,15 +243,38 @@ void DoormanModule::add_open_door_schedules(odb::result &schedu if (door->name() == door_name) { auto schedule_copy = std::make_shared(schedule); door->add_always_open_sched(schedule_copy); + + for (const auto &tf : schedule_copy->timeframes()) { + door_open_timeframes[door_name].push_back(tf); + } } } } } } + // TODO: Add schedules to doors in zones + } + } +} + +void DoormanModule::add_close_door_schedules(odb::result &schedules, + std::map> &door_open_timeframes) +{ + for (const auto &door : doors_) { + std::string door_name = door->name(); + auto timeframes = door_open_timeframes[door_name]; + auto inverse_schedules = create_inverse_schedules(timeframes); + for (const auto &schedule : inverse_schedules) { + door->add_always_close_sched(schedule); } } } +std::vector> DoormanModule::create_inverse_schedules(const std::vector &timeframes) { + // TODO: Implement this + return {}; +} + void DoormanModule::clear_door_schedules() { for (auto &door : doors_) door->clear_schedules(); diff --git a/src/modules/doorman/DoormanModule.hpp b/src/modules/doorman/DoormanModule.hpp index 17133a942..7241a76fe 100644 --- a/src/modules/doorman/DoormanModule.hpp +++ b/src/modules/doorman/DoormanModule.hpp @@ -82,8 +82,18 @@ class DoormanModule : public BaseModule /** * Adds open schedules to the doors from the database. */ - void add_open_door_schedules(odb::result &schedules); + void add_open_door_schedules(odb::result &schedules, std::map> &door_open_timeframes); + /** + * Adds close schedules to the doors from the database. + */ + void add_close_door_schedules(odb::result &schedules, std::map> &door_open_timeframes); + + /** + * Creates an inverse schedule based off of the open timeframes to close the door + * when the open schedule is not active. + */ + std::vector> create_inverse_schedules(const std::vector &timeframes); /** * Sets the db_service_ member variable if use_db_schedules_ is true. */ From 249b5adf89a503563f8d6a4c297d71f352057a5c Mon Sep 17 00:00:00 2001 From: joshusooyaa <106642518+joshusooyaa@users.noreply.github.com> Date: Wed, 20 Aug 2025 10:28:17 -0700 Subject: [PATCH 38/57] Add 24_7 closed schedule to doors with no schedule --- src/modules/doorman/DoormanModule.cpp | 18 +++++++++++++++++- src/modules/doorman/DoormanModule.hpp | 6 ++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/modules/doorman/DoormanModule.cpp b/src/modules/doorman/DoormanModule.cpp index bc932d182..8913f03a8 100644 --- a/src/modules/doorman/DoormanModule.cpp +++ b/src/modules/doorman/DoormanModule.cpp @@ -265,16 +265,32 @@ void DoormanModule::add_close_door_schedules(odb::result &sched auto timeframes = door_open_timeframes[door_name]; auto inverse_schedules = create_inverse_schedules(timeframes); for (const auto &schedule : inverse_schedules) { + INFO("Adding inverse schedule to door " << door_name); door->add_always_close_sched(schedule); } } } std::vector> DoormanModule::create_inverse_schedules(const std::vector &timeframes) { - // TODO: Implement this + std::vector> inverse_schedules; + + if (timeframes.empty()) { + inverse_schedules.push_back(create_24_7_schedule()); + return inverse_schedules; + } + return {}; } +std::shared_ptr DoormanModule::create_24_7_schedule() { + auto schedule = std::make_shared("closed_24_7"); + for (int day = 0; day < 7; ++day) { + Tools::SingleTimeFrame tf(day, 0, 0, 23, 59); + schedule->add_timeframe(tf); + } + return schedule; +} + void DoormanModule::clear_door_schedules() { for (auto &door : doors_) door->clear_schedules(); diff --git a/src/modules/doorman/DoormanModule.hpp b/src/modules/doorman/DoormanModule.hpp index 7241a76fe..aa80e81cc 100644 --- a/src/modules/doorman/DoormanModule.hpp +++ b/src/modules/doorman/DoormanModule.hpp @@ -94,6 +94,12 @@ class DoormanModule : public BaseModule * when the open schedule is not active. */ std::vector> create_inverse_schedules(const std::vector &timeframes); + + /** + * Creates a schedule with 7 timeframes (all days 00:00-23:59). + */ + std::shared_ptr create_24_7_schedule(); + /** * Sets the db_service_ member variable if use_db_schedules_ is true. */ From 90f67fd49cc7bbf4eb57007238892ea62fa89b09 Mon Sep 17 00:00:00 2001 From: joshusooyaa <106642518+joshusooyaa@users.noreply.github.com> Date: Wed, 20 Aug 2025 11:45:56 -0700 Subject: [PATCH 39/57] Group and insert closed full days Groups timeframes by days, then checks if a full day should be closed --- src/modules/doorman/DoormanModule.cpp | 41 +++++++++++++++++---------- src/modules/doorman/DoormanModule.hpp | 9 ++++-- 2 files changed, 33 insertions(+), 17 deletions(-) diff --git a/src/modules/doorman/DoormanModule.cpp b/src/modules/doorman/DoormanModule.cpp index 8913f03a8..ba246fe30 100644 --- a/src/modules/doorman/DoormanModule.cpp +++ b/src/modules/doorman/DoormanModule.cpp @@ -220,7 +220,7 @@ void DoormanModule::process_db_schedules() { clear_door_schedules(); add_open_door_schedules(schedules, door_open_timeframes); - add_close_door_schedules(schedules, door_open_timeframes); + add_close_door_schedules(door_open_timeframes); t.commit(); } catch (const std::exception &e) { @@ -257,40 +257,51 @@ void DoormanModule::add_open_door_schedules(odb::result &schedu } } -void DoormanModule::add_close_door_schedules(odb::result &schedules, - std::map> &door_open_timeframes) +void DoormanModule::add_close_door_schedules(std::map> &door_open_timeframes) { for (const auto &door : doors_) { std::string door_name = door->name(); auto timeframes = door_open_timeframes[door_name]; - auto inverse_schedules = create_inverse_schedules(timeframes); - for (const auto &schedule : inverse_schedules) { - INFO("Adding inverse schedule to door " << door_name); - door->add_always_close_sched(schedule); + if (auto inverse_schedule = create_inverse_schedule(timeframes)) { + door->add_always_close_sched(inverse_schedule); } } } -std::vector> DoormanModule::create_inverse_schedules(const std::vector &timeframes) { - std::vector> inverse_schedules; +std::shared_ptr DoormanModule::create_inverse_schedule(const std::vector &timeframes) { + if (timeframes.empty()) return create_24_7_schedule(); + + auto inverse_schedule = std::make_shared("inverse_schedule"); + + std::map> timeframes_grouped_by_day; + for (const auto &tf : timeframes) { + timeframes_grouped_by_day[tf.day].push_back(tf); + } - if (timeframes.empty()) { - inverse_schedules.push_back(create_24_7_schedule()); - return inverse_schedules; + for (int day = 0; day < 7; ++day) { + auto tf_map = timeframes_grouped_by_day.find(day); + if (tf_map == timeframes_grouped_by_day.end() || tf_map->second.empty()) { + add_timeframe_to_schedule(inverse_schedule, day, 0, 0, 23, 59); + continue; + } } - return {}; + return inverse_schedule; } std::shared_ptr DoormanModule::create_24_7_schedule() { auto schedule = std::make_shared("closed_24_7"); for (int day = 0; day < 7; ++day) { - Tools::SingleTimeFrame tf(day, 0, 0, 23, 59); - schedule->add_timeframe(tf); + add_timeframe_to_schedule(schedule, day, 0, 0, 23, 59); } return schedule; } +void DoormanModule::add_timeframe_to_schedule(std::shared_ptr schedule, int day, int start_hour, int start_min, int end_hour, int end_min) { + Tools::SingleTimeFrame tf(day, start_hour, start_min, end_hour, end_min); + schedule->add_timeframe(tf); +} + void DoormanModule::clear_door_schedules() { for (auto &door : doors_) door->clear_schedules(); diff --git a/src/modules/doorman/DoormanModule.hpp b/src/modules/doorman/DoormanModule.hpp index aa80e81cc..ea29919e4 100644 --- a/src/modules/doorman/DoormanModule.hpp +++ b/src/modules/doorman/DoormanModule.hpp @@ -87,19 +87,24 @@ class DoormanModule : public BaseModule /** * Adds close schedules to the doors from the database. */ - void add_close_door_schedules(odb::result &schedules, std::map> &door_open_timeframes); + void add_close_door_schedules(std::map> &door_open_timeframes); /** * Creates an inverse schedule based off of the open timeframes to close the door * when the open schedule is not active. */ - std::vector> create_inverse_schedules(const std::vector &timeframes); + std::shared_ptr create_inverse_schedule(const std::vector &timeframes); /** * Creates a schedule with 7 timeframes (all days 00:00-23:59). */ std::shared_ptr create_24_7_schedule(); + /** + * Adds a timeframe to a schedule. + */ + void add_timeframe_to_schedule(std::shared_ptr schedule, int day, int start_hour, int start_min, int end_hour, int end_min); + /** * Sets the db_service_ member variable if use_db_schedules_ is true. */ From 5ec1a778d93ad3e5b87eb509a886d7b79cf5e571 Mon Sep 17 00:00:00 2001 From: joshusooyaa <106642518+joshusooyaa@users.noreply.github.com> Date: Wed, 20 Aug 2025 12:01:29 -0700 Subject: [PATCH 40/57] Sort tf vector for future creation of closed schedule --- src/modules/doorman/DoormanModule.cpp | 13 +++++++++++++ src/modules/doorman/DoormanModule.hpp | 6 ++++++ 2 files changed, 19 insertions(+) diff --git a/src/modules/doorman/DoormanModule.cpp b/src/modules/doorman/DoormanModule.cpp index ba246fe30..77868cec5 100644 --- a/src/modules/doorman/DoormanModule.cpp +++ b/src/modules/doorman/DoormanModule.cpp @@ -284,6 +284,9 @@ std::shared_ptr DoormanModule::create_inverse_schedule add_timeframe_to_schedule(inverse_schedule, day, 0, 0, 23, 59); continue; } + + auto &open_tfs_for_day = tf_map->second; + sort_tf_vec_by_time(tfs_for_day); } return inverse_schedule; @@ -302,6 +305,16 @@ void DoormanModule::add_timeframe_to_schedule(std::shared_ptradd_timeframe(tf); } +void DoormanModule::sort_tf_vec_by_time(std::vector &timeframes) { + std::sort(timeframes.begin(), timeframes.end(), + [](const Tools::SingleTimeFrame &a, const Tools::SingleTimeFrame &b) { + if (a.start_hour != b.start_hour) { + return a.start_hour < b.start_hour; + } + return a.start_min < b.start_min; + }); +} + void DoormanModule::clear_door_schedules() { for (auto &door : doors_) door->clear_schedules(); diff --git a/src/modules/doorman/DoormanModule.hpp b/src/modules/doorman/DoormanModule.hpp index ea29919e4..c4a07793b 100644 --- a/src/modules/doorman/DoormanModule.hpp +++ b/src/modules/doorman/DoormanModule.hpp @@ -105,6 +105,12 @@ class DoormanModule : public BaseModule */ void add_timeframe_to_schedule(std::shared_ptr schedule, int day, int start_hour, int start_min, int end_hour, int end_min); + /** + * Sorts a vector of timeframes by start time hh:mm. + * Does not sort by day. + */ + void sort_tf_vec_by_time(std::vector &timeframes); + /** * Sets the db_service_ member variable if use_db_schedules_ is true. */ From 394d31f79dba420e97038fb993645f84bda2b7e8 Mon Sep 17 00:00:00 2001 From: joshusooyaa <106642518+joshusooyaa@users.noreply.github.com> Date: Wed, 20 Aug 2025 12:01:59 -0700 Subject: [PATCH 41/57] Improve tf var name clarity --- src/modules/doorman/DoormanModule.cpp | 24 ++++++++++++------------ src/modules/doorman/DoormanModule.hpp | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/modules/doorman/DoormanModule.cpp b/src/modules/doorman/DoormanModule.cpp index 77868cec5..374f6101a 100644 --- a/src/modules/doorman/DoormanModule.cpp +++ b/src/modules/doorman/DoormanModule.cpp @@ -262,38 +262,38 @@ void DoormanModule::add_close_door_schedules(std::mapname(); auto timeframes = door_open_timeframes[door_name]; - if (auto inverse_schedule = create_inverse_schedule(timeframes)) { + if (auto inverse_schedule = create_closed_schedule(timeframes)) { door->add_always_close_sched(inverse_schedule); } } } -std::shared_ptr DoormanModule::create_inverse_schedule(const std::vector &timeframes) { - if (timeframes.empty()) return create_24_7_schedule(); +std::shared_ptr DoormanModule::create_closed_schedule(const std::vector &open_timeframes) { + if (open_timeframes.empty()) return create_24_7_schedule(); - auto inverse_schedule = std::make_shared("inverse_schedule"); + auto inverse_schedule = std::make_shared("closed_schedule"); - std::map> timeframes_grouped_by_day; - for (const auto &tf : timeframes) { - timeframes_grouped_by_day[tf.day].push_back(tf); + std::map> open_timeframes_grouped_by_day; + for (const auto &tf : open_timeframes) { + open_timeframes_grouped_by_day[tf.day].push_back(tf); } for (int day = 0; day < 7; ++day) { - auto tf_map = timeframes_grouped_by_day.find(day); - if (tf_map == timeframes_grouped_by_day.end() || tf_map->second.empty()) { + auto open_tf_map = open_timeframes_grouped_by_day.find(day); + if (open_tf_map == open_timeframes_grouped_by_day.end() || open_tf_map->second.empty()) { add_timeframe_to_schedule(inverse_schedule, day, 0, 0, 23, 59); continue; } - auto &open_tfs_for_day = tf_map->second; - sort_tf_vec_by_time(tfs_for_day); + auto &open_tfs_for_day = open_tf_map->second; + sort_tf_vec_by_time(open_tfs_for_day); } return inverse_schedule; } std::shared_ptr DoormanModule::create_24_7_schedule() { - auto schedule = std::make_shared("closed_24_7"); + auto schedule = std::make_shared("closed_schedule"); for (int day = 0; day < 7; ++day) { add_timeframe_to_schedule(schedule, day, 0, 0, 23, 59); } diff --git a/src/modules/doorman/DoormanModule.hpp b/src/modules/doorman/DoormanModule.hpp index c4a07793b..105d6b275 100644 --- a/src/modules/doorman/DoormanModule.hpp +++ b/src/modules/doorman/DoormanModule.hpp @@ -93,7 +93,7 @@ class DoormanModule : public BaseModule * Creates an inverse schedule based off of the open timeframes to close the door * when the open schedule is not active. */ - std::shared_ptr create_inverse_schedule(const std::vector &timeframes); + std::shared_ptr create_closed_schedule(const std::vector &open_timeframes); /** * Creates a schedule with 7 timeframes (all days 00:00-23:59). From e7498a22e4e08276839ebc1be30f8f7f9933ed43 Mon Sep 17 00:00:00 2001 From: joshusooyaa <106642518+joshusooyaa@users.noreply.github.com> Date: Wed, 20 Aug 2025 13:18:23 -0700 Subject: [PATCH 42/57] Add closed timeframes to door schedule --- src/modules/doorman/DoormanModule.cpp | 38 ++++++++++++++++++++++----- src/modules/doorman/DoormanModule.hpp | 6 +++++ 2 files changed, 37 insertions(+), 7 deletions(-) diff --git a/src/modules/doorman/DoormanModule.cpp b/src/modules/doorman/DoormanModule.cpp index 374f6101a..6460d729e 100644 --- a/src/modules/doorman/DoormanModule.cpp +++ b/src/modules/doorman/DoormanModule.cpp @@ -262,8 +262,8 @@ void DoormanModule::add_close_door_schedules(std::mapname(); auto timeframes = door_open_timeframes[door_name]; - if (auto inverse_schedule = create_closed_schedule(timeframes)) { - door->add_always_close_sched(inverse_schedule); + if (auto closed_schedule = create_closed_schedule(timeframes)) { + door->add_always_close_sched(closed_schedule); } } } @@ -271,7 +271,7 @@ void DoormanModule::add_close_door_schedules(std::map DoormanModule::create_closed_schedule(const std::vector &open_timeframes) { if (open_timeframes.empty()) return create_24_7_schedule(); - auto inverse_schedule = std::make_shared("closed_schedule"); + auto closed_schedule = std::make_shared("closed_schedule"); std::map> open_timeframes_grouped_by_day; for (const auto &tf : open_timeframes) { @@ -281,15 +281,16 @@ std::shared_ptr DoormanModule::create_closed_schedule( for (int day = 0; day < 7; ++day) { auto open_tf_map = open_timeframes_grouped_by_day.find(day); if (open_tf_map == open_timeframes_grouped_by_day.end() || open_tf_map->second.empty()) { - add_timeframe_to_schedule(inverse_schedule, day, 0, 0, 23, 59); + add_timeframe_to_schedule(closed_schedule, day, 0, 0, 23, 59); continue; } - auto &open_tfs_for_day = open_tf_map->second; - sort_tf_vec_by_time(open_tfs_for_day); + auto &open_tfs = open_tf_map->second; + sort_tf_vec_by_time(open_tfs); + add_closed_tfs(open_tfs, closed_schedule, day); } - return inverse_schedule; + return closed_schedule; } std::shared_ptr DoormanModule::create_24_7_schedule() { @@ -315,6 +316,29 @@ void DoormanModule::sort_tf_vec_by_time(std::vector &tim }); } +void DoormanModule::add_closed_tfs(std::vector &open_tfs, std::shared_ptr closed_schedule, int day) { + int max_tf_index = open_tfs.size() - 1; + for (int i = 0; i < max_tf_index; ++i) { + const auto ¤t_tf = open_tfs[i]; + const auto &next_tf = open_tfs[i + 1]; + + // Add the beginning of the day timeframe if it exists + if (i == 0 && (current_tf.start_hour > 0 || current_tf.start_min > 0)) { + add_timeframe_to_schedule(closed_schedule, day, 0, 0, open_tfs[0].start_hour, open_tfs[0].start_min); + } + + // Add the gaps between timeframes + if (i != 0 && i+1 != max_tf_index && (current_tf.end_hour < next_tf.start_hour || current_tf.end_min < next_tf.start_min)) { + add_timeframe_to_schedule(closed_schedule, day, current_tf.end_hour, current_tf.end_min, next_tf.start_hour, next_tf.start_min); + } + + // Add the end of the day timeframe if it exists + if (i+1 == max_tf_index && (next_tf.end_hour < 23 || (next_tf.end_hour == 23 && next_tf.end_min < 59))) { + add_timeframe_to_schedule(closed_schedule, day, next_tf.end_hour, next_tf.end_min, 23, 59); + } + } +} + void DoormanModule::clear_door_schedules() { for (auto &door : doors_) door->clear_schedules(); diff --git a/src/modules/doorman/DoormanModule.hpp b/src/modules/doorman/DoormanModule.hpp index 105d6b275..c6708a9dc 100644 --- a/src/modules/doorman/DoormanModule.hpp +++ b/src/modules/doorman/DoormanModule.hpp @@ -100,6 +100,12 @@ class DoormanModule : public BaseModule */ std::shared_ptr create_24_7_schedule(); + /** + * Adds the beginning of the day timeframe to a schedule. + * Ex: If the open schedule is 08:00-17:00, the closed schedule will be 00:00-08:00. + */ + void add_closed_tfs(std::vector &open_tfs, std::shared_ptr closed_schedule, int day); + /** * Adds a timeframe to a schedule. */ From 8ad4d7ead05745314e89810341fd908fafabbdac Mon Sep 17 00:00:00 2001 From: joshusooyaa <106642518+joshusooyaa@users.noreply.github.com> Date: Wed, 20 Aug 2025 13:18:59 -0700 Subject: [PATCH 43/57] Log door open and closed schedules --- src/modules/doorman/DoormanModule.cpp | 57 +++++++++++++++++++++++++++ src/modules/doorman/DoormanModule.hpp | 7 ++++ 2 files changed, 64 insertions(+) diff --git a/src/modules/doorman/DoormanModule.cpp b/src/modules/doorman/DoormanModule.cpp index 6460d729e..7a708fc0d 100644 --- a/src/modules/doorman/DoormanModule.cpp +++ b/src/modules/doorman/DoormanModule.cpp @@ -263,7 +263,10 @@ void DoormanModule::add_close_door_schedules(std::mapname(); auto timeframes = door_open_timeframes[door_name]; if (auto closed_schedule = create_closed_schedule(timeframes)) { + log_open_and_closed_timeframes(timeframes, closed_schedule, door_name); door->add_always_close_sched(closed_schedule); + } else { + INFO("Door " << door_name << " is always open"); } } } @@ -339,6 +342,60 @@ void DoormanModule::add_closed_tfs(std::vector &open_tfs } } +void DoormanModule::log_open_and_closed_timeframes(const std::vector &open_tfs, + const std::shared_ptr &closed_schedule, + const std::string &door_name) +{ + INFO("=== SCHEDULE SUMMARY FOR DOOR: " << door_name << " ==="); + + INFO("OPEN timeframes (" << open_tfs.size() << " total):"); + for (size_t i = 0; i < open_tfs.size(); ++i) { + const auto& tf = open_tfs[i]; + std::string day_name; + switch(tf.day) { + case 0: day_name = "Sunday"; break; + case 1: day_name = "Monday"; break; + case 2: day_name = "Tuesday"; break; + case 3: day_name = "Wednesday"; break; + case 4: day_name = "Thursday"; break; + case 5: day_name = "Friday"; break; + case 6: day_name = "Saturday"; break; + default: day_name = "Unknown"; break; + } + + INFO(" Open " << (i + 1) << ": " << day_name << " " + << std::setfill('0') << std::setw(2) << tf.start_hour << ":" + << std::setfill('0') << std::setw(2) << tf.start_min << " to " + << std::setfill('0') << std::setw(2) << tf.end_hour << ":" + << std::setfill('0') << std::setw(2) << tf.end_min); + } + + INFO("CLOSED timeframes (" << closed_schedule->timeframes().size() << " total):"); + auto created_timeframes = closed_schedule->timeframes(); + for (size_t i = 0; i < created_timeframes.size(); ++i) { + const auto& tf = created_timeframes[i]; + std::string day_name; + switch(tf.day) { + case 0: day_name = "Sunday"; break; + case 1: day_name = "Monday"; break; + case 2: day_name = "Tuesday"; break; + case 3: day_name = "Wednesday"; break; + case 4: day_name = "Thursday"; break; + case 5: day_name = "Friday"; break; + case 6: day_name = "Saturday"; break; + default: day_name = "Unknown"; break; + } + + INFO(" Closed " << (i + 1) << ": " << day_name << " " + << std::setfill('0') << std::setw(2) << tf.start_hour << ":" + << std::setfill('0') << std::setw(2) << tf.start_min << " to " + << std::setfill('0') << std::setw(2) << tf.end_hour << ":" + << std::setfill('0') << std::setw(2) << tf.end_min); + } + + INFO("=== END SCHEDULE SUMMARY ==="); +} + void DoormanModule::clear_door_schedules() { for (auto &door : doors_) door->clear_schedules(); diff --git a/src/modules/doorman/DoormanModule.hpp b/src/modules/doorman/DoormanModule.hpp index c6708a9dc..234b42842 100644 --- a/src/modules/doorman/DoormanModule.hpp +++ b/src/modules/doorman/DoormanModule.hpp @@ -106,6 +106,13 @@ class DoormanModule : public BaseModule */ void add_closed_tfs(std::vector &open_tfs, std::shared_ptr closed_schedule, int day); + /** + * Logs the open and closed timeframes for a door. + */ + void log_open_and_closed_timeframes(const std::vector &open_tfs, + const std::shared_ptr &closed_schedule, + const std::string &door_name); + /** * Adds a timeframe to a schedule. */ From cdf14412e73e9bc5aa4b701f44a3e721c50c88be Mon Sep 17 00:00:00 2001 From: joshusooyaa <106642518+joshusooyaa@users.noreply.github.com> Date: Wed, 20 Aug 2025 13:33:39 -0700 Subject: [PATCH 44/57] Refresh schedules every 60 seconds --- src/modules/doorman/DoormanModule.cpp | 13 +++++++++++++ src/modules/doorman/DoormanModule.hpp | 12 ++++++++++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/modules/doorman/DoormanModule.cpp b/src/modules/doorman/DoormanModule.cpp index 7a708fc0d..413d11c4f 100644 --- a/src/modules/doorman/DoormanModule.cpp +++ b/src/modules/doorman/DoormanModule.cpp @@ -30,10 +30,13 @@ #include "tools/ScheduleMapping.hpp" #include "core/auth/Door.hpp" #include "core/auth/Door_odb.h" +#include using namespace Leosac::Module::Doorman; using namespace Leosac::Auth; +const std::chrono::seconds DoormanModule::SCHEDULE_REFRESH_INTERVAL; + DoormanModule::DoormanModule(zmqpp::context &ctx, zmqpp::socket *pipe, const boost::property_tree::ptree &cfg, CoreUtilsPtr utils) @@ -411,9 +414,19 @@ bool DoormanModule::is_door_schedule(const Tools::ScheduleMappingPtr &mapping) { return (has_doors || has_zones) && !has_users && !has_groups && !has_credentials; } +void DoormanModule::refresh_db_schedules(std::chrono::system_clock::time_point now) { + if (!use_db_schedules_ || !db_service_) return; + + if ((now - last_schedule_refresh_) >= SCHEDULE_REFRESH_INTERVAL) { + process_db_schedules(); + last_schedule_refresh_ = now; + } +} + void DoormanModule::update() { auto now = std::chrono::system_clock::now(); + refresh_db_schedules(now); for (auto &&doorman : doormen_) { diff --git a/src/modules/doorman/DoormanModule.hpp b/src/modules/doorman/DoormanModule.hpp index 234b42842..080d4aaea 100644 --- a/src/modules/doorman/DoormanModule.hpp +++ b/src/modules/doorman/DoormanModule.hpp @@ -26,8 +26,6 @@ #include "tools/db/db_fwd.hpp" #include "tools/Schedule.hpp" #include -#include -#include #include namespace Leosac @@ -134,6 +132,12 @@ class DoormanModule : public BaseModule */ void clear_door_schedules(); + /** + * Re-checks the database for updated/deleted schedules + * Removes all schedules and re-adds them. + */ + void refresh_db_schedules(std::chrono::system_clock::time_point now); + /** * Checks if a schedule mapping contains only doors */ @@ -154,6 +158,10 @@ class DoormanModule : public BaseModule bool use_db_schedules_; DBServicePtr db_service_; + + std::chrono::system_clock::time_point last_schedule_refresh_; + + static constexpr std::chrono::seconds SCHEDULE_REFRESH_INTERVAL{60}; }; } } From 14e711843e12b2ea7a4e177e4e65b9589244db73 Mon Sep 17 00:00:00 2001 From: joshusooyaa <106642518+joshusooyaa@users.noreply.github.com> Date: Wed, 20 Aug 2025 14:00:01 -0700 Subject: [PATCH 45/57] Fix closed tfs not adding correctly --- src/modules/doorman/DoormanModule.cpp | 30 +++++++++++++-------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/src/modules/doorman/DoormanModule.cpp b/src/modules/doorman/DoormanModule.cpp index 413d11c4f..4629af2ed 100644 --- a/src/modules/doorman/DoormanModule.cpp +++ b/src/modules/doorman/DoormanModule.cpp @@ -323,26 +323,24 @@ void DoormanModule::sort_tf_vec_by_time(std::vector &tim } void DoormanModule::add_closed_tfs(std::vector &open_tfs, std::shared_ptr closed_schedule, int day) { + auto add_tf = [&](int start_hour, int start_min, int end_hour, int end_min) { + if (start_hour < end_hour || (start_hour == end_hour && start_min < end_min)) { + add_timeframe_to_schedule(closed_schedule, day, start_hour, start_min, end_hour, end_min); + } + }; + + auto &first_tf = open_tfs[0]; + add_tf(0, 0, first_tf.start_hour, first_tf.start_min); + int max_tf_index = open_tfs.size() - 1; - for (int i = 0; i < max_tf_index; ++i) { + for (int i = 1; i < max_tf_index; ++i) { const auto ¤t_tf = open_tfs[i]; const auto &next_tf = open_tfs[i + 1]; - - // Add the beginning of the day timeframe if it exists - if (i == 0 && (current_tf.start_hour > 0 || current_tf.start_min > 0)) { - add_timeframe_to_schedule(closed_schedule, day, 0, 0, open_tfs[0].start_hour, open_tfs[0].start_min); - } - - // Add the gaps between timeframes - if (i != 0 && i+1 != max_tf_index && (current_tf.end_hour < next_tf.start_hour || current_tf.end_min < next_tf.start_min)) { - add_timeframe_to_schedule(closed_schedule, day, current_tf.end_hour, current_tf.end_min, next_tf.start_hour, next_tf.start_min); - } - - // Add the end of the day timeframe if it exists - if (i+1 == max_tf_index && (next_tf.end_hour < 23 || (next_tf.end_hour == 23 && next_tf.end_min < 59))) { - add_timeframe_to_schedule(closed_schedule, day, next_tf.end_hour, next_tf.end_min, 23, 59); - } + add_tf(current_tf.end_hour, current_tf.end_min, next_tf.start_hour, next_tf.start_min); } + + auto &last_tf = open_tfs.back(); + add_tf(last_tf.end_hour, last_tf.end_min, 23, 59); } void DoormanModule::log_open_and_closed_timeframes(const std::vector &open_tfs, From 697fec145e788927d99f85d798ea7d4be787c335 Mon Sep 17 00:00:00 2001 From: joshusooyaa <106642518+joshusooyaa@users.noreply.github.com> Date: Wed, 20 Aug 2025 14:42:42 -0700 Subject: [PATCH 46/57] Adjust closed timeframe start/end times Prevents closed start/end being the same as open start/end of open schedules. Causes a 1 minute conflict without this. --- src/modules/doorman/DoormanModule.cpp | 21 +++++++++++++++++++++ src/modules/doorman/DoormanModule.hpp | 7 +++++++ 2 files changed, 28 insertions(+) diff --git a/src/modules/doorman/DoormanModule.cpp b/src/modules/doorman/DoormanModule.cpp index 4629af2ed..559b054ff 100644 --- a/src/modules/doorman/DoormanModule.cpp +++ b/src/modules/doorman/DoormanModule.cpp @@ -325,6 +325,7 @@ void DoormanModule::sort_tf_vec_by_time(std::vector &tim void DoormanModule::add_closed_tfs(std::vector &open_tfs, std::shared_ptr closed_schedule, int day) { auto add_tf = [&](int start_hour, int start_min, int end_hour, int end_min) { if (start_hour < end_hour || (start_hour == end_hour && start_min < end_min)) { + adjust_tf_times(start_hour, start_min, end_hour, end_min); add_timeframe_to_schedule(closed_schedule, day, start_hour, start_min, end_hour, end_min); } }; @@ -337,12 +338,32 @@ void DoormanModule::add_closed_tfs(std::vector &open_tfs const auto ¤t_tf = open_tfs[i]; const auto &next_tf = open_tfs[i + 1]; add_tf(current_tf.end_hour, current_tf.end_min, next_tf.start_hour, next_tf.start_min); + // 14:00, 18:30 } auto &last_tf = open_tfs.back(); add_tf(last_tf.end_hour, last_tf.end_min, 23, 59); } +void DoormanModule::adjust_tf_times(int &start_hour, int &start_min, int &end_hour, int &end_min) { + if ((start_hour == 0 && start_min == 0) || (end_hour == 0 && end_min == 0)) {} + else { + if(start_min == 59) { + start_hour++; + start_min = 0; + } else { + start_min++; + } + } + + if (end_min == 0) { + end_min = 59; + end_hour--; + } else if (end_hour != 23) { + end_min--; + } +} + void DoormanModule::log_open_and_closed_timeframes(const std::vector &open_tfs, const std::shared_ptr &closed_schedule, const std::string &door_name) diff --git a/src/modules/doorman/DoormanModule.hpp b/src/modules/doorman/DoormanModule.hpp index 080d4aaea..9a1f72568 100644 --- a/src/modules/doorman/DoormanModule.hpp +++ b/src/modules/doorman/DoormanModule.hpp @@ -122,6 +122,13 @@ class DoormanModule : public BaseModule */ void sort_tf_vec_by_time(std::vector &timeframes); + /** + * Adds and subtracts 1 minute from the start and end times of a timeframe. + * Prevents open and close start/end times from being the same. + * Ex: Open: 14:00-18:30 -> Closed: 14:01-18:29 + */ + void adjust_tf_times(int &start_hour, int &start_min, int &end_hour, int &end_min); + /** * Sets the db_service_ member variable if use_db_schedules_ is true. */ From ad478c8e45b4bf740449a1448c2785c5dddd2ab9 Mon Sep 17 00:00:00 2001 From: joshusooyaa <106642518+joshusooyaa@users.noreply.github.com> Date: Wed, 20 Aug 2025 17:08:22 -0700 Subject: [PATCH 47/57] Set logging level through config --- src/core/kernel.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/core/kernel.cpp b/src/core/kernel.cpp index cec2caa9a..dbb09f349 100644 --- a/src/core/kernel.cpp +++ b/src/core/kernel.cpp @@ -371,6 +371,7 @@ void Kernel::configure_logger() bool use_database = false; std::string syslog_min_level = "WARNING"; std::shared_ptr console; + std::string console_level = "DEBUG"; // Drop existing logger, if any. (This is for the case of a "in process" restart) spdlog::drop("syslog"); @@ -382,6 +383,7 @@ void Kernel::configure_logger() use_syslog = log_cfg_node->get("enable_syslog", true); use_database = log_cfg_node->get("enable_database", false); syslog_min_level = log_cfg_node->get("min_syslog", "WARNING"); + console_level = log_cfg_node->get("console_level", "DEBUG"); } if (use_syslog) { @@ -399,7 +401,9 @@ void Kernel::configure_logger() else console = spdlog::create( "console", {std::make_shared()}); - console->set_level(spdlog::level::debug); + + console->set_level(static_cast( + LogHelper::log_level_from_string(console_level))); } const ModuleManager &Kernel::module_manager() const From 48dcbc5547a69bbbb87081b08374b8391037e7f3 Mon Sep 17 00:00:00 2001 From: joshusooyaa <106642518+joshusooyaa@users.noreply.github.com> Date: Wed, 20 Aug 2025 17:16:57 -0700 Subject: [PATCH 48/57] Toggle logging through config for websockapi --- src/modules/websock-api/WSServer.cpp | 11 +++++++---- src/modules/websock-api/WSServer.hpp | 7 ++++++- src/modules/websock-api/WebSockAPI.cpp | 3 ++- src/modules/websock-api/WebSockAPI.hpp | 5 +++++ 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/modules/websock-api/WSServer.cpp b/src/modules/websock-api/WSServer.cpp index 79b4aa99b..e2e1474ff 100644 --- a/src/modules/websock-api/WSServer.cpp +++ b/src/modules/websock-api/WSServer.cpp @@ -75,10 +75,11 @@ using namespace Leosac::Module::WebSockAPI; using json = nlohmann::json; -WSServer::WSServer(WebSockAPIModule &module, DBPtr database) +WSServer::WSServer(WebSockAPIModule &module, DBPtr database, bool enable_logging) : auth_(*this) , dbsrv_(std::make_shared(database)) , module_(module) + , enable_logging_(enable_logging) { ASSERT_LOG(database, "No database object passed into WSServer."); using websocketpp::lib::placeholders::_1; @@ -89,9 +90,11 @@ WSServer::WSServer(WebSockAPIModule &module, DBPtr database) srv_.set_close_handler(std::bind(&WSServer::on_close, this, _1)); srv_.set_message_handler(std::bind(&WSServer::on_message, this, _1, _2)); srv_.set_reuse_addr(true); - // clear all logs. - // srv_.clear_access_channels(websocketpp::log::alevel::all); - + + if (!enable_logging_) { + srv_.clear_access_channels(websocketpp::log::alevel::all); + srv_.clear_error_channels(websocketpp::log::elevel::all); + } // Register internal handlers, ie handler that are managed by the Websocket // module itself. diff --git a/src/modules/websock-api/WSServer.hpp b/src/modules/websock-api/WSServer.hpp index f9b06a7e1..1f3d1de6e 100644 --- a/src/modules/websock-api/WSServer.hpp +++ b/src/modules/websock-api/WSServer.hpp @@ -65,7 +65,7 @@ class WSServer * @param database A (non-null) pointer to the * database. */ - WSServer(WebSockAPIModule &module, DBPtr database); + WSServer(WebSockAPIModule &module, DBPtr database, bool enable_logging = true); ~WSServer(); using Server = websocketpp::server; @@ -257,6 +257,11 @@ class WSServer */ WebSockAPIModule &module_; + /** + * WebSocket logging configuration + */ + bool enable_logging_; + /** * Work used to keep the io_service alive while someone * has a reference to (WS) Service object. diff --git a/src/modules/websock-api/WebSockAPI.cpp b/src/modules/websock-api/WebSockAPI.cpp index ace9ef7fb..205d5051a 100644 --- a/src/modules/websock-api/WebSockAPI.cpp +++ b/src/modules/websock-api/WebSockAPI.cpp @@ -36,6 +36,7 @@ WebSockAPIModule::WebSockAPIModule(zmqpp::context &ctx, zmqpp::socket *pipe, { port_ = cfg.get("module_config.port", 8976); interface_ = cfg.get("module_config.interface", "127.0.0.1"); + enable_websocket_logging_ = cfg.get("module_config.enable_logging", true); auto endpoint_colorized = Colorize::green( Colorize::underline(fmt::format("{}:{}", interface_, port_))); @@ -45,7 +46,7 @@ WebSockAPIModule::WebSockAPIModule(zmqpp::context &ctx, zmqpp::socket *pipe, void WebSockAPIModule::run() { - wssrv_ = std::make_unique(*this, core_utils()->database()); + wssrv_ = std::make_unique(*this, core_utils()->database(), enable_websocket_logging_); std::thread thread(std::bind(&WSServer::run, wssrv_.get(), interface_, port_)); while (is_running_) diff --git a/src/modules/websock-api/WebSockAPI.hpp b/src/modules/websock-api/WebSockAPI.hpp index ef054800a..088fa9c11 100644 --- a/src/modules/websock-api/WebSockAPI.hpp +++ b/src/modules/websock-api/WebSockAPI.hpp @@ -70,6 +70,11 @@ class WebSockAPIModule : public BaseModule */ std::string interface_; + /** + * WebSocket logging configuration + */ + bool enable_websocket_logging_; + /** * Our websocket server object. */ From 43f9ed595902223ef10924df1d97afe15f907edd Mon Sep 17 00:00:00 2001 From: joshusooyaa <106642518+joshusooyaa@users.noreply.github.com> Date: Wed, 20 Aug 2025 17:20:59 -0700 Subject: [PATCH 49/57] Fix bits threshold to greater/less than or equal Config states bits_x_threshold checks less than or equal to. While the code was only checking less than (and vise versa). --- src/modules/auth/auth-db/AuthDBInstance.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/auth/auth-db/AuthDBInstance.cpp b/src/modules/auth/auth-db/AuthDBInstance.cpp index 13b8efe89..369145030 100644 --- a/src/modules/auth/auth-db/AuthDBInstance.cpp +++ b/src/modules/auth/auth-db/AuthDBInstance.cpp @@ -377,11 +377,11 @@ void AuthDBInstance::log_credentials(Cred::ICredentialPtr &credentials) { } bool AuthDBInstance::is_noise(const int nb_bits) const { - if (bits_low_threshold_ != -1 && nb_bits < bits_low_threshold_) { + if (bits_low_threshold_ != -1 && nb_bits <= bits_low_threshold_) { return true; } - if (bits_high_threshold_ != -1 && nb_bits > bits_high_threshold_) { + if (bits_high_threshold_ != -1 && nb_bits >= bits_high_threshold_) { return true; } From eda9e166a2cfb98693f0c55497f51b7f74526f85 Mon Sep 17 00:00:00 2001 From: joshusooyaa <106642518+joshusooyaa@users.noreply.github.com> Date: Mon, 25 Aug 2025 05:50:59 -0700 Subject: [PATCH 50/57] Fix inverse schedule logic Was missing the first two schedule comparison --- src/modules/doorman/DoormanModule.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/modules/doorman/DoormanModule.cpp b/src/modules/doorman/DoormanModule.cpp index 559b054ff..79dde7544 100644 --- a/src/modules/doorman/DoormanModule.cpp +++ b/src/modules/doorman/DoormanModule.cpp @@ -334,11 +334,10 @@ void DoormanModule::add_closed_tfs(std::vector &open_tfs add_tf(0, 0, first_tf.start_hour, first_tf.start_min); int max_tf_index = open_tfs.size() - 1; - for (int i = 1; i < max_tf_index; ++i) { + for (int i = 0; i < max_tf_index; ++i) { const auto ¤t_tf = open_tfs[i]; const auto &next_tf = open_tfs[i + 1]; add_tf(current_tf.end_hour, current_tf.end_min, next_tf.start_hour, next_tf.start_min); - // 14:00, 18:30 } auto &last_tf = open_tfs.back(); From 0d2a2ed1938ec526e0114d8fceb32c60c2ce71db Mon Sep 17 00:00:00 2001 From: joshusooyaa <106642518+joshusooyaa@users.noreply.github.com> Date: Tue, 26 Aug 2025 15:59:38 -0700 Subject: [PATCH 51/57] Fix duplicate doorman objects For each instance, an object for every door in the config was created. This makes it so only objects for the doors under the instance are created. --- src/modules/doorman/DoormanInstance.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/modules/doorman/DoormanInstance.cpp b/src/modules/doorman/DoormanInstance.cpp index 45cf33dbf..c8c9ee5e6 100644 --- a/src/modules/doorman/DoormanInstance.cpp +++ b/src/modules/doorman/DoormanInstance.cpp @@ -53,8 +53,17 @@ DoormanInstance::DoormanInstance(DoormanModule &module, zmqpp::context &ctx, for (auto &d : module.doors()) { - auto door = std::make_shared(d, ctx); - doors_.push_back(door); + bool door_in_instance = false; + for (const auto &action : actions_) { + if (action.target_ == d->gpio()->name()) { + door_in_instance = true; + break; + } + } + if (door_in_instance) { + auto door = std::make_shared(d, ctx); + doors_.push_back(door); + } } } From 8df9c6db343faf7f175485e4fd9d08a60dcc38be Mon Sep 17 00:00:00 2001 From: joshusooyaa <106642518+joshusooyaa@users.noreply.github.com> Date: Tue, 26 Aug 2025 16:07:32 -0700 Subject: [PATCH 52/57] Prevent always closed from denying access events Before - always_closed schedules would prevent access to the door even if the user was authorized. I've removed this because this should be configured instead. If the door shouldn't have access to it then this should be configured in the schedule mapping itself for the user(s) that should not be allowed access during that time. Ultimately, there likely is never going to be a door that should be denied access by all users ever. And if there is, then it can be configured so in the schedule mapping to prevent user access during a certain timeframe. --- src/modules/doorman/DoormanDoor.cpp | 11 +++++++++++ src/modules/doorman/DoormanDoor.hpp | 5 +++++ src/modules/doorman/DoormanInstance.cpp | 19 +++++++++++++++++-- src/modules/doorman/DoormanInstance.hpp | 2 +- src/modules/doorman/DoormanModule.cpp | 5 ++++- 5 files changed, 38 insertions(+), 4 deletions(-) diff --git a/src/modules/doorman/DoormanDoor.cpp b/src/modules/doorman/DoormanDoor.cpp index 60a7df1b9..83a089880 100644 --- a/src/modules/doorman/DoormanDoor.cpp +++ b/src/modules/doorman/DoormanDoor.cpp @@ -92,3 +92,14 @@ std::chrono::system_clock::time_point DoormanDoor::contact_lastupdate() const { return contact_lastupdate_; } + +bool DoormanDoor::is_door_override_active() const +{ + auto now = std::chrono::system_clock::now(); + return = now < door_override_until_; +} + +void DoormanDoor::set_door_override_until(std::chrono::system_clock::time_point until) +{ + door_override_until_ = until; +} \ No newline at end of file diff --git a/src/modules/doorman/DoormanDoor.hpp b/src/modules/doorman/DoormanDoor.hpp index 5afc0154d..8d74f049c 100644 --- a/src/modules/doorman/DoormanDoor.hpp +++ b/src/modules/doorman/DoormanDoor.hpp @@ -67,6 +67,9 @@ class DoormanDoor void alarm_forced(const std::string& alarm); + bool is_door_override_active() const; + void set_door_override_until(std::chrono::system_clock::time_point until); + private: Leosac::Auth::AuthTargetPtr door_; @@ -82,6 +85,8 @@ class DoormanDoor std::chrono::system_clock::time_point contact_lastupdate_; std::string alarm_forced_; + + std::chrono::system_clock::time_point door_override_until_; }; } } diff --git a/src/modules/doorman/DoormanInstance.cpp b/src/modules/doorman/DoormanInstance.cpp index c8c9ee5e6..7dee6e369 100644 --- a/src/modules/doorman/DoormanInstance.cpp +++ b/src/modules/doorman/DoormanInstance.cpp @@ -90,6 +90,8 @@ void DoormanInstance::handle_bus_msg() DEBUG("ACTION (target = " << action.target_ << ")"); zmqpp::message msg; + int door_duration = 0; + for (auto &frame : action.cmd_) { // we try to convert argument to int. if it works we send as int64_t, @@ -99,6 +101,9 @@ void DoormanInstance::handle_bus_msg() try { v = std::stoi(frame); + if (msg.parts() == 1 && v > 0) { + door_duration = v; + } } catch (...) { @@ -110,6 +115,17 @@ void DoormanInstance::handle_bus_msg() msg << static_cast(v); DEBUG("would do : " << frame << " to target: " << action.target_); } + + if (door_duration > 0) { + for (auto& door : doors()) { + if (door->door()->gpio()->name() == action.target_) { + auto override_end = std::chrono::system_clock::now() + std::chrono::milliseconds(door_duration); + door->set_door_override_until(override_end); + break; + } + } + } + command_send_recv(action.target_, std::move(msg)); } } @@ -150,8 +166,7 @@ bool DoormanInstance::ignore_action(const DoormanAction &action, return true; auto target = find_target(action.target_); - if (target && (target->is_always_closed(std::chrono::system_clock::now()) || - target->is_always_open(std::chrono::system_clock::now()))) + if (target && target->is_always_open(std::chrono::system_clock::now())) { INFO("Door " << target->name() << " is in immutable state (always open, " "or always closed) so we ignore this " diff --git a/src/modules/doorman/DoormanInstance.hpp b/src/modules/doorman/DoormanInstance.hpp index d587236bb..ee489d9f8 100644 --- a/src/modules/doorman/DoormanInstance.hpp +++ b/src/modules/doorman/DoormanInstance.hpp @@ -98,7 +98,7 @@ class DoormanInstance * There are multiple reason why we might wanna ignore an action: * 1. The expected status (`granted` / `denied`) does not match the received * status. - * 2. The door is in always_open (or alway_closed) mode. + * 2. The door is in always_open mode. */ bool ignore_action(const DoormanAction &action, Auth::AccessStatus status) const; diff --git a/src/modules/doorman/DoormanModule.cpp b/src/modules/doorman/DoormanModule.cpp index 79dde7544..16d63fffb 100644 --- a/src/modules/doorman/DoormanModule.cpp +++ b/src/modules/doorman/DoormanModule.cpp @@ -475,7 +475,10 @@ void DoormanModule::update() door->alarm_forced(""); } } - d->resetToExpectedState(now); + + if (!door->is_door_override_active()) { + d->resetToExpectedState(now); + } } } } From af2fd4f229351c1b79355569f0f3ba7a1668004d Mon Sep 17 00:00:00 2001 From: joshusooyaa <106642518+joshusooyaa@users.noreply.github.com> Date: Tue, 26 Aug 2025 16:08:26 -0700 Subject: [PATCH 53/57] Fix cmake for auth-db --- src/modules/auth/auth-db/CMakeLists.txt | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/modules/auth/auth-db/CMakeLists.txt b/src/modules/auth/auth-db/CMakeLists.txt index 53b532d5d..710e1ae34 100644 --- a/src/modules/auth/auth-db/CMakeLists.txt +++ b/src/modules/auth/auth-db/CMakeLists.txt @@ -14,14 +14,23 @@ set_target_properties(${AUTH-DB_BIN} PROPERTIES COMPILE_FLAGS "${MODULE_COMPILE_FLAGS}" ) -find_library(LEOSAC_DB_LIB leosac_db PATHS /home/pi/leosac/build NO_DEFAULT_PATH) -find_library(LEOSAC_LIB_LIB leosac_lib PATHS /home/pi/leosac/build NO_DEFAULT_PATH) +find_library(LEOSAC_DB_LIB leosac_db PATHS ${CMAKE_BINARY_DIR} NO_DEFAULT_PATH) +find_library(LEOSAC_LIB_LIB leosac_lib PATHS ${CMAKE_BINARY_DIR} NO_DEFAULT_PATH) -target_link_libraries(${AUTH-DB_BIN} - ${Boost_LIBRARIES} - ${LEOSAC_DB_LIB} - ${LEOSAC_LIB_LIB} - ) +if(LEOSAC_DB_LIB AND LEOSAC_LIB_LIB) + target_link_libraries(${AUTH-DB_BIN} + ${Boost_LIBRARIES} + ${LEOSAC_DB_LIB} + ${LEOSAC_LIB_LIB} + ) +else() + target_link_libraries(${AUTH-DB_BIN} + ${Boost_LIBRARIES} + leosac_db + leosac_lib + ) + add_dependencies(${AUTH-DB_BIN} leosac_db leosac_lib) +endif() target_include_directories(${AUTH-DB_BIN} PUBLIC From 0f421146b7936c35996ced5d60f3fcfa821c6059 Mon Sep 17 00:00:00 2001 From: joshusooyaa <106642518+joshusooyaa@users.noreply.github.com> Date: Tue, 26 Aug 2025 16:21:27 -0700 Subject: [PATCH 54/57] Fix return --- src/modules/doorman/DoormanDoor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/doorman/DoormanDoor.cpp b/src/modules/doorman/DoormanDoor.cpp index 83a089880..a70bf6aec 100644 --- a/src/modules/doorman/DoormanDoor.cpp +++ b/src/modules/doorman/DoormanDoor.cpp @@ -96,7 +96,7 @@ std::chrono::system_clock::time_point DoormanDoor::contact_lastupdate() const bool DoormanDoor::is_door_override_active() const { auto now = std::chrono::system_clock::now(); - return = now < door_override_until_; + return now < door_override_until_; } void DoormanDoor::set_door_override_until(std::chrono::system_clock::time_point until) From 6e3ecba801b99156a82b4dc87649e11f29f29748 Mon Sep 17 00:00:00 2001 From: joshusooyaa <106642518+joshusooyaa@users.noreply.github.com> Date: Tue, 26 Aug 2025 16:22:44 -0700 Subject: [PATCH 55/57] Update cmake Related to commit af2fd4f --- cmake/UseODB.cmake | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cmake/UseODB.cmake b/cmake/UseODB.cmake index 288975b12..7f9d2e3ab 100644 --- a/cmake/UseODB.cmake +++ b/cmake/UseODB.cmake @@ -175,8 +175,9 @@ function(odb_compile outvar) list(APPEND ODB_ARGS "-I${dir}") endforeach() - # file(REMOVE_RECURSE "${ODB_COMPILE_OUTPUT_DIR}") # Temporary for debugging and fast make - # file(MAKE_DIRECTORY "${ODB_COMPILE_OUTPUT_DIR}") # Temporary for debugging and fast make + if(NOT EXISTS "${ODB_COMPILE_OUTPUT_DIR}") + file(MAKE_DIRECTORY "${ODB_COMPILE_OUTPUT_DIR}") + endif() foreach(input ${PARAM_FILES}) get_filename_component(fname "${input}" NAME_WE) From 5a788c6f14df8ba08c06e390af00ebaec9d6665c Mon Sep 17 00:00:00 2001 From: joshusooyaa <106642518+joshusooyaa@users.noreply.github.com> Date: Wed, 27 Aug 2025 12:53:56 -0700 Subject: [PATCH 56/57] Fix build dependency order Link leosac_db and lib properly, and set doorman to require them as well since doorman now works with the database --- src/modules/auth/auth-db/CMakeLists.txt | 1 - src/modules/doorman/CMakeLists.txt | 15 +++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/modules/auth/auth-db/CMakeLists.txt b/src/modules/auth/auth-db/CMakeLists.txt index 710e1ae34..5a403e274 100644 --- a/src/modules/auth/auth-db/CMakeLists.txt +++ b/src/modules/auth/auth-db/CMakeLists.txt @@ -29,7 +29,6 @@ else() leosac_db leosac_lib ) - add_dependencies(${AUTH-DB_BIN} leosac_db leosac_lib) endif() target_include_directories(${AUTH-DB_BIN} diff --git a/src/modules/doorman/CMakeLists.txt b/src/modules/doorman/CMakeLists.txt index 764dbf076..59205aa24 100644 --- a/src/modules/doorman/CMakeLists.txt +++ b/src/modules/doorman/CMakeLists.txt @@ -13,6 +13,21 @@ set_target_properties(${DOORMAN_BIN} PROPERTIES COMPILE_FLAGS "${MODULE_COMPILE_FLAGS}" ) +find_library(LEOSAC_DB_LIB leosac_db PATHS ${CMAKE_BINARY_DIR} NO_DEFAULT_PATH) +find_library(LEOSAC_LIB_LIB leosac_lib PATHS ${CMAKE_BINARY_DIR} NO_DEFAULT_PATH) + +if(LEOSAC_DB_LIB AND LEOSAC_LIB_LIB) + target_link_libraries(${DOORMAN_BIN} + ${LEOSAC_DB_LIB} + ${LEOSAC_LIB_LIB} + ) +else() + target_link_libraries(${DOORMAN_BIN} + leosac_db + leosac_lib + ) +endif() + target_include_directories(${DOORMAN_BIN} PUBLIC ${CMAKE_SOURCE_DIR}/src From 1ad1dfeb41a103a3ba10e9aa12e8beb87df9dbef Mon Sep 17 00:00:00 2001 From: joshusooyaa <106642518+joshusooyaa@users.noreply.github.com> Date: Fri, 5 Sep 2025 11:09:07 -0700 Subject: [PATCH 57/57] Merge profile schedules Old code was temporarily only returning the first profile it found. This fixes this by merging all schedules into one. --- src/modules/auth/auth-db/AuthDBInstance.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/modules/auth/auth-db/AuthDBInstance.cpp b/src/modules/auth/auth-db/AuthDBInstance.cpp index c7b55cdcc..d9e867700 100644 --- a/src/modules/auth/auth-db/AuthDBInstance.cpp +++ b/src/modules/auth/auth-db/AuthDBInstance.cpp @@ -28,6 +28,7 @@ #include "core/auth/User_odb.h" #include "core/auth/AuthSourceBuilder.hpp" #include "core/auth/SimpleAccessProfile.hpp" +#include "core/auth/ProfileMerger.hpp" #include "core/credentials/ICredential.hpp" #include "core/credentials/RFIDCard.hpp" #include "core/credentials/PinCode.hpp" @@ -252,10 +253,17 @@ ::Leosac::Auth::IAccessProfilePtr AuthDBInstance::build_profile(UserPtr &user, I if (profiles.empty()) { return nullptr; + } else if (profiles.size() == 1) { + return profiles.at(0); + } else { + ProfileMerger merger; + auto merged_profile = profiles.at(0); + for (size_t i = 1; i < profiles.size(); ++i) { + merged_profile = merger.merge(merged_profile, profiles.at(i)); + } + return merged_profile; } - return profiles.at(0); // Temporary - } catch (const std::exception &e) { WARN("AuthDBInstance::build_profile - Error building profile: " << e.what()); return nullptr;