diff --git a/CMakeLists.txt b/CMakeLists.txt index 99e19ab4f..fc8454eab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -134,9 +134,10 @@ mark_as_advanced( ZM_SYSTEMD ZM_MANPAGE_DEST_PREFIX) +option(BUILD_TEST_SUITE "Build the test suite" 0) option(BUILD_MAN "Build man pages" 1) -set(ZM_RUNDIR "/var/run/zm" CACHE PATH +set(ZM_RUNDIR "/var/run/zm" CACHE PATH "Location of transient process files, default: /var/run/zm") set(ZM_SOCKDIR "/var/run/zm" CACHE PATH "Location of Unix domain socket files, default /var/run/zm") @@ -899,6 +900,16 @@ add_subdirectory(web) add_subdirectory(misc) add_subdirectory(onvif) +if(BUILD_TEST_SUITE) + message("Building unit tests: Yes") + find_package(Catch2 REQUIRED) + + include(CTest) + add_subdirectory(tests) +else() + message("Building unit tests: No (default)") +endif(BUILD_TEST_SUITE) + # Process distro subdirectories if((ZM_TARGET_DISTRO MATCHES "^el") OR (ZM_TARGET_DISTRO MATCHES "^fc")) add_subdirectory(distros/redhat) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c9f11b064..8fa36cf40 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -72,6 +72,11 @@ set(ZM_BIN_SRC_FILES # A fix for cmake recompiling the source files for every target. add_library(zm STATIC ${ZM_BIN_SRC_FILES}) + +target_include_directories(zm + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}) + link_directories(libbcrypt) add_executable(zmc zmc.cpp) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 000000000..ba0e46436 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,30 @@ +# This file is part of the ZoneMinder Project. +# +# This file is free software; as a special exception the author gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + +include(Catch) + +set(TEST_SOURCES + zm_crypt.cpp) + +add_executable(tests main.cpp ${TEST_SOURCES}) + +target_link_libraries(tests + PRIVATE + zm + ${ZM_BIN_LIBS} + bcrypt + Catch2::Catch2) + +target_include_directories(tests + PRIVATE + ${CMAKE_CURRENT_BINARY_DIR}) + +catch_discover_tests(tests) diff --git a/tests/main.cpp b/tests/main.cpp new file mode 100644 index 000000000..1a00d1291 --- /dev/null +++ b/tests/main.cpp @@ -0,0 +1,19 @@ +/* + * This file is part of the ZoneMinder Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#define CATCH_CONFIG_MAIN +#include "catch2/catch.hpp" diff --git a/tests/zm_crypt.cpp b/tests/zm_crypt.cpp new file mode 100644 index 000000000..d3a315ef4 --- /dev/null +++ b/tests/zm_crypt.cpp @@ -0,0 +1,78 @@ +/* + * This file is part of the ZoneMinder Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include "catch2/catch.hpp" + +#include "zm_crypt.h" + +TEST_CASE("JWT validation") { + std::string key = "testsecret"; + + SECTION("Valid token") { + std::string token = + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJab25lTWluZGVyIiwidXNlciI6ImpvaG5kb2UiLCJ0eXBlIjoiYWNjZXNzIiwiaWF0IjoxMjM0fQ.94WPmBAVl_83KCI9B3Jq9sNpoOdi0Hm1dR4sc6MCPUA"; + std::pair result = verifyToken(token, key); + + REQUIRE(result.first == "johndoe"); + REQUIRE(result.second == 1234); + } + + SECTION("Invalid signature") { + std::string token = + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJab25lTWluZGVyIiwidXNlciI6ImpvaG5kb2UiLCJ0eXBlIjoiYWNjZXNzIiwiaWF0IjoxMjM0fQ.DhviT6RkDLmbXh5F9zM4l0VbWNPCuKptF6fORv1lBlA"; + std::pair result = verifyToken(token, key); + + REQUIRE(result.first == ""); + REQUIRE(result.second == 0); + } + + SECTION("Missing user claim") { + std::string token = + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJab25lTWluZGVyIiwidHlwZSI6ImFjY2VzcyIsImlhdCI6MTIzNH0.mfi3ZHnqUAPUh5ECxDIkAM9WW9a8HbKrP73LC3yYJmw"; + std::pair result = verifyToken(token, key); + + REQUIRE(result.first == ""); + REQUIRE(result.second == 0); + } + + SECTION("Missing type claim") { + std::string token = + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJab25lTWluZGVyIiwidXNlciI6ImpvaG5kb2UiLCJpYXQiOjEyMzR9.D4Irs1gHfzO4psRY2xsOdClTg-Sp1kM__mmfNLs7CII"; + std::pair result = verifyToken(token, key); + + REQUIRE(result.first == ""); + REQUIRE(result.second == 0); + } + + SECTION("Wrong type claim") { + std::string token = + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJab25lTWluZGVyIiwidXNlciI6ImpvaG5kb2UiLCJ0eXBlIjoid3JvbmciLCJpYXQiOjEyMzR9.I1Gd50J6mck05vzc_kzjaH4RNjLBaFGpOnie6-PbX28"; + std::pair result = verifyToken(token, key); + + REQUIRE(result.first == ""); + REQUIRE(result.second == 0); + } + + SECTION("Missing iat claim") { + std::string token = + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJab25lTWluZGVyIiwidXNlciI6ImpvaG5kb2UiLCJ0eXBlIjoid3JvbmcifQ.8iUFOUKJAK5vU8JWKm8D0EOEhm1rJoIulCO11O_Tsp0"; + std::pair result = verifyToken(token, key); + + REQUIRE(result.first == ""); + REQUIRE(result.second == 0); + } +}