diff --git a/libs/base/src/ecflow/base/Algorithms.hpp b/libs/base/src/ecflow/base/Algorithms.hpp index 6dc20cd34..0ee992b67 100644 --- a/libs/base/src/ecflow/base/Algorithms.hpp +++ b/libs/base/src/ecflow/base/Algorithms.hpp @@ -81,7 +81,7 @@ template void visit(const Defs& defs, const Path& path, PREDICATE& predicate) { // a. Visit the 'definitions' (which includes the server state) - predicate(defs); + predicate.handle(defs); if (path.empty()) { return; @@ -97,7 +97,12 @@ void visit(const Defs& defs, const Path& path, PREDICATE& predicate) { else { current = current->find_immediate_child(token); } - predicate(*current); + if (current == nullptr) { + predicate.not_found(); + return; + } + + predicate.handle(*current); } } diff --git a/libs/base/src/ecflow/base/AuthorisationDetails.hpp b/libs/base/src/ecflow/base/AuthorisationDetails.hpp index 6ef7eeafc..5eab6aa7c 100644 --- a/libs/base/src/ecflow/base/AuthorisationDetails.hpp +++ b/libs/base/src/ecflow/base/AuthorisationDetails.hpp @@ -11,6 +11,9 @@ #ifndef ecflow_base_AuthorisationDetails_HPP #define ecflow_base_AuthorisationDetails_HPP +#include +#include + #include "ecflow/base/AbstractServer.hpp" #include "ecflow/base/Authorisation.hpp" #include "ecflow/base/cts/ClientToServerCmd.hpp" @@ -55,63 +58,40 @@ namespace ecf { namespace implementation { +// The Authoriser struct is a template that provides a static accept() function + +template +struct Authoriser +{ +}; + +template +void accumulate_paths(const COMMAND& command, std::vector& paths) { + Authoriser::paths(command, paths); +} + +template +std::vector get_affected_paths(const COMMAND& command) { + std::vector paths; + accumulate_paths(command, paths); + return paths; +} + template authorisation_t allows_as_per_read_write_rules(const COMMAND& command, AbstractServer& server) { auto base = dynamic_cast(&server); - std::vector paths; + static_assert(std::is_base_of_v || std::is_base_of_v, + "The command must be either a TaskCmd or a UserCmd"); + if constexpr (std::is_base_of_v) { // No actual verification is done for task commands return authorisation_t::success("Authorisation (task) granted"); } - else if constexpr (std::is_base_of_v) { - if constexpr (std::is_same_v || std::is_same_v || - std::is_same_v || std::is_same_v || - std::is_same_v || std::is_same_v || - std::is_same_v) { - auto&& p = command.paths(); - for (auto& path : p) { - paths.push_back(path); - } - } - else if constexpr (std::is_same_v || std::is_same_v || - std::is_same_v || std::is_same_v || - std::is_same_v || std::is_same_v || - std::is_same_v || std::is_same_v || - std::is_same_v || std::is_same_v || - std::is_same_v || std::is_same_v) { - paths.push_back("/"); - } - else if constexpr (std::is_same_v || std::is_same_v) { - paths.push_back(command.pathToNode()); - } - else if constexpr (std::is_same_v) { - paths.push_back(command.path_to_node()); - } - else if constexpr (std::is_same_v) { - paths.push_back(command.path_to_task()); - } - else if constexpr (std::is_same_v) { - paths.push_back(command.src_node()); - paths.push_back(command.dest()); - } - else if constexpr (std::is_same_v) { - paths.push_back(command.source()); - paths.push_back(command.dest()); - } - else { - auto path = command.absNodePath() == "" ? "/" : command.absNodePath(); - paths.push_back(path); - } - } - else { - static_assert(std::is_base_of_v || std::is_base_of_v, - "The command must be either a TaskCmd or a UserCmd"); - } + std::vector paths = get_affected_paths(command); const std::string required_permission = command.isWrite() ? "write" : "read"; - if (base->authorisation().allows(command.identity(), *base, paths, required_permission)) { return authorisation_t::success("Authorisation (user) granted"); } @@ -119,13 +99,6 @@ authorisation_t allows_as_per_read_write_rules(const COMMAND& command, AbstractS return authorisation_t::failure("Authorisation (user) failed, due to: Insufficient permissions"); } -// The Authoriser struct is a template that provides a static accept() function - -template -struct Authoriser -{ -}; - template authorisation_t do_authorise(const COMMAND& command, AbstractServer& server) { return Authoriser::accept(command, server); @@ -139,6 +112,8 @@ struct Authoriser static authorisation_t accept(const InitCmd& command, AbstractServer& server) { return allows_as_per_read_write_rules(command, server); } + + static void paths(const InitCmd& command, std::vector& paths) { /* Nothing to do... */ } }; template <> @@ -147,6 +122,8 @@ struct Authoriser static authorisation_t accept(const CompleteCmd& command, AbstractServer& server) { return allows_as_per_read_write_rules(command, server); } + + static void paths(const CompleteCmd& command, std::vector& paths) { /* Nothing to do... */ } }; template <> @@ -155,6 +132,8 @@ struct Authoriser static authorisation_t accept(const AbortCmd& command, AbstractServer& server) { return allows_as_per_read_write_rules(command, server); } + + static void paths(const AbortCmd& command, std::vector& paths) { /* Nothing to do... */ } }; template <> @@ -163,6 +142,8 @@ struct Authoriser static authorisation_t accept(const LabelCmd& command, AbstractServer& server) { return allows_as_per_read_write_rules(command, server); } + + static void paths(const LabelCmd& command, std::vector& paths) { /* Nothing to do... */ } }; template <> @@ -171,6 +152,8 @@ struct Authoriser static authorisation_t accept(const MeterCmd& command, AbstractServer& server) { return allows_as_per_read_write_rules(command, server); } + + static void paths(const MeterCmd& command, std::vector& paths) { /* Nothing to do... */ } }; template <> @@ -179,6 +162,8 @@ struct Authoriser static authorisation_t accept(const EventCmd& command, AbstractServer& server) { return allows_as_per_read_write_rules(command, server); } + + static void paths(const EventCmd& command, std::vector& paths) { /* Nothing to do... */ } }; template <> @@ -187,16 +172,48 @@ struct Authoriser static authorisation_t accept(const QueueCmd& command, AbstractServer& server) { return allows_as_per_read_write_rules(command, server); } + + static void paths(const QueueCmd& command, std::vector& paths) { /* Nothing to do... */ } +}; + +template <> +struct Authoriser +{ + static authorisation_t accept(const CtsWaitCmd& command, AbstractServer& server) { + return allows_as_per_read_write_rules(command, server); + } + + static void paths(const CtsWaitCmd& command, std::vector& paths) { /* Nothing to do... */ } }; // User commands +template +inline void select_all_paths(const COMMAND& command, std::vector& paths) { + auto&& affected = command.paths(); + for (auto&& path : affected) { + paths.push_back(path); + } +} + +template +inline void select_root_path([[maybe_unused]] const COMMAND& command, std::vector& paths) { + paths.push_back("/"); +} + +template +inline void select_node_path(const COMMAND& command, std::vector& paths) { + paths.push_back(command.pathToNode()); +} + template <> struct Authoriser { static authorisation_t accept(const AlterCmd& command, AbstractServer& server) { return allows_as_per_read_write_rules(command, server); } + + static void paths(const AlterCmd& command, std::vector& paths) { select_all_paths(command, paths); } }; template <> @@ -205,6 +222,8 @@ struct Authoriser static authorisation_t accept(const BeginCmd& command, AbstractServer& server) { return allows_as_per_read_write_rules(command, server); } + + static void paths(const BeginCmd& command, std::vector& paths) { select_root_path(command, paths); } }; template <> @@ -213,6 +232,8 @@ struct Authoriser static authorisation_t accept(const CFileCmd& command, AbstractServer& server) { return allows_as_per_read_write_rules(command, server); } + + static void paths(const CFileCmd& command, std::vector& paths) { select_node_path(command, paths); } }; template <> @@ -221,6 +242,8 @@ struct Authoriser static authorisation_t accept(const CheckPtCmd& command, AbstractServer& server) { return allows_as_per_read_write_rules(command, server); } + + static void paths(const CheckPtCmd& command, std::vector& paths) { select_root_path(command, paths); } }; template <> @@ -229,6 +252,10 @@ struct Authoriser static authorisation_t accept(const ClientHandleCmd& command, AbstractServer& server) { return allows_as_per_read_write_rules(command, server); } + + static void paths(const ClientHandleCmd& command, std::vector& paths) { + select_root_path(command, paths); + } }; template <> @@ -237,6 +264,8 @@ struct Authoriser static authorisation_t accept(const CSyncCmd& command, AbstractServer& server) { return allows_as_per_read_write_rules(command, server); } + + static void paths(const CSyncCmd& command, std::vector& paths) { select_root_path(command, paths); } }; template <> @@ -245,6 +274,8 @@ struct Authoriser static authorisation_t accept(const CtsCmd& command, AbstractServer& server) { return allows_as_per_read_write_rules(command, server); } + + static void paths(const CtsCmd& command, std::vector& paths) { select_root_path(command, paths); } }; template <> @@ -253,13 +284,14 @@ struct Authoriser static authorisation_t accept(const CtsNodeCmd& command, AbstractServer& server) { return allows_as_per_read_write_rules(command, server); } -}; -template <> -struct Authoriser -{ - static authorisation_t accept(const CtsWaitCmd& command, AbstractServer& server) { - return allows_as_per_read_write_rules(command, server); + static void paths(const CtsNodeCmd& command, std::vector& paths) { + if (auto&& path = command.pathToNode(); path.empty()) { + select_root_path(command, paths); + } + else { + paths.push_back(path); + } } }; @@ -269,6 +301,15 @@ struct Authoriser static authorisation_t accept(const DeleteCmd& command, AbstractServer& server) { return allows_as_per_read_write_rules(command, server); } + + static void paths(const DeleteCmd& command, std::vector& paths) { + if (auto&& affected = command.paths(); affected.empty()) { + paths.push_back("/"); + } + else { + paths.push_back(affected[0]); + } + } }; template <> @@ -277,6 +318,10 @@ struct Authoriser static authorisation_t accept(const EditScriptCmd& command, AbstractServer& server) { return allows_as_per_read_write_rules(command, server); } + + static void paths(const EditScriptCmd& command, std::vector& paths) { + select_node_path(command, paths); + } }; template <> @@ -285,6 +330,8 @@ struct Authoriser static authorisation_t accept(const ForceCmd& command, AbstractServer& server) { return allows_as_per_read_write_rules(command, server); } + + static void paths(const ForceCmd& command, std::vector& paths) { select_all_paths(command, paths); } }; template <> @@ -293,19 +340,8 @@ struct Authoriser static authorisation_t accept(const FreeDepCmd& command, AbstractServer& server) { return allows_as_per_read_write_rules(command, server); } -}; -template <> -struct Authoriser -{ - static authorisation_t accept(const GroupCTSCmd& command, AbstractServer& server) { - for (auto& cmd : command.cmdVec()) { - if (auto result = cmd->authorise(server); !result.ok()) { - return result; - } - } - return allows_as_per_read_write_rules(command, server); - } + static void paths(const FreeDepCmd& command, std::vector& paths) { select_root_path(command, paths); } }; template <> @@ -314,6 +350,8 @@ struct Authoriser static authorisation_t accept(const LoadDefsCmd& command, AbstractServer& server) { return allows_as_per_read_write_rules(command, server); } + + static void paths(const LoadDefsCmd& command, std::vector& paths) { select_root_path(command, paths); } }; template <> @@ -322,6 +360,8 @@ struct Authoriser static authorisation_t accept(const LogCmd& command, AbstractServer& server) { return allows_as_per_read_write_rules(command, server); } + + static void paths(const LogCmd& command, std::vector& paths) { select_root_path(command, paths); } }; template <> @@ -330,6 +370,10 @@ struct Authoriser static authorisation_t accept(const LogMessageCmd& command, AbstractServer& server) { return allows_as_per_read_write_rules(command, server); } + + static void paths(const LogMessageCmd& command, std::vector& paths) { + select_root_path(command, paths); + } }; template <> @@ -338,6 +382,8 @@ struct Authoriser static authorisation_t accept(const MoveCmd& command, AbstractServer& server) { return allows_as_per_read_write_rules(command, server); } + + static void paths(const MoveCmd& command, std::vector& paths) { paths.push_back(command.src_path()); } }; template <> @@ -346,6 +392,10 @@ struct Authoriser static authorisation_t accept(const OrderNodeCmd& command, AbstractServer& server) { return allows_as_per_read_write_rules(command, server); } + + static void paths(const OrderNodeCmd& command, std::vector& paths) { + select_node_path(command, paths); + } }; template <> @@ -354,6 +404,15 @@ struct Authoriser static authorisation_t accept(const PathsCmd& command, AbstractServer& server) { return allows_as_per_read_write_rules(command, server); } + + static void paths(const PathsCmd& command, std::vector& paths) { + if (command.paths().empty()) { + select_root_path(command, paths); + } + else { + select_all_paths(command, paths); + } + } }; template <> @@ -362,6 +421,8 @@ struct Authoriser static authorisation_t accept(const PlugCmd& command, AbstractServer& server) { return allows_as_per_read_write_rules(command, server); } + + static void paths(const PlugCmd& command, std::vector& paths) { paths.push_back(command.source()); } }; template <> @@ -370,6 +431,10 @@ struct Authoriser static authorisation_t accept(const QueryCmd& command, AbstractServer& server) { return allows_as_per_read_write_rules(command, server); } + + static void paths(const QueryCmd& command, std::vector& paths) { + paths.push_back(command.path_to_attribute()); + } }; template <> @@ -378,6 +443,10 @@ struct Authoriser static authorisation_t accept(const ReplaceNodeCmd& command, AbstractServer& server) { return allows_as_per_read_write_rules(command, server); } + + static void paths(const ReplaceNodeCmd& command, std::vector& paths) { + select_node_path(command, paths); + } }; template <> @@ -386,6 +455,10 @@ struct Authoriser static authorisation_t accept(const RequeueNodeCmd& command, AbstractServer& server) { return allows_as_per_read_write_rules(command, server); } + + static void paths(const RequeueNodeCmd& command, std::vector& paths) { + select_all_paths(command, paths); + } }; template <> @@ -394,6 +467,8 @@ struct Authoriser static authorisation_t accept(const RunNodeCmd& command, AbstractServer& server) { return allows_as_per_read_write_rules(command, server); } + + static void paths(const RunNodeCmd& command, std::vector& paths) { select_all_paths(command, paths); } }; template <> @@ -402,6 +477,10 @@ struct Authoriser static authorisation_t accept(const ServerVersionCmd& command, AbstractServer& server) { return allows_as_per_read_write_rules(command, server); } + + static void paths(const ServerVersionCmd& command, std::vector& paths) { + select_root_path(command, paths); + } }; template <> @@ -410,6 +489,8 @@ struct Authoriser static authorisation_t accept(const ShowCmd& command, AbstractServer& server) { return allows_as_per_read_write_rules(command, server); } + + static void paths(const ShowCmd& command, std::vector& paths) { select_root_path(command, paths); } }; template <> @@ -418,9 +499,89 @@ struct Authoriser static authorisation_t accept(const ZombieCmd& command, AbstractServer& server) { return allows_as_per_read_write_rules(command, server); } + + static void paths(const ZombieCmd& command, std::vector& paths) { select_all_paths(command, paths); } +}; + +template +struct Apply +{ + template + static void to(const Cmd_ptr& cmd, F f) { + ( + [&] { + if (const auto* c = dynamic_cast(cmd.get()); c != nullptr) { + f(*c); + } + }(), + ...); + } }; -// Entry point for the accept() function +template <> +struct Authoriser +{ + using Apply_t = Apply; + + static authorisation_t accept(const GroupCTSCmd& command, AbstractServer& server) { + for (auto& cmd : command.cmdVec()) { + authorisation_t found = + authorisation_t::failure("Authorisation (user) failed, due to: Insufficient permissions"); + + Apply_t::to(cmd, [&](auto&& c) { + if (auto&& result = allows_as_per_read_write_rules(c, server); result.ok()) { + found = result; + } + }); + + if (!found.ok()) { + return found; + } + } + + return authorisation_t::success("Authorisation (user) granted (group command)"); + } + + static void paths(const GroupCTSCmd& command, std::vector& paths) { + for (auto&& cmd : command.cmdVec()) { + Apply_t::to(cmd, [&](auto&& c) { accumulate_paths(c, paths); }); + } + } +}; } // namespace implementation diff --git a/libs/base/src/ecflow/base/cts/user/CtsNodeCmd.hpp b/libs/base/src/ecflow/base/cts/user/CtsNodeCmd.hpp index 12180e4c5..d412d26f5 100644 --- a/libs/base/src/ecflow/base/cts/user/CtsNodeCmd.hpp +++ b/libs/base/src/ecflow/base/cts/user/CtsNodeCmd.hpp @@ -34,7 +34,8 @@ class CtsNodeCmd final : public UserCmd { CtsNodeCmd() = default; Api api() const { return api_; } - const std::string& absNodePath() const { return absNodePath_; } + [[deprecated]] const std::string& absNodePath() const { return absNodePath_; } + const std::string& pathToNode() const { return absNodePath_; } void print(std::string&) const override; void print_only(std::string&) const override; diff --git a/libs/base/src/ecflow/base/cts/user/EditScriptCmd.hpp b/libs/base/src/ecflow/base/cts/user/EditScriptCmd.hpp index d1eee75f1..8ad110f32 100644 --- a/libs/base/src/ecflow/base/cts/user/EditScriptCmd.hpp +++ b/libs/base/src/ecflow/base/cts/user/EditScriptCmd.hpp @@ -49,7 +49,8 @@ class EditScriptCmd final : public UserCmd { EditScriptCmd() = default; // Uses by equals only - const std::string& path_to_node() const { return path_to_node_; } + [[deprecated]] const std::string& path_to_node() const { return path_to_node_; } + const std::string& pathToNode() const { return path_to_node_; } EditType edit_type() const { return edit_type_; } bool alias() const { return alias_; } bool run() const { return run_; } diff --git a/libs/base/src/ecflow/base/cts/user/MoveCmd.hpp b/libs/base/src/ecflow/base/cts/user/MoveCmd.hpp index cc83aba61..85dbc5c0e 100644 --- a/libs/base/src/ecflow/base/cts/user/MoveCmd.hpp +++ b/libs/base/src/ecflow/base/cts/user/MoveCmd.hpp @@ -21,6 +21,7 @@ class MoveCmd final : public UserCmd { Node* source() const; const std::string& src_node() const { return src_node_; } + const std::string& src_path() const { return src_path_; } const std::string& dest() const { return dest_; } bool handleRequestIsTestable() const override { return false; } diff --git a/libs/base/src/ecflow/base/cts/user/OrderNodeCmd.hpp b/libs/base/src/ecflow/base/cts/user/OrderNodeCmd.hpp index 6df58462f..01821018e 100644 --- a/libs/base/src/ecflow/base/cts/user/OrderNodeCmd.hpp +++ b/libs/base/src/ecflow/base/cts/user/OrderNodeCmd.hpp @@ -19,7 +19,8 @@ class OrderNodeCmd final : public UserCmd { OrderNodeCmd(const std::string& absNodepath, NOrder::Order op) : absNodepath_(absNodepath), option_(op) {} OrderNodeCmd() = default; - const std::string& absNodePath() const { return absNodepath_; } + [[deprecated]] const std::string& absNodePath() const { return absNodepath_; } + const std::string& pathToNode() const { return absNodepath_; } NOrder::Order option() const { return option_; } bool isWrite() const override { return true; } diff --git a/libs/base/test/TestAlgorithms.cpp b/libs/base/test/TestAlgorithms.cpp index 2c46a8e3f..886b9658c 100644 --- a/libs/base/test/TestAlgorithms.cpp +++ b/libs/base/test/TestAlgorithms.cpp @@ -93,8 +93,9 @@ BOOST_AUTO_TEST_CASE(test_can_visit_defs) { struct Visitor { - void operator()(const Defs& defs) { collected.push_back("defs"); } - void operator()(const Node& s) { collected.push_back("node: " + s.name()); } + void handle(const Defs& defs) { collected.push_back("defs"); } + void handle(const Node& s) { collected.push_back("node: " + s.name()); } + void not_found() {} std::vector collected; }; diff --git a/libs/server/src/ecflow/server/AuthorisationService.cpp b/libs/server/src/ecflow/server/AuthorisationService.cpp index 099a13332..07786b6bf 100644 --- a/libs/server/src/ecflow/server/AuthorisationService.cpp +++ b/libs/server/src/ecflow/server/AuthorisationService.cpp @@ -77,53 +77,52 @@ bool AuthorisationService::allows(const Identity& identity, } bool allowed = false; - std::visit( - overload{[&allowed](const Unrestricted&) { - // when no rules are loaded, we allow everything... - // Dangerous, but backward compatible! - allowed = true; - }, - [&server, &identity, &paths, &permission, &allowed](const Rules& rules) { - for (auto&& path : paths) { - - auto u = identity.as_string(); - auto a = permission; - - struct Visitor - { - void operator()(const Defs& defs) { - auto p = defs.server().permissions(); - permissions = p.is_empty() ? permissions : p; - } - void operator()(const Node& s) { - auto p = s.permissions(); - permissions = p.is_empty() ? permissions : p; - } - - Permissions permissions = Permissions::make_empty(); - }; - - auto d = server.defs(); - auto p = Path::make(path).value(); - auto v = Visitor{}; - ecf::visit(*d, p, v); - - if (v.permissions.is_empty()) { - std::cout << "Allowed! No custom permissions found for: " << p.to_string() << std::endl; - allowed = true; - } - else if (v.permissions.allows(identity.username())) { - std::cout << "Allowed! Specific permissions found for: " << p.to_string() << std::endl; - allowed = true; - } - else { - std::cout << "Not allowed! Specific permissions found for: " << p.to_string() << std::endl; - allowed = false; - break; - } - } - }}, - impl_->permissions_); + std::visit(overload{[&allowed](const Unrestricted&) { + // when no rules are loaded, we allow everything... + // Dangerous, but backward compatible! + allowed = true; + }, + [&server, &identity, &paths, &permission, &allowed](const Rules& rules) { + for (auto&& path : paths) { + + auto u = identity.as_string(); + auto a = permission; + + struct Visitor + { + void handle(const Defs& defs) { + auto p = defs.server().permissions(); + permissions = p.is_empty() ? permissions : p; + } + void handle(const Node& s) { + auto p = s.permissions(); + permissions = p.is_empty() ? permissions : p; + } + + void not_found() { permissions = Permissions::make_empty(); } + + Permissions permissions = Permissions::make_empty(); + }; + + auto d = server.defs(); + auto p = Path::make(path).value(); + auto v = Visitor{}; + + ecf::visit(*d, p, v); + + if (v.permissions.is_empty()) { + allowed = true; + } + else if (v.permissions.allows(identity.username())) { + allowed = true; + } + else { + allowed = false; + break; + } + } + }}, + impl_->permissions_); return allowed; }