Skip to content

Commit 797c04f

Browse files
CuriousGeorgiysergepetrenko
authored andcommitted
box: pass statement being rolled back (if any) to priv_grant
In scope of tarantool#9893 we are going to run statement `on_rollback` triggers before rolling back the corresponding statement. During rollback of DDL in the `_priv` space, the database is accessed from `user_reload_privs` to reload user privileges, so we need it to account for the current statement being rolled back: i.e., the new tuple that was introduced (if any) must not be used, while the old tuple (if any) must be used. Needed for tarantool#9893 NO_CHANGELOG=<refactoring> NO_DOC=<refactoring>
1 parent 4ccb6db commit 797c04f

File tree

4 files changed

+157
-33
lines changed

4 files changed

+157
-33
lines changed

src/box/alter.cc

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3970,10 +3970,11 @@ priv_def_check(struct priv_def *priv, enum priv_type priv_type)
39703970

39713971
/**
39723972
* Update a metadata cache object with the new access
3973-
* data.
3973+
* data. For the purpose of the rolled back statement, please refer to
3974+
* `user_reload_privs`.
39743975
*/
39753976
static int
3976-
grant_or_revoke(struct priv_def *priv)
3977+
grant_or_revoke(struct priv_def *priv, struct txn_stmt *rolled_back_stmt)
39773978
{
39783979
struct user *grantee = user_by_id(priv->grantee_id);
39793980
if (grantee == NULL)
@@ -3994,7 +3995,7 @@ grant_or_revoke(struct priv_def *priv)
39943995
return -1;
39953996
}
39963997
} else {
3997-
if (priv_grant(grantee, priv) != 0)
3998+
if (priv_grant(grantee, priv, rolled_back_stmt) != 0)
39983999
return -1;
39994000
}
40004001
return 0;
@@ -4004,13 +4005,12 @@ grant_or_revoke(struct priv_def *priv)
40044005
static int
40054006
revoke_priv(struct trigger *trigger, void *event)
40064007
{
4007-
(void) event;
40084008
struct tuple *tuple = (struct tuple *)trigger->data;
40094009
struct priv_def priv;
40104010
if (priv_def_create_from_tuple(&priv, tuple) != 0)
40114011
return -1;
40124012
priv.access = 0;
4013-
if (grant_or_revoke(&priv) != 0)
4013+
if (grant_or_revoke(&priv, (struct txn_stmt *)event) != 0)
40144014
return -1;
40154015
return 0;
40164016
}
@@ -4019,11 +4019,10 @@ revoke_priv(struct trigger *trigger, void *event)
40194019
static int
40204020
modify_priv(struct trigger *trigger, void *event)
40214021
{
4022-
(void) event;
40234022
struct tuple *tuple = (struct tuple *)trigger->data;
40244023
struct priv_def priv;
40254024
if (priv_def_create_from_tuple(&priv, tuple) != 0 ||
4026-
grant_or_revoke(&priv) != 0)
4025+
grant_or_revoke(&priv, (struct txn_stmt *)event) != 0)
40274026
return -1;
40284027
return 0;
40294028
}
@@ -4044,7 +4043,7 @@ on_replace_dd_priv(struct trigger * /* trigger */, void *event)
40444043
if (new_tuple != NULL && old_tuple == NULL) { /* grant */
40454044
if (priv_def_create_from_tuple(&priv, new_tuple) != 0 ||
40464045
priv_def_check(&priv, PRIV_GRANT) != 0 ||
4047-
grant_or_revoke(&priv) != 0)
4046+
grant_or_revoke(&priv, NULL) != 0)
40484047
return -1;
40494048
struct trigger *on_rollback =
40504049
txn_alter_trigger_new(revoke_priv, new_tuple);
@@ -4057,7 +4056,7 @@ on_replace_dd_priv(struct trigger * /* trigger */, void *event)
40574056
priv_def_check(&priv, PRIV_REVOKE) != 0)
40584057
return -1;
40594058
priv.access = 0;
4060-
if (grant_or_revoke(&priv) != 0)
4059+
if (grant_or_revoke(&priv, NULL) != 0)
40614060
return -1;
40624061
struct trigger *on_rollback =
40634062
txn_alter_trigger_new(modify_priv, old_tuple);
@@ -4067,7 +4066,7 @@ on_replace_dd_priv(struct trigger * /* trigger */, void *event)
40674066
} else { /* modify */
40684067
if (priv_def_create_from_tuple(&priv, new_tuple) != 0 ||
40694068
priv_def_check(&priv, PRIV_GRANT) != 0 ||
4070-
grant_or_revoke(&priv) != 0)
4069+
grant_or_revoke(&priv, NULL) != 0)
40714070
return -1;
40724071
struct trigger *on_rollback =
40734072
txn_alter_trigger_new(modify_priv, old_tuple);

src/box/user.cc

Lines changed: 64 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
#include "sequence.h"
4242
#include "trivia/util.h"
4343
#include "tt_static.h"
44+
#include "txn.h"
4445

