diff --git a/src/box/alter.cc b/src/box/alter.cc index 6f6fcb097d7675bcd805efe1e6af6682436ccfee..48af128f1cae2b269b14c5b37138f4daa39f6fde 100644 --- a/src/box/alter.cc +++ b/src/box/alter.cc @@ -2523,8 +2523,10 @@ priv_def_check(struct priv_def *priv, enum priv_type priv_type) break; case SC_SPACE: { - struct space *space = space_cache_find_xc(priv->object_id); - if (space->def->uid != grantor->def->uid && + struct space *space = NULL; + if (priv->object_id != 0) + space = space_cache_find_xc(priv->object_id); + if ((space == NULL || space->def->uid != grantor->def->uid) && grantor->def->uid != ADMIN) { tnt_raise(AccessDeniedError, priv_name(priv_type), @@ -2535,8 +2537,10 @@ priv_def_check(struct priv_def *priv, enum priv_type priv_type) } case SC_FUNCTION: { - struct func *func = func_cache_find(priv->object_id); - if (func->def->uid != grantor->def->uid && + struct func *func = NULL; + if (priv->object_id != 0) + func = func_cache_find(priv->object_id); + if ((func == NULL || func->def->uid != grantor->def->uid) && grantor->def->uid != ADMIN) { tnt_raise(AccessDeniedError, priv_name(priv_type), @@ -2547,8 +2551,10 @@ priv_def_check(struct priv_def *priv, enum priv_type priv_type) } case SC_SEQUENCE: { - struct sequence *seq = sequence_cache_find(priv->object_id); - if (seq->def->uid != grantor->def->uid && + struct sequence *seq = NULL; + if (priv->object_id != 0) + seq = sequence_cache_find(priv->object_id); + if ((seq == NULL || seq->def->uid != grantor->def->uid) && grantor->def->uid != ADMIN) { tnt_raise(AccessDeniedError, priv_name(priv_type), diff --git a/src/box/call.c b/src/box/call.c index 6388e1e68fa472b8b4b4e7d7d81ec2eea7e99b9c..438be190ed336c747803e1e4431a89a70fc966a2 100644 --- a/src/box/call.c +++ b/src/box/call.c @@ -71,6 +71,8 @@ access_check_func(const char *name, uint32_t name_len, struct func **funcp) return 0; } user_access_t access = PRIV_X | PRIV_U; + /* Check access for all functions. */ + access &= ~entity_access_get(SC_FUNCTION)[credentials->auth_token].effective; user_access_t func_access = access & ~credentials->universal_access; if (func == NULL || /* Check for missing Usage access, ignore owner rights. */ diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua index 55018d6b60e219574e27b5f929434d99a4154927..2b0597708dca4192bd1a0e8913f13eab8684c5d4 100644 --- a/src/box/lua/schema.lua +++ b/src/box/lua/schema.lua @@ -1804,6 +1804,9 @@ local function object_resolve(object_type, object_name) return 0 end if object_type == 'space' then + if object_name == nil or object_name == 0 then + return 0 + end local space = box.space[object_name] if space == nil then box.error(box.error.NO_SUCH_SPACE, object_name) @@ -1811,6 +1814,9 @@ local function object_resolve(object_type, object_name) return space.id end if object_type == 'function' then + if object_name == nil or object_name == 0 then + return 0 + end local _vfunc = box.space[box.schema.VFUNC_ID] local func if type(object_name) == 'string' then @@ -1825,6 +1831,9 @@ local function object_resolve(object_type, object_name) end end if object_type == 'sequence' then + if object_name == nil or object_name == 0 then + return 0 + end local seq = sequence_resolve(object_name) if seq == nil then box.error(box.error.NO_SUCH_SEQUENCE, object_name) diff --git a/src/box/schema.cc b/src/box/schema.cc index 8df4aa73bb41005201aafa296d71d05d8978f8e5..32d7791a66f1c15be650a2c488e63f157db6f275 100644 --- a/src/box/schema.cc +++ b/src/box/schema.cc @@ -69,6 +69,8 @@ struct rlist on_alter_sequence = RLIST_HEAD_INITIALIZER(on_alter_sequence); */ struct latch schema_lock = LATCH_INITIALIZER(schema_lock); +struct entity_access entity_access; + bool space_is_system(struct space *space) { @@ -528,6 +530,8 @@ schema_find_name(enum schema_object_type type, uint32_t object_id) return ""; case SC_SPACE: { + if (object_id == 0) + return "SPACE"; struct space *space = space_by_id(object_id); if (space == NULL) break; @@ -535,6 +539,8 @@ schema_find_name(enum schema_object_type type, uint32_t object_id) } case SC_FUNCTION: { + if (object_id == 0) + return "FUNCTION"; struct func *func = func_by_id(object_id); if (func == NULL) break; @@ -542,6 +548,8 @@ schema_find_name(enum schema_object_type type, uint32_t object_id) } case SC_SEQUENCE: { + if (object_id == 0) + return "SEQUENCE"; struct sequence *seq = sequence_by_id(object_id); if (seq == NULL) break; diff --git a/src/box/schema.h b/src/box/schema.h index 2b87f5f500d3a9a61cba8d3b345b3c023d9094cf..dd31d2d9daafb3e263f7a3b56d4eeef344622ccd 100644 --- a/src/box/schema.h +++ b/src/box/schema.h @@ -235,4 +235,30 @@ struct on_access_denied_ctx { const char *object_name; }; +/** Global grants to classes of objects. */ +struct entity_access { + struct access space[BOX_USER_MAX]; + struct access function[BOX_USER_MAX]; + struct access sequence[BOX_USER_MAX]; +}; + +/** A single instance of the global entities. */ +extern struct entity_access entity_access; + +static inline +struct access * +entity_access_get(enum schema_object_type type) +{ + switch (type) { + case SC_SPACE: + return entity_access.space; + case SC_FUNCTION: + return entity_access.function; + case SC_SEQUENCE: + return entity_access.sequence; + default: + return NULL; + } +} + #endif /* INCLUDES_TARANTOOL_BOX_SCHEMA_H */ diff --git a/src/box/sequence.c b/src/box/sequence.c index 162147cdedbb8266d6c8d42a170325bd63ba9cb2..35b7605d22f1eaeec298672bd93bab5f9e25c2e7 100644 --- a/src/box/sequence.c +++ b/src/box/sequence.c @@ -250,6 +250,7 @@ access_check_sequence(struct sequence *seq) user_access_t access = PRIV_U | PRIV_W; user_access_t sequence_access = access & ~cr->universal_access; + sequence_access &= ~entity_access_get(SC_SEQUENCE)[cr->auth_token].effective; if (sequence_access && /* Check for missing Usage access, ignore owner rights. */ (sequence_access & PRIV_U || diff --git a/src/box/space.c b/src/box/space.c index 0510f232d263887393ed437b952ac708f76bdcb9..631877699eeef0e827220389175eaf274122e4d6 100644 --- a/src/box/space.c +++ b/src/box/space.c @@ -42,6 +42,7 @@ #include "request.h" #include "xrow.h" #include "iproto_constants.h" +#include "schema.h" int access_check_space(struct space *space, user_access_t access) @@ -57,6 +58,7 @@ access_check_space(struct space *space, user_access_t access) * since ADMIN has universal access. */ user_access_t space_access = access & ~cr->universal_access; + space_access &= ~entity_access_get(SC_SPACE)[cr->auth_token].effective; if (space_access && /* Check for missing Usage access, ignore owner rights. */ diff --git a/src/box/sysview_index.c b/src/box/sysview_index.c index f1dfbefe5abcd08ba4eefd9a5cfbbb9e38f3ea5e..3e56d1fa90f6f2b3f35db115cb1004709181b73e 100644 --- a/src/box/sysview_index.c +++ b/src/box/sysview_index.c @@ -222,6 +222,9 @@ vspace_filter(struct space *source, struct tuple *tuple) */ if (PRIV_WRDA & cr->universal_access) return true; + /* Allow access for a user with space privileges. */ + if (PRIV_WRDA & entity_access_get(SC_SPACE)[cr->auth_token].effective) + return true; if (PRIV_R & source->access[cr->auth_token].effective) return true; /* read access to _space space */ uint32_t space_id; @@ -295,6 +298,10 @@ vfunc_filter(struct space *source, struct tuple *tuple) */ if ((PRIV_WRDA | PRIV_X) & cr->universal_access) return true; + /* Allow access for a user with function privileges. */ + if ((PRIV_WRDA | PRIV_X) & + entity_access_get(SC_FUNCTION)[cr->auth_token].effective) + return true; if (PRIV_R & source->access[cr->auth_token].effective) return true; /* read access to _func space */ @@ -320,6 +327,10 @@ vsequence_filter(struct space *source, struct tuple *tuple) */ if ((PRIV_WRDA | PRIV_X) & cr->universal_access) return true; + /* Allow access for a user with sequence privileges. */ + if ((PRIV_WRDA | PRIV_X) & + entity_access_get(SC_SEQUENCE)[cr->auth_token].effective) + return true; if (PRIV_R & source->access[cr->auth_token].effective) return true; /* read access to _sequence space */ diff --git a/src/box/user.cc b/src/box/user.cc index 7fa66da8f1c11d8a3a9d3d40b2b5cac265d6e201..fbf06566a7df4e43346686b76d3c7a91543a7ca1 100644 --- a/src/box/user.cc +++ b/src/box/user.cc @@ -209,6 +209,10 @@ access_find(struct priv_def *priv) } case SC_SPACE: { + if (priv->object_id == 0) { + access = entity_access.space; + break; + } struct space *space = space_by_id(priv->object_id); if (space) access = space->access; @@ -216,6 +220,10 @@ access_find(struct priv_def *priv) } case SC_FUNCTION: { + if (priv->object_id == 0) { + access = entity_access.function; + break; + } struct func *func = func_by_id(priv->object_id); if (func) access = func->access; @@ -223,6 +231,10 @@ access_find(struct priv_def *priv) } case SC_SEQUENCE: { + if (priv->object_id == 0) { + access = entity_access.sequence; + break; + } struct sequence *seq = sequence_by_id(priv->object_id); if (seq) access = seq->access; diff --git a/test/box/access.result b/test/box/access.result index 72f91173b19e0c3deef99fe9efbcef160315c43a..ccd0b737ff4ceb5eabd7c06de4e1f924ffc824d3 100644 --- a/test/box/access.result +++ b/test/box/access.result @@ -1662,3 +1662,80 @@ box.schema.user.grant("guest", "read,write,execute", "role") --- - error: Unsupported role privilege 'read,write,execute' ... +-- Check entities DML +box.schema.user.create("tester", { password = '123' }) +--- +... +s = box.schema.space.create("test") +--- +... +_ = s:create_index("primary", {parts={1, "unsigned"}}) +--- +... +seq = box.schema.sequence.create("test") +--- +... +box.schema.func.create("func") +--- +... +c = (require 'net.box').connect(LISTEN.host, LISTEN.service, {user='tester', password = '123'}) +--- +... +box.session.su("tester", s.select, s) +--- +- error: Read access to space 'test' is denied for user 'tester' +... +box.session.su("tester", seq.set, seq, 1) +--- +- error: Write access to sequence 'test' is denied for user 'tester' +... +c:call("func") +--- +- error: Execute access to function 'func' is denied for user 'tester' +... +box.schema.user.grant("tester", "read", "space") +--- +... +box.schema.user.grant("tester", "write", "sequence") +--- +... +box.schema.user.grant("tester", "execute", "function") +--- +... +box.session.su("tester", s.select, s) +--- +- [] +... +box.session.su("tester", seq.next, seq) +--- +- 1 +... +c:call("func") +--- +... +box.session.su("tester", s.insert, s, {1}) +--- +- error: Write access to space 'test' is denied for user 'tester' +... +box.schema.user.grant("tester", "write", "space") +--- +... +box.session.su("tester", s.insert, s, {1}) +--- +- [1] +... +box.schema.user.drop("tester") +--- +... +s:drop() +--- +... +seq:drop() +--- +... +box.schema.func.drop("func") +--- +... +c:close() +--- +... diff --git a/test/box/access.test.lua b/test/box/access.test.lua index 62691c471844ce557d605d80da02fcba78902240..8d90b3813382bf42790054028536d989cc85fd08 100644 --- a/test/box/access.test.lua +++ b/test/box/access.test.lua @@ -640,3 +640,32 @@ box.schema.user.grant("guest", "alter", "function") box.schema.user.grant("guest", "execute", "sequence") box.schema.user.grant("guest", "read,execute", "sequence") box.schema.user.grant("guest", "read,write,execute", "role") + +-- Check entities DML +box.schema.user.create("tester", { password = '123' }) +s = box.schema.space.create("test") +_ = s:create_index("primary", {parts={1, "unsigned"}}) +seq = box.schema.sequence.create("test") +box.schema.func.create("func") +c = (require 'net.box').connect(LISTEN.host, LISTEN.service, {user='tester', password = '123'}) + +box.session.su("tester", s.select, s) +box.session.su("tester", seq.set, seq, 1) +c:call("func") +box.schema.user.grant("tester", "read", "space") +box.schema.user.grant("tester", "write", "sequence") +box.schema.user.grant("tester", "execute", "function") +box.session.su("tester", s.select, s) +box.session.su("tester", seq.next, seq) +c:call("func") + +box.session.su("tester", s.insert, s, {1}) +box.schema.user.grant("tester", "write", "space") +box.session.su("tester", s.insert, s, {1}) + +box.schema.user.drop("tester") +s:drop() +seq:drop() +box.schema.func.drop("func") +c:close() + diff --git a/test/box/lua/identifier.lua b/test/box/lua/identifier.lua index c3149bce7920cb27b163c46c878fd0c6c25444eb..0cfb9e722a208ef25b806a6ba0277a77d2ea3cf5 100644 --- a/test/box/lua/identifier.lua +++ b/test/box/lua/identifier.lua @@ -36,7 +36,6 @@ invalid_testcases = { function run_test(create_func, cleanup_func) local json = require("json") - print("loosadlalsd") local bad_tests = {} for i, identifier in ipairs(valid_testcases) do local ok, res = pcall(create_func,identifier)