Hi! Thankyou for review! Please see my answers regarding extra code in grant/revoke and object_resolve functions below. The new diff is also below. > 7 авг. 2018 г., в 19:38, Vladimir Davydov написал(а): > > On Thu, Aug 02, 2018 at 01:55:58PM +0300, Serge Petrenko wrote: >> When granting or revoking a privilege on an entire entity, id 0 was used >> to indicate the fact that we don't grant a privilege on a single object, >> but on a whole entity. This caused confusion, because for entity USER, >> for example, id 0 is a valid object id (user 'guest' uses it). >> Any non-zero id dedicated to this cause obviously may be confused as well. >> Fix this by creating separate schema_object_types for entities: >> SC_ENTITY_SPACE, SC_ENTITY_USER, etc. >> >> Closes: #3574 >> Prerequisite: #3524 >> --- >> https://github.com/tarantool/tarantool/tree/sergepetrenko/gh-3574-whole-entity-types >> https://github.com/tarantool/tarantool/issues/3574 >> >> Changes in v2: >> - keep only old syntax for granting access >> to all entities >> - add an upgrade script to alter indices of spaces >> _priv and _vpriv to store 'scalar' in object_id field, >> and use an asterisk ('*') in object_id to indicate >> granting on an entire entity. > > Hmm, I wonder what happens if someone creates an object (say space) and > names it "*» … It is ok, when operating with a space named ‘*’, ‘*’ is in object_name, when operating on a whole entity, ‘*’ is set internally in object_id field. So there are no conflicts here. > >> - keep the new entity types in priv_def and use them >> internally. >> >> src/box/alter.cc | 27 ++++++++++++++++++++++++++- >> src/box/bootstrap.snap | Bin 1540 -> 1557 bytes >> src/box/lua/schema.lua | 41 ++++++++++++++++++++++++----------------- >> src/box/lua/upgrade.lua | 37 +++++++++++++++++++++++++++++++++++++ >> src/box/schema.cc | 18 ++++++++++++++++++ >> src/box/schema.h | 23 +++++++++++++---------- >> src/box/schema_def.c | 39 +++++++++++++++++++++++++++++++-------- >> src/box/schema_def.h | 28 ++++++++++++++++++++++------ >> src/box/user.cc | 27 +++++++++++++++------------ >> test/box-py/bootstrap.result | 14 +++++++------- >> test/box/access.result | 6 +++--- >> test/box/access_misc.result | 8 ++++---- >> test/box/alter.result | 8 ++++---- >> test/xlog/upgrade.result | 14 +++++++------- >> 14 files changed, 211 insertions(+), 79 deletions(-) >> >> diff --git a/src/box/alter.cc b/src/box/alter.cc >> index 3007a131d..6057b66c9 100644 >> --- a/src/box/alter.cc >> +++ b/src/box/alter.cc >> @@ -2529,6 +2529,31 @@ on_replace_dd_collation(struct trigger * /* trigger */, void *event) >> } >> } >> >> +void >> +priv_def_try_fill(struct priv_def *priv, struct tuple *tuple) > > This function should be static. Anyway, I don't think it's worth > factoring this code out, because it's pretty straightforward and > executed only in priv_def_create_form_tuple(). > I removed the function. >> +{ >> + const char *object_id = tuple_field(tuple, BOX_PRIV_FIELD_OBJECT_ID); >> + if (object_id == NULL) { >> + tnt_raise(ClientError, ER_NO_SUCH_FIELD, >> + BOX_PRIV_FIELD_OBJECT_ID + TUPLE_INDEX_BASE); >> + } > > Please use tuple_field_u32() and tuple_field_str() instead. > >> + /* >> + * When granting or revoking privileges on a whole entity we >> + * pass an asterisk ('*') to object_id to indicate grant on every >> + * object of that entity. So check for that first. >> + */ >> + if (mp_typeof(*object_id) == MP_STR) { > > Shouldn't you check that the string is actually "*»? Added a check > >> + priv->object_id = 0; >> + priv->object_type = schema_entity_type(priv->object_type); > > This assumes that priv->object_type is initialized before > priv_def_try_fill(). IMO this makes the function protocol > rather abstruse. Another reason not to factor it out. > Done. >> + } else if (mp_typeof(*object_id) == MP_UINT) { >> + priv->object_id = mp_decode_uint(&object_id); >> + } else { >> + tnt_raise(ClientError, ER_FIELD_TYPE, >> + BOX_PRIV_FIELD_OBJECT_ID + TUPLE_INDEX_BASE, >> + field_type_strs[FIELD_TYPE_UNSIGNED]); >> + } >> +} >> + >> /** >> * Create a privilege definition from tuple. >> */ >> @@ -2539,8 +2564,8 @@ priv_def_create_from_tuple(struct priv_def *priv, struct tuple *tuple) >> priv->grantee_id = tuple_field_u32_xc(tuple, BOX_PRIV_FIELD_UID); >> const char *object_type = >> tuple_field_cstr_xc(tuple, BOX_PRIV_FIELD_OBJECT_TYPE); >> - priv->object_id = tuple_field_u32_xc(tuple, BOX_PRIV_FIELD_OBJECT_ID); >> priv->object_type = schema_object_type(object_type); >> + priv_def_try_fill(priv, tuple); >> if (priv->object_type == SC_UNKNOWN) { >> tnt_raise(ClientError, ER_UNKNOWN_SCHEMA_OBJECT, >> object_type); >> diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua >> index b9b8c9004..294dcf4d3 100644 >> --- a/src/box/lua/schema.lua >> +++ b/src/box/lua/schema.lua >> @@ -1809,9 +1809,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 >> + if object_name == nil then >> + return '*' >> + end > > Bad indentation - we use spaces in Lua code, not tabs. > Fixed. >> local space = box.space[object_name] >> if space == nil then >> box.error(box.error.NO_SUCH_SPACE, object_name) >> @@ -1819,9 +1819,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 >> + if object_name == nil then >> + return '*' >> + end > > Ditto Fixed > >> local _vfunc = box.space[box.schema.VFUNC_ID] >> local func >> if type(object_name) == 'string' then >> @@ -1836,8 +1836,8 @@ 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 >> + if object_name == nil then >> + return '*' >> end >> local seq = sequence_resolve(object_name) >> if seq == nil then >> @@ -1846,6 +1846,9 @@ local function object_resolve(object_type, object_name) >> return seq >> end >> if object_type == 'role' then >> + if object_name == nil then >> + return '*' >> + end > > Ditto. > Fixed >> local _vuser = box.space[box.schema.VUSER_ID] >> local role >> if type(object_name) == 'string' then >> @@ -1867,6 +1870,9 @@ local function object_name(object_type, object_id) >> if object_type == 'universe' then >> return "" >> end >> + if object_id == '*' then >> + return '*' >> + end >> local space >> if object_type == 'space' then >> space = box.space._vspace >> @@ -2079,9 +2085,8 @@ local function grant(uid, name, privilege, object_type, >> object_name = privilege >> privilege = 'execute' >> end >> - local privilege_hex = privilege_check(privilege, object_type) >> - >> local oid = object_resolve(object_type, object_name) >> + local privilege_hex = privilege_check(privilege, object_type) > > I don't understand why you need this. Removed, no need to do this. > >> options = options or {} >> if options.grantor == nil then >> options.grantor = session.euid() >> @@ -2106,10 +2111,10 @@ local function grant(uid, name, privilege, object_type, >> _priv:replace{options.grantor, uid, object_type, oid, privilege_hex} >> elseif not options.if_not_exists then >> if object_type == 'role' then >> - box.error(box.error.ROLE_GRANTED, name, object_name) >> + box.error(box.error.ROLE_GRANTED, name, object_name or '*') >> else >> box.error(box.error.PRIV_GRANTED, name, privilege, >> - object_type, object_name) >> + object_type, object_name or '*') > > Instead you could set object_name to '*' in the beginning of > grant/revoke functions. This would make your patch shorter, > because you wouldn't have to do it here, nor in object_resolve. > If I set object_name to ‘*’ the case of granting to an entity as a whole will Be indistinguishable from granting to an object named ‘*’. So I have to handle This case like I did. I changed the code in object_resolve a little bit to save some Extra lines. >> end >> end >> end >> @@ -2122,9 +2127,9 @@ local function revoke(uid, name, privilege, object_type, object_name, options) >> object_name = privilege >> privilege = 'execute' >> end >> - local privilege_hex = privilege_check(privilege, object_type) >> options = options or {} >> local oid = object_resolve(object_type, object_name) >> + local privilege_hex = privilege_check(privilege, object_type) > > I don't understand why you need this. Removed. > >> local _priv = box.space[box.schema.PRIV_ID] >> local _vpriv = box.space[box.schema.VPRIV_ID] >> local tuple = _vpriv:get{uid, object_type, oid} >> @@ -2134,10 +2139,10 @@ local function revoke(uid, name, privilege, object_type, object_name, options) >> return >> end >> if object_type == 'role' then >> - box.error(box.error.ROLE_NOT_GRANTED, name, object_name) >> + box.error(box.error.ROLE_NOT_GRANTED, name, object_name or '*') >> else >> box.error(box.error.PRIV_NOT_GRANTED, name, privilege, >> - object_type, object_name) >> + object_type, object_name or '*') >> end >> end >> local old_privilege = tuple[5] >> @@ -2153,13 +2158,14 @@ local function revoke(uid, name, privilege, object_type, object_name, options) >> return >> end >> box.error(box.error.PRIV_NOT_GRANTED, name, privilege, >> - object_type, object_name) >> + object_type, object_name or '*') > > This hunk as well as the one above wouldn't be needed if you set > object_name to '*' in the beginning of the function (also, see my > comment to grant function). Answered above. > >> end >> if privilege_hex ~= 0 then >> _priv:replace{grantor, uid, object_type, oid, privilege_hex} >> else >> _priv:delete{uid, object_type, oid} >> end >> + > > Extraneous change. Please remove. Done > >> end >> >> local function drop(uid, opts) >> @@ -2194,7 +2200,8 @@ local function drop(uid, opts) >> for k, tuple in pairs(privs) do >> -- we need an additional box.session.su() here, because of >> -- unnecessary check for privilege PRIV_REVOKE in priv_def_check() >> - box.session.su("admin", revoke, uid, uid, tuple[5], tuple[3], tuple[4]) >> + local oid = tuple[4] ~= '*' and tuple[4] or nil >> + box.session.su("admin", revoke, uid, uid, tuple[5], tuple[3], oid) > > Why? The 'revoke' function should be able to interpret '*' correctly. > No, answered above. >> end >> box.space[box.schema.USER_ID]:delete{uid} >> end >> diff --git a/src/box/lua/upgrade.lua b/src/box/lua/upgrade.lua >> index 0293f6ef8..4acf20152 100644 >> --- a/src/box/lua/upgrade.lua >> +++ b/src/box/lua/upgrade.lua >> @@ -964,6 +964,42 @@ local function upgrade_to_1_10_0() >> create_vsequence_space() >> end >> >> +-------------------------------------------------------------------------------- >> +--- Tarantool 1.10.1 >> +-------------------------------------------------------------------------------- >> +local function upgrade_space_priv_to_1_10_1() >> + local _priv = box.space._priv >> + local _vpriv = box.space._vpriv >> + local f = _priv:format() >> + >> + f[4].type = 'scalar' >> + _priv:format(f) >> + f = _vpriv:format() >> + f[4].type = 'scalar' >> + _vpriv:format(f) >> + _priv.index.primary:alter{parts={2, 'unsigned', 3, 'string', 4, 'scalar'}} >> + _vpriv.index.primary:alter{parts={2, 'unsigned', 3, 'string', 4, 'scalar'}} >> + _priv.index.object:alter{parts={3, 'string', 4, 'scalar'}} >> + _vpriv.index.object:alter{parts={3, 'string', 4, 'scalar'}} >> +end >> + >> +local function upgrade_privs_to_1_10_1() > > No need to split this code in two functions. > >> + local _priv = box.space._priv >> + >> + for _, priv in _priv:pairs() do > > AFAICS interpretation of '0' as 'all objects of a kind' was added after > 1.10.1 i.e. there was no Tarantool release that would use this feature. > So I guess you don't need to upgrade existing privileges, neither do you > need to preserve any code that handles '0' object id. Please consult > with Georgy and Kostja regarding this. > >> + if priv[4] == 0 then >> + if priv[3] ~= 'universe' and priv[3] ~= 'user' and priv[3] ~= 'role' then > > Please rewrite it as > > if priv[4] == 0 and priv[3] ~= 'universe' and ... then > >> + _priv:delete{priv[2], priv[3], priv[4]} >> + _priv:insert{priv[1], priv[2], priv[3], '*', priv[5]} > > Bad indentation - we use spaces in Lua code, not tabs. > Removed the privilege upgrade function together with some leftover code handling Id 0. >> + end >> + end >> + end >> +end >> + >> +local function upgrade_to_1_10_1() >> + upgrade_space_priv_to_1_10_1() >> + upgrade_privs_to_1_10_1() >> +end >> >> local function get_version() >> local version = box.space._schema:get{'version'} >> @@ -991,6 +1027,7 @@ local function upgrade(options) >> {version = mkversion(1, 7, 6), func = upgrade_to_1_7_6, auto = false}, >> {version = mkversion(1, 7, 7), func = upgrade_to_1_7_7, auto = true}, >> {version = mkversion(1, 10, 0), func = upgrade_to_1_10_0, auto = true}, >> + {version = mkversion(1, 10, 1), func = upgrade_to_1_10_1, auto = true}, >> } >> >> for _, handler in ipairs(handlers) do >> diff --git a/src/box/schema.cc b/src/box/schema.cc >> index 433f52c08..02f55aaad 100644 >> --- a/src/box/schema.cc >> +++ b/src/box/schema.cc >> @@ -536,8 +536,26 @@ schema_find_name(enum schema_object_type type, uint32_t object_id) >> switch (type) { >> case SC_UNIVERSE: >> return ""; >> + case SC_ENTITY_SPACE: >> + return "SPACE"; >> + case SC_ENTITY_FUNCTION: >> + return "FUNCTION"; >> + case SC_ENTITY_SEQUENCE: >> + return "SEQUENCE"; >> + case SC_ENTITY_ROLE: >> + return "ROLE"; >> + case SC_ENTITY_USER: >> + return "USER"; > > Shouldn't it be "*»? It should, indeed. Fixed. >> case SC_SPACE: >> { >> + /* >> + * Even though we have a separate type >> + * for grants on entire entity, we have to >> + * leave the check for an empty object_id in place, >> + * because without it recover from an old version WAL >> + * fails even before the upgrade script is being run. >> + * Same applies to SC_FUNCTION and SC_SEQUENCE below. >> + */ >> if (object_id == 0) >> return "SPACE"; >> struct space *space = space_by_id(object_id); >> diff --git a/src/box/schema.h b/src/box/schema.h >> index 0822262d0..f1735ff34 100644 >> --- a/src/box/schema.h >> +++ b/src/box/schema.h >> @@ -250,16 +250,19 @@ 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; >> - } >> + switch (type) { >> + case SC_SPACE: >> + case SC_ENTITY_SPACE: >> + return entity_access.space; >> + case SC_FUNCTION: >> + case SC_ENTITY_FUNCTION: >> + return entity_access.function; >> + case SC_SEQUENCE: >> + case SC_ENTITY_SEQUENCE: >> + return entity_access.sequence; >> + default: >> + return NULL; >> + } >> } >> >> #endif /* INCLUDES_TARANTOOL_BOX_SCHEMA_H */ >> diff --git a/src/box/schema_def.c b/src/box/schema_def.c >> index 97c074ab2..51d418e42 100644 >> --- a/src/box/schema_def.c >> +++ b/src/box/schema_def.c >> @@ -31,16 +31,39 @@ >> #include "schema_def.h" >> >> static const char *object_type_strs[] = { >> - /* [SC_UKNNOWN] = */ "unknown", >> - /* [SC_UNIVERSE] = */ "universe", >> - /* [SC_SPACE] = */ "space", >> - /* [SC_FUNCTION] = */ "function", >> - /* [SC_USER] = */ "user", >> - /* [SC_ROLE] = */ "role", >> - /* [SC_SEQUENCE] = */ "sequence", >> - /* [SC_COLLATION] = */ "collation", >> + /* [SC_UKNNOWN] = */ "unknown", >> + /* [SC_UNIVERSE] = */ "universe", >> + /* [SC_SPACE] = */ "space", >> + /* [SC_ENTITY_SPACE] = */ "all spaces", >> + /* [SC_FUNCTION] = */ "function", >> + /* [SC_ENTITY_FUNCTION] = */ "all functions", >> + /* [SC_USER] = */ "user", >> + /* [SC_ENTITY_USER] = */ "all users", >> + /* [SC_ROLE] = */ "role", >> + /* [SC_ENTITY_ROLE] = */ "all roles", >> + /* [SC_SEQUENCE] = */ "sequence", >> + /* [SC_ENTITY_SEQUENCE] = */ "all sequences", >> + /* [SC_COLLATION] = */ "collation", >> + /* [SC_ENTITY_COLLATION] = */ "all collations", > > Why? I don't think that you need to report "all spaces" anywhere. fixed. Here’s the new diff: src/box/alter.cc | 33 ++++++++++++++++++++++++++++++++- src/box/bootstrap.snap | Bin 1540 -> 1557 bytes src/box/lua/schema.lua | 43 ++++++++++++++++++++++--------------------- src/box/lua/upgrade.lua | 23 +++++++++++++++++++++++ src/box/schema.cc | 16 ++++++++++------ src/box/schema.h | 23 +++++++++++++---------- src/box/schema_def.c | 39 +++++++++++++++++++++++++++++++-------- src/box/schema_def.h | 28 ++++++++++++++++++++++------ src/box/user.cc | 27 +++++++++++++++------------ test/box-py/bootstrap.result | 14 +++++++------- test/box/access.result | 6 +++--- test/box/access_misc.result | 8 ++++---- test/box/alter.result | 8 ++++---- test/xlog/upgrade.result | 14 +++++++------- 14 files changed, 193 insertions(+), 89 deletions(-) diff --git a/src/box/alter.cc b/src/box/alter.cc index 3007a131d..7d65cab3a 100644 --- a/src/box/alter.cc +++ b/src/box/alter.cc @@ -2537,10 +2537,41 @@ priv_def_create_from_tuple(struct priv_def *priv, struct tuple *tuple) { priv->grantor_id = tuple_field_u32_xc(tuple, BOX_PRIV_FIELD_ID); priv->grantee_id = tuple_field_u32_xc(tuple, BOX_PRIV_FIELD_UID); + const char *object_type = tuple_field_cstr_xc(tuple, BOX_PRIV_FIELD_OBJECT_TYPE); - priv->object_id = tuple_field_u32_xc(tuple, BOX_PRIV_FIELD_OBJECT_ID); priv->object_type = schema_object_type(object_type); + + const char *object_id = tuple_field(tuple, BOX_PRIV_FIELD_OBJECT_ID); + if (object_id == NULL) { + tnt_raise(ClientError, ER_NO_SUCH_FIELD, + BOX_PRIV_FIELD_OBJECT_ID + TUPLE_INDEX_BASE); + } + /* + * When granting or revoking privileges on a whole entity + * we pass an asterisk ('*') to object_id to indicate + * grant on every object of that entity. + * So check for that first. + */ + if (mp_typeof(*object_id) == MP_STR) { + object_id = tuple_field_cstr_xc(tuple, BOX_PRIV_FIELD_OBJECT_ID); + if(*object_id == '*' && strlen(object_id) == 1) { + priv->object_id = 0; + priv->object_type = schema_entity_type(priv->object_type); + } else { + tnt_raise(ClientError, ER_FIELD_TYPE, + BOX_PRIV_FIELD_OBJECT_ID + TUPLE_INDEX_BASE, + field_type_strs[FIELD_TYPE_UNSIGNED]); + } + } else if (mp_typeof(*object_id) == MP_UINT) { + priv->object_id = tuple_field_u32_xc(tuple, + BOX_PRIV_FIELD_OBJECT_ID); + } else { + tnt_raise(ClientError, ER_FIELD_TYPE, + BOX_PRIV_FIELD_OBJECT_ID + TUPLE_INDEX_BASE, + field_type_strs[FIELD_TYPE_UNSIGNED]); + } + if (priv->object_type == SC_UNKNOWN) { tnt_raise(ClientError, ER_UNKNOWN_SCHEMA_OBJECT, object_type); diff --git a/src/box/bootstrap.snap b/src/box/bootstrap.snap index b610828c9c9ae9a22acdd8c150c16c6838b7a273..da6961d1be7621fe9821623c7c2d57125e8ac7bc 100644 GIT binary patch delta 1552 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 diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua index b9b8c9004..361810c79 100644 --- a/src/box/lua/schema.lua +++ b/src/box/lua/schema.lua @@ -1801,17 +1801,19 @@ local function privilege_name(privilege) end local function object_resolve(object_type, object_name) + if object_name ~= nil and type(object_name) ~= 'string' + and type(object_name) ~= 'number' then + box.error(box.error.ILLEGAL_PARAMS, "wrong object name type") + end if object_type == 'universe' then - if object_name ~= nil and type(object_name) ~= 'string' - and type(object_name) ~= 'number' then - box.error(box.error.ILLEGAL_PARAMS, "wrong object name type") - end return 0 end + if (object_type == 'space' or object_type == 'function' or + object_type == 'sequence' or object_type == 'role') and + object_name == nil then + return '*' + 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) @@ -1819,9 +1821,6 @@ 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 @@ -1836,9 +1835,6 @@ 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) @@ -1867,6 +1863,9 @@ local function object_name(object_type, object_id) if object_type == 'universe' then return "" end + if object_id == '*' then + return '*' + end local space if object_type == 'space' then space = box.space._vspace @@ -2106,10 +2105,10 @@ local function grant(uid, name, privilege, object_type, _priv:replace{options.grantor, uid, object_type, oid, privilege_hex} elseif not options.if_not_exists then if object_type == 'role' then - box.error(box.error.ROLE_GRANTED, name, object_name) + box.error(box.error.ROLE_GRANTED, name, object_name or '*') else box.error(box.error.PRIV_GRANTED, name, privilege, - object_type, object_name) + object_type, object_name or '*') end end end @@ -2134,10 +2133,10 @@ local function revoke(uid, name, privilege, object_type, object_name, options) return end if object_type == 'role' then - box.error(box.error.ROLE_NOT_GRANTED, name, object_name) + box.error(box.error.ROLE_NOT_GRANTED, name, object_name or '*') else box.error(box.error.PRIV_NOT_GRANTED, name, privilege, - object_type, object_name) + object_type, object_name or '*') end end local old_privilege = tuple[5] @@ -2153,13 +2152,14 @@ local function revoke(uid, name, privilege, object_type, object_name, options) return end box.error(box.error.PRIV_NOT_GRANTED, name, privilege, - object_type, object_name) + object_type, object_name or '*') end if privilege_hex ~= 0 then _priv:replace{grantor, uid, object_type, oid, privilege_hex} else _priv:delete{uid, object_type, oid} end + end local function drop(uid, opts) @@ -2192,9 +2192,10 @@ local function drop(uid, opts) local privs = _vpriv.index.primary:select{uid} for k, tuple in pairs(privs) do - -- we need an additional box.session.su() here, because of - -- unnecessary check for privilege PRIV_REVOKE in priv_def_check() - box.session.su("admin", revoke, uid, uid, tuple[5], tuple[3], tuple[4]) + -- we need an additional box.session.su() here, because of + -- unnecessary check for privilege PRIV_REVOKE in priv_def_check() + local oid = tuple[4] ~= '*' and tuple[4] or nil + box.session.su("admin", revoke, uid, uid, tuple[5], tuple[3], oid) end box.space[box.schema.USER_ID]:delete{uid} end diff --git a/src/box/lua/upgrade.lua b/src/box/lua/upgrade.lua index 0293f6ef8..5dbc09bbb 100644 --- a/src/box/lua/upgrade.lua +++ b/src/box/lua/upgrade.lua @@ -964,6 +964,28 @@ local function upgrade_to_1_10_0() create_vsequence_space() end +-------------------------------------------------------------------------------- +--- Tarantool 1.10.1 +-------------------------------------------------------------------------------- +local function upgrade_space_priv_to_1_10_1() + local _priv = box.space._priv + local _vpriv = box.space._vpriv + local f = _priv:format() + + f[4].type = 'scalar' + _priv:format(f) + f = _vpriv:format() + f[4].type = 'scalar' + _vpriv:format(f) + _priv.index.primary:alter{parts={2, 'unsigned', 3, 'string', 4, 'scalar'}} + _vpriv.index.primary:alter{parts={2, 'unsigned', 3, 'string', 4, 'scalar'}} + _priv.index.object:alter{parts={3, 'string', 4, 'scalar'}} + _vpriv.index.object:alter{parts={3, 'string', 4, 'scalar'}} +end + +local function upgrade_to_1_10_1() + upgrade_space_priv_to_1_10_1() +end local function get_version() local version = box.space._schema:get{'version'} @@ -991,6 +1013,7 @@ local function upgrade(options) {version = mkversion(1, 7, 6), func = upgrade_to_1_7_6, auto = false}, {version = mkversion(1, 7, 7), func = upgrade_to_1_7_7, auto = true}, {version = mkversion(1, 10, 0), func = upgrade_to_1_10_0, auto = true}, + {version = mkversion(1, 10, 1), func = upgrade_to_1_10_1, auto = true}, } for _, handler in ipairs(handlers) do diff --git a/src/box/schema.cc b/src/box/schema.cc index 433f52c08..9958e9016 100644 --- a/src/box/schema.cc +++ b/src/box/schema.cc @@ -536,10 +536,18 @@ schema_find_name(enum schema_object_type type, uint32_t object_id) switch (type) { case SC_UNIVERSE: return ""; + case SC_ENTITY_SPACE: + return ""; + case SC_ENTITY_FUNCTION: + return ""; + case SC_ENTITY_SEQUENCE: + return ""; + case SC_ENTITY_ROLE: + return ""; + case SC_ENTITY_USER: + return ""; case SC_SPACE: { - if (object_id == 0) - return "SPACE"; struct space *space = space_by_id(object_id); if (space == NULL) break; @@ -547,8 +555,6 @@ 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; @@ -556,8 +562,6 @@ 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 0822262d0..f1735ff34 100644 --- a/src/box/schema.h +++ b/src/box/schema.h @@ -250,16 +250,19 @@ 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; - } + switch (type) { + case SC_SPACE: + case SC_ENTITY_SPACE: + return entity_access.space; + case SC_FUNCTION: + case SC_ENTITY_FUNCTION: + return entity_access.function; + case SC_SEQUENCE: + case SC_ENTITY_SEQUENCE: + return entity_access.sequence; + default: + return NULL; + } } #endif /* INCLUDES_TARANTOOL_BOX_SCHEMA_H */ diff --git a/src/box/schema_def.c b/src/box/schema_def.c index 97c074ab2..18ec6c8d2 100644 --- a/src/box/schema_def.c +++ b/src/box/schema_def.c @@ -31,16 +31,39 @@ #include "schema_def.h" static const char *object_type_strs[] = { - /* [SC_UKNNOWN] = */ "unknown", - /* [SC_UNIVERSE] = */ "universe", - /* [SC_SPACE] = */ "space", - /* [SC_FUNCTION] = */ "function", - /* [SC_USER] = */ "user", - /* [SC_ROLE] = */ "role", - /* [SC_SEQUENCE] = */ "sequence", - /* [SC_COLLATION] = */ "collation", + /* [SC_UKNNOWN] = */ "unknown", + /* [SC_UNIVERSE] = */ "universe", + /* [SC_SPACE] = */ "space", + /* [SC_ENTITY_SPACE] = */ "space", + /* [SC_FUNCTION] = */ "function", + /* [SC_ENTITY_FUNCTION] = */ "function", + /* [SC_USER] = */ "user", + /* [SC_ENTITY_USER] = */ "user", + /* [SC_ROLE] = */ "role", + /* [SC_ENTITY_ROLE] = */ "role", + /* [SC_SEQUENCE] = */ "sequence", + /* [SC_ENTITY_SEQUENCE] = */ "sequence", + /* [SC_COLLATION] = */ "collation", + /* [SC_ENTITY_COLLATION] = */ "collation", }; +enum schema_object_type +schema_entity_type(enum schema_object_type type) +{ + switch(type) { + case SC_SPACE: + case SC_FUNCTION: + case SC_USER: + case SC_ROLE: + case SC_SEQUENCE: + case SC_COLLATION: + return type + 1; + break; + default: + unreachable(); + } +} + enum schema_object_type schema_object_type(const char *name) { diff --git a/src/box/schema_def.h b/src/box/schema_def.h index 2edb8d37f..9b5bd6864 100644 --- a/src/box/schema_def.h +++ b/src/box/schema_def.h @@ -218,19 +218,35 @@ enum { * * Use 0 for unknown to use the same index consistently * even when there are more object types in the future. + * + * When adding new types please follow this rule: + * SC_ENTITY_NEW_OBJECT = SC_NEW_OBJECT + 1 + * schema_entity_type() relies on this convention. */ enum schema_object_type { SC_UNKNOWN = 0, SC_UNIVERSE = 1, SC_SPACE = 2, - SC_FUNCTION = 3, - SC_USER = 4, - SC_ROLE = 5, - SC_SEQUENCE = 6, - SC_COLLATION = 7, - schema_object_type_MAX = 8 + SC_ENTITY_SPACE = 3, + SC_FUNCTION = 4, + SC_ENTITY_FUNCTION = 5, + SC_USER = 6, + SC_ENTITY_USER = 7, + SC_ROLE = 8, + SC_ENTITY_ROLE = 9, + SC_SEQUENCE = 10, + SC_ENTITY_SEQUENCE = 11, + SC_COLLATION = 12, + SC_ENTITY_COLLATION = 13, + schema_object_type_MAX = 13 }; +/** + * Given a object type, return an entity type it belongs to. + */ +enum schema_object_type +schema_entity_type(enum schema_object_type type); + enum schema_object_type schema_object_type(const char *name); diff --git a/src/box/user.cc b/src/box/user.cc index fbf06566a..eec785652 100644 --- a/src/box/user.cc +++ b/src/box/user.cc @@ -207,12 +207,23 @@ access_find(struct priv_def *priv) access = universe.access; break; } + case SC_ENTITY_SPACE: + { + access = entity_access.space; + break; + } + case SC_ENTITY_FUNCTION: + { + access = entity_access.function; + break; + } + case SC_ENTITY_SEQUENCE: + { + access = entity_access.sequence; + break; + } 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; @@ -220,10 +231,6 @@ 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; @@ -231,10 +238,6 @@ 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-py/bootstrap.result b/test/box-py/bootstrap.result index 16c2027cf..1641c318e 100644 --- a/test/box-py/bootstrap.result +++ b/test/box-py/bootstrap.result @@ -5,7 +5,7 @@ box.space._schema:select{} --- - - ['cluster', ''] - ['max_id', 511] - - ['version', 1, 10, 0] + - ['version', 1, 10, 1] ... box.space._cluster:select{} --- @@ -58,10 +58,10 @@ box.space._space:select{} 'type': 'string'}, {'name': 'auth', 'type': 'map'}]] - [312, 1, '_priv', 'memtx', 0, {}, [{'name': 'grantor', 'type': 'unsigned'}, { 'name': 'grantee', 'type': 'unsigned'}, {'name': 'object_type', 'type': 'string'}, - {'name': 'object_id', 'type': 'unsigned'}, {'name': 'privilege', 'type': 'unsigned'}]] + {'name': 'object_id', 'type': 'scalar'}, {'name': 'privilege', 'type': 'unsigned'}]] - [313, 1, '_vpriv', 'sysview', 0, {}, [{'name': 'grantor', 'type': 'unsigned'}, {'name': 'grantee', 'type': 'unsigned'}, {'name': 'object_type', 'type': 'string'}, - {'name': 'object_id', 'type': 'unsigned'}, {'name': 'privilege', 'type': 'unsigned'}]] + {'name': 'object_id', 'type': 'scalar'}, {'name': 'privilege', 'type': 'unsigned'}]] - [320, 1, '_cluster', 'memtx', 0, {}, [{'name': 'id', 'type': 'unsigned'}, {'name': 'uuid', 'type': 'string'}]] - [330, 1, '_truncate', 'memtx', 0, {}, [{'name': 'id', 'type': 'unsigned'}, {'name': 'count', @@ -104,13 +104,13 @@ box.space._index:select{} - [305, 1, 'owner', 'tree', {'unique': false}, [[1, 'unsigned']]] - [305, 2, 'name', 'tree', {'unique': true}, [[2, 'string']]] - [312, 0, 'primary', 'tree', {'unique': true}, [[1, 'unsigned'], [2, 'string'], - [3, 'unsigned']]] + [3, 'scalar']]] - [312, 1, 'owner', 'tree', {'unique': false}, [[0, 'unsigned']]] - - [312, 2, 'object', 'tree', {'unique': false}, [[2, 'string'], [3, 'unsigned']]] + - [312, 2, 'object', 'tree', {'unique': false}, [[2, 'string'], [3, 'scalar']]] - [313, 0, 'primary', 'tree', {'unique': true}, [[1, 'unsigned'], [2, 'string'], - [3, 'unsigned']]] + [3, 'scalar']]] - [313, 1, 'owner', 'tree', {'unique': false}, [[0, 'unsigned']]] - - [313, 2, 'object', 'tree', {'unique': false}, [[2, 'string'], [3, 'unsigned']]] + - [313, 2, 'object', 'tree', {'unique': false}, [[2, 'string'], [3, 'scalar']]] - [320, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned']]] - [320, 1, 'uuid', 'tree', {'unique': true}, [[1, 'string']]] - [330, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned']]] diff --git a/test/box/access.result b/test/box/access.result index f4669a4a3..687bbf579 100644 --- a/test/box/access.result +++ b/test/box/access.result @@ -504,7 +504,7 @@ box.space._priv:select{id} ... box.schema.user.grant('user', 'read', 'universe') --- -- error: User 'user' already has read access on universe 'nil' +- error: User 'user' already has read access on universe '*' ... box.space._priv:select{id} --- @@ -690,7 +690,7 @@ box.schema.user.grant('guest', 'read,write,execute', 'universe') ... box.schema.user.grant('guest', 'read,write,execute', 'universe') --- -- error: User 'guest' already has read,write,execute access on universe 'nil' +- error: User 'guest' already has read,write,execute access on universe '*' ... box.schema.user.grant('guest', 'read,write,execute', 'universe', '', { if_not_exists = true }) --- @@ -703,7 +703,7 @@ box.schema.user.revoke('guest', 'usage,session', 'universe') ... box.schema.user.revoke('guest', 'read,write,execute', 'universe') --- -- error: User 'guest' does not have read,write,execute access on universe 'nil' +- error: User 'guest' does not have read,write,execute access on universe '*' ... box.schema.user.revoke('guest', 'read,write,execute', 'universe', '', { if_exists = true }) --- diff --git a/test/box/access_misc.result b/test/box/access_misc.result index 2d87fa2d5..7d6aa0a4b 100644 --- a/test/box/access_misc.result +++ b/test/box/access_misc.result @@ -436,7 +436,7 @@ box.schema.user.revoke('testuser', 'usage,session', 'universe') ... box.schema.user.revoke('testuser', 'read, write, execute', 'universe') --- -- error: User 'testuser' does not have read, write, execute access on universe 'nil' +- error: User 'testuser' does not have read, write, execute access on universe '*' ... box.schema.user.revoke('testuser', 'create', 'universe') --- @@ -797,10 +797,10 @@ box.space._space:select() 'type': 'string'}, {'name': 'auth', 'type': 'map'}]] - [312, 1, '_priv', 'memtx', 0, {}, [{'name': 'grantor', 'type': 'unsigned'}, { 'name': 'grantee', 'type': 'unsigned'}, {'name': 'object_type', 'type': 'string'}, - {'name': 'object_id', 'type': 'unsigned'}, {'name': 'privilege', 'type': 'unsigned'}]] + {'name': 'object_id', 'type': 'scalar'}, {'name': 'privilege', 'type': 'unsigned'}]] - [313, 1, '_vpriv', 'sysview', 0, {}, [{'name': 'grantor', 'type': 'unsigned'}, {'name': 'grantee', 'type': 'unsigned'}, {'name': 'object_type', 'type': 'string'}, - {'name': 'object_id', 'type': 'unsigned'}, {'name': 'privilege', 'type': 'unsigned'}]] + {'name': 'object_id', 'type': 'scalar'}, {'name': 'privilege', 'type': 'unsigned'}]] - [320, 1, '_cluster', 'memtx', 0, {}, [{'name': 'id', 'type': 'unsigned'}, {'name': 'uuid', 'type': 'string'}]] - [330, 1, '_truncate', 'memtx', 0, {}, [{'name': 'id', 'type': 'unsigned'}, {'name': 'count', @@ -853,7 +853,7 @@ box.schema.user.grant('tester', 'read', 'universe') -- error: the privilege is not granted box.schema.user.revoke('tester', 'create', 'universe') --- -- error: User 'tester' does not have create access on universe 'nil' +- error: User 'tester' does not have create access on universe '*' ... -- no error: if_exists clause box.schema.user.revoke('tester', 'create', 'universe', nil, { if_exists = true }) diff --git a/test/box/alter.result b/test/box/alter.result index eb7014d8b..36bdb5fd3 100644 --- a/test/box/alter.result +++ b/test/box/alter.result @@ -214,13 +214,13 @@ _index:select{} - [305, 1, 'owner', 'tree', {'unique': false}, [[1, 'unsigned']]] - [305, 2, 'name', 'tree', {'unique': true}, [[2, 'string']]] - [312, 0, 'primary', 'tree', {'unique': true}, [[1, 'unsigned'], [2, 'string'], - [3, 'unsigned']]] + [3, 'scalar']]] - [312, 1, 'owner', 'tree', {'unique': false}, [[0, 'unsigned']]] - - [312, 2, 'object', 'tree', {'unique': false}, [[2, 'string'], [3, 'unsigned']]] + - [312, 2, 'object', 'tree', {'unique': false}, [[2, 'string'], [3, 'scalar']]] - [313, 0, 'primary', 'tree', {'unique': true}, [[1, 'unsigned'], [2, 'string'], - [3, 'unsigned']]] + [3, 'scalar']]] - [313, 1, 'owner', 'tree', {'unique': false}, [[0, 'unsigned']]] - - [313, 2, 'object', 'tree', {'unique': false}, [[2, 'string'], [3, 'unsigned']]] + - [313, 2, 'object', 'tree', {'unique': false}, [[2, 'string'], [3, 'scalar']]] - [320, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned']]] - [320, 1, 'uuid', 'tree', {'unique': true}, [[1, 'string']]] - [330, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned']]] diff --git a/test/xlog/upgrade.result b/test/xlog/upgrade.result index f02996bba..8f5b4ad26 100644 --- a/test/xlog/upgrade.result +++ b/test/xlog/upgrade.result @@ -36,7 +36,7 @@ box.space._schema:select() --- - - ['cluster', ''] - ['max_id', 513] - - ['version', 1, 10, 0] + - ['version', 1, 10, 1] ... box.space._space:select() --- @@ -85,10 +85,10 @@ box.space._space:select() 'type': 'string'}, {'name': 'auth', 'type': 'map'}]] - [312, 1, '_priv', 'memtx', 0, {}, [{'name': 'grantor', 'type': 'unsigned'}, { 'name': 'grantee', 'type': 'unsigned'}, {'name': 'object_type', 'type': 'string'}, - {'name': 'object_id', 'type': 'unsigned'}, {'name': 'privilege', 'type': 'unsigned'}]] + {'name': 'object_id', 'type': 'scalar'}, {'name': 'privilege', 'type': 'unsigned'}]] - [313, 1, '_vpriv', 'sysview', 0, {}, [{'name': 'grantor', 'type': 'unsigned'}, {'name': 'grantee', 'type': 'unsigned'}, {'name': 'object_type', 'type': 'string'}, - {'name': 'object_id', 'type': 'unsigned'}, {'name': 'privilege', 'type': 'unsigned'}]] + {'name': 'object_id', 'type': 'scalar'}, {'name': 'privilege', 'type': 'unsigned'}]] - [320, 1, '_cluster', 'memtx', 0, {}, [{'name': 'id', 'type': 'unsigned'}, {'name': 'uuid', 'type': 'string'}]] - [330, 1, '_truncate', 'memtx', 0, {}, [{'name': 'id', 'type': 'unsigned'}, {'name': 'count', @@ -134,13 +134,13 @@ box.space._index:select() - [305, 1, 'owner', 'tree', {'unique': false}, [[1, 'unsigned']]] - [305, 2, 'name', 'tree', {'unique': true}, [[2, 'string']]] - [312, 0, 'primary', 'tree', {'unique': true}, [[1, 'unsigned'], [2, 'string'], - [3, 'unsigned']]] + [3, 'scalar']]] - [312, 1, 'owner', 'tree', {'unique': false}, [[0, 'unsigned']]] - - [312, 2, 'object', 'tree', {'unique': false}, [[2, 'string'], [3, 'unsigned']]] + - [312, 2, 'object', 'tree', {'unique': false}, [[2, 'string'], [3, 'scalar']]] - [313, 0, 'primary', 'tree', {'unique': true}, [[1, 'unsigned'], [2, 'string'], - [3, 'unsigned']]] + [3, 'scalar']]] - [313, 1, 'owner', 'tree', {'unique': false}, [[0, 'unsigned']]] - - [313, 2, 'object', 'tree', {'unique': false}, [[2, 'string'], [3, 'unsigned']]] + - [313, 2, 'object', 'tree', {'unique': false}, [[2, 'string'], [3, 'scalar']]] - [320, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned']]] - [320, 1, 'uuid', 'tree', {'unique': true}, [[1, 'string']]] - [330, 0, 'primary', 'tree', {'unique': true}, [[0, 'unsigned']]] -- 2.15.2 (Apple Git-101.1)