Skip to content

Commit c5b4386

Browse files
committed
Allow sticky permissions
Extend the 'rwxo' permissions to 'rwxos' where 's' stands for sticky, and protects the rights of admin users for which the access rights must be safe-guarded. Once a permissions is classified as Sticky, the usual permissions override mechanism is disabled. Re ECFLOW-1960
1 parent 097710a commit c5b4386

File tree

5 files changed

+296
-67
lines changed

5 files changed

+296
-67
lines changed

libs/base/test/TestPermissions.cpp

Lines changed: 202 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020

2121
BOOST_AUTO_TEST_SUITE(U_Base)
2222

23-
BOOST_AUTO_TEST_SUITE(T_Perms)
23+
BOOST_AUTO_TEST_SUITE(T_Permissions)
2424

2525
BOOST_AUTO_TEST_CASE(test_file_automatic_name_exists) {
2626
ECF_NAME_THIS_TEST();
@@ -66,37 +66,215 @@ BOOST_AUTO_TEST_CASE(can_do_permissions) {
6666
auto s = d.add_suite("s1");
6767
s->addVariable(Variable("PERMISSIONS", "u1:rwx,u2:rw"));
6868
auto f = s->add_family("f1");
69-
f->addVariable(Variable("PERMISSIONS", "u2:r,u3:rw"));
69+
f->addVariable(Variable("PERMISSIONS", "u2:rx,u3:rw"));
7070
auto t = f->add_task("t1");
7171

7272
MockServer server(&d);
73-
d.server_state().add_or_update_server_variable("PERMISSIONS", "a:rwxo");
73+
d.server_state().add_or_update_server_variable("PERMISSIONS", "a:rwxos");
7474

7575
AuthorisationService service = AuthorisationService::load_permissions_from_nodes().value();
7676

77+
auto a = Username{"a"};
78+
auto u1 = Username{"u1"};
79+
auto u2 = Username{"u2"};
80+
auto u3 = Username{"u3"};
81+
7782
{
78-
auto selected = service.permissions_at(d, "/s1/f1/t1"s);
79-
80-
BOOST_REQUIRE(selected.allows(Username{"a"}, Allowed::READ));
81-
BOOST_REQUIRE(selected.allows(Username{"a"}, Allowed::WRITE));
82-
BOOST_REQUIRE(selected.allows(Username{"a"}, Allowed::EXECUTE));
83-
BOOST_REQUIRE(selected.allows(Username{"a"}, Allowed::OWNER));
84-
85-
BOOST_REQUIRE(selected.allows(Username{"u1"}, Allowed::READ));
86-
BOOST_REQUIRE(selected.allows(Username{"u1"}, Allowed::WRITE));
87-
BOOST_REQUIRE(selected.allows(Username{"u1"}, Allowed::EXECUTE));
88-
BOOST_REQUIRE(!selected.allows(Username{"u1"}, Allowed::OWNER));
89-
90-
BOOST_REQUIRE(selected.allows(Username{"u2"}, Allowed::READ));
91-
BOOST_REQUIRE(!selected.allows(Username{"u2"}, Allowed::WRITE));
92-
BOOST_REQUIRE(!selected.allows(Username{"u2"}, Allowed::EXECUTE));
93-
BOOST_REQUIRE(!selected.allows(Username{"u2"}, Allowed::OWNER));
94-
95-
BOOST_REQUIRE(!selected.allows(Username{"u3"}, Allowed::READ));
96-
BOOST_REQUIRE(!selected.allows(Username{"u3"}, Allowed::WRITE));
97-
BOOST_REQUIRE(!selected.allows(Username{"u3"}, Allowed::EXECUTE));
98-
BOOST_REQUIRE(!selected.allows(Username{"u3"}, Allowed::OWNER));
83+
auto selected = service.permissions_at(d, "/s1"s);
84+
85+
BOOST_CHECK(selected.allows(a, Allowed::READ));
86+
BOOST_CHECK(selected.allows(a, Allowed::WRITE));
87+
BOOST_CHECK(selected.allows(a, Allowed::EXECUTE));
88+
BOOST_CHECK(selected.allows(a, Allowed::OWNER));
89+
BOOST_CHECK(selected.allows(a, Allowed::STICKY));
90+
91+
BOOST_CHECK(selected.allows(u1, Allowed::READ));
92+
BOOST_CHECK(selected.allows(u1, Allowed::WRITE));
93+
BOOST_CHECK(selected.allows(u1, Allowed::EXECUTE));
94+
BOOST_CHECK(!selected.allows(u1, Allowed::OWNER));
95+
BOOST_CHECK(!selected.allows(u1, Allowed::STICKY));
96+
97+
BOOST_CHECK(selected.allows(u2, Allowed::READ));
98+
BOOST_CHECK(selected.allows(u2, Allowed::WRITE));
99+
BOOST_CHECK(!selected.allows(u2, Allowed::EXECUTE));
100+
BOOST_CHECK(!selected.allows(u2, Allowed::OWNER));
101+
BOOST_CHECK(!selected.allows(u2, Allowed::STICKY));
102+
103+
BOOST_CHECK(!selected.allows(u3, Allowed::READ));
104+
BOOST_CHECK(!selected.allows(u3, Allowed::WRITE));
105+
BOOST_CHECK(!selected.allows(u3, Allowed::EXECUTE));
106+
BOOST_CHECK(!selected.allows(u3, Allowed::OWNER));
107+
BOOST_CHECK(!selected.allows(u3, Allowed::STICKY));
99108
}
109+
110+
{
111+
auto selected = service.permissions_at(d, "/s1/f1"s);
112+
113+
BOOST_CHECK(selected.allows(a, Allowed::READ));
114+
BOOST_CHECK(selected.allows(a, Allowed::WRITE));
115+
BOOST_CHECK(selected.allows(a, Allowed::EXECUTE));
116+
BOOST_CHECK(selected.allows(a, Allowed::OWNER));
117+
BOOST_CHECK(selected.allows(a, Allowed::STICKY));
118+
119+
BOOST_CHECK(!selected.allows(u1, Allowed::READ));
120+
BOOST_CHECK(!selected.allows(u1, Allowed::WRITE));
121+
BOOST_CHECK(!selected.allows(u1, Allowed::EXECUTE));
122+
BOOST_CHECK(!selected.allows(u1, Allowed::OWNER));
123+
BOOST_CHECK(!selected.allows(u1, Allowed::STICKY));
124+
125+
BOOST_CHECK(selected.allows(u2, Allowed::READ));
126+
BOOST_CHECK(!selected.allows(u2, Allowed::WRITE));
127+
BOOST_CHECK(!selected.allows(u2, Allowed::EXECUTE));
128+
BOOST_CHECK(!selected.allows(u2, Allowed::OWNER));
129+
BOOST_CHECK(!selected.allows(u2, Allowed::STICKY));
130+
131+
BOOST_CHECK(!selected.allows(u3, Allowed::READ));
132+
BOOST_CHECK(!selected.allows(u3, Allowed::WRITE));
133+
BOOST_CHECK(!selected.allows(u3, Allowed::EXECUTE));
134+
BOOST_CHECK(!selected.allows(u3, Allowed::OWNER));
135+
BOOST_CHECK(!selected.allows(u3, Allowed::STICKY));
136+
}
137+
}
138+
139+
BOOST_AUTO_TEST_CASE(can_calculate_permission_superseeding_basic_rules) {
140+
using namespace ecf;
141+
142+
auto a = Username{"a"};
143+
auto u1 = Username{"u1"};
144+
auto u2 = Username{"u2"};
145+
146+
auto p = Permissions::make_from_variable("a:rws,u1:rw");
147+
BOOST_CHECK(p.allows(a, Allowed::READ));
148+
BOOST_CHECK(p.allows(a, Allowed::WRITE));
149+
BOOST_CHECK(!p.allows(a, Allowed::EXECUTE));
150+
BOOST_CHECK(!p.allows(a, Allowed::OWNER));
151+
BOOST_CHECK(p.allows(a, Allowed::STICKY));
152+
BOOST_CHECK(p.allows(u1, Allowed::READ));
153+
BOOST_CHECK(p.allows(u1, Allowed::WRITE));
154+
BOOST_CHECK(!p.allows(u1, Allowed::EXECUTE));
155+
BOOST_CHECK(!p.allows(u1, Allowed::OWNER));
156+
BOOST_CHECK(!p.allows(u1, Allowed::STICKY));
157+
158+
auto q = Permissions::make_from_variable("a:r,u1:rwx,u2:rw");
159+
BOOST_CHECK(q.allows(a, Allowed::READ));
160+
BOOST_CHECK(!q.allows(a, Allowed::WRITE));
161+
BOOST_CHECK(!q.allows(a, Allowed::EXECUTE));
162+
BOOST_CHECK(!q.allows(a, Allowed::OWNER));
163+
BOOST_CHECK(!q.allows(a, Allowed::STICKY));
164+
BOOST_CHECK(q.allows(u1, Allowed::READ));
165+
BOOST_CHECK(q.allows(u1, Allowed::WRITE));
166+
BOOST_CHECK(q.allows(u1, Allowed::EXECUTE));
167+
BOOST_CHECK(!q.allows(u1, Allowed::OWNER));
168+
BOOST_CHECK(!q.allows(u1, Allowed::STICKY));
169+
BOOST_CHECK(q.allows(u2, Allowed::READ));
170+
BOOST_CHECK(q.allows(u2, Allowed::WRITE));
171+
BOOST_CHECK(!q.allows(u2, Allowed::EXECUTE));
172+
BOOST_CHECK(!q.allows(u2, Allowed::OWNER));
173+
BOOST_CHECK(!q.allows(u2, Allowed::STICKY));
174+
175+
auto r = Permissions::combine_supersede(p, q);
176+
BOOST_CHECK(r.allows(a, Allowed::READ));
177+
BOOST_CHECK(r.allows(a, Allowed::WRITE));
178+
BOOST_CHECK(!r.allows(a, Allowed::EXECUTE));
179+
BOOST_CHECK(!r.allows(a, Allowed::OWNER));
180+
BOOST_CHECK(r.allows(a, Allowed::STICKY));
181+
BOOST_CHECK(r.allows(u1, Allowed::READ));
182+
BOOST_CHECK(r.allows(u1, Allowed::WRITE));
183+
BOOST_CHECK(r.allows(u1, Allowed::EXECUTE));
184+
BOOST_CHECK(!r.allows(u1, Allowed::OWNER));
185+
BOOST_CHECK(!r.allows(u1, Allowed::STICKY));
186+
BOOST_CHECK(r.allows(u2, Allowed::READ));
187+
BOOST_CHECK(r.allows(u2, Allowed::WRITE));
188+
BOOST_CHECK(!r.allows(u2, Allowed::EXECUTE));
189+
BOOST_CHECK(!r.allows(u2, Allowed::OWNER));
190+
BOOST_CHECK(!r.allows(u2, Allowed::STICKY));
191+
}
192+
193+
BOOST_AUTO_TEST_CASE(can_calculate_permission_overriding_basic_rules) {
194+
using namespace ecf;
195+
196+
auto u1 = Username{"u1"};
197+
auto u2 = Username{"u2"};
198+
199+
auto p = Permissions::make_from_variable("u1:rwx");
200+
BOOST_CHECK(p.allows(u1, Allowed::READ));
201+
BOOST_CHECK(p.allows(u1, Allowed::WRITE));
202+
BOOST_CHECK(p.allows(u1, Allowed::EXECUTE));
203+
BOOST_CHECK(!p.allows(u1, Allowed::OWNER));
204+
BOOST_CHECK(!p.allows(u1, Allowed::STICKY));
205+
206+
auto q = Permissions::make_from_variable("u1:rw,u2:rwxo");
207+
BOOST_CHECK(q.allows(u1, Allowed::READ));
208+
BOOST_CHECK(q.allows(u1, Allowed::WRITE));
209+
BOOST_CHECK(!q.allows(u1, Allowed::EXECUTE));
210+
BOOST_CHECK(!q.allows(u1, Allowed::OWNER));
211+
BOOST_CHECK(!q.allows(u1, Allowed::STICKY));
212+
213+
auto r = Permissions::combine_override(p, q);
214+
BOOST_CHECK(r.allows(u1, Allowed::READ));
215+
BOOST_CHECK(r.allows(u1, Allowed::WRITE));
216+
BOOST_CHECK(!r.allows(u1, Allowed::EXECUTE));
217+
BOOST_CHECK(!r.allows(u1, Allowed::OWNER));
218+
BOOST_CHECK(!r.allows(u1, Allowed::STICKY));
219+
220+
BOOST_CHECK(!r.allows(u2, Allowed::READ));
221+
BOOST_CHECK(!r.allows(u2, Allowed::WRITE));
222+
BOOST_CHECK(!r.allows(u2, Allowed::EXECUTE));
223+
BOOST_CHECK(!r.allows(u2, Allowed::STICKY));
224+
}
225+
226+
BOOST_AUTO_TEST_CASE(can_calculate_permission_overriding_does_not_extend_allowances) {
227+
using namespace ecf;
228+
229+
auto u1 = Username{"u1"};
230+
231+
auto p = Permissions::make_from_variable("u1:rw");
232+
BOOST_CHECK(p.allows(u1, Allowed::READ));
233+
BOOST_CHECK(p.allows(u1, Allowed::WRITE));
234+
BOOST_CHECK(!p.allows(u1, Allowed::EXECUTE));
235+
BOOST_CHECK(!p.allows(u1, Allowed::OWNER));
236+
BOOST_CHECK(!p.allows(u1, Allowed::STICKY));
237+
238+
auto q = Permissions::make_from_variable("u1:rwxo");
239+
BOOST_CHECK(q.allows(u1, Allowed::READ));
240+
BOOST_CHECK(q.allows(u1, Allowed::WRITE));
241+
BOOST_CHECK(q.allows(u1, Allowed::EXECUTE));
242+
BOOST_CHECK(q.allows(u1, Allowed::OWNER));
243+
BOOST_CHECK(!q.allows(u1, Allowed::STICKY));
244+
245+
auto r = Permissions::combine_override(p, q);
246+
BOOST_CHECK(r.allows(u1, Allowed::READ));
247+
BOOST_CHECK(r.allows(u1, Allowed::WRITE));
248+
BOOST_CHECK(!r.allows(u1, Allowed::EXECUTE));
249+
BOOST_CHECK(!r.allows(u1, Allowed::OWNER));
250+
BOOST_CHECK(!r.allows(u1, Allowed::STICKY));
251+
}
252+
253+
BOOST_AUTO_TEST_CASE(can_calculate_permission_overriding_keeps_sticky_allowances) {
254+
using namespace ecf;
255+
256+
auto u1 = Username{"u1"};
257+
258+
auto p = Permissions::make_from_variable("u1:rwxs");
259+
BOOST_CHECK(p.allows(u1, Allowed::READ));
260+
BOOST_CHECK(p.allows(u1, Allowed::WRITE));
261+
BOOST_CHECK(p.allows(u1, Allowed::EXECUTE));
262+
BOOST_CHECK(!p.allows(u1, Allowed::OWNER));
263+
BOOST_CHECK(p.allows(u1, Allowed::STICKY));
264+
265+
auto q = Permissions::make_from_variable("u1:r");
266+
BOOST_CHECK(q.allows(u1, Allowed::READ));
267+
BOOST_CHECK(!q.allows(u1, Allowed::WRITE));
268+
BOOST_CHECK(!q.allows(u1, Allowed::EXECUTE));
269+
BOOST_CHECK(!q.allows(u1, Allowed::OWNER));
270+
BOOST_CHECK(!q.allows(u1, Allowed::STICKY));
271+
272+
auto r = Permissions::combine_override(p, q);
273+
BOOST_CHECK(r.allows(u1, Allowed::READ));
274+
BOOST_CHECK(r.allows(u1, Allowed::WRITE));
275+
BOOST_CHECK(r.allows(u1, Allowed::EXECUTE));
276+
BOOST_CHECK(!r.allows(u1, Allowed::OWNER));
277+
BOOST_CHECK(r.allows(u1, Allowed::STICKY));
100278
}
101279

102280
BOOST_AUTO_TEST_SUITE_END()

libs/node/src/ecflow/node/Permissions.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ namespace ecf {
1717

1818
Permissions Permissions::make_from_variable(const std::string& value) {
1919

20+
if (value.empty()) {
21+
return Permissions::make_empty();
22+
}
23+
2024
// Expecting a comma-separated list of user/permissions, e.g. "USER1:RWXO,USER2:R"
2125

2226
std::vector<std::string> entries;
@@ -54,6 +58,11 @@ Permissions Permissions::make_from_variable(const std::string& value) {
5458
case 'O':
5559
perms |= Allowed::OWNER;
5660
break;
61+
case 's':
62+
[[fallthrough]];
63+
case 'S':
64+
perms |= Allowed::STICKY;
65+
break;
5766
default:
5867
throw std::runtime_error("Invalid permission character: " + std::string(1, c) +
5968
". Expected one of: [r, w, x, o]");

0 commit comments

Comments
 (0)