4546
struct universe universe;
4647
static struct user users[BOX_USER_MAX];
@@ -401,10 +402,31 @@ user_set_effective_access(struct user *user)
401402
}
402403

403404
/**
404-
* Reload user privileges and re-grant them.
405+
* Reload a single user privilege from a privilege tuple.
405406
*/
406407
static int
407-
user_reload_privs(struct user *user)
408+
user_reload_priv(struct user *user, struct tuple *tuple)
409+
{
410+
struct priv_def priv;
411+
if (priv_def_create_from_tuple(&priv, tuple) != 0)
412+
return -1;
413+
/**
414+
* Skip role grants, we're only
415+
* interested in real objects.
416+
*/
417+
if (priv.object_type != SC_ROLE || !(priv.access & PRIV_X))
418+
user_grant_priv(user, &priv);
419+
return 0;
420+
}
421+
422+
/**
423+
* Reload user privileges and re-grant them. If a rolled back statement is
424+
* passed, the privileges are reloaded as part of the rollback process, so we
425+
* must account for the changes made by the statement and use the old data
426+
* (which will be the actual data after rollback).
427+
*/
428+
static int
429+
user_reload_privs(struct user *user, struct txn_stmt *rolled_back_stmt)
408430
{
409431
if (user->is_dirty == false)
410432
return 0;
@@ -444,23 +466,42 @@ user_reload_privs(struct user *user)
444466
if (it == NULL)
445467
return -1;
446468
IteratorGuard iter_guard(it);
447-
469+
bool in_rollback = rolled_back_stmt != NULL;
470+
struct tuple *old_tuple = in_rollback ?
471+
rolled_back_stmt->old_tuple : NULL;
472+
struct tuple *new_tuple = in_rollback ?
473+
rolled_back_stmt->new_tuple : NULL;
474+
assert(!in_rollback || old_tuple != NULL || new_tuple != NULL);
475+
struct key_def *key_def = index->def->key_def;
476+
int cmp_old = old_tuple != NULL ?
477+
tuple_compare_with_key(old_tuple, HINT_NONE, key,
478+
1, HINT_NONE, key_def) : 0;
479+
int cmp_new = new_tuple != NULL && old_tuple == NULL ?
480+
tuple_compare_with_key(new_tuple, HINT_NONE, key,
481+
1, HINT_NONE, key_def) : 0;
482+
if (cmp_old != 0 || cmp_new != 0)
483+
in_rollback = false;
484+
assert(old_tuple == NULL || new_tuple == NULL ||
485+
tuple_compare(new_tuple, HINT_NONE, old_tuple, HINT_NONE,
486+
key_def) == 0);
448487
struct tuple *tuple;
449488
if (iterator_next(it, &tuple) != 0)
450489
return -1;
451490
while (tuple != NULL) {
452-
struct priv_def priv;
453-
if (priv_def_create_from_tuple(&priv, tuple) != 0)
491+
if (in_rollback && tuple == new_tuple) {
492+
tuple = old_tuple;
493+
if (tuple == NULL)
494+
goto next_tuple;
495+
}
496+
if (user_reload_priv(user, tuple) != 0)
454497
return -1;
455-
/**
456-
* Skip role grants, we're only
457-
* interested in real objects.
458-
*/
459-
if (priv.object_type != SC_ROLE || !(priv.access & PRIV_X))
460-
user_grant_priv(user, &priv);
498+
next_tuple:
461499
if (iterator_next(it, &tuple) != 0)
462500
return -1;
463501
}
502+
if (in_rollback && new_tuple == NULL &&
503+
user_reload_priv(user, old_tuple) != 0)
504+
return -1;
464505
}
465506
{
466507
/* Take into account privs granted through roles. */
@@ -746,11 +787,13 @@ role_check(struct user *grantee, struct user *role)
746787
}
747788

