Skip to content

Commit 9e6094f

Browse files
committed
{WIP} Adding per node Authorization algorithm
Re ECFLOW-1960
1 parent 32a61fa commit 9e6094f

File tree

6 files changed

+307
-38
lines changed

6 files changed

+307
-38
lines changed

libs/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ set(srcs
5757
# Base -- Headers
5858
base/src/ecflow/base/AbstractClientEnv.hpp
5959
base/src/ecflow/base/AbstractServer.hpp
60+
base/src/ecflow/base/Algorithms.hpp
6061
base/src/ecflow/base/Authentication.hpp
6162
base/src/ecflow/base/AuthenticationDetails.hpp
6263
base/src/ecflow/base/Authorisation.hpp

libs/base/CMakeLists.txt

+24
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,30 @@ target_clangformat(u_base
5959
CONDITION ENABLE_TESTS
6060
)
6161

62+
set(test_srcs
63+
# Sources
64+
test/TestAlgorithms.cpp
65+
)
66+
67+
ecbuild_add_test(
68+
TARGET
69+
u_base_algorithms
70+
LABELS
71+
unit nightly
72+
SOURCES
73+
${test_srcs}
74+
LIBS
75+
ecflow_all
76+
test_scaffold
77+
test_harness.base
78+
Threads::Threads
79+
$<$<BOOL:${OPENSSL_FOUND}>:OpenSSL::SSL>
80+
)
81+
target_clangformat(u_base_algorithms
82+
CONDITION ENABLE_TESTS
83+
)
84+
85+
6286
# The following is not technically a test (as it makes no checks),
6387
# but a tool to measure the time it takes to generate a job file
6488
if (ENABLE_ALL_TESTS)
+117
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/*
2+
* Copyright 2009- ECMWF.
3+
*
4+
* This software is licensed under the terms of the Apache Licence version 2.0
5+
* which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
6+
* In applying this licence, ECMWF does not waive the privileges and immunities
7+
* granted to it by virtue of its status as an intergovernmental organisation
8+
* nor does it submit to any jurisdiction.
9+
*/
10+
11+
#ifndef ecflow_base_Algorithms_hpp
12+
#define ecflow_base_Algorithms_hpp
13+
14+
#include <string>
15+
16+
#include <boost/algorithm/string.hpp>
17+
#include <boost/tokenizer.hpp>
18+
19+
#include "ecflow/base/AbstractServer.hpp"
20+
#include "ecflow/core/Result.hpp"
21+
#include "ecflow/node/Defs.hpp"
22+
23+
namespace ecf {
24+
25+
///
26+
/// Represents a path in the server's hierarchy
27+
///
28+
/// A path object is always represents a valid path (even if it does not exist).
29+
///
30+
/// A path with 0 tokens represents the root of the hierarchy (represented by a slash, "/").
31+
/// A path with N tokens represents a path from the root to a node.
32+
///
33+
/// Multiple consecutive separators (i.e. slashes, "/") are treated as a single slash.
34+
///
35+
struct Path
36+
{
37+
static Result<Path> make(const std::string& path) {
38+
if (path.empty()) {
39+
return Result<Path>::failure("Invalid path: '" + path + "' (cannot be empty)");
40+
}
41+
42+
if (path == "/") {
43+
return Result<Path>::success(Path(std::vector<std::string>()));
44+
}
45+
46+
std::vector<std::string> tokens;
47+
boost::tokenizer<boost::char_separator<char>> tokenizer(path, boost::char_separator<char>("/ "));
48+
for (const auto& token : tokenizer) {
49+
tokens.push_back(token);
50+
}
51+
return Result<Path>::success(Path(std::move(tokens)));
52+
}
53+
54+
[[nodiscard]] std::string to_string() const {
55+
if (tokens_.empty()) {
56+
return "/";
57+
}
58+
59+
std::string result;
60+
for (auto&& token : tokens_) {
61+
result += '/';
62+
result += token;
63+
}
64+
return result;
65+
}
66+
67+
[[nodiscard]] bool empty() const { return tokens_.empty(); }
68+
[[nodiscard]] size_t size() const { return tokens_.size(); }
69+
[[nodiscard]] const std::string& operator[](size_t idx) const { return tokens_[idx]; }
70+
71+
auto begin() const { return tokens_.begin(); }
72+
auto end() const { return tokens_.end(); }
73+
74+
private:
75+
explicit Path(std::vector<std::string> tokens) : tokens_(std::move(tokens)) {}
76+
77+
std::vector<std::string> tokens_;
78+
};
79+
80+
template <typename PREDICATE>
81+
void visit(const Defs& defs, const Path& path, PREDICATE& predicate) {
82+
83+
// a. Visit the server 'definitions'
84+
predicate(defs);
85+
86+
// b. Visit each one of the 'nodes' along the given path
87+
88+
node_ptr current = nullptr;
89+
for (auto&& token : path) {
90+
if (current == nullptr) {
91+
current = defs.findSuite(token);
92+
}
93+
else {
94+
current = current->find_immediate_child(token);
95+
}
96+
predicate(*current);
97+
}
98+
99+
}
100+
101+
template <typename PREDICATE>
102+
void visit(const AbstractServer& server, const Path& path, PREDICATE& predicate) {
103+
104+
// Visit the 'server'
105+
predicate(server);
106+
107+
if (path.empty()) {
108+
return;
109+
}
110+
111+
visit(*server.defs(), path, predicate);
112+
}
113+
114+
115+
} // namespace ecf
116+
117+
#endif // ecflow_base_Algorithms_hpp

libs/base/test/TestAlgorithms.cpp

+146
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
/*
2+
* Copyright 2009- ECMWF.
3+
*
4+
* This software is licensed under the terms of the Apache Licence version 2.0
5+
* which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
6+
* In applying this licence, ECMWF does not waive the privileges and immunities
7+
* granted to it by virtue of its status as an intergovernmental organisation
8+
* nor does it submit to any jurisdiction.
9+
*/
10+
11+
#define BOOST_TEST_MODULE Test_Base
12+
#include <boost/test/included/unit_test.hpp>
13+
14+
#include "MockServer.hpp"
15+
#include "ecflow/base/Algorithms.hpp"
16+
#include "ecflow/node/Family.hpp"
17+
#include "ecflow/server/BaseServer.hpp"
18+
#include "ecflow/test/scaffold/Naming.hpp"
19+
20+
BOOST_AUTO_TEST_SUITE(U_Base)
21+
22+
BOOST_AUTO_TEST_SUITE(T_Path)
23+
24+
BOOST_AUTO_TEST_CASE(test_cannot_create_path_from_empty_string) {
25+
ECF_NAME_THIS_TEST();
26+
27+
auto result = ecf::Path::make("");
28+
BOOST_CHECK_MESSAGE(!result.ok(), "expected !ok");
29+
}
30+
31+
BOOST_AUTO_TEST_CASE(test_can_create_path_from_root_only) {
32+
ECF_NAME_THIS_TEST();
33+
34+
auto result = ecf::Path::make("/");
35+
BOOST_CHECK(result.ok());
36+
auto path = result.value();
37+
BOOST_CHECK(path.empty());
38+
BOOST_CHECK_EQUAL(path.size(), 0ul);
39+
BOOST_CHECK_EQUAL(path.to_string(), "/");
40+
}
41+
42+
BOOST_AUTO_TEST_CASE(test_can_create_path_with_single_token) {
43+
ECF_NAME_THIS_TEST();
44+
45+
auto result = ecf::Path::make("/suite");
46+
BOOST_CHECK(result.ok());
47+
auto path = result.value();
48+
BOOST_CHECK(!path.empty());
49+
BOOST_CHECK_EQUAL(path.size(), 1ul);
50+
BOOST_CHECK_EQUAL(path[0], "suite");
51+
BOOST_CHECK_EQUAL(path.to_string(), "/suite");
52+
}
53+
54+
BOOST_AUTO_TEST_CASE(test_can_create_path_with_multiple_tokens) {
55+
ECF_NAME_THIS_TEST();
56+
57+
auto result = ecf::Path::make("/suite/family/task");
58+
BOOST_CHECK(result.ok());
59+
auto path = result.value();
60+
BOOST_CHECK(!path.empty());
61+
BOOST_CHECK_EQUAL(path.size(), 3ul);
62+
BOOST_CHECK_EQUAL(path[0], "suite");
63+
BOOST_CHECK_EQUAL(path[1], "family");
64+
BOOST_CHECK_EQUAL(path[2], "task");
65+
BOOST_CHECK_EQUAL(path.to_string(), "/suite/family/task");
66+
}
67+
68+
BOOST_AUTO_TEST_CASE(test_can_create_path_with_empty_tokens) {
69+
ECF_NAME_THIS_TEST();
70+
71+
auto result = ecf::Path::make("///suite///family///task");
72+
BOOST_CHECK(result.ok());
73+
auto path = result.value();
74+
BOOST_CHECK(!path.empty());
75+
BOOST_CHECK_EQUAL(path.size(), 3ul);
76+
BOOST_CHECK_EQUAL(path[0], "suite");
77+
BOOST_CHECK_EQUAL(path[1], "family");
78+
BOOST_CHECK_EQUAL(path[2], "task");
79+
BOOST_CHECK_EQUAL(path.to_string(), "/suite/family/task");
80+
}
81+
82+
BOOST_AUTO_TEST_SUITE_END() // T_Path
83+
84+
BOOST_AUTO_TEST_SUITE(T_Algorithms)
85+
86+
BOOST_AUTO_TEST_CASE(test_can_visit_defs) {
87+
ECF_NAME_THIS_TEST();
88+
89+
Defs defs;
90+
suite_ptr s = defs.add_suite("suite");
91+
family_ptr f = s->add_family("family");
92+
task_ptr t = f->add_task("task");
93+
94+
struct Visitor
95+
{
96+
void operator()(const Defs& defs) { collected.push_back("defs"); }
97+
void operator()(const Node& s) { collected.push_back("node: " + s.name()); }
98+
99+
std::vector<std::string> collected;
100+
};
101+
102+
auto path = ecf::Path::make("/suite/family/task").value();
103+
Visitor visitor;
104+
ecf::visit(defs, path, visitor);
105+
106+
BOOST_CHECK_EQUAL(visitor.collected.size(), 4ul);
107+
BOOST_CHECK_EQUAL(visitor.collected[0], "defs");
108+
BOOST_CHECK_EQUAL(visitor.collected[1], "node: suite");
109+
BOOST_CHECK_EQUAL(visitor.collected[2], "node: family");
110+
BOOST_CHECK_EQUAL(visitor.collected[3], "node: task");
111+
}
112+
113+
BOOST_AUTO_TEST_CASE(test_can_visit_server) {
114+
ECF_NAME_THIS_TEST();
115+
116+
Defs defs;
117+
suite_ptr s = defs.add_suite("suite");
118+
family_ptr f = s->add_family("family");
119+
task_ptr t = f->add_task("task");
120+
121+
MockServer server(&defs);
122+
123+
struct Visitor
124+
{
125+
void operator()(const AbstractServer& server) { collected.push_back("server"); }
126+
void operator()(const Defs& defs) { collected.push_back("defs"); }
127+
void operator()(const Node& s) { collected.push_back("node: " + s.name()); }
128+
129+
std::vector<std::string> collected;
130+
};
131+
132+
auto path = ecf::Path::make("/suite/family/task").value();
133+
Visitor visitor;
134+
ecf::visit(server, path, visitor);
135+
136+
BOOST_CHECK_EQUAL(visitor.collected.size(), 5ul);
137+
BOOST_CHECK_EQUAL(visitor.collected[0], "server");
138+
BOOST_CHECK_EQUAL(visitor.collected[1], "defs");
139+
BOOST_CHECK_EQUAL(visitor.collected[2], "node: suite");
140+
BOOST_CHECK_EQUAL(visitor.collected[3], "node: family");
141+
BOOST_CHECK_EQUAL(visitor.collected[4], "node: task");
142+
}
143+
144+
BOOST_AUTO_TEST_SUITE_END() // T_Algorithms
145+
146+
BOOST_AUTO_TEST_SUITE_END()

libs/node/src/ecflow/node/Defs.cpp

-38
Original file line numberDiff line numberDiff line change
@@ -1019,7 +1019,6 @@ bool Defs::operator==(const Defs& rhs) const {
10191019
}
10201020

10211021
node_ptr Defs::findAbsNode(const std::string& pathToNode) const {
1022-
// std::cout << "Defs::findAbsNode " << pathToNode << "\n";
10231022
// The pathToNode is of the form:
10241023
// /suite
10251024
// /suite/family
@@ -1031,70 +1030,33 @@ node_ptr Defs::findAbsNode(const std::string& pathToNode) const {
10311030
StringSplitter string_splitter(pathToNode, Str::PATH_SEPARATOR());
10321031
while (!string_splitter.finished()) {
10331032
std::string_view path_token = string_splitter.next();
1034-
// std::cout << "path_token:'" << path_token << "' last = " << string_splitter.last() << "\n";
10351033
if (!first) {
10361034
for (const auto& suite : suiteVec_) {
10371035
if (path_token == suite->name()) {
10381036
ret = suite;
10391037
if (string_splitter.last()) {
1040-
// cout << "finished returning " << ret->absNodePath() << " *last* suite found\n";
10411038
return ret;
10421039
}
10431040
break;
10441041
}
10451042
}
10461043
if (!ret) {
1047-
// cout << "finished returning suite not found\n";
10481044
return node_ptr();
10491045
}
10501046
first = true;
10511047
}
10521048
else {
1053-
// cout << "seraching from " << ret->absNodePath() << " for " << ref << "\n";
10541049
ret = ret->find_immediate_child(path_token);
10551050
if (ret) {
10561051
if (string_splitter.last()) {
1057-
// cout << "finished returning " << ret->absNodePath() << " *last* \n";
10581052
return ret;
10591053
}
10601054
continue;
10611055
}
1062-
// cout << "finished returning node not found\n";
10631056
return node_ptr();
10641057
}
10651058
}
1066-
// cout << "finished returning node not found, end of loop\n";
10671059
return node_ptr();
1068-
1069-
// OLD code, slower. Since we are creating a vector of strings
1070-
// std::vector<std::string> theNodeNames; theNodeNames.reserve(4);
1071-
// NodePath::split(pathToNode,theNodeNames);
1072-
// if ( theNodeNames.empty() ) {
1073-
// return node_ptr();
1074-
// }
1075-
// size_t child_pos = 0 ; // unused
1076-
// size_t pathSize = theNodeNames.size();
1077-
// size_t theSuiteVecSize = suiteVec_.size();
1078-
// for(size_t s = 0; s < theSuiteVecSize; s++) {
1079-
// size_t index = 0;
1080-
// if (theNodeNames[index] == suiteVec_[s]->name()) {
1081-
// node_ptr the_node = suiteVec_[s];
1082-
// if (pathSize == 1) return the_node;
1083-
// index++; // skip over suite,
1084-
// while (index < pathSize) {
1085-
// the_node = the_node->findImmediateChild(theNodeNames[index],child_pos);
1086-
// if (the_node) {
1087-
// if (index == pathSize - 1) return the_node;
1088-
// index++;
1089-
// }
1090-
// else {
1091-
// return node_ptr();
1092-
// }
1093-
// }
1094-
// return node_ptr();
1095-
// }
1096-
// }
1097-
// return node_ptr();
10981060
}
10991061

11001062
node_ptr Defs::find_closest_matching_node(const std::string& pathToNode) const {

0 commit comments

Comments
 (0)