diff --git a/libs/server/src/ecflow/server/ServerEnvironment.cpp b/libs/server/src/ecflow/server/ServerEnvironment.cpp index b0210e101..ba96cdf1e 100644 --- a/libs/server/src/ecflow/server/ServerEnvironment.cpp +++ b/libs/server/src/ecflow/server/ServerEnvironment.cpp @@ -327,20 +327,7 @@ bool ServerEnvironment::valid(std::string& errorMsg) const { /// read in the ecf white list file that specifies valid users and their access rights /// If the file can't be opened returns false and an error message and false; /// Automatically add server admin(user) with write access, as this will allow admin reload - if (auto loaded = load_whitelist_file(errorMsg); loaded) { - if (auto result = AuthorisationService::load_permissions_from_whitelist(this->white_list_file_); result.ok()) { - authorisation_service_ = result.value(); - std::cout << "Loaded server permissions based on Whitelist file\n"; - return true; - } - else { - std::cout << "Unable to load server permissions, due to: " << result.reason() << '\n'; - return false; - } - } - else { - return false; - } + return load_whitelist_file(errorMsg); } std::pair ServerEnvironment::hostPort() const { @@ -426,7 +413,16 @@ bool ServerEnvironment::load_whitelist_file(std::string& errorMsg) const { // (Requires terminate, modify white list, restart to fix) // Hence always allow server user write access *IF* required for non-empty file white_list_file_.allow_write_access_for_server_user(); - return true; + + if (auto result = AuthorisationService::load_permissions_from_whitelist(this->white_list_file_); result.ok()) { + authorisation_service_ = result.value(); + std::cout << "Loaded server permissions based on Whitelist file\n"; + return true; + } + else { + std::cout << "Unable to load server permissions, due to: " << result.reason() << '\n'; + return false; + } } return false; } diff --git a/libs/test/foolproof/CMakeLists.txt b/libs/test/foolproof/CMakeLists.txt index 6d39e7e4f..70cca10ad 100644 --- a/libs/test/foolproof/CMakeLists.txt +++ b/libs/test/foolproof/CMakeLists.txt @@ -42,5 +42,7 @@ ecbuild_add_test( DEPENDS ecflow_server ecflow_client + ARGS + --random=1 ) target_clangformat(s_foolproof CONDITION ENABLE_TESTS) diff --git a/libs/test/foolproof/TestFoolproof.PasswordsAndWhiteLists.cpp b/libs/test/foolproof/TestFoolproof.PasswordsAndWhiteLists.cpp index 908c12094..fa85ef587 100644 --- a/libs/test/foolproof/TestFoolproof.PasswordsAndWhiteLists.cpp +++ b/libs/test/foolproof/TestFoolproof.PasswordsAndWhiteLists.cpp @@ -324,7 +324,7 @@ BOOST_AUTO_TEST_CASE(test_e2e_reload_whitelist) { /* * Description * - * This test case ensures that the 'reloadwsfile' command effectively reloads the content of white list. + * This test case ensures that the 'reloadwsfile' command effectively reloads the content of a white list file. * * Requirements * @@ -449,6 +449,130 @@ endsuite; } } +BOOST_AUTO_TEST_CASE(test_e2e_reload_whitelist_created_after_server_is_launched) { + ECF_NAME_THIS_TEST(); + + /* + * Description + * + * This test case ensures that the 'reloadwsfile' command effectively reloads the content of a white list file, + * created after the server is launched. + * + * Requirements + * + * - The 'reloadwsfile' command loads the content of the whitelist file, created after the server is launched. + * + */ + + using namespace foolproof::scaffold; + + auto cwd = MakeDirectory{}.create(); + + auto user_a = User{"alice", "somesecret", "rw"}; + auto user_b = User{"bob", "anothersecret", "r"}; + + auto host = MakeHost{}.create(); + auto port = MakePort{}.with(AutomaticPortValue{}).create(); + + auto authentication = MakeTestFile{} + .with(SpecificFileLocation{"custom.passwds", cwd}) + .with(PasswordsFile{host, port, user_a, user_b}.data()) + .create(); + + auto authorisation_file = fs::path{"custom.lists"}; + + auto server_environment_cfg = + MakeTestFile{} + .with(SpecificFileLocation{"server_environment.cfg", cwd}) + .with(ServerEnvironmentFile{std::make_tuple("ECF_PASSWD", authentication.filename()), + std::make_tuple("ECF_CUSTOM_PASSWD", authentication.filename()), + std::make_tuple("ECF_LISTS", authorisation_file)} + .data()) + .create(); + + const auto server = MakeServer{}.with(host).with(port).with(cwd).launch(); + { + BOOST_REQUIRE(server.ok()); + auto& s = server.value(); + BOOST_CHECK(s.pid() > 0); + BOOST_CHECK(s.port().value() == port.value()); + BOOST_CHECK(s.host().is_valid()); + } + + auto defs = MakeTestFile{} + .with(SpecificFileLocation{"suite.def", cwd}) + .with(R"--( +suite s + family f + task task + label l "original_value" + endfamily +endsuite; +)--") + .create(); + + { // #authorisation, perform write operation (without whitelist) -- load defs file % [success] + auto client = + RunClient{}.with(host).with(port).with(user_a).with(cwd).execute(RunClient::CommandLoad{defs.path()}); + BOOST_REQUIRE(client.ok()); + auto& c = client.value(); + + ECF_TEST_DBG("Output of --load:\n" << c.stdout_buffer); + } + + { // #authorisation, perform write operation (without whitelist) -- update label value % [success] + auto client = RunClient{}.with(host).with(port).with(user_a).with(cwd).execute( + RunClient::CommandUpdateLabel{"/s/f/task", "l", "updated_value"}); + BOOST_REQUIRE(client.ok()); + auto c = client.value(); + + ECF_TEST_DBG("Output of --alter label:\n" << c.stdout_buffer); + } + + { // #authorisation, perform write operation (without whitelist) -- update label value % [success] + auto client = RunClient{}.with(host).with(port).with(user_b).with(cwd).execute( + RunClient::CommandUpdateLabel{"/s/f/task", "l", "another_updated_value"}); + BOOST_REQUIRE(client.ok()); + auto c = client.value(); + + ECF_TEST_DBG("Output of --alter label:\n" << c.stdout_buffer); + } + + auto authorisation = MakeTestFile{} + .with(SpecificFileLocation{authorisation_file, cwd}) + .with(WhitelistFile{user_a, user_b}.data()) + .create(); + + { // #authorisation, perform write operation (without whitelist) -- reload whitelist file % [success] + auto client = + RunClient{}.with(host).with(port).with(user_a).with(cwd).execute(RunClient::CommandReloadWhitelist{}); + BOOST_REQUIRE(client.ok()); + auto c = client.value(); + + ECF_TEST_DBG("Output of --reloadwsfile:\n" << c.stdout_buffer); + } + + { // #authorisation, perform write operation with "rw" access -- update label value % [success] + auto client = RunClient{}.with(host).with(port).with(user_a).with(cwd).execute( + RunClient::CommandUpdateLabel{"/s/f/task", "l", "updated_value"}); + BOOST_REQUIRE(client.ok()); + auto c = client.value(); + + ECF_TEST_DBG("Output of --alter label:\n" << c.stdout_buffer); + } + + { // #authorisation, perform write operation with only "r" access -- update label value % [failure] + auto client = RunClient{}.with(host).with(port).with(user_b).with(cwd).execute( + RunClient::CommandUpdateLabel{"/s/f/task", "l", "another_updated_value"}); + BOOST_REQUIRE(!client.ok()); + + BOOST_CHECK( + client.reason().find( + "Command not accepted, due to: Authorisation (user) failed, due to: Insufficient permissions") != + std::string::npos); + } +} + BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()