748789
/**
749-
* Re-calculate effective grants of the linked subgraph
750-
* this user/role is a part of.
790+
* Re-calculate effective grants of the linked subgraph this user/role is a
791+
* part of. For the purpose of the rolled back statement, please refer to
792+
* `user_reload_privs`.
751793
*/
752794
int
753-
rebuild_effective_grants(struct user *grantee)
795+
rebuild_effective_grants(struct user *grantee,
796+
struct txn_stmt *rolled_back_stmt)
754797
{
755798
/*
756799
* Recurse over all roles to which grantee is granted
@@ -801,7 +844,8 @@ rebuild_effective_grants(struct user *grantee)
801844
struct user_map indirect_edges = user->roles;
802845
user_map_minus(&indirect_edges, &transitive_closure);
803846
if (user_map_is_empty(&indirect_edges)) {
804-
if (user_reload_privs(user) != 0)
847+
if (user_reload_privs(user,
848+
rolled_back_stmt) != 0)
805849
return -1;
806850
user_map_union(&next_layer, &user->users);
807851
} else {
@@ -837,7 +881,7 @@ role_grant(struct user *grantee, struct user *role)
837881
{
838882
user_map_set(&role->users, grantee->auth_token);
839883
user_map_set(&grantee->roles, role->auth_token);
840-
if (rebuild_effective_grants(grantee) != 0)
884+
if (rebuild_effective_grants(grantee, NULL) != 0)
841885
return -1;
842886
return 0;
843887
}
@@ -851,13 +895,14 @@ role_revoke(struct user *grantee, struct user *role)
851895
{
852896
user_map_clear(&role->users, grantee->auth_token);
853897
user_map_clear(&grantee->roles, role->auth_token);
854-
if (rebuild_effective_grants(grantee) != 0)
898+
if (rebuild_effective_grants(grantee, NULL) != 0)
855899
return -1;
856900
return 0;
857901
}
858902

859903
int
860-
priv_grant(struct user *grantee, struct priv_def *priv)
904+
priv_grant(struct user *grantee, struct priv_def *priv,
905+
struct txn_stmt *rolled_back_stmt)
861906
{
862907
struct access *object = access_get(priv);
863908
if (object == NULL)
@@ -872,7 +917,7 @@ priv_grant(struct user *grantee, struct priv_def *priv)
872917
struct access *access = &object[grantee->auth_token];
873918
access->granted = priv->access;
874919
access_put(priv, object);
875-
if (rebuild_effective_grants(grantee) != 0)
920+
if (rebuild_effective_grants(grantee, rolled_back_stmt) != 0)
876921
return -1;
877922
return 0;
878923
}

src/box/user.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -232,12 +232,13 @@ int
232232
role_revoke(struct user *grantee, struct user *role);
233233

234234
/**
235-
* Grant or revoke a single privilege to a user or role
236-
* and re-evaluate effective access of all users of this
237-
* role if this role.
235+
* Grant or revoke a single privilege to a user or role and re-evaluate
236+
* effective access of all users of this role if this role. For the purpose of
237+
* the rolled back statement, please refer to `user_reload_privs`.
238238
*/
239239
int
240-
priv_grant(struct user *grantee, struct priv_def *priv);
240+
priv_grant(struct user *grantee, struct priv_def *priv,
241+
struct txn_stmt *rolled_back_stmt);
241242

242243
int
243244
priv_def_create_from_tuple(struct priv_def *priv, struct tuple *tuple);
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
local server = require('luatest.server')
2+
local t = require('luatest')
3+
4+
local g = t.group()
5+
6+
g.before_all(function(cg)
7+
cg.server = server:new()
8+
cg.server:start()
9+
cg.server:exec(function()
10+
local t = box.schema.space.create('test')
11+
t:create_index('pk')
12+
end)
13+
end)
14+
15+
g.after_all(function(cg)
16+
cg.server:drop()
17+
end)
18+
19+
-- Test that rollback of privilege grant works correctly.
20+
g.test_rollback_grant = function(cg)
21+
cg.server:exec(function()
22+
box.schema.user.create('grant')
23+
local msg = "Read access to space 'test' is denied for user 'grant'"
24+
box.session.su('grant', function()
25+
t.assert_error_msg_content_equals(msg, function()
26+
box.space.test:select{}
27+
end)
28+
end)
29+
box.begin()
30+
box.schema.user.grant('grant', 'read', 'space', 'test')
31+
box.rollback()
32+
box.session.su('grant', function()
33+
t.assert_error_msg_content_equals(msg, function()
34+
box.space.test:select{}
35+
end)
36+
end)
37+
end)
38+
end
39+
40+
-- Test that rollback of privilege revoke works correctly.
41+
g.test_rollback_revoke = function(cg)
42+
cg.server:exec(function()
43+
box.schema.user.create('revoke')
44+
box.schema.user.grant('revoke', 'read', 'space', 'test')
45+
box.session.su('revoke', function()
46+
t.assert_equals({}, box.space.test:select{})
47+
end)
48+
box.begin()
49+
box.schema.user.revoke('revoke', 'read', 'space', 'test')
50+
box.rollback()
51+
box.session.su('revoke', function()
52+
t.assert_equals({}, box.space.test:select{})
53+
end)
54+
end)
55+
end
56+
57+
-- Test that rollback of privilege modification works correctly.
58+
g.test_rollback_modify = function(cg)
59+
cg.server:exec(function()
60+
box.schema.user.create('modify')
61+
box.schema.user.grant('modify', 'read', 'space', 'test')
62+
local msg = "Write access to space 'test' is denied for user 'modify'"
63+
box.session.su('modify', function()
64+
t.assert_equals({}, box.space.test:select{})
65+
t.assert_error_msg_content_equals(msg, function()
66+
box.space.test:delete{0}
67+
end)
68+
end)
69+
box.begin()
70+
box.schema.user.grant('modify', 'write', 'space', 'test')
71+
box.rollback()
72+
box.session.su('modify', function()
73+
t.assert_equals({}, box.space.test:select{})
74+
t.assert_error_msg_content_equals(msg, function()
75+
box.space.test:delete{0}
76+
end)
77+
end)
78+
end)
79+
end

0 commit comments

Comments
 (0)