/* * Copyright (c) 2019 - 2024 Stefan Strobel * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ /** * @file test_integration_auth.cpp * @brief integration test implementation for the authentication functions * @author Stefan Strobel */ #include "test/framework/catch.hpp" extern "C" { #include "shellmatta.h" } #include static uint32_t write_callCnt = 0u; static char write_data[1024]; static uint32_t write_length; static uint32_t privateCallCount; static uint32_t logUserId; static shellmatta_auth_log_event_t logEvent; #define TEST_SHELLMATTA_SETUP shellmatta_retCode_t ret; \ shellmatta_instance_t inst; \ shellmatta_handle_t handle; \ char buffer[1024] = {0}; \ char historyBuffer[1024] = {0}; \ \ shellmatta_doInit( &inst, \ &handle, \ buffer, \ sizeof(buffer), \ historyBuffer, \ sizeof(historyBuffer), \ "shellmatta->", \ NULL, \ writeFct); \ \ write_callCnt = 0u; \ memset(write_data, 0, sizeof(write_data)); \ write_length = 0u; \ privateCallCount = 0u; \ logUserId = 255u; \ logEvent = SHELLMATTA_AUTH_EVENT_NONE; \ \ shellmatta_addCmd(handle, &publicCmd); \ shellmatta_addCmd(handle, &privateCmd); #define TEST_SHELLMATTA_AUTH_SETUP shellmatta_auth_user_t userList[] = { \ {1, false, "shimatta", "12345678"}, \ {2, false, "not_shimatta", "87654321"}, \ {3, true, "root", "rootpw"} \ }; \ \ uint32_t privateCmdPerms[] = {1}; \ shellmatta_auth_perm_t permList[] = { \ {"private", privateCmdPerms, sizeof(privateCmdPerms)/sizeof(privateCmdPerms[0])}, \ {"additional", privateCmdPerms, sizeof(privateCmdPerms)/sizeof(privateCmdPerms[0])} \ }; \ \ shellmatta_auth_init(handle, userList, 3, permList, 2, false, NULL, NULL); static shellmatta_retCode_t writeFct(const char* data, uint32_t length) { write_callCnt ++; while((length > 0) && (write_length < sizeof(write_data))) { write_data[write_length] = *data; data ++; length --; write_length ++; } return SHELLMATTA_OK; } static shellmatta_retCode_t publicCmdFct(shellmatta_handle_t handle, const char *arguments, uint32_t length) { (void) handle; (void) arguments; (void) length; shellmatta_retCode_t ret = SHELLMATTA_OK; return ret; } shellmatta_cmd_t publicCmd = {(char*)"public", (char*)"p", NULL, NULL, publicCmdFct, NULL, NULL}; static shellmatta_retCode_t privateCmdFct(shellmatta_handle_t handle, const char *arguments, uint32_t length) { (void) handle; (void) arguments; (void) length; privateCallCount ++; return SHELLMATTA_OK; } shellmatta_cmd_t privateCmd = {(char*)"private", (char*)"r", NULL, NULL, privateCmdFct, NULL, NULL}; static shellmatta_retCode_t additionalCmdFct(shellmatta_handle_t handle, const char *arguments, uint32_t length) { (void) handle; (void) arguments; (void) length; return SHELLMATTA_OK; } shellmatta_cmd_t additionalCmd = {(char*)"additional", (char*)"a", NULL, NULL, additionalCmdFct, NULL, NULL}; SCENARIO("Check help auth uninitialized") { GIVEN("An initialized shellmatta instance without initialized auth") { TEST_SHELLMATTA_SETUP; WHEN("The help command is called") { ret = shellmatta_processData(handle, (char*)"help\r", 5); CHECK(ret == SHELLMATTA_OK); THEN("The help command prints all commands - without login/logout.") { char *dummyData = (char*) "help\r\n" "help ? help [command] - print help or usage information\r\n" "private r\r\n" "public p\r\n" "\r\n" "shellmatta->"; CHECK(write_length == strlen(dummyData)); REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData)); } } } } SCENARIO("Check auth unauthorized") { GIVEN("An initialized shellmatta instance with initialized auth") { TEST_SHELLMATTA_SETUP; TEST_SHELLMATTA_AUTH_SETUP; WHEN("The help command is called") { ret = shellmatta_processData(handle, (char*)"help\r", 5); CHECK(ret == SHELLMATTA_OK); THEN("The help command prints only public commands.") { char *dummyData = (char*) "help\r\n" "help ? help [command] - print help or usage information\r\n" "login li Login command\r\n" "logout lo Logout command\r\n" "public p\r\n" "\r\n" "shellmatta->"; CHECK(write_length == strlen(dummyData)); REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData)); } } WHEN("A private function is called") { ret = shellmatta_processData(handle, (char*)"private\r", 8); CHECK(ret == SHELLMATTA_OK); THEN("The command is not executed.") { char *dummyData = (char*) "private\r\n" "\r\n" "Command: private not found\r\n" "shellmatta->"; CHECK(privateCallCount == 0u); CHECK(write_length == strlen(dummyData)); REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData)); } } WHEN("Autocomplete is triggered") { ret = shellmatta_processData(handle, (char*)"\t\t", 2); CHECK(ret == SHELLMATTA_OK); THEN("Only public commands are shown.") { char *dummyData = (char*) "\r\n" "help ? login li logout lo public p \r\n" "shellmatta->"; CHECK(write_length == strlen(dummyData)); REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData)); } } } } SCENARIO("Check authorized") { GIVEN("An initialized shellmatta instance with initialized auth") { TEST_SHELLMATTA_SETUP; TEST_SHELLMATTA_AUTH_SETUP; WHEN("The user shimatta is logged in") { ret = shellmatta_auth_login(handle, 1); CHECK(ret == SHELLMATTA_OK); AND_WHEN("The help command is called") { ret = shellmatta_processData(handle, (char*)"help\r", 5); CHECK(ret == SHELLMATTA_OK); THEN("The help command prints all commands.") { char *dummyData = (char*) "help\r\n" "help ? help [command] - print help or usage information\r\n" "login li Login command\r\n" "logout lo Logout command\r\n" "private r\r\n" "public p\r\n" "\r\n" "shimatta@shellmatta->"; CHECK(write_length == strlen(dummyData)); REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData)); AND_THEN("The shimatta user is logged in") { char usernameBuffer[16] = {0}; uint32_t usernameBufferLength = sizeof(usernameBuffer); CHECK(1 == shellmatta_auth_getLoggedInUserId(handle)); ret = shellmatta_auth_getLoggedInUserName(handle, usernameBuffer, &usernameBufferLength); CHECK(ret == SHELLMATTA_OK); CHECK(usernameBufferLength == strlen("shimatta")); REQUIRE_THAT(usernameBuffer, Catch::Matchers::Equals("shimatta")); } } AND_WHEN("The user is logged out using the logout command") { write_length = 0; memset(write_data, 0, sizeof(write_data)); ret = shellmatta_processData(handle, (char*)"logout\r", 7); CHECK(ret == SHELLMATTA_OK); THEN("The Shellmatta prints the logout message") { char *dummyData = (char*) "logout\r\n" "good bye\r\n\r\n" "shellmatta->"; CHECK(write_length == strlen(dummyData)); REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData)); } AND_WHEN("The help command is called") { write_length = 0; memset(write_data, 0, sizeof(write_data)); ret = shellmatta_processData(handle, (char*)"help\r", 5); CHECK(ret == SHELLMATTA_OK); THEN("The help command prints only public commands.") { char *dummyData = (char*) "help\r\n" "help ? help [command] - print help or usage information\r\n" "login li Login command\r\n" "logout lo Logout command\r\n" "public p\r\n" "\r\n" "shellmatta->"; CHECK(write_length == strlen(dummyData)); REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData)); } } } } AND_WHEN("A private function is called") { ret = shellmatta_processData(handle, (char*)"private\r", 8); CHECK(ret == SHELLMATTA_OK); THEN("The command executed.") { char *dummyData = (char*) "private\r\n" "\r\n" "shimatta@shellmatta->"; CHECK(privateCallCount == 1u); CHECK(write_length == strlen(dummyData)); REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData)); } } } WHEN("The user not_shellmatta is logged in") { ret = shellmatta_auth_login(handle, 2); CHECK(ret == SHELLMATTA_OK); AND_WHEN("The help command is called") { ret = shellmatta_processData(handle, (char*)"help\r", 5); CHECK(ret == SHELLMATTA_OK); THEN("The help command prints not all commands.") { char *dummyData = (char*) "help\r\n" "help ? help [command] - print help or usage information\r\n" "login li Login command\r\n" "logout lo Logout command\r\n" "public p\r\n" "\r\n" "not_shimatta@shellmatta->"; CHECK(ret == SHELLMATTA_OK); CHECK(write_length == strlen(dummyData)); REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData)); AND_THEN("The not_shimatta user is logged in") { char usernameBuffer[16] = {0}; uint32_t usernameBufferLength = sizeof(usernameBuffer); CHECK(2 == shellmatta_auth_getLoggedInUserId(handle)); ret = shellmatta_auth_getLoggedInUserName(handle, usernameBuffer, &usernameBufferLength); CHECK(ret == SHELLMATTA_OK); CHECK(usernameBufferLength == strlen("not_shimatta")); REQUIRE_THAT(usernameBuffer, Catch::Matchers::Equals("not_shimatta")); } } } AND_WHEN("A private function is called") { ret = shellmatta_processData(handle, (char*)"private\r", 8); CHECK(ret == SHELLMATTA_OK); THEN("The command is not executed.") { char *dummyData = (char*) "private\r\n" "\r\n" "Command: private not found\r\n" "not_shimatta@shellmatta->"; CHECK(privateCallCount == 0u); CHECK(write_length == strlen(dummyData)); REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData)); } } } WHEN("The user root is logged in") { ret = shellmatta_auth_login(handle, 3); CHECK(ret == SHELLMATTA_OK); AND_WHEN("The help command is called") { ret = shellmatta_processData(handle, (char*)"help\r", 5); CHECK(ret == SHELLMATTA_OK); THEN("The help command prints all commands.") { char *dummyData = (char*) "help\r\n" "help ? help [command] - print help or usage information\r\n" "login li Login command\r\n" "logout lo Logout command\r\n" "private r\r\n" "public p\r\n" "\r\n" "root@shellmatta->"; CHECK(ret == SHELLMATTA_OK); CHECK(write_length == strlen(dummyData)); REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData)); } } } } } SCENARIO("Check login command with privileged user") { GIVEN("An initialized shellmatta instance with initialized auth") { TEST_SHELLMATTA_SETUP; TEST_SHELLMATTA_AUTH_SETUP; WHEN("The user shimatta logs in interactively using the correct credentials") { ret = shellmatta_processData(handle, (char*)"login\r\n" "shimatta\r\n" "12345678\r", 26); CHECK(ret == SHELLMATTA_OK); THEN("The login message is printed - password is hidden") { char *dummyData = (char*) "login\r\n" "enter username:\r\n" "shimatta\r\n" "enter password:\r\n" "\r\n" "login successful\r\n" "\r\n" "shimatta@shellmatta->"; CHECK(write_length == strlen(dummyData)); REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData)); AND_THEN("The shimatta user is logged in") { REQUIRE(1 == shellmatta_auth_getLoggedInUserId(handle)); } } } WHEN("The user shimatta logs in interactively using wrong credentials") { ret = shellmatta_processData(handle, (char*)"login\r\n" "shimatta\r\n" "11111111\r", 26); CHECK(ret == SHELLMATTA_OK); THEN("A warning message is printed") { char *dummyData = (char*) "login\r\n" "enter username:\r\n" "shimatta\r\n" "enter password:\r\n" "\r\n" "username or password is wrong\r\n" "\r\n" "shellmatta->"; CHECK(write_length == strlen(dummyData)); REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData)); AND_THEN("The shimatta user is not logged in") { REQUIRE(0 == shellmatta_auth_getLoggedInUserId(handle)); } } } WHEN("The user shimatta logs in interactively manipulating the input") { ret = shellmatta_processData(handle, (char*)"login\r" "shimg\batta\r" "1h" "\x7f" "2346\033[D5\033[C78\b8\r", 36); CHECK(ret == SHELLMATTA_OK); THEN("The login message is printed - password is hidden") { char *dummyData = (char*) "login\r\n" "enter username:\r\n" "shimg\033[1D\033[K\033[s\033[uatta\r\n" "enter password:\r\n" "\r\n" "login successful\r\n" "\r\n" "shimatta@shellmatta->"; CHECK(write_length == strlen(dummyData)); REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData)); AND_THEN("The shimatta user is logged in") { REQUIRE(1 == shellmatta_auth_getLoggedInUserId(handle)); } } } WHEN("The user shimatta logs in passing the credentials none interactively") { ret = shellmatta_processData(handle, (char*)"login -u shimatta -p 12345678\r", 30); CHECK(ret == SHELLMATTA_OK); THEN("The login message is printed") { char *dummyData = (char*) "login -u shimatta -p 12345678\r\n" "login successful\r\n" "\r\n" "shimatta@shellmatta->"; CHECK(write_length == strlen(dummyData)); REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData)); AND_THEN("The shimatta user is logged in") { REQUIRE(1 == shellmatta_auth_getLoggedInUserId(handle)); } } } WHEN("The user shimatta logs in passing the credentials half interactively") { ret = shellmatta_processData(handle, (char*)"login -u shimatta\r12345678\r", 27); CHECK(ret == SHELLMATTA_OK); THEN("The login message is printed - password is hidden") { char *dummyData = (char*) "login -u shimatta\r\n\r\n" "enter password:\r\n" "\r\n" "login successful\r\n" "\r\n" "shimatta@shellmatta->"; CHECK(write_length == strlen(dummyData)); REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData)); AND_THEN("The shimatta user is logged in") { REQUIRE(1 == shellmatta_auth_getLoggedInUserId(handle)); } } } WHEN("The user shimatta tries to login non interactively without username") { ret = shellmatta_processData(handle, (char*)"login -p 12345678\r", 18); CHECK(ret == SHELLMATTA_OK); THEN("A warning message is printed") { char *dummyData = (char*) "login -p 12345678\r\n" "Missing username\r\n" "\r\n" "shellmatta->"; CHECK(write_length == strlen(dummyData)); REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData)); AND_THEN("The shimatta user is not logged in") { REQUIRE(0 == shellmatta_auth_getLoggedInUserId(handle)); } } } WHEN("The user shimatta tries to login using the wrong options") { ret = shellmatta_processData(handle, (char*)"login -o meow\r", 14); CHECK(ret == SHELLMATTA_OK); THEN("A warning message is printed") { char *dummyData = (char*) "login -o meow\r\n" "Unknown option\r\n" "\r\n" "shellmatta->"; CHECK(write_length == strlen(dummyData)); REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData)); AND_THEN("The shimatta user is not logged in") { REQUIRE(0 == shellmatta_auth_getLoggedInUserId(handle)); } } } } } SCENARIO("Check login command with unprivileged user") { GIVEN("An initialized shellmatta instance with initialized auth") { TEST_SHELLMATTA_SETUP; TEST_SHELLMATTA_AUTH_SETUP; WHEN("The user not_shimatta logs in interactively using the correct credentials") { ret = shellmatta_processData(handle, (char*)"login\r" "not_shimatta\r" "87654321\r", 28); CHECK(ret == SHELLMATTA_OK); THEN("The login message is printed - password is hidden") { char *dummyData = (char*) "login\r\n" "enter username:\r\n" "not_shimatta\r\n" "enter password:\r\n" "\r\n" "login successful\r\n" "\r\n" "not_shimatta@shellmatta->"; CHECK(write_length == strlen(dummyData)); REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData)); AND_THEN("The not_shimatta user is logged in") { REQUIRE(2 == shellmatta_auth_getLoggedInUserId(handle)); } } } } } SCENARIO("Check adding commands after the authentication is initialized") { GIVEN("An initialized shellmatta instance with initialized auth") { TEST_SHELLMATTA_SETUP; TEST_SHELLMATTA_AUTH_SETUP; WHEN("A command is added") { ret = shellmatta_addCmd(handle, &additionalCmd); CHECK(ret == SHELLMATTA_OK); AND_WHEN("The help command is called") { ret = shellmatta_processData(handle, (char*)"help\r", 5); CHECK(ret == SHELLMATTA_OK); THEN("The additional command is not shown as it requires login") { char *dummyData = (char*) "help\r\n" "help ? help [command] - print help or usage information\r\n" "login li Login command\r\n" "logout lo Logout command\r\n" "public p\r\n" "\r\n" "shellmatta->"; CHECK(write_length == strlen(dummyData)); REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData)); AND_WHEN("The root user is logged in") { write_length = 0; memset(write_data, 0, sizeof(write_data)); ret = shellmatta_auth_login(handle, 3); CHECK(ret == SHELLMATTA_OK); AND_WHEN("The help command is called") { ret = shellmatta_processData(handle, (char*)"help\r", 5); CHECK(ret == SHELLMATTA_OK); THEN("all commands are shown including the additional command") { char *dummyData = (char*) "help\r\n" "additional a\r\n" "help ? help [command] - print help or usage information\r\n" "login li Login command\r\n" "logout lo Logout command\r\n" "private r\r\n" "public p\r\n" "\r\n" "root@shellmatta->"; CHECK(write_length == strlen(dummyData)); REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData)); } } } } } } } } shellmatta_retCode_t customLogin(const uint32_t userId, const char *password) { if ((userId == 1) && (0 == strcmp(password, "123456789"))) { return SHELLMATTA_OK; } else if ((userId == 2) && (0 == strcmp(password, "876543210"))) { return SHELLMATTA_OK; } return SHELLMATTA_ERROR; } void logFct(const uint32_t userId, shellmatta_auth_log_event_t event) { logUserId = userId; logEvent = event; } SCENARIO("Check custom login") { GIVEN("An initialized shellmatta instance with initialized auth with custom login and log") { TEST_SHELLMATTA_SETUP; shellmatta_auth_user_t userList[] = { {1, false, "shimatta", NULL}, {2, false, "not_shimatta", NULL} }; uint32_t privateCmdPerms[] = {1}; shellmatta_auth_perm_t permList[] = { {"private", privateCmdPerms, sizeof(privateCmdPerms)/sizeof(privateCmdPerms[0])} }; shellmatta_auth_init(handle, userList, 2, permList, 1, false, customLogin, logFct); WHEN("The user shimatta logs in interactively using the correct credentials") { ret = shellmatta_processData(handle, (char*)"login\r" "shimatta\r" "123456789\r", 25); CHECK(ret == SHELLMATTA_OK); THEN("The login message is printed - password is hidden") { char *dummyData = (char*) "login\r\n" "enter username:\r\n" "shimatta\r\n" "enter password:\r\n" "\r\n" "login successful\r\n" "\r\n" "shimatta@shellmatta->"; CHECK(write_length == strlen(dummyData)); REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData)); AND_THEN("The shimatta user is logged in") { REQUIRE(1 == shellmatta_auth_getLoggedInUserId(handle)); AND_WHEN("The shimatta user logs out") { ret = shellmatta_processData(handle, (char*)"logout\r", 7); CHECK(ret == SHELLMATTA_OK); THEN("The logout event is logged") { CHECK(1 == logUserId); REQUIRE(SHELLMATTA_AUTH_EVENT_LOGOUT == logEvent); } } } AND_THEN("The login event is logged") { CHECK(1 == logUserId); REQUIRE(SHELLMATTA_AUTH_EVENT_LOGIN == logEvent); } } } WHEN("The user shimatta logs in interactively using the wrong credentials") { ret = shellmatta_processData(handle, (char*)"login\r" "shimatta\r" "12345678\r", 24); CHECK(ret == SHELLMATTA_OK); THEN("Login error message is printed") { char *dummyData = (char*) "login\r\n" "enter username:\r\n" "shimatta\r\n" "enter password:\r\n" "\r\n" "username or password is wrong\r\n" "\r\n" "shellmatta->"; CHECK(write_length == strlen(dummyData)); REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData)); AND_THEN("The shimatta user is not logged in") { REQUIRE(0 == shellmatta_auth_getLoggedInUserId(handle)); } AND_THEN("The failed login event is logged") { CHECK(1 == logUserId); REQUIRE(SHELLMATTA_AUTH_EVENT_LOGIN_FAILED == logEvent); } } } } } SCENARIO("Check custom login with custom login function") { GIVEN("An initialized shellmatta instance with initialized auth with custom login and log") { TEST_SHELLMATTA_SETUP; shellmatta_auth_user_t userList[] = { {1, false, "shimatta", NULL}, {2, false, "not_shimatta", NULL} }; uint32_t privateCmdPerms[] = {1}; shellmatta_auth_perm_t permList[] = { {"private", privateCmdPerms, sizeof(privateCmdPerms)/sizeof(privateCmdPerms[0])} }; shellmatta_auth_init(handle, userList, 2, permList, 1, true, customLogin, logFct); WHEN("The help command is called") { ret = shellmatta_processData(handle, (char*)"help\r", 5); CHECK(ret == SHELLMATTA_OK); THEN("There is no login command") { char *dummyData = (char*) "help\r\n" "help ? help [command] - print help or usage information\r\n" "logout lo Logout command\r\n" "public p\r\n" "\r\n" "shellmatta->"; CHECK(write_length == strlen(dummyData)); REQUIRE_THAT(write_data, Catch::Matchers::Equals(dummyData)); } } } } SCENARIO("Check if passwords can be changed") { GIVEN("An initialized shellmatta instance with initialized auth") { TEST_SHELLMATTA_SETUP; TEST_SHELLMATTA_AUTH_SETUP; WHEN("The password of the shellmatta user is changed") { ret = shellmatta_auth_chpasswd(handle, "shimatta", "new_password"); CHECK(ret == SHELLMATTA_OK); AND_WHEN("The user shimatta logs in passing the new credentials") { ret = shellmatta_processData(handle, (char*)"login -u shimatta -p new_password\r", 34); CHECK(ret == SHELLMATTA_OK); THEN("The shimatta user is logged in") { REQUIRE(1 == shellmatta_auth_getLoggedInUserId(handle)); } } AND_WHEN("The user shimatta logs in passing the old credentials") { ret = shellmatta_processData(handle, (char*)"login -u shimatta -p 12345678\r", 30); CHECK(ret == SHELLMATTA_OK); THEN("The shimatta user is not logged in") { REQUIRE(0 == shellmatta_auth_getLoggedInUserId(handle)); } } } } } SCENARIO("Use functions with wrong parameters") { GIVEN("An initialized shellmatta instance with initialized auth") { TEST_SHELLMATTA_SETUP; TEST_SHELLMATTA_AUTH_SETUP; WHEN("Trying to log in user 0") { ret = shellmatta_auth_login(handle, 0); THEN("Shellmatta returns SHELLMATTA_ERROR") { CHECK(ret == SHELLMATTA_ERROR); AND_THEN("No user is logged in") { REQUIRE(0 == shellmatta_auth_getLoggedInUserId(handle)); } } } } }